From: Justin Pettit Date: Wed, 31 Dec 2008 06:31:38 +0000 (-0800) Subject: Add support for "brctl show". X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c58ffb6bb4988d733ff1c6ee98b22c441058e127;p=openvswitch Add support for "brctl show". 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. --- diff --git a/datapath/brcompat.c b/datapath/brcompat.c index a5652f81..29526c57 100644 --- a/datapath/brcompat.c +++ b/datapath/brcompat.c @@ -6,6 +6,7 @@ #include #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; idev_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; } diff --git a/datapath/datapath.c b/datapath/datapath.c index e5f011d1..286c4c02 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -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) { diff --git a/datapath/dp_dev.c b/datapath/dp_dev.c index f2d1acd1..f866fb4e 100644 --- a/datapath/dp_dev.c +++ b/datapath/dp_dev.c @@ -7,15 +7,9 @@ #include #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) { diff --git a/datapath/dp_dev.h b/datapath/dp_dev.h index 1d1abac2..6afc4672 100644 --- a/datapath/dp_dev.h +++ b/datapath/dp_dev.h @@ -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 *);