Add support for "brctl show".
authorJustin Pettit <jpettit@nicira.com>
Wed, 31 Dec 2008 06:31:38 +0000 (22:31 -0800)
committerJustin Pettit <jpettit@nicira.com>
Wed, 31 Dec 2008 06:32:06 +0000 (22:32 -0800)
This makes datapaths and their interfaces show up as you'd expect when
"brctl show" is run.  To get this functionality, you must insmod the
brcompat kernel module.

datapath/brcompat.c
datapath/datapath.c
datapath/dp_dev.c
datapath/dp_dev.h

index a5652f817f1f1b3bd4d93bacd38340c449f9d651..29526c575412d6cdc45328564e9c5b7bddaea027 100644 (file)
@@ -6,6 +6,7 @@
 #include <net/genetlink.h>
 
 #include "compat.h"
+#include "openflow/openflow-netlink.h"
 #include "openflow/brcompat-netlink.h"
 #include "datapath.h"
 #include "dp_dev.h"
@@ -16,8 +17,74 @@ static struct genl_multicast_group brc_mc_group;
 int brc_send_dp_add_del(const char *dp_name, int add);
 int brc_send_port_add_del(const char *dp_name, const char *port_name, int add);
 
+/* Called with RTNL lock. */
+static int
+get_dp_ifindices(int *indices, int num)
+{
+       int i, index = 0;
+
+       mutex_lock(&dp_mutex);
+       for (i=0; i < DP_MAX && index < num; i++) {
+               struct datapath *dp = dp_get_by_idx(i);
+               if (!dp)
+                       continue;
+               indices[index++] = dp->netdev->ifindex;
+       }
+       mutex_unlock(&dp_mutex);
+
+       return index;
+}
+
+/* Called with RTNL lock. */
+static void
+get_port_ifindices(struct datapath *dp, int *ifindices, int num)
+{
+       struct net_bridge_port *p;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu (p, &dp->port_list, node) {
+               if (p->port_no < num)
+                       ifindices[p->port_no] = p->dev->ifindex;
+       }
+       rcu_read_unlock();
+}
+
+/* Legacy deviceless bridge ioctl's.  We only use this to provide
+ * support for "brctl show". */
+static int
+old_deviceless(void __user *uarg)
+{
+       unsigned long args[3];
+
+       if (copy_from_user(args, uarg, sizeof(args)))
+               return -EFAULT;
+
+       switch (args[0]) {
+       case BRCTL_GET_BRIDGES: { 
+               int *indices;
+               int ret = 0;
+
+               if (args[2] >= 2048)
+                       return -ENOMEM;
+
+               indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
+               if (indices == NULL)
+                       return -ENOMEM;
+
+               args[2] = get_dp_ifindices(indices, args[2]);
+
+               ret = copy_to_user((void __user *)args[1], indices, 
+                               args[2]*sizeof(int)) ? -EFAULT : args[2];
+
+               kfree(indices);
+               return ret;
+       }
+       }
 
-int
+       return -EOPNOTSUPP;
+}
+
+static int
 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
 brc_ioctl_deviceless_stub(unsigned int cmd, void __user *uarg)
 #else
