From 59ba307325ee2f9c6e1fe80f4c8a2fac7f6b32f2 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 21 May 2008 11:36:43 -0700 Subject: [PATCH] Make the OFPP_LOCAL port work in the kernel OpenFlow implementation. --- datapath/datapath.c | 167 +++++++++--------- datapath/datapath.h | 6 +- datapath/dp_dev.c | 134 ++++++++++---- datapath/dp_dev.h | 9 + .../compat-2.4/include/linux/netdevice.h | 8 + include/openflow.h | 2 +- 6 files changed, 195 insertions(+), 131 deletions(-) create mode 100644 datapath/dp_dev.h diff --git a/datapath/datapath.c b/datapath/datapath.c index f2d8a67d..4b706499 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -33,6 +33,7 @@ #include "datapath.h" #include "table.h" #include "chain.h" +#include "dp_dev.h" #include "forward.h" #include "flow.h" #include "datapath_t.h" @@ -60,8 +61,6 @@ struct net_bridge_port { static struct genl_family dp_genl_family; static struct genl_multicast_group mc_group; -int dp_dev_setup(struct net_device *dev); - /* It's hard to imagine wanting more than one datapath, but... */ #define DP_MAX 32 @@ -78,6 +77,9 @@ static DEFINE_MUTEX(dp_mutex); static int dp_maint_func(void *data); static int send_port_status(struct net_bridge_port *p, uint8_t status); static int dp_genl_openflow_done(struct netlink_callback *); +static struct net_bridge_port *new_nbp(struct datapath *, + struct net_device *, int port_no); +static int del_switch_port(struct net_bridge_port *); /* nla_shrink - reduce amount of space reserved by nla_reserve * @skb: socket buffer from which to recover room @@ -270,35 +272,42 @@ static int new_dp(int dp_idx) if (dp == NULL) goto err_unlock; + /* Setup our "of" device */ + err = dp_dev_setup(dp); + if (err) + goto err_free_dp; + dp->dp_idx = dp_idx; dp->id = gen_datapath_id(dp_idx); dp->chain = chain_create(dp); if (dp->chain == NULL) - goto err_free_dp; + goto err_destroy_dp_dev; INIT_LIST_HEAD(&dp->port_list); -#if 0 - /* Setup our "of" device */ - dp->dev.priv = dp; - rtnl_lock(); - err = dp_dev_setup(&dp->dev); - rtnl_unlock(); - if (err != 0) - printk("datapath: problem setting up 'of' device\n"); -#endif + dp->local_port = new_nbp(dp, dp->netdev, OFPP_LOCAL); + if (IS_ERR(dp->local_port)) { + err = PTR_ERR(dp->local_port); + goto err_destroy_local_port; + } dp->flags = 0; dp->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; dp->dp_task = kthread_run(dp_maint_func, dp, "dp%d", dp_idx); if (IS_ERR(dp->dp_task)) - goto err_free_dp; + goto err_destroy_chain; rcu_assign_pointer(dps[dp_idx], dp); mutex_unlock(&dp_mutex); return 0; +err_destroy_local_port: + del_switch_port(dp->local_port); +err_destroy_chain: + chain_destroy(dp->chain); +err_destroy_dp_dev: + dp_dev_destroy(dp); err_free_dp: kfree(dp); err_unlock: @@ -318,23 +327,29 @@ static int find_portno(struct datapath *dp) } static struct net_bridge_port *new_nbp(struct datapath *dp, - struct net_device *dev) + struct net_device *dev, int port_no) { struct net_bridge_port *p; - int port_no; - port_no = find_portno(dp); - if (port_no < 0) - return ERR_PTR(port_no); + if (dev->br_port != NULL) + return ERR_PTR(-EBUSY); p = kzalloc(sizeof(*p), GFP_KERNEL); if (p == NULL) return ERR_PTR(-ENOMEM); - p->dp = dp; + rtnl_lock(); + dev_set_promiscuity(dev, 1); + rtnl_unlock(); dev_hold(dev); + p->dp = dp; p->dev = dev; p->port_no = port_no; + if (port_no != OFPP_LOCAL) + rcu_assign_pointer(dev->br_port, p); + if (port_no < OFPP_MAX) + rcu_assign_pointer(dp->ports[port_no], p); + list_add_rcu(&p->node, &dp->port_list); return p; } @@ -343,26 +358,20 @@ static struct net_bridge_port *new_nbp(struct datapath *dp, int add_switch_port(struct datapath *dp, struct net_device *dev) { struct net_bridge_port *p; + int port_no; - if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER) + if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER + || is_dp_dev(dev)) return -EINVAL; - if (dev->br_port != NULL) - return -EBUSY; + port_no = find_portno(dp); + if (port_no < 0) + return port_no; - p = new_nbp(dp, dev); + p = new_nbp(dp, dev, port_no); if (IS_ERR(p)) return PTR_ERR(p); - dev_hold(dev); - rcu_assign_pointer(dev->br_port, p); - rtnl_lock(); - dev_set_promiscuity(dev, 1); - rtnl_unlock(); - - rcu_assign_pointer(dp->ports[p->port_no], p); - list_add_rcu(&p->node, &dp->port_list); - /* Notify the ctlpath that this port has been added */ send_port_status(p, OFPPR_ADD); @@ -396,19 +405,13 @@ static int del_switch_port(struct net_bridge_port *p) /* Called with dp_mutex. */ static void del_dp(struct datapath *dp) { - struct net_bridge_port *p, *n; - -#if 0 - /* Unregister the "of" device of this dp */ - rtnl_lock(); - unregister_netdevice(&dp->dev); - rtnl_unlock(); -#endif + struct net_bridge_port *p; + dp_dev_destroy(dp); kthread_stop(dp->dp_task); /* Drop references to DP. */ - list_for_each_entry_safe (p, n, &dp->port_list, node) + list_for_each_entry_rcu (p, &dp->port_list, node) del_switch_port(p); rcu_assign_pointer(dps[dp->dp_idx], NULL); @@ -479,21 +482,6 @@ static void dp_frame_hook(struct sk_buff *skb) /* Forwarding output path. * Based on net/bridge/br_forward.c. */ -/* Don't forward packets to originating port. If we're flooding, - * then don't send out ports with flooding disabled. - */ -static inline int should_deliver(const struct net_bridge_port *p, - const struct sk_buff *skb, int flood) -{ - if (skb->dev == p->dev) - return 0; - - if (flood && (p->flags & BRIDGE_PORT_NO_FLOOD)) - return 0; - - return 1; -} - static inline unsigned packet_length(const struct sk_buff *skb) { int length = skb->len - ETH_HLEN; @@ -508,12 +496,12 @@ static inline unsigned packet_length(const struct sk_buff *skb) static int output_all(struct datapath *dp, struct sk_buff *skb, int flood) { + u32 disable = flood ? BRIDGE_PORT_NO_FLOOD : 0; struct net_bridge_port *p; - int prev_port; + int prev_port = -1; - prev_port = -1; list_for_each_entry_rcu (p, &dp->port_list, node) { - if (!should_deliver(p, skb, flood)) + if (skb->dev == p->dev || p->flags & disable) continue; if (prev_port != -1) { struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC); @@ -538,8 +526,11 @@ output_all(struct datapath *dp, struct sk_buff *skb, int flood) int dp_set_origin(struct datapath *dp, uint16_t in_port, struct sk_buff *skb) { - if (in_port < OFPP_MAX && dp->ports[in_port]) { - skb->dev = dp->ports[in_port]->dev; + struct net_bridge_port *p = (in_port < OFPP_MAX ? dp->ports[in_port] + : in_port == OFPP_LOCAL ? dp->local_port + : NULL); + if (p) { + skb->dev = p->dev; return 0; } return -ENOENT; @@ -549,9 +540,6 @@ int dp_set_origin(struct datapath *dp, uint16_t in_port, */ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port) { - struct net_bridge_port *p; - int len = skb->len; - BUG_ON(!skb); if (out_port == OFPP_FLOOD) return output_all(dp, skb, 1); @@ -561,10 +549,11 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port) return dp_output_control(dp, skb, fwd_save_skb(skb), 0, OFPR_ACTION); else if (out_port == OFPP_TABLE) { + struct net_bridge_port *p = skb->dev->br_port; struct sw_flow_key key; struct sw_flow *flow; - flow_extract(skb, skb->dev->br_port->port_no, &key); + flow_extract(skb, p ? p->port_no : OFPP_LOCAL, &key); flow = chain_lookup(dp->chain, &key); if (likely(flow != NULL)) { flow_used(flow, skb); @@ -572,25 +561,27 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port) return 0; } return -ESRCH; - } else if (out_port >= OFPP_MAX) - goto bad_port; + } else if (out_port == OFPP_LOCAL) { + struct net_device *dev = dp->netdev; + return dev ? dp_dev_recv(dev, skb) : -ESRCH; + } else if (out_port >= 0 && out_port < OFPP_MAX) { + struct net_bridge_port *p = dp->ports[out_port]; + int len = skb->len; + if (p == NULL) + goto bad_port; + skb->dev = p->dev; + if (packet_length(skb) > skb->dev->mtu) { + printk("dropped over-mtu packet: %d > %d\n", + packet_length(skb), skb->dev->mtu); + kfree_skb(skb); + return -E2BIG; + } - p = dp->ports[out_port]; - if (p == NULL) - goto bad_port; + dev_queue_xmit(skb); - skb->dev = p->dev; - if (packet_length(skb) > skb->dev->mtu) { - printk("dropped over-mtu packet: %d > %d\n", - packet_length(skb), skb->dev->mtu); - kfree_skb(skb); - return -E2BIG; + return len; } - dev_queue_xmit(skb); - - return len; - bad_port: kfree_skb(skb); if (net_ratelimit()) @@ -612,6 +603,7 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, * forward the whole packet? */ struct sk_buff *f_skb; struct ofp_packet_in *opi; + struct net_bridge_port *p; size_t fwd_len, opi_len; int err; @@ -627,7 +619,8 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, } opi->buffer_id = htonl(buffer_id); opi->total_len = htons(skb->len); - opi->in_port = htons(skb->dev->br_port->port_no); + p = skb->dev->br_port; + opi->in_port = htons(p ? p->port_no : OFPP_LOCAL); opi->reason = reason; opi->pad = 0; memcpy(opi->data, skb_mac_header(skb), fwd_len); @@ -744,16 +737,14 @@ dp_send_config_reply(struct datapath *dp, const struct sender *sender) int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp) { - struct net_bridge_port *p; - - p = dp->ports[htons(opp->port_no)]; - + int port_no = ntohs(opp->port_no); + struct net_bridge_port *p = (port_no < OFPP_MAX ? dp->ports[port_no] + : port_no == OFPP_LOCAL ? dp->local_port + : NULL); /* Make sure the port id hasn't changed since this was sent */ - if (!p || memcmp(opp->hw_addr, p->dev->dev_addr, ETH_ALEN) != 0) + if (!p || memcmp(opp->hw_addr, p->dev->dev_addr, ETH_ALEN)) return -1; - p->flags = htonl(opp->flags); - return 0; } diff --git a/datapath/datapath.h b/datapath/datapath.h index 1a884f83..b342cbb5 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -45,8 +45,7 @@ struct datapath { struct task_struct *dp_task; /* Kernel thread for maintenance. */ /* Data related to the "of" device of this datapath */ - struct net_device dev; - struct net_device_stats stats; + struct net_device *netdev; /* Configuration set from controller */ uint16_t flags; @@ -54,7 +53,8 @@ struct datapath { /* Switch ports. */ struct net_bridge_port *ports[OFPP_MAX]; - struct list_head port_list; /* List of ports, for flooding. */ + struct net_bridge_port *local_port; /* OFPP_LOCAL port. */ + struct list_head port_list; /* All ports, including local_port. */ }; /* Information necessary to reply to the sender of an OpenFlow message. */ diff --git a/datapath/dp_dev.c b/datapath/dp_dev.c index e2ebf5a4..4047e926 100644 --- a/datapath/dp_dev.c +++ b/datapath/dp_dev.c @@ -6,73 +6,129 @@ #include "datapath.h" #include "forward.h" -static int dp_dev_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +struct dp_dev { + struct net_device_stats stats; + struct datapath *dp; +}; + +static struct dp_dev *dp_dev_priv(struct net_device *netdev) { - printk("xxx_do_ioctl called\n"); - return 0; + return netdev_priv(netdev); } -static struct net_device_stats *dp_dev_get_stats(struct net_device *dev) +static struct net_device_stats *dp_dev_get_stats(struct net_device *netdev) { - struct datapath *dp = netdev_priv(dev); - return &dp->stats; + struct dp_dev *dp_dev = dp_dev_priv(netdev); + return &dp_dev->stats; } -int dp_dev_xmit(struct sk_buff *skb, struct net_device *dev) +int dp_dev_recv(struct net_device *netdev, struct sk_buff *skb) { - struct datapath *dp = netdev_priv(dev); - - printk("xxx dp_dev_xmit not implemented yet!\n"); - return 0; + int len = skb->len; + struct dp_dev *dp_dev = dp_dev_priv(netdev); + skb->pkt_type = PACKET_HOST; + skb->protocol = eth_type_trans(skb, netdev); + netif_rx(skb); + netdev->last_rx = jiffies; + dp_dev->stats.rx_packets++; + dp_dev->stats.rx_bytes += len; + return len; +} - printk("xxx_xmit called send to dp_frame_hook\n"); +static int dp_dev_mac_addr(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; - rcu_read_lock(); /* xxx Only for 2.4 kernels? */ - fwd_port_input(dp->chain, skb, OFPP_LOCAL); - rcu_read_unlock(); /* xxx Only for 2.4 kernels? */ - + if (netif_running(dev)) + return -EBUSY; + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); return 0; } -static int dp_dev_open(struct net_device *dev) +static int dp_dev_xmit(struct sk_buff *skb, struct net_device *netdev) { - netif_start_queue(dev); + struct dp_dev *dp_dev = dp_dev_priv(netdev); + struct datapath *dp; + rcu_read_lock(); + dp = dp_dev->dp; + if (likely(dp != NULL)) { + dp_dev->stats.tx_packets++; + dp_dev->stats.tx_bytes += skb->len; + skb_reset_mac_header(skb); + fwd_port_input(dp->chain, skb, OFPP_LOCAL); + } else { + dp_dev->stats.tx_dropped++; + kfree_skb(skb); + } + rcu_read_unlock(); return 0; } -static void dp_dev_set_multicast_list(struct net_device *dev) +static int dp_dev_open(struct net_device *netdev) { - printk("xxx_set_multi called\n"); + netif_start_queue(netdev); + return 0; } -static int dp_dev_stop(struct net_device *dev) +static int dp_dev_stop(struct net_device *netdev) { - netif_stop_queue(dev); + netif_stop_queue(netdev); return 0; } -int dp_dev_setup(struct net_device *dev) +static void +do_setup(struct net_device *netdev) { + ether_setup(netdev); + + netdev->get_stats = dp_dev_get_stats; + netdev->hard_start_xmit = dp_dev_xmit; + netdev->open = dp_dev_open; + netdev->stop = dp_dev_stop; + netdev->tx_queue_len = 0; + netdev->set_mac_address = dp_dev_mac_addr; + + netdev->flags = IFF_BROADCAST | IFF_MULTICAST; + + random_ether_addr(netdev->dev_addr); +} + + +int dp_dev_setup(struct datapath *dp) +{ + struct dp_dev *dp_dev; + struct net_device *netdev; + char of_name[8]; int err; - strncpy(dev->name, "of%d", IFNAMSIZ); - err = dev_alloc_name(dev, dev->name); - if (err < 0) - return err; + snprintf(of_name, sizeof of_name, "of%d", dp->dp_idx); + netdev = alloc_netdev(sizeof(struct dp_dev), of_name, do_setup); + if (!netdev) + return -ENOMEM; - dev->do_ioctl = dp_dev_do_ioctl; - dev->get_stats = dp_dev_get_stats; - dev->hard_start_xmit = dp_dev_xmit; - dev->open = dp_dev_open; - dev->set_multicast_list = dp_dev_set_multicast_list; - dev->stop = dp_dev_stop; - dev->tx_queue_len = 0; - dev->set_mac_address = NULL; + err = register_netdev(netdev); + if (err) { + free_netdev(netdev); + return err; + } - dev->flags = IFF_BROADCAST | IFF_NOARP | IFF_MULTICAST; + dp_dev = dp_dev_priv(netdev); + dp_dev->dp = dp; + dp->netdev = netdev; + return 0; +} - random_ether_addr(dev->dev_addr); +void dp_dev_destroy(struct datapath *dp) +{ + struct dp_dev *dp_dev = dp_dev_priv(dp->netdev); + dp_dev->dp = NULL; + synchronize_net(); + unregister_netdev(dp->netdev); +} - ether_setup(dev); - return register_netdevice(dev); +int is_dp_dev(struct net_device *netdev) +{ + return netdev->open == dp_dev_open; } diff --git a/datapath/dp_dev.h b/datapath/dp_dev.h new file mode 100644 index 00000000..2d4453b5 --- /dev/null +++ b/datapath/dp_dev.h @@ -0,0 +1,9 @@ +#ifndef DP_DEV_H +#define DP_DEV_H 1 + +int dp_dev_setup(struct datapath *); +void dp_dev_destroy(struct datapath *); +int dp_dev_recv(struct net_device *, struct sk_buff *); +int is_dp_dev(struct net_device *); + +#endif /* dp_dev.h */ diff --git a/datapath/linux-2.4/compat-2.4/include/linux/netdevice.h b/datapath/linux-2.4/compat-2.4/include/linux/netdevice.h index 6eba6513..60c81b0c 100644 --- a/datapath/linux-2.4/compat-2.4/include/linux/netdevice.h +++ b/datapath/linux-2.4/compat-2.4/include/linux/netdevice.h @@ -2,6 +2,8 @@ #define __LINUX_NETDEVICE_WRAPPER_H 1 #include_next +#include +#include /*---------------------------------------------------------------------------- * In 2.6.24, a namespace argument became required for dev_get_by_name. @@ -28,4 +30,10 @@ static inline void *netdev_priv(struct net_device *dev) } #endif +/* Synchronize with packet receive processing. */ +static inline void synchronize_net(void) +{ + synchronize_rcu(); +} + #endif diff --git a/include/openflow.h b/include/openflow.h index 2f5da5d8..6303b490 100644 --- a/include/openflow.h +++ b/include/openflow.h @@ -75,7 +75,7 @@ enum ofp_port { those disabled by STP. */ OFPP_ALL = 0xfffc, /* All physical ports except input port. */ OFPP_CONTROLLER = 0xfffd, /* Send to controller. */ - OFPP_LOCAL = 0xfffe, /* Local openflow "port". */ /* xxx Want?! */ + OFPP_LOCAL = 0xfffe, /* Local openflow "port". */ OFPP_NONE = 0xffff /* Not associated with a physical port. */ }; -- 2.30.2