X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=vswitchd%2Fovs-brcompatd.c;h=99d08c5bd4337e4540bc26c60f5a287dc5fe3abf;hb=3c85d3d0e7a5ce7692aaf7230d84070c49d9ce92;hp=99e9c8033d408bd2851953830247f1be194b42ea;hpb=7f42c1d707c65befb9d92bebae069e4f4b0be1fb;p=sliver-openvswitch.git diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c index 99e9c8033..99d08c5bd 100644 --- a/vswitchd/ovs-brcompatd.c +++ b/vswitchd/ovs-brcompatd.c @@ -38,7 +38,6 @@ #include "coverage.h" #include "daemon.h" #include "dirs.h" -#include "dpif.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "fault.h" @@ -240,21 +239,14 @@ rewrite_and_reload_config(void) return 0; } -/* Get all the interfaces for 'bridge' as 'ifaces', breaking bonded interfaces - * 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, int vlan) +do_get_bridge_parts(const char *bridge, struct svec *parts, int vlan, + bool break_down_bonds) { struct svec ports; int i; svec_init(&ports); - svec_init(ifaces); cfg_get_all_keys(&ports, "bridge.%s.port", bridge); for (i = 0; i < ports.n; i++) { const char *port_name = ports.names[i]; @@ -267,26 +259,50 @@ get_bridge_ifaces(const char *bridge, struct svec *ifaces, int vlan) continue; } } - if (cfg_has_section("bonding.%s", port_name)) { + if (break_down_bonds && cfg_has_section("bonding.%s", port_name)) { struct svec slaves; svec_init(&slaves); cfg_get_all_keys(&slaves, "bonding.%s.slave", port_name); - svec_append(ifaces, &slaves); + svec_append(parts, &slaves); svec_destroy(&slaves); } else { - svec_add(ifaces, port_name); + svec_add(parts, port_name); } } svec_destroy(&ports); } +/* Add all the interfaces for 'bridge' to 'ifaces', breaking bonded interfaces + * 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 implicit VLAN 'vlan' are + * reported. */ +static void +get_bridge_ifaces(const char *bridge, struct svec *ifaces, int vlan) +{ + do_get_bridge_parts(bridge, ifaces, vlan, true); +} + +/* Add all the ports for 'bridge' to 'ports'. Bonded ports are reported under + * the bond name, not broken down into their constituent interfaces. + * + * If 'vlan' < 0, all ports on 'bridge' are reported. If 'vlan' == 0, then + * only trunk ports or ports with implicit VLAN 0 are reported. If 'vlan' > 0, + * only port with implicit VLAN 'vlan' are reported. */ +static void +get_bridge_ports(const char *bridge, struct svec *ports, int vlan) +{ + do_get_bridge_parts(bridge, ports, vlan, false); +} + /* Go through the configuration file and remove any ports that no longer * exist associated with a bridge. */ static void prune_ports(void) { int i, j; - int error; struct svec bridges, delete; if (cfg_lock(NULL, 0)) { @@ -302,10 +318,10 @@ prune_ports(void) struct svec ifaces; /* Check that each bridge interface exists. */ + svec_init(&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; /* The local port and internal ports are created and destroyed by * ovs-vswitchd itself, so don't bother checking for them at all. @@ -316,14 +332,10 @@ prune_ports(void) continue; } - error = netdev_nodev_get_flags(iface_name, &flags); - if (error == ENODEV) { + if (!netdev_exists(iface_name)) { VLOG_INFO_RL(&rl, "removing dead interface %s from %s", iface_name, br_name); svec_add(&delete, iface_name); - } else if (error) { - VLOG_INFO_RL(&rl, "unknown error %d on interface %s from %s", - error, iface_name, br_name); } } svec_destroy(&ifaces); @@ -345,30 +357,6 @@ prune_ports(void) svec_destroy(&delete); } - -/* Checks whether a network device named 'name' exists and returns true if so, - * false otherwise. - * - * XXX it is possible that this doesn't entirely accomplish what we want in - * context, since ovs-vswitchd.conf may cause vswitchd to create or destroy - * network devices based on iface.*.internal settings. - * - * XXX may want to move this to lib/netdev. - * - * XXX why not just use netdev_nodev_get_flags() or similar function? */ -static bool -netdev_exists(const char *name) -{ - struct stat s; - char *filename; - int error; - - filename = xasprintf("/sys/class/net/%s", name); - error = stat(filename, &s); - free(filename); - return !error; -} - static int add_bridge(const char *br_name) { @@ -445,30 +433,38 @@ parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name, return 0; } -static void -send_reply(uint32_t seq, int error, struct ofpbuf *fdb_query_data) +/* Composes and returns a reply to a request made by the datapath with Netlink + * sequence number 'seq' and error code 'error'. The caller may add additional + * attributes to the message, then it may send it with send_reply(). */ +static struct ofpbuf * +compose_reply(uint32_t seq, int error) { - struct ofpbuf msg; - int retval; - - /* Compose reply. */ - ofpbuf_init(&msg, 0); - nl_msg_put_genlmsghdr(&msg, brc_sock, 32, brc_family, NLM_F_REQUEST, + struct ofpbuf *reply = ofpbuf_new(4096); + nl_msg_put_genlmsghdr(reply, brc_sock, 32, brc_family, NLM_F_REQUEST, BRC_GENL_C_DP_RESULT, 1); - ((struct nlmsghdr *) msg.data)->nlmsg_seq = seq; - nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, error); - if (fdb_query_data) { - nl_msg_put_unspec(&msg, BRC_GENL_A_FDB_DATA, - fdb_query_data->data, fdb_query_data->size); - } + ((struct nlmsghdr *) reply->data)->nlmsg_seq = seq; + nl_msg_put_u32(reply, BRC_GENL_A_ERR_CODE, error); + return reply; +} - /* Send reply. */ - retval = nl_sock_send(brc_sock, &msg, false); +/* Sends 'reply' to the datapath and frees it. */ +static void +send_reply(struct ofpbuf *reply) +{ + int retval = nl_sock_send(brc_sock, reply, false); if (retval) { VLOG_WARN_RL(&rl, "replying to brcompat request: %s", strerror(retval)); } - ofpbuf_uninit(&msg); + ofpbuf_delete(reply); +} + +/* Composes and sends a reply to a request made by the datapath with Netlink + * sequence number 'seq' and error code 'error'. */ +static void +send_simple_reply(uint32_t seq, int error) +{ + send_reply(compose_reply(seq, error)); } static int @@ -484,7 +480,7 @@ handle_bridge_cmd(struct ofpbuf *buffer, bool add) if (!error) { error = rewrite_and_reload_config(); } - send_reply(seq, error, NULL); + send_simple_reply(seq, error); } return error; } @@ -499,7 +495,7 @@ del_port(const char *br_name, const char *port_name) { cfg_del_entry("bridge.%s.port=%s", br_name, port_name); cfg_del_match("bonding.*.slave=%s", port_name); - cfg_del_match("vlan.%s.*", port_name); + cfg_del_match("vlan.%s.[!0-9]*", port_name); } static int @@ -529,7 +525,7 @@ handle_port_cmd(struct ofpbuf *buffer, bool add) VLOG_INFO("%s %s %s: success", cmd_name, br_name, port_name); error = rewrite_and_reload_config(); } - send_reply(seq, error, NULL); + send_simple_reply(seq, error); } return error; @@ -556,6 +552,33 @@ get_bridge_containing_port(const char *port_name) return xmemdup0(start, end - start); } +static int +linux_bridge_to_ovs_bridge(const char *linux_bridge, + char **ovs_bridge, int *br_vlan) +{ + if (bridge_exists(linux_bridge)) { + /* Bridge name is the same. We are interested in VLAN 0. */ + *ovs_bridge = xstrdup(linux_bridge); + *br_vlan = 0; + return 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) { + return 0; + } else { + free(*ovs_bridge); + return ENODEV; + } + } +} + static int handle_fdb_query_cmd(struct ofpbuf *buffer) { @@ -591,6 +614,7 @@ handle_fdb_query_cmd(struct ofpbuf *buffer) struct svec ifaces; struct ofpbuf query_data; + struct ofpbuf *reply; char *unixctl_command; uint64_t count, skip; char *output; @@ -606,24 +630,10 @@ handle_fdb_query_cmd(struct ofpbuf *buffer) /* 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; - } + error = linux_bridge_to_ovs_bridge(linux_bridge, &ovs_bridge, &br_vlan); + if (error) { + send_simple_reply(seq, error); + return error; } /* Fetch the forwarding database using ovs-appctl. */ @@ -632,20 +642,27 @@ handle_fdb_query_cmd(struct ofpbuf *buffer) free(unixctl_command); if (error) { free(ovs_bridge); - send_reply(seq, error, NULL); + send_simple_reply(seq, error); 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. */ + svec_init(&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++) { const char *iface_name = ifaces.names[i]; struct mac *mac = &local_macs[n_local_macs]; - if (!netdev_nodev_get_etheraddr(iface_name, mac->addr)) { - n_local_macs++; + struct netdev *netdev; + + error = netdev_open(iface_name, NETDEV_ETH_TYPE_NONE, &netdev); + if (netdev) { + if (!netdev_get_etheraddr(netdev, mac->addr)) { + n_local_macs++; + } + netdev_close(netdev); } } svec_destroy(&ifaces); @@ -705,13 +722,135 @@ handle_fdb_query_cmd(struct ofpbuf *buffer) } free(output); - send_reply(seq, 0, &query_data); + /* Compose and send reply to datapath. */ + reply = compose_reply(seq, 0); + nl_msg_put_unspec(reply, BRC_GENL_A_FDB_DATA, + query_data.data, query_data.size); + send_reply(reply); + + /* Free memory. */ ofpbuf_uninit(&query_data); free(ovs_bridge); return 0; } +static void +send_ifindex_reply(uint32_t seq, struct svec *ifaces) +{ + struct ofpbuf *reply; + const char *iface; + size_t n_indices; + int *indices; + size_t i; + + /* Make sure that any given interface only occurs once. This shouldn't + * happen, but who knows what people put into their configuration files. */ + svec_sort_unique(ifaces); + + /* Convert 'ifaces' into ifindexes. */ + n_indices = 0; + indices = xmalloc(ifaces->n * sizeof *indices); + SVEC_FOR_EACH (i, iface, ifaces) { + int ifindex = if_nametoindex(iface); + if (ifindex) { + indices[n_indices++] = ifindex; + } + } + + /* Compose and send reply. */ + reply = compose_reply(seq, 0); + nl_msg_put_unspec(reply, BRC_GENL_A_IFINDEXES, + indices, n_indices * sizeof *indices); + send_reply(reply); + + /* Free memory. */ + free(indices); +} + +static int +handle_get_bridges_cmd(struct ofpbuf *buffer) +{ + struct svec bridges; + const char *br_name; + size_t i; + + uint32_t seq; + + int error; + + /* Parse Netlink command. + * + * The command doesn't actually have any arguments, but we need the + * sequence number to send the reply. */ + error = parse_command(buffer, &seq, NULL, NULL, NULL, NULL); + if (error) { + return error; + } + + /* Get all the real bridges and all the fake ones. */ + cfg_read(); + svec_init(&bridges); + cfg_get_subsections(&bridges, "bridge"); + SVEC_FOR_EACH (i, br_name, &bridges) { + const char *iface_name; + struct svec ifaces; + size_t j; + + svec_init(&ifaces); + get_bridge_ifaces(br_name, &ifaces, -1); + SVEC_FOR_EACH (j, iface_name, &ifaces) { + if (cfg_get_bool(0, "iface.%s.fake-bridge", iface_name)) { + svec_add(&bridges, iface_name); + } + } + svec_destroy(&ifaces); + } + + send_ifindex_reply(seq, &bridges); + svec_destroy(&bridges); + + return 0; +} + +static int +handle_get_ports_cmd(struct ofpbuf *buffer) +{ + uint32_t seq; + + const char *linux_bridge; + char *ovs_bridge; + int br_vlan; + + struct svec ports; + + int error; + + /* Parse Netlink command. */ + error = parse_command(buffer, &seq, &linux_bridge, NULL, NULL, NULL); + if (error) { + return error; + } + + cfg_read(); + error = linux_bridge_to_ovs_bridge(linux_bridge, &ovs_bridge, &br_vlan); + if (error) { + send_simple_reply(seq, error); + return error; + } + + svec_init(&ports); + get_bridge_ports(ovs_bridge, &ports, br_vlan); + svec_sort(&ports); + svec_del(&ports, linux_bridge); + send_ifindex_reply(seq, &ports); /* XXX bonds won't show up */ + svec_destroy(&ports); + + free(ovs_bridge); + + return 0; +} + static int brc_recv_update(void) { @@ -774,6 +913,14 @@ brc_recv_update(void) retval = handle_fdb_query_cmd(buffer); break; + case BRC_GENL_C_GET_BRIDGES: + retval = handle_get_bridges_cmd(buffer); + break; + + case BRC_GENL_C_GET_PORTS: + retval = handle_get_ports_cmd(buffer); + break; + default: retval = EPROTO; } @@ -823,8 +970,6 @@ rtnl_recv_update(void) const char *port_name = nl_attr_get_string(attrs[IFLA_IFNAME]); char br_name[IFNAMSIZ]; uint32_t br_idx = nl_attr_get_u32(attrs[IFLA_MASTER]); - struct svec ports; - enum netdev_flags flags; if (!if_indextoname(br_idx, br_name)) { ofpbuf_delete(buf); @@ -838,10 +983,13 @@ rtnl_recv_update(void) return; } - if (netdev_nodev_get_flags(port_name, &flags) == ENODEV) { + if (!netdev_exists(port_name)) { /* Network device is really gone. */ + struct svec ports; + VLOG_INFO("network device %s destroyed, " "removing from bridge %s", port_name, br_name); + svec_init(&ports); cfg_get_all_keys(&ports, "bridge.%s.port", br_name); svec_sort(&ports); @@ -849,6 +997,7 @@ rtnl_recv_update(void) del_port(br_name, port_name); rewrite_and_reload_config(); } + svec_destroy(&ports); } else { /* A network device by that name exists even though the kernel * told us it had disappeared. Probably, what happened was @@ -942,6 +1091,7 @@ main(int argc, char *argv[]) for (;;) { unixctl_server_run(unixctl); brc_recv_update(); + netdev_run(); /* If 'prune_timeout' is non-zero, we actively prune from the * config file any 'bridge..port' entries that are no @@ -963,6 +1113,7 @@ main(int argc, char *argv[]) nl_sock_wait(brc_sock, POLLIN); unixctl_server_wait(unixctl); + netdev_wait(); poll_block(); } @@ -1014,10 +1165,7 @@ parse_options(int argc, char *argv[]) char *short_options = long_options_to_short_options(long_options); int error; - appctl_command = xasprintf("%s/ovs-appctl -t " - "%s/ovs-vswitchd.`cat %s/ovs-vswitchd.pid`.ctl " - "-e '%%s'", - ovs_bindir, ovs_rundir, ovs_rundir); + appctl_command = xasprintf("%s/ovs-appctl %%s", ovs_bindir); for (;;) { int c;