X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=vswitchd%2Fbridge.c;h=7ec774aca3324e38b98746ef8a1152db837021ae;hb=f35409904bc4a0a695a4aecb2435933d8a8187fe;hp=0236f14cbffbac823a65dd1cbb1074997c451fd9;hpb=5eccf359391f7fe2cdb0edbaaf5680895c115218;p=openvswitch diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 0236f14c..7ec774ac 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "bitmap.h" #include "cfg.h" @@ -129,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. */ @@ -191,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 *); @@ -205,6 +208,8 @@ static uint64_t bridge_pick_datapath_id(struct bridge *, const char *devname); static uint64_t dpid_from_hash(const void *, size_t nbytes); +static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args); + static void bond_init(void); static void bond_run(struct bridge *); static void bond_wait(struct bridge *); @@ -279,6 +284,8 @@ bridge_init(void) bond_init(); + unixctl_command_register("fdb/show", bridge_unixctl_fdb_show); + for (i = 0; i < DP_MAX; i++) { struct dpif dpif; char devname[16]; @@ -298,6 +305,8 @@ bridge_init(void) } } + unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows); + bridge_reconfigure(); } @@ -323,6 +332,7 @@ bridge_configure_ssl(void) static char *private_key_file; static char *certificate_file; static char *cacert_file; + struct stat s; if (config_string_change("ssl.private-key", &private_key_file)) { vconn_ssl_set_private_key_file(private_key_file); @@ -332,7 +342,13 @@ bridge_configure_ssl(void) vconn_ssl_set_certificate_file(certificate_file); } - if (config_string_change("ssl.ca-cert", &cacert_file)) { + /* We assume that even if the filename hasn't changed, if the CA cert + * file has been removed, that we want to move back into + * boot-strapping mode. This opens a small security hole, because + * the old certificate will still be trusted until vSwitch is + * restarted. We may want to address this in vconn's SSL library. */ + if (config_string_change("ssl.ca-cert", &cacert_file) + || (stat(cacert_file, &s) && errno == ENOENT)) { vconn_ssl_set_ca_cert_file(cacert_file, cfg_get_bool(0, "ssl.bootstrap-ca-cert")); } @@ -445,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", @@ -601,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); @@ -780,6 +854,35 @@ bridge_flush(struct bridge *br) } } +/* Bridge unixctl user interface functions. */ +static void +bridge_unixctl_fdb_show(struct unixctl_conn *conn, const char *args) +{ + struct ds ds = DS_EMPTY_INITIALIZER; + const struct bridge *br; + + br = bridge_lookup(args); + if (!br) { + unixctl_command_reply(conn, 501, "no such bridge"); + return; + } + + ds_put_cstr(&ds, " port VLAN MAC Age\n"); + 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", + 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)); + ds_destroy(&ds); +} + /* Bridge reconfiguration functions. */ static struct bridge * @@ -885,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) { @@ -1068,10 +1192,15 @@ bridge_reconfigure_controller(struct bridge *br) int rate_limit, burst_limit; if (!strcmp(controller, "discover")) { + bool update_resolv_conf = true; + + if (cfg_has("%s.update-resolv.conf", pfx)) { + update_resolv_conf = cfg_get_bool(0, "%s.update-resolv.conf", + pfx); + } ofproto_set_discovery(br->ofproto, true, cfg_get_string(0, "%s.accept-regex", pfx), - cfg_get_bool(0, "%s.update-resolv.conf", - pfx)); + update_resolv_conf); } else { struct netdev *netdev; bool in_band; @@ -1123,14 +1252,19 @@ bridge_reconfigure_controller(struct bridge *br) || !strcmp(fail_mode, "open"))); probe = cfg_get_int(0, "%s.inactivity-probe", pfx); - ofproto_set_probe_interval(br->ofproto, - probe ? probe : cfg_get_int(0, "mgmt.inactivity-probe")); + if (probe < 5) { + probe = cfg_get_int(0, "mgmt.inactivity-probe"); + if (probe < 5) { + probe = 5; + } + } + ofproto_set_probe_interval(br->ofproto, probe); max_backoff = cfg_get_int(0, "%s.max-backoff", pfx); 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); @@ -1201,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 @@ -1298,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]; @@ -1323,6 +1461,10 @@ bond_link_status_update(struct iface *iface, bool carrier) iface->delay_expires = LLONG_MAX; VLOG_INFO_RL(&rl, "interface %s: will not be %s", iface->name, carrier ? "disabled" : "enabled"); + } else if (carrier && port->updelay && port->active_iface < 0) { + iface->delay_expires = time_msec(); + VLOG_INFO_RL(&rl, "interface %s: skipping %d ms updelay since no " + "other interface is up", iface->name, port->updelay); } else { int delay = carrier ? port->updelay : port->downdelay; iface->delay_expires = time_msec() + delay; @@ -1366,7 +1508,7 @@ bond_enable_slave(struct iface *iface, bool enable) iface->enabled = enable; if (!iface->enabled) { - VLOG_WARN("interface %s: enabled", iface->name); + VLOG_WARN("interface %s: disabled", iface->name); ofproto_revalidate(br->ofproto, iface->tag); if (iface->port_ifidx == port->active_iface) { ofproto_revalidate(br->ofproto, @@ -1375,7 +1517,7 @@ bond_enable_slave(struct iface *iface, bool enable) } bond_send_learning_packets(port); } else { - VLOG_WARN("interface %s: disabled", iface->name); + VLOG_WARN("interface %s: enabled", iface->name); if (port->active_iface < 0) { ofproto_revalidate(br->ofproto, port->no_ifaces_tag); bond_choose_active_iface(port); @@ -1392,6 +1534,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; } @@ -2174,6 +2322,7 @@ bond_rebalance_port(struct port *port) } else { from++; } + port->bond_compat_is_stale = true; } } @@ -2415,8 +2564,8 @@ bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_) return; } - if (sscanf(hash_s, "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8, - &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) { + if (sscanf(hash_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac)) + == ETH_ADDR_SCAN_COUNT) { hash = bond_hash(mac); } else if (strspn(hash_s, "0123456789") == strlen(hash_s)) { hash = atoi(hash_s) & BOND_MASK; @@ -2440,6 +2589,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"); } @@ -2704,6 +2854,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]; @@ -2770,7 +2921,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) { @@ -2785,23 +2936,39 @@ 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++) { @@ -2815,6 +2982,7 @@ port_update_bond_compat(struct port *port) } memcpy(slave->mac, iface->mac, ETH_ADDR_LEN); } + proc_net_compat_update_bond(port->name, &bond); free(bond.slaves); } @@ -2870,8 +3038,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,