X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=datapath%2Fdatapath.c;h=07fa92d0740883f996119a476b49cbf66bf75793;hb=8c1c06ba950d615713ccad28a9449a3aec3c417f;hp=3fa8cdc0af51cd9dc4a932ff728a595fba8b784d;hpb=4d0a3296522f2695a6f8b769100615ada1a54315;p=sliver-openvswitch.git diff --git a/datapath/datapath.c b/datapath/datapath.c index 3fa8cdc0a..07fa92d07 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -26,13 +26,17 @@ #include #include #include +#include #include #include #include #include +#include -#include "openflow-netlink.h" +#include "openflow/nicira-ext.h" +#include "openflow/openflow-netlink.h" #include "datapath.h" +#include "nx_act_snat.h" #include "table.h" #include "chain.h" #include "dp_dev.h" @@ -44,9 +48,9 @@ /* Strings to describe the manufacturer, hardware, and software. This data * is queriable through the switch description stats message. */ -static char mfr_desc[DESC_STR_LEN] = "Nicira Networks"; +static char mfr_desc[DESC_STR_LEN] = "Nicira Networks, Inc."; static char hw_desc[DESC_STR_LEN] = "Reference Linux Kernel Module"; -static char sw_desc[DESC_STR_LEN] = VERSION; +static char sw_desc[DESC_STR_LEN] = VERSION BUILDNR; static char serial_num[SERIAL_NUM_LEN] = "None"; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) @@ -218,37 +222,15 @@ send_openflow_skb(struct sk_buff *skb, const struct sender *sender) : genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC)); } -/* Generates a unique datapath id. It incorporates the datapath index - * and a hardware address, if available. If not, it generates a random - * one. - */ +/* Retrieves the datapath id, which is the MAC address of the "of" device. */ static -uint64_t gen_datapath_id(uint16_t dp_idx) +uint64_t get_datapath_id(struct net_device *dev) { - uint64_t id; + uint64_t id = 0; int i; - struct net_device *dev; - - /* The top 16 bits are used to identify the datapath. The lower 48 bits - * use an interface address. */ - id = (uint64_t)dp_idx << 48; - if ((dev = dev_get_by_name(&init_net, "ctl0")) - || (dev = dev_get_by_name(&init_net, "eth0"))) { - for (i=0; idev_addr[i] << (8*(ETH_ALEN-1 - i)); - } - dev_put(dev); - } else { - /* Randomly choose the lower 48 bits if we cannot find an - * address and mark the most significant bit to indicate that - * this was randomly generated. */ - uint8_t rand[ETH_ALEN]; - get_random_bytes(rand, ETH_ALEN); - id |= (uint64_t)1 << 63; - for (i=0; idev_addr[i] << (8*(ETH_ALEN-1 - i)); return id; } @@ -283,7 +265,7 @@ static int new_dp(int dp_idx) goto err_free_dp; dp->dp_idx = dp_idx; - dp->id = gen_datapath_id(dp_idx); + dp->id = get_datapath_id(dp->netdev); dp->chain = chain_create(dp); if (dp->chain == NULL) goto err_destroy_dp_dev; @@ -323,7 +305,7 @@ err_unlock: static int find_portno(struct datapath *dp) { int i; - for (i = 0; i < OFPP_MAX; i++) + for (i = 0; i < DP_MAX_PORTS; i++) if (dp->ports[i] == NULL) return i; return -EXFULL; @@ -352,7 +334,7 @@ static struct net_bridge_port *new_nbp(struct datapath *dp, INIT_WORK(&p->port_task, NULL); if (port_no != OFPP_LOCAL) rcu_assign_pointer(dev->br_port, p); - if (port_no < OFPP_MAX) + if (port_no < DP_MAX_PORTS) rcu_assign_pointer(dp->ports[port_no], p); list_add_rcu(&p->node, &dp->port_list); @@ -387,6 +369,10 @@ int add_switch_port(struct datapath *dp, struct net_device *dev) /* Delete 'p' from switch. */ int dp_del_switch_port(struct net_bridge_port *p) { +#ifdef SUPPORT_SNAT + unsigned long flags; +#endif + /* First drop references to device. */ cancel_work_sync(&p->port_task); rtnl_lock(); @@ -400,6 +386,13 @@ int dp_del_switch_port(struct net_bridge_port *p) /* Then wait until no one is still using it, and destroy it. */ synchronize_rcu(); +#ifdef SUPPORT_SNAT + /* Free any SNAT configuration on the port. */ + spin_lock_irqsave(&p->lock, flags); + snat_free_conf(p); + spin_unlock_irqrestore(&p->lock, flags); +#endif + /* Notify the ctlpath that this port no longer exists */ dp_send_port_status(p, OFPPR_DELETE); @@ -441,6 +434,16 @@ static int dp_maint_func(void *data) struct datapath *dp = (struct datapath *) data; while (!kthread_should_stop()) { +#ifdef SUPPORT_SNAT + struct net_bridge_port *p; + + /* Expire old SNAT entries */ + rcu_read_lock(); + list_for_each_entry_rcu (p, &dp->port_list, node) + snat_maint(p); + rcu_read_unlock(); +#endif + /* Timeout old entries */ chain_timeout(dp->chain); msleep_interruptible(MAINT_SLEEP_MSECS); @@ -452,8 +455,25 @@ static int dp_maint_func(void *data) static void do_port_input(struct net_bridge_port *p, struct sk_buff *skb) { + /* Make our own copy of the packet. Otherwise we will mangle the + * packet for anyone who came before us (e.g. tcpdump via AF_PACKET). + * (No one comes after us, since we tell handle_bridge() that we took + * the packet.) */ + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return; + +#ifdef SUPPORT_SNAT + /* Check if this packet needs early SNAT processing. */ + if (snat_pre_route(skb)) { + kfree_skb(skb); + return; + } +#endif + /* Push the Ethernet header back on. */ skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); fwd_port_input(p->dp->chain, skb, p); } @@ -536,16 +556,18 @@ output_all(struct datapath *dp, struct sk_buff *skb, int flood) void dp_set_origin(struct datapath *dp, uint16_t in_port, struct sk_buff *skb) { - struct net_bridge_port *p = (in_port < OFPP_MAX ? dp->ports[in_port] - : in_port == OFPP_LOCAL ? dp->local_port - : NULL); + struct net_bridge_port *p; + p = (in_port < DP_MAX_PORTS ? dp->ports[in_port] + : in_port == OFPP_LOCAL ? dp->local_port + : NULL); if (p) skb->dev = p->dev; else skb->dev = NULL; } -static int xmit_skb(struct sk_buff *skb) +int +dp_xmit_skb(struct sk_buff *skb) { int len = skb->len; if (packet_length(skb) > skb->dev->mtu) { @@ -573,10 +595,10 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port, if (!skb->dev) { if (net_ratelimit()) printk("skb device not set forwarding to in_port\n"); - kfree(skb); + kfree_skb(skb); return -ESRCH; } - return xmit_skb(skb); + return dp_xmit_skb(skb); case OFPP_TABLE: { int retval = run_flow_through_tables(dp->chain, skb, @@ -598,10 +620,13 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port, case OFPP_LOCAL: { struct net_device *dev = dp->netdev; +#ifdef SUPPORT_SNAT + snat_local_in(skb); +#endif return dev ? dp_dev_recv(dev, skb) : -ESRCH; } - case 0 ... OFPP_MAX-1: { + case 0 ... DP_MAX_PORTS - 1: { struct net_bridge_port *p = dp->ports[out_port]; if (p == NULL) goto bad_port; @@ -617,7 +642,7 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port, return 0; } skb->dev = p->dev; - return xmit_skb(skb); + return dp_xmit_skb(skb); } default: @@ -645,7 +670,6 @@ 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; @@ -661,11 +685,12 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, } opi->buffer_id = htonl(buffer_id); opi->total_len = htons(skb->len); - p = skb->dev->br_port; - opi->in_port = htons(p ? p->port_no : OFPP_LOCAL); + opi->in_port = htons(skb->dev && skb->dev->br_port + ? skb->dev->br_port->port_no + : OFPP_LOCAL); opi->reason = reason; opi->pad = 0; - memcpy(opi->data, skb_mac_header(skb), fwd_len); + skb_copy_bits(skb, 0, opi->data, fwd_len); err = send_openflow_skb(f_skb, NULL); out: @@ -808,7 +833,7 @@ dp_send_features_reply(struct datapath *dp, const struct sender *sender) int port_count; /* Overallocate. */ - port_max_len = sizeof(struct ofp_phy_port) * OFPP_MAX; + port_max_len = sizeof(struct ofp_phy_port) * DP_MAX_PORTS; ofr = alloc_openflow_skb(dp, sizeof(*ofr) + port_max_len, OFPT_FEATURES_REPLY, sender, &skb); if (!ofr) @@ -898,9 +923,10 @@ dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm) { unsigned long int flags; int port_no = ntohs(opm->port_no); - struct net_bridge_port *p = (port_no < OFPP_MAX ? dp->ports[port_no] - : port_no == OFPP_LOCAL ? dp->local_port - : NULL); + struct net_bridge_port *p; + p = (port_no < DP_MAX_PORTS ? 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(opm->hw_addr, p->dev->dev_addr, ETH_ALEN)) @@ -1492,7 +1518,7 @@ static int port_stats_dump(struct datapath *dp, void *state, ops = body; n_ports = 0; - for (i = s->port; i < OFPP_MAX && n_ports < max_ports; i++) { + for (i = s->port; i < DP_MAX_PORTS && n_ports < max_ports; i++) { struct net_bridge_port *p = dp->ports[i]; struct net_device_stats *stats; if (!p) @@ -1766,12 +1792,39 @@ static void dp_uninit_netlink(void) genl_unregister_family(&dp_genl_family); } +/* Set the description strings if appropriate values are available from + * the DMI. */ +static void set_desc(void) +{ + const char *uuid = dmi_get_system_info(DMI_PRODUCT_UUID); + const char *uptr = uuid + 24; + + if (!uuid || *uuid == '\0' || strlen(uuid) != 36) + return; + + /* We are only interested version 1 UUIDs, since the last six bytes + * are an IEEE 802 MAC address. */ + if (uuid[14] != '1') + return; + + /* Only set if the UUID is from Nicira. */ + if (strncmp(uptr, NICIRA_OUI_STR, strlen(NICIRA_OUI_STR))) + return; + + strlcpy(mfr_desc, dmi_get_system_info(DMI_SYS_VENDOR), sizeof(mfr_desc)); + snprintf(hw_desc, sizeof(hw_desc), "%s %s", + dmi_get_system_info(DMI_PRODUCT_NAME), + dmi_get_system_info(DMI_PRODUCT_VERSION)); + strlcpy(serial_num, dmi_get_system_info(DMI_PRODUCT_SERIAL), + sizeof(serial_num)); +} + static int __init dp_init(void) { int err; - printk("OpenFlow "VERSION", built "__DATE__" "__TIME__", " - "protocol 0x%02x\n", OFP_VERSION); + printk("OpenFlow %s, built "__DATE__" "__TIME__", " + "protocol 0x%02x\n", VERSION BUILDNR, OFP_VERSION); err = flow_init(); if (err) @@ -1785,6 +1838,10 @@ static int __init dp_init(void) if (err) goto error_unreg_notifier; + /* Check if better descriptions of the switch are available than the + * defaults. */ + set_desc(); + /* Hook into callback used by the bridge to intercept packets. * Parasites we are. */ if (br_handle_frame_hook)