X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=vswitchd%2Fbridge.c;h=d894233ab58126bcdc6e8f925aad580e2a858a29;hb=2c360fbb2777fba6d35599e4b53287e2ecb26fa9;hp=26b4f707524a5505f65544603a829c9de0272166;hpb=59d7b2b65665d5d0db9a348789f7eba4b3f16d54;p=openvswitch diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 26b4f707..d894233a 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -73,6 +73,7 @@ #include "xenserver.h" #include "vlog.h" #include "sflow_api.h" +#include "vlan-bitmap.h" VLOG_DEFINE_THIS_MODULE(bridge); @@ -147,6 +148,8 @@ struct port { * A bridge port for bonding has at least 2 interfaces. */ struct list ifaces; /* List of "struct iface"s. */ + struct lacp *lacp; /* NULL if LACP is not enabled. */ + /* Bonding info. */ struct bond *bond; @@ -183,6 +186,11 @@ struct bridge { /* Port mirroring. */ struct mirror *mirrors[MAX_MIRRORS]; + + /* Synthetic local port if necessary. */ + struct ovsrec_port synth_local_port; + struct ovsrec_interface synth_local_iface; + struct ovsrec_interface *synth_local_ifacep; }; /* List of all bridges. */ @@ -196,10 +204,11 @@ static struct ovsdb_idl *idl; #define STATS_INTERVAL (5 * 1000) /* In milliseconds. */ static long long int stats_timer = LLONG_MIN; -/* Stores the time after which CFM statistics may be written to the database. - * Only updated when changes to the database require rate limiting. */ -#define CFM_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */ -static long long int cfm_limiter = LLONG_MIN; +/* Stores the time after which rate limited statistics may be written to the + * database. Only updated when changes to the database require rate limiting. + */ +#define DB_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */ +static long long int db_limiter = LLONG_MIN; static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg); static void bridge_destroy(struct bridge *); @@ -238,6 +247,7 @@ static struct port *port_lookup(const struct bridge *, const char *name); static struct iface *port_get_an_iface(const struct port *); static struct port *port_from_dp_ifidx(const struct bridge *, uint16_t dp_ifidx); +static void port_reconfigure_lacp(struct port *); static void port_reconfigure_bond(struct port *); static void port_send_learning_packets(struct port *); @@ -260,6 +270,7 @@ static void iface_update_qos(struct iface *, const struct ovsrec_qos *); static void iface_update_cfm(struct iface *); static bool iface_refresh_cfm_stats(struct iface *iface); static bool iface_get_carrier(const struct iface *); +static bool iface_is_synthetic(const struct iface *); static void shash_from_ovs_idl_map(char **keys, char **values, size_t n, struct shash *); @@ -339,6 +350,7 @@ bridge_init(const char *remote) NULL); unixctl_command_register("bridge/reconnect", bridge_unixctl_reconnect, NULL); + lacp_init(); bond_init(); } @@ -407,73 +419,6 @@ bridge_configure_once(const struct ovsrec_open_vswitch *cfg) sset_destroy(&dpif_types); } -/* Callback for iterate_and_prune_ifaces(). */ -static bool -check_iface(struct bridge *br, struct iface *iface, void *aux OVS_UNUSED) -{ - if (!iface->netdev) { - /* We already reported a related error, don't bother duplicating it. */ - return false; - } - - if (iface->dp_ifidx < 0) { - VLOG_ERR("%s interface not in %s, dropping", - iface->name, dpif_name(br->dpif)); - return false; - } - - VLOG_DBG("%s has interface %s on port %d", dpif_name(br->dpif), - iface->name, iface->dp_ifidx); - return true; -} - -/* Callback for iterate_and_prune_ifaces(). */ -static bool -set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface, - void *aux OVS_UNUSED) -{ - /* Set policing attributes. */ - netdev_set_policing(iface->netdev, - iface->cfg->ingress_policing_rate, - iface->cfg->ingress_policing_burst); - - /* Set MAC address of internal interfaces other than the local - * interface. */ - if (iface->dp_ifidx != ODPP_LOCAL && !strcmp(iface->type, "internal")) { - iface_set_mac(iface); - } - - return true; -} - -/* Calls 'cb' for each interfaces in 'br', passing along the 'aux' argument. - * Deletes from 'br' all the interfaces for which 'cb' returns false, and then - * deletes from 'br' any ports that no longer have any interfaces. */ -static void -iterate_and_prune_ifaces(struct bridge *br, - bool (*cb)(struct bridge *, struct iface *, - void *aux), - void *aux) -{ - struct port *port, *next_port; - - HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) { - struct iface *iface, *next_iface; - - LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) { - if (!cb(br, iface, aux)) { - iface_set_ofport(iface->cfg, -1); - iface_destroy(iface); - } - } - - if (list_is_empty(&port->ifaces)) { - VLOG_WARN("%s port has no interfaces, dropping", port->name); - port_destroy(port); - } - } -} - /* Looks at the list of managers in 'ovs_cfg' and extracts their remote IP * addresses and ports into '*managersp' and '*n_managersp'. The caller is * responsible for freeing '*managersp' (with free()). @@ -606,9 +551,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) && strcmp(dpif_port.name, br->name)) { int retval = dpif_port_del(br->dpif, dpif_port.port_no); if (retval) { - VLOG_WARN("failed to remove %s interface from %s: %s", - dpif_port.name, dpif_name(br->dpif), - strerror(retval)); + VLOG_WARN("bridge %s: failed to remove %s interface (%s)", + br->name, dpif_port.name, strerror(retval)); } } } @@ -655,6 +599,13 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) dpif_port = NULL; } if (iface) { + if (iface->port->bond) { + /* The bond has a pointer to the netdev, so remove it + * from the bond before closing the netdev. The slave + * will get added back to the bond later, after a new + * netdev is available. */ + bond_slave_unregister(iface->port->bond, iface); + } netdev_close(iface->netdev); iface->netdev = NULL; } @@ -694,13 +645,13 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) if (error) { netdev_close(netdev); if (error == EFBIG) { - VLOG_ERR("ran out of valid port numbers on %s", - dpif_name(br->dpif)); + VLOG_ERR("bridge %s: out of valid port numbers", + br->name); break; } else { - VLOG_WARN("failed to add %s interface to %s: %s", - if_name, dpif_name(br->dpif), - strerror(error)); + VLOG_WARN("bridge %s: failed to add %s interface " + "(%s)", + br->name, if_name, strerror(error)); continue; } } @@ -735,6 +686,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) uint8_t ea[ETH_ADDR_LEN]; uint64_t dpid; struct iface *local_iface; + struct port *port, *next_port; struct iface *hw_addr_iface; char *dpid_string; @@ -742,9 +694,34 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) /* Delete interfaces that cannot be opened. * - * From this point forward we are guaranteed that every "struct iface" - * has nonnull 'netdev' and correct 'dp_ifidx'. */ - iterate_and_prune_ifaces(br, check_iface, NULL); + * Following this loop, every remaining "struct iface" has nonnull + * 'netdev' and correct 'dp_ifidx'. */ + HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) { + struct iface *iface, *next_iface; + + LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) { + if (iface->netdev && iface->dp_ifidx >= 0) { + VLOG_DBG("bridge %s: interface %s is on port %d", + br->name, iface->name, iface->dp_ifidx); + } else { + if (iface->netdev) { + VLOG_ERR("bridge %s: missing %s interface, dropping", + br->name, iface->name); + } else { + /* We already reported a related error, don't bother + * duplicating it. */ + } + + iface_set_ofport(iface->cfg, -1); + iface_destroy(iface); + } + } + + if (list_is_empty(&port->ifaces)) { + VLOG_WARN("%s port has no interfaces, dropping", port->name); + port_destroy(port); + } + } /* Pick local port hardware address, datapath ID. */ bridge_pick_local_hw_addr(br, ea, &hw_addr_iface); @@ -882,16 +859,18 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct iface *iface; + port_reconfigure_lacp(port); port_reconfigure_bond(port); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_update_qos(iface, port->cfg->qos); + netdev_set_policing(iface->netdev, + iface->cfg->ingress_policing_rate, + iface->cfg->ingress_policing_burst); + iface_set_mac(iface); } } } - LIST_FOR_EACH (br, node, &all_bridges) { - iterate_and_prune_ifaces(br, set_iface_properties, NULL); - } /* Some reconfiguration operations require the bridge to have been run at * least once. */ @@ -1136,6 +1115,10 @@ iface_refresh_status(struct iface *iface) int64_t mtu_64; int error; + if (iface_is_synthetic(iface)) { + return; + } + shash_init(&sh); if (!netdev_get_status(iface->netdev, &sh)) { @@ -1227,6 +1210,27 @@ iface_refresh_cfm_stats(struct iface *iface) return changed; } +static bool +iface_refresh_lacp_stats(struct iface *iface) +{ + bool *db_current = iface->cfg->lacp_current; + bool changed = false; + + if (iface->port->lacp) { + bool current = lacp_slave_is_current(iface->port->lacp, iface); + + if (!db_current || *db_current != current) { + changed = true; + ovsrec_interface_set_lacp_current(iface->cfg, ¤t, 1); + } + } else if (db_current) { + changed = true; + ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0); + } + + return changed; +} + static void iface_refresh_stats(struct iface *iface) { @@ -1257,6 +1261,10 @@ iface_refresh_stats(struct iface *iface) struct netdev_stats stats; + if (iface_is_synthetic(iface)) { + return; + } + /* Intentionally ignore return value, since errors will set 'stats' to * all-1s, and we will deal with that correctly below. */ netdev_get_stats(iface->netdev, &stats); @@ -1415,7 +1423,7 @@ bridge_run(void) stats_timer = time_msec() + STATS_INTERVAL; } - if (time_msec() >= cfm_limiter) { + if (time_msec() >= db_limiter) { struct ovsdb_idl_txn *txn; bool changed = false; @@ -1428,12 +1436,13 @@ bridge_run(void) LIST_FOR_EACH (iface, port_elem, &port->ifaces) { changed = iface_refresh_cfm_stats(iface) || changed; + changed = iface_refresh_lacp_stats(iface) || changed; } } } if (changed) { - cfm_limiter = time_msec() + CFM_LIMIT_INTERVAL; + db_limiter = time_msec() + DB_LIMIT_INTERVAL; } ovsdb_idl_txn_commit(txn); @@ -1458,8 +1467,8 @@ bridge_wait(void) ovsdb_idl_wait(idl); poll_timer_wait_until(stats_timer); - if (cfm_limiter > time_msec()) { - poll_timer_wait_until(cfm_limiter); + if (db_limiter > time_msec()) { + poll_timer_wait_until(db_limiter); } } @@ -1659,7 +1668,7 @@ bridge_create(const struct ovsrec_bridge *br_cfg) list_push_back(&all_bridges, &br->node); - VLOG_INFO("created bridge %s on %s", br->name, dpif_name(br->dpif)); + VLOG_INFO("bridge %s: created", br->name); return br; } @@ -1670,22 +1679,27 @@ bridge_destroy(struct bridge *br) if (br) { struct port *port, *next; int error; + int i; HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) { port_destroy(port); } + for (i = 0; i < MAX_MIRRORS; i++) { + mirror_destroy(br->mirrors[i]); + } list_remove(&br->node); ofproto_destroy(br->ofproto); error = dpif_delete(br->dpif); if (error && error != ENOENT) { - VLOG_ERR("failed to delete %s: %s", - dpif_name(br->dpif), strerror(error)); + VLOG_ERR("bridge %s: failed to destroy (%s)", + br->name, strerror(error)); } dpif_close(br->dpif); mac_learning_destroy(br->ml); hmap_destroy(&br->ifaces); hmap_destroy(&br->ports); shash_destroy(&br->iface_by_name); + free(br->synth_local_iface.type); free(br->name); free(br); } @@ -1811,22 +1825,28 @@ bridge_reconfigure_one(struct bridge *br) br->name, name); } } + if (!shash_find(&new_ports, br->name)) { + struct dpif_port dpif_port; + char *type; - /* If we have a controller, then we need a local port. Complain if the - * user didn't specify one. - * - * XXX perhaps we should synthesize a port ourselves in this case. */ - if (bridge_get_controllers(br, NULL)) { - char local_name[IF_NAMESIZE]; - int error; + VLOG_WARN("bridge %s: no port named %s, synthesizing one", + br->name, br->name); - error = dpif_port_get_name(br->dpif, ODPP_LOCAL, - local_name, sizeof local_name); - if (!error && !shash_find(&new_ports, local_name)) { - VLOG_WARN("bridge %s: controller specified but no local port " - "(port named %s) defined", - br->name, local_name); - } + dpif_port_query_by_number(br->dpif, ODPP_LOCAL, &dpif_port); + type = xstrdup(dpif_port.type ? dpif_port.type : "internal"); + dpif_port_destroy(&dpif_port); + + br->synth_local_port.interfaces = &br->synth_local_ifacep; + br->synth_local_port.n_interfaces = 1; + br->synth_local_port.name = br->name; + + br->synth_local_iface.name = br->name; + free(br->synth_local_iface.type); + br->synth_local_iface.type = type; + + br->synth_local_ifacep = &br->synth_local_iface; + + shash_add(&new_ports, br->name, &br->synth_local_port); } /* Get rid of deleted ports. @@ -1865,16 +1885,8 @@ bridge_reconfigure_one(struct bridge *br) || !strcmp(br->cfg->fail_mode, "standalone") ? OFPROTO_FAIL_STANDALONE : OFPROTO_FAIL_SECURE; - if (ofproto_get_fail_mode(br->ofproto) != fail_mode - && !ofproto_has_primary_controller(br->ofproto)) { - ofproto_flush_flows(br->ofproto); - } ofproto_set_fail_mode(br->ofproto, fail_mode); - /* Delete all flows if we're switching from connected to standalone or vice - * versa. (XXX Should we delete all flows if we are switching from one - * controller to another?) */ - /* Configure OpenFlow controller connection snooping. */ if (!ofproto_has_snoops(br->ofproto)) { struct sset snoops; @@ -1975,7 +1987,6 @@ bridge_reconfigure_remotes(struct bridge *br, struct ovsrec_controller **controllers; size_t n_controllers; - bool had_primary; struct ofproto_controller *ocs; size_t n_ocs; @@ -1997,7 +2008,6 @@ bridge_reconfigure_remotes(struct bridge *br, } else { ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers); } - had_primary = ofproto_has_primary_controller(br->ofproto); n_controllers = bridge_get_controllers(br, &controllers); @@ -2014,9 +2024,9 @@ bridge_reconfigure_remotes(struct bridge *br, /* Prevent remote ovsdb-server users from accessing arbitrary Unix * domain sockets and overwriting arbitrary local files. */ - VLOG_ERR_RL(&rl, "%s: not adding Unix domain socket controller " - "\"%s\" due to possibility for remote exploit", - dpif_name(br->dpif), c->target); + VLOG_ERR_RL(&rl, "bridge %s: not adding Unix domain socket " + "controller \"%s\" due to possibility for remote " + "exploit", br->name, c->target); continue; } @@ -2031,28 +2041,6 @@ bridge_reconfigure_remotes(struct bridge *br, ofproto_set_controllers(br->ofproto, ocs, n_ocs); free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */ free(ocs); - - if (had_primary != ofproto_has_primary_controller(br->ofproto)) { - ofproto_flush_flows(br->ofproto); - } - - /* If there are no controllers and the bridge is in standalone - * mode, set up a flow that matches every packet and directs - * them to OFPP_NORMAL (which goes to us). Otherwise, the - * switch is in secure mode and we won't pass any traffic until - * a controller has been defined and it tells us to do so. */ - if (!n_controllers - && ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) { - union ofp_action action; - struct cls_rule rule; - - memset(&action, 0, sizeof action); - action.type = htons(OFPAT_OUTPUT); - action.output.len = htons(sizeof action); - action.output.port = htons(OFPP_NORMAL); - cls_rule_init_catchall(&rule, 0); - ofproto_add_flow(br->ofproto, &rule, &action, 1); - } } static void @@ -2102,11 +2090,11 @@ bridge_fetch_dp_ifaces(struct bridge *br) struct iface *iface = iface_lookup(br, dpif_port.name); if (iface) { if (iface->dp_ifidx >= 0) { - VLOG_WARN("%s reported interface %s twice", - dpif_name(br->dpif), dpif_port.name); + VLOG_WARN("bridge %s: interface %s reported twice", + br->name, dpif_port.name); } else if (iface_from_dp_ifidx(br, dpif_port.port_no)) { - VLOG_WARN("%s reported interface %"PRIu16" twice", - dpif_name(br->dpif), dpif_port.port_no); + VLOG_WARN("bridge %s: interface %"PRIu16" reported twice", + br->name, dpif_port.port_no); } else { iface->dp_ifidx = dpif_port.port_no; hmap_insert(&br->ifaces, &iface->dp_ifidx_node, @@ -2199,8 +2187,7 @@ dst_is_duplicate(const struct dst_set *set, const struct dst *test) static bool port_trunks_vlan(const struct port *port, uint16_t vlan) { - return (port->vlan < 0 - && (!port->trunks || bitmap_is_set(port->trunks, vlan))); + return (port->vlan < 0 || vlan_bitmap_contains(port->trunks, vlan)); } static bool @@ -2630,8 +2617,11 @@ bridge_special_ofhook_cb(const struct flow *flow, iface = iface_from_dp_ifidx(br, flow->in_port); if (flow->dl_type == htons(ETH_TYPE_LACP)) { - if (iface && iface->port->bond && packet) { - bond_process_lacp(iface->port->bond, iface, packet); + if (iface && iface->port->lacp && packet) { + const struct lacp_pdu *pdu = parse_lacp_packet(packet); + if (pdu) { + lacp_process_pdu(iface->port->lacp, iface, pdu); + } } return false; } @@ -2729,12 +2719,55 @@ static struct ofhooks bridge_ofhooks = { /* Port functions. */ +static void +lacp_send_pdu_cb(void *iface_, const struct lacp_pdu *pdu) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10); + struct iface *iface = iface_; + uint8_t ea[ETH_ADDR_LEN]; + int error; + + error = netdev_get_etheraddr(iface->netdev, ea); + if (!error) { + struct lacp_pdu *packet_pdu; + struct ofpbuf packet; + + ofpbuf_init(&packet, 0); + packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP, + sizeof *packet_pdu); + *packet_pdu = *pdu; + error = netdev_send(iface->netdev, &packet); + if (error) { + VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed " + "(%s)", iface->port->name, iface->name, + strerror(error)); + } + ofpbuf_uninit(&packet); + } else { + VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface " + "%s (%s)", iface->port->name, iface->name, + strerror(error)); + } +} + static void port_run(struct port *port) { + if (port->lacp) { + lacp_run(port->lacp, lacp_send_pdu_cb); + } + if (port->bond) { + struct iface *iface; + + LIST_FOR_EACH (iface, port_elem, &port->ifaces) { + bool may_enable = lacp_slave_may_enable(port->lacp, iface); + bond_slave_set_lacp_may_enable(port->bond, iface, may_enable); + } + bond_run(port->bond, - ofproto_get_revalidate_set(port->bridge->ofproto)); + ofproto_get_revalidate_set(port->bridge->ofproto), + lacp_negotiated(port->lacp)); if (bond_should_send_learning_packets(port->bond)) { port_send_learning_packets(port); } @@ -2744,6 +2777,10 @@ port_run(struct port *port) static void port_wait(struct port *port) { + if (port->lacp) { + lacp_wait(port->lacp); + } + if (port->bond) { bond_wait(port->bond); } @@ -2904,35 +2941,16 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg) /* Get trunked VLANs. */ trunks = NULL; if (vlan < 0 && cfg->n_trunks) { - size_t n_errors; - - trunks = bitmap_allocate(4096); - n_errors = 0; - for (i = 0; i < cfg->n_trunks; i++) { - int trunk = cfg->trunks[i]; - if (trunk >= 0) { - bitmap_set1(trunks, trunk); - } else { - n_errors++; - } - } - if (n_errors) { - VLOG_ERR("port %s: invalid values for %zu trunk VLANs", - port->name, cfg->n_trunks); - } - if (n_errors == cfg->n_trunks) { + trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks); + if (!trunks) { VLOG_ERR("port %s: no valid trunks, trunking all VLANs", port->name); - bitmap_free(trunks); - trunks = NULL; } } else if (vlan >= 0 && cfg->n_trunks) { VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan", port->name); } - if (trunks == NULL - ? port->trunks != NULL - : port->trunks == NULL || !bitmap_equal(trunks, port->trunks, 4096)) { + if (!vlan_bitmap_equal(trunks, port->trunks)) { need_flush = true; } bitmap_free(port->trunks); @@ -2966,6 +2984,8 @@ port_destroy(struct port *port) VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name); + bond_destroy(port->bond); + lacp_destroy(port->lacp); port_flush_macs(port); bitmap_free(port->trunks); @@ -3017,46 +3037,94 @@ enable_lacp(struct port *port, bool *activep) } } -static struct lacp_settings * -port_reconfigure_bond_lacp(struct port *port, struct lacp_settings *s) +static void +iface_reconfigure_lacp(struct iface *iface) { - if (!enable_lacp(port, &s->active)) { - return NULL; - } + struct lacp_slave_settings s; + int priority, portid; + + portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0")); + priority = atoi(get_interface_other_config(iface->cfg, + "lacp-port-priority", "0")); - s->name = port->name; - memcpy(s->id, port->bridge->ea, ETH_ADDR_LEN); - s->priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority", - "0")); - s->fast = !strcmp(get_port_other_config(port->cfg, "lacp-time", "slow"), - "fast"); + if (portid <= 0 || portid > UINT16_MAX) { + portid = iface->dp_ifidx; + } - if (s->priority <= 0 || s->priority > UINT16_MAX) { - /* Prefer bondable links if unspecified. */ - s->priority = UINT16_MAX - !list_is_short(&port->ifaces); + if (priority <= 0 || priority > UINT16_MAX) { + priority = UINT16_MAX; } - return s; + + s.name = iface->name; + s.id = portid; + s.priority = priority; + lacp_slave_register(iface->port->lacp, iface, &s); } static void -iface_reconfigure_bond(struct iface *iface) +port_reconfigure_lacp(struct port *port) { - struct lacp_slave_settings s; + static struct lacp_settings s; + struct iface *iface; + uint8_t sysid[ETH_ADDR_LEN]; + const char *sysid_str; + const char *lacp_time; + long long int custom_time; int priority; - s.name = iface->name; - s.id = iface->dp_ifidx; - priority = atoi(get_interface_other_config( - iface->cfg, "lacp-port-priority", "0")); - s.priority = (priority >= 0 && priority <= UINT16_MAX - ? priority : UINT16_MAX); - bond_slave_register(iface->port->bond, iface, iface->netdev, &s); + if (!enable_lacp(port, &s.active)) { + lacp_destroy(port->lacp); + port->lacp = NULL; + return; + } + + sysid_str = get_port_other_config(port->cfg, "lacp-system-id", NULL); + if (sysid_str && eth_addr_from_string(sysid_str, sysid)) { + memcpy(s.id, sysid, ETH_ADDR_LEN); + } else { + memcpy(s.id, port->bridge->ea, ETH_ADDR_LEN); + } + + s.name = port->name; + + /* Prefer bondable links if unspecified. */ + priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority", + "0")); + s.priority = (priority > 0 && priority <= UINT16_MAX + ? priority + : UINT16_MAX - !list_is_short(&port->ifaces)); + + s.strict = !strcmp(get_port_other_config(port->cfg, "lacp-strict", + "false"), + "true"); + + lacp_time = get_port_other_config(port->cfg, "lacp-time", "slow"); + custom_time = atoi(lacp_time); + if (!strcmp(lacp_time, "fast")) { + s.lacp_time = LACP_TIME_FAST; + } else if (!strcmp(lacp_time, "slow")) { + s.lacp_time = LACP_TIME_SLOW; + } else if (custom_time > 0) { + s.lacp_time = LACP_TIME_CUSTOM; + s.custom_time = custom_time; + } else { + s.lacp_time = LACP_TIME_SLOW; + } + + if (!port->lacp) { + port->lacp = lacp_create(); + } + + lacp_configure(port->lacp, &s); + + LIST_FOR_EACH (iface, port_elem, &port->ifaces) { + iface_reconfigure_lacp(iface); + } } static void port_reconfigure_bond(struct port *port) { - struct lacp_settings lacp_settings; struct bond_settings s; const char *detect_s; struct iface *iface; @@ -3102,7 +3170,6 @@ port_reconfigure_bond(struct port *port) } s.fake_iface = port->cfg->bond_fake_iface; - s.lacp = port_reconfigure_bond_lacp(port, &lacp_settings); if (!port->bond) { port->bond = bond_create(&s); @@ -3113,7 +3180,11 @@ port_reconfigure_bond(struct port *port) } LIST_FOR_EACH (iface, port_elem, &port->ifaces) { - iface_reconfigure_bond(iface); + uint16_t stable_id = (port->lacp + ? lacp_slave_get_port_id(port->lacp, iface) + : iface->dp_ifidx); + bond_slave_register(iface->port->bond, iface, stable_id, + iface->netdev); } } @@ -3186,6 +3257,10 @@ iface_destroy(struct iface *iface) bond_slave_unregister(port->bond, iface); } + if (port->lacp) { + lacp_slave_unregister(port->lacp, iface); + } + shash_find_and_delete_assert(&br->iface_by_name, iface->name); if (iface->dp_ifidx >= 0) { @@ -3245,13 +3320,15 @@ iface_set_mac(struct iface *iface) { uint8_t ea[ETH_ADDR_LEN]; - if (iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) { - if (eth_addr_is_multicast(ea)) { + if (!strcmp(iface->type, "internal") + && iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) { + if (iface->dp_ifidx == ODPP_LOCAL) { + VLOG_ERR("interface %s: ignoring mac in Interface record " + "(use Bridge record to set local port's mac)", + iface->name); + } else if (eth_addr_is_multicast(ea)) { VLOG_ERR("interface %s: cannot set MAC to multicast address", iface->name); - } else if (iface->dp_ifidx == ODPP_LOCAL) { - VLOG_ERR("ignoring iface.%s.mac; use bridge.%s.mac instead", - iface->name, iface->name); } else { int error = netdev_set_etheraddr(iface->netdev, ea); if (error) { @@ -3266,7 +3343,7 @@ iface_set_mac(struct iface *iface) static void iface_set_ofport(const struct ovsrec_interface *if_cfg, int64_t ofport) { - if (if_cfg) { + if (if_cfg && !ovsdb_idl_row_is_synthetic(&if_cfg->header_)) { ovsrec_interface_set_ofport(if_cfg, &ofport, 1); } } @@ -3426,6 +3503,14 @@ iface_get_carrier(const struct iface *iface) /* XXX */ return netdev_get_carrier(iface->netdev); } + +/* Returns true if 'iface' is synthetic, that is, if we constructed it locally + * instead of obtaining it from the database. */ +static bool +iface_is_synthetic(const struct iface *iface) +{ + return ovsdb_idl_row_is_synthetic(&iface->cfg->header_); +} /* Port mirroring. */ @@ -3490,24 +3575,14 @@ mirror_reconfigure(struct bridge *br) /* Update flooded vlans (for RSPAN). */ rspan_vlans = NULL; if (br->cfg->n_flood_vlans) { - rspan_vlans = bitmap_allocate(4096); - - for (i = 0; i < br->cfg->n_flood_vlans; i++) { - int64_t vlan = br->cfg->flood_vlans[i]; - if (vlan >= 0 && vlan < 4096) { - bitmap_set1(rspan_vlans, vlan); - VLOG_INFO("bridge %s: disabling learning on vlan %"PRId64, - br->name, vlan); - } else { - VLOG_ERR("bridge %s: invalid value %"PRId64 "for flood VLAN", - br->name, vlan); - } - } + rspan_vlans = vlan_bitmap_from_array(br->cfg->flood_vlans, + br->cfg->n_flood_vlans); } if (mac_learning_set_flood_vlans(br->ml, rspan_vlans)) { bridge_flush(br); mac_learning_flush(br->ml); } + free(rspan_vlans); } static void @@ -3532,6 +3607,7 @@ mirror_create(struct bridge *br, struct ovsrec_mirror *cfg) mac_learning_flush(br->ml); br->mirrors[i] = m = xzalloc(sizeof *m); + m->uuid = cfg->header_.uuid; m->bridge = br; m->idx = i; m->name = xstrdup(cfg->name); @@ -3621,19 +3697,6 @@ vlan_is_mirrored(const struct mirror *m, int vlan) return false; } -static bool -port_trunks_any_mirrored_vlan(const struct mirror *m, const struct port *p) -{ - size_t i; - - for (i = 0; i < m->n_vlans; i++) { - if (port_trunks_vlan(p, m->vlans[i])) { - return true; - } - } - return false; -} - static void mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) { @@ -3718,11 +3781,7 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) /* Update ports. */ mirror_bit = MIRROR_MASK_C(1) << m->idx; HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) { - if (sset_contains(&m->src_ports, port->name) - || (m->n_vlans - && (!port->vlan - ? port_trunks_any_mirrored_vlan(m, port) - : vlan_is_mirrored(m, port->vlan)))) { + if (sset_contains(&m->src_ports, port->name)) { port->src_mirrors |= mirror_bit; } else { port->src_mirrors &= ~mirror_bit;