#include <net/genetlink.h>
#include "compat.h"
+#include "openflow/openflow-netlink.h"
#include "openflow/brcompat-netlink.h"
#include "datapath.h"
#include "dp_dev.h"
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
#endif
{
switch (cmd) {
+ case SIOCGIFBR:
+ return old_deviceless(uarg);
+
case SIOCBRADDBR:
case SIOCBRDELBR: {
char dp_name[IFNAMSIZ];
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;
}