Add support for sysfs when the bridge compatibility module is running.
Currently, this only works for 2.6.18 kernels. Working on all kernels
should be fixed soon. Also, add ethtool support to the datapath device.
--- /dev/null
+#ifndef BRC_SYSFS_H
+#define BRC_SYSFS_H 1
+
+struct datapath;
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
+/* brc_sysfs_dp.c */
+int brc_sysfs_add_dp(struct datapath *dp);
+int brc_sysfs_del_dp(struct datapath *dp);
+
+/* brc_sysfs_if.c */
+int brc_sysfs_add_if(struct net_bridge_port *p);
+int brc_sysfs_del_if(struct net_bridge_port *p);
+#else
+static inline int brc_sysfs_add_dp(struct datapath *dp) { return 0; }
+static inline int brc_sysfs_del_dp(struct datapath *dp) { return 0; }
+static inline int brc_sysfs_add_if(struct net_bridge_port *p) { return 0; }
+static inline int brc_sysfs_del_if(struct net_bridge_port *p) { return 0; }
+#endif
+
+#endif /* brc_sysfs.h */
+
--- /dev/null
+#include <linux/version.h>
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
+
+/*
+ * Sysfs attributes of bridge for OpenFlow
+ *
+ * This has been shamelessly copied from the kernel sources.
+ */
+
+#include <linux/capability.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <linux/times.h>
+#include <linux/version.h>
+
+#include "brc_sysfs.h"
+#include "datapath.h"
+#include "dp_dev.h"
+
+#define to_dev(obj) container_of(obj, struct device, kobj)
+
+/* Hack to attempt to build on more platforms. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define to_kobj(d) &(d)->class_dev.kobj
+#define BRC_DEVICE_ATTR CLASS_DEVICE_ATTR
+#else
+#define to_kobj(d) &(d)->dev.kobj
+#define BRC_DEVICE_ATTR DEVICE_ATTR
+#endif
+
+
+/*
+ * Common code for storing bridge parameters.
+ */
+static ssize_t store_bridge_parm(struct class_device *d,
+ const char *buf, size_t len,
+ void (*set)(struct datapath *, unsigned long))
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+#endif
+ char *endp;
+ unsigned long val;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ return -EINVAL;
+
+#if 0
+ spin_lock_bh(&br->lock);
+ (*set)(br, val);
+ spin_unlock_bh(&br->lock);
+#else
+ printk("xxx writing dp parms not supported yet!\n");
+#endif
+ return len;
+}
+
+
+static ssize_t show_forward_delay(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_forward_delay(struct datapath *dp, unsigned long val)
+{
+#if 0
+ unsigned long delay = clock_t_to_jiffies(val);
+ br->forward_delay = delay;
+ if (br_is_root_bridge(br))
+ br->bridge_forward_delay = delay;
+#else
+ printk("xxx attempt to set_forward_delay()\n");
+#endif
+}
+
+static ssize_t store_forward_delay(struct class_device *d,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_forward_delay);
+}
+static BRC_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
+ show_forward_delay, store_forward_delay);
+
+static ssize_t show_hello_time(struct class_device *d, char *buf)
+{
+#if 0
+ return sprintf(buf, "%lu\n",
+ jiffies_to_clock_t(to_bridge(d)->hello_time));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_hello_time(struct datapath *dp, unsigned long val)
+{
+#if 0
+ unsigned long t = clock_t_to_jiffies(val);
+ br->hello_time = t;
+ if (br_is_root_bridge(br))
+ br->bridge_hello_time = t;
+#else
+ printk("xxx attempt to set_hello_time()\n");
+#endif
+}
+
+static ssize_t store_hello_time(struct class_device *d,
+ const char *buf,
+ size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_hello_time);
+}
+static BRC_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
+ store_hello_time);
+
+static ssize_t show_max_age(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%lu\n",
+ jiffies_to_clock_t(to_bridge(d)->max_age));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_max_age(struct datapath *dp, unsigned long val)
+{
+#if 0
+ unsigned long t = clock_t_to_jiffies(val);
+ br->max_age = t;
+ if (br_is_root_bridge(br))
+ br->bridge_max_age = t;
+#else
+ printk("xxx attempt to set_max_age()\n");
+#endif
+}
+
+static ssize_t store_max_age(struct class_device *d,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_max_age);
+}
+static BRC_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
+
+static ssize_t show_ageing_time(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_ageing_time(struct datapath *dp, unsigned long val)
+{
+#if 0
+ br->ageing_time = clock_t_to_jiffies(val);
+#else
+ printk("xxx attempt to set_ageing_time()\n");
+#endif
+}
+
+static ssize_t store_ageing_time(struct class_device *d,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_ageing_time);
+}
+static BRC_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
+ store_ageing_time);
+
+static ssize_t show_stp_state(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%d\n", br->stp_enabled);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+
+static ssize_t store_stp_state(struct class_device *d,
+ const char *buf,
+ size_t len)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ char *endp;
+ unsigned long val;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ return -EINVAL;
+
+ rtnl_lock();
+ br_stp_set_enabled(br, val);
+ rtnl_unlock();
+#else
+ printk("xxx attempt to set_stp_state()\n");
+#endif
+
+ return len;
+}
+static BRC_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
+ store_stp_state);
+
+static ssize_t show_priority(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%d\n",
+ (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_priority(struct datapath *dp, unsigned long val)
+{
+#if 0
+ br_stp_set_bridge_priority(br, (u16) val);
+#else
+ printk("xxx attempt to set_priority()\n");
+#endif
+}
+
+static ssize_t store_priority(struct class_device *d,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_priority);
+}
+static BRC_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
+
+static ssize_t show_root_id(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
+#else
+ return sprintf(buf, "0000.010203040506\n");
+#endif
+}
+static BRC_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
+
+static ssize_t show_bridge_id(struct class_device *d,
+ char *buf)
+{
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ const unsigned char *addr = dp->netdev->dev_addr;
+
+ /* xxx Do we need a lock of some sort? */
+ return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
+ 0, 0, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+static BRC_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
+
+static ssize_t show_root_port(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", to_bridge(d)->root_port);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
+
+static ssize_t show_root_path_cost(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
+
+static ssize_t show_topology_change(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
+
+static ssize_t show_topology_change_detected(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%d\n", br->topology_change_detected);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(topology_change_detected, S_IRUGO,
+ show_topology_change_detected, NULL);
+
+static ssize_t show_hello_timer(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
+
+static ssize_t show_tcn_timer(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
+
+static ssize_t show_topology_change_timer(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
+ NULL);
+
+static ssize_t show_gc_timer(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
+
+static ssize_t show_group_addr(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%x:%x:%x:%x:%x:%x\n",
+ br->group_addr[0], br->group_addr[1],
+ br->group_addr[2], br->group_addr[3],
+ br->group_addr[4], br->group_addr[5]);
+#else
+ return sprintf(buf, "00:01:02:03:04:05\n");
+#endif
+}
+
+static ssize_t store_group_addr(struct class_device *d,
+ const char *buf, size_t len)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ unsigned new_addr[6];
+ int i;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
+ &new_addr[0], &new_addr[1], &new_addr[2],
+ &new_addr[3], &new_addr[4], &new_addr[5]) != 6)
+ return -EINVAL;
+
+ /* Must be 01:80:c2:00:00:0X */
+ for (i = 0; i < 5; i++)
+ if (new_addr[i] != br_group_address[i])
+ return -EINVAL;
+
+ if (new_addr[5] & ~0xf)
+ return -EINVAL;
+
+ if (new_addr[5] == 1 /* 802.3x Pause address */
+ || new_addr[5] == 2 /* 802.3ad Slow protocols */
+ || new_addr[5] == 3) /* 802.1X PAE address */
+ return -EINVAL;
+
+ spin_lock_bh(&br->lock);
+ for (i = 0; i < 6; i++)
+ br->group_addr[i] = new_addr[i];
+ spin_unlock_bh(&br->lock);
+#else
+ printk("xxx attempt to store_group_addr()\n");
+#endif
+ return len;
+}
+
+static BRC_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
+ show_group_addr, store_group_addr);
+
+static struct attribute *bridge_attrs[] = {
+ &class_device_attr_forward_delay.attr,
+ &class_device_attr_hello_time.attr,
+ &class_device_attr_max_age.attr,
+ &class_device_attr_ageing_time.attr,
+ &class_device_attr_stp_state.attr,
+ &class_device_attr_priority.attr,
+ &class_device_attr_bridge_id.attr,
+ &class_device_attr_root_id.attr,
+ &class_device_attr_root_path_cost.attr,
+ &class_device_attr_root_port.attr,
+ &class_device_attr_topology_change.attr,
+ &class_device_attr_topology_change_detected.attr,
+ &class_device_attr_hello_timer.attr,
+ &class_device_attr_tcn_timer.attr,
+ &class_device_attr_topology_change_timer.attr,
+ &class_device_attr_gc_timer.attr,
+ &class_device_attr_group_addr.attr,
+ NULL
+};
+
+static struct attribute_group bridge_group = {
+ .name = SYSFS_BRIDGE_ATTR,
+ .attrs = bridge_attrs,
+};
+
+/*
+ * Add entries in sysfs onto the existing network class device
+ * for the bridge.
+ * Adds a attribute group "bridge" containing tuning parameters.
+ * Sub directory to hold links to interfaces.
+ *
+ * Note: the ifobj exists only to be a subdirectory
+ * to hold links. The ifobj exists in the same data structure
+ * as its parent the bridge so reference counting works.
+ */
+int brc_sysfs_add_dp(struct datapath *dp)
+{
+ struct net_device *dev = dp->netdev;
+ struct kobject *kobj = to_kobj(dev);
+ int err;
+
+ err = sysfs_create_group(kobj, &bridge_group);
+ if (err) {
+ pr_info("%s: can't create group %s/%s\n",
+ __func__, dev->name, bridge_group.name);
+ goto out1;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ kobject_set_name(&dp->ifobj, SYSFS_BRIDGE_PORT_SUBDIR);
+ dp->ifobj.ktype = NULL;
+ dp->ifobj.kset = NULL;
+ dp->ifobj.parent = kobj;
+
+ err = kobject_register(&dp->ifobj);
+ if (err) {
+ pr_info("%s: can't add kobject (directory) %s/%s\n",
+ __FUNCTION__, dev->name, dp->ifobj.name);
+ goto out2;
+ }
+#else
+ br->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, kobj);
+ if (!br->ifobj) {
+ pr_info("%s: can't add kobject (directory) %s/%s\n",
+ __func__, dev->name, SYSFS_BRIDGE_PORT_SUBDIR);
+ goto out2;
+ }
+#endif
+ return 0;
+
+ out2:
+ sysfs_remove_group(to_kobj(dev), &bridge_group);
+ out1:
+ return err;
+}
+
+int brc_sysfs_del_dp(struct datapath *dp)
+{
+ struct net_device *dev = dp->netdev;
+ struct kobject *kobj = to_kobj(dev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ kobject_unregister(&dp->ifobj);
+#else
+ kobject_put(dp->ifobj);
+#endif
+ sysfs_remove_group(kobj, &bridge_group);
+
+ return 0;
+}
+#endif /* Only support 2.6.18 */
--- /dev/null
+#include <linux/version.h>
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
+
+/*
+ * Sysfs attributes of bridge ports for OpenFlow
+ *
+ * This has been shamelessly copied from the kernel sources.
+ */
+
+#include <linux/capability.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+
+#include "brc_sysfs.h"
+#include "datapath.h"
+
+struct brport_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct net_bridge_port *, char *);
+ ssize_t (*store)(struct net_bridge_port *, unsigned long);
+};
+
+#define BRPORT_ATTR(_name,_mode,_show,_store) \
+struct brport_attribute brport_attr_##_name = { \
+ .attr = {.name = __stringify(_name), \
+ .mode = _mode, \
+ .owner = THIS_MODULE, }, \
+ .show = _show, \
+ .store = _store, \
+};
+
+static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->path_cost);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v)
+{
+#if 0
+ br_stp_set_path_cost(p, v);
+#endif
+ return 0;
+}
+static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR,
+ show_path_cost, store_path_cost);
+
+static ssize_t show_priority(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->priority);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static ssize_t store_priority(struct net_bridge_port *p, unsigned long v)
+{
+#if 0
+ if (v >= (1<<(16-BR_PORT_BITS)))
+ return -ERANGE;
+ br_stp_set_port_priority(p, v);
+#endif
+ return 0;
+}
+static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR,
+ show_priority, store_priority);
+
+static ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return br_show_bridge_id(buf, &p->designated_root);
+#else
+ return sprintf(buf, "0000.010203040506\n");
+#endif
+}
+static BRPORT_ATTR(designated_root, S_IRUGO, show_designated_root, NULL);
+
+static ssize_t show_designated_bridge(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return br_show_bridge_id(buf, &p->designated_bridge);
+#else
+ return sprintf(buf, "0000.060504030201\n");
+#endif
+}
+static BRPORT_ATTR(designated_bridge, S_IRUGO, show_designated_bridge, NULL);
+
+static ssize_t show_designated_port(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->designated_port);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(designated_port, S_IRUGO, show_designated_port, NULL);
+
+static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->designated_cost);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(designated_cost, S_IRUGO, show_designated_cost, NULL);
+
+static ssize_t show_port_id(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "0x%x\n", p->port_id);
+#else
+ return sprintf(buf, "0x%x\n", 0);
+#endif
+}
+static BRPORT_ATTR(port_id, S_IRUGO, show_port_id, NULL);
+
+static ssize_t show_port_no(struct net_bridge_port *p, char *buf)
+{
+ return sprintf(buf, "0x%x\n", p->port_no);
+}
+
+static BRPORT_ATTR(port_no, S_IRUGO, show_port_no, NULL);
+
+static ssize_t show_change_ack(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->topology_change_ack);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(change_ack, S_IRUGO, show_change_ack, NULL);
+
+static ssize_t show_config_pending(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->config_pending);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(config_pending, S_IRUGO, show_config_pending, NULL);
+
+static ssize_t show_port_state(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->state);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(state, S_IRUGO, show_port_state, NULL);
+
+static ssize_t show_message_age_timer(struct net_bridge_port *p,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(message_age_timer, S_IRUGO, show_message_age_timer, NULL);
+
+static ssize_t show_forward_delay_timer(struct net_bridge_port *p,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(forward_delay_timer, S_IRUGO, show_forward_delay_timer, NULL);
+
+static ssize_t show_hold_timer(struct net_bridge_port *p,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
+
+static struct brport_attribute *brport_attrs[] = {
+ &brport_attr_path_cost,
+ &brport_attr_priority,
+ &brport_attr_port_id,
+ &brport_attr_port_no,
+ &brport_attr_designated_root,
+ &brport_attr_designated_bridge,
+ &brport_attr_designated_port,
+ &brport_attr_designated_cost,
+ &brport_attr_state,
+ &brport_attr_change_ack,
+ &brport_attr_config_pending,
+ &brport_attr_message_age_timer,
+ &brport_attr_forward_delay_timer,
+ &brport_attr_hold_timer,
+ NULL
+};
+
+#define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr)
+#define to_brport(obj) container_of(obj, struct net_bridge_port, kobj)
+
+static ssize_t brport_show(struct kobject * kobj,
+ struct attribute * attr, char * buf)
+{
+ struct brport_attribute * brport_attr = to_brport_attr(attr);
+ struct net_bridge_port * p = to_brport(kobj);
+
+ return brport_attr->show(p, buf);
+}
+
+static ssize_t brport_store(struct kobject * kobj,
+ struct attribute * attr,
+ const char * buf, size_t count)
+{
+#if 0
+ struct brport_attribute * brport_attr = to_brport_attr(attr);
+ struct net_bridge_port * p = to_brport(kobj);
+ char *endp;
+ unsigned long val;
+#endif
+ ssize_t ret = -EINVAL;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+#if 0
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp != buf) {
+ rtnl_lock();
+ if (p->dev && p->br && brport_attr->store) {
+ spin_lock_bh(&p->br->lock);
+ ret = brport_attr->store(p, val);
+ spin_unlock_bh(&p->br->lock);
+ if (ret == 0)
+ ret = count;
+ }
+ rtnl_unlock();
+ }
+#else
+ printk("xxx writing port parms not supported yet!\n");
+#endif
+ return ret;
+}
+
+struct sysfs_ops brport_sysfs_ops = {
+ .show = brport_show,
+ .store = brport_store,
+};
+
+static void release_nbp(struct kobject *kobj)
+{
+ struct net_bridge_port *p
+ = container_of(kobj, struct net_bridge_port, kobj);
+ kfree(p);
+}
+
+struct kobj_type brport_ktype = {
+ .sysfs_ops = &brport_sysfs_ops,
+ .release = release_nbp
+};
+
+/*
+ * Add sysfs entries to ethernet device added to a bridge.
+ * Creates a brport subdirectory with bridge attributes.
+ * Puts symlink in bridge's brport subdirectory
+ */
+int brc_sysfs_add_if(struct net_bridge_port *p)
+{
+ struct datapath *dp = p->dp;
+ struct brport_attribute **a;
+ int err;
+
+ kobject_init(&p->kobj);
+ kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR);
+ p->kobj.ktype = &brport_ktype;
+ p->kobj.kset = NULL;
+ p->kobj.parent = &(p->dev->class_dev.kobj);
+
+ err = kobject_add(&p->kobj);
+ if (err)
+ goto err_put;
+
+ err = sysfs_create_link(&p->kobj, &dp->netdev->class_dev.kobj,
+ SYSFS_BRIDGE_PORT_LINK);
+ if (err)
+ goto err_del;
+
+ for (a = brport_attrs; *a; ++a) {
+ err = sysfs_create_file(&p->kobj, &((*a)->attr));
+ if (err)
+ goto err_del;
+ }
+
+ err = sysfs_create_link(&dp->ifobj, &p->kobj, p->dev->name);
+ if (err)
+ goto err_del;
+
+ kobject_uevent(&p->kobj, KOBJ_ADD);
+
+ return err;
+
+err_del:
+ kobject_del(&p->kobj);
+err_put:
+ kobject_put(&p->kobj);
+ return err;
+}
+
+int brc_sysfs_del_if(struct net_bridge_port *p)
+{
+ struct net_device *dev = p->dev;
+
+ kobject_uevent(&p->kobj, KOBJ_REMOVE);
+ kobject_del(&p->kobj);
+
+ dev_put(dev);
+
+ kobject_put(&p->kobj);
+
+ return 0;
+}
+#endif /* Only support 2.6.18 */
#include "compat.h"
#include "openflow/openflow-netlink.h"
#include "openflow/brcompat-netlink.h"
+#include "brc_sysfs.h"
#include "datapath.h"
#include "dp_dev.h"
int brc_send_port_add_del(struct net_device *dev, struct net_device *port,
int add);
-/* Called with RTNL lock. */
+
static int
get_dp_ifindices(int *indices, int num)
{
int i, index = 0;
- mutex_lock(&dp_mutex);
+ rcu_read_lock();
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);
+ rcu_read_unlock();
return index;
}
-/* Called with RTNL lock. */
static void
get_port_ifindices(struct datapath *dp, int *ifindices, int num)
{
rcu_read_unlock();
}
-/* Legacy deviceless bridge ioctl's. The primary reason to do this
- * is to provide support for 'brctl show' without having to implement
- * the bridge's sysfs system. Also, 'brctl' will fall back on the
- * legacy device ioctl's if an attempt to add or delete a bridge results in
- * an error. By re-attempting the failed add or delete call, the error
- * messages from 'brctl' are meaningful. */
+/* Legacy deviceless bridge ioctl's. Called with br_ioctl_mutex. */
static int
old_deviceless(void __user *uarg)
{
return -EFAULT;
dp_name[IFNAMSIZ-1] = 0;
- if (args[0] == BRCTL_ADD_BRIDGE)
+ if (args[0] == BRCTL_ADD_BRIDGE)
return brc_send_dp_add_del(dp_name, 1);
return brc_send_dp_add_del(dp_name, 0);
{
switch (cmd) {
case SIOCGIFBR:
- case SIOCSIFBR:
+ case SIOCSIFBR:
return old_deviceless(uarg);
case SIOCBRADDBR:
return -EFAULT;
dp_name[IFNAMSIZ-1] = 0;
- if (cmd == SIOCBRADDBR)
- return brc_send_dp_add_del(dp_name, 1);
-
- return brc_send_dp_add_del(dp_name, 0);
+ return brc_send_dp_add_del(dp_name, cmd == SIOCBRADDBR);
}
}
return -EOPNOTSUPP;
}
-/* Legacy ioctl's through SIOCDEVPRIVATE. The primary reason to do this
- * is to provide support for 'brctl show' without having to implement
- * the bridge's sysfs system. Also, 'brctl' will fall back on the
- * legacy device ioctl's if an attempt to modify an interface results in
- * an error. By re-attempting the failed modify call, the error messages
- * from 'brctl' are meaningful. */
+/* Legacy ioctl's through SIOCDEVPRIVATE. Called with rtnl_lock. */
static int
old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
port = dev_get_by_index(&init_net, args[1]);
if (!port)
return -EINVAL;
+
err = brc_send_port_add_del(dev, port, args[0] == BRCTL_ADD_IF);
dev_put(port);
return err;
port = dev_get_by_index(&init_net, rq->ifr_ifindex);
if (!port)
return -EINVAL;
+
err = brc_send_port_add_del(dev, port, cmd == SIOCBRADDIF);
dev_put(port);
break;
return -EINVAL;
}
+int brc_add_dp(struct datapath *dp)
+{
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+#if CONFIG_SYSFS
+ brc_sysfs_add_dp(dp);
+#endif
+
+ return 0;
+}
+
+int brc_del_dp(struct datapath *dp)
+{
+#if CONFIG_SYSFS
+ brc_sysfs_del_dp(dp);
+#endif
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
static int
__init brc_init(void)
{
+ int i;
int err;
printk("OpenFlow Bridge Compatibility, built "__DATE__" "__TIME__"\n");
+ rcu_read_lock();
+ for (i=0; i<DP_MAX; i++) {
+ if (dp_get_by_idx(i)) {
+ rcu_read_unlock();
+ printk(KERN_EMERG "brcompat: no datapaths may exist!\n");
+ return -EEXIST;
+ }
+ }
+ rcu_read_unlock();
+
/* Set the bridge ioctl handler */
brioctl_set(brc_ioctl_deviceless_stub);
/* Set the OpenFlow device ioctl handler */
dp_ioctl_hook = brc_dev_ioctl;
+ /* Register hooks for datapath adds and deletes */
+ dp_add_dp_hook = brc_add_dp;
+ dp_del_dp_hook = brc_del_dp;
+
+ /* Register hooks for interface adds and deletes */
+#if CONFIG_SYSFS
+ dp_add_if_hook = brc_sysfs_add_if;
+ dp_del_if_hook = brc_sysfs_del_if;
+#endif
+
/* Register generic netlink family to communicate changes to
* userspace. */
err = genl_register_family(&brc_genl_family);
if (err)
- return err;
+ goto error;
err = genl_register_ops(&brc_genl_family, &brc_genl_ops_query_dp);
if (err != 0)
err_unregister:
genl_unregister_family(&brc_genl_family);
+error:
+ printk(KERN_EMERG "brcompat: failed to install!");
return err;
}
static void
brc_cleanup(void)
{
- /* Check refcount for datapaths so hook doesn't disappear? */
+ /* Unregister hooks for datapath adds and deletes */
+ dp_add_dp_hook = NULL;
+ dp_del_dp_hook = NULL;
+
+ /* Unregister hooks for interface adds and deletes */
+ dp_add_if_hook = NULL;
+ dp_del_if_hook = NULL;
+
+ /* Unregister ioctl hooks */
dp_ioctl_hook = NULL;
-
brioctl_set(NULL);
+
genl_unregister_family(&brc_genl_family);
}
module_exit(brc_cleanup);
MODULE_DESCRIPTION("OpenFlow bridge compatibility");
-MODULE_AUTHOR("Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University");
+MODULE_AUTHOR("Copyright (c) 2009 The Board of Trustees of The Leland Stanford Junior University");
MODULE_LICENSE("GPL");
int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
EXPORT_SYMBOL(dp_ioctl_hook);
+int (*dp_add_dp_hook)(struct datapath *dp);
+EXPORT_SYMBOL(dp_add_dp_hook);
+
+int (*dp_del_dp_hook)(struct datapath *dp);
+EXPORT_SYMBOL(dp_del_dp_hook);
+
+int (*dp_add_if_hook)(struct net_bridge_port *p);
+EXPORT_SYMBOL(dp_add_if_hook);
+
+int (*dp_del_if_hook)(struct net_bridge_port *p);
+EXPORT_SYMBOL(dp_del_if_hook);
+
/* Number of milliseconds between runs of the maintenance thread. */
#define MAINT_SLEEP_MSECS 1000
mutex_unlock(&dp_mutex);
rtnl_unlock();
+ if (dp_add_dp_hook)
+ dp_add_dp_hook(dp);
+
return 0;
err_destroy_local_port:
init_port_status(p);
+ if (dp_add_if_hook)
+ dp_add_if_hook(p);
+
/* Notify the ctlpath that this port has been added */
dp_send_port_status(p, OFPPR_ADD);
unsigned long flags;
#endif
+#if CONFIG_SYSFS
+ if ((p->port_no != OFPP_LOCAL) && dp_del_if_hook)
+ sysfs_remove_link(&p->dp->ifobj, p->dev->name);
+#endif
+
/* First drop references to device. */
dev_set_promiscuity(p->dev, -1);
list_del_rcu(&p->node);
/* Notify the ctlpath that this port no longer exists */
dp_send_port_status(p, OFPPR_DELETE);
- dev_put(p->dev);
- kfree(p);
+ if ((p->port_no != OFPP_LOCAL) && dp_del_if_hook) {
+ dp_del_if_hook(p);
+ } else {
+ dev_put(p->dev);
+ kfree(p);
+ }
return 0;
}
/* Drop references to DP. */
list_for_each_entry_safe (p, n, &dp->port_list, node)
dp_del_switch_port(p);
+
+ if (dp_del_dp_hook)
+ dp_del_dp_hook(dp);
+
rcu_assign_pointer(dps[dp->dp_idx], NULL);
/* Kill off local_port dev references from buffered packets that have
.dumpit = NULL,
};
+/* Must be called with rcu_read_lock or dp_mutex. */
struct datapath *dp_get_by_idx(int dp_idx)
{
if (dp_idx < 0 || dp_idx >= DP_MAX)
}
EXPORT_SYMBOL(dp_get_by_idx);
+/* Must be called with rcu_read_lock or dp_mutex. */
struct datapath *dp_get_by_name(const char *dp_name)
{
int i;
return NULL;
}
+/* Must be called with rcu_read_lock or dp_mutex. */
static struct datapath *
lookup_dp(struct genl_info *info)
{
if (err)
goto error_unreg_notifier;
- dp_ioctl_hook = NULL;
+ dp_ioctl_hook = NULL;
+ dp_add_dp_hook = NULL;
+ dp_del_dp_hook = NULL;
+ dp_add_if_hook = NULL;
+ dp_del_if_hook = NULL;
/* Check if better descriptions of the switch are available than the
* defaults. */
/* Flag controlling whether Flow End messages are generated. */
uint8_t send_flow_end;
+ struct kobject ifobj;
+
/* Switch ports. */
struct net_bridge_port *ports[DP_MAX_PORTS];
struct net_bridge_port *local_port; /* OFPP_LOCAL port. */
spinlock_t lock;
struct datapath *dp;
struct net_device *dev;
+ struct kobject kobj;
struct snat_conf *snat; /* Only set if SNAT is configured for this port. */
struct list_head node; /* Element in datapath.ports. */
};
extern struct mutex dp_mutex;
extern struct notifier_block dp_device_notifier;
extern int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
+extern int (*dp_add_dp_hook)(struct datapath *dp);
+extern int (*dp_del_dp_hook)(struct datapath *dp);
+extern int (*dp_add_if_hook)(struct net_bridge_port *p);
+extern int (*dp_del_if_hook)(struct net_bridge_port *p);
int dp_del_switch_port(struct net_bridge_port *);
int dp_xmit_skb(struct sk_buff *skb);
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/workqueue.h>
return netdev_priv(netdev);
}
+struct datapath *dp_dev_get_dp(struct net_device *netdev)
+{
+ return dp_dev_priv(netdev)->dp;
+}
+EXPORT_SYMBOL(dp_dev_get_dp);
+
static struct net_device_stats *dp_dev_get_stats(struct net_device *netdev)
{
struct dp_dev *dp_dev = dp_dev_priv(netdev);
memcpy(netdev->dev_addr, mac, ETH_ALEN);
}
+static void dp_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, "openflow");
+ sprintf(info->version, "0x%d", OFP_VERSION);
+ strcpy(info->fw_version, "N/A");
+ strcpy(info->bus_info, "N/A");
+}
+
+static struct ethtool_ops dp_ethtool_ops = {
+ .get_drvinfo = dp_getinfo,
+ .get_link = ethtool_op_get_link,
+ .get_sg = ethtool_op_get_sg,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .get_tso = ethtool_op_get_tso,
+};
+
static void
do_setup(struct net_device *netdev)
{
netdev->get_stats = dp_dev_get_stats;
netdev->hard_start_xmit = dp_dev_xmit;
netdev->open = dp_dev_open;
+ SET_ETHTOOL_OPS(netdev, &dp_ethtool_ops);
netdev->stop = dp_dev_stop;
netdev->tx_queue_len = 100;
netdev->set_mac_address = dp_dev_mac_addr;
void dp_dev_destroy(struct datapath *);
int dp_dev_recv(struct net_device *, struct sk_buff *);
int is_dp_dev(struct net_device *);
+struct datapath *dp_dev_get_dp(struct net_device *);
#endif /* dp_dev.h */
/Makefile
/Makefile.main
/brcompat.c
+/brc_sysfs_dp.c
+/brc_sysfs_if.c
/chain.c
/crc32.c
/crc_t.c
/flow.c
/forward.c
/forward_t.c
-/genetlink.c
+/genetlink-brcompat.c
+/genetlink-openflow.c
/hwtable_dummy.c
/nx_act.c
/nx_act_snat.c
brcompat_sources = \
linux-2.6/compat-2.6/genetlink-brcompat.c \
- brcompat.c
+ brcompat.c \
+ brc_sysfs_dp.c \
+ brc_sysfs_if.c
-brcompat_headers =
+brcompat_headers = \
+ brc_sysfs.h
--- /dev/null
+#ifndef __LINUX_NETDEVICE_WRAPPER_H
+#define __LINUX_NETDEVICE_WRAPPER_H 1
+
+#include_next <linux/netdevice.h>
+
+#ifndef to_net_dev
+#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+#endif
+
+#endif
xasprintf("bridge.%s.port = %s\n",
old_br.names[i], old_keys.names[j]));
}
-
- cfg_get_all_keys(&old_keys, "bridge.%s.enabled", old_br.names[i]);
- for (j = 0; j < old_keys.n; j++) {
- svec_add_nocopy(&new_cfg,
- xasprintf("bridge.%s.enabled = true\n",
- old_br.names[i]));
- }
}
svec_destroy(&old_br);
svec_destroy(&old_keys);
- /* If the last interface was removed from the datapath, we'll add
- * this dummy entry so that it's still created when vswitchd is
- * restarted. */
- if ((act == BMC_ADD_DP) || (n_ports == 0)) {
+ /* Always add the datapath's own device. */
+ if (act == BMC_ADD_DP) {
svec_add_nocopy(&new_cfg,
- xasprintf("bridge.%s.enabled = true\n", dp_name));
+ xasprintf("bridge.%s.port = %s\n", dp_name, dp_name));
}
if (act == BMC_ADD_PORT) {