X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=vswitchd%2Fbridge.c;h=d883596de83c0e129c088974516cd4554d6495d8;hb=cc8d12f9364760456c86e92378d529c0dd9aaa66;hp=42d0572af0f1a3db606faaad41e244a7c1e2afcd;hpb=f620b43af153c97420623034b18d83833bdbbe7e;p=openvswitch diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 42d0572a..d883596d 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -81,8 +81,8 @@ COVERAGE_DEFINE(bridge_process_flow); COVERAGE_DEFINE(bridge_reconfigure); struct dst { + struct iface *iface; uint16_t vlan; - uint16_t dp_ifidx; }; struct dst_set { @@ -146,7 +146,8 @@ struct port { /* An ordinary bridge port has 1 interface. * A bridge port for bonding has at least 2 interfaces. */ struct list ifaces; /* List of "struct iface"s. */ - size_t n_ifaces; /* list_size(ifaces). */ + + struct lacp *lacp; /* NULL if LACP is not enabled. */ /* Bonding info. */ struct bond *bond; @@ -184,6 +185,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. */ @@ -197,10 +203,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 *); @@ -239,6 +246,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 *); @@ -261,6 +269,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 *); @@ -284,16 +293,54 @@ bridge_init(const char *remote) ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_cur_cfg); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_statistics); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_external_ids); + ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_ovs_version); + ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_db_version); + ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_system_type); + ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_system_version); + ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_datapath_id); ovsdb_idl_omit(idl, &ovsrec_bridge_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_port_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_port_col_fake_bridge); + ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_admin_state); + ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_duplex); + ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_speed); + ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_state); + ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mtu); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_ofport); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_statistics); + ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_status); ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids); + ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_is_connected); + ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_role); + ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_status); + ovsdb_idl_omit(idl, &ovsrec_controller_col_external_ids); + + ovsdb_idl_omit_alert(idl, &ovsrec_maintenance_point_col_fault); + + ovsdb_idl_omit_alert(idl, &ovsrec_monitor_col_fault); + + ovsdb_idl_omit(idl, &ovsrec_qos_col_external_ids); + + ovsdb_idl_omit(idl, &ovsrec_queue_col_external_ids); + + ovsdb_idl_omit(idl, &ovsrec_mirror_col_external_ids); + + ovsdb_idl_omit(idl, &ovsrec_netflow_col_external_ids); + + ovsdb_idl_omit(idl, &ovsrec_sflow_col_external_ids); + + ovsdb_idl_omit(idl, &ovsrec_manager_col_external_ids); + ovsdb_idl_omit(idl, &ovsrec_manager_col_inactivity_probe); + ovsdb_idl_omit(idl, &ovsrec_manager_col_is_connected); + ovsdb_idl_omit(idl, &ovsrec_manager_col_max_backoff); + ovsdb_idl_omit(idl, &ovsrec_manager_col_status); + + ovsdb_idl_omit(idl, &ovsrec_ssl_col_external_ids); + /* Register unixctl commands. */ unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL); unixctl_command_register("cfm/show", cfm_unixctl_show, NULL); @@ -302,6 +349,7 @@ bridge_init(const char *remote) NULL); unixctl_command_register("bridge/reconnect", bridge_unixctl_reconnect, NULL); + lacp_init(); bond_init(); } @@ -402,9 +450,7 @@ set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface, /* 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); - } + iface_set_mac(iface); return true; } @@ -430,7 +476,7 @@ iterate_and_prune_ifaces(struct bridge *br, } } - if (!port->n_ifaces) { + if (list_is_empty(&port->ifaces)) { VLOG_WARN("%s port has no interfaces, dropping", port->name); port_destroy(port); } @@ -618,6 +664,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; } @@ -845,6 +898,7 @@ 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) { @@ -856,8 +910,13 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) iterate_and_prune_ifaces(br, set_iface_properties, NULL); } + /* Some reconfiguration operations require the bridge to have been run at + * least once. */ LIST_FOR_EACH (br, node, &all_bridges) { struct iface *iface; + + bridge_run_one(br); + HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) { iface_update_cfm(iface); } @@ -1094,6 +1153,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)) { @@ -1185,6 +1248,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) { @@ -1215,6 +1299,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); @@ -1373,7 +1461,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; @@ -1386,12 +1474,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); @@ -1416,8 +1505,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); } } @@ -1628,10 +1717,14 @@ 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); @@ -1644,6 +1737,7 @@ bridge_destroy(struct bridge *br) 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); } @@ -1769,22 +1863,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. @@ -1810,7 +1910,7 @@ bridge_reconfigure_one(struct bridge *br) } port_reconfigure(port, node->data); - if (!port->n_ifaces) { + if (list_is_empty(&port->ifaces)) { VLOG_WARN("bridge %s: port %s has no interfaces, dropping", br->name, port->name); port_destroy(port); @@ -2025,7 +2125,7 @@ bridge_get_all_ifaces(const struct bridge *br, struct shash *ifaces) LIST_FOR_EACH (iface, port_elem, &port->ifaces) { shash_add_once(ifaces, iface->name, iface); } - if (port->n_ifaces > 1 && port->cfg->bond_fake_iface) { + if (!list_is_short(&port->ifaces) && port->cfg->bond_fake_iface) { shash_add_once(ifaces, port->name, NULL); } } @@ -2086,69 +2186,17 @@ set_dst(struct dst *dst, const struct flow *flow, const struct port *in_port, const struct port *out_port, tag_type *tags) { - struct iface *iface; - uint16_t vlan; - - vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE - : in_port->vlan >= 0 ? in_port->vlan - : flow->vlan_tci == 0 ? OFP_VLAN_NONE - : vlan_tci_to_vid(flow->vlan_tci)); - - iface = (!out_port->bond - ? port_get_an_iface(out_port) - : bond_choose_output_slave(out_port->bond, flow, vlan, tags)); - if (iface) { - dst->vlan = vlan; - dst->dp_ifidx = iface->dp_ifidx; - return true; - } else { - return false; - } -} - -static void -swap_dst(struct dst *p, struct dst *q) -{ - struct dst tmp = *p; - *p = *q; - *q = tmp; -} - -/* Moves all the dsts with vlan == 'vlan' to the front of the 'n_dsts' in - * 'dsts'. (This may help performance by reducing the number of VLAN changes - * that we push to the datapath. We could in fact fully sort the array by - * vlan, but in most cases there are at most two different vlan tags so that's - * possibly overkill.) */ -static void -partition_dsts(struct dst_set *set, int vlan) -{ - struct dst *first = set->dsts; - struct dst *last = set->dsts + set->n; - - while (first != last) { - /* Invariants: - * - All dsts < first have vlan == 'vlan'. - * - All dsts >= last have vlan != 'vlan'. - * - first < last. */ - while (first->vlan == vlan) { - if (++first == last) { - return; - } - } + dst->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE + : in_port->vlan >= 0 ? in_port->vlan + : flow->vlan_tci == 0 ? OFP_VLAN_NONE + : vlan_tci_to_vid(flow->vlan_tci)); - /* Same invariants, plus one additional: - * - first->vlan != vlan. - */ - while (last[-1].vlan != vlan) { - if (--last == first) { - return; - } - } + dst->iface = (!out_port->bond + ? port_get_an_iface(out_port) + : bond_choose_output_slave(out_port->bond, flow, + dst->vlan, tags)); - /* Same invariants, plus one additional: - * - last[-1].vlan == vlan.*/ - swap_dst(first++, --last); - } + return dst->iface != NULL; } static int @@ -2199,7 +2247,7 @@ dst_is_duplicate(const struct dst_set *set, const struct dst *test) size_t i; for (i = 0; i < set->n; i++) { if (set->dsts[i].vlan == test->vlan - && set->dsts[i].dp_ifidx == test->dp_ifidx) { + && set->dsts[i].iface == test->iface) { return true; } } @@ -2245,14 +2293,7 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan, const struct port *in_port, const struct port *out_port, struct dst_set *set, tag_type *tags, uint16_t *nf_output_iface) { - mirror_mask_t mirrors = in_port->src_mirrors; struct dst dst; - int flow_vlan; - - flow_vlan = vlan_tci_to_vid(flow->vlan_tci); - if (flow_vlan == 0) { - flow_vlan = OFP_VLAN_NONE; - } if (out_port == FLOOD_PORT) { struct port *port; @@ -2263,20 +2304,44 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan, && port_includes_vlan(port, vlan) && !port->is_mirror_output_port && set_dst(&dst, flow, in_port, port, tags)) { - mirrors |= port->dst_mirrors; dst_set_add(set, &dst); } } *nf_output_iface = NF_OUT_FLOOD; } else if (out_port && set_dst(&dst, flow, in_port, out_port, tags)) { dst_set_add(set, &dst); - *nf_output_iface = dst.dp_ifidx; - mirrors |= out_port->dst_mirrors; + *nf_output_iface = dst.iface->dp_ifidx; + } +} + +static void +compose_mirror_dsts(const struct bridge *br, const struct flow *flow, + uint16_t vlan, const struct port *in_port, + struct dst_set *set, tag_type *tags) +{ + mirror_mask_t mirrors; + int flow_vlan; + size_t i; + + mirrors = in_port->src_mirrors; + for (i = 0; i < set->n; i++) { + mirrors |= set->dsts[i].iface->port->dst_mirrors; + } + + if (!mirrors) { + return; + } + + flow_vlan = vlan_tci_to_vid(flow->vlan_tci); + if (flow_vlan == 0) { + flow_vlan = OFP_VLAN_NONE; } while (mirrors) { struct mirror *m = br->mirrors[mirror_mask_ffs(mirrors) - 1]; if (!m->n_vlans || vlan_is_mirrored(m, vlan)) { + struct dst dst; + if (m->out_port) { if (set_dst(&dst, flow, in_port, m->out_port, tags) && !dst_is_duplicate(set, &dst)) { @@ -2313,23 +2378,6 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan, } mirrors &= mirrors - 1; } - - partition_dsts(set, flow_vlan); -} - -static void OVS_UNUSED -print_dsts(const struct dst_set *set) -{ - size_t i; - - for (i = 0; i < set->n; i++) { - const struct dst *dst = &set->dsts[i]; - - printf(">p%"PRIu16, dst->dp_ifidx); - if (dst->vlan != OFP_VLAN_NONE) { - printf("v%"PRIu16, dst->vlan); - } - } } static void @@ -2338,20 +2386,33 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan, tag_type *tags, struct ofpbuf *actions, uint16_t *nf_output_iface) { + uint16_t initial_vlan, cur_vlan; + const struct dst *dst; struct dst_set set; - uint16_t cur_vlan; - size_t i; dst_set_init(&set); compose_dsts(br, flow, vlan, in_port, out_port, &set, tags, nf_output_iface); + compose_mirror_dsts(br, flow, vlan, in_port, &set, tags); - cur_vlan = vlan_tci_to_vid(flow->vlan_tci); - if (cur_vlan == 0) { - cur_vlan = OFP_VLAN_NONE; + /* Output all the packets we can without having to change the VLAN. */ + initial_vlan = vlan_tci_to_vid(flow->vlan_tci); + if (initial_vlan == 0) { + initial_vlan = OFP_VLAN_NONE; + } + for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) { + if (dst->vlan != initial_vlan) { + continue; + } + nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->iface->dp_ifidx); } - for (i = 0; i < set.n; i++) { - const struct dst *dst = &set.dsts[i]; + + /* Then output the rest. */ + cur_vlan = initial_vlan; + for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) { + if (dst->vlan == initial_vlan) { + continue; + } if (dst->vlan != cur_vlan) { if (dst->vlan == OFP_VLAN_NONE) { nl_msg_put_flag(actions, ODP_ACTION_ATTR_STRIP_VLAN); @@ -2363,8 +2424,9 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan, } cur_vlan = dst->vlan; } - nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->dp_ifidx); + nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->iface->dp_ifidx); } + dst_set_free(&set); } @@ -2431,7 +2493,7 @@ update_learning_table(struct bridge *br, const struct flow *flow, int vlan, if (is_gratuitous_arp(flow)) { /* We don't want to learn from gratuitous ARP packets that are * reflected back over bond slaves so we lock the learning table. */ - if (in_port->n_ifaces == 1) { + if (!in_port->bond) { mac_entry_set_grat_arp_lock(mac); } else if (mac_entry_is_grat_arp_locked(mac)) { return; @@ -2626,8 +2688,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; } @@ -2690,21 +2755,90 @@ bridge_account_checkpoint_ofhook_cb(void *br_) } } +static uint16_t +bridge_autopath_ofhook_cb(const struct flow *flow, uint32_t ofp_port, + tag_type *tags, void *br_) +{ + struct bridge *br = br_; + uint16_t odp_port = ofp_port_to_odp_port(ofp_port); + struct port *port = port_from_dp_ifidx(br, odp_port); + uint16_t ret; + + if (!port) { + ret = ODPP_NONE; + } else if (list_is_short(&port->ifaces)) { + ret = odp_port; + } else { + struct iface *iface; + + /* Autopath does not support VLAN hashing. */ + iface = bond_choose_output_slave(port->bond, flow, + OFP_VLAN_NONE, tags); + ret = iface ? iface->dp_ifidx : ODPP_NONE; + } + + return odp_port_to_ofp_port(ret); +} + static struct ofhooks bridge_ofhooks = { bridge_normal_ofhook_cb, bridge_special_ofhook_cb, bridge_account_flow_ofhook_cb, bridge_account_checkpoint_ofhook_cb, + bridge_autopath_ofhook_cb, }; /* 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); } @@ -2714,6 +2848,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); } @@ -2813,12 +2951,12 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg) port->cfg = cfg; - /* Add new interfaces and update 'cfg' member of existing ones. */ sset_init(&new_ifaces); for (i = 0; i < cfg->n_interfaces; i++) { const struct ovsrec_interface *if_cfg = cfg->interfaces[i]; struct iface *iface; + const char *type; if (!sset_add(&new_ifaces, if_cfg->name)) { VLOG_WARN("port %s: %s specified twice as port interface", @@ -2827,8 +2965,18 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg) continue; } + /* Determine interface type. The local port always has type + * "internal". Other ports take their type from the database and + * default to "system" if none is specified. */ + type = (!strcmp(if_cfg->name, port->bridge->name) ? "internal" + : if_cfg->type[0] ? if_cfg->type + : "system"); + iface = iface_lookup(port->bridge, if_cfg->name); - if (iface) { + if (!strcmp(type, "null")) { + iface_destroy(iface); + continue; + } else if (iface) { if (iface->port != port) { VLOG_ERR("bridge %s: %s interface is on multiple ports, " "removing from %s", @@ -2840,19 +2988,14 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg) iface = iface_create(port, if_cfg); } - /* Determine interface type. The local port always has type - * "internal". Other ports take their type from the database and - * default to "system" if none is specified. */ - iface->type = (!strcmp(if_cfg->name, port->bridge->name) ? "internal" - : if_cfg->type[0] ? if_cfg->type - : "system"); + iface->type = type; } sset_destroy(&new_ifaces); /* Get VLAN tag. */ vlan = -1; if (cfg->tag) { - if (port->n_ifaces < 2) { + if (list_is_short(&port->ifaces)) { vlan = *cfg->tag; if (vlan >= 0 && vlan <= 4095) { VLOG_DBG("port %s: assigning VLAN tag %d", port->name, vlan); @@ -2936,6 +3079,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); @@ -2987,51 +3132,106 @@ 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, key; + + portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0")); + priority = atoi(get_interface_other_config(iface->cfg, + "lacp-port-priority", "0")); + key = atoi(get_interface_other_config(iface->cfg, "lacp-aggregation-key", + "0")); + + if (portid <= 0 || portid > UINT16_MAX) { + portid = iface->dp_ifidx; } - 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 (priority <= 0 || priority > UINT16_MAX) { + priority = UINT16_MAX; + } - if (s->priority <= 0 || s->priority > UINT16_MAX) { - /* Prefer bondable links if unspecified. */ - s->priority = UINT16_MAX - (port->n_ifaces > 1); + if (key < 0 || key > UINT16_MAX) { + key = 0; } - return s; + + s.name = iface->name; + s.id = portid; + s.priority = priority; + s.key = key; + 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.heartbeat = !strcmp(get_port_other_config(port->cfg, + "lacp-heartbeat", + "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; - if (port->n_ifaces < 2) { + if (list_is_short(&port->ifaces)) { /* Not a bonded port. */ bond_destroy(port->bond); port->bond = NULL; @@ -3065,6 +3265,8 @@ port_reconfigure_bond(struct port *port) s.up_delay = MAX(0, port->cfg->bond_updelay); s.down_delay = MAX(0, port->cfg->bond_downdelay); + s.basis = atoi(get_port_other_config(port->cfg, "bond-hash-basis", "0")); + s.rebalance_interval = atoi( get_port_other_config(port->cfg, "bond-rebalance-interval", "10000")); if (s.rebalance_interval < 1000) { @@ -3072,16 +3274,27 @@ 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); } else { - bond_reconfigure(port->bond, &s); + if (bond_reconfigure(port->bond, &s)) { + bridge_flush(port->bridge); + } } LIST_FOR_EACH (iface, port_elem, &port->ifaces) { - iface_reconfigure_bond(iface); + long long stable_id; + + stable_id = atoll(get_interface_other_config(iface->cfg, + "bond-stable-id", "0")); + + if (stable_id <= 0 || stable_id >= UINT32_MAX) { + stable_id = odp_port_to_ofp_port(iface->dp_ifidx); + } + + bond_slave_register(iface->port->bond, iface, stable_id, + iface->netdev); } } @@ -3135,7 +3348,6 @@ iface_create(struct port *port, const struct ovsrec_interface *if_cfg) shash_add_assert(&br->iface_by_name, iface->name, iface); list_push_back(&port->ifaces, &iface->port_elem); - port->n_ifaces++; VLOG_DBG("attached network device %s to port %s", iface->name, port->name); @@ -3155,6 +3367,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) { @@ -3162,7 +3378,6 @@ iface_destroy(struct iface *iface) } list_remove(&iface->port_elem); - port->n_ifaces--; netdev_close(iface->netdev); @@ -3215,13 +3430,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) { @@ -3236,7 +3453,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); } } @@ -3396,6 +3613,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. */ @@ -3502,6 +3727,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); @@ -3591,19 +3817,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) { @@ -3688,11 +3901,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;