Merge citrix branch into master.
authorBen Pfaff <blp@nicira.com>
Wed, 18 Nov 2009 22:14:29 +0000 (14:14 -0800)
committerBen Pfaff <blp@nicira.com>
Wed, 18 Nov 2009 22:14:29 +0000 (14:14 -0800)
1  2 
datapath/actions.c
ofproto/ofproto.c
vswitchd/bridge.c
xenserver/etc_init.d_vswitch
xenserver/etc_xensource_scripts_vif
xenserver/opt_xensource_libexec_interface-reconfigure

diff --combined datapath/actions.c
  #include "actions.h"
  #include "openvswitch/datapath-protocol.h"
  
- struct sk_buff *
- make_writable(struct sk_buff *skb, gfp_t gfp)
+ static struct sk_buff *
+ make_writable(struct sk_buff *skb, unsigned min_headroom, gfp_t gfp)
  {
        if (skb_shared(skb) || skb_cloned(skb)) {
-               struct sk_buff *nskb = skb_copy(skb, gfp);
+               struct sk_buff *nskb;
+               unsigned headroom = max(min_headroom, skb_headroom(skb));
+               nskb = skb_copy_expand(skb, headroom, skb_tailroom(skb), gfp);
                if (nskb) {
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+                       /* Before 2.6.24 these fields were not copied when
+                        * doing an skb_copy_expand. */
+                       nskb->ip_summed = skb->ip_summed;
+                       nskb->csum = skb->csum;
+ #endif
+ #if defined(CONFIG_XEN) && LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
+                       /* These fields are copied in skb_clone but not in
+                        * skb_copy or related functions.  We need to manually
+                        * copy them over here. */
+                       nskb->proto_data_valid = skb->proto_data_valid;
+                       nskb->proto_csum_blank = skb->proto_csum_blank;
+ #endif
                        kfree_skb(skb);
                        return nskb;
                }
@@@ -80,7 -96,7 +96,7 @@@ modify_vlan_tci(struct datapath *dp, st
                mask = VLAN_PCP_MASK;
        }
  
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, VLAN_HLEN, gfp);
        if (!skb)
                return ERR_PTR(-ENOMEM);
  
                 * when we send the packet out on the wire, and it will fail at
                 * that point because skb_checksum_setup() will not look inside
                 * an 802.1Q header. */
 -              skb_checksum_setup(skb);
 +              vswitch_skb_checksum_setup(skb);
  
                /* GSO is not implemented for packets with an 802.1Q header, so
                 * we have to do segmentation before we add that header.
  static struct sk_buff *strip_vlan(struct sk_buff *skb,
                                  struct odp_flow_key *key, gfp_t gfp)
  {
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, 0, gfp);
        if (skb) {
                vlan_pull_tag(skb);
                key->dl_vlan = htons(ODP_VLAN_NONE);
@@@ -180,7 -196,7 +196,7 @@@ static struct sk_buff *set_dl_addr(stru
                                   const struct odp_action_dl_addr *a,
                                   gfp_t gfp)
  {
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, 0, gfp);
        if (skb) {
                struct ethhdr *eh = eth_hdr(skb);
                memcpy(a->type == ODPAT_SET_DL_SRC ? eh->h_source : eh->h_dest,
@@@ -216,7 -232,7 +232,7 @@@ static struct sk_buff *set_nw_addr(stru
        if (key->dl_type != htons(ETH_P_IP))
                return skb;
  
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, 0, gfp);
        if (skb) {
                struct iphdr *nh = ip_hdr(skb);
                u32 *f = a->type == ODPAT_SET_NW_SRC ? &nh->saddr : &nh->daddr;
@@@ -253,14 -269,14 +269,14 @@@ set_tp_port(struct sk_buff *skb, struc
        else
                return skb;
  
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, 0, gfp);
        if (skb) {
                struct udphdr *th = udp_hdr(skb);
                u16 *f = a->type == ODPAT_SET_TP_SRC ? &th->source : &th->dest;
                u16 old = *f;
                u16 new = a->tp_port;
 -              update_csum((u16*)((u8*)skb->data + check_ofs),
 -                          skb, old, new, 1);
 +              update_csum((u16*)(skb_transport_header(skb) + check_ofs), 
 +                              skb, old, new, 1);
                *f = new;
        }
        return skb;
@@@ -307,7 -323,7 +323,7 @@@ do_output(struct datapath *dp, struct s
        dev = skb->dev = p->dev;
        if (is_dp_dev(dev))
                dp_dev_recv(dev, skb);
 -        else
 +      else
                dp_xmit_skb(skb);
        return;
  
@@@ -361,7 -377,7 +377,7 @@@ int execute_actions(struct datapath *dp
         * then freeing the original skbuff is wasteful.  So the following code
         * is slightly obscure just to avoid that. */
        int prev_port = -1;
 -      int err = 0;
 +      int err;
        for (; n_actions > 0; a++, n_actions--) {
                WARN_ON_ONCE(skb_shared(skb));
                if (prev_port != -1) {
                do_output(dp, skb, prev_port);
        else
                kfree_skb(skb);
 -      return err;
 +      return 0;
  }
diff --combined ofproto/ofproto.c
@@@ -134,7 -134,7 +134,7 @@@ rule_is_hidden(const struct rule *rule
          return true;
      }
  
 -    /* Rules with priority higher than UINT16_MAX are set up by secchan itself
 +    /* Rules with priority higher than UINT16_MAX are set up by ofproto itself
       * (e.g. by in-band control) and are intentionally hidden from the
       * controller. */
      if (rule->cr.priority > UINT16_MAX) {
@@@ -193,8 -193,8 +193,8 @@@ struct ofproto 
      char *serial;               /* Serial number. */
  
      /* Datapath. */
 -    struct dpif dpif;
 -    struct dpifmon *dpifmon;
 +    struct dpif *dpif;
 +    struct netdev_monitor *netdev_monitor;
      struct port_array ports;    /* Index is ODP port nr; ofport->opp.port_no is
                                   * OFP port nr. */
      struct shash port_by_name;
@@@ -236,7 -236,7 +236,7 @@@ static struct vlog_rate_limit rl = VLOG
  
  static const struct ofhooks default_ofhooks;
  
 -static uint64_t pick_datapath_id(struct dpif *, uint64_t fallback_dpid);
 +static uint64_t pick_datapath_id(const struct ofproto *);
  static uint64_t pick_fallback_dpid(void);
  static void send_packet_in_miss(struct ofpbuf *, void *ofproto);
  static void send_packet_in_action(struct ofpbuf *, void *ofproto);
@@@ -262,9 -262,10 +262,9 @@@ in
  ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
                 struct ofproto **ofprotop)
  {
 -    struct dpifmon *dpifmon;
      struct odp_stats stats;
      struct ofproto *p;
 -    struct dpif dpif;
 +    struct dpif *dpif;
      int error;
  
      *ofprotop = NULL;
          VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error));
          return error;
      }
 -    error = dpif_get_dp_stats(&dpif, &stats);
 +    error = dpif_get_dp_stats(dpif, &stats);
      if (error) {
          VLOG_ERR("failed to obtain stats for datapath %s: %s",
                   datapath, strerror(error));
 -        dpif_close(&dpif);
 +        dpif_close(dpif);
          return error;
      }
 -    error = dpif_set_listen_mask(&dpif, ODPL_MISS | ODPL_ACTION);
 +    error = dpif_recv_set_mask(dpif, ODPL_MISS | ODPL_ACTION);
      if (error) {
          VLOG_ERR("failed to listen on datapath %s: %s",
                   datapath, strerror(error));
 -        dpif_close(&dpif);
 -        return error;
 -    }
 -    dpif_flow_flush(&dpif);
 -    dpif_purge(&dpif);
 -
 -    /* Start monitoring datapath ports for status changes. */
 -    error = dpifmon_create(datapath, &dpifmon);
 -    if (error) {
 -        VLOG_ERR("failed to starting monitoring datapath %s: %s",
 -                 datapath, strerror(error));
 -        dpif_close(&dpif);
 +        dpif_close(dpif);
          return error;
      }
 +    dpif_flow_flush(dpif);
 +    dpif_recv_purge(dpif);
  
      /* Initialize settings. */
      p = xcalloc(1, sizeof *p);
      p->fallback_dpid = pick_fallback_dpid();
 -    p->datapath_id = pick_datapath_id(&dpif, p->fallback_dpid);
 -    VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
 +    p->datapath_id = p->fallback_dpid;
      p->manufacturer = xstrdup("Nicira Networks, Inc.");
      p->hardware = xstrdup("Reference Implementation");
      p->software = xstrdup(VERSION BUILDNR);
  
      /* Initialize datapath. */
      p->dpif = dpif;
 -    p->dpifmon = dpifmon;
 +    p->netdev_monitor = netdev_monitor_create();
      port_array_init(&p->ports);
      shash_init(&p->port_by_name);
      p->max_ports = stats.max_ports;
          return error;
      }
  
 +    /* Pick final datapath ID. */
 +    p->datapath_id = pick_datapath_id(p);
 +    VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
 +
      *ofprotop = p;
      return 0;
  }
