vswitchd: Make the MAC entry aging time configurable.
[sliver-openvswitch.git] / vswitchd / ovs-brcompatd.c
index 04ee2c0..4a80289 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010 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.
 #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"
 #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,7 +230,7 @@ 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 svec *parts,
                     int vlan, bool break_down_bonds)
 {
     struct svec ports;
@@ -260,7 +267,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 svec *ifaces,
                   int vlan)
 {
     do_get_bridge_parts(br, ifaces, vlan, true);
@@ -273,74 +280,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 svec *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; i<bridges.n; i++) {
-        const char *br_name = bridges.names[i];
-        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];
-
-            /* The local port and internal ports are created and destroyed by
-             * ovs-vswitchd itself, so don't bother checking for them at all.
-             * In practice, they might not exist if ovs-vswitchd hasn't
-             * finished reloading since the configuration file was updated. */
-            if (!strcmp(iface_name, br_name)
-                || cfg_get_bool(0, "iface.%s.internal", iface_name)) {
-                continue;
-            }
-
-            if (!netdev_exists(iface_name)) {
-                VLOG_INFO_RL(&rl, "removing dead interface %s from %s",
-                             iface_name, br_name);
-                svec_add(&delete, iface_name);
-            }
-        }
-        svec_destroy(&ifaces);
-    }
-    svec_destroy(&bridges);
-
-    if (delete.n) {
-        size_t i;
-
-        for (i = 0; i < delete.n; i++) {
-            cfg_del_match("bridge.*.port=%s", delete.names[i]);
-            cfg_del_match("bonding.*.slave=%s", delete.names[i]);
-        }
-        reload_config();
-        cfg_unlock();
-    } else {
-        cfg_unlock();
-    }
-    svec_destroy(&delete);
-}
-#endif
-
 static struct ovsdb_idl_txn *
 txn_from_openvswitch(const struct ovsrec_open_vswitch *ovs)
 {
@@ -360,7 +305,7 @@ ovs_insert_bridge(const struct ovsrec_open_vswitch *ovs,
                   struct ovsrec_bridge *bridge)
 {
     struct ovsrec_bridge **bridges;
-    size_t i;     
+    size_t i;
 
     bridges = xmalloc(sizeof *ovs->bridges * (ovs->n_bridges + 1));
     for (i = 0; i < ovs->n_bridges; i++) {
@@ -369,14 +314,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
-add_bridge(const struct ovsrec_open_vswitch *ovs, const char *br_name)
+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(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 +433,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 +479,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 +578,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 +592,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 +639,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 +667,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 +677,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 +696,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 +718,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);
     }
@@ -728,7 +835,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);
@@ -756,7 +863,7 @@ handle_fdb_query_cmd(const struct ovsrec_open_vswitch *ovs,
         struct netdev *netdev;
 
         error = netdev_open_default(iface_name, &netdev);
-        if (netdev) {
+        if (!error) {
             if (!netdev_get_etheraddr(netdev, mac->addr)) {
                 n_local_macs++;
             }
@@ -785,7 +892,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,6 +935,7 @@ handle_fdb_query_cmd(const struct ovsrec_open_vswitch *ovs,
 
     /* Free memory. */
     ofpbuf_uninit(&query_data);
+    free(local_macs);
 
     return 0;
 }
@@ -926,7 +1034,7 @@ 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);
@@ -943,26 +1051,45 @@ handle_get_ports_cmd(const struct ovsrec_open_vswitch *ovs,
     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,9 +1105,11 @@ brc_recv_update(const struct ovsrec_open_vswitch *ovs)
         goto error;
     }
 
-    /* Just drop the request on the floor if a valid configuration
-     * doesn't exist.  We don't immediately do this check, because we
-     * want to drain pending netlink messages. */
+    /* 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;
@@ -988,19 +1117,19 @@ brc_recv_update(const struct ovsrec_open_vswitch *ovs)
 
     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:
@@ -1017,7 +1146,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;
     }
 
@@ -1028,7 +1157,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;
 
@@ -1038,7 +1168,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)];
@@ -1051,8 +1181,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))) {
@@ -1072,19 +1202,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
@@ -1139,20 +1283,21 @@ 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();
@@ -1168,28 +1313,32 @@ 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);
-        ovs = ovsrec_open_vswitch_first(idl);
-        brc_recv_update(ovs);
+        brc_recv_update(idl);
 
+        ovs = ovsrec_open_vswitch_first(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 "
@@ -1198,8 +1347,8 @@ main(int argc, char *argv[])
         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.
@@ -1208,47 +1357,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);
@@ -1290,7 +1403,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'},
@@ -1304,7 +1418,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;