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
index 7bb8cd70b9bb59e12a18bf57e9c42bb1af329f37,520e1892948c968ce0b3b0d80eca4ce622640d36..da33fe8b6fab4b994ffc2c0ee3793c56f171cf7c
@@@ -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
index 370336608bac6baf891bce73966d006a0911e463,0000000000000000000000000000000000000000..f31794f9ae0413ad20cb8cb3be00eb03f1dbf2a3
mode 100644,000000..100644
--- /dev/null
@@@ -1,3428 -1,0 +1,3440 @@@
-     if (!ofport || !(ofport->opp.config & OFPPC_NO_FWD)) {
-         odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
 +/*
 + * Copyright (c) 2009 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +#include "ofproto.h"
 +#include <errno.h>
 +#include <inttypes.h>
 +#include <net/if.h>
 +#include <netinet/in.h>
 +#include <stdbool.h>
 +#include <stdlib.h>
 +#include "classifier.h"
 +#include "coverage.h"
 +#include "discovery.h"
 +#include "dpif.h"
 +#include "dynamic-string.h"
 +#include "executer.h"
 +#include "fail-open.h"
 +#include "in-band.h"
 +#include "mac-learning.h"
 +#include "netdev.h"
 +#include "netflow.h"
 +#include "odp-util.h"
 +#include "ofp-print.h"
 +#include "ofpbuf.h"
 +#include "openflow/nicira-ext.h"
 +#include "openflow/openflow.h"
 +#include "openflow/openflow-mgmt.h"
 +#include "openvswitch/datapath-protocol.h"
 +#include "packets.h"
 +#include "pinsched.h"
 +#include "pktbuf.h"
 +#include "poll-loop.h"
 +#include "port-array.h"
 +#include "rconn.h"
 +#include "shash.h"
 +#include "status.h"
 +#include "stp.h"
 +#include "svec.h"
 +#include "tag.h"
 +#include "timeval.h"
 +#include "unixctl.h"
 +#include "vconn.h"
 +#include "vconn-ssl.h"
 +#include "xtoxll.h"
 +
 +#define THIS_MODULE VLM_ofproto
 +#include "vlog.h"
 +
 +enum {
 +    DP_GROUP_FLOOD = 0,
 +    DP_GROUP_ALL = 1
 +};
 +
 +enum {
 +    TABLEID_HASH = 0,
 +    TABLEID_CLASSIFIER = 1
 +};
 +
 +struct ofport {
 +    struct netdev *netdev;
 +    struct ofp_phy_port opp;    /* In host byte order. */
 +};
 +
 +static void ofport_free(struct ofport *);
 +static void hton_ofp_phy_port(struct ofp_phy_port *);
 +
 +static int xlate_actions(const union ofp_action *in, size_t n_in,
 +                         const flow_t *flow, struct ofproto *ofproto,
 +                         const struct ofpbuf *packet,
 +                         struct odp_actions *out, tag_type *tags,
 +                         bool *may_setup_flow);
 +
 +struct rule {
 +    struct cls_rule cr;
 +
 +    uint16_t idle_timeout;      /* In seconds from time of last use. */
 +    uint16_t hard_timeout;      /* In seconds from time of creation. */
 +    long long int used;         /* Last-used time (0 if never used). */
 +    long long int created;      /* Creation time. */
 +    uint64_t packet_count;      /* Number of packets received. */
 +    uint64_t byte_count;        /* Number of bytes received. */
 +    uint64_t accounted_bytes;   /* Number of bytes passed to account_cb. */
 +    uint8_t tcp_flags;          /* Bitwise-OR of all TCP flags seen. */
 +    uint8_t ip_tos;             /* Last-seen IP type-of-service. */
 +    tag_type tags;              /* Tags (set only by hooks). */
 +
 +    /* If 'super' is non-NULL, this rule is a subrule, that is, it is an
 +     * exact-match rule (having cr.wc.wildcards of 0) generated from the
 +     * wildcard rule 'super'.  In this case, 'list' is an element of the
 +     * super-rule's list.
 +     *
 +     * If 'super' is NULL, this rule is a super-rule, and 'list' is the head of
 +     * a list of subrules.  A super-rule with no wildcards (where
 +     * cr.wc.wildcards is 0) will never have any subrules. */
 +    struct rule *super;
 +    struct list list;
 +
 +    /* OpenFlow actions.
 +     *
 +     * A subrule has no actions (it uses the super-rule's actions). */
 +    int n_actions;
 +    union ofp_action *actions;
 +
 +    /* Datapath actions.
 +     *
 +     * A super-rule with wildcard fields never has ODP actions (since the
 +     * datapath only supports exact-match flows). */
 +    bool installed;             /* Installed in datapath? */
 +    bool may_install;           /* True ordinarily; false if actions must
 +                                 * be reassessed for every packet. */
 +    int n_odp_actions;
 +    union odp_action *odp_actions;
 +};
 +
 +static inline bool
 +rule_is_hidden(const struct rule *rule)
 +{
 +    /* Subrules are merely an implementation detail, so hide them from the
 +     * controller. */
 +    if (rule->super != NULL) {
 +        return true;
 +    }
 +
 +    /* 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) {
 +        return true;
 +    }
 +
 +    return false;
 +}
 +
 +static struct rule *rule_create(struct rule *super, const union ofp_action *,
 +                                size_t n_actions, uint16_t idle_timeout,
 +                                uint16_t hard_timeout);
 +static void rule_free(struct rule *);
 +static void rule_destroy(struct ofproto *, struct rule *);
 +static struct rule *rule_from_cls_rule(const struct cls_rule *);
 +static void rule_insert(struct ofproto *, struct rule *,
 +                        struct ofpbuf *packet, uint16_t in_port);
 +static void rule_remove(struct ofproto *, struct rule *);
 +static bool rule_make_actions(struct ofproto *, struct rule *,
 +                              const struct ofpbuf *packet);
 +static void rule_install(struct ofproto *, struct rule *,
 +                         struct rule *displaced_rule);
 +static void rule_uninstall(struct ofproto *, struct rule *);
 +static void rule_post_uninstall(struct ofproto *, struct rule *);
 +
 +struct ofconn {
 +    struct list node;
 +    struct rconn *rconn;
 +    struct pktbuf *pktbuf;
 +    bool send_flow_exp;
 +    int miss_send_len;
 +
 +    struct rconn_packet_counter *packet_in_counter;
 +
 +    /* Number of OpenFlow messages queued as replies to OpenFlow requests, and
 +     * the maximum number before we stop reading OpenFlow requests.  */
 +#define OFCONN_REPLY_MAX 100
 +    struct rconn_packet_counter *reply_counter;
 +};
 +
 +static struct ofconn *ofconn_create(struct ofproto *, struct rconn *);
 +static void ofconn_destroy(struct ofconn *, struct ofproto *);
 +static void ofconn_run(struct ofconn *, struct ofproto *);
 +static void ofconn_wait(struct ofconn *);
 +static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
 +                     struct rconn_packet_counter *counter);
 +
 +struct ofproto {
 +    /* Settings. */
 +    uint64_t datapath_id;       /* Datapath ID. */
 +    uint64_t fallback_dpid;     /* Datapath ID if no better choice found. */
 +    uint64_t mgmt_id;           /* Management channel identifier. */
 +    char *manufacturer;         /* Manufacturer. */
 +    char *hardware;             /* Hardware. */
 +    char *software;             /* Software version. */
 +    char *serial;               /* Serial number. */
 +
 +    /* Datapath. */
 +    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;
 +    uint32_t max_ports;
 +
 +    /* Configuration. */
 +    struct switch_status *switch_status;
 +    struct status_category *ss_cat;
 +    struct in_band *in_band;
 +    struct discovery *discovery;
 +    struct fail_open *fail_open;
 +    struct pinsched *miss_sched, *action_sched;
 +    struct executer *executer;
 +    struct netflow *netflow;
 +
 +    /* Flow table. */
 +    struct classifier cls;
 +    bool need_revalidate;
 +    long long int next_expiration;
 +    struct tag_set revalidate_set;
 +
 +    /* OpenFlow connections. */
 +    struct list all_conns;
 +    struct ofconn *controller;
 +    struct pvconn **listeners;
 +    size_t n_listeners;
 +    struct pvconn **snoops;
 +    size_t n_snoops;
 +
 +    /* Hooks for ovs-vswitchd. */
 +    const struct ofhooks *ofhooks;
 +    void *aux;
 +
 +    /* Used by default ofhooks. */
 +    struct mac_learning *ml;
 +};
 +
 +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +
 +static const struct ofhooks default_ofhooks;
 +
 +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);
 +static void update_used(struct ofproto *);
 +static void update_stats(struct rule *, const struct odp_flow_stats *);
 +static void expire_rule(struct cls_rule *, void *ofproto);
 +static bool revalidate_rule(struct ofproto *p, struct rule *rule);
 +static void revalidate_cb(struct cls_rule *rule_, void *p_);
 +
 +static void handle_odp_msg(struct ofproto *, struct ofpbuf *);
 +
 +static void handle_openflow(struct ofconn *, struct ofproto *,
 +                            struct ofpbuf *);
 +
 +static void refresh_port_group(struct ofproto *, unsigned int group);
 +static void update_port(struct ofproto *, const char *devname);
 +static int init_ports(struct ofproto *);
 +static void reinit_ports(struct ofproto *);
 +
 +int
 +ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
 +               struct ofproto **ofprotop)
 +{
 +    struct odp_stats stats;
 +    struct ofproto *p;
 +    struct dpif *dpif;
 +    int error;
 +
 +    *ofprotop = NULL;
 +
 +    /* Connect to datapath and start listening for messages. */
 +    error = dpif_open(datapath, &dpif);
 +    if (error) {
 +        VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error));
 +        return error;
 +    }
 +    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);
 +        return error;
 +    }
 +    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_recv_purge(dpif);
 +
 +    /* Initialize settings. */
 +    p = xcalloc(1, sizeof *p);
 +    p->fallback_dpid = pick_fallback_dpid();
 +    p->datapath_id = p->fallback_dpid;
 +    p->manufacturer = xstrdup("Nicira Networks, Inc.");
 +    p->hardware = xstrdup("Reference Implementation");
 +    p->software = xstrdup(VERSION BUILDNR);
 +    p->serial = xstrdup("None");
 +
 +    /* Initialize datapath. */
 +    p->dpif = dpif;
 +    p->netdev_monitor = netdev_monitor_create();
 +    port_array_init(&p->ports);
 +    shash_init(&p->port_by_name);
 +    p->max_ports = stats.max_ports;
 +
 +    /* Initialize submodules. */
 +    p->switch_status = switch_status_create(p);
 +    p->in_band = NULL;
 +    p->discovery = NULL;
 +    p->fail_open = NULL;
 +    p->miss_sched = p->action_sched = NULL;
 +    p->executer = NULL;
 +    p->netflow = NULL;
 +
 +    /* Initialize flow table. */
 +    classifier_init(&p->cls);
 +    p->need_revalidate = false;
 +    p->next_expiration = time_msec() + 1000;
 +    tag_set_init(&p->revalidate_set);
 +
 +    /* Initialize OpenFlow connections. */
 +    list_init(&p->all_conns);
 +    p->controller = ofconn_create(p, rconn_create(5, 8));
 +    p->controller->pktbuf = pktbuf_create();
 +    p->controller->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
 +    p->listeners = NULL;
 +    p->n_listeners = 0;
 +    p->snoops = NULL;
 +    p->n_snoops = 0;
 +
 +    /* Initialize hooks. */
 +    if (ofhooks) {
 +        p->ofhooks = ofhooks;
 +        p->aux = aux;
 +        p->ml = NULL;
 +    } else {
 +        p->ofhooks = &default_ofhooks;
 +        p->aux = p;
 +        p->ml = mac_learning_create();
 +    }
 +
 +    /* Register switch status category. */
 +    p->ss_cat = switch_status_register(p->switch_status, "remote",
 +                                       rconn_status_cb, p->controller->rconn);
 +
 +    /* Almost done... */
 +    error = init_ports(p);
 +    if (error) {
 +        ofproto_destroy(p);
 +        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;
 +}
 +
 +void
 +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);
 +    if (p->datapath_id != old_dpid) {
 +        VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id);
 +        rconn_reconnect(p->controller->rconn);
 +    }
 +}
 +
 +void
 +ofproto_set_mgmt_id(struct ofproto *p, uint64_t mgmt_id)
 +{
 +    p->mgmt_id = mgmt_id;
 +}
 +
 +void
 +ofproto_set_probe_interval(struct ofproto *p, int probe_interval)
 +{
 +    probe_interval = probe_interval ? MAX(probe_interval, 5) : 0;
 +    rconn_set_probe_interval(p->controller->rconn, probe_interval);
 +    if (p->fail_open) {
 +        int trigger_duration = probe_interval ? probe_interval * 3 : 15;
 +        fail_open_set_trigger_duration(p->fail_open, trigger_duration);
 +    }
 +}
 +
 +void
 +ofproto_set_max_backoff(struct ofproto *p, int max_backoff)
 +{
 +    rconn_set_max_backoff(p->controller->rconn, max_backoff);
 +}
 +
 +void
 +ofproto_set_desc(struct ofproto *p,
 +                 const char *manufacturer, const char *hardware,
 +                 const char *software, const char *serial)
 +{
 +    if (manufacturer) {
 +        free(p->manufacturer);
 +        p->manufacturer = xstrdup(manufacturer);
 +    }
 +    if (hardware) {
 +        free(p->hardware);
 +        p->hardware = xstrdup(hardware);
 +    }
 +    if (software) {
 +        free(p->software);
 +        p->software = xstrdup(software);
 +    }
 +    if (serial) {
 +        free(p->serial);
 +        p->serial = xstrdup(serial);
 +    }
 +}
 +
 +int
 +ofproto_set_in_band(struct ofproto *p, bool in_band)
 +{
 +    if (in_band != (p->in_band != NULL)) {
 +        if (in_band) {
 +            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);
 +            p->in_band = NULL;
 +        }
 +        rconn_reconnect(p->controller->rconn);
 +    }
 +    return 0;
 +}
 +
 +int
 +ofproto_set_discovery(struct ofproto *p, bool discovery,
 +                      const char *re, bool update_resolv_conf)
 +{
 +    if (discovery != (p->discovery != NULL)) {
 +        if (discovery) {
 +            int error = ofproto_set_in_band(p, true);
 +            if (error) {
 +                return error;
 +            }
 +            error = discovery_create(re, update_resolv_conf,
 +                                     p->dpif, p->switch_status,
 +                                     &p->discovery);
 +            if (error) {
 +                return error;
 +            }
 +        } else {
 +            discovery_destroy(p->discovery);
 +            p->discovery = NULL;
 +        }
 +        rconn_disconnect(p->controller->rconn);
 +    } else if (discovery) {
 +        discovery_set_update_resolv_conf(p->discovery, update_resolv_conf);
 +        return discovery_set_accept_controller_re(p->discovery, re);
 +    }
 +    return 0;
 +}
 +
 +int
 +ofproto_set_controller(struct ofproto *ofproto, const char *controller)
 +{
 +    if (ofproto->discovery) {
 +        return EINVAL;
 +    } else if (controller) {
 +        if (strcmp(rconn_get_name(ofproto->controller->rconn), controller)) {
 +            return rconn_connect(ofproto->controller->rconn, controller);
 +        } else {
 +            return 0;
 +        }
 +    } else {
 +        rconn_disconnect(ofproto->controller->rconn);
 +        return 0;
 +    }
 +}
 +
 +static int
 +set_pvconns(struct pvconn ***pvconnsp, size_t *n_pvconnsp,
 +            const struct svec *svec)
 +{
 +    struct pvconn **pvconns = *pvconnsp;
 +    size_t n_pvconns = *n_pvconnsp;
 +    int retval = 0;
 +    size_t i;
 +
 +    for (i = 0; i < n_pvconns; i++) {
 +        pvconn_close(pvconns[i]);
 +    }
 +    free(pvconns);
 +
 +    pvconns = xmalloc(svec->n * sizeof *pvconns);
 +    n_pvconns = 0;
 +    for (i = 0; i < svec->n; i++) {
 +        const char *name = svec->names[i];
 +        struct pvconn *pvconn;
 +        int error;
 +
 +        error = pvconn_open(name, &pvconn);
 +        if (!error) {
 +            pvconns[n_pvconns++] = pvconn;
 +        } else {
 +            VLOG_ERR("failed to listen on %s: %s", name, strerror(error));
 +            if (!retval) {
 +                retval = error;
 +            }
 +        }
 +    }
 +
 +    *pvconnsp = pvconns;
 +    *n_pvconnsp = n_pvconns;
 +
 +    return retval;
 +}
 +
 +int
 +ofproto_set_listeners(struct ofproto *ofproto, const struct svec *listeners)
 +{
 +    return set_pvconns(&ofproto->listeners, &ofproto->n_listeners, listeners);
 +}
 +
 +int
 +ofproto_set_snoops(struct ofproto *ofproto, const struct svec *snoops)
 +{
 +    return set_pvconns(&ofproto->snoops, &ofproto->n_snoops, snoops);
 +}
 +
 +int
 +ofproto_set_netflow(struct ofproto *ofproto, const struct svec *collectors,
 +        uint8_t engine_type, uint8_t engine_id, bool add_id_to_iface)
 +{
 +    if (collectors && collectors->n) {
 +        if (!ofproto->netflow) {
 +            ofproto->netflow = netflow_create();
 +        }
 +        netflow_set_engine(ofproto->netflow, engine_type, engine_id, 
 +                add_id_to_iface);
 +        return netflow_set_collectors(ofproto->netflow, collectors);
 +    } else {
 +        netflow_destroy(ofproto->netflow);
 +        ofproto->netflow = NULL;
 +        return 0;
 +    }
 +}
 +
 +void
 +ofproto_set_failure(struct ofproto *ofproto, bool fail_open)
 +{
 +    if (fail_open) {
 +        struct rconn *rconn = ofproto->controller->rconn;
 +        int trigger_duration = rconn_get_probe_interval(rconn) * 3;
 +        if (!ofproto->fail_open) {
 +            ofproto->fail_open = fail_open_create(ofproto, trigger_duration,
 +                                                  ofproto->switch_status,
 +                                                  rconn);
 +        } else {
 +            fail_open_set_trigger_duration(ofproto->fail_open,
 +                                           trigger_duration);
 +        }
 +    } else {
 +        fail_open_destroy(ofproto->fail_open);
 +        ofproto->fail_open = NULL;
 +    }
 +}
 +
 +void
 +ofproto_set_rate_limit(struct ofproto *ofproto,
 +                       int rate_limit, int burst_limit)
 +{
 +    if (rate_limit > 0) {
 +        if (!ofproto->miss_sched) {
 +            ofproto->miss_sched = pinsched_create(rate_limit, burst_limit,
 +                                                  ofproto->switch_status);
 +            ofproto->action_sched = pinsched_create(rate_limit, burst_limit,
 +                                                    NULL);
 +        } else {
 +            pinsched_set_limits(ofproto->miss_sched, rate_limit, burst_limit);
 +            pinsched_set_limits(ofproto->action_sched,
 +                                rate_limit, burst_limit);
 +        }
 +    } else {
 +        pinsched_destroy(ofproto->miss_sched);
 +        ofproto->miss_sched = NULL;
 +        pinsched_destroy(ofproto->action_sched);
 +        ofproto->action_sched = NULL;
 +    }
 +}
 +
 +int
 +ofproto_set_stp(struct ofproto *ofproto UNUSED, bool enable_stp)
 +{
 +    /* XXX */
 +    if (enable_stp) {
 +        VLOG_WARN("STP is not yet implemented");
 +        return EINVAL;
 +    } else {
 +        return 0;
 +    }
 +}
 +
 +int
 +ofproto_set_remote_execution(struct ofproto *ofproto, const char *command_acl,
 +                             const char *command_dir)
 +{
 +    if (command_acl) {
 +        if (!ofproto->executer) {
 +            return executer_create(command_acl, command_dir,
 +                                   &ofproto->executer);
 +        } else {
 +            executer_set_acl(ofproto->executer, command_acl, command_dir);
 +        }
 +    } else {
 +        executer_destroy(ofproto->executer);
 +        ofproto->executer = NULL;
 +    }
 +    return 0;
 +}
 +
 +uint64_t
 +ofproto_get_datapath_id(const struct ofproto *ofproto)
 +{
 +    return ofproto->datapath_id;
 +}
 +
 +uint64_t
 +ofproto_get_mgmt_id(const struct ofproto *ofproto)
 +{
 +    return ofproto->mgmt_id;
 +}
 +
 +int
 +ofproto_get_probe_interval(const struct ofproto *ofproto)
 +{
 +    return rconn_get_probe_interval(ofproto->controller->rconn);
 +}
 +
 +int
 +ofproto_get_max_backoff(const struct ofproto *ofproto)
 +{
 +    return rconn_get_max_backoff(ofproto->controller->rconn);
 +}
 +
 +bool
 +ofproto_get_in_band(const struct ofproto *ofproto)
 +{
 +    return ofproto->in_band != NULL;
 +}
 +
 +bool
 +ofproto_get_discovery(const struct ofproto *ofproto)
 +{
 +    return ofproto->discovery != NULL;
 +}
 +
 +const char *
 +ofproto_get_controller(const struct ofproto *ofproto)
 +{
 +    return rconn_get_name(ofproto->controller->rconn);
 +}
 +
 +void
 +ofproto_get_listeners(const struct ofproto *ofproto, struct svec *listeners)
 +{
 +    size_t i;
 +
 +    for (i = 0; i < ofproto->n_listeners; i++) {
 +        svec_add(listeners, pvconn_get_name(ofproto->listeners[i]));
 +    }
 +}
 +
 +void
 +ofproto_get_snoops(const struct ofproto *ofproto, struct svec *snoops)
 +{
 +    size_t i;
 +
 +    for (i = 0; i < ofproto->n_snoops; i++) {
 +        svec_add(snoops, pvconn_get_name(ofproto->snoops[i]));
 +    }
 +}
 +
 +void
 +ofproto_destroy(struct ofproto *p)
 +{
 +    struct ofconn *ofconn, *next_ofconn;
 +    struct ofport *ofport;
 +    unsigned int port_no;
 +    size_t i;
 +
 +    if (!p) {
 +        return;
 +    }
 +
 +    ofproto_flush_flows(p);
 +    classifier_destroy(&p->cls);
 +
 +    LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node,
 +                        &p->all_conns) {
 +        ofconn_destroy(ofconn, p);
 +    }
 +
 +    dpif_close(p->dpif);
 +    netdev_monitor_destroy(p->netdev_monitor);
 +    PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
 +        ofport_free(ofport);
 +    }
 +    shash_destroy(&p->port_by_name);
 +
 +    switch_status_destroy(p->switch_status);
 +    in_band_destroy(p->in_band);
 +    discovery_destroy(p->discovery);
 +    fail_open_destroy(p->fail_open);
 +    pinsched_destroy(p->miss_sched);
 +    pinsched_destroy(p->action_sched);
 +    executer_destroy(p->executer);
 +    netflow_destroy(p->netflow);
 +
 +    switch_status_unregister(p->ss_cat);
 +
 +    for (i = 0; i < p->n_listeners; i++) {
 +        pvconn_close(p->listeners[i]);
 +    }
 +    free(p->listeners);
 +
 +    for (i = 0; i < p->n_snoops; i++) {
 +        pvconn_close(p->snoops[i]);
 +    }
 +    free(p->snoops);
 +
 +    mac_learning_destroy(p->ml);
 +
 +    free(p);
 +}
 +
 +int
 +ofproto_run(struct ofproto *p)
 +{
 +    int error = ofproto_run1(p);
 +    if (!error) {
 +        error = ofproto_run2(p, false);
 +    }
 +    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 ofconn *ofconn, *next_ofconn;
 +    char *devname;
 +    int error;
 +    int i;
 +
 +    for (i = 0; i < 50; i++) {
 +        struct ofpbuf *buf;
 +        int error;
 +
 +        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, "%s: datapath was destroyed externally",
 +                            dpif_name(p->dpif));
 +                return ENODEV;
 +            }
 +            break;
 +        }
 +
 +        handle_odp_msg(p, buf);
 +    }
 +
 +    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) {
 +        in_band_run(p->in_band);
 +    }
 +    if (p->discovery) {
 +        char *controller_name;
 +        if (rconn_is_connectivity_questionable(p->controller->rconn)) {
 +            discovery_question_connectivity(p->discovery);
 +        }
 +        if (discovery_run(p->discovery, &controller_name)) {
 +            if (controller_name) {
 +                rconn_connect(p->controller->rconn, controller_name);
 +            } else {
 +                rconn_disconnect(p->controller->rconn);
 +            }
 +        }
 +    }
 +    pinsched_run(p->miss_sched, send_packet_in_miss, p);
 +    pinsched_run(p->action_sched, send_packet_in_action, p);
 +    if (p->executer) {
 +        executer_run(p->executer);
 +    }
 +
 +    LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node,
 +                        &p->all_conns) {
 +        ofconn_run(ofconn, p);
 +    }
 +
 +    /* Fail-open maintenance.  Do this after processing the ofconns since
 +     * fail-open checks the status of the controller rconn. */
 +    if (p->fail_open) {
 +        fail_open_run(p->fail_open);
 +    }
 +
 +    for (i = 0; i < p->n_listeners; i++) {
 +        struct vconn *vconn;
 +        int retval;
 +
 +        retval = pvconn_accept(p->listeners[i], OFP_VERSION, &vconn);
 +        if (!retval) {
 +            ofconn_create(p, rconn_new_from_vconn("passive", vconn));
 +        } else if (retval != EAGAIN) {
 +            VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
 +        }
 +    }
 +
 +    for (i = 0; i < p->n_snoops; i++) {
 +        struct vconn *vconn;
 +        int retval;
 +
 +        retval = pvconn_accept(p->snoops[i], OFP_VERSION, &vconn);
 +        if (!retval) {
 +            rconn_add_monitor(p->controller->rconn, vconn);
 +        } else if (retval != EAGAIN) {
 +            VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
 +        }
 +    }
 +
 +    if (time_msec() >= p->next_expiration) {
 +        COVERAGE_INC(ofproto_expiration);
 +        p->next_expiration = time_msec() + 1000;
 +        update_used(p);
 +
 +        classifier_for_each(&p->cls, CLS_INC_ALL, expire_rule, p);
 +
 +        /* Let the hook know that we're at a stable point: all outstanding data
 +         * in existing flows has been accounted to the account_cb.  Thus, the
 +         * hook can now reasonably do operations that depend on having accurate
 +         * flow volume accounting (currently, that's just bond rebalancing). */
 +        if (p->ofhooks->account_checkpoint_cb) {
 +            p->ofhooks->account_checkpoint_cb(p->aux);
 +        }
 +    }
 +
 +    if (p->netflow) {
 +        netflow_run(p->netflow);
 +    }
 +
 +    return 0;
 +}
 +
 +struct revalidate_cbdata {
 +    struct ofproto *ofproto;
 +    bool revalidate_all;        /* Revalidate all exact-match rules? */
 +    bool revalidate_subrules;   /* Revalidate all exact-match subrules? */
 +    struct tag_set revalidate_set; /* Set of tags to revalidate. */
 +};
 +
 +int
 +ofproto_run2(struct ofproto *p, bool revalidate_all)
 +{
 +    if (p->need_revalidate || revalidate_all
 +        || !tag_set_is_empty(&p->revalidate_set)) {
 +        struct revalidate_cbdata cbdata;
 +        cbdata.ofproto = p;
 +        cbdata.revalidate_all = revalidate_all;
 +        cbdata.revalidate_subrules = p->need_revalidate;
 +        cbdata.revalidate_set = p->revalidate_set;
 +        tag_set_init(&p->revalidate_set);
 +        COVERAGE_INC(ofproto_revalidate);
 +        classifier_for_each(&p->cls, CLS_INC_EXACT, revalidate_cb, &cbdata);
 +        p->need_revalidate = false;
 +    }
 +
 +    return 0;
 +}
 +
 +void
 +ofproto_wait(struct ofproto *p)
 +{
 +    struct ofconn *ofconn;
 +    size_t i;
 +
 +    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);
 +    }
 +    if (p->in_band) {
 +        in_band_wait(p->in_band);
 +    }
 +    if (p->discovery) {
 +        discovery_wait(p->discovery);
 +    }
 +    if (p->fail_open) {
 +        fail_open_wait(p->fail_open);
 +    }
 +    pinsched_wait(p->miss_sched);
 +    pinsched_wait(p->action_sched);
 +    if (p->executer) {
 +        executer_wait(p->executer);
 +    }
 +    if (!tag_set_is_empty(&p->revalidate_set)) {
 +        poll_immediate_wake();
 +    }
 +    if (p->need_revalidate) {
 +        /* Shouldn't happen, but if it does just go around again. */
 +        VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
 +        poll_immediate_wake();
 +    } else if (p->next_expiration != LLONG_MAX) {
 +        poll_timer_wait(p->next_expiration - time_msec());
 +    }
 +    for (i = 0; i < p->n_listeners; i++) {
 +        pvconn_wait(p->listeners[i]);
 +    }
 +    for (i = 0; i < p->n_snoops; i++) {
 +        pvconn_wait(p->snoops[i]);
 +    }
 +}
 +
 +void
 +ofproto_revalidate(struct ofproto *ofproto, tag_type tag)
 +{
 +    tag_set_add(&ofproto->revalidate_set, tag);
 +}
 +
 +struct tag_set *
 +ofproto_get_revalidate_set(struct ofproto *ofproto)
 +{
 +    return &ofproto->revalidate_set;
 +}
 +
 +bool
 +ofproto_is_alive(const struct ofproto *p)
 +{
 +    return p->discovery || rconn_is_alive(p->controller->rconn);
 +}
 +
 +int
 +ofproto_send_packet(struct ofproto *p, const flow_t *flow,
 +                    const union ofp_action *actions, size_t n_actions,
 +                    const struct ofpbuf *packet)
 +{
 +    struct odp_actions odp_actions;
 +    int error;
 +
 +    error = xlate_actions(actions, n_actions, flow, p, packet, &odp_actions,
 +                          NULL, NULL);
 +    if (error) {
 +        return error;
 +    }
 +
 +    /* XXX Should we translate the dpif_execute() errno value into an OpenFlow
 +     * error code? */
 +    dpif_execute(p->dpif, flow->in_port, odp_actions.actions,
 +                 odp_actions.n_actions, packet);
 +    return 0;
 +}
 +
 +void
 +ofproto_add_flow(struct ofproto *p,
 +                 const flow_t *flow, uint32_t wildcards, unsigned int priority,
 +                 const union ofp_action *actions, size_t n_actions,
 +                 int idle_timeout)
 +{
 +    struct rule *rule;
 +    rule = rule_create(NULL, actions, n_actions,
 +                       idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 0);
 +    cls_rule_from_flow(&rule->cr, flow, wildcards, priority);
 +    rule_insert(p, rule, NULL, 0);
 +}
 +
 +void
 +ofproto_delete_flow(struct ofproto *ofproto, const flow_t *flow,
 +                    uint32_t wildcards, unsigned int priority)
 +{
 +    struct rule *rule;
 +
 +    rule = rule_from_cls_rule(classifier_find_rule_exactly(&ofproto->cls,
 +                                                           flow, wildcards,
 +                                                           priority));
 +    if (rule) {
 +        rule_remove(ofproto, rule);
 +    }
 +}
 +
 +static void
 +destroy_rule(struct cls_rule *rule_, void *ofproto_)
 +{
 +    struct rule *rule = rule_from_cls_rule(rule_);
 +    struct ofproto *ofproto = ofproto_;
 +
 +    /* Mark the flow as not installed, even though it might really be
 +     * installed, so that rule_remove() doesn't bother trying to uninstall it.
 +     * There is no point in uninstalling it individually since we are about to
 +     * blow away all the flows with dpif_flow_flush(). */
 +    rule->installed = false;
 +
 +    rule_remove(ofproto, rule);
 +}
 +
 +void
 +ofproto_flush_flows(struct ofproto *ofproto)
 +{
 +    COVERAGE_INC(ofproto_flush);
 +    classifier_for_each(&ofproto->cls, CLS_INC_ALL, destroy_rule, ofproto);
 +    dpif_flow_flush(ofproto->dpif);
 +    if (ofproto->in_band) {
 +        in_band_flushed(ofproto->in_band);
 +    }
 +    if (ofproto->fail_open) {
 +        fail_open_flushed(ofproto->fail_open);
 +    }
 +}
 +\f
 +static void
 +reinit_ports(struct ofproto *p)
 +{
 +    struct svec devnames;
 +    struct ofport *ofport;
 +    unsigned int port_no;
 +    struct odp_port *odp_ports;
 +    size_t n_odp_ports;
 +    size_t i;
 +
 +    svec_init(&devnames);
 +    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);
 +    for (i = 0; i < n_odp_ports; i++) {
 +        svec_add (&devnames, odp_ports[i].devname);
 +    }
 +    free(odp_ports);
 +
 +    svec_sort_unique(&devnames);
 +    for (i = 0; i < devnames.n; i++) {
 +        update_port(p, devnames.names[i]);
 +    }
 +    svec_destroy(&devnames);
 +}
 +
 +static void
 +refresh_port_group(struct ofproto *p, unsigned int group)
 +{
 +    uint16_t *ports;
 +    size_t n_ports;
 +    struct ofport *port;
 +    unsigned int port_no;
 +
 +    assert(group == DP_GROUP_ALL || group == DP_GROUP_FLOOD);
 +
 +    ports = xmalloc(port_array_count(&p->ports) * sizeof *ports);
 +    n_ports = 0;
 +    PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
 +        if (group == DP_GROUP_ALL || !(port->opp.config & OFPPC_NO_FLOOD)) {
 +            ports[n_ports++] = port_no;
 +        }
 +    }
 +    dpif_port_group_set(p->dpif, group, ports, n_ports);
 +    free(ports);
 +}
 +
 +static void
 +refresh_port_groups(struct ofproto *p)
 +{
 +    refresh_port_group(p, DP_GROUP_FLOOD);
 +    refresh_port_group(p, DP_GROUP_ALL);
 +}
 +
 +static struct ofport *
 +make_ofport(const struct odp_port *odp_port)
 +{
 +    enum netdev_flags flags;
 +    struct ofport *ofport;
 +    struct netdev *netdev;
 +    bool carrier;
 +    int error;
 +
 +    error = netdev_open(odp_port->devname, NETDEV_ETH_TYPE_NONE, &netdev);
 +    if (error) {
 +        VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s "
 +                     "cannot be opened (%s)",
 +                     odp_port->devname, odp_port->port,
 +                     odp_port->devname, strerror(error));
 +        return NULL;
 +    }
 +
 +    ofport = xmalloc(sizeof *ofport);
 +    ofport->netdev = netdev;
 +    ofport->opp.port_no = odp_port_to_ofp_port(odp_port->port);
 +    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';
 +
 +    netdev_get_flags(netdev, &flags);
 +    ofport->opp.config = flags & NETDEV_UP ? 0 : OFPPC_PORT_DOWN;
 +
 +    netdev_get_carrier(netdev, &carrier);
 +    ofport->opp.state = carrier ? 0 : OFPPS_LINK_DOWN;
 +
 +    netdev_get_features(netdev,
 +                        &ofport->opp.curr, &ofport->opp.advertised,
 +                        &ofport->opp.supported, &ofport->opp.peer);
 +    return ofport;
 +}
 +
 +static bool
 +ofport_conflicts(const struct ofproto *p, const struct odp_port *odp_port)
 +{
 +    if (port_array_get(&p->ports, odp_port->port)) {
 +        VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
 +                     odp_port->port);
 +        return true;
 +    } else if (shash_find(&p->port_by_name, odp_port->devname)) {
 +        VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
 +                     odp_port->devname);
 +        return true;
 +    } else {
 +        return false;
 +    }
 +}
 +
 +static int
 +ofport_equal(const struct ofport *a_, const struct ofport *b_)
 +{
 +    const struct ofp_phy_port *a = &a_->opp;
 +    const struct ofp_phy_port *b = &b_->opp;
 +
 +    BUILD_ASSERT_DECL(sizeof *a == 48); /* Detect ofp_phy_port changes. */
 +    return (a->port_no == b->port_no
 +            && !memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr)
 +            && !strcmp((char *) a->name, (char *) b->name)
 +            && a->state == b->state
 +            && a->config == b->config
 +            && a->curr == b->curr
 +            && a->advertised == b->advertised
 +            && a->supported == b->supported
 +            && a->peer == b->peer);
 +}
 +
 +static void
 +send_port_status(struct ofproto *p, const struct ofport *ofport,
 +                 uint8_t reason)
 +{
 +    /* XXX Should limit the number of queued port status change messages. */
 +    struct ofconn *ofconn;
 +    LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
 +        struct ofp_port_status *ops;
 +        struct ofpbuf *b;
 +
 +        ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b);
 +        ops->reason = reason;
 +        ops->desc = ofport->opp;
 +        hton_ofp_phy_port(&ops->desc);
 +        queue_tx(b, ofconn, NULL);
 +    }
 +    if (p->ofhooks->port_changed_cb) {
 +        p->ofhooks->port_changed_cb(reason, &ofport->opp, p->aux);
 +    }
 +}
 +
 +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));
 +}
 +
 +static void
 +ofport_free(struct ofport *ofport)
 +{
 +    if (ofport) {
 +        netdev_close(ofport->netdev);
 +        free(ofport);
 +    }
 +}
 +
 +static void
 +update_port(struct ofproto *p, const char *devname)
 +{
 +    struct odp_port odp_port;
 +    struct ofport *old_ofport;
 +    struct ofport *new_ofport;
 +    int error;
 +
 +    COVERAGE_INC(ofproto_update_port);
 +
 +    /* Query the datapath for port information. */
 +    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);
 +    if (!error) {
 +        if (!old_ofport) {
 +            /* There's no port named 'devname' but there might be a port with
 +             * the same port number.  This could happen if a port is deleted
 +             * and then a new one added in its place very quickly, or if a port
 +             * is renamed.  In the former case we want to send an OFPPR_DELETE
 +             * and an OFPPR_ADD, and in the latter case we want to send a
 +             * single OFPPR_MODIFY.  We can distinguish the cases by comparing
 +             * the old port's ifindex against the new port, or perhaps less
 +             * reliably but more portably by comparing the old port's MAC
 +             * against the new port's MAC.  However, this code isn't that smart
 +             * and always sends an OFPPR_MODIFY (XXX). */
 +            old_ofport = port_array_get(&p->ports, odp_port.port);
 +        }
 +    } else if (error != ENOENT && error != ENODEV) {
 +        VLOG_WARN_RL(&rl, "dpif_port_query_by_name returned unexpected error "
 +                     "%s", strerror(error));
 +        return;
 +    }
 +
 +    /* Create a new ofport. */
 +    new_ofport = !error ? make_ofport(&odp_port) : NULL;
 +
 +    /* Eliminate a few pathological cases. */
 +    if (!old_ofport && !new_ofport) {
 +        return;
 +    } else if (old_ofport && new_ofport) {
 +        /* Most of the 'config' bits are OpenFlow soft state, but
 +         * OFPPC_PORT_DOWN is maintained the kernel.  So transfer the OpenFlow
 +         * bits from old_ofport.  (make_ofport() only sets OFPPC_PORT_DOWN and
 +         * leaves the other bits 0.)  */
 +        new_ofport->opp.config |= old_ofport->opp.config & ~OFPPC_PORT_DOWN;
 +
 +        if (ofport_equal(old_ofport, new_ofport)) {
 +            /* False alarm--no change. */
 +            ofport_free(new_ofport);
 +            return;
 +        }
 +    }
 +
 +    /* Now deal with the normal cases. */
 +    if (old_ofport) {
 +        ofport_remove(p, old_ofport);
 +    }
 +    if (new_ofport) {
 +        ofport_install(p, new_ofport);
 +    }
 +    send_port_status(p, new_ofport ? new_ofport : old_ofport,
 +                     (!old_ofport ? OFPPR_ADD
 +                      : !new_ofport ? OFPPR_DELETE
 +                      : OFPPR_MODIFY));
 +    ofport_free(old_ofport);
 +
 +    /* Update port groups. */
 +    refresh_port_groups(p);
 +}
 +
 +static int
 +init_ports(struct ofproto *p)
 +{
 +    struct odp_port *ports;
 +    size_t n_ports;
 +    size_t i;
 +    int error;
 +
 +    error = dpif_port_list(p->dpif, &ports, &n_ports);
 +    if (error) {
 +        return error;
 +    }
 +
 +    for (i = 0; i < n_ports; i++) {
 +        const struct odp_port *odp_port = &ports[i];
 +        if (!ofport_conflicts(p, odp_port)) {
 +            struct ofport *ofport = make_ofport(odp_port);
 +            if (ofport) {
 +                ofport_install(p, ofport);
 +            }
 +        }
 +    }
 +    free(ports);
 +    refresh_port_groups(p);
 +    return 0;
 +}
 +\f
 +static struct ofconn *
 +ofconn_create(struct ofproto *p, struct rconn *rconn)
 +{
 +    struct ofconn *ofconn = xmalloc(sizeof *ofconn);
 +    list_push_back(&p->all_conns, &ofconn->node);
 +    ofconn->rconn = rconn;
 +    ofconn->pktbuf = NULL;
 +    ofconn->send_flow_exp = false;
 +    ofconn->miss_send_len = 0;
 +    ofconn->packet_in_counter = rconn_packet_counter_create ();
 +    ofconn->reply_counter = rconn_packet_counter_create ();
 +    return ofconn;
 +}
 +
 +static void
 +ofconn_destroy(struct ofconn *ofconn, struct ofproto *p)
 +{
 +    if (p->executer) {
 +        executer_rconn_closing(p->executer, ofconn->rconn);
 +    }
 +
 +    list_remove(&ofconn->node);
 +    rconn_destroy(ofconn->rconn);
 +    rconn_packet_counter_destroy(ofconn->packet_in_counter);
 +    rconn_packet_counter_destroy(ofconn->reply_counter);
 +    pktbuf_destroy(ofconn->pktbuf);
 +    free(ofconn);
 +}
 +
 +static void
 +ofconn_run(struct ofconn *ofconn, struct ofproto *p)
 +{
 +    int iteration;
 +
 +    rconn_run(ofconn->rconn);
 +
 +    if (rconn_packet_counter_read (ofconn->reply_counter) < OFCONN_REPLY_MAX) {
 +        /* Limit the number of iterations to prevent other tasks from
 +         * starving. */
 +        for (iteration = 0; iteration < 50; iteration++) {
 +            struct ofpbuf *of_msg = rconn_recv(ofconn->rconn);
 +            if (!of_msg) {
 +                break;
 +            }
 +            if (p->fail_open) {
 +                fail_open_maybe_recover(p->fail_open);
 +            }
 +            handle_openflow(ofconn, p, of_msg);
 +            ofpbuf_delete(of_msg);
 +        }
 +    }
 +
 +    if (ofconn != p->controller && !rconn_is_alive(ofconn->rconn)) {
 +        ofconn_destroy(ofconn, p);
 +    }
 +}
 +
 +static void
 +ofconn_wait(struct ofconn *ofconn)
 +{
 +    rconn_run_wait(ofconn->rconn);
 +    if (rconn_packet_counter_read (ofconn->reply_counter) < OFCONN_REPLY_MAX) {
 +        rconn_recv_wait(ofconn->rconn);
 +    } else {
 +        COVERAGE_INC(ofproto_ofconn_stuck);
 +    }
 +}
 +\f
 +/* Caller is responsible for initializing the 'cr' member of the returned
 + * rule. */
 +static struct rule *
 +rule_create(struct rule *super,
 +            const union ofp_action *actions, size_t n_actions,
 +            uint16_t idle_timeout, uint16_t hard_timeout)
 +{
 +    struct rule *rule = xcalloc(1, sizeof *rule);
 +    rule->idle_timeout = idle_timeout;
 +    rule->hard_timeout = hard_timeout;
 +    rule->used = rule->created = time_msec();
 +    rule->super = super;
 +    if (super) {
 +        list_push_back(&super->list, &rule->list);
 +    } else {
 +        list_init(&rule->list);
 +    }
 +    rule->n_actions = n_actions;
 +    rule->actions = xmemdup(actions, n_actions * sizeof *actions);
 +    return rule;
 +}
 +
 +static struct rule *
 +rule_from_cls_rule(const struct cls_rule *cls_rule)
 +{
 +    return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL;
 +}
 +
 +static void
 +rule_free(struct rule *rule)
 +{
 +    free(rule->actions);
 +    free(rule->odp_actions);
 +    free(rule);
 +}
 +
 +/* Destroys 'rule'.  If 'rule' is a subrule, also removes it from its
 + * super-rule's list of subrules.  If 'rule' is a super-rule, also iterates
 + * through all of its subrules and revalidates them, destroying any that no
 + * longer has a super-rule (which is probably all of them).
 + *
 + * Before calling this function, the caller must make have removed 'rule' from
 + * the classifier.  If 'rule' is an exact-match rule, the caller is also
 + * responsible for ensuring that it has been uninstalled from the datapath. */
 +static void
 +rule_destroy(struct ofproto *ofproto, struct rule *rule)
 +{
 +    if (!rule->super) {
 +        struct rule *subrule, *next;
 +        LIST_FOR_EACH_SAFE (subrule, next, struct rule, list, &rule->list) {
 +            revalidate_rule(ofproto, subrule);
 +        }
 +    } else {
 +        list_remove(&rule->list);
 +    }
 +    rule_free(rule);
 +}
 +
 +static bool
 +rule_has_out_port(const struct rule *rule, uint16_t out_port)
 +{
 +    const union ofp_action *oa;
 +    struct actions_iterator i;
 +
 +    if (out_port == htons(OFPP_NONE)) {
 +        return true;
 +    }
 +    for (oa = actions_first(&i, rule->actions, rule->n_actions); oa;
 +         oa = actions_next(&i)) {
 +        if (oa->type == htons(OFPAT_OUTPUT) && oa->output.port == out_port) {
 +            return true;
 +        }
 +    }
 +    return false;
 +}
 +
 +/* Executes the actions indicated by 'rule' on 'packet', which is in flow
 + * 'flow' and is considered to have arrived on ODP port 'in_port'.
 + *
 + * The flow that 'packet' actually contains does not need to actually match
 + * 'rule'; the actions in 'rule' will be applied to it either way.  Likewise,
 + * the packet and byte counters for 'rule' will be credited for the packet sent
 + * out whether or not the packet actually matches 'rule'.
 + *
 + * If 'rule' is an exact-match rule and 'flow' actually equals the rule's flow,
 + * the caller must already have accurately composed ODP actions for it given
 + * 'packet' using rule_make_actions().  If 'rule' is a wildcard rule, or if
 + * 'rule' is an exact-match rule but 'flow' is not the rule's flow, then this
 + * function will compose a set of ODP actions based on 'rule''s OpenFlow
 + * actions and apply them to 'packet'. */
 +static void
 +rule_execute(struct ofproto *ofproto, struct rule *rule,
 +             struct ofpbuf *packet, const flow_t *flow)
 +{
 +    const union odp_action *actions;
 +    size_t n_actions;
 +    struct odp_actions a;
 +
 +    /* Grab or compose the ODP actions.
 +     *
 +     * The special case for an exact-match 'rule' where 'flow' is not the
 +     * rule's flow is important to avoid, e.g., sending a packet out its input
 +     * port simply because the ODP actions were composed for the wrong
 +     * scenario. */
 +    if (rule->cr.wc.wildcards || !flow_equal(flow, &rule->cr.flow)) {
 +        struct rule *super = rule->super ? rule->super : rule;
 +        if (xlate_actions(super->actions, super->n_actions, flow, ofproto,
 +                          packet, &a, NULL, 0)) {
 +            return;
 +        }
 +        actions = a.actions;
 +        n_actions = a.n_actions;
 +    } else {
 +        actions = rule->odp_actions;
 +        n_actions = rule->n_odp_actions;
 +    }
 +
 +    /* Execute the ODP actions. */
 +    if (!dpif_execute(ofproto->dpif, flow->in_port,
 +                      actions, n_actions, packet)) {
 +        struct odp_flow_stats stats;
 +        flow_extract_stats(flow, packet, &stats);
 +        update_stats(rule, &stats);
 +        rule->used = time_msec();
 +    }
 +}
 +
 +static void
 +rule_insert(struct ofproto *p, struct rule *rule, struct ofpbuf *packet,
 +            uint16_t in_port)
 +{
 +    struct rule *displaced_rule;
 +
 +    /* Insert the rule in the classifier. */
 +    displaced_rule = rule_from_cls_rule(classifier_insert(&p->cls, &rule->cr));
 +    if (!rule->cr.wc.wildcards) {
 +        rule_make_actions(p, rule, packet);
 +    }
 +
 +    /* Send the packet and credit it to the rule. */
 +    if (packet) {
 +        flow_t flow;
 +        flow_extract(packet, in_port, &flow);
 +        rule_execute(p, rule, packet, &flow);
 +    }
 +
 +    /* Install the rule in the datapath only after sending the packet, to
 +     * avoid packet reordering.  */
 +    if (rule->cr.wc.wildcards) {
 +        COVERAGE_INC(ofproto_add_wc_flow);
 +        p->need_revalidate = true;
 +    } else {
 +        rule_install(p, rule, displaced_rule);
 +    }
 +
 +    /* Free the rule that was displaced, if any. */
 +    if (displaced_rule) {
 +        rule_destroy(p, displaced_rule);
 +    }
 +}
 +
 +static struct rule *
 +rule_create_subrule(struct ofproto *ofproto, struct rule *rule,
 +                    const flow_t *flow)
 +{
 +    struct rule *subrule = rule_create(rule, NULL, 0,
 +                                       rule->idle_timeout, rule->hard_timeout);
 +    COVERAGE_INC(ofproto_subrule_create);
 +    cls_rule_from_flow(&subrule->cr, flow, 0,
 +                       (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
 +                        : rule->cr.priority));
 +    classifier_insert_exact(&ofproto->cls, &subrule->cr);
 +
 +    return subrule;
 +}
 +
 +static void
 +rule_remove(struct ofproto *ofproto, struct rule *rule)
 +{
 +    if (rule->cr.wc.wildcards) {
 +        COVERAGE_INC(ofproto_del_wc_flow);
 +        ofproto->need_revalidate = true;
 +    } else {
 +        rule_uninstall(ofproto, rule);
 +    }
 +    classifier_remove(&ofproto->cls, &rule->cr);
 +    rule_destroy(ofproto, rule);
 +}
 +
 +/* Returns true if the actions changed, false otherwise. */
 +static bool
 +rule_make_actions(struct ofproto *p, struct rule *rule,
 +                  const struct ofpbuf *packet)
 +{
 +    const struct rule *super;
 +    struct odp_actions a;
 +    size_t actions_len;
 +
 +    assert(!rule->cr.wc.wildcards);
 +
 +    super = rule->super ? rule->super : rule;
 +    rule->tags = 0;
 +    xlate_actions(super->actions, super->n_actions, &rule->cr.flow, p,
 +                  packet, &a, &rule->tags, &rule->may_install);
 +
 +    actions_len = a.n_actions * sizeof *a.actions;
 +    if (rule->n_odp_actions != a.n_actions
 +        || memcmp(rule->odp_actions, a.actions, actions_len)) {
 +        COVERAGE_INC(ofproto_odp_unchanged);
 +        free(rule->odp_actions);
 +        rule->n_odp_actions = a.n_actions;
 +        rule->odp_actions = xmemdup(a.actions, actions_len);
 +        return true;
 +    } else {
 +        return false;
 +    }
 +}
 +
 +static int
 +do_put_flow(struct ofproto *ofproto, struct rule *rule, int flags,
 +            struct odp_flow_put *put)
 +{
 +    memset(&put->flow.stats, 0, sizeof put->flow.stats);
 +    put->flow.key = rule->cr.flow;
 +    put->flow.actions = rule->odp_actions;
 +    put->flow.n_actions = rule->n_odp_actions;
 +    put->flags = flags;
 +    return dpif_flow_put(ofproto->dpif, put);
 +}
 +
 +static void
 +rule_install(struct ofproto *p, struct rule *rule, struct rule *displaced_rule)
 +{
 +    assert(!rule->cr.wc.wildcards);
 +
 +    if (rule->may_install) {
 +        struct odp_flow_put put;
 +        if (!do_put_flow(p, rule,
 +                         ODPPF_CREATE | ODPPF_MODIFY | ODPPF_ZERO_STATS,
 +                         &put)) {
 +            rule->installed = true;
 +            if (displaced_rule) {
 +                update_stats(rule, &put.flow.stats);
 +                rule_post_uninstall(p, displaced_rule);
 +            }
 +        }
 +    } else if (displaced_rule) {
 +        rule_uninstall(p, displaced_rule);
 +    }
 +}
 +
 +static void
 +rule_reinstall(struct ofproto *ofproto, struct rule *rule)
 +{
 +    if (rule->installed) {
 +        struct odp_flow_put put;
 +        COVERAGE_INC(ofproto_dp_missed);
 +        do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY, &put);
 +    } else {
 +        rule_install(ofproto, rule, NULL);
 +    }
 +}
 +
 +static void
 +rule_update_actions(struct ofproto *ofproto, struct rule *rule)
 +{
 +    bool 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);
 +            }
 +        } else {
 +            rule_install(ofproto, rule, NULL);
 +        }
 +    } else {
 +        rule_uninstall(ofproto, rule);
 +    }
 +}
 +
 +static void
 +rule_account(struct ofproto *ofproto, struct rule *rule, uint64_t extra_bytes)
 +{
 +    uint64_t total_bytes = rule->byte_count + extra_bytes;
 +
 +    if (ofproto->ofhooks->account_flow_cb
 +        && total_bytes > rule->accounted_bytes)
 +    {
 +        ofproto->ofhooks->account_flow_cb(
 +            &rule->cr.flow, rule->odp_actions, rule->n_odp_actions,
 +            total_bytes - rule->accounted_bytes, ofproto->aux);
 +        rule->accounted_bytes = total_bytes;
 +    }
 +}
 +
 +static void
 +rule_uninstall(struct ofproto *p, struct rule *rule)
 +{
 +    assert(!rule->cr.wc.wildcards);
 +    if (rule->installed) {
 +        struct odp_flow odp_flow;
 +
 +        odp_flow.key = rule->cr.flow;
 +        odp_flow.actions = NULL;
 +        odp_flow.n_actions = 0;
 +        if (!dpif_flow_del(p->dpif, &odp_flow)) {
 +            update_stats(rule, &odp_flow.stats);
 +        }
 +        rule->installed = false;
 +
 +        rule_post_uninstall(p, rule);
 +    }
 +}
 +
 +static void
 +rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
 +{
 +    struct rule *super = rule->super;
 +
 +    rule_account(ofproto, rule, 0);
 +    if (ofproto->netflow && rule->byte_count) {
 +        struct ofexpired expired;
 +        expired.flow = rule->cr.flow;
 +        expired.packet_count = rule->packet_count;
 +        expired.byte_count = rule->byte_count;
 +        expired.used = rule->used;
 +        expired.created = rule->created;
 +        expired.tcp_flags = rule->tcp_flags;
 +        expired.ip_tos = rule->ip_tos;
 +        netflow_expire(ofproto->netflow, &expired);
 +    }
 +    if (super) {
 +        super->packet_count += rule->packet_count;
 +        super->byte_count += rule->byte_count;
 +        super->tcp_flags |= rule->tcp_flags;
 +        if (rule->packet_count) {
 +            super->ip_tos = rule->ip_tos;
 +        }
 +    }
 +
 +    /* Reset counters to prevent double counting if the rule ever gets
 +     * reinstalled. */
 +    rule->packet_count = 0;
 +    rule->byte_count = 0;
 +    rule->accounted_bytes = 0;
 +    rule->tcp_flags = 0;
 +    rule->ip_tos = 0;
 +}
 +\f
 +static void
 +queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
 +         struct rconn_packet_counter *counter)
 +{
 +    update_openflow_length(msg);
 +    if (rconn_send(ofconn->rconn, msg, counter)) {
 +        ofpbuf_delete(msg);
 +    }
 +}
 +
 +static void
 +send_error(const struct ofconn *ofconn, const struct ofp_header *oh,
 +           int error, const void *data, size_t len)
 +{
 +    struct ofpbuf *buf;
 +    struct ofp_error_msg *oem;
 +
 +    if (!(error >> 16)) {
 +        VLOG_WARN_RL(&rl, "not sending bad error code %d to controller",
 +                     error);
 +        return;
 +    }
 +
 +    COVERAGE_INC(ofproto_error);
 +    oem = make_openflow_xid(len + sizeof *oem, OFPT_ERROR,
 +                            oh ? oh->xid : 0, &buf);
 +    oem->type = htons((unsigned int) error >> 16);
 +    oem->code = htons(error & 0xffff);
 +    memcpy(oem->data, data, len);
 +    queue_tx(buf, ofconn, ofconn->reply_counter);
 +}
 +
 +static void
 +send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
 +              int error)
 +{
 +    size_t oh_length = ntohs(oh->length);
 +    send_error(ofconn, oh, error, oh, MIN(oh_length, 64));
 +}
 +
 +static void
 +hton_ofp_phy_port(struct ofp_phy_port *opp)
 +{
 +    opp->port_no = htons(opp->port_no);
 +    opp->config = htonl(opp->config);
 +    opp->state = htonl(opp->state);
 +    opp->curr = htonl(opp->curr);
 +    opp->advertised = htonl(opp->advertised);
 +    opp->supported = htonl(opp->supported);
 +    opp->peer = htonl(opp->peer);
 +}
 +
 +static int
 +handle_echo_request(struct ofconn *ofconn, struct ofp_header *oh)
 +{
 +    struct ofp_header *rq = oh;
 +    queue_tx(make_echo_reply(rq), ofconn, ofconn->reply_counter);
 +    return 0;
 +}
 +
 +static int
 +handle_features_request(struct ofproto *p, struct ofconn *ofconn,
 +                        struct ofp_header *oh)
 +{
 +    struct ofp_switch_features *osf;
 +    struct ofpbuf *buf;
 +    unsigned int port_no;
 +    struct ofport *port;
 +
 +    osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf);
 +    osf->datapath_id = htonll(p->datapath_id);
 +    osf->n_buffers = htonl(pktbuf_capacity());
 +    osf->n_tables = 2;
 +    osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
 +                              OFPC_PORT_STATS | OFPC_MULTI_PHY_TX);
 +    osf->actions = htonl((1u << OFPAT_OUTPUT) |
 +                         (1u << OFPAT_SET_VLAN_VID) |
 +                         (1u << OFPAT_SET_VLAN_PCP) |
 +                         (1u << OFPAT_STRIP_VLAN) |
 +                         (1u << OFPAT_SET_DL_SRC) |
 +                         (1u << OFPAT_SET_DL_DST) |
 +                         (1u << OFPAT_SET_NW_SRC) |
 +                         (1u << OFPAT_SET_NW_DST) |
 +                         (1u << OFPAT_SET_TP_SRC) |
 +                         (1u << OFPAT_SET_TP_DST));
 +
 +    PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
 +        hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp));
 +    }
 +
 +    queue_tx(buf, ofconn, ofconn->reply_counter);
 +    return 0;
 +}
 +
 +static int
 +handle_get_config_request(struct ofproto *p, struct ofconn *ofconn,
 +                          struct ofp_header *oh)
 +{
 +    struct ofpbuf *buf;
 +    struct ofp_switch_config *osc;
 +    uint16_t flags;
 +    bool drop_frags;
 +
 +    /* Figure out flags. */
 +    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;
 +    }
 +
 +    /* Send reply. */
 +    osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
 +    osc->flags = htons(flags);
 +    osc->miss_send_len = htons(ofconn->miss_send_len);
 +    queue_tx(buf, ofconn, ofconn->reply_counter);
 +
 +    return 0;
 +}
 +
 +static int
 +handle_set_config(struct ofproto *p, struct ofconn *ofconn,
 +                  struct ofp_switch_config *osc)
 +{
 +    uint16_t flags;
 +    int error;
 +
 +    error = check_ofp_message(&osc->header, OFPT_SET_CONFIG, sizeof *osc);
 +    if (error) {
 +        return error;
 +    }
 +    flags = ntohs(osc->flags);
 +
 +    ofconn->send_flow_exp = (flags & OFPC_SEND_FLOW_EXP) != 0;
 +
 +    if (ofconn == p->controller) {
 +        switch (flags & OFPC_FRAG_MASK) {
 +        case OFPC_FRAG_NORMAL:
 +            dpif_set_drop_frags(p->dpif, false);
 +            break;
 +        case OFPC_FRAG_DROP:
 +            dpif_set_drop_frags(p->dpif, true);
 +            break;
 +        default:
 +            VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
 +                         osc->flags);
 +            break;
 +        }
 +    }
 +
 +    if ((ntohs(osc->miss_send_len) != 0) != (ofconn->miss_send_len != 0)) {
 +        if (ntohs(osc->miss_send_len) != 0) {
 +            ofconn->pktbuf = pktbuf_create();
 +        } else {
 +            pktbuf_destroy(ofconn->pktbuf);
 +        }
 +    }
 +
 +    ofconn->miss_send_len = ntohs(osc->miss_send_len);
 +
 +    return 0;
 +}
 +
 +static void
 +add_output_group_action(struct odp_actions *actions, uint16_t group)
 +{
 +    odp_actions_add(actions, ODPAT_OUTPUT_GROUP)->output_group.group = group;
 +}
 +
 +static void
 +add_controller_action(struct odp_actions *actions,
 +                      const struct ofp_action_output *oao)
 +{
 +    union odp_action *a = odp_actions_add(actions, ODPAT_CONTROLLER);
 +    a->controller.arg = oao->max_len ? ntohs(oao->max_len) : UINT32_MAX;
 +}
 +
 +struct action_xlate_ctx {
 +    /* Input. */
 +    const flow_t *flow;         /* Flow to which these actions correspond. */
 +    int recurse;                /* Recursion level, via xlate_table_action. */
 +    struct ofproto *ofproto;
 +    const struct ofpbuf *packet; /* The packet corresponding to 'flow', or a
 +                                  * null pointer if we are revalidating
 +                                  * without a packet to refer to. */
 +
 +    /* Output. */
 +    struct odp_actions *out;    /* Datapath actions. */
 +    tag_type *tags;             /* Tags associated with OFPP_NORMAL actions. */
 +    bool may_setup_flow;        /* True ordinarily; false if the actions must
 +                                 * be reassessed for every packet. */
 +};
 +
 +static void do_xlate_actions(const union ofp_action *in, size_t n_in,
 +                             struct action_xlate_ctx *ctx);
 +
 +static void
 +add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
 +{
 +    const struct ofport *ofport = port_array_get(&ctx->ofproto->ports, 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 *
 +lookup_valid_rule(struct ofproto *ofproto, const flow_t *flow)
 +{
 +    struct rule *rule;
 +    rule = rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow));
 +
 +    /* The rule we found might not be valid, since we could be in need of
 +     * revalidation.  If it is not valid, don't return it. */
 +    if (rule
 +        && rule->super
 +        && ofproto->need_revalidate
 +        && !revalidate_rule(ofproto, rule)) {
 +        COVERAGE_INC(ofproto_invalidated);
 +        return NULL;
 +    }
 +
 +    return rule;
 +}
 +
 +static void
 +xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
 +{
 +    if (!ctx->recurse) {
 +        struct rule *rule;
 +        flow_t flow;
 +
 +        flow = *ctx->flow;
 +        flow.in_port = in_port;
 +
 +        rule = lookup_valid_rule(ctx->ofproto, &flow);
 +        if (rule) {
 +            if (rule->super) {
 +                rule = rule->super;
 +            }
 +
 +            ctx->recurse++;
 +            do_xlate_actions(rule->actions, rule->n_actions, ctx);
 +            ctx->recurse--;
 +        }
 +    }
 +}
 +
 +static void
 +xlate_output_action(struct action_xlate_ctx *ctx,
 +                    const struct ofp_action_output *oao)
 +{
 +    uint16_t odp_port;
 +
 +    switch (ntohs(oao->port)) {
 +    case OFPP_IN_PORT:
 +        add_output_action(ctx, ctx->flow->in_port);
 +        break;
 +    case OFPP_TABLE:
 +        xlate_table_action(ctx, ctx->flow->in_port);
 +        break;
 +    case OFPP_NORMAL:
 +        if (!ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->packet,
 +                                              ctx->out, ctx->tags,
 +                                              ctx->ofproto->aux)) {
 +            COVERAGE_INC(ofproto_uninstallable);
 +            ctx->may_setup_flow = false;
 +        }
 +        break;
 +    case OFPP_FLOOD:
 +        add_output_group_action(ctx->out, DP_GROUP_FLOOD);
 +        break;
 +    case OFPP_ALL:
 +        add_output_group_action(ctx->out, DP_GROUP_ALL);
 +        break;
 +    case OFPP_CONTROLLER:
 +        add_controller_action(ctx->out, oao);
 +        break;
 +    case OFPP_LOCAL:
 +        add_output_action(ctx, ODPP_LOCAL);
 +        break;
 +    default:
 +        odp_port = ofp_port_to_odp_port(ntohs(oao->port));
 +        if (odp_port != ctx->flow->in_port) {
 +            add_output_action(ctx, odp_port);
 +        }
 +        break;
 +    }
 +}
 +
 +static void
 +xlate_nicira_action(struct action_xlate_ctx *ctx,
 +                    const struct nx_action_header *nah)
 +{
 +    const struct nx_action_resubmit *nar;
 +    int subtype = ntohs(nah->subtype);
 +
 +    assert(nah->vendor == htonl(NX_VENDOR_ID));
 +    switch (subtype) {
 +    case NXAST_RESUBMIT:
 +        nar = (const struct nx_action_resubmit *) nah;
 +        xlate_table_action(ctx, ofp_port_to_odp_port(ntohs(nar->in_port)));
 +        break;
 +
 +    default:
 +        VLOG_DBG_RL(&rl, "unknown Nicira action type %"PRIu16, subtype);
 +        break;
 +    }
 +}
 +
 +static void
 +do_xlate_actions(const union ofp_action *in, size_t n_in,
 +                 struct action_xlate_ctx *ctx)
 +{
 +    struct actions_iterator iter;
 +    const union ofp_action *ia;
 +    const struct ofport *port;
 +
 +    port = port_array_get(&ctx->ofproto->ports, ctx->flow->in_port);
 +    if (port && port->opp.config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
 +        port->opp.config & (eth_addr_equals(ctx->flow->dl_dst, stp_eth_addr)
 +                            ? OFPPC_NO_RECV_STP : OFPPC_NO_RECV)) {
 +        /* Drop this flow. */
 +        return;
 +    }
 +
 +    for (ia = actions_first(&iter, in, n_in); ia; ia = actions_next(&iter)) {
 +        uint16_t type = ntohs(ia->type);
 +        union odp_action *oa;
 +
 +        switch (type) {
 +        case OFPAT_OUTPUT:
 +            xlate_output_action(ctx, &ia->output);
 +            break;
 +
 +        case OFPAT_SET_VLAN_VID:
 +            oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_VID);
 +            oa->vlan_vid.vlan_vid = ia->vlan_vid.vlan_vid;
 +            break;
 +
 +        case OFPAT_SET_VLAN_PCP:
 +            oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_PCP);
 +            oa->vlan_pcp.vlan_pcp = ia->vlan_pcp.vlan_pcp;
 +            break;
 +
 +        case OFPAT_STRIP_VLAN:
 +            odp_actions_add(ctx->out, ODPAT_STRIP_VLAN);
 +            break;
 +
 +        case OFPAT_SET_DL_SRC:
 +            oa = odp_actions_add(ctx->out, ODPAT_SET_DL_SRC);
 +            memcpy(oa->dl_addr.dl_addr,
 +                   ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
 +            break;
 +
 +        case OFPAT_SET_DL_DST:
 +            oa = odp_actions_add(ctx->out, ODPAT_SET_DL_DST);
 +            memcpy(oa->dl_addr.dl_addr,
 +                   ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
 +            break;
 +
 +        case OFPAT_SET_NW_SRC:
 +            oa = odp_actions_add(ctx->out, ODPAT_SET_NW_SRC);
 +            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_VENDOR:
 +            xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
 +            break;
 +
 +        default:
 +            VLOG_DBG_RL(&rl, "unknown action type %"PRIu16, type);
 +            break;
 +        }
 +    }
 +}
 +
 +static int
 +xlate_actions(const union ofp_action *in, size_t n_in,
 +              const flow_t *flow, struct ofproto *ofproto,
 +              const struct ofpbuf *packet,
 +              struct odp_actions *out, tag_type *tags, bool *may_setup_flow)
 +{
 +    tag_type no_tags = 0;
 +    struct action_xlate_ctx ctx;
 +    COVERAGE_INC(ofproto_ofp2odp);
 +    odp_actions_init(out);
 +    ctx.flow = flow;
 +    ctx.recurse = 0;
 +    ctx.ofproto = ofproto;
 +    ctx.packet = packet;
 +    ctx.out = out;
 +    ctx.tags = tags ? tags : &no_tags;
 +    ctx.may_setup_flow = true;
 +    do_xlate_actions(in, n_in, &ctx);
 +
 +    /* Check with in-band control to see if we're allowed to setup this
 +     * flow. */
 +    if (!in_band_rule_check(ofproto->in_band, flow, out)) {
 +        ctx.may_setup_flow = false;
 +    }
 +
 +    if (may_setup_flow) {
 +        *may_setup_flow = ctx.may_setup_flow;
 +    }
 +    if (odp_actions_overflow(out)) {
 +        odp_actions_init(out);
 +        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_TOO_MANY);
 +    }
 +    return 0;
 +}
 +
 +static int
 +handle_packet_out(struct ofproto *p, struct ofconn *ofconn,
 +                  struct ofp_header *oh)
 +{
 +    struct ofp_packet_out *opo;
 +    struct ofpbuf payload, *buffer;
 +    struct odp_actions actions;
 +    int n_actions;
 +    uint16_t in_port;
 +    flow_t flow;
 +    int error;
 +
 +    error = check_ofp_packet_out(oh, &payload, &n_actions, p->max_ports);
 +    if (error) {
 +        return error;
 +    }
 +    opo = (struct ofp_packet_out *) oh;
 +
 +    COVERAGE_INC(ofproto_packet_out);
 +    if (opo->buffer_id != htonl(UINT32_MAX)) {
 +        error = pktbuf_retrieve(ofconn->pktbuf, ntohl(opo->buffer_id),
 +                                &buffer, &in_port);
 +        if (error || !buffer) {
 +            return error;
 +        }
 +        payload = *buffer;
 +    } else {
 +        buffer = NULL;
 +    }
 +
 +    flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
 +    error = xlate_actions((const union ofp_action *) opo->actions, n_actions,
 +                          &flow, p, &payload, &actions, NULL, NULL);
 +    if (error) {
 +        return error;
 +    }
 +
 +    dpif_execute(p->dpif, flow.in_port, actions.actions, actions.n_actions,
 +                 &payload);
 +    ofpbuf_delete(buffer);
 +
 +    return 0;
 +}
 +
 +static void
 +update_port_config(struct ofproto *p, struct ofport *port,
 +                   uint32_t config, uint32_t mask)
 +{
 +    mask &= config ^ port->opp.config;
 +    if (mask & OFPPC_PORT_DOWN) {
 +        if (config & OFPPC_PORT_DOWN) {
 +            netdev_turn_flags_off(port->netdev, NETDEV_UP, true);
 +        } else {
 +            netdev_turn_flags_on(port->netdev, NETDEV_UP, true);
 +        }
 +    }
 +#define REVALIDATE_BITS (OFPPC_NO_RECV | OFPPC_NO_RECV_STP | OFPPC_NO_FWD)
 +    if (mask & REVALIDATE_BITS) {
 +        COVERAGE_INC(ofproto_costly_flags);
 +        port->opp.config ^= mask & REVALIDATE_BITS;
 +        p->need_revalidate = true;
 +    }
 +#undef REVALIDATE_BITS
 +    if (mask & OFPPC_NO_FLOOD) {
 +        port->opp.config ^= OFPPC_NO_FLOOD;
 +        refresh_port_group(p, DP_GROUP_FLOOD);
 +    }
 +    if (mask & OFPPC_NO_PACKET_IN) {
 +        port->opp.config ^= OFPPC_NO_PACKET_IN;
 +    }
 +}
 +
 +static int
 +handle_port_mod(struct ofproto *p, struct ofp_header *oh)
 +{
 +    const struct ofp_port_mod *opm;
 +    struct ofport *port;
 +    int error;
 +
 +    error = check_ofp_message(oh, OFPT_PORT_MOD, sizeof *opm);
 +    if (error) {
 +        return error;
 +    }
 +    opm = (struct ofp_port_mod *) oh;
 +
 +    port = port_array_get(&p->ports,
 +                          ofp_port_to_odp_port(ntohs(opm->port_no)));
 +    if (!port) {
 +        return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT);
 +    } else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) {
 +        return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR);
 +    } else {
 +        update_port_config(p, port, ntohl(opm->config), ntohl(opm->mask));
 +        if (opm->advertise) {
 +            netdev_set_advertisements(port->netdev, ntohl(opm->advertise));
 +        }
 +    }
 +    return 0;
 +}
 +
 +static struct ofpbuf *
 +make_stats_reply(uint32_t xid, uint16_t type, size_t body_len)
 +{
 +    struct ofp_stats_reply *osr;
 +    struct ofpbuf *msg;
 +
 +    msg = ofpbuf_new(MIN(sizeof *osr + body_len, UINT16_MAX));
 +    osr = put_openflow_xid(sizeof *osr, OFPT_STATS_REPLY, xid, msg);
 +    osr->type = type;
 +    osr->flags = htons(0);
 +    return msg;
 +}
 +
 +static struct ofpbuf *
 +start_stats_reply(const struct ofp_stats_request *request, size_t body_len)
 +{
 +    return make_stats_reply(request->header.xid, request->type, body_len);
 +}
 +
 +static void *
 +append_stats_reply(size_t nbytes, struct ofconn *ofconn, struct ofpbuf **msgp)
 +{
 +    struct ofpbuf *msg = *msgp;
 +    assert(nbytes <= UINT16_MAX - sizeof(struct ofp_stats_reply));
 +    if (nbytes + msg->size > UINT16_MAX) {
 +        struct ofp_stats_reply *reply = msg->data;
 +        reply->flags = htons(OFPSF_REPLY_MORE);
 +        *msgp = make_stats_reply(reply->header.xid, reply->type, nbytes);
 +        queue_tx(msg, ofconn, ofconn->reply_counter);
 +    }
 +    return ofpbuf_put_uninit(*msgp, nbytes);
 +}
 +
 +static int
 +handle_desc_stats_request(struct ofproto *p, struct ofconn *ofconn,
 +                           struct ofp_stats_request *request)
 +{
 +    struct ofp_desc_stats *ods;
 +    struct ofpbuf *msg;
 +
 +    msg = start_stats_reply(request, sizeof *ods);
 +    ods = append_stats_reply(sizeof *ods, ofconn, &msg);
 +    strncpy(ods->mfr_desc, p->manufacturer, sizeof ods->mfr_desc);
 +    strncpy(ods->hw_desc, p->hardware, sizeof ods->hw_desc);
 +    strncpy(ods->sw_desc, p->software, sizeof ods->sw_desc);
 +    strncpy(ods->serial_num, p->serial, sizeof ods->serial_num);
 +    queue_tx(msg, ofconn, ofconn->reply_counter);
 +
 +    return 0;
 +}
 +
 +static void
 +count_subrules(struct cls_rule *cls_rule, void *n_subrules_)
 +{
 +    struct rule *rule = rule_from_cls_rule(cls_rule);
 +    int *n_subrules = n_subrules_;
 +
 +    if (rule->super) {
 +        (*n_subrules)++;
 +    }
 +}
 +
 +static int
 +handle_table_stats_request(struct ofproto *p, struct ofconn *ofconn,
 +                           struct ofp_stats_request *request)
 +{
 +    struct ofp_table_stats *ots;
 +    struct ofpbuf *msg;
 +    struct odp_stats dpstats;
 +    int n_exact, n_subrules, n_wild;
 +
 +    msg = start_stats_reply(request, sizeof *ots * 2);
 +
 +    /* Count rules of various kinds. */
 +    n_subrules = 0;
 +    classifier_for_each(&p->cls, CLS_INC_EXACT, count_subrules, &n_subrules);
 +    n_exact = classifier_count_exact(&p->cls) - n_subrules;
 +    n_wild = classifier_count(&p->cls) - classifier_count_exact(&p->cls);
 +
 +    /* Hash table. */
 +    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;
 +    strcpy(ots->name, "hash");
 +    ots->wildcards = htonl(0);
 +    ots->max_entries = htonl(dpstats.max_capacity);
 +    ots->active_count = htonl(n_exact);
 +    ots->lookup_count = htonll(dpstats.n_frags + dpstats.n_hit +
 +                               dpstats.n_missed);
 +    ots->matched_count = htonll(dpstats.n_hit); /* XXX */
 +
 +    /* Classifier table. */
 +    ots = append_stats_reply(sizeof *ots, ofconn, &msg);
 +    memset(ots, 0, sizeof *ots);
 +    ots->table_id = TABLEID_CLASSIFIER;
 +    strcpy(ots->name, "classifier");
 +    ots->wildcards = htonl(OFPFW_ALL);
 +    ots->max_entries = htonl(65536);
 +    ots->active_count = htonl(n_wild);
 +    ots->lookup_count = htonll(0);              /* XXX */
 +    ots->matched_count = htonll(0);             /* XXX */
 +
 +    queue_tx(msg, ofconn, ofconn->reply_counter);
 +    return 0;
 +}
 +
 +static int
 +handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn,
 +                          struct ofp_stats_request *request)
 +{
 +    struct ofp_port_stats *ops;
 +    struct ofpbuf *msg;
 +    struct ofport *port;
 +    unsigned int port_no;
 +
 +    msg = start_stats_reply(request, sizeof *ops * 16);
 +    PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
 +        struct netdev_stats stats;
 +
 +        /* Intentionally ignore return value, since errors will set 'stats' to
 +         * all-1s, which is correct for OpenFlow, and netdev_get_stats() will
 +         * log errors. */
 +        netdev_get_stats(port->netdev, &stats);
 +
 +        ops = append_stats_reply(sizeof *ops, ofconn, &msg);
 +        ops->port_no = htons(odp_port_to_ofp_port(port_no));
 +        memset(ops->pad, 0, sizeof ops->pad);
 +        ops->rx_packets = htonll(stats.rx_packets);
 +        ops->tx_packets = htonll(stats.tx_packets);
 +        ops->rx_bytes = htonll(stats.rx_bytes);
 +        ops->tx_bytes = htonll(stats.tx_bytes);
 +        ops->rx_dropped = htonll(stats.rx_dropped);
 +        ops->tx_dropped = htonll(stats.tx_dropped);
 +        ops->rx_errors = htonll(stats.rx_errors);
 +        ops->tx_errors = htonll(stats.tx_errors);
 +        ops->rx_frame_err = htonll(stats.rx_frame_errors);
 +        ops->rx_over_err = htonll(stats.rx_over_errors);
 +        ops->rx_crc_err = htonll(stats.rx_crc_errors);
 +        ops->collisions = htonll(stats.collisions);
 +    }
 +
 +    queue_tx(msg, ofconn, ofconn->reply_counter);
 +    return 0;
 +}
 +
 +struct flow_stats_cbdata {
 +    struct ofproto *ofproto;
 +    struct ofconn *ofconn;
 +    uint16_t out_port;
 +    struct ofpbuf *msg;
 +};
 +
 +static void
 +query_stats(struct ofproto *p, struct rule *rule,
 +            uint64_t *packet_countp, uint64_t *byte_countp)
 +{
 +    uint64_t packet_count, byte_count;
 +    struct rule *subrule;
 +    struct odp_flow *odp_flows;
 +    size_t n_odp_flows;
 +
 +    n_odp_flows = rule->cr.wc.wildcards ? list_size(&rule->list) : 1;
 +    odp_flows = xcalloc(1, n_odp_flows * sizeof *odp_flows);
 +    if (rule->cr.wc.wildcards) {
 +        size_t i = 0;
 +        LIST_FOR_EACH (subrule, struct rule, list, &rule->list) {
 +            odp_flows[i++].key = subrule->cr.flow;
 +        }
 +    } else {
 +        odp_flows[0].key = rule->cr.flow;
 +    }
 +
 +    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];
 +            packet_count += odp_flow->stats.n_packets;
 +            byte_count += odp_flow->stats.n_bytes;
 +        }
 +    }
 +    free(odp_flows);
 +
 +    *packet_countp = packet_count;
 +    *byte_countp = byte_count;
 +}
 +
 +static void
 +flow_stats_cb(struct cls_rule *rule_, void *cbdata_)
 +{
 +    struct rule *rule = rule_from_cls_rule(rule_);
 +    struct flow_stats_cbdata *cbdata = cbdata_;
 +    struct ofp_flow_stats *ofs;
 +    uint64_t packet_count, byte_count;
 +    size_t act_len, len;
 +
 +    if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) {
 +        return;
 +    }
 +
 +    act_len = sizeof *rule->actions * rule->n_actions;
 +    len = offsetof(struct ofp_flow_stats, actions) + act_len;
 +
 +    query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
 +
 +    ofs = append_stats_reply(len, cbdata->ofconn, &cbdata->msg);
 +    ofs->length = htons(len);
 +    ofs->table_id = rule->cr.wc.wildcards ? TABLEID_CLASSIFIER : TABLEID_HASH;
 +    ofs->pad = 0;
 +    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofs->match);
 +    ofs->duration = htonl((time_msec() - rule->created) / 1000);
 +    ofs->priority = htons(rule->cr.priority);
 +    ofs->idle_timeout = htons(rule->idle_timeout);
 +    ofs->hard_timeout = htons(rule->hard_timeout);
 +    memset(ofs->pad2, 0, sizeof ofs->pad2);
 +    ofs->packet_count = htonll(packet_count);
 +    ofs->byte_count = htonll(byte_count);
 +    memcpy(ofs->actions, rule->actions, act_len);
 +}
 +
 +static int
 +table_id_to_include(uint8_t table_id)
 +{
 +    return (table_id == TABLEID_HASH ? CLS_INC_EXACT
 +            : table_id == TABLEID_CLASSIFIER ? CLS_INC_WILD
 +            : table_id == 0xff ? CLS_INC_ALL
 +            : 0);
 +}
 +
 +static int
 +handle_flow_stats_request(struct ofproto *p, struct ofconn *ofconn,
 +                          const struct ofp_stats_request *osr,
 +                          size_t arg_size)
 +{
 +    struct ofp_flow_stats_request *fsr;
 +    struct flow_stats_cbdata cbdata;
 +    struct cls_rule target;
 +
 +    if (arg_size != sizeof *fsr) {
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
 +    }
 +    fsr = (struct ofp_flow_stats_request *) osr->body;
 +
 +    COVERAGE_INC(ofproto_flows_req);
 +    cbdata.ofproto = p;
 +    cbdata.ofconn = ofconn;
 +    cbdata.out_port = fsr->out_port;
 +    cbdata.msg = start_stats_reply(osr, 1024);
 +    cls_rule_from_match(&target, &fsr->match, 0);
 +    classifier_for_each_match(&p->cls, &target,
 +                              table_id_to_include(fsr->table_id),
 +                              flow_stats_cb, &cbdata);
 +    queue_tx(cbdata.msg, ofconn, ofconn->reply_counter);
 +    return 0;
 +}
 +
 +struct flow_stats_ds_cbdata {
 +    struct ofproto *ofproto;
 +    struct ds *results;
 +};
 +
 +static void
 +flow_stats_ds_cb(struct cls_rule *rule_, void *cbdata_)
 +{
 +    struct rule *rule = rule_from_cls_rule(rule_);
 +    struct flow_stats_ds_cbdata *cbdata = cbdata_;
 +    struct ds *results = cbdata->results;
 +    struct ofp_match match;
 +    uint64_t packet_count, byte_count;
 +    size_t act_len = sizeof *rule->actions * rule->n_actions;
 +
 +    /* Don't report on subrules. */
 +    if (rule->super != NULL) {
 +        return;
 +    }
 +
 +    query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
 +    flow_to_ovs_match(&rule->cr.flow, rule->cr.wc.wildcards, &match);
 +
 +    ds_put_format(results, "duration=%llds, ",
 +                  (time_msec() - rule->created) / 1000);
 +    ds_put_format(results, "priority=%u, ", rule->cr.priority);
 +    ds_put_format(results, "n_packets=%"PRIu64", ", packet_count);
 +    ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
 +    ofp_print_match(results, &match, true);
 +    ofp_print_actions(results, &rule->actions->header, act_len);
 +    ds_put_cstr(results, "\n");
 +}
 +
 +/* Adds a pretty-printed description of all flows to 'results', including 
 + * those marked hidden by secchan (e.g., by in-band control). */
 +void
 +ofproto_get_all_flows(struct ofproto *p, struct ds *results)
 +{
 +    struct ofp_match match;
 +    struct cls_rule target;
 +    struct flow_stats_ds_cbdata cbdata;
 +
 +    memset(&match, 0, sizeof match);
 +    match.wildcards = htonl(OFPFW_ALL);
 +
 +    cbdata.ofproto = p;
 +    cbdata.results = results;
 +
 +    cls_rule_from_match(&target, &match, 0);
 +    classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
 +                              flow_stats_ds_cb, &cbdata);
 +}
 +
 +struct aggregate_stats_cbdata {
 +    struct ofproto *ofproto;
 +    uint16_t out_port;
 +    uint64_t packet_count;
 +    uint64_t byte_count;
 +    uint32_t n_flows;
 +};
 +
 +static void
 +aggregate_stats_cb(struct cls_rule *rule_, void *cbdata_)
 +{
 +    struct rule *rule = rule_from_cls_rule(rule_);
 +    struct aggregate_stats_cbdata *cbdata = cbdata_;
 +    uint64_t packet_count, byte_count;
 +
 +    if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) {
 +        return;
 +    }
 +
 +    query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
 +
 +    cbdata->packet_count += packet_count;
 +    cbdata->byte_count += byte_count;
 +    cbdata->n_flows++;
 +}
 +
 +static int
 +handle_aggregate_stats_request(struct ofproto *p, struct ofconn *ofconn,
 +                               const struct ofp_stats_request *osr,
 +                               size_t arg_size)
 +{
 +    struct ofp_aggregate_stats_request *asr;
 +    struct ofp_aggregate_stats_reply *reply;
 +    struct aggregate_stats_cbdata cbdata;
 +    struct cls_rule target;
 +    struct ofpbuf *msg;
 +
 +    if (arg_size != sizeof *asr) {
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
 +    }
 +    asr = (struct ofp_aggregate_stats_request *) osr->body;
 +
 +    COVERAGE_INC(ofproto_agg_request);
 +    cbdata.ofproto = p;
 +    cbdata.out_port = asr->out_port;
 +    cbdata.packet_count = 0;
 +    cbdata.byte_count = 0;
 +    cbdata.n_flows = 0;
 +    cls_rule_from_match(&target, &asr->match, 0);
 +    classifier_for_each_match(&p->cls, &target,
 +                              table_id_to_include(asr->table_id),
 +                              aggregate_stats_cb, &cbdata);
 +
 +    msg = start_stats_reply(osr, sizeof *reply);
 +    reply = append_stats_reply(sizeof *reply, ofconn, &msg);
 +    reply->flow_count = htonl(cbdata.n_flows);
 +    reply->packet_count = htonll(cbdata.packet_count);
 +    reply->byte_count = htonll(cbdata.byte_count);
 +    queue_tx(msg, ofconn, ofconn->reply_counter);
 +    return 0;
 +}
 +
 +static int
 +handle_stats_request(struct ofproto *p, struct ofconn *ofconn,
 +                     struct ofp_header *oh)
 +{
 +    struct ofp_stats_request *osr;
 +    size_t arg_size;
 +    int error;
 +
 +    error = check_ofp_message_array(oh, OFPT_STATS_REQUEST, sizeof *osr,
 +                                    1, &arg_size);
 +    if (error) {
 +        return error;
 +    }
 +    osr = (struct ofp_stats_request *) oh;
 +
 +    switch (ntohs(osr->type)) {
 +    case OFPST_DESC:
 +        return handle_desc_stats_request(p, ofconn, osr);
 +
 +    case OFPST_FLOW:
 +        return handle_flow_stats_request(p, ofconn, osr, arg_size);
 +
 +    case OFPST_AGGREGATE:
 +        return handle_aggregate_stats_request(p, ofconn, osr, arg_size);
 +
 +    case OFPST_TABLE:
 +        return handle_table_stats_request(p, ofconn, osr);
 +
 +    case OFPST_PORT:
 +        return handle_port_stats_request(p, ofconn, osr);
 +
 +    case OFPST_VENDOR:
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
 +
 +    default:
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT);
 +    }
 +}
 +
 +static long long int
 +msec_from_nsec(uint64_t sec, uint32_t nsec)
 +{
 +    return !sec ? 0 : sec * 1000 + nsec / 1000000;
 +}
 +
 +static void
 +update_time(struct rule *rule, const struct odp_flow_stats *stats)
 +{
 +    long long int used = msec_from_nsec(stats->used_sec, stats->used_nsec);
 +    if (used > rule->used) {
 +        rule->used = used;
 +    }
 +}
 +
 +static void
 +update_stats(struct rule *rule, const struct odp_flow_stats *stats)
 +{
 +    update_time(rule, stats);
 +    rule->packet_count += stats->n_packets;
 +    rule->byte_count += stats->n_bytes;
 +    rule->tcp_flags |= stats->tcp_flags;
 +    if (stats->n_packets) {
 +        rule->ip_tos = stats->ip_tos;
 +    }
 +}
 +
 +static int
 +add_flow(struct ofproto *p, struct ofconn *ofconn,
 +         struct ofp_flow_mod *ofm, size_t n_actions)
 +{
 +    struct ofpbuf *packet;
 +    struct rule *rule;
 +    uint16_t in_port;
 +    int error;
 +
 +    rule = rule_create(NULL, (const union ofp_action *) ofm->actions,
 +                       n_actions, ntohs(ofm->idle_timeout),
 +                       ntohs(ofm->hard_timeout));
 +    cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
 +
 +    packet = NULL;
 +    error = 0;
 +    if (ofm->buffer_id != htonl(UINT32_MAX)) {
 +        error = pktbuf_retrieve(ofconn->pktbuf, ntohl(ofm->buffer_id),
 +                                &packet, &in_port);
 +    }
 +
 +    rule_insert(p, rule, packet, in_port);
 +    ofpbuf_delete(packet);
 +    return error;
 +}
 +
 +static int
 +modify_flow(struct ofproto *p, const struct ofp_flow_mod *ofm,
 +            size_t n_actions, uint16_t command, struct rule *rule)
 +{
 +    if (rule_is_hidden(rule)) {
 +        return 0;
 +    }
 +
 +    if (command == OFPFC_DELETE) {
 +        rule_remove(p, rule);
 +    } else {
 +        size_t actions_len = n_actions * sizeof *rule->actions;
 +
 +        if (n_actions == rule->n_actions
 +            && !memcmp(ofm->actions, rule->actions, actions_len))
 +        {
 +            return 0;
 +        }
 +
 +        free(rule->actions);
 +        rule->actions = xmemdup(ofm->actions, actions_len);
 +        rule->n_actions = n_actions;
 +
 +        if (rule->cr.wc.wildcards) {
 +            COVERAGE_INC(ofproto_mod_wc_flow);
 +            p->need_revalidate = true;
 +        } else {
 +            rule_update_actions(p, rule);
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static int
 +modify_flows_strict(struct ofproto *p, const struct ofp_flow_mod *ofm,
 +                    size_t n_actions, uint16_t command)
 +{
 +    struct rule *rule;
 +    uint32_t wildcards;
 +    flow_t flow;
 +
 +    flow_from_match(&flow, &wildcards, &ofm->match);
 +    rule = rule_from_cls_rule(classifier_find_rule_exactly(
 +                                  &p->cls, &flow, wildcards,
 +                                  ntohs(ofm->priority)));
 +
 +    if (rule) {
 +        if (command == OFPFC_DELETE
 +            && ofm->out_port != htons(OFPP_NONE)
 +            && !rule_has_out_port(rule, ofm->out_port)) {
 +            return 0;
 +        }
 +
 +        modify_flow(p, ofm, n_actions, command, rule);
 +    }
 +    return 0;
 +}
 +
 +struct modify_flows_cbdata {
 +    struct ofproto *ofproto;
 +    const struct ofp_flow_mod *ofm;
 +    uint16_t out_port;
 +    size_t n_actions;
 +    uint16_t command;
 +};
 +
 +static void
 +modify_flows_cb(struct cls_rule *rule_, void *cbdata_)
 +{
 +    struct rule *rule = rule_from_cls_rule(rule_);
 +    struct modify_flows_cbdata *cbdata = cbdata_;
 +
 +    if (cbdata->out_port != htons(OFPP_NONE)
 +        && !rule_has_out_port(rule, cbdata->out_port)) {
 +        return;
 +    }
 +
 +    modify_flow(cbdata->ofproto, cbdata->ofm, cbdata->n_actions,
 +                cbdata->command, rule);
 +}
 +
 +static int
 +modify_flows_loose(struct ofproto *p, const struct ofp_flow_mod *ofm,
 +                   size_t n_actions, uint16_t command)
 +{
 +    struct modify_flows_cbdata cbdata;
 +    struct cls_rule target;
 +
 +    cbdata.ofproto = p;
 +    cbdata.ofm = ofm;
 +    cbdata.out_port = (command == OFPFC_DELETE ? ofm->out_port
 +                       : htons(OFPP_NONE));
 +    cbdata.n_actions = n_actions;
 +    cbdata.command = command;
 +
 +    cls_rule_from_match(&target, &ofm->match, 0);
 +
 +    classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
 +                              modify_flows_cb, &cbdata);
 +    return 0;
 +}
 +
 +static int
 +handle_flow_mod(struct ofproto *p, struct ofconn *ofconn,
 +                struct ofp_flow_mod *ofm)
 +{
 +    size_t n_actions;
 +    int error;
 +
 +    error = check_ofp_message_array(&ofm->header, OFPT_FLOW_MOD, sizeof *ofm,
 +                                    sizeof *ofm->actions, &n_actions);
 +    if (error) {
 +        return error;
 +    }
 +
 +    normalize_match(&ofm->match);
 +    if (!ofm->match.wildcards) {
 +        ofm->priority = htons(UINT16_MAX);
 +    }
 +
 +    error = validate_actions((const union ofp_action *) ofm->actions,
 +                             n_actions, p->max_ports);
 +    if (error) {
 +        return error;
 +    }
 +
 +    switch (ntohs(ofm->command)) {
 +    case OFPFC_ADD:
 +        return add_flow(p, ofconn, ofm, n_actions);
 +
 +    case OFPFC_MODIFY:
 +        return modify_flows_loose(p, ofm, n_actions, OFPFC_MODIFY);
 +
 +    case OFPFC_MODIFY_STRICT:
 +        return modify_flows_strict(p, ofm, n_actions, OFPFC_MODIFY);
 +
 +    case OFPFC_DELETE:
 +        return modify_flows_loose(p, ofm, n_actions, OFPFC_DELETE);
 +
 +    case OFPFC_DELETE_STRICT:
 +        return modify_flows_strict(p, ofm, n_actions, OFPFC_DELETE);
 +
 +    default:
 +        return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND);
 +    }
 +}
 +
 +static void
 +send_capability_reply(struct ofproto *p, struct ofconn *ofconn, uint32_t xid)
 +{
 +    struct ofmp_capability_reply *ocr;
 +    struct ofpbuf *b;
 +    char capabilities[] = "com.nicira.mgmt.manager=false\n";
 +
 +    ocr = make_openflow_xid(sizeof(*ocr), OFPT_VENDOR, xid, &b);
 +    ocr->header.header.vendor = htonl(NX_VENDOR_ID);
 +    ocr->header.header.subtype = htonl(NXT_MGMT);
 +    ocr->header.type = htons(OFMPT_CAPABILITY_REPLY);
 +
 +    ocr->format = htonl(OFMPCOF_SIMPLE);
 +    ocr->mgmt_id = htonll(p->mgmt_id);
 +
 +    ofpbuf_put(b, capabilities, strlen(capabilities));
 +
 +    queue_tx(b, ofconn, ofconn->reply_counter);
 +}
 +
 +static int
 +handle_ofmp(struct ofproto *p, struct ofconn *ofconn, 
 +            struct ofmp_header *ofmph)
 +{
 +    size_t msg_len = ntohs(ofmph->header.header.length);
 +    if (msg_len < sizeof(*ofmph)) {
 +        VLOG_WARN_RL(&rl, "dropping short managment message: %d\n", msg_len);
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
 +    }
 +
 +    if (ofmph->type == htons(OFMPT_CAPABILITY_REQUEST)) {
 +        struct ofmp_capability_request *ofmpcr;
 +
 +        if (msg_len < sizeof(struct ofmp_capability_request)) {
 +            VLOG_WARN_RL(&rl, "dropping short capability request: %d\n", 
 +                    msg_len);
 +            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
 +        }
 +
 +        ofmpcr = (struct ofmp_capability_request *)ofmph;
 +        if (ofmpcr->format != htonl(OFMPCAF_SIMPLE)) {
 +            /* xxx Find a better type than bad subtype */
 +            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
 +        }
 +
 +        send_capability_reply(p, ofconn, ofmph->header.header.xid);
 +        return 0;
 +    } else {
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
 +    }
 +}
 +
 +static int
 +handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
 +{
 +    struct ofp_vendor_header *ovh = msg;
 +    struct nicira_header *nh;
 +
 +    if (ntohs(ovh->header.length) < sizeof(struct ofp_vendor_header)) {
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
 +    }
 +    if (ovh->vendor != htonl(NX_VENDOR_ID)) {
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
 +    }
 +    if (ntohs(ovh->header.length) < sizeof(struct nicira_header)) {
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
 +    }
 +
 +    nh = msg;
 +    switch (ntohl(nh->subtype)) {
 +    case NXT_STATUS_REQUEST:
 +        return switch_status_handle_request(p->switch_status, ofconn->rconn,
 +                                            msg);
 +
 +    case NXT_ACT_SET_CONFIG:
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); /* XXX */
 +
 +    case NXT_ACT_GET_CONFIG:
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); /* XXX */
 +
 +    case NXT_COMMAND_REQUEST:
 +        if (p->executer) {
 +            return executer_handle_request(p->executer, ofconn->rconn, msg);
 +        }
 +        break;
 +
 +    case NXT_MGMT:
 +        return handle_ofmp(p, ofconn, msg);
 +    }
 +
 +    return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
 +}
 +
 +static void
 +handle_openflow(struct ofconn *ofconn, struct ofproto *p,
 +                struct ofpbuf *ofp_msg)
 +{
 +    struct ofp_header *oh = ofp_msg->data;
 +    int error;
 +
 +    COVERAGE_INC(ofproto_recv_openflow);
 +    switch (oh->type) {
 +    case OFPT_ECHO_REQUEST:
 +        error = handle_echo_request(ofconn, oh);
 +        break;
 +
 +    case OFPT_ECHO_REPLY:
 +        error = 0;
 +        break;
 +
 +    case OFPT_FEATURES_REQUEST:
 +        error = handle_features_request(p, ofconn, oh);
 +        break;
 +
 +    case OFPT_GET_CONFIG_REQUEST:
 +        error = handle_get_config_request(p, ofconn, oh);
 +        break;
 +
 +    case OFPT_SET_CONFIG:
 +        error = handle_set_config(p, ofconn, ofp_msg->data);
 +        break;
 +
 +    case OFPT_PACKET_OUT:
 +        error = handle_packet_out(p, ofconn, ofp_msg->data);
 +        break;
 +
 +    case OFPT_PORT_MOD:
 +        error = handle_port_mod(p, oh);
 +        break;
 +
 +    case OFPT_FLOW_MOD:
 +        error = handle_flow_mod(p, ofconn, ofp_msg->data);
 +        break;
 +
 +    case OFPT_STATS_REQUEST:
 +        error = handle_stats_request(p, ofconn, oh);
 +        break;
 +
 +    case OFPT_VENDOR:
 +        error = handle_vendor(p, ofconn, ofp_msg->data);
 +        break;
 +
 +    default:
 +        if (VLOG_IS_WARN_ENABLED()) {
 +            char *s = ofp_to_string(oh, ntohs(oh->length), 2);
 +            VLOG_DBG_RL(&rl, "OpenFlow message ignored: %s", s);
 +            free(s);
 +        }
 +        error = ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE);
 +        break;
 +    }
 +
 +    if (error) {
 +        send_error_oh(ofconn, ofp_msg->data, error);
 +    }
 +}
 +\f
 +static void
 +handle_odp_msg(struct ofproto *p, struct ofpbuf *packet)
 +{
 +    struct odp_msg *msg = packet->data;
 +    uint16_t in_port = odp_port_to_ofp_port(msg->port);
 +    struct rule *rule;
 +    struct ofpbuf payload;
 +    flow_t flow;
 +
 +    /* Handle controller actions. */
 +    if (msg->type == _ODPL_ACTION_NR) {
 +        COVERAGE_INC(ofproto_ctlr_action);
 +        pinsched_send(p->action_sched, in_port, packet,
 +                      send_packet_in_action, p);
 +        return;
 +    }
 +
 +    payload.data = msg + 1;
 +    payload.size = msg->length - sizeof *msg;
 +    flow_extract(&payload, msg->port, &flow);
 +
 +    /* Check with in-band control to see if this packet should be sent
 +     * to the local port regardless of the flow table. */
 +    if (in_band_msg_in_hook(p->in_band, &flow, &payload)) {
 +        union odp_action action;
 +
 +        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);
 +    }
 +
 +    rule = lookup_valid_rule(p, &flow);
 +    if (!rule) {
 +        /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
 +        struct ofport *port = port_array_get(&p->ports, msg->port);
 +        if (port) {
 +            if (port->opp.config & OFPPC_NO_PACKET_IN) {
 +                COVERAGE_INC(ofproto_no_packet_in);
 +                /* XXX install 'drop' flow entry */
 +                ofpbuf_delete(packet);
 +                return;
 +            }
 +        } else {
 +            VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, msg->port);
 +        }
 +
 +        COVERAGE_INC(ofproto_packet_in);
 +        pinsched_send(p->miss_sched, in_port, packet, send_packet_in_miss, p);
 +        return;
 +    }
 +
 +    if (rule->cr.wc.wildcards) {
 +        rule = rule_create_subrule(p, rule, &flow);
 +        rule_make_actions(p, rule, packet);
 +    } else {
 +        if (!rule->may_install) {
 +            /* The rule is not installable, that is, we need to process every
 +             * packet, so process the current packet and set its actions into
 +             * 'subrule'. */
 +            rule_make_actions(p, rule, packet);
 +        } else {
 +            /* XXX revalidate rule if it needs it */
 +        }
 +    }
 +
 +    rule_execute(p, rule, &payload, &flow);
 +    rule_reinstall(p, rule);
 +
 +    if (rule->super && rule->super->cr.priority == FAIL_OPEN_PRIORITY
 +        && rconn_is_connected(p->controller->rconn)) {
 +        /*
 +         * Extra-special case for fail-open mode.
 +         *
 +         * We are in fail-open mode and the packet matched the fail-open rule,
 +         * but we are connected to a controller too.  We should send the packet
 +         * up to the controller in the hope that it will try to set up a flow
 +         * and thereby allow us to exit fail-open.
 +         *
 +         * See the top-level comment in fail-open.c for more information.
 +         */
 +        pinsched_send(p->miss_sched, in_port, packet, send_packet_in_miss, p);
 +    } else {
 +        ofpbuf_delete(packet);
 +    }
 +}
 +\f
 +static void
 +revalidate_cb(struct cls_rule *sub_, void *cbdata_)
 +{
 +    struct rule *sub = rule_from_cls_rule(sub_);
 +    struct revalidate_cbdata *cbdata = cbdata_;
 +
 +    if (cbdata->revalidate_all
 +        || (cbdata->revalidate_subrules && sub->super)
 +        || (tag_set_intersects(&cbdata->revalidate_set, sub->tags))) {
 +        revalidate_rule(cbdata->ofproto, sub);
 +    }
 +}
 +
 +static bool
 +revalidate_rule(struct ofproto *p, struct rule *rule)
 +{
 +    const flow_t *flow = &rule->cr.flow;
 +
 +    COVERAGE_INC(ofproto_revalidate_rule);
 +    if (rule->super) {
 +        struct rule *super;
 +        super = rule_from_cls_rule(classifier_lookup_wild(&p->cls, flow));
 +        if (!super) {
 +            rule_remove(p, rule);
 +            return false;
 +        } else if (super != rule->super) {
 +            COVERAGE_INC(ofproto_revalidate_moved);
 +            list_remove(&rule->list);
 +            list_push_back(&super->list, &rule->list);
 +            rule->super = super;
 +            rule->hard_timeout = super->hard_timeout;
 +            rule->idle_timeout = super->idle_timeout;
 +            rule->created = super->created;
 +            rule->used = 0;
 +        }
 +    }
 +
 +    rule_update_actions(p, rule);
 +    return true;
 +}
 +
 +static struct ofpbuf *
 +compose_flow_exp(const struct rule *rule, long long int now, uint8_t reason)
 +{
 +    struct ofp_flow_expired *ofe;
 +    struct ofpbuf *buf;
 +
 +    ofe = make_openflow(sizeof *ofe, OFPT_FLOW_EXPIRED, &buf);
 +    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofe->match);
 +    ofe->priority = htons(rule->cr.priority);
 +    ofe->reason = reason;
 +    ofe->duration = (now - rule->created) / 1000;
 +    ofe->packet_count = rule->packet_count;
 +    ofe->byte_count = rule->byte_count;
 +
 +    return buf;
 +}
 +
 +static void
 +send_flow_exp(struct ofproto *p, struct rule *rule,
 +              long long int now, uint8_t reason)
 +{
 +    struct ofconn *ofconn;
 +    struct ofconn *prev;
 +    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
 +     * OpenFlow requests from being processed also prevents new flows from
 +     * being added (and expiring).  (It also prevents processing OpenFlow
 +     * requests that would not add new flows, so it is imperfect.) */
 +
 +    prev = NULL;
 +    LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
 +        if (ofconn->send_flow_exp && rconn_is_connected(ofconn->rconn)) {
 +            if (prev) {
 +                queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter);
 +            } else {
 +                buf = compose_flow_exp(rule, now, reason);
 +            }
 +            prev = ofconn;
 +        }
 +    }
 +    if (prev) {
 +        queue_tx(buf, prev, prev->reply_counter);
 +    }
 +}
 +
 +static void
 +uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule)
 +{
 +    assert(rule->installed);
 +    assert(!rule->cr.wc.wildcards);
 +
 +    if (rule->super) {
 +        rule_remove(ofproto, rule);
 +    } else {
 +        rule_uninstall(ofproto, rule);
 +    }
 +}
 +
 +static void
 +expire_rule(struct cls_rule *cls_rule, void *p_)
 +{
 +    struct ofproto *p = p_;
 +    struct rule *rule = rule_from_cls_rule(cls_rule);
 +    long long int hard_expire, idle_expire, expire, now;
 +
 +    hard_expire = (rule->hard_timeout
 +                   ? rule->created + rule->hard_timeout * 1000
 +                   : LLONG_MAX);
 +    idle_expire = (rule->idle_timeout
 +                   && (rule->super || list_is_empty(&rule->list))
 +                   ? rule->used + rule->idle_timeout * 1000
 +                   : LLONG_MAX);
 +    expire = MIN(hard_expire, idle_expire);
 +    if (expire == LLONG_MAX) {
 +        if (rule->installed && time_msec() >= rule->used + 5000) {
 +            uninstall_idle_flow(p, rule);
 +        }
 +        return;
 +    }
 +
 +    now = time_msec();
 +    if (now < expire) {
 +        if (rule->installed && now >= rule->used + 5000) {
 +            uninstall_idle_flow(p, rule);
 +        }
 +        return;
 +    }
 +
 +    COVERAGE_INC(ofproto_expired);
 +    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);
 +        }
 +    }
 +
 +    send_flow_exp(p, rule, now,
 +                  (now >= hard_expire
 +                   ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
 +    rule_remove(p, rule);
 +}
 +
 +static void
 +update_used(struct ofproto *p)
 +{
 +    struct odp_flow *flows;
 +    size_t n_flows;
 +    size_t i;
 +    int error;
 +
 +    error = dpif_flow_list_all(p->dpif, &flows, &n_flows);
 +    if (error) {
 +        return;
 +    }
 +
 +    for (i = 0; i < n_flows; i++) {
 +        struct odp_flow *f = &flows[i];
 +        struct rule *rule;
 +
 +        rule = rule_from_cls_rule(
 +            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);
 +            continue;
 +        }
 +
 +        update_time(rule, &f->stats);
 +        rule_account(p, rule, f->stats.n_bytes);
 +    }
 +    free(flows);
 +}
 +
 +static void
 +do_send_packet_in(struct ofconn *ofconn, uint32_t buffer_id,
 +                  const struct ofpbuf *packet, int send_len)
 +{
 +    struct odp_msg *msg = packet->data;
 +    struct ofpbuf payload;
 +    struct ofpbuf *opi;
 +    uint8_t reason;
 +
 +    /* Extract packet payload from 'msg'. */
 +    payload.data = msg + 1;
 +    payload.size = msg->length - sizeof *msg;
 +
 +    /* Construct ofp_packet_in message. */
 +    reason = msg->type == _ODPL_ACTION_NR ? OFPR_ACTION : OFPR_NO_MATCH;
 +    opi = make_packet_in(buffer_id, odp_port_to_ofp_port(msg->port), reason,
 +                         &payload, send_len);
 +
 +    /* Send. */
 +    rconn_send_with_limit(ofconn->rconn, opi, ofconn->packet_in_counter, 100);
 +}
 +
 +static void
 +send_packet_in_action(struct ofpbuf *packet, void *p_)
 +{
 +    struct ofproto *p = p_;
 +    struct ofconn *ofconn;
 +    struct odp_msg *msg;
 +
 +    msg = packet->data;
 +    LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
 +        if (ofconn == p->controller || ofconn->miss_send_len) {
 +            do_send_packet_in(ofconn, UINT32_MAX, packet, msg->arg);
 +        }
 +    }
 +    ofpbuf_delete(packet);
 +}
 +
 +static void
 +send_packet_in_miss(struct ofpbuf *packet, void *p_)
 +{
 +    struct ofproto *p = p_;
 +    bool in_fail_open = p->fail_open && fail_open_is_active(p->fail_open);
 +    struct ofconn *ofconn;
 +    struct ofpbuf payload;
 +    struct odp_msg *msg;
 +
 +    msg = packet->data;
 +    payload.data = msg + 1;
 +    payload.size = msg->length - sizeof *msg;
 +    LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
 +        if (ofconn->miss_send_len) {
 +            struct pktbuf *pb = ofconn->pktbuf;
 +            uint32_t buffer_id = (in_fail_open
 +                                  ? pktbuf_get_null()
 +                                  : pktbuf_save(pb, &payload, msg->port));
 +            int send_len = (buffer_id != UINT32_MAX ? ofconn->miss_send_len
 +                            : UINT32_MAX);
 +            do_send_packet_in(ofconn, buffer_id, packet, send_len);
 +        }
 +    }
 +    ofpbuf_delete(packet);
 +}
 +
 +static uint64_t
 +pick_datapath_id(const struct ofproto *ofproto)
 +{
 +    const struct ofport *port;
 +
 +    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)",
 +                  netdev_get_name(port->netdev), strerror(error));
 +    }
 +    return ofproto->fallback_dpid;
 +}
 +
 +static uint64_t
 +pick_fallback_dpid(void)
 +{
 +    uint8_t ea[ETH_ADDR_LEN];
 +    eth_addr_random(ea);
 +    ea[0] = 0x00;               /* Set Nicira OUI. */
 +    ea[1] = 0x23;
 +    ea[2] = 0x20;
 +    return eth_addr_to_uint64(ea);
 +}
 +\f
 +static bool
 +default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
 +                         struct odp_actions *actions, tag_type *tags,
 +                         void *ofproto_)
 +{
 +    struct ofproto *ofproto = ofproto_;
 +    int out_port;
 +
 +    /* Drop frames for reserved multicast addresses. */
 +    if (eth_addr_is_reserved(flow->dl_dst)) {
 +        return true;
 +    }
 +
 +    /* Learn source MAC (but don't try to learn from revalidation). */
 +    if (packet != NULL) {
 +        tag_type rev_tag = mac_learning_learn(ofproto->ml, flow->dl_src,
 +                                              0, flow->in_port);
 +        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, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
 +                        ETH_ADDR_ARGS(flow->dl_src), flow->in_port);
 +            ofproto_revalidate(ofproto, rev_tag);
 +        }
 +    }
 +
 +    /* Determine output port. */
 +    out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags);
 +    if (out_port < 0) {
 +        add_output_group_action(actions, DP_GROUP_FLOOD);
 +    } else if (out_port != flow->in_port) {
 +        odp_actions_add(actions, ODPAT_OUTPUT)->output.port = out_port;
 +    } else {
 +        /* Drop. */
 +    }
 +
 +    return true;
 +}
 +
 +static const struct ofhooks default_ofhooks = {
 +    NULL,
 +    default_normal_ofhook_cb,
 +    NULL,
 +    NULL
 +};
diff --combined vswitchd/bridge.c
index 933f4afa5d9604f0de8e34081781ac3b66b582c8,36be700dff1f53f6ca9d99ad39acd6899ee79acc..ef390f77e16101a78306a1be91b105a8abc37cce
  #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);
  }
index e7a1739711da8d84db41b5dd2e517f5ed4f51841,7f989b40fd8b49b9d6409aa8ed2907c497037ff8..4d4bb482ab4258ba73d46952f6f1afd320f4bcc6
  .  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
index 9e0efe0729fc9af6bd40813941058da466dd4803,f0b8be2c89b82fa3547c21c08f2b43ee11f76bcb..01450ed5d07a43c5638955dbd230bb99b5b896a5
  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"