Merge remote-tracking branch 'ovs-dev/master'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Wed, 20 Nov 2013 13:44:38 +0000 (14:44 +0100)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Wed, 20 Nov 2013 13:44:38 +0000 (14:44 +0100)
1  2 
Makefile.am
lib/dpif-netdev.c
lib/dpif.c

diff --combined Makefile.am
@@@ -22,15 -22,20 +22,20 @@@ AM_CPPFLAGS += -DNDEBU
  AM_CFLAGS += -fomit-frame-pointer
  endif
  
+ if WIN32
+ psep=";"
+ else
+ psep=":"
+ endif
  # PYTHONDONTWRITEBYTECODE=yes keeps Python from creating .pyc and .pyo
  # files.  Creating .py[co] works OK for any given version of Open
  # vSwitch, but it causes trouble if you switch from a version with
  # foo/__init__.py into an (older) version with plain foo.py, since
  # foo/__init__.pyc will cause Python to ignore foo.py.
  if INCLUDE_PYTHON_COMPAT
- run_python = PYTHONPATH=$(top_srcdir)/python:$(top_srcdir)/python/compat:$$PYTHONPATH
+ run_python = PYTHONPATH=$(top_srcdir)/python$(psep)$(top_srcdir)/python/compat$(psep)$$PYTHONPATH
  else
- run_python = PYTHONPATH=$(top_srcdir)/python:$$PYTHONPATH
+ run_python = PYTHONPATH=$(top_srcdir)/python$(psep)$$PYTHONPATH
  endif
  run_python += PYTHONDONTWRITEBYTECODE=yes $(PYTHON)
  
@@@ -111,7 -116,6 +116,7 @@@ ro_shell = printf '\043 Generated autom
  
  SUFFIXES += .in
  .in:
 +      @mkdir -p $$(dirname $@)
        $(PERL) $(srcdir)/build-aux/soexpand.pl -I$(srcdir) < $< | \
            sed \
                -e 's,[@]PKIDIR[@],$(PKIDIR),g' \
@@@ -153,7 -157,7 +158,7 @@@ dist-hook-git: distfile
          (cd datapath && $(MAKE) distfiles);                               \
          (cat distfiles; sed 's|^|datapath/|' datapath/distfiles) |        \
            sort -u > all-distfiles;                                        \
 -        (cd $(srcdir) && git ls-files) | grep -v '\.gitignore$$' |        \
 +        (cd $(srcdir) && git ls-files) | grep -vFf $(srcdir)/.non-distfiles |     \
            sort -u > all-gitfiles;                                         \
          comm -1 -3 all-distfiles all-gitfiles > missing-distfiles;        \
          if test -s missing-distfiles; then                                \
@@@ -277,6 -281,5 +282,6 @@@ include rhel/automake.m
  include xenserver/automake.mk
  include python/automake.mk
  include python/compat/automake.mk
 +include planetlab/automake.mk
  include tutorial/automake.mk
  include vtep/automake.mk
diff --combined lib/dpif-netdev.c
@@@ -31,6 -31,7 +31,7 @@@
  #include <sys/stat.h>
  #include <unistd.h>
  
+ #include "classifier.h"
  #include "csum.h"
  #include "dpif.h"
  #include "dpif-provider.h"
@@@ -59,6 -60,9 +60,9 @@@
  
  VLOG_DEFINE_THIS_MODULE(dpif_netdev);
  
+ /* By default, choose a priority in the middle. */
+ #define NETDEV_RULE_PRIORITY 0x8000
  /* Configuration parameters. */
  enum { MAX_PORTS = 256 };       /* Maximum number of ports. */
  enum { MAX_FLOWS = 65536 };     /* Maximum number of flows in flow table. */
@@@ -92,6 -96,7 +96,7 @@@ struct dp_netdev 
      int max_mtu;                /* Maximum MTU of any port added so far. */
  
      struct dp_netdev_queue queues[N_QUEUES];
+     struct classifier cls;      /* Classifier. */
      struct hmap flow_table;     /* Flow table. */
      struct seq *queue_seq;      /* Incremented whenever a packet is queued. */
  
