Merge "citrix" branch into "master.
authorBen Pfaff <blp@nicira.com>
Mon, 5 Oct 2009 17:29:07 +0000 (10:29 -0700)
committerBen Pfaff <blp@nicira.com>
Mon, 5 Oct 2009 17:29:07 +0000 (10:29 -0700)
1  2 
lib/shash.c
ofproto/ofproto.c
vswitchd/bridge.c
vswitchd/ovs-vswitchd.conf.5.in
xenserver/etc_init.d_vswitch

diff --combined lib/shash.c
@@@ -36,6 -36,7 +36,7 @@@ shash_destroy(struct shash *sh
  {
      if (sh) {
          shash_clear(sh);
+         hmap_destroy(&sh->map);
      }
  }
  
@@@ -44,29 -45,22 +45,29 @@@ shash_clear(struct shash *sh
  {
      struct shash_node *node, *next;
  
 -    HMAP_FOR_EACH_SAFE (node, next, struct shash_node, node, &sh->map) {
 +    SHASH_FOR_EACH_SAFE (node, next, sh) {
          hmap_remove(&sh->map, &node->node);
          free(node->name);
          free(node);
      }
  }
  
 -/* It is the caller's responsible to avoid duplicate names, if that is
 +bool
 +shash_is_empty(const struct shash *shash)
 +{
 +    return hmap_is_empty(&shash->map);
 +}
 +
 +/* It is the caller's responsibility to avoid duplicate names, if that is
   * desirable. */
 -void
 +struct shash_node *
  shash_add(struct shash *sh, const char *name, void *data)
  {
      struct shash_node *node = xmalloc(sizeof *node);
      node->name = xstrdup(name);
      node->data = data;
      hmap_insert(&sh->map, &node->node, hash_name(name));
 +    return node;
  }
  
  void
@@@ -98,11 -92,3 +99,11 @@@ shash_find_data(const struct shash *sh
      struct shash_node *node = shash_find(sh, name);
      return node ? node->data : NULL;
  }
 +
 +struct shash_node *
 +shash_first(const struct shash *shash)
 +{
 +    struct hmap_node *node = hmap_first(&shash->map);
 +    return node ? CONTAINER_OF(node, struct shash_node, node) : NULL;
 +}
 +
diff --combined ofproto/ofproto.c
@@@ -135,7 -135,7 +135,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) {
@@@ -194,8 -194,8 +194,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;
@@@ -237,7 -237,7 +237,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);
@@@ -261,9 -261,10 +261,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;
  }
@@@ -366,7 -373,9 +366,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);
@@@ -424,8 -433,9 +424,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);
@@@ -447,7 -457,7 +447,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;
@@@ -704,8 -714,8 +704,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);
      }
@@@ -747,17 -757,6 +747,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) {
@@@ -907,9 -907,8 +907,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);
      }
@@@ -979,7 -978,7 +979,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;
  }
@@@ -1031,7 -1030,7 +1031,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);
      }
@@@ -1054,7 -1053,7 +1054,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);
      }
@@@ -1084,7 -1083,7 +1084,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);
  }
  
@@@ -1116,7 -1115,7 +1116,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';
@@@ -1191,7 -1190,6 +1191,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));
@@@ -1226,7 -1223,7 +1226,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);
@@@ -1295,7 -1292,7 +1295,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;
      }
@@@ -1500,7 -1497,7 +1500,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);
@@@ -1609,7 -1606,7 +1609,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
@@@ -1690,7 -1687,7 +1690,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(rule, &odp_flow.stats);
          }
          rule->installed = false;
@@@ -1838,7 -1835,7 +1838,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;
@@@ -1871,10 -1868,10 +1871,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")",
@@@ -1933,9 -1930,21 +1933,21 @@@ static voi
  add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
  {
      const struct ofport *ofport = port_array_get(&ctx->ofproto->ports, port);
-     if (!ofport || !(ofport->opp.config & OFPPC_NO_FWD)) {
-         odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
+     if (ofport) {
+         if (ofport->opp.config & OFPPC_NO_FWD) {
+             /* Forwarding disabled on port. */
+             return;
+         }
+     } else {
+         /*
+          * We don't have an ofport record for this port, but it doesn't hurt to
+          * allow forwarding to it anyhow.  Maybe such a port will appear later
+          * and we're pre-populating the flow table.
+          */
      }
+     odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
  }
  
  static struct rule *
@@@ -2186,7 -2195,7 +2198,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);
  