@@ -25,6 +92,9 @@ brc_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
 #endif
 {
        switch (cmd) {
+       case SIOCGIFBR:
+               return old_deviceless(uarg);
+
        case SIOCBRADDBR:
        case SIOCBRDELBR: {
                char dp_name[IFNAMSIZ];
@@ -43,30 +113,95 @@ brc_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
        return -EOPNOTSUPP;
 }
 
+/* Legacy ioctl's through SIOCDEVPRIVATE.  We only use this to provide
+ * support for "brctl show". */
+static int
+old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       struct dp_dev *dp_dev = netdev_priv(dev);
+       struct datapath *dp = dp_dev->dp;
+       unsigned long args[4];
+
+       if (copy_from_user(args, rq->ifr_data, sizeof(args)))
+               return -EFAULT;
+
+       switch (args[0]) {
+       case BRCTL_GET_BRIDGE_INFO: {
+               struct __bridge_info b;
+               uint64_t id = 0;
+               int i;
+
+               memset(&b, 0, sizeof(struct __bridge_info));
+
+               /* xxx Mutex Locking? */
+               rcu_read_lock();
+               for (i=0; i<ETH_ALEN; i++) 
+                       id |= (uint64_t)dev->dev_addr[i] << (8*(ETH_ALEN-1 - i));
+               b.bridge_id = cpu_to_be64(id);
+               b.stp_enabled = 0;
+               rcu_read_unlock();
+
+               if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
+                       return -EFAULT;
+
+               return 0;
+       }
+
+       case BRCTL_GET_PORT_LIST: {
+               int num, *indices;
+
+               num = args[2];
+               if (num < 0)
+                       return -EINVAL;
+               if (num == 0)
+                       num = 256;
+               if (num > OFPP_MAX)
+                       num = OFPP_MAX;
+
+               indices = kcalloc(num, sizeof(int), GFP_KERNEL);
+               if (indices == NULL)
+                       return -ENOMEM;
+
+               get_port_ifindices(dp, indices, num);
+               if (copy_to_user((void __user *)args[1], indices, num*sizeof(int)))
+                       num = -EFAULT;
+               kfree(indices);
+               return num;
+       }
+       }
+
+       return -EOPNOTSUPP;
+}
+
 /* Called with the rtnl_lock. */
-int
+static int
 brc_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
        int err;
-       struct net_device *port;
-
-       port = dev_get_by_index(&init_net, rq->ifr_ifindex);
-       if (!port) 
-               return -EINVAL;
 
        switch (cmd) {
-               case SIOCBRADDIF:
-                       err = brc_send_port_add_del(dev->name, port->name, 1);
+               case SIOCDEVPRIVATE:
+                       err = old_dev_ioctl(dev, rq, cmd);
                        break;
-               case SIOCBRDELIF:
-                       err = brc_send_port_add_del(dev->name, port->name, 0);
+
+               case SIOCBRADDIF:
+               case SIOCBRDELIF: {
+                       struct net_device *port;
+
+                       port = dev_get_by_index(&init_net, rq->ifr_ifindex);
+                       if (!port) 
+                               return -EINVAL;
+                       err = brc_send_port_add_del(dev->name, port->name, 
+                                               cmd == SIOCBRADDIF);
+                       dev_put(port);
                        break;
+               }
+
                default:
                        err = -EOPNOTSUPP;
                        break;
        }
 
-       dev_put(port);
        return err;
 }
 
index e5f011d1336de48ab5091fb1d0a9be2216f0105d..286c4c02aeed6b26d8af531db5eae7ba784133c9 100644 (file)
@@ -1136,6 +1136,7 @@ struct datapath *dp_get_by_idx(int dp_idx)
                return NULL;
        return rcu_dereference(dps[dp_idx]);
 }
+EXPORT_SYMBOL(dp_get_by_idx);
 
 struct datapath *dp_get_by_name(const char *dp_name)
 {
index f2d1acd150a99e1617061937fc5518932f92cc9b..f866fb4e1ba0cb7acb031ad7f81f6978ec355149 100644 (file)
@@ -7,15 +7,9 @@
 #include <linux/dmi.h>
 
 #include "datapath.h"
+#include "dp_dev.h"
 #include "forward.h"
 
-struct dp_dev {
-       struct net_device_stats stats;
-       struct datapath *dp;
-       struct sk_buff_head xmit_queue;
-       struct work_struct xmit_work;
-};
-
 
 static struct dp_dev *dp_dev_priv(struct net_device *netdev) 
 {
index 1d1abac2a9337b4cc90209fd2d3b58ecf9a693ab..6afc467206b85cb91c5faea9d9c78d371aeb4843 100644 (file)
@@ -1,6 +1,12 @@
 #ifndef DP_DEV_H
 #define DP_DEV_H 1
 
+struct dp_dev {
+       struct net_device_stats stats;
+       struct datapath *dp;
+       struct sk_buff_head xmit_queue;
+       struct work_struct xmit_work;
+};
 
 int dp_dev_setup(struct datapath *, const char *);
 void dp_dev_destroy(struct datapath *);