X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=vswitchd%2Fbridge.c;h=c39670a3b2a0d90f01a470e26cf8fa40479a9fd4;hb=2e8873af89047bc4093ad8f2755867af632b9363;hp=b0b2a8f11ab755dac8e669b3c3b45b6e8b3bbf74;hpb=8c4c1387fdebb4b650a093340842ce2226e118af;p=openvswitch diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index b0b2a8f1..c39670a3 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -130,6 +130,7 @@ struct port { tag_type active_iface_tag; /* Tag for bcast flows. */ tag_type no_ifaces_tag; /* Tag for flows when all ifaces disabled. */ int updelay, downdelay; /* Delay before iface goes up/down, in ms. */ + bool bond_compat_is_stale; /* Need to call port_update_bond_compat()? */ /* Port mirroring info. */ mirror_mask_t src_mirrors; /* Mirrors triggered when packet received. */ @@ -192,6 +193,7 @@ enum { DP_MAX = 256 }; static struct bridge *bridge_create(const char *name); static void bridge_destroy(struct bridge *); static struct bridge *bridge_lookup(const char *name); +static void bridge_unixctl_dump_flows(struct unixctl_conn *, const char *); static int bridge_run_one(struct bridge *); static void bridge_reconfigure_one(struct bridge *); static void bridge_reconfigure_controller(struct bridge *); @@ -303,6 +305,8 @@ bridge_init(void) } } + unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows); + bridge_reconfigure(); } @@ -457,9 +461,23 @@ bridge_reconfigure(void) for (i = 0; i < add_ifaces.n; i++) { const char *if_name = add_ifaces.names[i]; for (;;) { - int internal = cfg_get_bool(0, "iface.%s.internal", if_name); - int error = dpif_port_add(&br->dpif, if_name, next_port_no++, - internal ? ODP_PORT_INTERNAL : 0); + bool internal; + int error; + + /* It's an internal interface if it's marked that way, or if + * it's a bonded interface for which we're faking up a network + * device. */ + internal = cfg_get_bool(0, "iface.%s.internal", if_name); + if (cfg_get_bool(0, "bonding.%s.fake-iface", if_name)) { + struct port *port = port_lookup(br, if_name); + if (port && port->n_ifaces > 1) { + internal = true; + } + } + + /* Add to datapath. */ + error = dpif_port_add(&br->dpif, if_name, next_port_no++, + internal ? ODP_PORT_INTERNAL : 0); if (error != EEXIST) { if (next_port_no >= 256) { VLOG_ERR("ran out of valid port numbers on dp%u", @@ -613,31 +631,75 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN], memset(ea, 0xff, sizeof ea); for (i = 0; i < br->n_ports; i++) { struct port *port = br->ports[i]; + uint8_t iface_ea[ETH_ADDR_LEN]; + uint64_t iface_ea_u64; + struct iface *iface; + + /* Mirror output ports don't participate. */ if (port->is_mirror_output_port) { continue; } - for (j = 0; j < port->n_ifaces; j++) { - struct iface *iface = port->ifaces[j]; - uint8_t iface_ea[ETH_ADDR_LEN]; + + /* Choose the MAC address to represent the port. */ + iface_ea_u64 = cfg_get_mac(0, "port.%s.mac", port->name); + if (iface_ea_u64) { + /* User specified explicitly. */ + eth_addr_from_uint64(iface_ea_u64, iface_ea); + + /* Find the interface with this Ethernet address (if any) so that + * we can provide the correct devname to the caller. */ + iface = NULL; + for (j = 0; j < port->n_ifaces; j++) { + struct iface *candidate = port->ifaces[j]; + uint8_t candidate_ea[ETH_ADDR_LEN]; + if (!netdev_nodev_get_etheraddr(candidate->name, candidate_ea) + && eth_addr_equals(iface_ea, candidate_ea)) { + iface = candidate; + } + } + } else { + /* Choose the interface whose MAC address will represent the port. + * The Linux kernel bonding code always chooses the MAC address of + * the first slave added to a bond, and the Fedora networking + * scripts always add slaves to a bond in alphabetical order, so + * for compatibility we choose the interface with the name that is + * first in alphabetical order. */ + iface = port->ifaces[0]; + for (j = 1; j < port->n_ifaces; j++) { + struct iface *candidate = port->ifaces[j]; + if (strcmp(candidate->name, iface->name) < 0) { + iface = candidate; + } + } + + /* The local port doesn't count (since we're trying to choose its + * MAC address anyway). Other internal ports don't count because + * we really want a physical MAC if we can get it, and internal + * ports typically have randomly generated MACs. */ if (iface->dp_ifidx == ODPP_LOCAL || cfg_get_bool(0, "iface.%s.internal", iface->name)) { continue; } + + /* Grab MAC. */ error = netdev_nodev_get_etheraddr(iface->name, iface_ea); - if (!error) { - if (!eth_addr_is_multicast(iface_ea) && - !eth_addr_is_reserved(iface_ea) && - !eth_addr_is_zero(iface_ea) && - memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0) { - memcpy(ea, iface_ea, ETH_ADDR_LEN); - *devname = iface->name; - } - } else { + if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_ERR_RL(&rl, "failed to obtain Ethernet address of %s: %s", iface->name, strerror(error)); + continue; } } + + /* Compare against our current choice. */ + if (!eth_addr_is_multicast(iface_ea) && + !eth_addr_is_reserved(iface_ea) && + !eth_addr_is_zero(iface_ea) && + memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0) + { + memcpy(ea, iface_ea, ETH_ADDR_LEN); + *devname = iface ? iface->name : NULL; + } } if (eth_addr_is_multicast(ea) || eth_addr_is_vif(ea)) { memcpy(ea, br->default_ea, ETH_ADDR_LEN); @@ -809,9 +871,12 @@ bridge_unixctl_fdb_show(struct unixctl_conn *conn, const char *args) if (br->ml) { const struct mac_entry *e; LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) { + if (e->port < 0 || e->port >= br->n_ports) { + continue; + } ds_put_format(&ds, "%5d %4d "ETH_ADDR_FMT" %3d\n", - e->port, e->vlan, ETH_ADDR_ARGS(e->mac), - mac_entry_age(e)); + br->ports[e->port]->ifaces[0]->dp_ifidx, + e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e)); } } unixctl_command_reply(conn, 200, ds_cstr(&ds)); @@ -923,6 +988,27 @@ bridge_get_datapathid(const char *name) return br ? ofproto_get_datapath_id(br->ofproto) : 0; } +/* Handle requests for a listing of all flows known by the OpenFlow + * stack, including those normally hidden. */ +static void +bridge_unixctl_dump_flows(struct unixctl_conn *conn, const char *args) +{ + struct bridge *br; + struct ds results; + + br = bridge_lookup(args); + if (!br) { + unixctl_command_reply(conn, 501, "Unknown bridge"); + return; + } + + ds_init(&results); + ofproto_get_all_flows(br->ofproto, &results); + + unixctl_command_reply(conn, 200, ds_cstr(&results)); + ds_destroy(&results); +} + static int bridge_run_one(struct bridge *br) { @@ -1169,7 +1255,7 @@ bridge_reconfigure_controller(struct bridge *br) if (probe < 5) { probe = cfg_get_int(0, "mgmt.inactivity-probe"); if (probe < 5) { - probe = 15; + probe = 5; } } ofproto_set_probe_interval(br->ofproto, probe); @@ -1178,7 +1264,7 @@ bridge_reconfigure_controller(struct bridge *br) if (!max_backoff) { max_backoff = cfg_get_int(0, "mgmt.max-backoff"); if (!max_backoff) { - max_backoff = 15; + max_backoff = 8; } } ofproto_set_max_backoff(br->ofproto, max_backoff); @@ -1249,9 +1335,12 @@ bridge_get_all_ifaces(const struct bridge *br, struct svec *ifaces) struct iface *iface = port->ifaces[j]; svec_add(ifaces, iface->name); } + if (port->n_ifaces > 1 + && cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) { + svec_add(ifaces, port->name); + } } - svec_sort(ifaces); - assert(svec_is_unique(ifaces)); + svec_sort_unique(ifaces); } /* For robustness, in case the administrator moves around datapath ports behind @@ -1346,6 +1435,7 @@ choose_output_iface(const struct port *port, const uint8_t *dl_src, return false; } e->iface_tag = tag_create_random(); + ((struct port *) port)->bond_compat_is_stale = true; } *tags |= e->iface_tag; iface = port->ifaces[e->iface_idx]; @@ -1435,6 +1525,7 @@ bond_enable_slave(struct iface *iface, bool enable) } iface->tag = tag_create_random(); } + port_update_bond_compat(port); } static void @@ -1444,6 +1535,12 @@ bond_run(struct bridge *br) for (i = 0; i < br->n_ports; i++) { struct port *port = br->ports[i]; + + if (port->bond_compat_is_stale) { + port->bond_compat_is_stale = false; + port_update_bond_compat(port); + } + if (port->n_ifaces < 2) { continue; } @@ -1614,12 +1711,14 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan, for (i = 0; i < br->n_ports; i++) { struct port *port = br->ports[i]; if (port_includes_vlan(port, m->out_vlan) - && set_dst(dst, flow, in_port, port, tags) - && !dst_is_duplicate(dsts, dst - dsts, dst)) + && set_dst(dst, flow, in_port, port, tags)) { if (port->vlan < 0) { dst->vlan = m->out_vlan; } + if (dst_is_duplicate(dsts, dst - dsts, dst)) { + continue; + } if (dst->dp_ifidx == flow->in_port && dst->vlan == vlan) { /* Don't send out input port on same VLAN. */ @@ -1793,7 +1892,7 @@ process_flow(struct bridge *br, const flow_t *flow, goto done; } else { /* Drop all multicast packets for which we have learned a different - * input port, because we probably sent the packet on one slaves + * input port, because we probably sent the packet on one slave * and got it back on the active slave. Broadcast ARP replies are * an exception to this rule: the host has moved to another * switch. */ @@ -2226,6 +2325,7 @@ bond_rebalance_port(struct port *port) } else { from++; } + port->bond_compat_is_stale = true; } } @@ -2492,6 +2592,7 @@ bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_) ofproto_revalidate(port->bridge->ofproto, entry->iface_tag); entry->iface_idx = iface->port_ifidx; entry->iface_tag = tag_create_random(); + port->bond_compat_is_stale = true; unixctl_command_reply(conn, 200, "migrated"); } @@ -2756,6 +2857,7 @@ port_destroy(struct port *port) size_t i; proc_net_compat_update_vlan(port->name, NULL, 0); + proc_net_compat_update_bond(port->name, NULL); for (i = 0; i < MAX_MIRRORS; i++) { struct mirror *m = br->mirrors[i]; @@ -2822,7 +2924,7 @@ port_update_bonding(struct port *port) if (port->bond_hash) { free(port->bond_hash); port->bond_hash = NULL; - proc_net_compat_update_bond(port->name, NULL); + port->bond_compat_is_stale = true; } } else { if (!port->bond_hash) { @@ -2837,36 +2939,52 @@ port_update_bonding(struct port *port) port->no_ifaces_tag = tag_create_random(); bond_choose_active_iface(port); } - port_update_bond_compat(port); + port->bond_compat_is_stale = true; } } static void port_update_bond_compat(struct port *port) { + struct compat_bond_hash compat_hashes[BOND_MASK + 1]; struct compat_bond bond; size_t i; if (port->n_ifaces < 2) { + proc_net_compat_update_bond(port->name, NULL); return; } bond.up = false; bond.updelay = port->updelay; bond.downdelay = port->downdelay; + + bond.n_hashes = 0; + bond.hashes = compat_hashes; + if (port->bond_hash) { + const struct bond_entry *e; + for (e = port->bond_hash; e <= &port->bond_hash[BOND_MASK]; e++) { + if (e->iface_idx >= 0 && e->iface_idx < port->n_ifaces) { + struct compat_bond_hash *cbh = &bond.hashes[bond.n_hashes++]; + cbh->hash = e - port->bond_hash; + cbh->netdev_name = port->ifaces[e->iface_idx]->name; + } + } + } + bond.n_slaves = port->n_ifaces; bond.slaves = xmalloc(port->n_ifaces * sizeof *bond.slaves); for (i = 0; i < port->n_ifaces; i++) { struct iface *iface = port->ifaces[i]; struct compat_bond_slave *slave = &bond.slaves[i]; slave->name = iface->name; - slave->up = ((iface->enabled && iface->delay_expires == LLONG_MAX) || - (!iface->enabled && iface->delay_expires != LLONG_MAX)); + slave->up = iface->enabled; if (slave->up) { bond.up = true; } memcpy(slave->mac, iface->mac, ETH_ADDR_LEN); } + proc_net_compat_update_bond(port->name, &bond); free(bond.slaves); } @@ -2922,8 +3040,17 @@ iface_create(struct port *port, const char *name) iface->tag = tag_create_random(); iface->delay_expires = LLONG_MAX; - netdev_nodev_get_etheraddr(name, iface->mac); - netdev_nodev_get_carrier(name, &iface->enabled); + if (!cfg_get_bool(0, "iface.%s.internal", iface->name)) { + netdev_nodev_get_etheraddr(name, iface->mac); + netdev_nodev_get_carrier(name, &iface->enabled); + } else { + /* Internal interfaces are created later by the call to dpif_port_add() + * in bridge_reconfigure(). Until then, we can't obtain any + * information about them. (There's no real value in doing so, anyway, + * because the 'mac' and 'enabled' values are only used for interfaces + * that are bond slaves, and it doesn't normally make sense to bond an + * internal interface.) */ + } if (port->n_ifaces >= port->allocated_ifaces) { port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces, @@ -3192,6 +3319,7 @@ mirror_reconfigure_one(struct mirror *m) int *vlans; size_t i; bool mirror_all_ports; + bool any_ports_specified; /* Get output port. */ out_port_name = cfg_get_key(0, "mirror.%s.%s.output.port", @@ -3230,11 +3358,18 @@ mirror_reconfigure_one(struct mirror *m) cfg_get_all_keys(&src_ports, "%s.select.src-port", pfx); cfg_get_all_keys(&dst_ports, "%s.select.dst-port", pfx); cfg_get_all_keys(&ports, "%s.select.port", pfx); + any_ports_specified = src_ports.n || dst_ports.n || ports.n; svec_append(&src_ports, &ports); svec_append(&dst_ports, &ports); svec_destroy(&ports); prune_ports(m, &src_ports); prune_ports(m, &dst_ports); + if (any_ports_specified && !src_ports.n && !dst_ports.n) { + VLOG_ERR("%s: none of the specified ports exist; " + "disabling port mirror %s", pfx, pfx); + mirror_destroy(m); + goto exit; + } /* Get all the vlans, and drop duplicate and invalid vlans. */ svec_init(&vlan_strings); @@ -3286,6 +3421,7 @@ mirror_reconfigure_one(struct mirror *m) } /* Clean up. */ +exit: svec_destroy(&src_ports); svec_destroy(&dst_ports); free(pfx);