@@@ -2329,7 -2338,7 +2341,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;
@@@ -2424,7 -2433,7 +2436,7 @@@ query_stats(struct ofproto *p, struct r
  
      packet_count = rule->packet_count;
      byte_count = rule->byte_count;
 -    if (!dpif_flow_get_multiple(&p->dpif, odp_flows, n_odp_flows)) {
 +    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];
@@@ -3048,7 -3057,7 +3060,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);
@@@ -3170,7 -3179,7 +3182,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
@@@ -3262,7 -3271,7 +3274,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;
          }
  
@@@ -3350,23 -3359,23 +3362,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/ofproto.h"
  #include "packets.h"
  #include "poll-loop.h"
  #include "port-array.h"
  #include "proc-net-compat.h"
  #include "process.h"
 -#include "secchan/ofproto.h"
  #include "socket-util.h"
  #include "stp.h"
  #include "svec.h"
@@@ -71,18 -71,17 +71,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
@@@ -160,7 -159,7 +160,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. */
@@@ -203,11 -202,10 +203,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);
@@@ -227,7 -225,6 +227,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 *);
@@@ -245,6 -242,8 +245,8 @@@ static void iface_destroy(struct iface 
  static struct iface *iface_lookup(const struct bridge *, const char *name);
  static struct iface *iface_from_dp_ifidx(const struct bridge *,
                                           uint16_t dp_ifidx);
+ static bool iface_is_internal(const struct bridge *, const char *name);
+ static void iface_set_mac(struct iface *);
  
  /* Hooks into ofproto processing. */
  static struct ofhooks bridge_ofhooks;
@@@ -266,8 -265,8 +268,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();
  }
  
@@@ -358,105 -350,43 +360,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
  
- set_iface_policing(struct bridge *br UNUSED, struct iface *iface,
 +/* 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
-     int rate = cfg_get_int(0, "port.%s.ingress.policing-rate", iface->name);
-     int burst = cfg_get_int(0, "port.%s.ingress.policing-burst", iface->name);
++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;
 +
-             /* It's an internal interface if it's marked that way, or if
-              * it's a bonded interface for which we're faking up a network
-              * device. */
-             internal = cfg_get_bool(0, "iface.%s.internal", if_name);
-             if (cfg_get_bool(0, "bonding.%s.fake-iface", if_name)) {
-                 struct port *port = port_lookup(br, if_name);
-                 if (port && port->n_ifaces > 1) {
-                     internal = true;
-                 }
-             }
 +            /* 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;
 -        uint8_t engine_type = br->dpif.minor;
 -        uint8_t engine_id = br->dpif.minor;
 +        struct iface *local_iface;
 +        struct iface *hw_addr_iface;
 +        uint8_t engine_type, engine_id;
          bool add_id_to_iface = false;
          struct svec nf_hosts;
  
          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. */
 +        dpif_get_netflow_ids(br->dpif, &engine_type, &engine_id);
          if (cfg_has("netflow.%s.engine-type", br->name)) {
              engine_type = cfg_get_int(0, "netflow.%s.engine-type", 
                      br->name);
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          for (i = 0; i < br->n_ports; i++) {
              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_policing, NULL);
++        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
@@@ -899,26 -855,6 +903,26 @@@ bridge_flush(struct bridge *br
          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
@@@ -961,7 -897,7 +965,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;
  }
@@@ -1012,12 -948,12 +1016,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);
@@@ -1109,29 -1045,13 +1113,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
@@@ -1270,8 -1204,9 +1274,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);
              }
          }
  