@@@ -118,8 -123,12 +123,12 @@@ struct dp_netdev_port 
  
  /* A flow in dp_netdev's 'flow_table'. */
  struct dp_netdev_flow {
-     struct hmap_node node;      /* Element in dp_netdev's 'flow_table'. */
-     struct flow key;
+     /* Packet classification. */
+     struct cls_rule cr;         /* In owning dp_netdev's 'cls'. */
+     /* Hash table index by unmasked flow.*/
+     struct hmap_node node;      /* In owning dp_netdev's 'flow_table'. */
+     struct flow flow;           /* The flow that created this entry. */
  
      /* Statistics. */
      long long int used;         /* Last used time, in monotonic msecs. */
@@@ -159,8 -168,8 +168,8 @@@ static int dpif_netdev_open(const struc
  static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *,
                                      int queue_no, const struct flow *,
                                      const struct nlattr *userdata);
- static void dp_netdev_execute_actions(struct dp_netdev *,
-                                       struct ofpbuf *, struct flow *,
+ static void dp_netdev_execute_actions(struct dp_netdev *, const struct flow *,
+                                       struct ofpbuf *,
                                        const struct nlattr *actions,
                                        size_t actions_len);
  static void dp_netdev_port_input(struct dp_netdev *dp,
@@@ -201,17 -210,10 +210,17 @@@ dpif_netdev_class_is_dummy(const struc
      return class != &dpif_netdev_class;
  }
  
 +static bool
 +dpif_netdev_class_is_planetlab(const struct dpif_class *class)
 +{
 +    return class == &dpif_planetlab_class;
 +}
 +
  static const char *
  dpif_netdev_port_open_type(const struct dpif_class *class, const char *type)
  {
      return strcmp(type, "internal") ? type
 +                  : dpif_netdev_class_is_planetlab(class) ? "pltap"
                    : dpif_netdev_class_is_dummy(class) ? "dummy"
                    : "tap";
  }
@@@ -239,8 -241,7 +248,8 @@@ choose_port(struct dp_netdev *dp, cons
  {
      uint32_t port_no;
  
 -    if (dp->class != &dpif_netdev_class) {
 +    if (dp->class != &dpif_netdev_class && 
 +        dp->class != &dpif_planetlab_class) {
          const char *p;
          int start_no = 0;
  
@@@ -291,6 -292,7 +300,7 @@@ create_dp_netdev(const char *name, cons
          dp->queues[i].head = dp->queues[i].tail = 0;
      }
      dp->queue_seq = seq_create();
+     classifier_init(&dp->cls, NULL);
      hmap_init(&dp->flow_table);
      list_init(&dp->port_list);
      dp->port_seq = seq_create();
@@@ -357,6 -359,7 +367,7 @@@ dp_netdev_free(struct dp_netdev *dp
      }
      dp_netdev_purge_queues(dp);
      seq_destroy(dp->queue_seq);
+     classifier_destroy(&dp->cls);
      hmap_destroy(&dp->flow_table);
      seq_destroy(dp->port_seq);
      free(dp->name);
@@@ -634,6 -637,11 +645,11 @@@ dpif_netdev_get_max_ports(const struct 
  static void
  dp_netdev_free_flow(struct dp_netdev *dp, struct dp_netdev_flow *netdev_flow)
  {
+     ovs_rwlock_wrlock(&dp->cls.rwlock);
+     classifier_remove(&dp->cls, &netdev_flow->cr);
+     ovs_rwlock_unlock(&dp->cls.rwlock);
+     cls_rule_destroy(&netdev_flow->cr);
      hmap_remove(&dp->flow_table, &netdev_flow->node);
      free(netdev_flow->actions);
      free(netdev_flow);
@@@ -742,13 -750,27 +758,27 @@@ dpif_netdev_port_poll_wait(const struc
  }
  
  static struct dp_netdev_flow *
- dp_netdev_lookup_flow(const struct dp_netdev *dp, const struct flow *key)
+ dp_netdev_lookup_flow(const struct dp_netdev *dp, const struct flow *flow)
+ {
+     struct cls_rule *cr;
+     ovs_rwlock_wrlock(&dp->cls.rwlock);
+     cr = classifier_lookup(&dp->cls, flow, NULL);
+     ovs_rwlock_unlock(&dp->cls.rwlock);
+     return (cr
+             ? CONTAINER_OF(cr, struct dp_netdev_flow, cr)
+             : NULL);
+ }
+ static struct dp_netdev_flow *
+ dp_netdev_find_flow(const struct dp_netdev *dp, const struct flow *flow)
  {
      struct dp_netdev_flow *netdev_flow;
  
-     HMAP_FOR_EACH_WITH_HASH (netdev_flow, node, flow_hash(key, 0),
+     HMAP_FOR_EACH_WITH_HASH (netdev_flow, node, flow_hash(flow, 0),
                               &dp->flow_table) {
-         if (flow_equal(&netdev_flow->key, key)) {
+         if (flow_equal(&netdev_flow->flow, flow)) {
              return netdev_flow;
          }
      }
@@@ -766,23 -788,29 +796,29 @@@ get_dpif_flow_stats(struct dp_netdev_fl
  }
  
  static int
- dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
-                               struct flow *flow)
+ dpif_netdev_flow_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
+                                    const struct nlattr *mask_key,
+                                    uint32_t mask_key_len, struct flow *flow,
+                                    struct flow *mask)
  {
      odp_port_t in_port;
  
-     if (odp_flow_key_to_flow(key, key_len, flow) != ODP_FIT_PERFECT) {
+     if (odp_flow_key_to_flow(key, key_len, flow)
+         || (mask_key
+             && odp_flow_key_to_mask(mask_key, mask_key_len, mask, flow))) {
          /* This should not happen: it indicates that odp_flow_key_from_flow()
-          * and odp_flow_key_to_flow() disagree on the acceptable form of a
-          * flow.  Log the problem as an error, with enough details to enable
-          * debugging. */
+          * and odp_flow_key_to_flow() disagree on the acceptable form of a flow
+          * or odp_flow_key_from_mask() and odp_flow_key_to_mask() disagree on
+          * the acceptable form of a mask.  Log the problem as an error, with
+          * enough details to enable debugging. */
          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
  
          if (!VLOG_DROP_ERR(&rl)) {
              struct ds s;
  
              ds_init(&s);
-             odp_flow_key_format(key, key_len, &s);
+             odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s,
+                             true);
              VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s));
              ds_destroy(&s);
          }
          return EINVAL;
      }
  
+     if (mask_key) {
+         /* Force unwildcard the in_port. */
+         mask->in_port.odp_port = u32_to_odp(UINT32_MAX);
+     }
      in_port = flow->in_port.odp_port;
      if (!is_valid_port_number(in_port) && in_port != ODPP_NONE) {
          return EINVAL;
      return 0;
  }
  
+ static int
+ dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
+                               struct flow *flow)
+ {
+     return dpif_netdev_flow_mask_from_nlattrs(key, key_len, NULL, 0, flow,
+                                               NULL);
+ }
  static int
  dpif_netdev_flow_get(const struct dpif *dpif,
                       const struct nlattr *nl_key, size_t nl_key_len,
      }
  
      ovs_mutex_lock(&dp_netdev_mutex);
-     netdev_flow = dp_netdev_lookup_flow(dp, &key);
+     netdev_flow = dp_netdev_find_flow(dp, &key);
      if (netdev_flow) {
          if (stats) {
              get_dpif_flow_stats(netdev_flow, stats);
@@@ -842,23 -883,36 +891,36 @@@ set_flow_actions(struct dp_netdev_flow 
  }
  
  static int
- dp_netdev_flow_add(struct dp_netdev *dp, const struct flow *key,
-                    const struct nlattr *actions, size_t actions_len)
+ dp_netdev_flow_add(struct dp_netdev *dp, const struct flow *flow,
+                    const struct flow_wildcards *wc,
+                    const struct nlattr *actions,
+                    size_t actions_len)
  {
      struct dp_netdev_flow *netdev_flow;
+     struct match match;
      int error;
  
      netdev_flow = xzalloc(sizeof *netdev_flow);
-     netdev_flow->key = *key;
+     netdev_flow->flow = *flow;
+     match_init(&match, flow, wc);
+     cls_rule_init(&netdev_flow->cr, &match, NETDEV_RULE_PRIORITY);
+     ovs_rwlock_wrlock(&dp->cls.rwlock);
+     classifier_insert(&dp->cls, &netdev_flow->cr);
+     ovs_rwlock_unlock(&dp->cls.rwlock);
  
      error = set_flow_actions(netdev_flow, actions, actions_len);
      if (error) {
+         ovs_rwlock_wrlock(&dp->cls.rwlock);
+         classifier_remove(&dp->cls, &netdev_flow->cr);
+         ovs_rwlock_unlock(&dp->cls.rwlock);
+         cls_rule_destroy(&netdev_flow->cr);
          free(netdev_flow);
          return error;
      }
  
-     hmap_insert(&dp->flow_table, &netdev_flow->node,
-                 flow_hash(&netdev_flow->key, 0));
+     hmap_insert(&dp->flow_table, &netdev_flow->node, flow_hash(flow, 0));
      return 0;
  }
  
@@@ -876,23 -930,25 +938,25 @@@ dpif_netdev_flow_put(struct dpif *dpif
  {
      struct dp_netdev *dp = get_dp_netdev(dpif);
      struct dp_netdev_flow *netdev_flow;
-     struct flow key;
+     struct flow flow;
+     struct flow_wildcards wc;
      int error;
  
-     error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &key);
+     error = dpif_netdev_flow_mask_from_nlattrs(put->key, put->key_len,
+                 put->mask, put->mask_len, &flow, &wc.masks);
      if (error) {
          return error;
      }
  
      ovs_mutex_lock(&dp_netdev_mutex);
-     netdev_flow = dp_netdev_lookup_flow(dp, &key);
+     netdev_flow = dp_netdev_lookup_flow(dp, &flow);
      if (!netdev_flow) {
          if (put->flags & DPIF_FP_CREATE) {
              if (hmap_count(&dp->flow_table) < MAX_FLOWS) {
                  if (put->stats) {
                      memset(put->stats, 0, sizeof *put->stats);
                  }
-                 error = dp_netdev_flow_add(dp, &key, put->actions,
+                 error = dp_netdev_flow_add(dp, &flow, &wc, put->actions,
                                             put->actions_len);
              } else {
                  error = EFBIG;
              error = ENOENT;
          }
      } else {
-         if (put->flags & DPIF_FP_MODIFY) {
+         if (put->flags & DPIF_FP_MODIFY
+             && flow_equal(&flow, &netdev_flow->flow)) {
              error = set_flow_actions(netdev_flow, put->actions,
                                       put->actions_len);
              if (!error) {
                      clear_stats(netdev_flow);
                  }
              }
-         } else {
+         } else if (put->flags & DPIF_FP_CREATE) {
              error = EEXIST;
+         } else {
+             /* Overlapping flow. */
+             error = EINVAL;
          }
      }
      ovs_mutex_unlock(&dp_netdev_mutex);
@@@ -935,7 -995,7 +1003,7 @@@ dpif_netdev_flow_del(struct dpif *dpif
      }
  
      ovs_mutex_lock(&dp_netdev_mutex);
-     netdev_flow = dp_netdev_lookup_flow(dp, &key);
+     netdev_flow = dp_netdev_find_flow(dp, &key);
      if (netdev_flow) {
          if (del->stats) {
              get_dpif_flow_stats(netdev_flow, del->stats);
@@@ -954,6 -1014,7 +1022,7 @@@ struct dp_netdev_flow_state 
      uint32_t offset;
      struct nlattr *actions;
      struct odputil_keybuf keybuf;
+     struct odputil_keybuf maskbuf;
      struct dpif_flow_stats stats;
  };
  
@@@ -994,16 -1055,24 +1063,24 @@@ dpif_netdev_flow_dump_next(const struc
          struct ofpbuf buf;
  
          ofpbuf_use_stack(&buf, &state->keybuf, sizeof state->keybuf);
-         odp_flow_key_from_flow(&buf, &netdev_flow->key,
-                                netdev_flow->key.in_port.odp_port);
+         odp_flow_key_from_flow(&buf, &netdev_flow->flow,
+                                netdev_flow->flow.in_port.odp_port);
  
          *key = buf.data;
          *key_len = buf.size;
      }
  
-     if (mask) {
-         *mask = NULL;
-         *mask_len = 0;
+     if (key && mask) {
+         struct ofpbuf buf;
+         struct flow_wildcards wc;
+         ofpbuf_use_stack(&buf, &state->maskbuf, sizeof state->maskbuf);
+         minimask_expand(&netdev_flow->cr.match.mask, &wc);
+         odp_flow_key_from_mask(&buf, &wc.masks, &netdev_flow->flow,
+                                odp_to_u32(wc.masks.in_port.odp_port));
+         *mask = buf.data;
+         *mask_len = buf.size;
      }
  
      if (actions) {
@@@ -1038,8 -1107,7 +1115,7 @@@ static in
  dpif_netdev_execute(struct dpif *dpif, const struct dpif_execute *execute)
  {
      struct dp_netdev *dp = get_dp_netdev(dpif);
-     struct ofpbuf copy;
-     struct flow key;
+     struct flow md;
      int error;
  
      if (execute->packet->size < ETH_HEADER_LEN ||
          return EINVAL;
      }
  
-     /* Make a deep copy of 'packet', because we might modify its data. */
-     ofpbuf_init(&copy, DP_NETDEV_HEADROOM + execute->packet->size);
-     ofpbuf_reserve(&copy, DP_NETDEV_HEADROOM);
-     ofpbuf_put(&copy, execute->packet->data, execute->packet->size);
-     flow_extract(&copy, 0, 0, NULL, NULL, &key);
-     error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len,
-                                           &key);
+     /* Get packet metadata. */
+     error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len, &md);
      if (!error) {
+         struct ofpbuf *copy;
+         struct flow key;
+         /* Make a deep copy of 'packet', because we might modify its data. */
+         copy = ofpbuf_clone_with_headroom(execute->packet, DP_NETDEV_HEADROOM);
+         /* Extract flow key. */
+         flow_extract(copy, md.skb_priority, md.pkt_mark, &md.tunnel,
+                      &md.in_port, &key);
          ovs_mutex_lock(&dp_netdev_mutex);
-         dp_netdev_execute_actions(dp, &copy, &key,
+         dp_netdev_execute_actions(dp, &key, copy,
                                    execute->actions, execute->actions_len);
          ovs_mutex_unlock(&dp_netdev_mutex);
+         ofpbuf_delete(copy);
      }
-     ofpbuf_uninit(&copy);
      return error;
  }
  
@@@ -1154,7 -1224,7 +1232,7 @@@ dp_netdev_flow_used(struct dp_netdev_fl
      netdev_flow->used = time_msec();
      netdev_flow->packet_count++;
      netdev_flow->byte_count += packet->size;
-     netdev_flow->tcp_flags |= packet_get_tcp_flags(packet, &netdev_flow->key);
+     netdev_flow->tcp_flags |= packet_get_tcp_flags(packet, &netdev_flow->flow);
  }
  
  static void
@@@ -1174,7 -1244,7 +1252,7 @@@ dp_netdev_port_input(struct dp_netdev *
      netdev_flow = dp_netdev_lookup_flow(dp, &key);
      if (netdev_flow) {
          dp_netdev_flow_used(netdev_flow, packet);
-         dp_netdev_execute_actions(dp, packet, &key,
+         dp_netdev_execute_actions(dp, &key, packet,
                                    netdev_flow->actions,
                                    netdev_flow->actions_len);
          dp->n_hit++;
@@@ -1244,17 -1314,6 +1322,6 @@@ dpif_netdev_wait(struct dpif *dpif
      ovs_mutex_unlock(&dp_netdev_mutex);
  }
  
- static void
- dp_netdev_output_port(void *dp_, struct ofpbuf *packet,
-                       const struct flow *flow OVS_UNUSED, odp_port_t out_port)
- {
-     struct dp_netdev *dp = dp_;
-     struct dp_netdev_port *p = dp->ports[odp_to_u32(out_port)];
-     if (p) {
-         netdev_send(p->netdev, packet);
-     }
- }
  static int
  dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
                             int queue_no, const struct flow *flow,
      }
  }
  
+ struct dp_netdev_execute_aux {
+     struct dp_netdev *dp;
+     const struct flow *key;
+ };
  static void
- dp_netdev_action_userspace(void *dp, struct ofpbuf *packet,
-                            const struct flow *key,
+ dp_netdev_action_output(void *aux_, struct ofpbuf *packet,
+                         const struct flow *flow OVS_UNUSED,
+                         odp_port_t out_port)
+ {
+     struct dp_netdev_execute_aux *aux = aux_;
+     struct dp_netdev_port *p = aux->dp->ports[odp_to_u32(out_port)];
+     if (p) {
+         netdev_send(p->netdev, packet);
+     }
+ }
+ static void
+ dp_netdev_action_userspace(void *aux_, struct ofpbuf *packet,
+                            const struct flow *flow OVS_UNUSED,
                             const struct nlattr *a)
  {
+     struct dp_netdev_execute_aux *aux = aux_;
      const struct nlattr *userdata;
  
      userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
-     dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION, key, userdata);
+     dp_netdev_output_userspace(aux->dp, packet, DPIF_UC_ACTION, aux->key,
+                                userdata);
  }
  
  static void
- dp_netdev_execute_actions(struct dp_netdev *dp,
-                           struct ofpbuf *packet, struct flow *key,
-                           const struct nlattr *actions,
-                           size_t actions_len)
+ dp_netdev_execute_actions(struct dp_netdev *dp, const struct flow *key,
+                           struct ofpbuf *packet,
+                           const struct nlattr *actions, size_t actions_len)
  {
-     odp_execute_actions(dp, packet, key, actions, actions_len,
-                         dp_netdev_output_port, dp_netdev_action_userspace);
+     struct dp_netdev_execute_aux aux = {dp, key};
+     struct flow md = *key;   /* Packet metadata, may be modified by actions. */
+     odp_execute_actions(&aux, packet, &md, actions, actions_len,
+                         dp_netdev_action_output, dp_netdev_action_userspace);
  }
  
 +#define DPIF_NETDEV_CLASS_FUNCTIONS                   \
 +    dpif_netdev_enumerate,                            \
 +    dpif_netdev_port_open_type,                               \
 +    dpif_netdev_open,                                 \
 +    dpif_netdev_close,                                        \
 +    dpif_netdev_destroy,                              \
 +    dpif_netdev_run,                                  \
 +    dpif_netdev_wait,                                 \
 +    dpif_netdev_get_stats,                            \
 +    dpif_netdev_port_add,                             \
 +    dpif_netdev_port_del,                             \
 +    dpif_netdev_port_query_by_number,                 \
 +    dpif_netdev_port_query_by_name,                   \
 +    dpif_netdev_get_max_ports,                                \
 +    NULL,                       /* port_get_pid */    \
 +    dpif_netdev_port_dump_start,                      \
 +    dpif_netdev_port_dump_next,                               \
 +    dpif_netdev_port_dump_done,                               \
 +    dpif_netdev_port_poll,                            \
 +    dpif_netdev_port_poll_wait,                               \
 +    dpif_netdev_flow_get,                             \
 +    dpif_netdev_flow_put,                             \
 +    dpif_netdev_flow_del,                             \
 +    dpif_netdev_flow_flush,                           \
 +    dpif_netdev_flow_dump_start,                      \
 +    dpif_netdev_flow_dump_next,                               \
 +    dpif_netdev_flow_dump_done,                               \
 +    dpif_netdev_execute,                              \
 +    NULL,                       /* operate */         \
 +    dpif_netdev_recv_set,                             \
 +    dpif_netdev_queue_to_priority,                    \
 +    dpif_netdev_recv,                                 \
 +    dpif_netdev_recv_wait,                            \
 +    dpif_netdev_recv_purge,                           \
 +
  const struct dpif_class dpif_netdev_class = {
      "netdev",
 -    dpif_netdev_enumerate,
 -    dpif_netdev_port_open_type,
 -    dpif_netdev_open,
 -    dpif_netdev_close,
 -    dpif_netdev_destroy,
 -    dpif_netdev_run,
 -    dpif_netdev_wait,
 -    dpif_netdev_get_stats,
 -    dpif_netdev_port_add,
 -    dpif_netdev_port_del,
 -    dpif_netdev_port_query_by_number,
 -    dpif_netdev_port_query_by_name,
 -    dpif_netdev_get_max_ports,
 -    NULL,                       /* port_get_pid */
 -    dpif_netdev_port_dump_start,
 -    dpif_netdev_port_dump_next,
 -    dpif_netdev_port_dump_done,
 -    dpif_netdev_port_poll,
 -    dpif_netdev_port_poll_wait,
 -    dpif_netdev_flow_get,
 -    dpif_netdev_flow_put,
 -    dpif_netdev_flow_del,
 -    dpif_netdev_flow_flush,
 -    dpif_netdev_flow_dump_start,
 -    dpif_netdev_flow_dump_next,
 -    dpif_netdev_flow_dump_done,
 -    dpif_netdev_execute,
 -    NULL,                       /* operate */
 -    dpif_netdev_recv_set,
 -    dpif_netdev_queue_to_priority,
 -    dpif_netdev_recv,
 -    dpif_netdev_recv_wait,
 -    dpif_netdev_recv_purge,
 +    DPIF_NETDEV_CLASS_FUNCTIONS
 +};
 +
 +const struct dpif_class dpif_planetlab_class = {
 +    "planetlab",
 +    DPIF_NETDEV_CLASS_FUNCTIONS
  };
  
  static void
@@@ -1441,4 -1513,3 +1529,4 @@@ dpif_dummy_register(bool override
                               "DP PORT NEW-NUMBER",
                               3, 3, dpif_dummy_change_port_number, NULL);
  }
 +
diff --combined lib/dpif.c
@@@ -61,7 -61,6 +61,7 @@@ static const struct dpif_class *base_dp
      &dpif_linux_class,
  #endif
      &dpif_netdev_class,
 +    &dpif_planetlab_class,
  };
  
  struct registered_dpif_class {
@@@ -633,14 -632,6 +633,6 @@@ dpif_port_query_by_name(const struct dp
      return error;
  }
  
- /* Returns one greater than the maximum port number accepted in flow
-  * actions. */
- uint32_t
- dpif_get_max_ports(const struct dpif *dpif)
- {
-     return dpif->dpif_class->get_max_ports(dpif);
- }
  /* Returns the Netlink PID value to supply in OVS_ACTION_ATTR_USERSPACE actions
   * as the OVS_USERSPACE_ATTR_PID attribute's value, for use in flows whose
   * packets arrived on port 'port_no'.
@@@ -892,15 -883,19 +884,19 @@@ dpif_flow_put__(struct dpif *dpif, cons
  
  /* Adds or modifies a flow in 'dpif'.  The flow is specified by the Netlink
   * attribute OVS_FLOW_ATTR_KEY with types OVS_KEY_ATTR_* in the 'key_len' bytes
-  * starting at 'key', and OVS_FLOW_ATTR_MASK with types of OVS_KEY_ATTR_* in the
-  * 'mask_len' bytes starting at 'mask'. The associated actions are specified by
-  * the Netlink attributes with types OVS_ACTION_ATTR_* in the 'actions_len'
-  * bytes starting at 'actions'.
+  * starting at 'key', and OVS_FLOW_ATTR_MASK with types of OVS_KEY_ATTR_* in
+  * the 'mask_len' bytes starting at 'mask'. The associated actions are
+  * specified by the Netlink attributes with types OVS_ACTION_ATTR_* in the
+  * 'actions_len' bytes starting at 'actions'.
   *
   * - If the flow's key does not exist in 'dpif', then the flow will be added if
   *   'flags' includes DPIF_FP_CREATE.  Otherwise the operation will fail with
   *   ENOENT.
   *
+  *   The datapath may reject attempts to insert overlapping flows with EINVAL
+  *   or EEXIST, but clients should not rely on this: avoiding overlapping flows
+  *   is primarily the client's responsibility.
+  *
   *   If the operation succeeds, then 'stats', if nonnull, will be zeroed.
   *
   * - If the flow's key does exist in 'dpif', then the flow's actions will be