X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fbridge%2Fbr_ioctl.c;h=17c7ee3b3aa6dc07e01cccce1d4c14c8ead71a99;hb=2ab7e6fcc578694936be605151227a660d2f1b95;hp=4cff15b1c705669f74fe2bd35ae4fb41d5a5319f;hpb=86090fcac5e27b630656fe3d963a6b80e26dac44;p=linux-2.6.git diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c index 4cff15b1c..17c7ee3b3 100644 --- a/net/bridge/br_ioctl.c +++ b/net/bridge/br_ioctl.c @@ -20,42 +20,102 @@ #include #include "br_private.h" -/* Report time remaining in user HZ */ -static unsigned long timer_residue(const struct timer_list *timer) +/* called with RTNL */ +static int get_bridge_ifindices(int *indices, int num) { - return timer_pending(timer) - ? jiffies_to_clock_t(timer->expires - jiffies) : 0; + struct net_device *dev; + int i = 0; + + for (dev = dev_base; dev && i < num; dev = dev->next) { + if (dev->priv_flags & IFF_EBRIDGE) + indices[i++] = dev->ifindex; + } + + return i; } -int br_ioctl_device(struct net_bridge *br, unsigned int cmd, - unsigned long arg0, unsigned long arg1, unsigned long arg2) +/* called with RTNL */ +static void get_port_ifindices(struct net_bridge *br, int *ifindices, int num) { - if (br == NULL) - return -EINVAL; - - switch (cmd) - { - case BRCTL_ADD_IF: - case BRCTL_DEL_IF: - { - struct net_device *dev; - int ret; + struct net_bridge_port *p; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; + list_for_each_entry(p, &br->port_list, list) { + if (p->port_no < num) + ifindices[p->port_no] = p->dev->ifindex; + } +} - dev = dev_get_by_index(arg0); - if (dev == NULL) - return -EINVAL; +/* + * Format up to a page worth of forwarding table entries + * userbuf -- where to copy result + * maxnum -- maximum number of entries desired + * (limited to a page for sanity) + * offset -- number of records to skip + */ +static int get_fdb_entries(struct net_bridge *br, void __user *userbuf, + unsigned long maxnum, unsigned long offset) +{ + int num; + void *buf; + size_t size = maxnum * sizeof(struct __fdb_entry); - if (cmd == BRCTL_ADD_IF) - ret = br_add_if(br, dev); - else - ret = br_del_if(br, dev); + if (size > PAGE_SIZE) { + size = PAGE_SIZE; + maxnum = PAGE_SIZE/sizeof(struct __fdb_entry); + } - dev_put(dev); - return ret; + buf = kmalloc(size, GFP_USER); + if (!buf) + return -ENOMEM; + + num = br_fdb_fillbuf(br, buf, maxnum, offset); + if (num > 0) { + if (copy_to_user(userbuf, buf, num*sizeof(struct __fdb_entry))) + num = -EFAULT; } + kfree(buf); + + return num; +} + +static int add_del_if(struct net_bridge *br, int ifindex, int isadd) +{ + struct net_device *dev; + int ret; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + dev = dev_get_by_index(ifindex); + if (dev == NULL) + return -EINVAL; + + if (isadd) + ret = br_add_if(br, dev); + else + ret = br_del_if(br, dev); + + dev_put(dev); + return ret; +} + +/* + * Legacy ioctl's through SIOCDEVPRIVATE + * This interface is deprecated because it was too difficult to + * to do the translation for 32/64bit ioctl compatability. + */ +static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct net_bridge *br = netdev_priv(dev); + unsigned long args[4]; + + if (copy_from_user(args, rq->ifr_data, sizeof(args))) + return -EFAULT; + + switch (args[0]) { + case BRCTL_ADD_IF: + case BRCTL_DEL_IF: + return add_del_if(br, args[1], args[0] == BRCTL_ADD_IF); case BRCTL_GET_BRIDGE_INFO: { @@ -77,13 +137,13 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, b.root_port = br->root_port; b.stp_enabled = br->stp_enabled; b.ageing_time = jiffies_to_clock_t(br->ageing_time); - b.hello_timer_value = timer_residue(&br->hello_timer); - b.tcn_timer_value = timer_residue(&br->tcn_timer); - b.topology_change_timer_value = timer_residue(&br->topology_change_timer); - b.gc_timer_value = timer_residue(&br->gc_timer); + b.hello_timer_value = br_timer_value(&br->hello_timer); + b.tcn_timer_value = br_timer_value(&br->tcn_timer); + b.topology_change_timer_value = br_timer_value(&br->topology_change_timer); + b.gc_timer_value = br_timer_value(&br->gc_timer); rcu_read_unlock(); - if (copy_to_user((void *)arg0, &b, sizeof(b))) + if (copy_to_user((void *)args[1], &b, sizeof(b))) return -EFAULT; return 0; @@ -93,7 +153,7 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, { int num, *indices; - num = arg1; + num = args[2]; if (num < 0) return -EINVAL; if (num == 0) @@ -107,8 +167,8 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, memset(indices, 0, num*sizeof(int)); - br_get_port_ifindices(br, indices, num); - if (copy_to_user((void *)arg0, indices, num*sizeof(int))) + get_port_ifindices(br, indices, num); + if (copy_to_user((void *)args[1], indices, num*sizeof(int))) num = -EFAULT; kfree(indices); return num; @@ -119,7 +179,7 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, return -EPERM; spin_lock_bh(&br->lock); - br->bridge_forward_delay = clock_t_to_jiffies(arg0); + br->bridge_forward_delay = clock_t_to_jiffies(args[1]); if (br_is_root_bridge(br)) br->forward_delay = br->bridge_forward_delay; spin_unlock_bh(&br->lock); @@ -130,7 +190,7 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, return -EPERM; spin_lock_bh(&br->lock); - br->bridge_hello_time = clock_t_to_jiffies(arg0); + br->bridge_hello_time = clock_t_to_jiffies(args[1]); if (br_is_root_bridge(br)) br->hello_time = br->bridge_hello_time; spin_unlock_bh(&br->lock); @@ -141,7 +201,7 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, return -EPERM; spin_lock_bh(&br->lock); - br->bridge_max_age = clock_t_to_jiffies(arg0); + br->bridge_max_age = clock_t_to_jiffies(args[1]); if (br_is_root_bridge(br)) br->max_age = br->bridge_max_age; spin_unlock_bh(&br->lock); @@ -151,7 +211,7 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, if (!capable(CAP_NET_ADMIN)) return -EPERM; - br->ageing_time = clock_t_to_jiffies(arg0); + br->ageing_time = clock_t_to_jiffies(args[1]); return 0; case BRCTL_GET_PORT_INFO: @@ -160,7 +220,7 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, struct net_bridge_port *pt; rcu_read_lock(); - if ((pt = br_get_port(br, arg1)) == NULL) { + if ((pt = br_get_port(br, args[2])) == NULL) { rcu_read_unlock(); return -EINVAL; } @@ -175,13 +235,13 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, p.state = pt->state; p.top_change_ack = pt->topology_change_ack; p.config_pending = pt->config_pending; - p.message_age_timer_value = timer_residue(&pt->message_age_timer); - p.forward_delay_timer_value = timer_residue(&pt->forward_delay_timer); - p.hold_timer_value = timer_residue(&pt->hold_timer); + p.message_age_timer_value = br_timer_value(&pt->message_age_timer); + p.forward_delay_timer_value = br_timer_value(&pt->forward_delay_timer); + p.hold_timer_value = br_timer_value(&pt->hold_timer); rcu_read_unlock(); - if (copy_to_user((void *)arg0, &p, sizeof(p))) + if (copy_to_user((void *)args[1], &p, sizeof(p))) return -EFAULT; return 0; @@ -191,7 +251,7 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, if (!capable(CAP_NET_ADMIN)) return -EPERM; - br->stp_enabled = arg0?1:0; + br->stp_enabled = args[1]?1:0; return 0; case BRCTL_SET_BRIDGE_PRIORITY: @@ -199,7 +259,7 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, return -EPERM; spin_lock_bh(&br->lock); - br_stp_set_bridge_priority(br, arg0); + br_stp_set_bridge_priority(br, args[1]); spin_unlock_bh(&br->lock); return 0; @@ -211,14 +271,14 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (arg1 >= (1<<(16-BR_PORT_BITS))) + if (args[2] >= (1<<(16-BR_PORT_BITS))) return -ERANGE; spin_lock_bh(&br->lock); - if ((p = br_get_port(br, arg0)) == NULL) + if ((p = br_get_port(br, args[1])) == NULL) ret = -EINVAL; else - br_stp_set_port_priority(p, arg1); + br_stp_set_port_priority(p, args[2]); spin_unlock_bh(&br->lock); return ret; } @@ -232,27 +292,30 @@ int br_ioctl_device(struct net_bridge *br, unsigned int cmd, return -EPERM; spin_lock_bh(&br->lock); - if ((p = br_get_port(br, arg0)) == NULL) + if ((p = br_get_port(br, args[1])) == NULL) ret = -EINVAL; else - br_stp_set_path_cost(p, arg1); + br_stp_set_path_cost(p, args[2]); spin_unlock_bh(&br->lock); return ret; } case BRCTL_GET_FDB_ENTRIES: - return br_fdb_get_entries(br, (void *)arg0, arg1, arg2); + return get_fdb_entries(br, (void __user *)args[1], + args[2], args[3]); } return -EOPNOTSUPP; } -static int br_ioctl_deviceless(unsigned int cmd, - unsigned long arg0, - unsigned long arg1) +static int old_deviceless(unsigned long uarg) { - switch (cmd) - { + unsigned long args[3]; + + if (copy_from_user(args, (void *)uarg, sizeof(args))) + return -EFAULT; + + switch (args[0]) { case BRCTL_GET_VERSION: return BRCTL_VERSION; @@ -261,15 +324,15 @@ static int br_ioctl_deviceless(unsigned int cmd, int *indices; int ret = 0; - indices = kmalloc(arg1*sizeof(int), GFP_KERNEL); + indices = kmalloc(args[2]*sizeof(int), GFP_KERNEL); if (indices == NULL) return -ENOMEM; - memset(indices, 0, arg1*sizeof(int)); - arg1 = br_get_bridge_ifindices(indices, arg1); + memset(indices, 0, args[2]*sizeof(int)); + args[2] = get_bridge_ifindices(indices, args[2]); - ret = copy_to_user((void *)arg0, indices, arg1*sizeof(int)) - ? -EFAULT : arg1; + ret = copy_to_user((void *)args[1], indices, args[2]*sizeof(int)) + ? -EFAULT : args[2]; kfree(indices); return ret; @@ -283,12 +346,12 @@ static int br_ioctl_deviceless(unsigned int cmd, if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (copy_from_user(buf, (void *)arg0, IFNAMSIZ)) + if (copy_from_user(buf, (void *)args[1], IFNAMSIZ)) return -EFAULT; buf[IFNAMSIZ-1] = 0; - if (cmd == BRCTL_ADD_BRIDGE) + if (args[0] == BRCTL_ADD_BRIDGE) return br_add_bridge(buf); return br_del_bridge(buf); @@ -298,13 +361,48 @@ static int br_ioctl_deviceless(unsigned int cmd, return -EOPNOTSUPP; } +int br_ioctl_deviceless_stub(unsigned int cmd, unsigned long uarg) +{ + switch (cmd) { + case SIOCGIFBR: + case SIOCSIFBR: + return old_deviceless(uarg); + + case SIOCBRADDBR: + case SIOCBRDELBR: + { + char buf[IFNAMSIZ]; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; -int br_ioctl_deviceless_stub(unsigned long arg) + if (copy_from_user(buf, (void __user *) uarg, IFNAMSIZ)) + return -EFAULT; + + buf[IFNAMSIZ-1] = 0; + if (cmd == SIOCBRADDBR) + return br_add_bridge(buf); + + return br_del_bridge(buf); + } + } + return -EOPNOTSUPP; +} + +int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - unsigned long i[3]; + struct net_bridge *br = netdev_priv(dev); - if (copy_from_user(i, (void *)arg, 3*sizeof(unsigned long))) - return -EFAULT; + switch(cmd) { + case SIOCDEVPRIVATE: + return old_dev_ioctl(dev, rq, cmd); + + case SIOCBRADDIF: + case SIOCBRDELIF: + return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF); - return br_ioctl_deviceless(i[0], i[1], i[2]); + } + + printk(KERN_DEBUG "Bridge does not support ioctl 0x%x\n", cmd); + return -EOPNOTSUPP; }