X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=vswitchd%2Fbridge.c;h=6449333de863bc52678987fbd0257738cc5d8a75;hb=94e0c3ff3c096456bddfe33ac8d6540be17bfe8e;hp=d085f47b151123b54c92a209e59be4cf6d6d3357;hpb=4bee05c217e365bce67878497db6f83f055f0cff;p=sliver-openvswitch.git diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index d085f47b1..6449333de 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks +/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,9 +28,12 @@ #include "dynamic-string.h" #include "hash.h" #include "hmap.h" +#include "hmapx.h" #include "jsonrpc.h" #include "lacp.h" #include "list.h" +#include "mac-learning.h" +#include "meta-flow.h" #include "netdev.h" #include "ofp-print.h" #include "ofpbuf.h" @@ -39,6 +42,7 @@ #include "sha1.h" #include "shash.h" #include "socket-util.h" +#include "stream.h" #include "stream-ssl.h" #include "sset.h" #include "system-stats.h" @@ -46,7 +50,7 @@ #include "util.h" #include "unixctl.h" #include "vlandev.h" -#include "vswitchd/vswitch-idl.h" +#include "vswitch-idl.h" #include "xenserver.h" #include "vlog.h" #include "sflow_api.h" @@ -154,8 +158,10 @@ static void bridge_configure_datapath_id(struct bridge *); static void bridge_configure_flow_eviction_threshold(struct bridge *); static void bridge_configure_netflow(struct bridge *); static void bridge_configure_forward_bpdu(struct bridge *); +static void bridge_configure_mac_idle_time(struct bridge *); static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number); static void bridge_configure_stp(struct bridge *); +static void bridge_configure_tables(struct bridge *); static void bridge_configure_remotes(struct bridge *, const struct sockaddr_in *managers, size_t n_managers); @@ -279,6 +285,7 @@ bridge_init(const char *remote) ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault); + ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_mpids); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_lacp_current); ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids); @@ -308,14 +315,16 @@ bridge_init(const char *remote) ovsdb_idl_omit(idl, &ovsrec_ssl_col_external_ids); /* Register unixctl commands. */ - unixctl_command_register("qos/show", "interface", qos_unixctl_show, NULL); - unixctl_command_register("bridge/dump-flows", "bridge", + unixctl_command_register("qos/show", "interface", 1, 1, + qos_unixctl_show, NULL); + unixctl_command_register("bridge/dump-flows", "bridge", 1, 1, bridge_unixctl_dump_flows, NULL); - unixctl_command_register("bridge/reconnect", "[bridge]", + unixctl_command_register("bridge/reconnect", "[bridge]", 0, 1, bridge_unixctl_reconnect, NULL); lacp_init(); bond_init(); cfm_init(); + stp_init(); } void @@ -371,10 +380,10 @@ collect_in_band_managers(const struct ovsrec_open_vswitch *ovs_cfg, SSET_FOR_EACH (target, &targets) { struct sockaddr_in *sin = &managers[n_managers]; - if ((!strncmp(target, "tcp:", 4) - && inet_parse_active(target + 4, JSONRPC_TCP_PORT, sin)) || - (!strncmp(target, "ssl:", 4) - && inet_parse_active(target + 4, JSONRPC_SSL_PORT, sin))) { + if (stream_parse_target_with_default_ports(target, + JSONRPC_TCP_PORT, + JSONRPC_SSL_PORT, + sin)) { n_managers++; } } @@ -429,8 +438,12 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) * has at least one iface, every "struct iface" has a valid ofp_port and * netdev. */ HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) { - if (!br->ofproto && !bridge_add_ofprotos(br)) { - bridge_destroy(br); + if (!br->ofproto) { + if (bridge_add_ofprotos(br)) { + bridge_del_ofproto_ports(br); + } else { + bridge_destroy(br); + } } } HMAP_FOR_EACH (br, node, &all_bridges) { @@ -444,6 +457,10 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) HMAP_FOR_EACH (br, node, &all_bridges) { struct port *port; + /* We need the datapath ID early to allow LACP ports to use it as the + * default system ID. */ + bridge_configure_datapath_id(br); + HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct iface *iface; @@ -456,13 +473,14 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) } } bridge_configure_mirrors(br); - bridge_configure_datapath_id(br); bridge_configure_flow_eviction_threshold(br); bridge_configure_forward_bpdu(br); + bridge_configure_mac_idle_time(br); bridge_configure_remotes(br, managers, n_managers); bridge_configure_netflow(br); bridge_configure_sflow(br, &sflow_bridge_number); bridge_configure_stp(br); + bridge_configure_tables(br); } free(managers); @@ -535,17 +553,8 @@ port_configure(struct port *port) /* Get VLAN tag. */ s.vlan = -1; - if (cfg->tag) { - if (list_is_short(&port->ifaces)) { - if (*cfg->tag >= 0 && *cfg->tag <= 4095) { - s.vlan = *cfg->tag; - } - } else { - /* It's possible that bonded, VLAN-tagged ports make sense. Maybe - * they even work as-is. But they have not been tested. */ - VLOG_WARN("port %s: VLAN tags not supported on bonded ports", - port->name); - } + if (cfg->tag && *cfg->tag >= 0 && *cfg->tag <= 4095) { + s.vlan = *cfg->tag; } /* Get VLAN trunks. */ @@ -837,7 +846,7 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port, if (config_str) { port_s->path_cost = strtoul(config_str, NULL, 10); } else { - uint32_t current; + enum netdev_features current; if (netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL)) { /* Couldn't get speed, so assume 100Mb/s. */ @@ -1274,14 +1283,30 @@ bridge_configure_forward_bpdu(struct bridge *br) ofproto_set_forward_bpdu(br->ofproto, forward_bpdu); } +/* Set MAC aging time for 'br'. */ +static void +bridge_configure_mac_idle_time(struct bridge *br) +{ + const char *idle_time_str; + int idle_time; + + idle_time_str = bridge_get_other_config(br->cfg, "mac-aging-time"); + idle_time = (idle_time_str && atoi(idle_time_str) + ? atoi(idle_time_str) + : MAC_ENTRY_DEFAULT_IDLE_TIME); + ofproto_set_mac_idle_time(br->ofproto, idle_time); +} + static void bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN], struct iface **hw_addr_iface) { + struct hmapx mirror_output_ports; const char *hwaddr; struct port *port; bool found_addr = false; int error; + int i; *hw_addr_iface = NULL; @@ -1298,6 +1323,18 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN], } } + /* Mirror output ports don't participate in picking the local hardware + * address. ofproto can't help us find out whether a given port is a + * mirror output because we haven't configured mirrors yet, so we need to + * accumulate them ourselves. */ + hmapx_init(&mirror_output_ports); + for (i = 0; i < br->cfg->n_mirrors; i++) { + struct ovsrec_mirror *m = br->cfg->mirrors[i]; + if (m->output_port) { + hmapx_add(&mirror_output_ports, m->output_port); + } + } + /* Otherwise choose the minimum non-local MAC address among all of the * interfaces. */ HMAP_FOR_EACH (port, hmap_node, &br->ports) { @@ -1306,7 +1343,7 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN], struct iface *iface; /* Mirror output ports don't participate. */ - if (ofproto_is_mirror_output_bundle(br->ofproto, port)) { + if (hmapx_contains(&mirror_output_ports, port->cfg)) { continue; } @@ -1369,6 +1406,8 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN], VLOG_WARN("bridge %s: using default bridge Ethernet " "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea)); } + + hmapx_destroy(&mirror_output_ports); } /* Choose and returns the datapath ID for bridge 'br' given that the bridge @@ -1444,8 +1483,8 @@ iface_refresh_status(struct iface *iface) { struct shash sh; + enum netdev_features current; enum netdev_flags flags; - uint32_t current; int64_t bps; int mtu; int64_t mtu_64; @@ -1520,10 +1559,23 @@ iface_refresh_cfm_stats(struct iface *iface) fault = ofproto_port_get_cfm_fault(iface->port->bridge->ofproto, iface->ofp_port); if (fault >= 0) { + const char *reasons[CFM_FAULT_N_REASONS]; bool fault_bool = fault; + size_t i, j; + + j = 0; + for (i = 0; i < CFM_FAULT_N_REASONS; i++) { + int reason = 1 << i; + if (fault & reason) { + reasons[j++] = cfm_fault_reason_to_str(reason); + } + } + ovsrec_interface_set_cfm_fault(cfg, &fault_bool, 1); + ovsrec_interface_set_cfm_fault_status(cfg, (char **) reasons, j); } else { ovsrec_interface_set_cfm_fault(cfg, NULL, 0); + ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0); } error = ofproto_port_get_cfm_remote_mpids(iface->port->bridge->ofproto, @@ -2023,8 +2075,8 @@ qos_unixctl_show_cb(unsigned int queue_id, } static void -qos_unixctl_show(struct unixctl_conn *conn, - const char *args, void *aux OVS_UNUSED) +qos_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct shash sh = SHASH_INITIALIZER(&sh); @@ -2034,9 +2086,9 @@ qos_unixctl_show(struct unixctl_conn *conn, struct qos_unixctl_show_cbdata data; int error; - iface = iface_find(args); + iface = iface_find(argv[1]); if (!iface) { - unixctl_command_reply(conn, 501, "no such interface"); + unixctl_command_reply_error(conn, "no such interface"); return; } @@ -2056,10 +2108,10 @@ qos_unixctl_show(struct unixctl_conn *conn, if (error) { ds_put_format(&ds, "failed to dump queues: %s", strerror(error)); } - unixctl_command_reply(conn, 200, ds_cstr(&ds)); + unixctl_command_reply(conn, ds_cstr(&ds)); } else { ds_put_format(&ds, "QoS not configured on %s\n", iface->name); - unixctl_command_reply(conn, 501, ds_cstr(&ds)); + unixctl_command_reply_error(conn, ds_cstr(&ds)); } shash_destroy_free_data(&sh); @@ -2133,22 +2185,22 @@ bridge_lookup(const char *name) /* 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, void *aux OVS_UNUSED) +bridge_unixctl_dump_flows(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *aux OVS_UNUSED) { struct bridge *br; struct ds results; - br = bridge_lookup(args); + br = bridge_lookup(argv[1]); if (!br) { - unixctl_command_reply(conn, 501, "Unknown bridge"); + unixctl_command_reply_error(conn, "Unknown bridge"); return; } ds_init(&results); ofproto_get_all_flows(br->ofproto, &results); - unixctl_command_reply(conn, 200, ds_cstr(&results)); + unixctl_command_reply(conn, ds_cstr(&results)); ds_destroy(&results); } @@ -2156,14 +2208,14 @@ bridge_unixctl_dump_flows(struct unixctl_conn *conn, * connections and reconnect. If BRIDGE is not specified, then all bridges * drop their controller connections and reconnect. */ static void -bridge_unixctl_reconnect(struct unixctl_conn *conn, - const char *args, void *aux OVS_UNUSED) +bridge_unixctl_reconnect(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) { struct bridge *br; - if (args[0] != '\0') { - br = bridge_lookup(args); + if (argc > 1) { + br = bridge_lookup(argv[1]); if (!br) { - unixctl_command_reply(conn, 501, "Unknown bridge"); + unixctl_command_reply_error(conn, "Unknown bridge"); return; } ofproto_reconnect_controllers(br->ofproto); @@ -2172,7 +2224,7 @@ bridge_unixctl_reconnect(struct unixctl_conn *conn, ofproto_reconnect_controllers(br->ofproto); } } - unixctl_command_reply(conn, 200, NULL); + unixctl_command_reply(conn, NULL); } static size_t @@ -2280,6 +2332,7 @@ bridge_ofproto_controller_for_mgmt(const struct bridge *br, oc->band = OFPROTO_OUT_OF_BAND; oc->rate_limit = 0; oc->burst_limit = 0; + oc->enable_async_msgs = true; } /* Converts ovsrec_controller 'c' into an ofproto_controller in 'oc'. */ @@ -2295,6 +2348,8 @@ bridge_ofproto_controller_from_ovsrec(const struct ovsrec_controller *c, oc->rate_limit = c->controller_rate_limit ? *c->controller_rate_limit : 0; oc->burst_limit = (c->controller_burst_limit ? *c->controller_burst_limit : 0); + oc->enable_async_msgs = (!c->enable_async_messages + || *c->enable_async_messages); } /* Configures the IP stack for 'br''s local interface properly according to the @@ -2459,6 +2514,66 @@ bridge_configure_remotes(struct bridge *br, sset_destroy(&snoops); } } + +static void +bridge_configure_tables(struct bridge *br) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + int n_tables; + int i, j; + + n_tables = ofproto_get_n_tables(br->ofproto); + j = 0; + for (i = 0; i < n_tables; i++) { + struct ofproto_table_settings s; + + s.name = NULL; + s.max_flows = UINT_MAX; + s.groups = NULL; + s.n_groups = 0; + + if (j < br->cfg->n_flow_tables && i == br->cfg->key_flow_tables[j]) { + struct ovsrec_flow_table *cfg = br->cfg->value_flow_tables[j++]; + + s.name = cfg->name; + if (cfg->n_flow_limit && *cfg->flow_limit < UINT_MAX) { + s.max_flows = *cfg->flow_limit; + } + if (cfg->overflow_policy + && !strcmp(cfg->overflow_policy, "evict")) { + size_t k; + + s.groups = xmalloc(cfg->n_groups * sizeof *s.groups); + for (k = 0; k < cfg->n_groups; k++) { + const char *string = cfg->groups[k]; + char *msg; + + msg = mf_parse_subfield__(&s.groups[k], &string); + if (msg) { + VLOG_WARN_RL(&rl, "bridge %s table %d: error parsing " + "'groups' (%s)", br->name, i, msg); + free(msg); + } else if (*string) { + VLOG_WARN_RL(&rl, "bridge %s table %d: 'groups' " + "element '%s' contains trailing garbage", + br->name, i, cfg->groups[k]); + } else { + s.n_groups++; + } + } + } + } + + ofproto_configure_table(br->ofproto, i, &s); + + free(s.groups); + } + for (; j < br->cfg->n_flow_tables; j++) { + VLOG_WARN_RL(&rl, "bridge %s: ignoring configuration for flow table " + "%"PRId64" not supported by this datapath", br->name, + br->cfg->key_flow_tables[j]); + } +} /* Port functions. */ @@ -2640,7 +2755,7 @@ enable_lacp(struct port *port, bool *activep) static struct lacp_settings * port_configure_lacp(struct port *port, struct lacp_settings *s) { - const char *lacp_time; + const char *lacp_time, *system_id; long long int custom_time; int priority; @@ -2649,7 +2764,23 @@ port_configure_lacp(struct port *port, struct lacp_settings *s) } s->name = port->name; - memcpy(s->id, port->bridge->ea, ETH_ADDR_LEN); + + system_id = get_port_other_config(port->cfg, "lacp-system-id", NULL); + if (system_id) { + if (sscanf(system_id, ETH_ADDR_SCAN_FMT, + ETH_ADDR_SCAN_ARGS(s->id)) != ETH_ADDR_SCAN_COUNT) { + VLOG_WARN("port %s: LACP system ID (%s) must be an Ethernet" + " address.", port->name, system_id); + return NULL; + } + } else { + memcpy(s->id, port->bridge->ea, ETH_ADDR_LEN); + } + + if (eth_addr_is_zero(s->id)) { + VLOG_WARN("port %s: Invalid zero LACP system ID.", port->name); + return NULL; + } /* Prefer bondable links if unspecified. */ priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority", @@ -2717,12 +2848,22 @@ port_configure_bond(struct port *port, struct bond_settings *s, size_t i; s->name = port->name; - s->balance = BM_SLB; - if (port->cfg->bond_mode - && !bond_mode_from_string(&s->balance, port->cfg->bond_mode)) { - VLOG_WARN("port %s: unknown bond_mode %s, defaulting to %s", - port->name, port->cfg->bond_mode, - bond_mode_to_string(s->balance)); + s->balance = BM_AB; + if (port->cfg->bond_mode) { + if (!bond_mode_from_string(&s->balance, port->cfg->bond_mode)) { + VLOG_WARN("port %s: unknown bond_mode %s, defaulting to %s", + port->name, port->cfg->bond_mode, + bond_mode_to_string(s->balance)); + } + } else { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + + /* XXX: Post version 1.5.*, the default bond_mode changed from SLB to + * active-backup. At some point we should remove this warning. */ + VLOG_WARN_RL(&rl, "port %s: Using the default bond_mode %s. Note that" + " in previous versions, the default bond_mode was" + " balance-slb", port->name, + bond_mode_to_string(s->balance)); } if (s->balance == BM_SLB && port->bridge->cfg->n_flood_vlans) { VLOG_WARN("port %s: SLB bonds are incompatible with flood_vlans, " @@ -2750,7 +2891,7 @@ port_configure_bond(struct port *port, struct bond_settings *s, 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) { + if (s->rebalance_interval && s->rebalance_interval < 1000) { s->rebalance_interval = 1000; } @@ -2926,6 +3067,7 @@ iface_clear_db_record(const struct ovsrec_interface *if_cfg) ovsrec_interface_set_link_state(if_cfg, NULL); ovsrec_interface_set_mtu(if_cfg, NULL, 0); ovsrec_interface_set_cfm_fault(if_cfg, NULL, 0); + ovsrec_interface_set_cfm_fault_status(if_cfg, NULL, 0); ovsrec_interface_set_cfm_remote_mpids(if_cfg, NULL, 0); ovsrec_interface_set_lacp_current(if_cfg, NULL, 0); ovsrec_interface_set_statistics(if_cfg, NULL, NULL, 0); @@ -3058,11 +3200,9 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos) shash_destroy(&details); } if (!queue_zero) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - VLOG_WARN_RL(&rl, "interface %s: QoS configured without a default " - "queue (queue 0). Packets not directed to a " - "correctly configured queue may be dropped.", - iface->name); + shash_init(&details); + netdev_set_queue(iface->netdev, 0, &details); + shash_destroy(&details); } } @@ -3086,6 +3226,7 @@ iface_configure_cfm(struct iface *iface) { const struct ovsrec_interface *cfg = iface->cfg; const char *extended_str, *opstate_str; + const char *cfm_ccm_vlan; struct cfm_settings s; if (!cfg->n_cfm_mpid) { @@ -3096,12 +3237,22 @@ iface_configure_cfm(struct iface *iface) s.mpid = *cfg->cfm_mpid; s.interval = atoi(get_interface_other_config(iface->cfg, "cfm_interval", "0")); - s.ccm_vlan = atoi(get_interface_other_config(iface->cfg, "cfm_ccm_vlan", - "0")); + cfm_ccm_vlan = get_interface_other_config(iface->cfg, "cfm_ccm_vlan", "0"); + s.ccm_pcp = atoi(get_interface_other_config(iface->cfg, "cfm_ccm_pcp", + "0")); if (s.interval <= 0) { s.interval = 1000; } + if (!strcasecmp("random", cfm_ccm_vlan)) { + s.ccm_vlan = CFM_RANDOM_VLAN; + } else { + s.ccm_vlan = atoi(cfm_ccm_vlan); + if (s.ccm_vlan == CFM_RANDOM_VLAN) { + s.ccm_vlan = 0; + } + } + extended_str = get_interface_other_config(iface->cfg, "cfm_extended", "false"); s.extended = !strcasecmp("true", extended_str); @@ -3374,8 +3525,13 @@ collect_splinter_vlans(const struct ovsrec_open_vswitch *ovs_cfg) struct bridge *br; size_t i; - splinter_vlans = NULL; + /* Free space allocated for synthesized ports and interfaces, since we're + * in the process of reconstructing all of them. */ + free_registered_blocks(); + + splinter_vlans = bitmap_allocate(4096); sset_init(&splinter_ifaces); + vlan_splinters_enabled_anywhere = false; for (i = 0; i < ovs_cfg->n_bridges; i++) { struct ovsrec_bridge *br_cfg = ovs_cfg->bridges[i]; size_t j; @@ -3388,21 +3544,22 @@ collect_splinter_vlans(const struct ovsrec_open_vswitch *ovs_cfg) struct ovsrec_interface *iface_cfg = port_cfg->interfaces[k]; if (vlan_splinters_is_enabled(iface_cfg)) { + vlan_splinters_enabled_anywhere = true; sset_add(&splinter_ifaces, iface_cfg->name); - - if (!splinter_vlans) { - splinter_vlans = bitmap_allocate(4096); - } vlan_bitmap_from_array__(port_cfg->trunks, port_cfg->n_trunks, splinter_vlans); } } + + if (port_cfg->tag && *port_cfg->tag > 0 && *port_cfg->tag < 4095) { + bitmap_set1(splinter_vlans, *port_cfg->tag); + } } } - vlan_splinters_enabled_anywhere = splinter_vlans != NULL; - if (!splinter_vlans) { + if (!vlan_splinters_enabled_anywhere) { + free(splinter_vlans); sset_destroy(&splinter_ifaces); return NULL; } @@ -3541,8 +3698,6 @@ add_vlan_splinter_ports(struct bridge *br, { size_t i; - free_registered_blocks(); - /* We iterate through 'br->cfg->ports' instead of 'ports' here because * we're modifying 'ports'. */ for (i = 0; i < br->cfg->n_ports; i++) {