struct net_device *dev = ptr;
        struct net_bridge_port *p = dev->br_port;
        unsigned long int flags;
-       uint32_t orig_state, orig_config;
 
 
        /* Check if monitored port */
                return NOTIFY_DONE;
 
        spin_lock_irqsave(&p->lock, flags);
-       orig_state = p->state;
-       orig_config = p->config;
-
        switch (event) {
-               case NETDEV_CHANGE:
-                       if (netif_carrier_ok(p->dev))
-                               p->state &= ~OFPPS_LINK_DOWN;
-                       else
-                               p->state |= OFPPS_LINK_DOWN;
-                       break;
-
-               case NETDEV_DOWN:
-                       p->config |= OFPPC_PORT_DOWN;
-                       break;
-
-               case NETDEV_UP:
-                       p->config &= ~OFPPC_PORT_DOWN;
-                       break;
-
                case NETDEV_UNREGISTER:
                        spin_unlock_irqrestore(&p->lock, flags);
                        mutex_lock(&dp_mutex);
        }
        spin_unlock_irqrestore(&p->lock, flags);
 
-       if ((orig_state != p->state) || (orig_config != p->config))
-               dp_send_port_status(p, OFPPR_MODIFY);
-
        return NOTIFY_DONE;
 }
 
 
 #include "poll-loop.h"
 #include "port-array.h"
 #include "rconn.h"
+#include "shash.h"
+#include "svec.h"
 #include "timeval.h"
 #include "vconn.h"
 #include "xtoxll.h"
     struct port_watcher_local_cb local_cbs[4];
     int n_local_cbs;
     char local_port_name[OFP_MAX_PORT_NAME_LEN + 1];
+    struct netdev_monitor *mon;
+    struct shash port_by_name;
 };
 
 /* Returns the number of fields that differ from 'a' to 'b'. */
     }
 }
 
+static void
+update_netdev_monitor_devices(struct port_watcher *pw)
+{
+    struct ofp_phy_port *p;
+    struct svec netdevs;
+    unsigned int port_no;
+
+    svec_init(&netdevs);
+    shash_clear(&pw->port_by_name);
+    for (p = port_array_first(&pw->ports, &port_no); p;
+         p = port_array_next(&pw->ports, &port_no)) {
+        const char *name = (const char *) p->name;
+        svec_add(&netdevs, name);
+        shash_add(&pw->port_by_name, name, p);
+    }
+    netdev_monitor_set_devices(pw->mon, netdevs.names, netdevs.n);
+    svec_destroy(&netdevs);
+}
+
 static bool
 port_watcher_local_packet_cb(struct relay *r, void *pw_)
 {
             }
         }
 
+        update_netdev_monitor_devices(pw);
+
         call_local_port_changed_callbacks(pw);
     } else if (oh->type == OFPT_PORT_STATUS
                && msg->size >= sizeof(struct ofp_port_status)) {
         if (ops->desc.port_no == htons(OFPP_LOCAL)) {
             call_local_port_changed_callbacks(pw);
         }
+        if (ops->reason == OFPPR_ADD || OFPPR_DELETE) {
+            update_netdev_monitor_devices(pw);
+        }
     }
     return false;
 }
     return false;
 }
 
