Global replace of Nicira Networks.
[sliver-openvswitch.git] / lib / dpif-netdev.c
index 1d05dd8..a6c06fe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@
 #include "poll-loop.h"
 #include "random.h"
 #include "shash.h"
+#include "sset.h"
 #include "timeval.h"
 #include "util.h"
 #include "vlog.h"
@@ -100,7 +101,7 @@ struct dp_netdev_port {
     int port_no;                /* Index into dp_netdev's 'ports'. */
     struct list node;           /* Element in dp_netdev's 'port_list'. */
     struct netdev *netdev;
-    bool internal;              /* Internal port? */
+    char *type;                 /* Port type as requested by user. */
 };
 
 /* A flow in dp_netdev's 'flow_table'. */
@@ -112,7 +113,7 @@ struct dp_netdev_flow {
     long long int used;         /* Last used time, in monotonic msecs. */
     long long int packet_count; /* Number of packets matched. */
     long long int byte_count;   /* Number of bytes matched. */
-    ovs_be16 tcp_ctl;           /* Bitwise-OR of seen tcp_ctl values. */
+    uint8_t tcp_flags;          /* Bitwise-OR of seen tcp_flags values. */
 
     /* Actions. */
     struct nlattr *actions;
@@ -123,7 +124,6 @@ struct dp_netdev_flow {
 struct dpif_netdev {
     struct dpif dpif;
     struct dp_netdev *dp;
-    int listen_mask;
     unsigned int dp_serial;
 };
 
@@ -152,8 +152,6 @@ static void dp_netdev_execute_actions(struct dp_netdev *,
                                       const struct nlattr *actions,
                                       size_t actions_len);
 
-static struct dpif_class dpif_dummy_class;
-
 static struct dpif_netdev *
 dpif_netdev_cast(const struct dpif *dpif)
 {
@@ -178,7 +176,6 @@ create_dpif_netdev(struct dp_netdev *dp)
     dpif = xmalloc(sizeof *dpif);
     dpif_init(&dpif->dpif, dp->class, dp->name, netflow_id >> 8, netflow_id);
     dpif->dp = dp;
-    dpif->listen_mask = 0;
     dpif->dp_serial = dp->serial;
 
     return &dpif->dpif;
@@ -311,28 +308,17 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
 {
     struct dp_netdev_port *port;
     struct netdev *netdev;
-    bool internal;
+    const char *open_type;
     int mtu;
     int error;
 
     /* XXX reject devices already in some dp_netdev. */
-    if (type[0] == '\0' || !strcmp(type, "system") || !strcmp(type, "dummy")) {
-        internal = false;
-    } else if (!strcmp(type, "internal")) {
-        internal = true;
-    } else {
-        VLOG_WARN("%s: unsupported port type %s", devname, type);
-        return EINVAL;
-    }
 
     /* Open and validate network device. */
-    if (dp->class == &dpif_dummy_class) {
-        type = "dummy";
-    } else if (internal) {
-        type = "tap";
-    }
-
-    error = netdev_open(devname, type, &netdev);
+    open_type = (strcmp(type, "internal") ? type
+                 : dp->class != &dpif_netdev_class ? "dummy"
+                 : "tap");
+    error = netdev_open(devname, open_type, &netdev);
     if (error) {
         return error;
     }
@@ -356,7 +342,7 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
     port = xmalloc(sizeof *port);
     port->port_no = port_no;
     port->netdev = netdev;
-    port->internal = internal;
+    port->type = xstrdup(type);
 
     error = netdev_get_mtu(netdev, &mtu);
     if (!error) {
@@ -371,19 +357,51 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
 }
 
 static int
-dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
-                     uint16_t *port_nop)
+choose_port(struct dpif *dpif, struct netdev *netdev)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     int port_no;
 
+    if (dpif->dpif_class != &dpif_netdev_class) {
+        /* If the port name contains a number, try to assign that port number.
+         * This can make writing unit tests easier because port numbers are
+         * predictable. */
+        const char *p;
+
+        for (p = netdev_get_name(netdev); *p != '\0'; p++) {
+            if (isdigit((unsigned char) *p)) {
+                port_no = strtol(p, NULL, 10);
+                if (port_no > 0 && port_no < MAX_PORTS
+                    && !dp->ports[port_no]) {
+                    return port_no;
+                }
+                break;
+            }
+        }
+    }
+
     for (port_no = 0; port_no < MAX_PORTS; port_no++) {
         if (!dp->ports[port_no]) {
-            *port_nop = port_no;
-            return do_add_port(dp, netdev_get_name(netdev),
-                               netdev_get_type(netdev), port_no);
+            return port_no;
         }
     }
+
+    return -1;
+}
+
+static int
+dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
+                     uint16_t *port_nop)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    int port_no;
+
+    port_no = choose_port(dpif, netdev);
+    if (port_no >= 0) {
+        *port_nop = port_no;
+        return do_add_port(dp, netdev_get_name(netdev),
+                           netdev_get_type(netdev), port_no);
+    }
     return EFBIG;
 }
 
