From: Ben Pfaff Date: Thu, 16 Jul 2009 22:16:06 +0000 (-0700) Subject: brcompat: Make "brctl showmacs" honor Linux notion of bridge composition. X-Git-Tag: v0.90.4~3 X-Git-Url: http://git.cascardo.info/?a=commitdiff_plain;h=c735214e852e1706a3e0ad346c114292fd3e7047;p=cascardo%2Fovs.git brcompat: Make "brctl showmacs" honor Linux notion of bridge composition. The kernel only handles a single VLAN per bridge, but vswitchd can deal with all the VLANs on a single bridge. This commit makes "brctl showmacs" pretend that the former is the case even though the latter is the implementation. Bug #1567. --- diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c index 16fc78668..2384f5cab 100644 --- a/vswitchd/ovs-brcompatd.c +++ b/vswitchd/ovs-brcompatd.c @@ -241,9 +241,14 @@ rewrite_and_reload_config(void) } /* Get all the interfaces for 'bridge' as 'ifaces', breaking bonded interfaces - * down into their constituent parts. */ + * down into their constituent parts. + * + * If 'vlan' < 0, all interfaces on 'bridge' are reported. If 'vlan' == 0, + * then only interfaces for trunk ports or ports with implicit VLAN 0 are + * reported. If 'vlan' > 0, only interfaces with implict VLAN 'vlan' are + * reported. */ static void -get_bridge_ifaces(const char *bridge, struct svec *ifaces) +get_bridge_ifaces(const char *bridge, struct svec *ifaces, int vlan) { struct svec ports; int i; @@ -253,6 +258,15 @@ get_bridge_ifaces(const char *bridge, struct svec *ifaces) cfg_get_all_keys(&ports, "bridge.%s.port", bridge); for (i = 0; i < ports.n; i++) { const char *port_name = ports.names[i]; + if (vlan >= 0) { + int port_vlan = cfg_get_vlan(0, "vlan.%s.tag", port_name); + if (port_vlan < 0) { + port_vlan = 0; + } + if (vlan != port_vlan) { + continue; + } + } if (cfg_has_section("bonding.%s", port_name)) { struct svec slaves; svec_init(&slaves); @@ -288,7 +302,7 @@ prune_ports(void) struct svec ifaces; /* Check that each bridge interface exists. */ - get_bridge_ifaces(br_name, &ifaces); + get_bridge_ifaces(br_name, &ifaces, -1); for (j = 0; j < ifaces.n; j++) { const char *iface_name = ifaces.names[j]; enum netdev_flags flags; @@ -518,6 +532,27 @@ handle_port_cmd(struct ofpbuf *buffer, bool add) return error; } +/* Returns the name of the bridge that contains a port named 'port_name', as a + * malloc'd string that the caller must free, or a null pointer if no bridge + * contains a port named 'port_name'. */ +static char * +get_bridge_containing_port(const char *port_name) +{ + struct svec matches; + const char *start, *end; + + svec_init(&matches); + cfg_get_matches(&matches, "bridge.*.port=%s", port_name); + if (!matches.n) { + return 0; + } + + start = matches.names[0] + strlen("bridge."); + end = strstr(start, ".port="); + assert(end); + return xmemdup0(start, end - start); +} + static int handle_fdb_query_cmd(struct ofpbuf *buffer) { @@ -542,35 +577,65 @@ handle_fdb_query_cmd(struct ofpbuf *buffer) int n_local_macs; int i; + /* Impedance matching between the vswitchd and Linux kernel notions of what + * a bridge is. The kernel only handles a single VLAN per bridge, but + * vswitchd can deal with all the VLANs on a single bridge. We have to + * pretend that the former is the case even though the latter is the + * implementation. */ + const char *linux_bridge; /* Name used by brctl. */ + char *ovs_bridge; /* Name used by ovs-vswitchd. */ + int br_vlan; /* VLAN tag. */ + struct svec ifaces; + struct ofpbuf query_data; char *unixctl_command; uint64_t count, skip; - const char *br_name; - struct svec ifaces; char *output; char *save_ptr; uint32_t seq; int error; /* Parse the command received from brcompat_mod. */ - error = parse_command(buffer, &seq, &br_name, NULL, &count, &skip); + error = parse_command(buffer, &seq, &linux_bridge, NULL, &count, &skip); if (error) { return error; } + /* Figure out vswitchd bridge and VLAN. */ + cfg_read(); + if (bridge_exists(linux_bridge)) { + /* Bridge name is the same. We are interested in VLAN 0. */ + ovs_bridge = xstrdup(linux_bridge); + br_vlan = 0; + } else { + /* No such Open vSwitch bridge 'linux_bridge', but there might be an + * internal port named 'linux_bridge' on some other bridge + * 'ovs_bridge'. If so then we are interested in the VLAN assigned to + * port 'linux_bridge' on the bridge named 'ovs_bridge'. */ + const char *port_name = linux_bridge; + + ovs_bridge = get_bridge_containing_port(port_name); + br_vlan = cfg_get_vlan(0, "vlan.%s.tag", port_name); + if (!ovs_bridge || br_vlan < 0) { + free(ovs_bridge); + send_reply(seq, ENODEV, NULL); + return error; + } + } + /* Fetch the forwarding database using ovs-appctl. */ - unixctl_command = xasprintf("fdb/show %s", br_name); + unixctl_command = xasprintf("fdb/show %s", ovs_bridge); error = execute_appctl_command(unixctl_command, &output); free(unixctl_command); if (error) { + free(ovs_bridge); send_reply(seq, error, NULL); return error; } /* Fetch the MAC address for each interface on the bridge, so that we can * fill in the is_local field in the response. */ - cfg_read(); - get_bridge_ifaces(br_name, &ifaces); + get_bridge_ifaces(ovs_bridge, &ifaces, br_vlan); local_macs = xmalloc(ifaces.n * sizeof *local_macs); n_local_macs = 0; for (i = 0; i < ifaces.n; i++) { @@ -607,6 +672,10 @@ handle_fdb_query_cmd(struct ofpbuf *buffer) continue; } + if (vlan != br_vlan) { + continue; + } + if (skip > 0) { skip--; continue; @@ -635,6 +704,7 @@ handle_fdb_query_cmd(struct ofpbuf *buffer) send_reply(seq, 0, &query_data); ofpbuf_uninit(&query_data); + free(ovs_bridge); return 0; }