X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=vswitchd%2Fbridge.c;h=3b7ec51f44fee1ae14326184d4d46b5e38143fe6;hb=bb60fa749da7f725f2f000a90ea317ad4b1383d2;hp=4e272248fcff8336e9ac9004576c24cf8c50fee6;hpb=d65349ea28bb67a0062a9b4b60ff97538206373b;p=openvswitch diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 4e272248..3b7ec51f 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -50,6 +50,7 @@ #include "port-array.h" #include "proc-net-compat.h" #include "process.h" +#include "shash.h" #include "socket-util.h" #include "stp.h" #include "svec.h" @@ -60,6 +61,7 @@ #include "vconn-ssl.h" #include "xenserver.h" #include "xtoxll.h" +#include "sflow_api.h" #define THIS_MODULE VLM_bridge #include "vlog.h" @@ -209,6 +211,7 @@ static uint64_t bridge_pick_datapath_id(struct bridge *, const uint8_t bridge_ea[ETH_ADDR_LEN], struct iface *hw_addr_iface); static struct iface *bridge_get_local_iface(struct bridge *); +static const char *bridge_get_controller(const struct bridge *br); static uint64_t dpid_from_hash(const void *, size_t nbytes); static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args); @@ -369,6 +372,70 @@ bridge_configure_ssl(void) } #endif +/* Attempt to create the network device 'iface_name' through the netdev + * library. */ +static int +set_up_iface(const char *iface_name, bool create) +{ + const char *type; + const char *arg; + struct svec arg_svec; + struct shash args; + int error; + size_t i; + + /* If a type is not explicitly declared, then assume it's an existing + * "system" device. */ + type = cfg_get_string(0, "iface.%s.type", iface_name); + if (!type || !strcmp(type, "system")) { + return 0; + } + + svec_init(&arg_svec); + cfg_get_subsections(&arg_svec, "iface.%s.args", iface_name); + + shash_init(&args); + SVEC_FOR_EACH (i, arg, &arg_svec) { + const char *value; + + value = cfg_get_string(0, "iface.%s.args.%s", iface_name, arg); + if (value) { + shash_add(&args, arg, xstrdup(value)); + } + } + + if (create) { + error = netdev_create(iface_name, type, &args); + } else { + /* xxx Check to make sure that the type hasn't changed. */ + error = netdev_reconfigure(iface_name, &args); + } + + svec_destroy(&arg_svec); + shash_destroy(&args); + + return error; +} + +static int +create_iface(const char *iface_name) +{ + return set_up_iface(iface_name, true); +} + +static int +reconfigure_iface(const char *iface_name) +{ + return set_up_iface(iface_name, false); +} + +static void +destroy_iface(const char *iface_name) +{ + netdev_destroy(iface_name); +} + + /* iterate_and_prune_ifaces() callback function that opens the network device * for 'iface', if it is not already open, and retrieves the interface's MAC * address and carrier status. */ @@ -462,6 +529,7 @@ bridge_reconfigure(void) struct svec old_br, new_br; struct bridge *br, *next; size_t i; + int sflow_bridge_number; COVERAGE_INC(bridge_reconfigure); @@ -524,6 +592,7 @@ bridge_reconfigure(void) p->devname, dpif_name(br->dpif), strerror(retval)); } + destroy_iface(p->devname); } } svec_destroy(&want_ifaces); @@ -544,11 +613,25 @@ bridge_reconfigure(void) bridge_get_all_ifaces(br, &want_ifaces); svec_diff(&want_ifaces, &cur_ifaces, &add_ifaces, NULL, NULL); + for (i = 0; i < cur_ifaces.n; i++) { + const char *if_name = cur_ifaces.names[i]; + reconfigure_iface(if_name); + } + for (i = 0; i < add_ifaces.n; i++) { const char *if_name = add_ifaces.names[i]; bool internal; int error; + /* Attempt to create the network interface in case it + * doesn't exist yet. */ + error = create_iface(if_name); + if (error) { + VLOG_WARN("could not create iface %s: %s\n", if_name, + strerror(error)); + continue; + } + /* Add to datapath. */ internal = iface_is_internal(br, if_name); error = dpif_port_add(br->dpif, if_name, @@ -566,6 +649,7 @@ bridge_reconfigure(void) svec_destroy(&want_ifaces); svec_destroy(&add_ifaces); } + sflow_bridge_number = 0; LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { uint8_t ea[8]; uint64_t dpid; @@ -636,6 +720,42 @@ bridge_reconfigure(void) } svec_destroy(&nf_options.collectors); + if (cfg_has("sflow.%s.host", br->name)) { + struct ofproto_sflow_options oso; + + svec_init(&oso.targets); + cfg_get_all_keys(&oso.targets, "sflow.%s.host", br->name); + + oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE; + if (cfg_has("sflow.%s.sampling", br->name)) { + oso.sampling_rate = cfg_get_int(0, "sflow.%s.sampling", + br->name); + } + + oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL; + if (cfg_has("sflow.%s.polling", br->name)) { + oso.polling_interval = cfg_get_int(0, "sflow.%s.polling", + br->name); + } + + oso.header_len = SFL_DEFAULT_HEADER_SIZE; + if (cfg_has("sflow.%s.header", br->name)) { + oso.header_len = cfg_get_int(0, "sflow.%s.header", br->name); + } + + oso.sub_id = sflow_bridge_number++; + oso.agent_device = (char *) cfg_get_string(0, "sflow.%s.agent", + br->name); + oso.control_ip = (char *) cfg_get_string(0, + "bridge.%s.controller.ip", + br->name); + ofproto_set_sflow(br->ofproto, &oso); + + svec_destroy(&oso.targets); + } else { + ofproto_set_sflow(br->ofproto, NULL); + } + /* Update the controller and related settings. It would be more * straightforward to call this from bridge_reconfigure_one(), but we * can't do it there for two reasons. First, and most importantly, at @@ -968,21 +1088,12 @@ bridge_create(const char *name) assert(!bridge_lookup(name)); br = xcalloc(1, sizeof *br); - error = dpif_create(name, &br->dpif); - if (error == EEXIST || error == EBUSY) { - error = dpif_open(name, &br->dpif); - if (error) { - VLOG_ERR("datapath %s already exists but cannot be opened: %s", - name, strerror(error)); - free(br); - return NULL; - } - dpif_flow_flush(br->dpif); - } else if (error) { - VLOG_ERR("failed to create datapath %s: %s", name, strerror(error)); + error = dpif_create_and_open(name, &br->dpif); + if (error) { free(br); return NULL; } + dpif_flow_flush(br->dpif); error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto); if (error) { @@ -1899,6 +2010,71 @@ compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan, } } +/* Returns the effective vlan of a packet, taking into account both the + * 802.1Q header and implicitly tagged ports. A value of 0 indicates that + * the packet is untagged and -1 indicates it has an invalid header and + * should be dropped. */ +static int flow_get_vlan(struct bridge *br, const flow_t *flow, + struct port *in_port, bool have_packet) +{ + /* Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet + * belongs to VLAN 0, so we should treat both cases identically. (In the + * former case, the packet has an 802.1Q header that specifies VLAN 0, + * presumably to allow a priority to be specified. In the latter case, the + * packet does not have any 802.1Q header.) */ + int vlan = ntohs(flow->dl_vlan); + if (vlan == OFP_VLAN_NONE) { + vlan = 0; + } + if (in_port->vlan >= 0) { + if (vlan) { + /* XXX support double tagging? */ + if (have_packet) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged " + "packet received on port %s configured with " + "implicit VLAN %"PRIu16, + br->name, ntohs(flow->dl_vlan), + in_port->name, in_port->vlan); + } + return -1; + } + vlan = in_port->vlan; + } else { + if (!port_includes_vlan(in_port, vlan)) { + if (have_packet) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged " + "packet received on port %s not configured for " + "trunking VLAN %d", + br->name, vlan, in_port->name, vlan); + } + return -1; + } + } + + return vlan; +} + +static void +update_learning_table(struct bridge *br, const flow_t *flow, int vlan, + struct port *in_port) +{ + tag_type rev_tag = mac_learning_learn(br->ml, flow->dl_src, + vlan, in_port->port_idx); + if (rev_tag) { + /* The log messages here could actually be useful in debugging, + * so keep the rate limit relatively high. */ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, + 300); + VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is " + "on port %s in VLAN %d", + br->name, ETH_ADDR_ARGS(flow->dl_src), + in_port->name, vlan); + ofproto_revalidate(br->ofproto, rev_tag); + } +} + static bool is_bcast_arp_reply(const flow_t *flow) { @@ -1947,41 +2123,9 @@ process_flow(struct bridge *br, const flow_t *flow, return true; } in_port = in_iface->port; - - /* Figure out what VLAN this packet belongs to. - * - * Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet - * belongs to VLAN 0, so we should treat both cases identically. (In the - * former case, the packet has an 802.1Q header that specifies VLAN 0, - * presumably to allow a priority to be specified. In the latter case, the - * packet does not have any 802.1Q header.) */ - vlan = ntohs(flow->dl_vlan); - if (vlan == OFP_VLAN_NONE) { - vlan = 0; - } - if (in_port->vlan >= 0) { - if (vlan) { - /* XXX support double tagging? */ - if (packet != NULL) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged " - "packet received on port %s configured with " - "implicit VLAN %"PRIu16, - br->name, ntohs(flow->dl_vlan), - in_port->name, in_port->vlan); - } - goto done; - } - vlan = in_port->vlan; - } else { - if (!port_includes_vlan(in_port, vlan)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged " - "packet received on port %s not configured for " - "trunking VLAN %d", - br->name, vlan, in_port->name, vlan); - goto done; - } + vlan = flow_get_vlan(br, flow, in_port, !!packet); + if (vlan < 0) { + goto done; } /* Drop frames for ports that STP wants entirely killed (both for @@ -2032,19 +2176,7 @@ process_flow(struct bridge *br, const flow_t *flow, out_port = FLOOD_PORT; /* Learn source MAC (but don't try to learn from revalidation). */ if (packet) { - tag_type rev_tag = mac_learning_learn(br->ml, flow->dl_src, - vlan, in_port->port_idx); - if (rev_tag) { - /* The log messages here could actually be useful in debugging, - * so keep the rate limit relatively high. */ - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, - 300); - VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is " - "on port %s in VLAN %d", - br->name, ETH_ADDR_ARGS(flow->dl_src), - in_port->name, vlan); - ofproto_revalidate(br->ofproto, rev_tag); - } + update_learning_table(br, flow, vlan, in_port); } /* Determine output port. */ @@ -2052,10 +2184,12 @@ process_flow(struct bridge *br, const flow_t *flow, tags); if (out_port_idx >= 0 && out_port_idx < br->n_ports) { out_port = br->ports[out_port_idx]; - } else if (!packet) { + } else if (!packet && !eth_addr_is_multicast(flow->dl_dst)) { /* If we are revalidating but don't have a learning entry then - * eject the flow. Installing a flow that floods packets will - * prevent us from seeing future packets and learning properly. */ + * eject the flow. Installing a flow that floods packets opens + * up a window of time where we could learn from a packet reflected + * on a bond and blackhole packets before the learning table is + * updated to reflect the correct port. */ return false; } @@ -2135,17 +2269,30 @@ bridge_account_flow_ofhook_cb(const flow_t *flow, void *br_) { struct bridge *br = br_; + struct port *in_port; const union odp_action *a; + /* Feed information from the active flows back into the learning table + * to ensure that table is always in sync with what is actually flowing + * through the datapath. */ + in_port = port_from_dp_ifidx(br, flow->in_port); + if (in_port) { + int vlan = flow_get_vlan(br, flow, in_port, false); + if (vlan >= 0) { + update_learning_table(br, flow, vlan, in_port); + } + } + if (!br->has_bonded_ports) { return; } for (a = actions; a < &actions[n_actions]; a++) { if (a->type == ODPAT_OUTPUT) { - struct port *port = port_from_dp_ifidx(br, a->output.port); - if (port && port->n_ifaces >= 2) { - struct bond_entry *e = lookup_bond_entry(port, flow->dl_src); + struct port *out_port = port_from_dp_ifidx(br, a->output.port); + if (out_port && out_port->n_ifaces >= 2) { + struct bond_entry *e = lookup_bond_entry(out_port, + flow->dl_src); e->tx_bytes += n_bytes; } } @@ -3402,6 +3549,8 @@ mirror_reconfigure(struct bridge *br) int vlan = cfg_get_vlan(i, "vlan.%s.disable-learning", br->name); if (vlan >= 0) { bitmap_set1(rspan_vlans, vlan); + VLOG_INFO("bridge %s: disabling learning on vlan %d\n", + br->name, vlan); } else { VLOG_ERR("bridge %s: invalid value '%s' for learning disabled " "VLAN", br->name,