+/* Sets 'bit' in '*word' to 0 or 1 according to 'value'. */
+static void
+set_bit(uint32_t bit, bool value, uint32_t *word)
+{
+    if (value) {
+        *word |= bit;
+    } else {
+        *word &= ~bit;
+    }
+}
+
 static void
 port_watcher_periodic_cb(void *pw_)
 {
     struct port_watcher *pw = pw_;
+    const char *name;
 
     if (!pw->got_feature_reply
         && time_now() >= pw->last_feature_request + 5
         rconn_send_with_limit(pw->local_rconn, b, &pw->n_txq, 1);
         pw->last_feature_request = time_now();
     }
+
+    netdev_monitor_run(pw->mon);
+    while ((name = netdev_monitor_poll(pw->mon)) != NULL) {
+        struct ofp_phy_port *opp;
+        struct ofp_phy_port new_opp;
+        enum netdev_flags flags;
+        int retval;
+
+        opp = shash_find_data(&pw->port_by_name, name);
+        if (!opp) {
+            continue;
+        }
+
+        retval = netdev_nodev_get_flags(name, &flags);
+        if (retval) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl, "could not get flags for %s", name);
+            continue;
+        }
+
+        new_opp = *opp;
+        set_bit(htonl(OFPPC_PORT_DOWN), flags & NETDEV_UP, &new_opp.config);
+        set_bit(htonl(OFPPS_LINK_DOWN), flags & NETDEV_CARRIER,
+                &new_opp.state);
+        if (opp->config != new_opp.config || opp->state != new_opp.state) {
+            struct ofp_port_status *ops;
+            struct ofpbuf *b;
+
+            /* Notify other secchan modules. */
+            update_phy_port(pw, &new_opp, OFPPR_MODIFY);
+            if (new_opp.port_no == htons(OFPP_LOCAL)) {
+                call_local_port_changed_callbacks(pw);
+            }
+
+            /* Notify the controller that the flags changed. */
+            ops = make_openflow(sizeof *ops, OFPT_PORT_STATUS, &b);
+            ops->reason = OFPPR_MODIFY;
+            ops->desc = new_opp;
+            rconn_send(pw->remote_rconn, b, NULL);
+        }
+    }
 }
 
 static void
             poll_immediate_wake();
         }
     }
+    netdev_monitor_wait(pw->mon);
 }
 
 static void
                    struct port_watcher **pwp)
 {
     struct port_watcher *pw;
+    int retval;
 
     pw = *pwp = xcalloc(1, sizeof *pw);
     pw->local_rconn = local_rconn;
     pw->last_feature_request = TIME_MIN;
     port_array_init(&pw->ports);
     pw->local_port_name[0] = '\0';
+    retval = netdev_monitor_create(&pw->mon);
+    if (retval) {
+        ofp_fatal(retval, "failed to start network device monitoring");
+    }
+    shash_init(&pw->port_by_name);
     port_watcher_register_callback(pw, log_port_status, NULL);
     add_hook(secchan, &port_watcher_hook_class, pw);
 }
 
 static void remote_destroy(struct remote *);
 
 static void update_port_flags(struct datapath *, const struct ofp_port_mod *);
-static int update_port_status(struct sw_port *p);
 static void send_port_status(struct sw_port *p, uint8_t status);
 
 /* Buffers are identified by a 31-bit opaque ID.  We divide the ID
         struct list deleted = LIST_INITIALIZER(&deleted);
         struct sw_flow *f, *n;
 
-        LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
-            if (update_port_status(p)) {
-                send_port_status(p, OFPPR_MODIFY);
-            }
-        }
-
         chain_timeout(dp->chain, &deleted);
         LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) {
             dp_send_flow_end(dp, f, f->reason);
     }
 }
 
-/* Update the port status field of the bridge port.  A non-zero return
- * value indicates some field has changed. 
- *
- * NB: Callers of this function may hold the RCU read lock, so any
- * additional checks must not sleep.
- */
-static int
-update_port_status(struct sw_port *p)
-{
-    int retval;
-    enum netdev_flags flags;
-    uint32_t orig_config = p->config;
-    uint32_t orig_state = p->state;
-
-    if (netdev_get_flags(p->netdev, &flags) < 0) {
-        VLOG_WARN_RL(&rl, "could not get netdev flags for %s", 
-                     netdev_get_name(p->netdev));
-        return 0;
-    }
-
-    if (flags & NETDEV_UP) {
-        p->config &= ~OFPPC_PORT_DOWN;
-    } else {
-        p->config |= OFPPC_PORT_DOWN;
-    }
-
-    if (flags & NETDEV_CARRIER) {
-        p->state &= ~OFPPS_LINK_DOWN;
-    } else {
-        p->state |= OFPPS_LINK_DOWN;
-    }
-
-    return ((orig_config != p->config) || (orig_state != p->state));
-}
-
 static void
 send_port_status(struct sw_port *p, uint8_t status) 
 {