@@ -446,6 +464,7 @@ do_del_port(struct dp_netdev *dp, uint16_t port_no)
 
     name = xstrdup(netdev_get_name(port->netdev));
     netdev_close(port->netdev);
+    free(port->type);
 
     free(name);
     free(port);
@@ -458,7 +477,7 @@ answer_port_query(const struct dp_netdev_port *port,
                   struct dpif_port *dpif_port)
 {
     dpif_port->name = xstrdup(netdev_get_name(port->netdev));
-    dpif_port->type = xstrdup(port->internal ? "internal" : "system");
+    dpif_port->type = xstrdup(port->type);
     dpif_port->port_no = port->port_no;
 }
 
@@ -550,7 +569,7 @@ dpif_netdev_port_dump_next(const struct dpif *dpif, void *state_,
             free(state->name);
             state->name = xstrdup(netdev_get_name(port->netdev));
             dpif_port->name = state->name;
-            dpif_port->type = port->internal ? "internal" : "system";
+            dpif_port->type = port->type;
             dpif_port->port_no = port->port_no;
             state->port_no = port_no + 1;
             return 0;
@@ -608,7 +627,7 @@ get_dpif_flow_stats(struct dp_netdev_flow *flow, struct dpif_flow_stats *stats)
     stats->n_packets = flow->packet_count;
     stats->n_bytes = flow->byte_count;
     stats->used = flow->used;
-    stats->tcp_flags = TCP_FLAGS(flow->tcp_ctl);
+    stats->tcp_flags = flow->tcp_flags;
 }
 
 static int
@@ -709,33 +728,30 @@ clear_stats(struct dp_netdev_flow *flow)
     flow->used = 0;
     flow->packet_count = 0;
     flow->byte_count = 0;
-    flow->tcp_ctl = 0;
+    flow->tcp_flags = 0;
 }
 
 static int
-dpif_netdev_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
-                    const struct nlattr *nl_key, size_t nl_key_len,
-                    const struct nlattr *actions, size_t actions_len,
-                    struct dpif_flow_stats *stats)
+dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     struct dp_netdev_flow *flow;
     struct flow key;
     int error;
 
