X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=vswitchd%2Fovs-brcompatd.c;h=39844f69f2420c5b3dcff0da44d73153dbe3f197;hb=8fef8c7121222233075a03d57db7e0b48d5f6be5;hp=70570e925ec42f1969f0813bf592ac47c1f98202;hpb=6dd3fad481b5d801695c2b0529c7d37cac2c9b19;p=sliver-openvswitch.git diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c index 70570e925..39844f69f 100644 --- a/vswitchd/ovs-brcompatd.c +++ b/vswitchd/ovs-brcompatd.c @@ -239,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]; @@ -266,19 +259,44 @@ 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 @@ -300,6 +318,7 @@ 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]; @@ -382,7 +401,7 @@ parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name, const char **port_name, uint64_t *count, uint64_t *skip) { static const struct nl_policy policy[] = { - [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING }, + [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING, .optional = true }, [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING, .optional = true }, [BRC_GENL_A_FDB_COUNT] = { .type = NL_A_U64, .optional = true }, [BRC_GENL_A_FDB_SKIP] = { .type = NL_A_U64, .optional = true }, @@ -391,6 +410,7 @@ parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name, if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, policy, attrs, ARRAY_SIZE(policy)) + || (br_name && !attrs[BRC_GENL_A_DP_NAME]) || (port_name && !attrs[BRC_GENL_A_PORT_NAME]) || (count && !attrs[BRC_GENL_A_FDB_COUNT]) || (skip && !attrs[BRC_GENL_A_FDB_SKIP])) { @@ -398,7 +418,9 @@ parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name, } *seq = ((struct nlmsghdr *) buffer->data)->nlmsg_seq; - *br_name = nl_attr_get_string(attrs[BRC_GENL_A_DP_NAME]); + if (br_name) { + *br_name = nl_attr_get_string(attrs[BRC_GENL_A_DP_NAME]); + } if (port_name) { *port_name = nl_attr_get_string(attrs[BRC_GENL_A_PORT_NAME]); } @@ -411,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 @@ -450,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; } @@ -495,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; @@ -522,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) { @@ -557,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; @@ -572,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. */ @@ -598,12 +642,13 @@ 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; @@ -677,13 +722,132 @@ 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(); + 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); + 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) { @@ -746,6 +910,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; } @@ -905,7 +1077,10 @@ main(int argc, char *argv[]) } } - cfg_read(); + retval = cfg_read(); + if (retval) { + ovs_fatal(retval, "could not read config file"); + } for (;;) { unixctl_server_run(unixctl); @@ -1040,6 +1215,7 @@ parse_options(int argc, char *argv[]) "use --help for usage"); } + cfg_init(); config_file = argv[0]; error = cfg_set_file(config_file); if (error) {