patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / net / bridge / br_ioctl.c
index 4cff15b..cceb018 100644 (file)
 #include <asm/uaccess.h>
 #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 __user *)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 __user *)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 __user *)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(void __user *uarg)
 {
-       switch (cmd)
-       {
+       unsigned long args[3];
+
+       if (copy_from_user(args, 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 __user *)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 __user *)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, void __user *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, 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;
 }