-    error = dpif_netdev_flow_from_nlattrs(nl_key, nl_key_len, &key);
+    error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &key);
     if (error) {
         return error;
     }
 
     flow = dp_netdev_lookup_flow(dp, &key);
     if (!flow) {
-        if (flags & DPIF_FP_CREATE) {
+        if (put->flags & DPIF_FP_CREATE) {
             if (hmap_count(&dp->flow_table) < MAX_FLOWS) {
-                if (stats) {
-                    memset(stats, 0, sizeof *stats);
+                if (put->stats) {
+                    memset(put->stats, 0, sizeof *put->stats);
                 }
-                return add_flow(dpif, &key, actions, actions_len);
+                return add_flow(dpif, &key, put->actions, put->actions_len);
             } else {
                 return EFBIG;
             }
@@ -743,13 +759,13 @@ dpif_netdev_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
             return ENOENT;
         }
     } else {
-        if (flags & DPIF_FP_MODIFY) {
-            int error = set_flow_actions(flow, actions, actions_len);
+        if (put->flags & DPIF_FP_MODIFY) {
+            int error = set_flow_actions(flow, put->actions, put->actions_len);
             if (!error) {
-                if (stats) {
-                    get_dpif_flow_stats(flow, stats);
+                if (put->stats) {
+                    get_dpif_flow_stats(flow, put->stats);
                 }
-                if (flags & DPIF_FP_ZERO_STATS) {
+                if (put->flags & DPIF_FP_ZERO_STATS) {
                     clear_stats(flow);
                 }
             }
@@ -761,24 +777,22 @@ dpif_netdev_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
 }
 
 static int
-dpif_netdev_flow_del(struct dpif *dpif,
-                     const struct nlattr *nl_key, size_t nl_key_len,
-                     struct dpif_flow_stats *stats)
+dpif_netdev_flow_del(struct dpif *dpif, const struct dpif_flow_del *del)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     struct dp_netdev_flow *flow;
     struct flow key;
     int error;
 
-    error = dpif_netdev_flow_from_nlattrs(nl_key, nl_key_len, &key);
+    error = dpif_netdev_flow_from_nlattrs(del->key, del->key_len, &key);
     if (error) {
         return error;
     }
 
     flow = dp_netdev_lookup_flow(dp, &key);
     if (flow) {
-        if (stats) {
-            get_dpif_flow_stats(flow, stats);
+        if (del->stats) {
+            get_dpif_flow_stats(flow, del->stats);
         }
         dp_netdev_free_flow(dp, flow);
         return 0;
@@ -862,30 +876,29 @@ dpif_netdev_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
 }
 
 static int
-dpif_netdev_execute(struct dpif *dpif,
-                    const struct nlattr *key_attrs, size_t key_len,
-                    const struct nlattr *actions, size_t actions_len,
-                    const struct ofpbuf *packet)
+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;
     int error;
 
-    if (packet->size < ETH_HEADER_LEN || packet->size > UINT16_MAX) {
+    if (execute->packet->size < ETH_HEADER_LEN ||
+        execute->packet->size > UINT16_MAX) {
         return EINVAL;
     }
 
     /* Make a deep copy of 'packet', because we might modify its data. */
-    ofpbuf_init(&copy, DP_NETDEV_HEADROOM + packet->size);
+    ofpbuf_init(&copy, DP_NETDEV_HEADROOM + execute->packet->size);
     ofpbuf_reserve(&copy, DP_NETDEV_HEADROOM);
-    ofpbuf_put(&copy, packet->data, packet->size);
+    ofpbuf_put(&copy, execute->packet->data, execute->packet->size);
 
-    flow_extract(&copy, 0, -1, &key);
-    error = dpif_netdev_flow_from_nlattrs(key_attrs, key_len, &key);
+    flow_extract(&copy, 0, 0, -1, &key);
+    error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len,
+                                          &key);
     if (!error) {
         dp_netdev_execute_actions(dp, &copy, &key,
-                                  actions, actions_len);
+                                  execute->actions, execute->actions_len);
     }
 
     ofpbuf_uninit(&copy);
@@ -893,32 +906,28 @@ dpif_netdev_execute(struct dpif *dpif,
 }
 
 static int
-dpif_netdev_recv_get_mask(const struct dpif *dpif, int *listen_mask)
+dpif_netdev_recv_set(struct dpif *dpif OVS_UNUSED, bool enable OVS_UNUSED)
 {
-    struct dpif_netdev *dpif_netdev = dpif_netdev_cast(dpif);
-    *listen_mask = dpif_netdev->listen_mask;
     return 0;
 }
 
 static int
-dpif_netdev_recv_set_mask(struct dpif *dpif, int listen_mask)
+dpif_netdev_queue_to_priority(const struct dpif *dpif OVS_UNUSED,
+                              uint32_t queue_id, uint32_t *priority)
 {
-    struct dpif_netdev *dpif_netdev = dpif_netdev_cast(dpif);
-    dpif_netdev->listen_mask = listen_mask;
+    *priority = queue_id;
     return 0;
 }
 
 static struct dp_netdev_queue *
 find_nonempty_queue(struct dpif *dpif)
 {
-    struct dpif_netdev *dpif_netdev = dpif_netdev_cast(dpif);
     struct dp_netdev *dp = get_dp_netdev(dpif);
-    int mask = dpif_netdev->listen_mask;
     int i;
 
     for (i = 0; i < N_QUEUES; i++) {
         struct dp_netdev_queue *q = &dp->queues[i];
-        if (q->head != q->tail && mask & (1u << i)) {
+        if (q->head != q->tail) {
             return q;
         }
     }
@@ -926,7 +935,8 @@ find_nonempty_queue(struct dpif *dpif)
 }
 
 static int
-dpif_netdev_recv(struct dpif *dpif, struct dpif_upcall *upcall)
+dpif_netdev_recv(struct dpif *dpif, struct dpif_upcall *upcall,
+                 struct ofpbuf *buf)
 {
     struct dp_netdev_queue *q = find_nonempty_queue(dpif);
     if (q) {
@@ -934,6 +944,9 @@ dpif_netdev_recv(struct dpif *dpif, struct dpif_upcall *upcall)
         *upcall = *u;
         free(u);
 
+        ofpbuf_uninit(buf);
+        *buf = *u->packet;
+
         return 0;
     } else {
         return EAGAIN;
@@ -965,10 +978,7 @@ dp_netdev_flow_used(struct dp_netdev_flow *flow, struct flow *key,
     flow->used = time_msec();
     flow->packet_count++;
     flow->byte_count += packet->size;
-    if (key->dl_type == htons(ETH_TYPE_IP) && key->nw_proto == IPPROTO_TCP) {
-        struct tcp_header *th = packet->l4;
-        flow->tcp_ctl |= th->tcp_ctl;
-    }
+    flow->tcp_flags |= packet_get_tcp_flags(packet, key);
 }
 
 static void
@@ -981,7 +991,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
     if (packet->size < ETH_HEADER_LEN) {
         return;
     }
-    flow_extract(packet, 0, port->port_no, &key);
+    flow_extract(packet, 0, 0, port->port_no, &key);
     flow = dp_netdev_lookup_flow(dp, &key);
     if (flow) {
         dp_netdev_flow_used(flow, &key, packet);
@@ -1033,24 +1043,6 @@ dpif_netdev_wait(struct dpif *dpif)
     }
 }
 
-static void
-dp_netdev_pop_vlan(struct ofpbuf *packet)
-{
-    struct vlan_eth_header *veh = packet->l2;
-    if (packet->size >= sizeof *veh
-        && veh->veth_type == htons(ETH_TYPE_VLAN)) {
-        struct eth_header tmp;
-
-        memcpy(tmp.eth_dst, veh->veth_dst, ETH_ADDR_LEN);
-        memcpy(tmp.eth_src, veh->veth_src, ETH_ADDR_LEN);
-        tmp.eth_type = veh->veth_next_type;
-
-        ofpbuf_pull(packet, VLAN_HEADER_LEN);
-        packet->l2 = (char*)packet->l2 + VLAN_HEADER_LEN;
-        memcpy(packet->data, &tmp, sizeof tmp);
-    }
-}
-
 static void
 dp_netdev_set_dl(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key)
 {
@@ -1060,89 +1052,6 @@ dp_netdev_set_dl(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key)
     memcpy(eh->eth_dst, eth_key->eth_dst, sizeof eh->eth_dst);
 }
 
-static void
-dp_netdev_set_ip_addr(struct ofpbuf *packet, ovs_be32 *addr, ovs_be32 new_addr)
-{
-    struct ip_header *nh = packet->l3;
-
-    if (nh->ip_proto == IPPROTO_TCP && packet->l7) {
-        struct tcp_header *th = packet->l4;
-        th->tcp_csum = recalc_csum32(th->tcp_csum, *addr, new_addr);
-    } else if (nh->ip_proto == IPPROTO_UDP && packet->l7) {
-        struct udp_header *uh = packet->l4;
-        if (uh->udp_csum) {
-            uh->udp_csum = recalc_csum32(uh->udp_csum, *addr, new_addr);
-            if (!uh->udp_csum) {
-                uh->udp_csum = htons(0xffff);
-            }
-        }
-    }
-    nh->ip_csum = recalc_csum32(nh->ip_csum, *addr, new_addr);
-    *addr = new_addr;
-}
-
-static void
-dp_netdev_set_ip_tos(struct ip_header *nh, uint8_t new_tos)
-{
-    uint8_t *field = &nh->ip_tos;
-
-    /* Set the DSCP bits and preserve the ECN bits. */
-    uint8_t new = new_tos | (nh->ip_tos & IP_ECN_MASK);
-
-    nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t)*field),
-                                   htons((uint16_t) new));
-    *field = new;
-}
-
-static void
-dp_netdev_set_ipv4(struct ofpbuf *packet, const struct ovs_key_ipv4 *ipv4_key)
-{
-    struct ip_header *nh = packet->l3;
-
-    if (nh->ip_src != ipv4_key->ipv4_src) {
-        dp_netdev_set_ip_addr(packet, &nh->ip_src, ipv4_key->ipv4_src);
-    }
-    if (nh->ip_dst != ipv4_key->ipv4_dst) {
-        dp_netdev_set_ip_addr(packet, &nh->ip_dst, ipv4_key->ipv4_dst);
-    }
-    if (nh->ip_tos != ipv4_key->ipv4_tos) {
-        dp_netdev_set_ip_tos(nh, ipv4_key->ipv4_tos);
-    }
-}
-
-static void
-dp_netdev_set_port(ovs_be16 *port, ovs_be16 new_port, ovs_be16 *csum)
-{
-    *csum = recalc_csum16(*csum, *port, new_port);
-    *port = new_port;
-}
-
-static void
-dp_netdev_set_tcp_port(struct ofpbuf *packet, const struct ovs_key_tcp *tcp_key)
-{
-    struct tcp_header *th = packet->l4;
-
-    if (th->tcp_src != tcp_key->tcp_src) {
-        dp_netdev_set_port(&th->tcp_src, tcp_key->tcp_src, &th->tcp_csum);
-    }
-    if (th->tcp_dst != tcp_key->tcp_dst) {
-        dp_netdev_set_port(&th->tcp_dst, tcp_key->tcp_dst, &th->tcp_csum);
-    }
-}
-
-static void
-dp_netdev_set_udp_port(struct ofpbuf *packet, const struct ovs_key_udp *udp_key)
-{
-    struct udp_header *uh = packet->l4;
-
-    if (uh->udp_src != udp_key->udp_src) {
-        dp_netdev_set_port(&uh->udp_src, udp_key->udp_src, &uh->udp_csum);
-    }
-    if (uh->udp_dst != udp_key->udp_dst) {
-        dp_netdev_set_port(&uh->udp_dst, udp_key->udp_dst, &uh->udp_csum);
-    }
-}
-
 static void
 dp_netdev_output_port(struct dp_netdev *dp, struct ofpbuf *packet,
                       uint16_t out_port)
@@ -1237,8 +1146,15 @@ static void
 execute_set_action(struct ofpbuf *packet, const struct nlattr *a)
 {
     enum ovs_key_attr type = nl_attr_type(a);
+    const struct ovs_key_ipv4 *ipv4_key;
+    const struct ovs_key_tcp *tcp_key;
+    const struct ovs_key_udp *udp_key;
+
     switch (type) {
     case OVS_KEY_ATTR_TUN_ID:
+    case OVS_KEY_ATTR_PRIORITY:
+    case OVS_KEY_ATTR_IPV6:
+        /* not implemented */
         break;
 
     case OVS_KEY_ATTR_ETHERNET:
@@ -1247,25 +1163,26 @@ execute_set_action(struct ofpbuf *packet, const struct nlattr *a)
         break;
 
     case OVS_KEY_ATTR_IPV4:
-        dp_netdev_set_ipv4(packet,
-                   nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4)));
+        ipv4_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4));
+        packet_set_ipv4(packet, ipv4_key->ipv4_src, ipv4_key->ipv4_dst,
+                        ipv4_key->ipv4_tos, ipv4_key->ipv4_ttl);
         break;
 
     case OVS_KEY_ATTR_TCP:
-        dp_netdev_set_tcp_port(packet,
-                   nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp)));
+        tcp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp));
+        packet_set_tcp_port(packet, tcp_key->tcp_src, tcp_key->tcp_dst);
         break;
 
      case OVS_KEY_ATTR_UDP:
