X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=vswitchd%2Fovs-brcompatd.c;h=32345296fe89daaa54f1f1c061af6946b84f8985;hb=96ca8c29569b84d881f64bd84dca374d15651a84;hp=09225439df6dec2c336ae6d4805409814f48094c;hpb=9c8149dca0d205631a5bda57b0ccde1367b3bb96;p=sliver-openvswitch.git diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c index 09225439d..32345296f 100644 --- a/vswitchd/ovs-brcompatd.c +++ b/vswitchd/ovs-brcompatd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2009 Nicira Networks +/* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,9 +39,11 @@ #include "dirs.h" #include "dynamic-string.h" #include "fatal-signal.h" +#include "json.h" #include "leak-checker.h" #include "netdev.h" #include "netlink.h" +#include "netlink-socket.h" #include "ofpbuf.h" #include "openvswitch/brcompat-netlink.h" #include "ovsdb-idl.h" @@ -49,14 +51,14 @@ #include "poll-loop.h" #include "process.h" #include "signals.h" -#include "svec.h" +#include "sset.h" #include "timeval.h" #include "unixctl.h" #include "util.h" +#include "vlog.h" #include "vswitchd/vswitch-idl.h" -#include "vlog.h" -#define THIS_MODULE VLM_brcompatd +VLOG_DEFINE_THIS_MODULE(brcompatd); /* xxx Just hangs if datapath is rmmod/insmod. Learn to reconnect? */ @@ -74,7 +76,7 @@ static void usage(void) NO_RETURN; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 60); -/* Maximum number of milliseconds to wait before pruning port entries that +/* Maximum number of milliseconds to wait before pruning port entries that * no longer exist. If set to zero, ports are never pruned. */ static int prune_timeout = 5000; @@ -109,12 +111,12 @@ lookup_brc_multicast_group(int *multicast_group) struct nlattr *attrs[ARRAY_SIZE(brc_multicast_policy)]; int retval; - retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &sock); + retval = nl_sock_create(NETLINK_GENERIC, &sock); if (retval) { return retval; } ofpbuf_init(&request, 0); - nl_msg_put_genlmsghdr(&request, sock, 0, brc_family, + nl_msg_put_genlmsghdr(&request, 0, brc_family, NLM_F_REQUEST, BRC_GENL_C_QUERY_MC, 1); retval = nl_sock_transact(sock, &request, &reply); ofpbuf_uninit(&request); @@ -154,12 +156,17 @@ brc_open(struct nl_sock **sock) return retval; } - retval = nl_sock_create(NETLINK_GENERIC, multicast_group, 0, 0, sock); + retval = nl_sock_create(NETLINK_GENERIC, sock); if (retval) { return retval; } - return 0; + retval = nl_sock_join_mcgroup(*sock, multicast_group); + if (retval) { + nl_sock_destroy(*sock); + *sock = NULL; + } + return retval; } static const struct nl_policy brc_dp_policy[] = { @@ -223,17 +230,14 @@ execute_appctl_command(const char *unixctl_command, char **output) } static void -do_get_bridge_parts(const struct ovsrec_bridge *br, struct svec *parts, +do_get_bridge_parts(const struct ovsrec_bridge *br, struct sset *parts, int vlan, bool break_down_bonds) { - struct svec ports; size_t i, j; - svec_init(&ports); for (i = 0; i < br->n_ports; i++) { const struct ovsrec_port *port = br->ports[i]; - svec_add(&ports, port->name); if (vlan >= 0) { int port_vlan = port->n_tag ? *port->tag : 0; if (vlan != port_vlan) { @@ -243,13 +247,12 @@ do_get_bridge_parts(const struct ovsrec_bridge *br, struct svec *parts, if (break_down_bonds) { for (j = 0; j < port->n_interfaces; j++) { const struct ovsrec_interface *iface = port->interfaces[j]; - svec_add(parts, iface->name); + sset_add(parts, iface->name); } } else { - svec_add(parts, port->name); + sset_add(parts, port->name); } } - svec_destroy(&ports); } /* Add all the interfaces for 'bridge' to 'ifaces', breaking bonded interfaces @@ -260,7 +263,7 @@ do_get_bridge_parts(const struct ovsrec_bridge *br, struct svec *parts, * reported. If 'vlan' > 0, only interfaces with implicit VLAN 'vlan' are * reported. */ static void -get_bridge_ifaces(const struct ovsrec_bridge *br, struct svec *ifaces, +get_bridge_ifaces(const struct ovsrec_bridge *br, struct sset *ifaces, int vlan) { do_get_bridge_parts(br, ifaces, vlan, true); @@ -273,74 +276,12 @@ get_bridge_ifaces(const struct ovsrec_bridge *br, struct svec *ifaces, * 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 struct ovsrec_bridge *br, struct svec *ports, +get_bridge_ports(const struct ovsrec_bridge *br, struct sset *ports, int vlan) { do_get_bridge_parts(br, ports, vlan, false); } -#if 0 -/* 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; - struct svec bridges, delete; - - if (cfg_lock(NULL, 0)) { - /* Couldn't lock config file. */ - return; - } - - svec_init(&bridges); - svec_init(&delete); - cfg_get_subsections(&bridges, "bridge"); - for (i=0; ibridges * (ovs->n_bridges + 1)); for (i = 0; i < ovs->n_bridges; i++) { @@ -369,14 +310,99 @@ ovs_insert_bridge(const struct ovsrec_open_vswitch *ovs, bridges[ovs->n_bridges] = bridge; ovsrec_open_vswitch_set_bridges(ovs, bridges, ovs->n_bridges + 1); free(bridges); -} +} + +static struct json * +where_uuid_equals(const struct uuid *uuid) +{ + return + json_array_create_1( + json_array_create_3( + json_string_create("_uuid"), + json_string_create("=="), + json_array_create_2( + json_string_create("uuid"), + json_string_create_nocopy( + xasprintf(UUID_FMT, UUID_ARGS(uuid)))))); +} + +/* Commits 'txn'. If 'wait_for_reload' is true, also waits for Open vSwitch to + reload the configuration before returning. + + Returns EAGAIN if the caller should try the operation again, 0 on success, + otherwise a positive errno value. */ +static int +commit_txn(struct ovsdb_idl_txn *txn, bool wait_for_reload) +{ + struct ovsdb_idl *idl = ovsdb_idl_txn_get_idl (txn); + enum ovsdb_idl_txn_status status; + int64_t next_cfg = 0; + + if (wait_for_reload) { + const struct ovsrec_open_vswitch *ovs = ovsrec_open_vswitch_first(idl); + struct json *where = where_uuid_equals(&ovs->header_.uuid); + ovsdb_idl_txn_increment(txn, "Open_vSwitch", "next_cfg", where); + json_destroy(where); + } + status = ovsdb_idl_txn_commit_block(txn); + if (wait_for_reload && status == TXN_SUCCESS) { + next_cfg = ovsdb_idl_txn_get_increment_new_value(txn); + } + ovsdb_idl_txn_destroy(txn); + + switch (status) { + case TXN_INCOMPLETE: + NOT_REACHED(); + + case TXN_ABORTED: + VLOG_ERR_RL(&rl, "OVSDB transaction unexpectedly aborted"); + return ECONNABORTED; + + case TXN_UNCHANGED: + return 0; + + case TXN_SUCCESS: + if (wait_for_reload) { + for (;;) { + /* We can't use 'ovs' any longer because ovsdb_idl_run() can + * destroy it. */ + const struct ovsrec_open_vswitch *ovs2; + + ovsdb_idl_run(idl); + OVSREC_OPEN_VSWITCH_FOR_EACH (ovs2, idl) { + if (ovs2->cur_cfg >= next_cfg) { + goto done; + } + } + ovsdb_idl_wait(idl); + poll_block(); + } + done: ; + } + return 0; + + case TXN_TRY_AGAIN: + VLOG_ERR_RL(&rl, "OVSDB transaction needs retry"); + return EAGAIN; + + case TXN_ERROR: + VLOG_ERR_RL(&rl, "OVSDB transaction failed: %s", + ovsdb_idl_txn_get_error(txn)); + return EBUSY; + + default: + NOT_REACHED(); + } +} static int -add_bridge(const struct ovsrec_open_vswitch *ovs, const char *br_name) +add_bridge(struct ovsdb_idl *idl, const struct ovsrec_open_vswitch *ovs, + const char *br_name) { struct ovsrec_bridge *br; struct ovsrec_port *port; struct ovsrec_interface *iface; + struct ovsdb_idl_txn *txn; if (find_bridge(ovs, br_name)) { VLOG_WARN("addbr %s: bridge %s exists", br_name, br_name); @@ -403,26 +429,28 @@ add_bridge(const struct ovsrec_open_vswitch *ovs, const char *br_name) return EEXIST; } + txn = ovsdb_idl_txn_create(idl); + + ovsdb_idl_txn_add_comment(txn, "ovs-brcompatd: addbr %s", br_name); + iface = ovsrec_interface_insert(txn_from_openvswitch(ovs)); ovsrec_interface_set_name(iface, br_name); port = ovsrec_port_insert(txn_from_openvswitch(ovs)); ovsrec_port_set_name(port, br_name); ovsrec_port_set_interfaces(port, &iface, 1); - + br = ovsrec_bridge_insert(txn_from_openvswitch(ovs)); ovsrec_bridge_set_name(br, br_name); ovsrec_bridge_set_ports(br, &port, 1); - - ovs_insert_bridge(ovs, br); - VLOG_INFO("addbr %s: success", br_name); + ovs_insert_bridge(ovs, br); - return 0; + return commit_txn(txn, true); } static void -add_port(const struct ovsrec_open_vswitch *ovs, +add_port(const struct ovsrec_open_vswitch *ovs, const struct ovsrec_bridge *br, const char *port_name) { struct ovsrec_interface *iface; @@ -447,46 +475,98 @@ add_port(const struct ovsrec_open_vswitch *ovs, free(ports); } +/* Deletes 'port' from 'br'. + * + * After calling this function, 'port' must not be referenced again. */ static void -del_port(const struct ovsrec_bridge *br, const char *port_name) +del_port(const struct ovsrec_bridge *br, const struct ovsrec_port *port) { - size_t i, j; - struct ovsrec_port *port_rec = NULL; + struct ovsrec_port **ports; + size_t i, n; + + /* Remove 'port' from the bridge's list of ports. */ + ports = xmalloc(sizeof *br->ports * br->n_ports); + for (i = n = 0; i < br->n_ports; i++) { + if (br->ports[i] != port) { + ports[n++] = br->ports[i]; + } + } + ovsrec_bridge_set_ports(br, ports, n); + free(ports); +} + +/* Delete 'iface' from 'port' (which must be within 'br'). If 'iface' was + * 'port''s only interface, delete 'port' from 'br' also. + * + * After calling this function, 'iface' must not be referenced again. */ +static void +del_interface(const struct ovsrec_bridge *br, + const struct ovsrec_port *port, + const struct ovsrec_interface *iface) +{ + if (port->n_interfaces == 1) { + del_port(br, port); + } else { + struct ovsrec_interface **ifaces; + size_t i, n; + + ifaces = xmalloc(sizeof *port->interfaces * port->n_interfaces); + for (i = n = 0; i < port->n_interfaces; i++) { + if (port->interfaces[i] != iface) { + ifaces[n++] = port->interfaces[i]; + } + } + ovsrec_port_set_interfaces(port, ifaces, n); + free(ifaces); + } +} + +/* Find and return a port within 'br' named 'port_name'. */ +static const struct ovsrec_port * +find_port(const struct ovsrec_bridge *br, const char *port_name) +{ + size_t i; for (i = 0; i < br->n_ports; i++) { struct ovsrec_port *port = br->ports[i]; if (!strcmp(port_name, port->name)) { - port_rec = port; - } - for (j = 0; j < port->n_interfaces; j++) { - struct ovsrec_interface *iface = port->interfaces[j]; - if (!strcmp(port_name, iface->name)) { - ovsrec_interface_delete(iface); - } + return port; } } + return NULL; +} - /* xxx Probably can move this into the "for" loop. */ - if (port_rec) { - struct ovsrec_port **ports; - size_t n; +/* Find and return an interface within 'br' named 'iface_name'. */ +static const struct ovsrec_interface * +find_interface(const struct ovsrec_bridge *br, const char *iface_name, + struct ovsrec_port **portp) +{ + size_t i; - ports = xmalloc(sizeof *br->ports * br->n_ports); - for (i = n = 0; i < br->n_ports; i++) { - if (br->ports[i] != port_rec) { - ports[n++] = br->ports[i]; + for (i = 0; i < br->n_ports; i++) { + struct ovsrec_port *port = br->ports[i]; + size_t j; + + for (j = 0; j < port->n_interfaces; j++) { + struct ovsrec_interface *iface = port->interfaces[j]; + if (!strcmp(iface->name, iface_name)) { + *portp = port; + return iface; } } - ovsrec_bridge_set_ports(br, ports, n); - free(ports); } + + *portp = NULL; + return NULL; } -static int -del_bridge(const struct ovsrec_open_vswitch *ovs, const char *br_name) +static int +del_bridge(struct ovsdb_idl *idl, + const struct ovsrec_open_vswitch *ovs, const char *br_name) { struct ovsrec_bridge *br = find_bridge(ovs, br_name); struct ovsrec_bridge **bridges; + struct ovsdb_idl_txn *txn; size_t i, n; if (!br) { @@ -494,10 +574,11 @@ del_bridge(const struct ovsrec_open_vswitch *ovs, const char *br_name) return ENXIO; } - del_port(br, br_name); + txn = ovsdb_idl_txn_create(idl); - ovsrec_bridge_delete(br); + ovsdb_idl_txn_add_comment(txn, "ovs-brcompatd: delbr %s", br_name); + /* Remove 'br' from the vswitch's list of bridges. */ bridges = xmalloc(sizeof *ovs->bridges * ovs->n_bridges); for (i = n = 0; i < ovs->n_bridges; i++) { if (ovs->bridges[i] != br) { @@ -507,9 +588,7 @@ del_bridge(const struct ovsrec_open_vswitch *ovs, const char *br_name) ovsrec_open_vswitch_set_bridges(ovs, bridges, n); free(bridges); - VLOG_INFO("delbr %s: success", br_name); - - return 0; + return commit_txn(txn, true); } static int @@ -556,7 +635,7 @@ static struct ofpbuf * compose_reply(uint32_t seq, int error) { struct ofpbuf *reply = ofpbuf_new(4096); - nl_msg_put_genlmsghdr(reply, brc_sock, 32, brc_family, NLM_F_REQUEST, + nl_msg_put_genlmsghdr(reply, 32, brc_family, NLM_F_REQUEST, BRC_GENL_C_DP_RESULT, 1); ((struct nlmsghdr *) reply->data)->nlmsg_seq = seq; nl_msg_put_u32(reply, BRC_GENL_A_ERR_CODE, error); @@ -584,7 +663,8 @@ send_simple_reply(uint32_t seq, int error) } static int -handle_bridge_cmd(const struct ovsrec_open_vswitch *ovs, +handle_bridge_cmd(struct ovsdb_idl *idl, + const struct ovsrec_open_vswitch *ovs, struct ofpbuf *buffer, bool add) { const char *br_name; @@ -593,7 +673,14 @@ handle_bridge_cmd(const struct ovsrec_open_vswitch *ovs, error = parse_command(buffer, &seq, &br_name, NULL, NULL, NULL); if (!error) { - error = add ? add_bridge(ovs, br_name) : del_bridge(ovs, br_name); + int retval; + + do { + retval = (add ? add_bridge : del_bridge)(idl, ovs, br_name); + VLOG_INFO_RL(&rl, "%sbr %s: %s", + add ? "add" : "del", br_name, strerror(retval)); + } while (retval == EAGAIN); + send_simple_reply(seq, error); } return error; @@ -605,7 +692,8 @@ static const struct nl_policy brc_port_policy[] = { }; static int -handle_port_cmd(const struct ovsrec_open_vswitch *ovs, +handle_port_cmd(struct ovsdb_idl *idl, + const struct ovsrec_open_vswitch *ovs, struct ofpbuf *buffer, bool add) { const char *cmd_name = add ? "add-if" : "del-if"; @@ -626,12 +714,27 @@ handle_port_cmd(const struct ovsrec_open_vswitch *ovs, cmd_name, br_name, port_name, port_name); error = EINVAL; } else { - if (add) { - add_port(ovs, br, port_name); - } else { - del_port(br, port_name); - } - VLOG_INFO("%s %s %s: success", cmd_name, br_name, port_name); + do { + struct ovsdb_idl_txn *txn = ovsdb_idl_txn_create(idl); + + if (add) { + ovsdb_idl_txn_add_comment(txn, "ovs-brcompatd: add-if %s", + port_name); + add_port(ovs, br, port_name); + } else { + const struct ovsrec_port *port = find_port(br, port_name); + if (port) { + ovsdb_idl_txn_add_comment(txn, + "ovs-brcompatd: del-if %s", + port_name); + del_port(br, port); + } + } + + error = commit_txn(txn, true); + VLOG_INFO_RL(&rl, "%s %s %s: %s", + cmd_name, br_name, port_name, strerror(error)); + } while (error == EAGAIN); } send_simple_reply(seq, error); } @@ -710,9 +813,10 @@ handle_fdb_query_cmd(const struct ovsrec_open_vswitch *ovs, const char *linux_name; /* Name used by brctl. */ const struct ovsrec_bridge *ovs_bridge; /* Bridge used by ovs-vswitchd. */ int br_vlan; /* VLAN tag. */ - struct svec ifaces; + struct sset ifaces; struct ofpbuf query_data; + const char *iface_name; struct ofpbuf *reply; char *unixctl_command; uint64_t count, skip; @@ -728,7 +832,7 @@ handle_fdb_query_cmd(const struct ovsrec_open_vswitch *ovs, } /* Figure out vswitchd bridge and VLAN. */ - error = linux_bridge_to_ovs_bridge(ovs, linux_name, + error = linux_bridge_to_ovs_bridge(ovs, linux_name, &ovs_bridge, &br_vlan); if (error) { send_simple_reply(seq, error); @@ -746,24 +850,23 @@ handle_fdb_query_cmd(const struct ovsrec_open_vswitch *ovs, /* 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); + sset_init(&ifaces); get_bridge_ifaces(ovs_bridge, &ifaces, br_vlan); - local_macs = xmalloc(ifaces.n * sizeof *local_macs); + local_macs = xmalloc(sset_count(&ifaces) * sizeof *local_macs); n_local_macs = 0; - for (i = 0; i < ifaces.n; i++) { - const char *iface_name = ifaces.names[i]; + SSET_FOR_EACH (iface_name, &ifaces) { struct mac *mac = &local_macs[n_local_macs]; struct netdev *netdev; - error = netdev_open(iface_name, NETDEV_ETH_TYPE_NONE, &netdev); - if (netdev) { + error = netdev_open_default(iface_name, &netdev); + if (!error) { if (!netdev_get_etheraddr(netdev, mac->addr)) { n_local_macs++; } netdev_close(netdev); } } - svec_destroy(&ifaces); + sset_destroy(&ifaces); /* Parse the response from ovs-appctl and convert it to binary format to * pass back to the kernel. */ @@ -785,7 +888,7 @@ handle_fdb_query_cmd(const struct ovsrec_open_vswitch *ovs, if (sscanf(line, "%d %d "ETH_ADDR_SCAN_FMT" %d", &port, &vlan, ETH_ADDR_SCAN_ARGS(mac), &age) != 2 + ETH_ADDR_SCAN_COUNT + 1) { - struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_INFO_RL(&rl, "fdb/show output has invalid format: %s", line); continue; } @@ -828,27 +931,23 @@ handle_fdb_query_cmd(const struct ovsrec_open_vswitch *ovs, /* Free memory. */ ofpbuf_uninit(&query_data); + free(local_macs); return 0; } static void -send_ifindex_reply(uint32_t seq, struct svec *ifaces) +send_ifindex_reply(uint32_t seq, struct sset *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) { + indices = xmalloc(sset_count(ifaces) * sizeof *indices); + SSET_FOR_EACH (iface, ifaces) { int ifindex = if_nametoindex(iface); if (ifindex) { indices[n_indices++] = ifindex; @@ -869,7 +968,7 @@ static int handle_get_bridges_cmd(const struct ovsrec_open_vswitch *ovs, struct ofpbuf *buffer) { - struct svec bridges; + struct sset bridges; size_t i, j; uint32_t seq; @@ -886,22 +985,22 @@ handle_get_bridges_cmd(const struct ovsrec_open_vswitch *ovs, } /* Get all the real bridges and all the fake ones. */ - svec_init(&bridges); + sset_init(&bridges); for (i = 0; i < ovs->n_bridges; i++) { const struct ovsrec_bridge *br = ovs->bridges[i]; - svec_add(&bridges, br->name); + sset_add(&bridges, br->name); for (j = 0; j < br->n_ports; j++) { const struct ovsrec_port *port = br->ports[j]; if (port->fake_bridge) { - svec_add(&bridges, port->name); + sset_add(&bridges, port->name); } } } send_ifindex_reply(seq, &bridges); - svec_destroy(&bridges); + sset_destroy(&bridges); return 0; } @@ -916,7 +1015,7 @@ handle_get_ports_cmd(const struct ovsrec_open_vswitch *ovs, const struct ovsrec_bridge *ovs_bridge; int br_vlan; - struct svec ports; + struct sset ports; int error; @@ -926,43 +1025,61 @@ handle_get_ports_cmd(const struct ovsrec_open_vswitch *ovs, return error; } - error = linux_bridge_to_ovs_bridge(ovs, linux_name, + error = linux_bridge_to_ovs_bridge(ovs, linux_name, &ovs_bridge, &br_vlan); if (error) { send_simple_reply(seq, error); return error; } - svec_init(&ports); + sset_init(&ports); get_bridge_ports(ovs_bridge, &ports, br_vlan); - svec_sort(&ports); - svec_del(&ports, linux_name); + sset_find_and_delete(&ports, linux_name); send_ifindex_reply(seq, &ports); /* XXX bonds won't show up */ - svec_destroy(&ports); + sset_destroy(&ports); return 0; } -static void -brc_recv_update(const struct ovsrec_open_vswitch *ovs) +static struct ofpbuf * +brc_recv_update__(void) { - int retval; - struct ofpbuf *buffer; - struct genlmsghdr *genlmsghdr; - + for (;;) { + struct ofpbuf *buffer; + int retval; - buffer = NULL; - do { - ofpbuf_delete(buffer); retval = nl_sock_recv(brc_sock, &buffer, false); - } while (retval == ENOBUFS - || (!retval - && (nl_msg_nlmsgerr(buffer, NULL) - || nl_msg_nlmsghdr(buffer)->nlmsg_type == NLMSG_DONE))); - if (retval) { - if (retval != EAGAIN) { + switch (retval) { + case 0: + if (nl_msg_nlmsgerr(buffer, NULL) + || nl_msg_nlmsghdr(buffer)->nlmsg_type == NLMSG_DONE) { + break; + } + return buffer; + + case ENOBUFS: + break; + + case EAGAIN: + return NULL; + + default: VLOG_WARN_RL(&rl, "brc_recv_update: %s", strerror(retval)); + return NULL; } + ofpbuf_delete(buffer); + } +} + +static void +brc_recv_update(struct ovsdb_idl *idl) +{ + struct ofpbuf *buffer; + struct genlmsghdr *genlmsghdr; + const struct ovsrec_open_vswitch *ovs; + + buffer = brc_recv_update__(); + if (!buffer) { return; } @@ -978,21 +1095,31 @@ brc_recv_update(const struct ovsrec_open_vswitch *ovs) goto error; } + /* Get the Open vSwitch configuration. Just drop the request on the floor + * if a valid configuration doesn't exist. (We could check this earlier, + * but we want to drain pending Netlink messages even when there is no Open + * vSwitch configuration.) */ + ovs = ovsrec_open_vswitch_first(idl); + if (!ovs) { + VLOG_WARN_RL(&rl, "could not find valid configuration to update"); + goto error; + } + switch (genlmsghdr->cmd) { case BRC_GENL_C_DP_ADD: - handle_bridge_cmd(ovs, buffer, true); + handle_bridge_cmd(idl, ovs, buffer, true); break; case BRC_GENL_C_DP_DEL: - handle_bridge_cmd(ovs, buffer, false); + handle_bridge_cmd(idl, ovs, buffer, false); break; case BRC_GENL_C_PORT_ADD: - handle_port_cmd(ovs, buffer, true); + handle_port_cmd(idl, ovs, buffer, true); break; case BRC_GENL_C_PORT_DEL: - handle_port_cmd(ovs, buffer, false); + handle_port_cmd(idl, ovs, buffer, false); break; case BRC_GENL_C_FDB_QUERY: @@ -1009,7 +1136,7 @@ brc_recv_update(const struct ovsrec_open_vswitch *ovs) default: VLOG_WARN_RL(&rl, "received unknown brc netlink command: %d\n", - genlmsghdr->cmd); + genlmsghdr->cmd); break; } @@ -1020,7 +1147,8 @@ error: /* Check for interface configuration changes announced through RTNL. */ static void -rtnl_recv_update(const struct ovsrec_open_vswitch *ovs) +rtnl_recv_update(struct ovsdb_idl *idl, + const struct ovsrec_open_vswitch *ovs) { struct ofpbuf *buf; @@ -1030,7 +1158,7 @@ rtnl_recv_update(const struct ovsrec_open_vswitch *ovs) } else if (error == ENOBUFS) { VLOG_WARN_RL(&rl, "network monitor socket overflowed"); } else if (error) { - VLOG_WARN_RL(&rl, "error on network monitor socket: %s", + VLOG_WARN_RL(&rl, "error on network monitor socket: %s", strerror(error)); } else { struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)]; @@ -1043,8 +1171,8 @@ rtnl_recv_update(const struct ovsrec_open_vswitch *ovs) VLOG_WARN_RL(&rl, "received bad rtnl message (no ifinfomsg)"); ofpbuf_delete(buf); return; - } - + } + if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg), rtnlgrp_link_policy, attrs, ARRAY_SIZE(rtnlgrp_link_policy))) { @@ -1064,19 +1192,33 @@ rtnl_recv_update(const struct ovsrec_open_vswitch *ovs) if (!netdev_exists(port_name)) { /* Network device is really gone. */ - struct ovsrec_bridge *br = find_bridge(ovs, br_name); + struct ovsdb_idl_txn *txn; + const struct ovsrec_interface *iface; + struct ovsrec_port *port; + struct ovsrec_bridge *br; VLOG_INFO("network device %s destroyed, " "removing from bridge %s", port_name, br_name); + br = find_bridge(ovs, br_name); if (!br) { - VLOG_WARN("no bridge named %s from which to remove %s", + VLOG_WARN("no bridge named %s from which to remove %s", br_name, port_name); ofpbuf_delete(buf); return; } - del_port(br, port_name); + txn = ovsdb_idl_txn_create(idl); + + iface = find_interface(br, port_name, &port); + if (iface) { + del_interface(br, port, iface); + ovsdb_idl_txn_add_comment(txn, + "ovs-brcompatd: destroy port %s", + port_name); + } + + commit_txn(txn, false); } else { /* A network device by that name exists even though the kernel * told us it had disappeared. Probably, what happened was @@ -1131,27 +1273,28 @@ rtnl_recv_update(const struct ovsrec_open_vswitch *ovs) int main(int argc, char *argv[]) { + extern struct vlog_module VLM_reconnect; struct unixctl_server *unixctl; const char *remote; struct ovsdb_idl *idl; int retval; + proctitle_init(argc, argv); set_program_name(argv[0]); - time_init(); - vlog_init(); - vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_WARN); - vlog_set_levels(VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN); + vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); + vlog_set_levels(&VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN); remote = parse_options(argc, argv); signal(SIGPIPE, SIG_IGN); process_init(); + ovsrec_init(); die_if_already_running(); daemonize_start(); retval = unixctl_server_create(NULL, &unixctl); if (retval) { - ovs_fatal(retval, "could not listen for vlog connections"); + exit(EXIT_FAILURE); } if (brc_open(&brc_sock)) { @@ -1160,40 +1303,42 @@ main(int argc, char *argv[]) } if (prune_timeout) { - if (nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &rtnl_sock)) { - ovs_fatal(0, "could not create rtnetlink socket"); + int error; + + error = nl_sock_create(NETLINK_ROUTE, &rtnl_sock); + if (error) { + ovs_fatal(error, "could not create rtnetlink socket"); + } + + error = nl_sock_join_mcgroup(rtnl_sock, RTNLGRP_LINK); + if (error) { + ovs_fatal(error, "could not join RTNLGRP_LINK multicast group"); } } daemonize_complete(); - idl = ovsdb_idl_create(remote, &ovsrec_idl_class); + idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true); for (;;) { const struct ovsrec_open_vswitch *ovs; - struct ovsdb_idl_txn *txn; - enum ovsdb_idl_txn_status status; ovsdb_idl_run(idl); - txn = ovsdb_idl_txn_create(idl); - unixctl_server_run(unixctl); + brc_recv_update(idl); + ovs = ovsrec_open_vswitch_first(idl); - if (ovs) { - brc_recv_update(ovs); - } else if (ovsdb_idl_get_seqno(idl)) { + if (!ovs && ovsdb_idl_has_ever_connected(idl)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "%s: database does not contain any Open vSwitch " "configuration", remote); - } else { - /* Haven't yet received initial database contents. */ } netdev_run(); /* If 'prune_timeout' is non-zero, we actively prune from the - * configuration of port entries that are no longer valid. We - * use two methods: + * configuration of port entries that are no longer valid. We + * use two methods: * * 1) The kernel explicitly notifies us of removed ports * through the RTNL messages. @@ -1202,47 +1347,11 @@ main(int argc, char *argv[]) * to see if they no longer exist. */ if (ovs && prune_timeout) { - rtnl_recv_update(ovs); -#if 0 - prune_ports(); -#endif - + rtnl_recv_update(idl, ovs); nl_sock_wait(rtnl_sock, POLLIN); poll_timer_wait(prune_timeout); } - while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { - ovsdb_idl_run(idl); - ovsdb_idl_wait(idl); - ovsdb_idl_txn_wait(txn); - poll_block(); - } - ovsdb_idl_txn_destroy(txn); - - switch (status) { - case TXN_INCOMPLETE: - NOT_REACHED(); - - case TXN_ABORTED: - /* Should not happen--we never call ovsdb_idl_txn_abort(). */ - ovs_fatal(0, "transaction aborted"); - - case TXN_SUCCESS: - case TXN_UNCHANGED: - break; - - case TXN_TRY_AGAIN: - /* xxx Handle this better! */ - printf("xxx We need to try again!\n"); - break; - - case TXN_ERROR: - /* xxx Is this what we want to do? */ - ovs_fatal(0, "transaction error"); - - default: - NOT_REACHED(); - } nl_sock_wait(brc_sock, POLLIN); ovsdb_idl_wait(idl); @@ -1284,7 +1393,8 @@ parse_options(int argc, char *argv[]) OPT_PRUNE_TIMEOUT, OPT_APPCTL_COMMAND, VLOG_OPTION_ENUMS, - LEAK_CHECKER_OPTION_ENUMS + LEAK_CHECKER_OPTION_ENUMS, + DAEMON_OPTION_ENUMS }; static struct option long_options[] = { {"help", no_argument, 0, 'h'}, @@ -1298,7 +1408,7 @@ parse_options(int argc, char *argv[]) }; char *short_options = long_options_to_short_options(long_options); - appctl_command = xasprintf("%s/ovs-appctl %%s", ovs_bindir); + appctl_command = xasprintf("%s/ovs-appctl %%s", ovs_bindir()); for (;;) { int c;