@@@ -367,7 -374,9 +367,7 @@@ voi
  ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
  {
      uint64_t old_dpid = p->datapath_id;
 -    p->datapath_id = (datapath_id
 -                      ? datapath_id
 -                      : pick_datapath_id(&p->dpif, p->fallback_dpid));
 +    p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
      if (p->datapath_id != old_dpid) {
          VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id);
          rconn_reconnect(p->controller->rconn);
@@@ -425,8 -434,9 +425,8 @@@ ofproto_set_in_band(struct ofproto *p, 
  {
      if (in_band != (p->in_band != NULL)) {
          if (in_band) {
 -            in_band_create(p, &p->dpif, p->switch_status, 
 -                           p->controller->rconn, &p->in_band);
 -            return 0;
 +            return in_band_create(p, p->dpif, p->switch_status,
 +                                  p->controller->rconn, &p->in_band);
          } else {
              ofproto_set_discovery(p, false, NULL, true);
              in_band_destroy(p->in_band);
@@@ -448,7 -458,7 +448,7 @@@ ofproto_set_discovery(struct ofproto *p
                  return error;
              }
              error = discovery_create(re, update_resolv_conf,
 -                                     &p->dpif, p->switch_status,
 +                                     p->dpif, p->switch_status,
                                       &p->discovery);
              if (error) {
                  return error;
@@@ -703,8 -713,8 +703,8 @@@ ofproto_destroy(struct ofproto *p
          ofconn_destroy(ofconn, p);
      }
  
 -    dpif_close(&p->dpif);
 -    dpifmon_destroy(p->dpifmon);
 +    dpif_close(p->dpif);
 +    netdev_monitor_destroy(p->netdev_monitor);
      PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
          ofport_free(ofport);
      }
@@@ -746,17 -756,6 +746,17 @@@ ofproto_run(struct ofproto *p
      return error;
  }
  
 +static void
 +process_port_change(struct ofproto *ofproto, int error, char *devname)
 +{
 +    if (error == ENOBUFS) {
 +        reinit_ports(ofproto);
 +    } else if (!error) {
 +        update_port(ofproto, devname);
 +        free(devname);
 +    }
 +}
 +
  int
  ofproto_run1(struct ofproto *p)
  {
          struct ofpbuf *buf;
          int error;
  
 -        error = dpif_recv(&p->dpif, &buf);
 +        error = dpif_recv(p->dpif, &buf);
          if (error) {
              if (error == ENODEV) {
                  /* Someone destroyed the datapath behind our back.  The caller
                   * better destroy us and give up, because we're just going to
                   * spin from here on out. */
                  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 -                VLOG_ERR_RL(&rl, "dp%u: datapath was destroyed externally",
 -                            dpif_id(&p->dpif));
 +                VLOG_ERR_RL(&rl, "%s: datapath was destroyed externally",
 +                            dpif_name(p->dpif));
                  return ENODEV;
              }
              break;
          handle_odp_msg(p, buf);
      }
  
 -    while ((error = dpifmon_poll(p->dpifmon, &devname)) != EAGAIN) {
 -        if (error == ENOBUFS) {
 -            reinit_ports(p);
 -        } else if (!error) {
 -            update_port(p, devname);
 -            free(devname);
 -        }
 +    while ((error = dpif_port_poll(p->dpif, &devname)) != EAGAIN) {
 +        process_port_change(p, error, devname);
 +    }
 +    while ((error = netdev_monitor_poll(p->netdev_monitor,
 +                                        &devname)) != EAGAIN) {
 +        process_port_change(p, error, devname);
      }
  
      if (p->in_band) {
@@@ -906,9 -906,8 +906,9 @@@ ofproto_wait(struct ofproto *p
      struct ofconn *ofconn;
      size_t i;
  
 -    dpif_recv_wait(&p->dpif);
 -    dpifmon_wait(p->dpifmon);
 +    dpif_recv_wait(p->dpif);
 +    dpif_port_poll_wait(p->dpif);
 +    netdev_monitor_poll_wait(p->netdev_monitor);
      LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
          ofconn_wait(ofconn);
      }
@@@ -978,7 -977,7 +978,7 @@@ ofproto_send_packet(struct ofproto *p, 
  
      /* XXX Should we translate the dpif_execute() errno value into an OpenFlow
       * error code? */
 -    dpif_execute(&p->dpif, flow->in_port, odp_actions.actions,
 +    dpif_execute(p->dpif, flow->in_port, odp_actions.actions,
                   odp_actions.n_actions, packet);
      return 0;
  }
@@@ -1030,7 -1029,7 +1030,7 @@@ ofproto_flush_flows(struct ofproto *ofp
  {
      COVERAGE_INC(ofproto_flush);
      classifier_for_each(&ofproto->cls, CLS_INC_ALL, destroy_rule, ofproto);
 -    dpif_flow_flush(&ofproto->dpif);
 +    dpif_flow_flush(ofproto->dpif);
      if (ofproto->in_band) {
          in_band_flushed(ofproto->in_band);
      }
@@@ -1053,7 -1052,7 +1053,7 @@@ reinit_ports(struct ofproto *p
      PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
          svec_add (&devnames, (char *) ofport->opp.name);
      }
 -    dpif_port_list(&p->dpif, &odp_ports, &n_odp_ports);
 +    dpif_port_list(p->dpif, &odp_ports, &n_odp_ports);
      for (i = 0; i < n_odp_ports; i++) {
          svec_add (&devnames, odp_ports[i].devname);
      }
@@@ -1083,7 -1082,7 +1083,7 @@@ refresh_port_group(struct ofproto *p, u
              ports[n_ports++] = port_no;
          }
      }
 -    dpif_port_group_set(&p->dpif, group, ports, n_ports);
 +    dpif_port_group_set(p->dpif, group, ports, n_ports);
      free(ports);
  }
  
@@@ -1115,7 -1114,7 +1115,7 @@@ make_ofport(const struct odp_port *odp_
      ofport = xmalloc(sizeof *ofport);
      ofport->netdev = netdev;
      ofport->opp.port_no = odp_port_to_ofp_port(odp_port->port);
 -    memcpy(ofport->opp.hw_addr, netdev_get_etheraddr(netdev), ETH_ALEN);
 +    netdev_get_etheraddr(netdev, ofport->opp.hw_addr);
      memcpy(ofport->opp.name, odp_port->devname,
             MIN(sizeof ofport->opp.name, sizeof odp_port->devname));
      ofport->opp.name[sizeof ofport->opp.name - 1] = '\0';
@@@ -1190,7 -1189,6 +1190,7 @@@ send_port_status(struct ofproto *p, con
  static void
  ofport_install(struct ofproto *p, struct ofport *ofport)
  {
 +    netdev_monitor_add(p->netdev_monitor, ofport->netdev);
      port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no),
                     ofport);
      shash_add(&p->port_by_name, (char *) ofport->opp.name, ofport);
  static void
  ofport_remove(struct ofproto *p, struct ofport *ofport)
  {
 +    netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
      port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no), NULL);
      shash_delete(&p->port_by_name,
                   shash_find(&p->port_by_name, (char *) ofport->opp.name));
@@@ -1225,7 -1222,7 +1225,7 @@@ update_port(struct ofproto *p, const ch
      COVERAGE_INC(ofproto_update_port);
  
      /* Query the datapath for port information. */
 -    error = dpif_port_query_by_name(&p->dpif, devname, &odp_port);
 +    error = dpif_port_query_by_name(p->dpif, devname, &odp_port);
  
      /* Find the old ofport. */
      old_ofport = shash_find_data(&p->port_by_name, devname);
@@@ -1294,7 -1291,7 +1294,7 @@@ init_ports(struct ofproto *p
      size_t i;
      int error;
  
 -    error = dpif_port_list(&p->dpif, &ports, &n_ports);
 +    error = dpif_port_list(p->dpif, &ports, &n_ports);
      if (error) {
          return error;
      }
@@@ -1502,7 -1499,7 +1502,7 @@@ rule_execute(struct ofproto *ofproto, s
      }
  
      /* Execute the ODP actions. */
 -    if (!dpif_execute(&ofproto->dpif, flow->in_port,
 +    if (!dpif_execute(ofproto->dpif, flow->in_port,
                        actions, n_actions, packet)) {
          struct odp_flow_stats stats;
          flow_extract_stats(flow, packet, &stats);
@@@ -1613,7 -1610,7 +1613,7 @@@ do_put_flow(struct ofproto *ofproto, st
      put->flow.actions = rule->odp_actions;
      put->flow.n_actions = rule->n_odp_actions;
      put->flags = flags;
 -    return dpif_flow_put(&ofproto->dpif, put);
 +    return dpif_flow_put(ofproto->dpif, put);
  }
  
  static void
@@@ -1628,7 -1625,7 +1628,7 @@@ rule_install(struct ofproto *p, struct 
                           &put)) {
              rule->installed = true;
              if (displaced_rule) {
-                 update_stats(p, rule, &put.flow.stats);
+                 update_stats(p, displaced_rule, &put.flow.stats);
                  rule_post_uninstall(p, displaced_rule);
              }
          }
@@@ -1652,14 -1649,27 +1652,27 @@@ rule_reinstall(struct ofproto *ofproto
  static void
  rule_update_actions(struct ofproto *ofproto, struct rule *rule)
  {
-     bool actions_changed = rule_make_actions(ofproto, rule, NULL);
+     bool actions_changed;
+     uint16_t new_out_iface, old_out_iface;
+     old_out_iface = rule->nf_flow.output_iface;
+     actions_changed = rule_make_actions(ofproto, rule, NULL);
      if (rule->may_install) {
          if (rule->installed) {
              if (actions_changed) {
-                 /* XXX should really do rule_post_uninstall() for the *old* set
-                  * of actions, and distinguish the old stats from the new. */
                  struct odp_flow_put put;
-                 do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY, &put);
+                 do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY
+                                            | ODPPF_ZERO_STATS, &put);
+                 update_stats(ofproto, rule, &put.flow.stats);
+                 /* Temporarily set the old output iface so that NetFlow
+                  * messages have the correct output interface for the old
+                  * stats. */
+                 new_out_iface = rule->nf_flow.output_iface;
+                 rule->nf_flow.output_iface = old_out_iface;
+                 rule_post_uninstall(ofproto, rule);
+                 rule->nf_flow.output_iface = new_out_iface;
              }
          } else {
              rule_install(ofproto, rule, NULL);
@@@ -1694,7 -1704,7 +1707,7 @@@ rule_uninstall(struct ofproto *p, struc
          odp_flow.key = rule->cr.flow;
          odp_flow.actions = NULL;
          odp_flow.n_actions = 0;
 -        if (!dpif_flow_del(&p->dpif, &odp_flow)) {
 +        if (!dpif_flow_del(p->dpif, &odp_flow)) {
              update_stats(p, rule, &odp_flow.stats);
          }
          rule->installed = false;
@@@ -1854,7 -1864,7 +1867,7 @@@ handle_get_config_request(struct ofprot
      bool drop_frags;
  
      /* Figure out flags. */
 -    dpif_get_drop_frags(&p->dpif, &drop_frags);
 +    dpif_get_drop_frags(p->dpif, &drop_frags);
      flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
      if (ofconn->send_flow_exp) {
          flags |= OFPC_SEND_FLOW_EXP;
@@@ -1887,10 -1897,10 +1900,10 @@@ handle_set_config(struct ofproto *p, st
      if (ofconn == p->controller) {
          switch (flags & OFPC_FRAG_MASK) {
          case OFPC_FRAG_NORMAL:
 -            dpif_set_drop_frags(&p->dpif, false);
 +            dpif_set_drop_frags(p->dpif, false);
              break;
          case OFPC_FRAG_DROP:
 -            dpif_set_drop_frags(&p->dpif, true);
 +            dpif_set_drop_frags(p->dpif, true);
              break;
          default:
              VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
@@@ -2147,21 -2157,11 +2160,21 @@@ do_xlate_actions(const union ofp_actio
              oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
              break;
  
 +        case OFPAT_SET_NW_DST:
 +            oa = odp_actions_add(ctx->out, ODPAT_SET_NW_DST);
 +            oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
 +            break;
 +
          case OFPAT_SET_TP_SRC:
              oa = odp_actions_add(ctx->out, ODPAT_SET_TP_SRC);
              oa->tp_port.tp_port = ia->tp_port.tp_port;
              break;
  
 +        case OFPAT_SET_TP_DST:
 +            oa = odp_actions_add(ctx->out, ODPAT_SET_TP_DST);
 +            oa->tp_port.tp_port = ia->tp_port.tp_port;
 +            break;
 +
          case OFPAT_VENDOR:
              xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
              break;
@@@ -2250,7 -2250,7 +2263,7 @@@ handle_packet_out(struct ofproto *p, st
          return error;
      }
  
 -    dpif_execute(&p->dpif, flow.in_port, actions.actions, actions.n_actions,
 +    dpif_execute(p->dpif, flow.in_port, actions.actions, actions.n_actions,
                   &payload);
      ofpbuf_delete(buffer);
  
@@@ -2393,7 -2393,7 +2406,7 @@@ handle_table_stats_request(struct ofpro
      n_wild = classifier_count(&p->cls) - classifier_count_exact(&p->cls);
  
      /* Hash table. */
 -    dpif_get_dp_stats(&p->dpif, &dpstats);
 +    dpif_get_dp_stats(p->dpif, &dpstats);
      ots = append_stats_reply(sizeof *ots, ofconn, &msg);
      memset(ots, 0, sizeof *ots);
      ots->table_id = TABLEID_HASH;
@@@ -2491,9 -2491,7 +2504,9 @@@ query_stats(struct ofproto *p, struct r
          odp_flows[0].key = rule->cr.flow;
      }
  
 -    if (!dpif_flow_get_multiple(&p->dpif, odp_flows, n_odp_flows)) {
 +    packet_count = rule->packet_count;
 +    byte_count = rule->byte_count;
 +    if (!dpif_flow_get_multiple(p->dpif, odp_flows, n_odp_flows)) {
          size_t i;
          for (i = 0; i < n_odp_flows; i++) {
              struct odp_flow *odp_flow = &odp_flows[i];
@@@ -2743,9 -2741,6 +2756,9 @@@ update_time(struct ofproto *ofproto, st
      long long int used = msec_from_nsec(stats->used_sec, stats->used_nsec);
      if (used > rule->used) {
          rule->used = used;
 +        if (rule->super && used > rule->super->used) {
 +            rule->super->used = used;
 +        }
          netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, used);
      }
  }
@@@ -3123,7 -3118,7 +3136,7 @@@ handle_odp_msg(struct ofproto *p, struc
          memset(&action, 0, sizeof(action));
          action.output.type = ODPAT_OUTPUT;
          action.output.port = ODPP_LOCAL;
 -        dpif_execute(&p->dpif, flow.in_port, &action, 1, &payload);
 +        dpif_execute(p->dpif, flow.in_port, &action, 1, &payload);
      }
  
      rule = lookup_valid_rule(p, &flow);
@@@ -3245,7 -3240,7 +3258,7 @@@ send_flow_exp(struct ofproto *p, struc
  {
      struct ofconn *ofconn;
      struct ofconn *prev;
 -    struct ofpbuf *buf;
 +    struct ofpbuf *buf = NULL;
  
      /* We limit the maximum number of queued flow expirations it by accounting
       * them under the counter for replies.  That works because preventing
@@@ -3310,19 -3305,23 +3323,23 @@@ expire_rule(struct cls_rule *cls_rule, 
      }
  
      COVERAGE_INC(ofproto_expired);
+     /* Update stats.  This code will be a no-op if the rule expired
+      * due to an idle timeout. */
      if (rule->cr.wc.wildcards) {
-         /* Update stats.  (This code will be a no-op if the rule expired
-          * due to an idle timeout, because in that case the rule has no
-          * subrules left.) */
          struct rule *subrule, *next;
          LIST_FOR_EACH_SAFE (subrule, next, struct rule, list, &rule->list) {
              rule_remove(p, subrule);
          }
+     } else {
+         rule_uninstall(p, rule);
      }
  
-     send_flow_exp(p, rule, now,
-                   (now >= hard_expire
-                    ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
+     if (!rule_is_hidden(rule)) {
+         send_flow_exp(p, rule, now,
+                       (now >= hard_expire
+                        ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
+     }
      rule_remove(p, rule);
  }
  
@@@ -3339,7 -3338,7 +3356,7 @@@ active_timeout(struct ofproto *ofproto
          if (rule->installed) {
              odp_flow.key = rule->cr.flow;
              odp_flow.flags = ODPFF_ZERO_TCP_FLAGS;
 -            dpif_flow_get(&ofproto->dpif, &odp_flow);
 +            dpif_flow_get(ofproto->dpif, &odp_flow);
  
              if (odp_flow.stats.n_packets) {
                  update_time(ofproto, rule, &odp_flow.stats);
@@@ -3370,7 -3369,7 +3387,7 @@@ update_used(struct ofproto *p
      size_t i;
      int error;
  
 -    error = dpif_flow_list_all(&p->dpif, &flows, &n_flows);
 +    error = dpif_flow_list_all(p->dpif, &flows, &n_flows);
      if (error) {
          return;
      }
              classifier_find_rule_exactly(&p->cls, &f->key, 0, UINT16_MAX));
          if (!rule || !rule->installed) {
              COVERAGE_INC(ofproto_unexpected_rule);
 -            dpif_flow_del(&p->dpif, f);
 +            dpif_flow_del(p->dpif, f);
              continue;
          }
  
@@@ -3458,23 -3457,23 +3475,23 @@@ send_packet_in_miss(struct ofpbuf *pack
  }
  
  static uint64_t
 -pick_datapath_id(struct dpif *dpif, uint64_t fallback_dpid)
 +pick_datapath_id(const struct ofproto *ofproto)
  {
 -    char local_name[IF_NAMESIZE];
 -    uint8_t ea[ETH_ADDR_LEN];
 -    int error;
 +    const struct ofport *port;
  
 -    error = dpif_get_name(dpif, local_name, sizeof local_name);
 -    if (!error) {
 -        error = netdev_nodev_get_etheraddr(local_name, ea);
 +    port = port_array_get(&ofproto->ports, ODPP_LOCAL);
 +    if (port) {
 +        uint8_t ea[ETH_ADDR_LEN];
 +        int error;
 +
 +        error = netdev_get_etheraddr(port->netdev, ea);
          if (!error) {
              return eth_addr_to_uint64(ea);
          }
          VLOG_WARN("could not get MAC address for %s (%s)",
 -                  local_name, strerror(error));
 +                  netdev_get_name(port->netdev), strerror(error));
      }
 -
 -    return fallback_dpid;
 +    return ofproto->fallback_dpid;
  }
  
  static uint64_t
diff --combined vswitchd/bridge.c
  #include "odp-util.h"
  #include "ofp-print.h"
  #include "ofpbuf.h"
 +#include "ofproto/netflow.h"
 +#include "ofproto/ofproto.h"
  #include "packets.h"
  #include "poll-loop.h"
  #include "port-array.h"
  #include "proc-net-compat.h"
  #include "process.h"
 -#include "secchan/netflow.h"
 -#include "secchan/ofproto.h"
  #include "socket-util.h"
  #include "stp.h"
  #include "svec.h"
@@@ -72,18 -72,17 +72,18 @@@ struct dst 
  extern uint64_t mgmt_id;
  
  struct iface {
 +    /* These members are always valid. */
      struct port *port;          /* Containing port. */
      size_t port_ifidx;          /* Index within containing port. */
 -
      char *name;                 /* Host network device name. */
 -    int dp_ifidx;               /* Index within kernel datapath. */
 -
 -    uint8_t mac[ETH_ADDR_LEN];  /* Ethernet address (all zeros if unknowns). */
 -
      tag_type tag;               /* Tag associated with this interface. */
 -    bool enabled;               /* May be chosen for flows? */
      long long delay_expires;    /* Time after which 'enabled' may change. */
 +
 +    /* These members are valid only after bridge_reconfigure() causes them to
 +     * be initialized.*/
 +    int dp_ifidx;               /* Index within kernel datapath. */
 +    struct netdev *netdev;      /* Network device. */
 +    bool enabled;               /* May be chosen for flows? */
  };
  
  #define BOND_MASK 0xff
@@@ -161,7 -160,7 +161,7 @@@ struct bridge 
      struct ofproto *ofproto;    /* OpenFlow switch. */
  
      /* Kernel datapath information. */
 -    struct dpif dpif;           /* Kernel datapath. */
 +    struct dpif *dpif;          /* Datapath. */
      struct port_array ifaces;   /* Indexed by kernel datapath port number. */
  
      /* Bridge ports. */
@@@ -204,11 -203,10 +204,11 @@@ static void bridge_fetch_dp_ifaces(stru
  static void bridge_flush(struct bridge *);
  static void bridge_pick_local_hw_addr(struct bridge *,
                                        uint8_t ea[ETH_ADDR_LEN],
 -                                      const char **devname);
 +                                      struct iface **hw_addr_iface);
  static uint64_t bridge_pick_datapath_id(struct bridge *,
                                          const uint8_t bridge_ea[ETH_ADDR_LEN],
 -                                        const char *devname);
 +                                        struct iface *hw_addr_iface);
 +static struct iface *bridge_get_local_iface(struct bridge *);
  static uint64_t dpid_from_hash(const void *, size_t nbytes);
  
  static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args);
@@@ -229,7 -227,6 +229,7 @@@ static struct port *port_from_dp_ifidx(
                                         uint16_t dp_ifidx);
  static void port_update_bond_compat(struct port *);
  static void port_update_vlan_compat(struct port *);
 +static void port_update_bonding(struct port *);
  
  static void mirror_create(struct bridge *, const char *name);
  static void mirror_destroy(struct mirror *);
@@@ -270,8 -267,8 +270,8 @@@ bridge_get_ifaces(struct svec *svec
              for (j = 0; j < port->n_ifaces; j++) {
                  struct iface *iface = port->ifaces[j];
                  if (iface->dp_ifidx < 0) {
 -                    VLOG_ERR("%s interface not in dp%u, ignoring",
 -                             iface->name, dpif_id(&br->dpif));
 +                    VLOG_ERR("%s interface not in datapath %s, ignoring",
 +                             iface->name, dpif_name(br->dpif));
                  } else {
                      if (iface->dp_ifidx != ODPP_LOCAL) {
                          svec_add(svec, iface->name);
  void
  bridge_init(void)
  {
 -    int retval;
 -    int i;
 -
 -    bond_init();
 +    struct svec dpif_names;
 +    size_t i;
  
      unixctl_command_register("fdb/show", bridge_unixctl_fdb_show);
  
 -    for (i = 0; i < DP_MAX; i++) {
 -        struct dpif dpif;
 -        char devname[16];
 +    svec_init(&dpif_names);
 +    dp_enumerate(&dpif_names);
 +    for (i = 0; i < dpif_names.n; i++) {
 +        const char *dpif_name = dpif_names.names[i];
 +        struct dpif *dpif;
 +        int retval;
  
 -        sprintf(devname, "dp%d", i);
 -        retval = dpif_open(devname, &dpif);
 +        retval = dpif_open(dpif_name, &dpif);
          if (!retval) {
 -            char dpif_name[IF_NAMESIZE];
 -            if (dpif_get_name(&dpif, dpif_name, sizeof dpif_name)
 -                || !cfg_has("bridge.%s.port", dpif_name)) {
 -                dpif_delete(&dpif);
 +            struct svec all_names;
 +            size_t j;
 +
 +            svec_init(&all_names);
 +            dpif_get_all_names(dpif, &all_names);
 +            for (j = 0; j < all_names.n; j++) {
 +                if (cfg_has("bridge.%s.port", all_names.names[j])) {
 +                    goto found;
 +                }
              }
 -            dpif_close(&dpif);
 -        } else if (retval != ENODEV) {
 -            VLOG_ERR("failed to delete datapath dp%d: %s",
 -                     i, strerror(retval));
 +            dpif_delete(dpif);
 +        found:
 +            svec_destroy(&all_names);
 +            dpif_close(dpif);
          }
      }
 +    svec_destroy(&dpif_names);
  
      unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
  
 +    bond_init();
      bridge_reconfigure();
  }
  
@@@ -362,116 -352,43 +362,116 @@@ bridge_configure_ssl(void
       * the old certificate will still be trusted until vSwitch is
       * restarted.  We may want to address this in vconn's SSL library. */
      if (config_string_change("ssl.ca-cert", &cacert_file)
 -            || (stat(cacert_file, &s) && errno == ENOENT)) {
 +        || (cacert_file && stat(cacert_file, &s) && errno == ENOENT)) {
          vconn_ssl_set_ca_cert_file(cacert_file,
                                     cfg_get_bool(0, "ssl.bootstrap-ca-cert"));
      }
  }
  #endif
  
 +/* iterate_and_prune_ifaces() callback function that opens the network device
 + * for 'iface', if it is not already open, and retrieves the interface's MAC
 + * address and carrier status. */
 +static bool
 +init_iface_netdev(struct bridge *br UNUSED, struct iface *iface,
 +                  void *aux UNUSED)
 +{
 +    if (iface->netdev) {
 +        return true;
 +    } else if (!netdev_open(iface->name, NETDEV_ETH_TYPE_NONE,
 +                            &iface->netdev)) {
 +        netdev_get_carrier(iface->netdev, &iface->enabled);
 +        return true;
 +    } else {
 +        /* If the network device can't be opened, then we're not going to try
 +         * to do anything with this interface. */
 +        return false;
 +    }
 +}
 +
 +static bool
 +check_iface_dp_ifidx(struct bridge *br, struct iface *iface, void *aux UNUSED)
 +{
 +    if (iface->dp_ifidx >= 0) {
 +        VLOG_DBG("%s has interface %s on port %d",
 +                 dpif_name(br->dpif),
 +                 iface->name, iface->dp_ifidx);
 +        return true;
 +    } else {
 +        VLOG_ERR("%s interface not in %s, dropping",
 +                 iface->name, dpif_name(br->dpif));
 +        return false;
 +    }
 +}
 +
 +static bool
 +set_iface_properties(struct bridge *br UNUSED, struct iface *iface,
 +                   void *aux UNUSED)
 +{
 +    int rate, burst;
 +
 +    /* Set policing attributes. */
 +    rate = cfg_get_int(0, "port.%s.ingress.policing-rate", iface->name);
 +    burst = cfg_get_int(0, "port.%s.ingress.policing-burst", iface->name);
 +    netdev_set_policing(iface->netdev, rate, burst);
 +
 +    /* Set MAC address of internal interfaces other than the local
 +     * interface. */
 +    if (iface->dp_ifidx != ODPP_LOCAL
 +        && iface_is_internal(br, iface->name)) {
 +        iface_set_mac(iface);
 +    }
 +
 +    return true;
 +}
 +
 +/* Calls 'cb' for each interfaces in 'br', passing along the 'aux' argument.
 + * Deletes from 'br' all the interfaces for which 'cb' returns false, and then
 + * deletes from 'br' any ports that no longer have any interfaces. */
 +static void
 +iterate_and_prune_ifaces(struct bridge *br,
 +                         bool (*cb)(struct bridge *, struct iface *,
 +                                    void *aux),
 +                         void *aux)
 +{
 +    size_t i, j;
 +
 +    for (i = 0; i < br->n_ports; ) {
 +        struct port *port = br->ports[i];
 +        for (j = 0; j < port->n_ifaces; ) {
 +            struct iface *iface = port->ifaces[j];
 +            if (cb(br, iface, aux)) {
 +                j++;
 +            } else {
 +                iface_destroy(iface);
 +            }
 +        }
 +
 +        if (port->n_ifaces) {
 +            i++;
 +        } else  {
 +            VLOG_ERR("%s port has no interfaces, dropping", port->name);
 +            port_destroy(port);
 +        }
 +    }
 +}
 +
  void
  bridge_reconfigure(void)
  {
 -    struct svec old_br, new_br, raw_new_br;
 +    struct svec old_br, new_br;
      struct bridge *br, *next;
 -    size_t i, j;
 +    size_t i;
  
      COVERAGE_INC(bridge_reconfigure);
  
 -    /* Collect old bridges. */
 +    /* Collect old and new bridges. */
      svec_init(&old_br);
 +    svec_init(&new_br);
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          svec_add(&old_br, br->name);
      }
 -
 -    /* Collect new bridges. */
 -    svec_init(&raw_new_br);
 -    cfg_get_subsections(&raw_new_br, "bridge");
 -    svec_init(&new_br);
 -    for (i = 0; i < raw_new_br.n; i++) {
 -        const char *name = raw_new_br.names[i];
 -        if ((!strncmp(name, "dp", 2) && isdigit(name[2])) ||
 -            (!strncmp(name, "nl:", 3) && isdigit(name[3]))) {
 -            VLOG_ERR("%s is not a valid bridge name (bridges may not be "
 -                     "named \"dp\" or \"nl:\" followed by a digit)", name);
 -        } else {
 -            svec_add(&new_br, name);
 -        }
 -    }
 -    svec_destroy(&raw_new_br);
 +    cfg_get_subsections(&new_br, "bridge");
  
      /* Get rid of deleted bridges and add new bridges. */
      svec_sort(&old_br);
          size_t n_dpif_ports;
          struct svec want_ifaces;
  
 -        dpif_port_list(&br->dpif, &dpif_ports, &n_dpif_ports);
 +        dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
          bridge_get_all_ifaces(br, &want_ifaces);
          for (i = 0; i < n_dpif_ports; i++) {
              const struct odp_port *p = &dpif_ports[i];
              if (!svec_contains(&want_ifaces, p->devname)
                  && strcmp(p->devname, br->name)) {
 -                int retval = dpif_port_del(&br->dpif, p->port);
 +                int retval = dpif_port_del(br->dpif, p->port);
                  if (retval) {
 -                    VLOG_ERR("failed to remove %s interface from dp%u: %s",
 -                             p->devname, dpif_id(&br->dpif), strerror(retval));
 +                    VLOG_ERR("failed to remove %s interface from %s: %s",
 +                             p->devname, dpif_name(br->dpif),
 +                             strerror(retval));
                  }
              }
          }
          struct odp_port *dpif_ports;
          size_t n_dpif_ports;
          struct svec cur_ifaces, want_ifaces, add_ifaces;
 -        int next_port_no;
  
 -        dpif_port_list(&br->dpif, &dpif_ports, &n_dpif_ports);
 +        dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
          svec_init(&cur_ifaces);
          for (i = 0; i < n_dpif_ports; i++) {
              svec_add(&cur_ifaces, dpif_ports[i].devname);
          bridge_get_all_ifaces(br, &want_ifaces);
          svec_diff(&want_ifaces, &cur_ifaces, &add_ifaces, NULL, NULL);
  
 -        next_port_no = 1;
          for (i = 0; i < add_ifaces.n; i++) {
              const char *if_name = add_ifaces.names[i];
 -            for (;;) {
 -                bool internal;
 -                int error;
 -
 -                /* Add to datapath. */
 -                internal = iface_is_internal(br, if_name);
 -                error = dpif_port_add(&br->dpif, if_name, next_port_no++,
 -                                      internal ? ODP_PORT_INTERNAL : 0);
 -                if (error != EEXIST) {
 -                    if (next_port_no >= 256) {
 -                        VLOG_ERR("ran out of valid port numbers on dp%u",
 -                                 dpif_id(&br->dpif));
 -                        goto out;
 -                    }
 -                    if (error) {
 -                        VLOG_ERR("failed to add %s interface to dp%u: %s",
 -                                 if_name, dpif_id(&br->dpif), strerror(error));
 -                    }
 -                    break;
 -                }
 +            bool internal;
 +            int error;
 +
 +            /* Add to datapath. */
 +            internal = iface_is_internal(br, if_name);
 +            error = dpif_port_add(br->dpif, if_name,
 +                                  internal ? ODP_PORT_INTERNAL : 0, NULL);
 +            if (error == EFBIG) {
 +                VLOG_ERR("ran out of valid port numbers on %s",
 +                         dpif_name(br->dpif));
 +                break;
 +            } else if (error) {
 +                VLOG_ERR("failed to add %s interface to %s: %s",
 +                         if_name, dpif_name(br->dpif), strerror(error));
              }
          }
 -    out:
          svec_destroy(&cur_ifaces);
          svec_destroy(&want_ifaces);
          svec_destroy(&add_ifaces);
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          uint8_t ea[8];
          uint64_t dpid;
 -        struct iface *local_iface = NULL;
 -        const char *devname;
 +        struct iface *local_iface;
 +        struct iface *hw_addr_iface;
          struct netflow_options nf_options;
  
          bridge_fetch_dp_ifaces(br);
 -        for (i = 0; i < br->n_ports; ) {
 -            struct port *port = br->ports[i];
 +        iterate_and_prune_ifaces(br, init_iface_netdev, NULL);
  
 -            for (j = 0; j < port->n_ifaces; ) {
 -                struct iface *iface = port->ifaces[j];
 -                if (iface->dp_ifidx < 0) {
 -                    VLOG_ERR("%s interface not in dp%u, dropping",
 -                             iface->name, dpif_id(&br->dpif));
 -                    iface_destroy(iface);
 -                } else {
 -                    if (iface->dp_ifidx == ODPP_LOCAL) {
 -                        local_iface = iface;
 -                    }
 -                    VLOG_DBG("dp%u has interface %s on port %d",
 -                             dpif_id(&br->dpif), iface->name, iface->dp_ifidx);
 -                    j++;
 -                }
 -            }
 -            if (!port->n_ifaces) {
 -                VLOG_ERR("%s port has no interfaces, dropping", port->name);
 -                port_destroy(port);
 -                continue;
 -            }
 -            i++;
 -        }
 +        iterate_and_prune_ifaces(br, check_iface_dp_ifidx, NULL);
  
          /* Pick local port hardware address, datapath ID. */
 -        bridge_pick_local_hw_addr(br, ea, &devname);
 +        bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
 +        local_iface = bridge_get_local_iface(br);
          if (local_iface) {
 -            int error = netdev_nodev_set_etheraddr(local_iface->name, ea);
 +            int error = netdev_set_etheraddr(local_iface->netdev, ea);
              if (error) {
                  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
                  VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge "
              }
          }
  
 -        dpid = bridge_pick_datapath_id(br, ea, devname);
 +        dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
          ofproto_set_datapath_id(br->ofproto, dpid);
  
          /* Set NetFlow configuration on this bridge. */
          memset(&nf_options, 0, sizeof nf_options);
 -        nf_options.engine_type = br->dpif.minor;
 -        nf_options.engine_id = br->dpif.minor;
 +        dpif_get_netflow_ids(br->dpif, &nf_options.engine_type,
 +                             &nf_options.engine_id);
          nf_options.active_timeout = -1;
  
          if (cfg_has("netflow.%s.engine-type", br->name)) {
              struct port *port = br->ports[i];
  
              port_update_vlan_compat(port);
 -
 -            for (j = 0; j < port->n_ifaces; j++) {
 -                struct iface *iface = port->ifaces[j];
 -                if (iface->dp_ifidx != ODPP_LOCAL
 -                    && iface_is_internal(br, iface->name)) {
 -                    iface_set_mac(iface);
 -                }
 -            }
 +            port_update_bonding(port);
          }
      }
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          brstp_reconfigure(br);
 +        iterate_and_prune_ifaces(br, set_iface_properties, NULL);
      }
  }
  
  static void
  bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
 -                          const char **devname)
 +                          struct iface **hw_addr_iface)
  {
      uint64_t requested_ea;
      size_t i, j;
      int error;
  
 -    *devname = NULL;
 +    *hw_addr_iface = NULL;
  
      /* Did the user request a particular MAC? */
      requested_ea = cfg_get_mac(0, "bridge.%s.mac", br->name);
              for (j = 0; j < port->n_ifaces; j++) {
                  struct iface *candidate = port->ifaces[j];
                  uint8_t candidate_ea[ETH_ADDR_LEN];
 -                if (!netdev_nodev_get_etheraddr(candidate->name, candidate_ea)
 +                if (!netdev_get_etheraddr(candidate->netdev, candidate_ea)
                      && eth_addr_equals(iface_ea, candidate_ea)) {
                      iface = candidate;
                  }
              }
  
              /* Grab MAC. */
 -            error = netdev_nodev_get_etheraddr(iface->name, iface_ea);
 +            error = netdev_get_etheraddr(iface->netdev, iface_ea);
              if (error) {
                  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
                  VLOG_ERR_RL(&rl, "failed to obtain Ethernet address of %s: %s",
              memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0)
          {
              memcpy(ea, iface_ea, ETH_ADDR_LEN);
 -            *devname = iface ? iface->name : NULL;
 +            *hw_addr_iface = iface;
          }
      }
      if (eth_addr_is_multicast(ea) || eth_addr_is_vif(ea)) {
          memcpy(ea, br->default_ea, ETH_ADDR_LEN);
 -        *devname = NULL;
 +        *hw_addr_iface = NULL;
          VLOG_WARN("bridge %s: using default bridge Ethernet "
                    "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
      } else {
  
  /* Choose and returns the datapath ID for bridge 'br' given that the bridge
   * Ethernet address is 'bridge_ea'.  If 'bridge_ea' is the Ethernet address of
 - * a network device, then that network device's name must be passed in as
 - * 'devname'; if 'bridge_ea' was derived some other way, then 'devname' must be
 - * passed in as a null pointer. */
 + * an interface on 'br', then that interface must be passed in as
 + * 'hw_addr_iface'; if 'bridge_ea' was derived some other way, then
 + * 'hw_addr_iface' must be passed in as a null pointer. */
  static uint64_t
  bridge_pick_datapath_id(struct bridge *br,
                          const uint8_t bridge_ea[ETH_ADDR_LEN],
 -                        const char *devname)
 +                        struct iface *hw_addr_iface)
  {
      /*
       * The procedure for choosing a bridge MAC address will, in the most
          return dpid;
      }
  
 -    if (devname) {
 +    if (hw_addr_iface) {
          int vlan;
 -        if (!netdev_get_vlan_vid(devname, &vlan)) {
 +        if (!netdev_get_vlan_vid(hw_addr_iface->netdev, &vlan)) {
              /*
               * A bridge whose MAC address is taken from a VLAN network device
               * (that is, a network device created with vconfig(8) or similar
@@@ -909,26 -861,6 +909,26 @@@ bridge_flush(struct bridge *br
      br->flush = true;
      mac_learning_flush(br->ml);
  }
 +
 +/* Returns the 'br' interface for the ODPP_LOCAL port, or null if 'br' has no
 + * such interface. */
 +static struct iface *
 +bridge_get_local_iface(struct bridge *br)
 +{
 +    size_t i, j;
 +
 +    for (i = 0; i < br->n_ports; i++) {
 +        struct port *port = br->ports[i];
 +        for (j = 0; j < port->n_ifaces; j++) {
 +            struct iface *iface = port->ifaces[j];
 +            if (iface->dp_ifidx == ODPP_LOCAL) {
 +                return iface;
 +            }
 +        }
 +    }
 +
 +    return NULL;
 +}
  \f
  /* Bridge unixctl user interface functions. */
  static void
@@@ -969,7 -901,7 +969,7 @@@ bridge_create(const char *name
      br = xcalloc(1, sizeof *br);
  
      error = dpif_create(name, &br->dpif);
 -    if (error == EEXIST) {
 +    if (error == EEXIST || error == EBUSY) {
          error = dpif_open(name, &br->dpif);
          if (error) {
              VLOG_ERR("datapath %s already exists but cannot be opened: %s",
              free(br);
              return NULL;
          }
 -        dpif_flow_flush(&br->dpif);
 +        dpif_flow_flush(br->dpif);
      } else if (error) {
          VLOG_ERR("failed to create datapath %s: %s", name, strerror(error));
          free(br);
      error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto);
      if (error) {
          VLOG_ERR("failed to create switch %s: %s", name, strerror(error));
 -        dpif_delete(&br->dpif);
 -        dpif_close(&br->dpif);
 +        dpif_delete(br->dpif);
 +        dpif_close(br->dpif);
          free(br);
          return NULL;
      }
  
      list_push_back(&all_bridges, &br->node);
  
 -    VLOG_INFO("created bridge %s on dp%u", br->name, dpif_id(&br->dpif));
 +    VLOG_INFO("created bridge %s on %s", br->name, dpif_name(br->dpif));
  
      return br;
  }
@@@ -1020,12 -952,12 +1020,12 @@@ bridge_destroy(struct bridge *br
              port_destroy(br->ports[br->n_ports - 1]);
          }
          list_remove(&br->node);
 -        error = dpif_delete(&br->dpif);
 +        error = dpif_delete(br->dpif);
          if (error && error != ENOENT) {
 -            VLOG_ERR("failed to delete dp%u: %s",
 -                     dpif_id(&br->dpif), strerror(error));
 +            VLOG_ERR("failed to delete %s: %s",
 +                     dpif_name(br->dpif), strerror(error));
          }
 -        dpif_close(&br->dpif);
 +        dpif_close(br->dpif);
          ofproto_destroy(br->ofproto);
          free(br->controller);
          mac_learning_destroy(br->ml);
@@@ -1115,29 -1047,13 +1115,29 @@@ bridge_get_controller(const struct brid
      return controller && controller[0] ? controller : NULL;
  }
  
 +static bool
 +check_duplicate_ifaces(struct bridge *br, struct iface *iface, void *ifaces_)
 +{
 +    struct svec *ifaces = ifaces_;
 +    if (!svec_contains(ifaces, iface->name)) {
 +        svec_add(ifaces, iface->name);
 +        svec_sort(ifaces);
 +        return true;
 +    } else {
 +        VLOG_ERR("bridge %s: %s interface is on multiple ports, "
 +                 "removing from %s",
 +                 br->name, iface->name, iface->port->name);
 +        return false;
 +    }
 +}
 +
  static void
  bridge_reconfigure_one(struct bridge *br)
  {
      struct svec old_ports, new_ports, ifaces;
      struct svec listeners, old_listeners;
      struct svec snoops, old_snoops;
 -    size_t i, j;
 +    size_t i;
  
      /* Collect old ports. */
      svec_init(&old_ports);
      svec_init(&new_ports);
      cfg_get_all_keys(&new_ports, "bridge.%s.port", br->name);
      svec_sort(&new_ports);
 -    if (bridge_get_controller(br) && !svec_contains(&new_ports, br->name)) {
 -        svec_add(&new_ports, br->name);
 -        svec_sort(&new_ports);
 +    if (bridge_get_controller(br)) {
 +        char local_name[IF_NAMESIZE];
 +        int error;
 +
 +        error = dpif_port_get_name(br->dpif, ODPP_LOCAL,
 +                                   local_name, sizeof local_name);
 +        if (!error && !svec_contains(&new_ports, local_name)) {
 +            svec_add(&new_ports, local_name);
 +            svec_sort(&new_ports);
 +        }
      }
      if (!svec_is_unique(&new_ports)) {
          VLOG_WARN("bridge %s: %s specified twice as bridge port",
  
      /* Check and delete duplicate interfaces. */
      svec_init(&ifaces);
 -    for (i = 0; i < br->n_ports; ) {
 -        struct port *port = br->ports[i];
 -        for (j = 0; j < port->n_ifaces; ) {
 -            struct iface *iface = port->ifaces[j];
 -            if (svec_contains(&ifaces, iface->name)) {
 -                VLOG_ERR("bridge %s: %s interface is on multiple ports, "
 -                         "removing from %s",
 -                         br->name, iface->name, port->name);
 -                iface_destroy(iface);
 -            } else {
 -                svec_add(&ifaces, iface->name);
 -                svec_sort(&ifaces);
 -                j++;
 -            }
 -        }
 -        if (!port->n_ifaces) {
 -            VLOG_ERR("%s port has no interfaces, dropping", port->name);
 -            port_destroy(port);
 -        } else {
 -            i++;
 -        }
 -    }
 +    iterate_and_prune_ifaces(br, check_duplicate_ifaces, &ifaces);
      svec_destroy(&ifaces);
  
      /* Delete all flows if we're switching from connected to standalone or vice
@@@ -1276,8 -1206,9 +1276,8 @@@ bridge_reconfigure_controller(struct br
                                    cfg_get_string(0, "%s.accept-regex", pfx),
                                    update_resolv_conf);
          } else {
 -            struct netdev *netdev;
 +            struct iface *local_iface;
              bool in_band;
 -            int error;
  
              in_band = (!cfg_is_valid(CFG_BOOL | CFG_REQUIRED,
                                       "%s.in-band", pfx)
              ofproto_set_discovery(br->ofproto, false, NULL, NULL);
              ofproto_set_in_band(br->ofproto, in_band);
  
 -            error = netdev_open(br->name, NETDEV_ETH_TYPE_NONE, &netdev);
 -            if (!error) {
 -                if (cfg_is_valid(CFG_IP | CFG_REQUIRED, "%s.ip", pfx)) {
 -                    struct in_addr ip, mask, gateway;
 -                    ip.s_addr = cfg_get_ip(0, "%s.ip", pfx);
 -                    mask.s_addr = cfg_get_ip(0, "%s.netmask", pfx);
 -                    gateway.s_addr = cfg_get_ip(0, "%s.gateway", pfx);
 -
 -                    netdev_turn_flags_on(netdev, NETDEV_UP, true);
 -                    if (!mask.s_addr) {
 -                        mask.s_addr = guess_netmask(ip.s_addr);
 -                    }
 -                    if (!netdev_set_in4(netdev, ip, mask)) {
 -                        VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
 -                                  "netmask "IP_FMT,
 -                                  br->name, IP_ARGS(&ip.s_addr),
 -                                  IP_ARGS(&mask.s_addr));
 -                    }
 +            local_iface = bridge_get_local_iface(br);
 +            if (local_iface
 +                && cfg_is_valid(CFG_IP | CFG_REQUIRED, "%s.ip", pfx)) {
 +                struct netdev *netdev = local_iface->netdev;
 +                struct in_addr ip, mask, gateway;
 +                ip.s_addr = cfg_get_ip(0, "%s.ip", pfx);
 +                mask.s_addr = cfg_get_ip(0, "%s.netmask", pfx);
 +                gateway.s_addr = cfg_get_ip(0, "%s.gateway", pfx);
 +
 +                netdev_turn_flags_on(netdev, NETDEV_UP, true);
 +                if (!mask.s_addr) {
 +                    mask.s_addr = guess_netmask(ip.s_addr);
 +                }
 +                if (!netdev_set_in4(netdev, ip, mask)) {
 +                    VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
 +                              "netmask "IP_FMT,
 +                              br->name, IP_ARGS(&ip.s_addr),
 +                              IP_ARGS(&mask.s_addr));
 +                }
  
 -                    if (gateway.s_addr) {
 -                        if (!netdev_add_router(gateway)) {
 -                            VLOG_INFO("bridge %s: configured gateway "IP_FMT,
 -                                      br->name, IP_ARGS(&gateway.s_addr));
 -                        }
 +                if (gateway.s_addr) {
 +                    if (!netdev_add_router(netdev, gateway)) {
 +                        VLOG_INFO("bridge %s: configured gateway "IP_FMT,
 +                                  br->name, IP_ARGS(&gateway.s_addr));
                      }
                  }
 -                netdev_close(netdev);
              }
          }
  
@@@ -1440,17 -1372,17 +1440,17 @@@ bridge_fetch_dp_ifaces(struct bridge *b
      }
      port_array_clear(&br->ifaces);
  
 -    dpif_port_list(&br->dpif, &dpif_ports, &n_dpif_ports);
 +    dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
      for (i = 0; i < n_dpif_ports; i++) {
          struct odp_port *p = &dpif_ports[i];
          struct iface *iface = iface_lookup(br, p->devname);
          if (iface) {
              if (iface->dp_ifidx >= 0) {
 -                VLOG_WARN("dp%u reported interface %s twice",
 -                          dpif_id(&br->dpif), p->devname);
 +                VLOG_WARN("%s reported interface %s twice",
 +                          dpif_name(br->dpif), p->devname);
              } else if (iface_from_dp_ifidx(br, p->port)) {
 -                VLOG_WARN("dp%u reported interface %"PRIu16" twice",
 -                          dpif_id(&br->dpif), p->port);
 +                VLOG_WARN("%s reported interface %"PRIu16" twice",
 +                          dpif_name(br->dpif), p->port);
              } else {
                  port_array_set(&br->ifaces, p->port, iface);
                  iface->dp_ifidx = p->port;
@@@ -1899,6 -1831,71 +1899,71 @@@ compose_actions(struct bridge *br, cons
      }
  }
  
+ /* Returns the effective vlan of a packet, taking into account both the
+  * 802.1Q header and implicitly tagged ports.  A value of 0 indicates that
+  * the packet is untagged and -1 indicates it has an invalid header and
+  * should be dropped. */
+ static int flow_get_vlan(struct bridge *br, const flow_t *flow,
+                          struct port *in_port, bool have_packet)
+ {
+     /* Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet
+      * belongs to VLAN 0, so we should treat both cases identically.  (In the
+      * former case, the packet has an 802.1Q header that specifies VLAN 0,
+      * presumably to allow a priority to be specified.  In the latter case, the
+      * packet does not have any 802.1Q header.) */
+     int vlan = ntohs(flow->dl_vlan);
+     if (vlan == OFP_VLAN_NONE) {
+         vlan = 0;
+     }
+     if (in_port->vlan >= 0) {
+         if (vlan) {
+             /* XXX support double tagging? */
+             if (have_packet) {
+                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                 VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
+                              "packet received on port %s configured with "
+                              "implicit VLAN %"PRIu16,
+                              br->name, ntohs(flow->dl_vlan),
+                              in_port->name, in_port->vlan);
+             }
+             return -1;
+         }
+         vlan = in_port->vlan;
+     } else {
+         if (!port_includes_vlan(in_port, vlan)) {
+             if (have_packet) {
+                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                 VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
+                              "packet received on port %s not configured for "
+                              "trunking VLAN %d",
+                              br->name, vlan, in_port->name, vlan);
+             }
+             return -1;
+         }
+     }
+     return vlan;
+ }
+ static void
+ update_learning_table(struct bridge *br, const flow_t *flow, int vlan,
+                       struct port *in_port)
+ {
+     tag_type rev_tag = mac_learning_learn(br->ml, flow->dl_src,
+                                           vlan, in_port->port_idx);
+     if (rev_tag) {
+         /* The log messages here could actually be useful in debugging,
+          * so keep the rate limit relatively high. */
+         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30,
+                                                                 300);
+         VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
+                     "on port %s in VLAN %d",
+                     br->name, ETH_ADDR_ARGS(flow->dl_src),
+                     in_port->name, vlan);
+         ofproto_revalidate(br->ofproto, rev_tag);
+     }
+ }
  static bool
  is_bcast_arp_reply(const flow_t *flow)
  {
@@@ -1947,41 -1944,9 +2012,9 @@@ process_flow(struct bridge *br, const f
          return true;
      }
      in_port = in_iface->port;
-     /* Figure out what VLAN this packet belongs to.
-      *
-      * Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet
-      * belongs to VLAN 0, so we should treat both cases identically.  (In the
-      * former case, the packet has an 802.1Q header that specifies VLAN 0,
-      * presumably to allow a priority to be specified.  In the latter case, the
-      * packet does not have any 802.1Q header.) */
-     vlan = ntohs(flow->dl_vlan);
-     if (vlan == OFP_VLAN_NONE) {
-         vlan = 0;
-     }
-     if (in_port->vlan >= 0) {
-         if (vlan) {
-             /* XXX support double tagging? */
-             if (packet != NULL) {
-                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                 VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
-                              "packet received on port %s configured with "
-                              "implicit VLAN %"PRIu16,
-                              br->name, ntohs(flow->dl_vlan),
-                              in_port->name, in_port->vlan);
-             }
-             goto done;
-         }
-         vlan = in_port->vlan;
-     } else {
-         if (!port_includes_vlan(in_port, vlan)) {
-             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-             VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
-                          "packet received on port %s not configured for "
-                          "trunking VLAN %d",
-                          br->name, vlan, in_port->name, vlan);
-             goto done;
-         }
+     vlan = flow_get_vlan(br, flow, in_port, !!packet);
+     if (vlan < 0) {
+         goto done;
      }
  
      /* Drop frames for ports that STP wants entirely killed (both for
      out_port = FLOOD_PORT;
      /* Learn source MAC (but don't try to learn from revalidation). */
      if (packet) {
-         tag_type rev_tag = mac_learning_learn(br->ml, flow->dl_src,
-                                               vlan, in_port->port_idx);
-         if (rev_tag) {
-             /* The log messages here could actually be useful in debugging,
-              * so keep the rate limit relatively high. */
-             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30,
-                                                                     300);
-             VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
-                         "on port %s in VLAN %d",
-                         br->name, ETH_ADDR_ARGS(flow->dl_src),
-                         in_port->name, vlan);
-             ofproto_revalidate(br->ofproto, rev_tag);
-         }
+         update_learning_table(br, flow, vlan, in_port);
      }
  
      /* Determine output port. */
                                             tags);
      if (out_port_idx >= 0 && out_port_idx < br->n_ports) {
          out_port = br->ports[out_port_idx];
-     } else if (!packet) {
+     } else if (!packet && !eth_addr_is_multicast(flow->dl_dst)) {
          /* If we are revalidating but don't have a learning entry then
-          * eject the flow.  Installing a flow that floods packets will
-          * prevent us from seeing future packets and learning properly. */
+          * eject the flow.  Installing a flow that floods packets opens
+          * up a window of time where we could learn from a packet reflected
+          * on a bond and blackhole packets before the learning table is
+          * updated to reflect the correct port. */
          return false;
      }
  
@@@ -2101,6 -2056,7 +2124,6 @@@ bridge_port_changed_ofhook_cb(enum ofp_
  
          bridge_flush(br);
      } else {
 -        memcpy(iface->mac, opp->hw_addr, ETH_ADDR_LEN);
          if (port->n_ifaces > 1) {
              bool up = !(opp->state & OFPPS_LINK_DOWN);
              bond_link_status_update(iface, up);
@@@ -2135,17 -2091,30 +2158,30 @@@ bridge_account_flow_ofhook_cb(const flo
                                void *br_)
  {
      struct bridge *br = br_;
+     struct port *in_port;
      const union odp_action *a;
  
+     /* Feed information from the active flows back into the learning table
+      * to ensure that table is always in sync with what is actually flowing
+      * through the datapath. */
+     in_port = port_from_dp_ifidx(br, flow->in_port);
+     if (in_port) {
+         int vlan = flow_get_vlan(br, flow, in_port, false);
+          if (vlan >= 0) {
+             update_learning_table(br, flow, vlan, in_port);
+         }
+     }
      if (!br->has_bonded_ports) {
          return;
      }
  
      for (a = actions; a < &actions[n_actions]; a++) {
          if (a->type == ODPAT_OUTPUT) {
-             struct port *port = port_from_dp_ifidx(br, a->output.port);
-             if (port && port->n_ifaces >= 2) {
-                 struct bond_entry *e = lookup_bond_entry(port, flow->dl_src);
+             struct port *out_port = port_from_dp_ifidx(br, a->output.port);
+             if (out_port && out_port->n_ifaces >= 2) {
+                 struct bond_entry *e = lookup_bond_entry(out_port,
+                                                          flow->dl_src);
                  e->tx_bytes += n_bytes;
              }
          }
@@@ -2813,25 -2782,6 +2849,25 @@@ bond_unixctl_disable_slave(struct unixc
      enable_slave(conn, args, false);
  }
  
 +static void
 +bond_unixctl_hash(struct unixctl_conn *conn, const char *args)
 +{
 +      uint8_t mac[ETH_ADDR_LEN];
 +      uint8_t hash;
 +      char *hash_cstr;
 +
 +      if (sscanf(args, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
 +          == ETH_ADDR_SCAN_COUNT) {
 +              hash = bond_hash(mac);
 +
 +              hash_cstr = xasprintf("%u", hash);
 +              unixctl_command_reply(conn, 200, hash_cstr);
 +              free(hash_cstr);
 +      } else {
 +              unixctl_command_reply(conn, 501, "invalid mac");
 +      }
 +}
 +
  static void
  bond_init(void)
  {
                               bond_unixctl_set_active_slave);
      unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave);
      unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave);
 +    unixctl_command_register("bond/hash", bond_unixctl_hash);
  }
  \f
  /* Port functions. */
@@@ -3141,7 -3090,7 +3177,7 @@@ port_update_bond_compat(struct port *po
          if (slave->up) {
              bond.up = true;
          }
 -        memcpy(slave->mac, iface->mac, ETH_ADDR_LEN);
 +        netdev_get_etheraddr(iface->netdev, slave->mac);
      }
  
      if (cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) {
@@@ -3185,8 -3134,7 +3221,8 @@@ port_update_vlan_compat(struct port *po
                  && p->n_ifaces
                  && (!vlandev_name || strcmp(p->name, vlandev_name) <= 0))
              {
 -                const uint8_t *ea = p->ifaces[0]->mac;
 +                uint8_t ea[ETH_ADDR_LEN];
 +                netdev_get_etheraddr(p->ifaces[0]->netdev, ea);
                  if (!eth_addr_is_multicast(ea) &&
                      !eth_addr_is_reserved(ea) &&
                      !eth_addr_is_zero(ea)) {
@@@ -3212,7 -3160,18 +3248,7 @@@ iface_create(struct port *port, const c
      iface->dp_ifidx = -1;
      iface->tag = tag_create_random();
      iface->delay_expires = LLONG_MAX;
 -
 -    if (!cfg_get_bool(0, "iface.%s.internal", iface->name)) {
 -        netdev_nodev_get_etheraddr(name, iface->mac);
 -        netdev_nodev_get_carrier(name, &iface->enabled);
 -    } else {
 -        /* Internal interfaces are created later by the call to dpif_port_add()
 -         * in bridge_reconfigure().  Until then, we can't obtain any
 -         * information about them.  (There's no real value in doing so, anyway,
 -         * because the 'mac' and 'enabled' values are only used for interfaces
 -         * that are bond slaves, and it doesn't normally make sense to bond an
 -         * internal interface.) */
 -    }
 +    iface->netdev = NULL;
  
      if (port->n_ifaces >= port->allocated_ifaces) {
          port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces,
  
      VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
  
 -    port_update_bonding(port);
      bridge_flush(port->bridge);
  }
  
@@@ -3244,7 -3204,6 +3280,7 @@@ iface_destroy(struct iface *iface
          del = port->ifaces[iface->port_ifidx] = port->ifaces[--port->n_ifaces];
          del->port_ifidx = iface->port_ifidx;
  
 +        netdev_close(iface->netdev);
          free(iface->name);
          free(iface);
  
              bond_send_learning_packets(port);
          }
  
 -        port_update_bonding(port);
          bridge_flush(port->bridge);
      }
  }
@@@ -3326,7 -3286,7 +3362,7 @@@ iface_set_mac(struct iface *iface
              VLOG_ERR("ignoring iface.%s.mac; use bridge.%s.mac instead",
                       iface->name, iface->name);
          } else {
 -            int error = netdev_nodev_set_etheraddr(iface->name, ea);
 +            int error = netdev_set_etheraddr(iface->netdev, ea);
              if (error) {
                  VLOG_ERR("interface %s: setting MAC failed (%s)",
                           iface->name, strerror(error));
@@@ -3402,6 -3362,8 +3438,8 @@@ mirror_reconfigure(struct bridge *br
              int vlan = cfg_get_vlan(i, "vlan.%s.disable-learning", br->name);
              if (vlan >= 0) {
                  bitmap_set1(rspan_vlans, vlan);
+                 VLOG_INFO("bridge %s: disabling learning on vlan %d\n",
+                           br->name, vlan);
              } else {
                  VLOG_ERR("bridge %s: invalid value '%s' for learning disabled "
                           "VLAN", br->name,
@@@ -3677,25 -3639,23 +3715,25 @@@ brstp_send_bpdu(struct ofpbuf *pkt, in
      if (!iface) {
          VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d",
                       br->name, port_no);
 -    } else if (eth_addr_is_zero(iface->mac)) {
 -        VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d with unknown MAC",
 -                     br->name, port_no);
      } else {
 -        union ofp_action action;
          struct eth_header *eth = pkt->l2;
 -        flow_t flow;
  
 -        memcpy(eth->eth_src, iface->mac, ETH_ADDR_LEN);
 +        netdev_get_etheraddr(iface->netdev, eth->eth_src);
 +        if (eth_addr_is_zero(eth->eth_src)) {
 +            VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d "
 +                         "with unknown MAC", br->name, port_no);
 +        } else {
 +            union ofp_action action;
 +            flow_t flow;
  
 -        memset(&action, 0, sizeof action);
 -        action.type = htons(OFPAT_OUTPUT);
 -        action.output.len = htons(sizeof action);
 -        action.output.port = htons(port_no);
 +            memset(&action, 0, sizeof action);
 +            action.type = htons(OFPAT_OUTPUT);
 +            action.output.len = htons(sizeof action);
 +            action.output.port = htons(port_no);
  
 -        flow_extract(pkt, ODPP_NONE, &flow);
 -        ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
 +            flow_extract(pkt, ODPP_NONE, &flow);
 +            ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
 +        }
      }
      ofpbuf_delete(pkt);
  }
  test -e /etc/sysconfig/vswitch && . /etc/sysconfig/vswitch
  
  # General config variables in /etc/sysconfig/vswitch
 -VSWITCH_BASE="${VSWITCH_BASE:-/root/vswitch}"
 -ENABLE_BRCOMPAT="${ENABLE_BRCOMPAT:-y}"
 -ENABLE_FAKE_PROC_NET="${ENABLE_FAKE_PROC_NET:-y}"
 -FORCE_COREFILES="${FORCE_COREFILES:-y}"
 +: ${ENABLE_BRCOMPAT:=y}
 +: ${ENABLE_FAKE_PROC_NET:=y}
 +: ${FORCE_COREFILES:=y}
  
  # Config variables specific to ovs-vswitchd
 -VSWITCHD_CONF="${VSWITCHD_CONF:-/etc/ovs-vswitchd.conf}"
 -VSWITCHD_PIDFILE="${VSWITCHD_PIDFILE:-/var/run/ovs-vswitchd.pid}"
 -VSWITCHD_RUN_DIR="${VSWITCHD_RUN_DIR:-/var/xen/vswitch}"
 -VSWITCHD_PRIORITY="${VSWITCHD_PRIORITY:--10}"
 -VSWITCHD_LOGFILE="${VSWITCHD_LOGFILE:-/var/log/ovs-vswitchd.log}"
 -VSWITCHD_FILE_LOGLEVEL="${VSWITCHD_FILE_LOGLEVEL:-INFO}"
 -VSWITCHD_SYSLOG_LOGLEVEL="${VSWITCHD_SYSLOG_LOGLEVEL:-ERR}"
 -VSWITCHD_MEMLEAK_LOGFILE="${VSWITCHD_MEMLEAK_LOGFILE:-}"
 -VSWITCHD_STRACE_LOG="${VSWITCHD_STRACE_LOG:-}"
 -VSWITCHD_STRACE_OPT="${VSWITCHD_STRACE_OPT:-}"
 -VSWITCHD_VALGRIND_LOG="${VSWITCHD_VALGRIND_LOG:-}"
 -VSWITCHD_VALGRIND_OPT="${VSWITCHD_VALGRIND_OPT:-}"
 +: ${VSWITCHD_CONF:=/etc/ovs-vswitchd.conf}
 +: ${VSWITCHD_PIDFILE:=/var/run/ovs-vswitchd.pid}
 +: ${VSWITCHD_RUN_DIR:=/var/xen/vswitch}
 +: ${VSWITCHD_PRIORITY:=-10}
 +: ${VSWITCHD_LOGFILE:=/var/log/ovs-vswitchd.log}
 +: ${VSWITCHD_FILE_LOGLEVEL:=INFO}
 +: ${VSWITCHD_SYSLOG_LOGLEVEL:=ERR}
 +: ${VSWITCHD_MEMLEAK_LOGFILE:=}
 +: ${VSWITCHD_STRACE_LOG:=}
 +: ${VSWITCHD_STRACE_OPT:=}
 +: ${VSWITCHD_VALGRIND_LOG:=}
 +: ${VSWITCHD_VALGRIND_OPT:=}
  
  # Config variables specific to ovs-brcompatd
 -BRCOMPATD_PIDFILE="${BRCOMPATD_PIDFILE:-/var/run/ovs-brcompatd.pid}"
 -BRCOMPATD_RUN_DIR="${BRCOMPATD_RUN_DIR:-/var/xen/vswitch}"
 -BRCOMPATD_PRIORITY="${BRCOMPATD_PRIORITY:--10}"
 -BRCOMPATD_LOGFILE="${BRCOMPATD_LOGFILE:-/var/log/ovs-brcompatd.log}"
 -BRCOMPATD_FILE_LOGLEVEL="${BRCOMPATD_FILE_LOGLEVEL:-INFO}"
 -BRCOMPATD_SYSLOG_LOGLEVEL="${BRCOMPATD_SYSLOG_LOGLEVEL:-ERR}"
 -BRCOMPATD_MEMLEAK_LOGFILE="${BRCOMPATD_MEMLEAK_LOGFILE:-}"
 -BRCOMPATD_STRACE_LOG="${BRCOMPATD_STRACE_LOG:-}"
 -BRCOMPATD_STRACE_OPT="${BRCOMPATD_STRACE_OPT:-}"
 -BRCOMPATD_VALGRIND_LOG="${BRCOMPATD_VALGRIND_LOG:-}"
 -BRCOMPATD_VALGRIND_OPT="${BRCOMPATD_VALGRIND_OPT:-}"
 -
 -
 -
 +: ${BRCOMPATD_PIDFILE:=/var/run/ovs-brcompatd.pid}
 +: ${BRCOMPATD_RUN_DIR:=/var/xen/vswitch}
 +: ${BRCOMPATD_PRIORITY:=-10}
 +: ${BRCOMPATD_LOGFILE:=/var/log/ovs-brcompatd.log}
 +: ${BRCOMPATD_FILE_LOGLEVEL:=INFO}
 +: ${BRCOMPATD_SYSLOG_LOGLEVEL:=ERR}
 +: ${BRCOMPATD_MEMLEAK_LOGFILE:=}
 +: ${BRCOMPATD_STRACE_LOG:=}
 +: ${BRCOMPATD_STRACE_OPT:=}
 +: ${BRCOMPATD_VALGRIND_LOG:=}
 +: ${BRCOMPATD_VALGRIND_OPT:=}
  
  # Full paths to executables & modules
 -vswitchd="$VSWITCH_BASE/sbin/ovs-vswitchd"
 -brcompatd="$VSWITCH_BASE/sbin/ovs-brcompatd"
 -dpctl="$VSWITCH_BASE/bin/ovs-dpctl"
 -appctl="$VSWITCH_BASE/bin/ovs-appctl"
 -ofctl="$VSWITCH_BASE/bin/ovs-ofctl"
 +vswitchd="/usr/sbin/ovs-vswitchd"
 +brcompatd="/usr/sbin/ovs-brcompatd"
 +dpctl="/usr/bin/ovs-dpctl"
 +appctl="/usr/bin/ovs-appctl"
 +ofctl="/usr/bin/ovs-ofctl"
  
  
  if [ "$ENABLE_FAKE_PROC_NET" = "y" ]; then
@@@ -87,10 -91,10 +87,10 @@@ function remove_all_dp 
  function insert_modules_if_required {
      if ! lsmod | grep -q "openvswitch_mod"; then
          action "Inserting llc module" modprobe llc
 -        action "Inserting openvswitch module" insmod $VSWITCH_BASE/kernel_modules/openvswitch_mod.ko
 +        action "Inserting openvswitch module" modprobe openvswitch_mod
      fi
      if [ -n "$BRCOMPATD_PIDFILE" ] && ! lsmod | grep -q "brcompat_mod"; then
 -        action "Inserting brcompat module" insmod $VSWITCH_BASE/kernel_modules/brcompat_mod.ko
 +        action "Inserting brcompat module" modprobe brcompat_mod
      fi
  }
  
@@@ -105,13 -109,16 +105,13 @@@ function remove_modules 
  
  function reload_vswitchd {
      if [ -f "$VSWITCHD_PIDFILE" ]; then
 -        "$appctl" \
 -            --target=ovs-vswitchd.$(cat "$VSWITCHD_PIDFILE").ctl \
 -            --execute=vswitchd/reload
 +        "$appctl" --target=/var/run/ovs-vswitchd.`cat $VSWITCHD_PIDFILE`.ctl vswitchd/reload
      fi
  }
  
  function reload_brcompatd {
      if [ -f "$BRCOMPATD_PIDFILE" ]; then
 -        "$appctl" \
 -            --target=ovs-brcompatd.$(cat "$BRCOMPATD_PIDFILE").ctl --reopen
 +        "$appctl" --target=/var/run/ovs-brcompatd.`cat $BRCOMPATD_PIDFILE`.ctl vlog/reopen
      fi
  }
  
@@@ -194,7 -201,7 +194,7 @@@ function start_brcompatd 
          valgrind_opt="valgrind --log-file=$BRCOMPATD_VALGRIND_LOG $BRCOMPATD_VALGRIND_OPT"
          daemonize="n"
      fi
 -    appctl_cmd="$appctl -t /var/run/ovs-vswitchd.\`cat $VSWITCHD_PIDFILE\`.ctl -e '%s'"
 +    appctl_cmd="$appctl --target=/var/run/ovs-vswitchd.\`cat $VSWITCHD_PIDFILE\`.ctl %s"
      if [ "$daemonize" != "y" ]; then
          # Start in background and force a "success" message
          action "Starting ovs-brcompatd ($strace_opt$valgrind_opt)" true
@@@ -268,19 -275,18 +268,20 @@@ function start 
              '--del-match=port.*' \
              '--del-match=bonding.*' \
              '--del-match=iface.*' \
-             '--del-match=vlan.*'
+             '--del-match=vlan.*.trunks=*' \
+             '--del-match=vlan.*.tag=*'
      fi
  
      start_vswitchd
      start_brcompatd
      reload_vswitchd  # ensures ovs-vswitchd has fully read config file.
 +    touch /var/lock/subsys/vswitch
  }
  
  function stop {
      stop_brcompatd
      stop_vswitchd
 +    rm -f /var/lock/subsys/vswitch
  }
  
  function restart {
@@@ -317,8 -323,8 +318,8 @@@ case "$1" i
          status -p ovs-brcompatd.pid ovs-brcompatd
          ;;
      version)
 -        "$VSWITCH_BASE"/sbin/ovs-vswitchd -V
 -        "$VSWITCH_BASE"/sbin/ovs-brcompatd -V
 +        /usr/sbin/ovs-vswitchd -V
 +        /usr/sbin/ovs-brcompatd -V
          ;;
      help)
          printf "vswitch [start|stop|restart|reload|unload|status|version]\n"
@@@ -1,7 -1,17 +1,17 @@@
  #!/bin/sh
  
- # Copyright (C) 2008,2009 Citrix Systems, Inc. All rights reserved.
+ # Copyright (C) 2008,2009 Citrix Systems, Inc.
  # Copyright (C) 2009 Nicira Networks, Inc.
+ #
+ # This program is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation; version 2.1 only. with the special
+ # exception on linking described in file LICENSE.
+ #
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU Lesser General Public License for more details.
  
  # CA-23900: Warning: when VIFs are added to windows guests with PV drivers the backend vif device is registered,
  # unregistered and then registered again. This causes the udev event to fire twice and this script runs twice.
@@@ -11,9 -21,8 +21,9 @@@
  
  # Keep other-config/ keys in sync with device.ml:vif_udev_keys
  
 -cfg_mod="/root/vswitch/bin/ovs-cfg-mod"
 -dump_vif_details="/root/vswitch/scripts/dump-vif-details"
 +cfg_mod="/usr/bin/ovs-cfg-mod"
 +vsctl="/usr/bin/ovs-vsctl"
 +dump_vif_details="/usr/share/vswitch/scripts/dump-vif-details"
  service="/sbin/service"
  
  TYPE=`echo ${XENBUS_PATH} | cut -f 2 -d '/'`
@@@ -71,11 -80,13 +81,11 @@@ add_to_bridge(
      fi
      logger -t scripts-vif "Adding ${vif} to ${bridge} with address ${address}"
  
 -    vid=
 -    if [ -e "/var/lib/openvswitch/br-$bridge" ]; then
 -      . "/var/lib/openvswitch/br-$bridge"
 -      if [ -n "$VLAN_SLAVE" -a -n "$VLAN_VID" ]; then
 -          bridge=$VLAN_SLAVE
 -          vid="--add=vlan.$vif.tag=$VLAN_VID"
 -      fi
 +    local VLAN_ID=$($vsctl br-to-vlan $bridge)
 +    local vid=
 +    if [ "$VLAN_ID" -ne 0 ] ; then
 +      bridge=$($vsctl br-to-parent $bridge)
 +      vid="--add=vlan.${vif}.tag=${VLAN_ID}"
      fi
  
      ${IP} link set "${vif}" down                        || logger -t scripts-vif "Failed to ip link set ${vif} down"
  
      $cfg_mod -F /etc/ovs-vswitchd.conf \
          --del-match="bridge.*.port=$vif" \
-         --del-match="vlan.$vif.[!0-9]*" \
+         --del-match="vlan.$vif.trunks=*" \
+         --del-match="vlan.$vif.tag=*" \
          --del-match="port.$vif.[!0-9]*" \
          --add="bridge.$bridge.port=$vif" \
          $vid $vif_details -c 
@@@ -127,7 -139,8 +138,8 @@@ remove
        logger -t scripts-vif "${vif} has been removed"
        $cfg_mod -vANY:console:emer -F /etc/ovs-vswitchd.conf \
            --del-match="bridge.*.port=${vif}" \
-           --del-match="vlan.${vif}.[!0-9]*" \
+           --del-match="vlan.${vif}.trunks=*" \
+           --del-match="vlan.${vif}.tag=*" \
            --del-match="port.${vif}.[!0-9]*" -c
        $service vswitch reload
        ;;
@@@ -1,8 -1,18 +1,18 @@@
  #!/usr/bin/python
  #
- # Copyright (c) 2008,2009 Citrix Systems, Inc. All rights reserved.
+ # Copyright (c) 2008,2009 Citrix Systems, Inc.
  # Copyright (c) 2009 Nicira Networks.
  #
+ # This program is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation; version 2.1 only. with the special
+ # exception on linking described in file LICENSE.
+ #
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU Lesser General Public License for more details.
+ #
  """Usage:
  
      %(command-name)s <PIF> up
@@@ -1250,7 -1260,8 +1260,8 @@@ def datapath_deconfigure_ipdev(interfac
      return ['--del-match=bridge.*.port=%s' % interface,
              '--del-match=port.%s.[!0-9]*' % interface,
              '--del-match=iface.%s.[!0-9]*' % interface,
-             '--del-match=vlan.%s.[!0-9]*' % interface]
+             '--del-match=vlan.%s.trunks=*' % interface,
+             '--del-match=vlan.%s.tag=*' % interface]
  
  def datapath_modify_config(commands):
      if debug_mode():
          for c in commands:
              log("  %s" % c)
  
 -    rc = run_command(['/root/vswitch/bin/ovs-cfg-mod', '-vANY:console:emer',
 +    rc = run_command(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer',
                   '-F', '/etc/ovs-vswitchd.conf']
                  + [c for c in commands if c[0] != '#'] + ['-c'])
      if not rc:
@@@ -1473,7 -1484,18 +1484,7 @@@ def action_up(pif)
          cfgmod_argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
          cfgmod_argv += ['--add=iface.%s.internal=true' % (ipdev)]
          cfgmod_argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
 -        if not os.path.exists(vswitch_state_dir):
 -            os.mkdir(vswitch_state_dir)
 -        br = ConfigurationFile("br-%s" % ipdev, vswitch_state_dir)
 -        br.write("VLAN_SLAVE=%s\n" % bridge)
 -        br.write("VLAN_VID=%s\n" % pifrec['VLAN'])
 -        br.close()
 -        f.attach_child(br)
 -    else:
 -        br = ConfigurationFile("br-%s" % ipdev, vswitch_state_dir)
 -        br.unlink()
 -        f.attach_child(br)
 -
 +        
      # Apply updated configuration.
      try:
          f.apply()