X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=vswitchd%2Fbridge.c;h=f5918be0332940f12de56c0f8a9ddf0ee86b5415;hb=cdcf42c664eeb6b260693fc182c1782669f7d39e;hp=59100ccee7a7d4f3596eb202073d489acaa9af2c;hpb=62f3aaedacdd1369e2cd65a01b29c3034735d53b;p=openvswitch diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 59100cce..f5918be0 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -147,6 +147,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; @@ -196,10 +198,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 +241,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 *); @@ -339,6 +343,7 @@ bridge_init(const char *remote) NULL); unixctl_command_register("bridge/reconnect", bridge_unixctl_reconnect, NULL); + lacp_init(); bond_init(); } @@ -439,9 +444,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; } @@ -882,6 +885,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) { @@ -1227,6 +1231,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) { @@ -1415,7 +1440,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 +1453,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 +1484,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); } } @@ -2630,8 +2656,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; } @@ -2694,21 +2723,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); } @@ -2718,6 +2816,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); } @@ -2940,6 +3042,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); @@ -2991,46 +3095,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; - 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"); + portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0")); + priority = atoi(get_interface_other_config(iface->cfg, + "lacp-port-priority", "0")); - if (s->priority <= 0 || s->priority > UINT16_MAX) { - /* Prefer bondable links if unspecified. */ - s->priority = UINT16_MAX - !list_is_short(&port->ifaces); + if (portid <= 0 || portid > UINT16_MAX) { + portid = iface->dp_ifidx; } - return s; + + if (priority <= 0 || priority > UINT16_MAX) { + priority = UINT16_MAX; + } + + 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; @@ -3076,16 +3228,21 @@ 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); + 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); } } @@ -3158,6 +3315,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) { @@ -3217,13 +3378,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) { @@ -3504,6 +3667,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); @@ -3593,19 +3757,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) { @@ -3690,11 +3841,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;