-        dp_netdev_set_udp_port(packet,
-                   nl_attr_get_unspec(a, sizeof(struct ovs_key_udp)));
+        udp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_udp));
+        packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst);
         break;
 
      case OVS_KEY_ATTR_UNSPEC:
+     case OVS_KEY_ATTR_ENCAP:
      case OVS_KEY_ATTR_ETHERTYPE:
-     case OVS_KEY_ATTR_IPV6:
      case OVS_KEY_ATTR_IN_PORT:
-     case OVS_KEY_ATTR_8021Q:
+     case OVS_KEY_ATTR_VLAN:
      case OVS_KEY_ATTR_ICMP:
      case OVS_KEY_ATTR_ICMPV6:
      case OVS_KEY_ATTR_ARP:
@@ -1286,8 +1203,7 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
     unsigned int left;
 
     NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
-        const struct nlattr *nested;
-        const struct ovs_key_8021q *q_key;
+        const struct ovs_action_push_vlan *vlan;
         int type = nl_attr_type(a);
 
         switch ((enum ovs_action_attr) type) {
@@ -1299,16 +1215,13 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
             dp_netdev_action_userspace(dp, packet, key, a);
             break;
 
-        case OVS_ACTION_ATTR_PUSH:
-            nested = nl_attr_get(a);
-            assert(nl_attr_type(nested) == OVS_KEY_ATTR_8021Q);
-            q_key = nl_attr_get_unspec(nested, sizeof(*q_key));
-            eth_push_vlan(packet, q_key->q_tci);
+        case OVS_ACTION_ATTR_PUSH_VLAN:
+            vlan = nl_attr_get(a);
+            eth_push_vlan(packet, vlan->vlan_tci);
             break;
 
-        case OVS_ACTION_ATTR_POP:
-            assert(nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q);
-            dp_netdev_pop_vlan(packet);
+        case OVS_ACTION_ATTR_POP_VLAN:
+            eth_pop_vlan(packet);
             break;
 
         case OVS_ACTION_ATTR_SET:
@@ -1319,11 +1232,6 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
             dp_netdev_sample(dp, packet, key, a);
             break;
 
-        case OVS_ACTION_ATTR_SET_PRIORITY:
-        case OVS_ACTION_ATTR_POP_PRIORITY:
-            /* not implemented */
-            break;
-
         case OVS_ACTION_ATTR_UNSPEC:
         case __OVS_ACTION_ATTR_MAX:
             NOT_REACHED();
@@ -1360,20 +1268,40 @@ const struct dpif_class dpif_netdev_class = {
     dpif_netdev_flow_dump_done,
     dpif_netdev_execute,
     NULL,                       /* operate */
-    dpif_netdev_recv_get_mask,
-    dpif_netdev_recv_set_mask,
-    NULL,                       /* queue_to_priority */
+    dpif_netdev_recv_set,
+    dpif_netdev_queue_to_priority,
     dpif_netdev_recv,
     dpif_netdev_recv_wait,
     dpif_netdev_recv_purge,
 };
 
+static void
+dpif_dummy_register__(const char *type)
+{
+    struct dpif_class *class;
+
+    class = xmalloc(sizeof *class);
+    *class = dpif_netdev_class;
+    class->type = xstrdup(type);
+    dp_register_provider(class);
+}
+
 void
-dpif_dummy_register(void)
+dpif_dummy_register(bool override)
 {
-    if (!dpif_dummy_class.type) {
-        dpif_dummy_class = dpif_netdev_class;
-        dpif_dummy_class.type = "dummy";
-        dp_register_provider(&dpif_dummy_class);
+    if (override) {
+        struct sset types;
+        const char *type;
+
+        sset_init(&types);
+        dp_enumerate_types(&types);
+        SSET_FOR_EACH (type, &types) {
+            if (!dp_unregister_provider(type)) {
+                dpif_dummy_register__(type);
+            }
+        }
+        sset_destroy(&types);
     }
+
+    dpif_dummy_register__("dummy");
 }