@@@ -1434,17 -1370,17 +1438,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;
@@@ -2057,6 -1993,7 +2061,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);
@@@ -2735,25 -2672,6 +2739,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. */
@@@ -3063,7 -2980,7 +3067,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);
      }
  
      proc_net_compat_update_bond(port->name, &bond);
@@@ -3094,8 -3011,7 +3098,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)) {
@@@ -3121,7 -3037,18 +3125,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);
  }
  
@@@ -3153,7 -3081,6 +3157,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);
      }
  }
@@@ -3189,6 -3117,60 +3193,60 @@@ iface_from_dp_ifidx(const struct bridg
  {
      return port_array_get(&br->ifaces, dp_ifidx);
  }
 -            int error = netdev_nodev_set_etheraddr(iface->name, ea);
+ /* Returns true if 'iface' is the name of an "internal" interface on bridge
+  * 'br', that is, an interface that is entirely simulated within the datapath.
+  * The local port (ODPP_LOCAL) is always an internal interface.  Other local
+  * interfaces are created by setting "iface.<iface>.internal = true".
+  *
+  * In addition, we have a kluge-y feature that creates an internal port with
+  * the name of a bonded port if "bonding.<bondname>.fake-iface = true" is set.
+  * This feature needs to go away in the long term.  Until then, this is one
+  * reason why this function takes a name instead of a struct iface: the fake
+  * interfaces created this way do not have a struct iface. */
+ static bool
+ iface_is_internal(const struct bridge *br, const char *iface)
+ {
+     if (!strcmp(iface, br->name)
+         || cfg_get_bool(0, "iface.%s.internal", iface)) {
+         return true;
+     }
+     if (cfg_get_bool(0, "bonding.%s.fake-iface", iface)) {
+         struct port *port = port_lookup(br, iface);
+         if (port && port->n_ifaces > 1) {
+             return true;
+         }
+     }
+     return false;
+ }
+ /* Set Ethernet address of 'iface', if one is specified in the configuration
+  * file. */
+ static void
+ iface_set_mac(struct iface *iface)
+ {
+     uint64_t mac = cfg_get_mac(0, "iface.%s.mac", iface->name);
+     if (mac) {
+         static uint8_t ea[ETH_ADDR_LEN];
+         eth_addr_from_uint64(mac, ea);
+         if (eth_addr_is_multicast(ea)) {
+             VLOG_ERR("interface %s: cannot set MAC to multicast address",
+                      iface->name);
+         } else if (iface->dp_ifidx == ODPP_LOCAL) {
+             VLOG_ERR("ignoring iface.%s.mac; use bridge.%s.mac instead",
+                      iface->name, iface->name);
+         } else {
++            int error = netdev_set_etheraddr(iface->netdev, ea);
+             if (error) {
+                 VLOG_ERR("interface %s: setting MAC failed (%s)",
+                          iface->name, strerror(error));
+             }
+         }
+     }
+ }
  \f
  /* Port mirroring. */
  
@@@ -3510,25 -3492,23 +3568,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);
  }
  .  RE
  .  PP
  ..
 -.TH ovs\-vswitchd.conf 5 "April 2009" "Open vSwitch" "Open vSwitch Manual"
 +.TH ovs\-vswitchd.conf 5 "June 2009" "Open vSwitch" "Open vSwitch Manual"
  .
  .SH NAME
  ovs\-vswitchd.conf \- configuration file for \fBovs\-vswitchd\fR
  .
  .SH DESCRIPTION
- This manual page describes the syntax for the configuration file used 
- by \fBovs\-vswitchd\fR(8), the Open vSwitch daemon.
+ This manual page explains how to configure \fBovs\-vswitchd\fR, the
+ Open vSwitch virtual switch daemon.  Refer to \fBovs\-vswitchd\fR(8)
+ for instructions on how to start, stop, and control the virtual switch
+ daemon and for an overview of its features.
+ .SS "Overview"
+ \fBovs\-vswitchd\fR configuration is hierarchical.
+ .ST "Global Configuration"
+ A few aspects of configuration apply to the entire \fBovs\-vswitchd\fR
+ process:
+ .IP \(bu
+ Remote management (see \fBRemote Management\fR below).
+ .IP \(bu
+ SSL key and certificate configuration (see \fBSSL Configuration\fR
+ below).
+ .ST "Bridge Configuration"
+ \fBovs\-vswitchd\fR manages one or more ``bridges.''  A bridge is,
+ conceptually, an Ethernet switch.  Properties configurable at the
+ bridge level include:
+ .
+ .IP \(bu
+ The set of bridge ports (see \fBBridge Configuration\fR below).
+ .IP \(bu
+ Mirroring of packets across ports and VLANs (see \fBPort mirroring
+ (SPAN and RSPAN)\fR below).
+ .IP \(bu
+ Flow logging via NetFlow (see \fBNetFlow v5 Flow Logging\fR below).
+ .IP \(bu
+ Connectivity to an OpenFlow controller (see \fBOpenFlow Controller
+ Connectivity\fR below).
+ .IP \(bu
+ Addresses on which to listen for OpenFlow management connections (see
+ \fBOpenFlow Management Connections\fR below) or for snooping on the
+ connection to the primary OpenFlow controller (see \fBOpenFlow
+ Controller Connection Snooping\fR below).
  .PP
- The configuration file is based on key-value pairs, which are given
+ .ST "Port Configuration"
+ Each bridge has one or more ``ports.''  The main configurable property
+ of a port is its 802.1Q VLAN configuration (see \fB802.1Q VLAN
+ support\fR below).
+ .PP
+ Most commonly, a port has exactly one ``interface.''  Such a port
+ logically corresponds to a port on a physical Ethernet switch.
+ .PP
+ A port that has more than one interface is a ``bonded port.''  Bonding
+ allows for load balancing and fail-over (see \fBNetwork Device
+ Bonding\fR below).
+ .ST "Interface Configuration"
+ There are two different kinds of interfaces:
+ .IP "``external interfaces''"
+ These interfaces are ordinary network devices, e.g. \fBeth0\fR on
+ Linux.
+ .IP "``internal interfaces''"
+ These interfaces are simulated network device that sent and receive
+ traffic.  Every bridge has one internal interface called the ``local
+ interface'' and may also have additional internal interfaces.  It does
+ not make sense to bond an internal interface, so the terms ``port''
+ and ``interface'' are often used imprecisely for internal interfaces.
+ .PP
+ Interfaces have a few configurable properties of their own:
+ .IP \(bu
+ Ingress rate-limiting (see \fBInterface Rate-Limiting\fR below).
+ .IP \(bu
+ Ethernet address (internal interfaces only, see \fBBridge
+ Configuration\fR below).
+ .SS "Configuration File Syntax"
+ The \fBovs\-vswitchd\fR configuration file syntax is based on
+ key-value pairs, which are given
  one per line in the form \fIkey\fB=\fIvalue\fR.  Each \fIkey\fR
  consists of one or more parts separated by dots,
  e.g. \fIpart1\fB.\fIpart2\fB.\fIpart3\fR.  Each \fIpart\fR may consist
@@@ -50,18 -113,19 +113,18 @@@ configure \fBovs\-vswitchd\fR
  .SS "Bridge Configuration"
  A bridge (switch) with a given \fIname\fR is configured by specifying
  the names of its network devices as values for key
 -\fBbridge.\fIname\fB.port\fR.  (The specified \fIname\fR may not begin
 -with \fBdp\fR or \fBnl:\fR followed by a digit.)
 +\fBbridge.\fIname\fB.port\fR.
  .PP
- The names given on \fBbridge.\fIname\fB.port\fR must be the names of
- existing network devices, except for ``internal ports.''  An internal
- port is a simulated network device that receives traffic only
- through the switch and switches any traffic sent it through the
- switch.  An internal port may configured with an IP address,
- etc. using the usual system tools (e.g. \fBifconfig\fR, \fBip\fR).  To
- designate network device \fInetdev\fR as an internal port, add
- \fBiface.\fInetdev\fB.internal=true\fR to the configuration file.
- \fBovs\-vswitchd\fR will honor this configuration setting by automatically
creating the named internal port.
+ To designate network device \fInetdev\fR as an internal port, add
+ \fBiface.\fInetdev\fB.internal=true\fR to the configuration file,
+ which causes \fBovs\-vswitchd\fR to automatically creates
+ \fInetdev\fR, which may then be configured using the usual system
+ tools (e.g. \fBifconfig\fR, \fBip\fR).  An internal interface by
+ default has a random Ethernet address, but you may configure a
+ specific address by setting \fBiface.\fInetdev\fB.mac\fR to a MAC
+ address in the format
+ \fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR, where each
\fIx\fR is a hex digit.
  .PP
  A bridge with a given \fIname\fR always has an internal port with the
  same \fIname\fR, called the ``local port.''  This network device may
@@@ -281,7 -345,14 +344,14 @@@ correctly pointed to port 1, with one t
  the end host to the Open vSwitch on port 2, instead of to the end host
  on port 1, disrupting connectivity.  If mirroring to a VLAN is desired
  in this scenario, then the physical switch must be replaced by one
- that learns Ethernet addresses on a per-VLAN basis.
+ that learns Ethernet addresses on a per-VLAN basis.  In addition,
+ learning should be disabled on the VLAN containing mirrored traffic.
+ If this is not done then the intermediate switch will learn the MAC
+ address of each end host from the mirrored traffic.  If packets being
+ sent to that end host are also mirrored, then they will be dropped
+ since the switch will attempt to send them out the input port.
+ Disabling learning for the VLAN will cause the switch to correctly
+ send the packet out all ports configured for that VLAN.
  .ST "Example"
  The following \fBovs\-vswitchd\fR configuration copies all frames received
  on \fBeth1\fR or \fBeth2\fR to \fBeth3\fR.
@@@ -299,16 -370,16 +369,16 @@@ mirror.mybr.a.output.port=eth
          
  .fi
  .RE
- .SS "Port Rate-Limiting"
- Traffic policing and shaping are configured on physical ports.  Policing
+ .SS "Interface Rate-Limiting"
+ Traffic policing and shaping are configured on interfaces.  Policing
  defines a hard limit at which traffic that exceeds the specified rate is
  dropped.  Shaping uses queues to delay packets so that egress traffic
  leaves at the specified rate.
  
  .ST "Ingress Policing"
- The rate at which traffic is allowed to enter through a port may be 
+ The rate at which traffic is allowed to enter through a interface may be 
  configured with ingress policing.  Note that "ingress" is from the 
- perspective of \fBovs\-vswitchd\fR.  If configured on a physical port
+ perspective of \fBovs\-vswitchd\fR.  If configured on a physical interface
  then it limits the rate at which traffic is allowed into the system from 
  the outside.  If configured on a virtual interface that is connected to 
  a virtual machine, then it limits the rate at which the guest is able to 
@@@ -318,9 -389,9 +388,9 @@@ The rate is specified in kilobits (100
  burst size specified in kilobits (1000 bits).  The burst size should be at 
  least the size of the interface's MTU.  
  
- A port may be configured to enforce ingress policing by defining the
+ An interface may be configured to enforce ingress policing by defining the
  key \fBport.\fIname\fB.ingress.policing-rate\fR with an integer
- indicating the rate.  The port \fIname\fR will only allow traffic to be
+ indicating the rate.  The interface \fIname\fR will only allow traffic to be
  received at the rate specified in kilobits per second.  If the rate is zero 
  or the key is not defined, then ingress policing is disabled.
  
@@@ -330,7 -401,7 +400,7 @@@ indicating the burst rate in kilobits
  zero, then the default burst is 10 kilobits.
  
  .PP
- The following syntax limits port \fBeth1\fR to receiving traffic at
+ The following syntax limits interface \fBeth1\fR to receiving traffic at
  \fB512\fR kilobits per second with a burst of \fB20\fR kilobits:
  .PP
  .RS
@@@ -354,7 -425,7 +424,7 @@@ This can be overridden with the \fBnetf
  \fBnetflow.\fIbridge\fB.engine-id\fR, respectively.  Each takes a value
  between 0 and 255, inclusive. 
  
 -Many NetFlow collectors do not expect multiple virtual switches to be
 +Many NetFlow collectors do not expect multiple switches to be
  sending messages from the same host, and they do not store the engine
  information which could be used to disambiguate the traffic.  To prevent
  flows from multiple switches appearing as if they came on the interface,
@@@ -428,7 -499,7 +498,7 @@@ switch will perform all configured brid
  .TP
  \fBdiscover\fR
  Use controller discovery to find the local OpenFlow controller.
 -Refer to \fBsecchan\fR(8) for information on how to configure a DHCP
 +Refer to \fB\ovs\-openflowd\fR(8) for information on how to configure a DHCP
  server to support controller discovery.  The following additional
  options control the discovery process:
  .
@@@ -441,8 -512,8 +511,8 @@@ the regular expression will be accepted
  .IP
  The default regular expression is \fBssl:.*\fR, meaning that only SSL
  controller connections will be accepted, when SSL is configured (see
 -\fBSSL Configuration\fR), and \fB.*\fR otherwise, meaning that any
 -controller will be accepted.
 +\fBSSL Configuration\fR), and \fBtcp:.*\fR otherwise, meaning that only
 +TCP controller connections will be accepted.
  .IP
  The regular expression is implicitly anchored at the beginning of the
  controller location string, as if it begins with \fB^\fR.
@@@ -487,7 -558,7 +557,7 @@@ not in use, the following additional se
  By default, or if this is set to \fBtrue\fR, \fBovs\-vswitchd\fR connects
  to the controller in-band.  If this is set to \fBfalse\fR,
  \fBovs\-vswitchd\fR connects to the controller out-of-band.  Refer to
 -\fBsecchan\fR(8) for a description of in-band and out-of-band control.
 +\fBovs\-openflowd\fR(8) for a description of in-band and out-of-band control.
  .IP "\fBbridge.\fIname\fB.controller.ip=\fIip\fR"
  If specified, the IP address to configure on the bridge's local port.
  .IP "\fBbridge.\fIname\fB.controller.netmask=\fInetmask\fR"
@@@ -505,11 -576,11 +575,11 @@@ This optional setting may be set to \fI
  The minimum value of \fIsecs\fR is 5 seconds.  The default is taken
  from \fBmgmt.inactivity-probe\fR (see above).
  .IP
 -When the virtual switch is connected to the controller, it waits for a
 +When the switch is connected to the controller, it waits for a
  message to be received from the controller for \fIsecs\fR seconds
  before it sends a inactivity probe to the controller.  After sending
  the inactivity probe, if no response is received for an additional
 -\fIsecs\fR seconds, the secure channel assumes that the connection has
 +\fIsecs\fR seconds, \fBovs-vswitchd\fR assumes that the connection has
  been broken and attempts to reconnect.
  .IP
  Changing the inactivity probe interval also changes the interval
@@@ -517,7 -588,7 +587,7 @@@ before entering standalone mode (see be
  .IP "\fBbridge.\fIname\fB.controller.fail-mode=\fBstandalone\fR|\fBsecure\fR"
  .IQ "\fBmgmt.fail-mode=standalone\fR|\fBsecure\fR"
  When a controller is configured, it is, ordinarily, responsible for
 -setting up all flows on the virtual switch.  Thus, if the connection to
 +setting up all flows on the switch.  Thus, if the connection to
  the controller fails, no new network connections can be set up.  If
  the connection to the controller stays down long enough, no packets
  can pass through the switch at all.
@@@ -542,23 -613,10 +612,23 @@@ connection attempts starts at 1 second 
  attempt until it reaches the maximum.  The default maximum backoff
  time is taken from \fBmgmt.max-backoff\fR.
  .ST "Controller Rate-Limiting"
 -These settings configure how the virtual switch applies a ``token
 +These settings configure how the switch applies a ``token
  bucket'' to limit the rate at which packets in unknown flows are
  forwarded to the OpenFlow controller for flow-setup processing.  This
  feature prevents a single bridge from overwhelming a controller.
 +.PP
 +In addition, when a high rate triggers rate-limiting,
 +\fBovs\-vswitchd\fR queues controller packets for each port and
 +transmits them to the controller at the configured rate.  The number
 +of queued packets is limited by a ``burst size'' parameter.  The
 +packet queue is shared fairly among the ports on a bridge.
 +.PP
 +\fBovs\-vswitchd\fR maintains two such packet rate-limiters per
 +bridge.  One of these applies to packets sent up to the controller
 +because they do not correspond to any flow.  The other applies to
 +packets sent up to the controller by request through flow actions.
 +When both rate-limiters are filled with packets, the actual rate that
 +packets are sent to the controller is up to twice the specified rate.
  .IP "\fBbridge.\fIname\fB.controller.rate-limit=\fIrate\fR"
  .IQ "\fBmgmt.rate-limit=\fIrate\fR"
  Limits the maximum rate at which packets will be forwarded to the
@@@ -608,23 -666,24 +678,23 @@@ When \fBovs\-vswitchd\fR is configured 
  for controller connectivity, the following settings are required:
  .TP
  \fBssl.private-key=\fIprivkey.pem\fR
 -Specifies a PEM file containing the private key used as the virtual
 +Specifies a PEM file containing the private key used as the 
  switch's identity for SSL connections to the controller.
  .TP
  \fBssl.certificate=\fIcert.pem\fR
  Specifies a PEM file containing a certificate, signed by the
  certificate authority (CA) used by the controller and manager, that
 -certifies the virtual switch's private key, identifying a trustworthy
 +certifies the switch's private key, identifying a trustworthy
  switch.
  .TP
  \fBssl.ca-cert=\fIcacert.pem\fR
  Specifies a PEM file containing the CA certificate used to verify that
 -the virtual switch is connected to a trustworthy controller.
 +the switch is connected to a trustworthy controller.
  .PP
  These files are read only once, at \fBovs\-vswitchd\fR startup time.  If
  their contents change, \fBovs\-vswitchd\fR must be killed and restarted.
  .PP
 -These SSL settings apply to all SSL connections made by the virtual
 -switch.
 +These SSL settings apply to all SSL connections made by the switch.
  .ST "CA Certificate Bootstrap"
  Ordinarily, all of the files named in the SSL configuration must exist
  when \fBovs\-vswitchd\fR starts.  However, if \fBssl.bootstrap-ca-cert\fR
@@@ -662,11 -721,8 +732,11 @@@ Listens for connections on the Unix dom
  Listens for SSL connections on \fIport\fR (default: 6633).  SSL must
  be configured when this form is used (see \fBSSL Configuration\fR,
  above).
 -.IP "\fBptcp:\fR[\fIport\fR]"
 +.IP "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]"
  Listens for TCP connections on \fIport\fR (default: 6633).
 +By default, \fB\ovs\-vswitchd\fR listens for connections to any local
 +IP address, but \fIip\fR may be specified to limit connections to the
 +specified local \fIip\fR.
  .RE
  To entirely disable listening for management connections, set
  \fBbridge.\fIname\fB.openflow.listeners\fR to the single value
  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
  }
  
@@@ -266,23 -270,22 +266,24 @@@ function start 
          action "Creating empty $VSWITCHD_CONF" touch "$VSWITCHD_CONF"
      elif [ ! -e /var/run/vswitch.booted ]; then
          touch /var/run/vswitch.booted
-       /usr/bin/ovs-cfg-mod '-vANY:console:emer' -F "$VSWITCHD_CONF" \
-           '--del-match=bridge.*' \
-           '--del-match=port.*' \
-           '--del-match=bonding.*' \
-           '--del-match=iface.*'
+         /usr/bin/ovs-cfg-mod '-vANY:console:emer' -F "$VSWITCHD_CONF" \
+             '--del-match=bridge.*' \
+             '--del-match=port.*' \
+             '--del-match=bonding.*' \
+             '--del-match=iface.*' \
+             '--del-match=vlan.*'
      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 {
@@@ -319,8 -322,8 +320,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"