dpif-netdev: Fix use-after-free in port_unref().
[cascardo/ovs.git] / lib / dpif-netdev.c
index 9ad9386..a2e3308 100644 (file)
@@ -237,11 +237,7 @@ struct dp_netdev_port {
  *
  * 'flow->ref_cnt' protects 'flow' from being freed.  It doesn't protect the
  * flow from being deleted from 'cls' (that's 'cls->rwlock') and it doesn't
- * protect members of 'flow' from modification (that's 'flow->mutex').
- *
- * 'flow->mutex' protects the members of 'flow' from modification.  It doesn't
- * protect the flow from being deleted from 'cls' (that's 'cls->rwlock') and it
- * doesn't prevent the flow from being freed (that's 'flow->ref_cnt').
+ * protect members of 'flow' from modification.
  *
  * Some members, marked 'const', are immutable.  Accessing other members
  * requires synchronization, as noted in more detail below.
@@ -254,21 +250,12 @@ struct dp_netdev_flow {
     const struct hmap_node node; /* In owning dp_netdev's 'flow_table'. */
     const struct flow flow;      /* The flow that created this entry. */
 
-    /* Protects members marked OVS_GUARDED.
-     *
-     * Acquire after datapath's flow_mutex. */
-    struct ovs_mutex mutex OVS_ACQ_AFTER(dp_netdev_mutex);
-
     /* Statistics.
      *
      * Reading or writing these members requires 'mutex'. */
     struct ovsthread_stats stats; /* Contains "struct dp_netdev_flow_stats". */
 
-    /* Actions.
-     *
-     * Reading 'actions' requires 'mutex'.
-     * Writing 'actions' requires 'mutex' and (to allow for transactions) the
-     * datapath's flow_mutex. */
+    /* Actions. */
     OVSRCU_TYPE(struct dp_netdev_actions *) actions;
 };
 
@@ -290,10 +277,7 @@ struct dp_netdev_flow_stats {
  * Thread-safety
  * =============
  *
- * A struct dp_netdev_actions 'actions' may be accessed without a risk of being
- * freed by code that holds a read-lock or write-lock on 'flow->mutex' (where
- * 'flow' is the dp_netdev_flow for which 'flow->actions == actions') or that
- * owns a reference to 'actions->ref_cnt' (or both). */
+ * A struct dp_netdev_actions 'actions' is protected with RCU. */
 struct dp_netdev_actions {
     /* These members are immutable: they do not change during the struct's
      * lifetime.  */
@@ -323,7 +307,6 @@ struct pmd_thread {
     pthread_t thread;
     int id;
     atomic_uint change_seq;
-    char *name;
 };
 
 /* Interface to netdev-based datapath. */
@@ -353,10 +336,11 @@ static int dpif_netdev_open(const struct dpif_class *, const char *name,
                             bool create, struct dpif **);
 static int dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *,
                                       int queue_no, int type,
-                                      const struct flow *,
+                                      const struct miniflow *,
                                       const struct nlattr *userdata);
 static void dp_netdev_execute_actions(struct dp_netdev *dp,
-                                      const struct flow *, struct ofpbuf *, bool may_steal,
+                                      const struct miniflow *,
+                                      struct ofpbuf *, bool may_steal,
                                       struct pkt_metadata *,
                                       const struct nlattr *actions,
                                       size_t actions_len);
@@ -827,12 +811,14 @@ static void
 port_unref(struct dp_netdev_port *port)
 {
     if (port && ovs_refcount_unref(&port->ref_cnt) == 1) {
+        int n_rxq;
         int i;
 
         netdev_close(port->netdev);
         netdev_restore_flags(port->sf);
 
-        for (i = 0; i < netdev_n_rxq(port->netdev); i++) {
+        n_rxq = netdev_n_rxq(port->netdev);
+        for (i = 0; i < n_rxq; i++) {
             netdev_rxq_close(port->rxq[i]);
         }
         free(port->type);
@@ -937,7 +923,6 @@ dp_netdev_flow_free(struct dp_netdev_flow *flow)
 
     cls_rule_destroy(CONST_CAST(struct cls_rule *, &flow->cr));
     dp_netdev_actions_free(dp_netdev_flow_get_actions(flow));
-    ovs_mutex_destroy(&flow->mutex);
     free(flow);
 }
 
@@ -1063,13 +1048,15 @@ dp_netdev_flow_cast(const struct cls_rule *cr)
 }
 
 static struct dp_netdev_flow *
-dp_netdev_lookup_flow(const struct dp_netdev *dp, const struct flow *flow)
+dp_netdev_lookup_flow(const struct dp_netdev *dp, const struct miniflow *key)
     OVS_EXCLUDED(dp->cls.rwlock)
 {
     struct dp_netdev_flow *netdev_flow;
+    struct cls_rule *rule;
 
     fat_rwlock_rdlock(&dp->cls.rwlock);
-    netdev_flow = dp_netdev_flow_cast(classifier_lookup(&dp->cls, flow, NULL));
+    rule = classifier_lookup_miniflow_first(&dp->cls, key);
+    netdev_flow = dp_netdev_flow_cast(rule);
     fat_rwlock_unlock(&dp->cls.rwlock);
 
     return netdev_flow;
@@ -1251,8 +1238,6 @@ dp_netdev_flow_add(struct dp_netdev *dp, const struct flow *flow,
     netdev_flow = xzalloc(sizeof *netdev_flow);
     *CONST_CAST(struct flow *, &netdev_flow->flow) = *flow;
 
-    ovs_mutex_init(&netdev_flow->mutex);
-
     ovsthread_stats_init(&netdev_flow->stats);
 
     ovsrcu_set(&netdev_flow->actions,
@@ -1294,6 +1279,7 @@ 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 *netdev_flow;
     struct flow flow;
+    struct miniflow miniflow;
     struct flow_wildcards wc;
     int error;
 
@@ -1307,9 +1293,10 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
     if (error) {
         return error;
     }
+    miniflow_init(&miniflow, &flow);
 
     ovs_mutex_lock(&dp->flow_mutex);
-    netdev_flow = dp_netdev_lookup_flow(dp, &flow);
+    netdev_flow = dp_netdev_lookup_flow(dp, &miniflow);
     if (!netdev_flow) {
         if (put->flags & DPIF_FP_CREATE) {
             if (hmap_count(&dp->flow_table) < MAX_FLOWS) {
@@ -1386,135 +1373,141 @@ dpif_netdev_flow_del(struct dpif *dpif, const struct dpif_flow_del *del)
     return error;
 }
 
-struct dp_netdev_flow_state {
-    struct dp_netdev_actions *actions;
-    struct odputil_keybuf keybuf;
-    struct odputil_keybuf maskbuf;
-    struct dpif_flow_stats stats;
-};
-
-struct dp_netdev_flow_iter {
+struct dpif_netdev_flow_dump {
+    struct dpif_flow_dump up;
     uint32_t bucket;
     uint32_t offset;
     int status;
     struct ovs_mutex mutex;
 };
 
-static void
-dpif_netdev_flow_dump_state_init(void **statep)
+static struct dpif_netdev_flow_dump *
+dpif_netdev_flow_dump_cast(struct dpif_flow_dump *dump)
 {
-    struct dp_netdev_flow_state *state;
-
-    *statep = state = xmalloc(sizeof *state);
-    state->actions = NULL;
+    return CONTAINER_OF(dump, struct dpif_netdev_flow_dump, up);
 }
 
-static void
-dpif_netdev_flow_dump_state_uninit(void *state_)
+static struct dpif_flow_dump *
+dpif_netdev_flow_dump_create(const struct dpif *dpif_)
 {
-    struct dp_netdev_flow_state *state = state_;
+    struct dpif_netdev_flow_dump *dump;
 
-    free(state);
+    dump = xmalloc(sizeof *dump);
+    dpif_flow_dump_init(&dump->up, dpif_);
+    dump->bucket = 0;
+    dump->offset = 0;
+    dump->status = 0;
+    ovs_mutex_init(&dump->mutex);
+
+    return &dump->up;
 }
 
 static int
-dpif_netdev_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **iterp)
+dpif_netdev_flow_dump_destroy(struct dpif_flow_dump *dump_)
 {
-    struct dp_netdev_flow_iter *iter;
+    struct dpif_netdev_flow_dump *dump = dpif_netdev_flow_dump_cast(dump_);
 
-    *iterp = iter = xmalloc(sizeof *iter);
-    iter->bucket = 0;
-    iter->offset = 0;
-    iter->status = 0;
-    ovs_mutex_init(&iter->mutex);
+    ovs_mutex_destroy(&dump->mutex);
+    free(dump);
     return 0;
 }
 
+struct dpif_netdev_flow_dump_thread {
+    struct dpif_flow_dump_thread up;
+    struct dpif_netdev_flow_dump *dump;
+    struct odputil_keybuf keybuf;
+    struct odputil_keybuf maskbuf;
+};
+
+static struct dpif_netdev_flow_dump_thread *
+dpif_netdev_flow_dump_thread_cast(struct dpif_flow_dump_thread *thread)
+{
+    return CONTAINER_OF(thread, struct dpif_netdev_flow_dump_thread, up);
+}
+
+static struct dpif_flow_dump_thread *
+dpif_netdev_flow_dump_thread_create(struct dpif_flow_dump *dump_)
+{
+    struct dpif_netdev_flow_dump *dump = dpif_netdev_flow_dump_cast(dump_);
+    struct dpif_netdev_flow_dump_thread *thread;
+
+    thread = xmalloc(sizeof *thread);
+    dpif_flow_dump_thread_init(&thread->up, &dump->up);
+    thread->dump = dump;
+    return &thread->up;
+}
+
+static void
+dpif_netdev_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread_)
+{
+    struct dpif_netdev_flow_dump_thread *thread
+        = dpif_netdev_flow_dump_thread_cast(thread_);
+
+    free(thread);
+}
+
 /* XXX the caller must use 'actions' without quiescing */
 static int
-dpif_netdev_flow_dump_next(const struct dpif *dpif, void *iter_, void *state_,
-                           const struct nlattr **key, size_t *key_len,
-                           const struct nlattr **mask, size_t *mask_len,
-                           const struct nlattr **actions, size_t *actions_len,
-                           const struct dpif_flow_stats **stats)
-{
-    struct dp_netdev_flow_iter *iter = iter_;
-    struct dp_netdev_flow_state *state = state_;
-    struct dp_netdev *dp = get_dp_netdev(dpif);
+dpif_netdev_flow_dump_next(struct dpif_flow_dump_thread *thread_,
+                           struct dpif_flow *f, int max_flows OVS_UNUSED)
+{
+    struct dpif_netdev_flow_dump_thread *thread
+        = dpif_netdev_flow_dump_thread_cast(thread_);
+    struct dpif_netdev_flow_dump *dump = thread->dump;
+    struct dpif_netdev *dpif = dpif_netdev_cast(thread->up.dpif);
+    struct dp_netdev *dp = get_dp_netdev(&dpif->dpif);
     struct dp_netdev_flow *netdev_flow;
+    struct flow_wildcards wc;
+    struct dp_netdev_actions *dp_actions;
+    struct ofpbuf buf;
     int error;
 
-    ovs_mutex_lock(&iter->mutex);
-    error = iter->status;
+    ovs_mutex_lock(&dump->mutex);
+    error = dump->status;
     if (!error) {
         struct hmap_node *node;
 
         fat_rwlock_rdlock(&dp->cls.rwlock);
-        node = hmap_at_position(&dp->flow_table, &iter->bucket, &iter->offset);
+        node = hmap_at_position(&dp->flow_table, &dump->bucket, &dump->offset);
         if (node) {
             netdev_flow = CONTAINER_OF(node, struct dp_netdev_flow, node);
         }
         fat_rwlock_unlock(&dp->cls.rwlock);
         if (!node) {
-            iter->status = error = EOF;
+            dump->status = error = EOF;
         }
     }
-    ovs_mutex_unlock(&iter->mutex);
+    ovs_mutex_unlock(&dump->mutex);
     if (error) {
-        return error;
+        return 0;
     }
 
-    if (key) {
-        struct ofpbuf buf;
-
-        ofpbuf_use_stack(&buf, &state->keybuf, sizeof state->keybuf);
-        odp_flow_key_from_flow(&buf, &netdev_flow->flow,
-                               netdev_flow->flow.in_port.odp_port);
+    minimask_expand(&netdev_flow->cr.match.mask, &wc);
 
-        *key = ofpbuf_data(&buf);
-        *key_len = ofpbuf_size(&buf);
-    }
+    /* Key. */
+    ofpbuf_use_stack(&buf, &thread->keybuf, sizeof thread->keybuf);
+    odp_flow_key_from_flow(&buf, &netdev_flow->flow, &wc.masks,
+                           netdev_flow->flow.in_port.odp_port, true);
+    f->key = ofpbuf_data(&buf);
+    f->key_len = ofpbuf_size(&buf);
 
-    if (key && mask) {
-        struct ofpbuf buf;
-        struct flow_wildcards wc;
+    /* Mask. */
+    ofpbuf_use_stack(&buf, &thread->maskbuf, sizeof thread->maskbuf);
+    odp_flow_key_from_mask(&buf, &wc.masks, &netdev_flow->flow,
+                           odp_to_u32(wc.masks.in_port.odp_port),
+                           SIZE_MAX, true);
+    f->mask = ofpbuf_data(&buf);
+    f->mask_len = ofpbuf_size(&buf);
 
-        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),
-                               SIZE_MAX);
-
-        *mask = ofpbuf_data(&buf);
-        *mask_len = ofpbuf_size(&buf);
-    }
+    /* Actions. */
+    dp_actions = dp_netdev_flow_get_actions(netdev_flow);
+    f->actions = dp_actions->actions;
+    f->actions_len = dp_actions->size;
 
-    if (actions || stats) {
-        state->actions = NULL;
-
-        if (actions) {
-            state->actions = dp_netdev_flow_get_actions(netdev_flow);
-            *actions = state->actions->actions;
-            *actions_len = state->actions->size;
-        }
-
-        if (stats) {
-            get_dpif_flow_stats(netdev_flow, &state->stats);
-            *stats = &state->stats;
-        }
-    }
-
-    return 0;
-}
-
-static int
-dpif_netdev_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *iter_)
-{
-    struct dp_netdev_flow_iter *iter = iter_;
+    /* Stats. */
+    get_dpif_flow_stats(netdev_flow, &f->stats);
 
-    ovs_mutex_destroy(&iter->mutex);
-    free(iter);
-    return 0;
+    return 1;
 }
 
 static int
@@ -1522,7 +1515,10 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     struct pkt_metadata *md = &execute->md;
-    struct flow key;
+    struct {
+        struct miniflow flow;
+        uint32_t buf[FLOW_U32S];
+    } key;
 
     if (ofpbuf_size(execute->packet) < ETH_HEADER_LEN ||
         ofpbuf_size(execute->packet) > UINT16_MAX) {
@@ -1530,10 +1526,11 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
     }
 
     /* Extract flow key. */
-    flow_extract(execute->packet, md, &key);
+    miniflow_initialize(&key.flow, key.buf);
+    miniflow_extract(execute->packet, md, &key.flow);
 
     ovs_rwlock_rdlock(&dp->port_rwlock);
-    dp_netdev_execute_actions(dp, &key, execute->packet, false, md,
+    dp_netdev_execute_actions(dp, &key.flow, execute->packet, false, md,
                               execute->actions, execute->actions_len);
     ovs_rwlock_unlock(&dp->port_rwlock);
 
@@ -1869,8 +1866,6 @@ pmd_thread_main(void *f_)
     int poll_cnt;
     int i;
 
-    f->name = xasprintf("pmd_%u", ovsthread_id_self());
-    set_subprogram_name("%s", f->name);
     poll_cnt = 0;
     poll_list = NULL;
 
@@ -1910,7 +1905,6 @@ reload:
     }
 
     free(poll_list);
-    free(f->name);
     return NULL;
 }
 
@@ -1947,7 +1941,7 @@ dp_netdev_set_pmd_threads(struct dp_netdev *dp, int n)
 
         /* Each thread will distribute all devices rx-queues among
          * themselves. */
-        xpthread_create(&f->thread, NULL, pmd_thread_main, f);
+        f->thread = ovs_thread_create("pmd", pmd_thread_main, f);
     }
 }
 
@@ -1963,9 +1957,9 @@ dp_netdev_flow_stats_new_cb(void)
 static void
 dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow,
                     const struct ofpbuf *packet,
-                    const struct flow *key)
+                    const struct miniflow *key)
 {
-    uint16_t tcp_flags = ntohs(key->tcp_flags);
+    uint16_t tcp_flags = miniflow_get_tcp_flags(key);
     long long int now = time_msec();
     struct dp_netdev_flow_stats *bucket;
 
@@ -2005,28 +1999,34 @@ dp_netdev_input(struct dp_netdev *dp, struct ofpbuf *packet,
     OVS_REQ_RDLOCK(dp->port_rwlock)
 {
     struct dp_netdev_flow *netdev_flow;
-    struct flow key;
+    struct {
+        struct miniflow flow;
+        uint32_t buf[FLOW_U32S];
+    } key;
 
     if (ofpbuf_size(packet) < ETH_HEADER_LEN) {
         ofpbuf_delete(packet);
         return;
     }
-    flow_extract(packet, md, &key);
-    netdev_flow = dp_netdev_lookup_flow(dp, &key);
+    miniflow_initialize(&key.flow, key.buf);
+    miniflow_extract(packet, md, &key.flow);
+
+    netdev_flow = dp_netdev_lookup_flow(dp, &key.flow);
     if (netdev_flow) {
         struct dp_netdev_actions *actions;
 
-        dp_netdev_flow_used(netdev_flow, packet, &key);
+        dp_netdev_flow_used(netdev_flow, packet, &key.flow);
 
         actions = dp_netdev_flow_get_actions(netdev_flow);
-        dp_netdev_execute_actions(dp, &key, packet, true, md,
+        dp_netdev_execute_actions(dp, &key.flow, packet, true, md,
                                   actions->actions, actions->size);
         dp_netdev_count_packet(dp, DP_STAT_HIT);
     } else if (dp->handler_queues) {
         dp_netdev_count_packet(dp, DP_STAT_MISS);
         dp_netdev_output_userspace(dp, packet,
-                                   flow_hash_5tuple(&key, 0) % dp->n_handlers,
-                                   DPIF_UC_MISS, &key, NULL);
+                                   miniflow_hash_5tuple(&key.flow, 0)
+                                   % dp->n_handlers,
+                                   DPIF_UC_MISS, &key.flow, NULL);
         ofpbuf_delete(packet);
     }
 }
@@ -2044,7 +2044,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf *packet,
 
 static int
 dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
-                           int queue_no, int type, const struct flow *flow,
+                           int queue_no, int type, const struct miniflow *key,
                            const struct nlattr *userdata)
 {
     struct dp_netdev_queue *q;
@@ -2058,6 +2058,7 @@ dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
         struct dpif_upcall *upcall = &u->upcall;
         struct ofpbuf *buf = &u->buf;
         size_t buf_size;
+        struct flow flow;
 
         upcall->type = type;
 
@@ -2070,7 +2071,8 @@ dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
         ofpbuf_init(buf, buf_size);
 
         /* Put ODP flow. */
-        odp_flow_key_from_flow(buf, flow, flow->in_port.odp_port);
+        miniflow_expand(key, &flow);
+        odp_flow_key_from_flow(buf, &flow, NULL, flow.in_port.odp_port, true);
         upcall->key = ofpbuf_data(buf);
         upcall->key_len = ofpbuf_size(buf);
 
@@ -2099,7 +2101,7 @@ dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
 
 struct dp_netdev_execute_aux {
     struct dp_netdev *dp;
-    const struct flow *key;
+    const struct miniflow *key;
 };
 
 static void
@@ -2127,7 +2129,7 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet,
         userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
 
         dp_netdev_output_userspace(aux->dp, packet,
-                                   flow_hash_5tuple(aux->key, 0)
+                                   miniflow_hash_5tuple(aux->key, 0)
                                        % aux->dp->n_handlers,
                                    DPIF_UC_ACTION, aux->key,
                                    userdata);
@@ -2138,25 +2140,35 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet,
         break;
     }
 
+    case OVS_ACTION_ATTR_HASH: {
+        const struct ovs_action_hash *hash_act;
+        uint32_t hash;
+
+        hash_act = nl_attr_get(a);
+        if (hash_act->hash_alg == OVS_HASH_ALG_L4) {
+            /* Hash need not be symmetric, nor does it need to include
+             * L2 fields. */
+            hash = miniflow_hash_5tuple(aux->key, hash_act->hash_basis);
+            if (!hash) {
+                hash = 1; /* 0 is not valid */
+            }
+
+        } else {
+            VLOG_WARN("Unknown hash algorithm specified for the hash action.");
+            hash = 2;
+        }
+
+        md->dp_hash = hash;
+        break;
+    }
+
     case OVS_ACTION_ATTR_RECIRC:
         if (*depth < MAX_RECIRC_DEPTH) {
             struct pkt_metadata recirc_md = *md;
             struct ofpbuf *recirc_packet;
-            const struct ovs_action_recirc *act;
 
             recirc_packet = may_steal ? packet : ofpbuf_clone(packet);
-
-            act = nl_attr_get(a);
-            recirc_md.recirc_id = act->recirc_id;
-            recirc_md.dp_hash = 0;
-
-            if (act->hash_alg == OVS_RECIRC_HASH_ALG_L4) {
-                recirc_md.dp_hash = flow_hash_symmetric_l4(aux->key,
-                                                           act->hash_bias);
-                if (!recirc_md.dp_hash) {
-                    recirc_md.dp_hash = 1;  /* 0 is not valid */
-                }
-            }
+            recirc_md.recirc_id = nl_attr_get_u32(a);
 
             (*depth)++;
             dp_netdev_input(aux->dp, recirc_packet, &recirc_md);
@@ -2181,7 +2193,7 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet,
 }
 
 static void
-dp_netdev_execute_actions(struct dp_netdev *dp, const struct flow *key,
+dp_netdev_execute_actions(struct dp_netdev *dp, const struct miniflow *key,
                           struct ofpbuf *packet, bool may_steal,
                           struct pkt_metadata *md,
                           const struct nlattr *actions, size_t actions_len)
@@ -2216,12 +2228,11 @@ const struct dpif_class dpif_netdev_class = {
     dpif_netdev_flow_put,
     dpif_netdev_flow_del,
     dpif_netdev_flow_flush,
-    dpif_netdev_flow_dump_state_init,
-    dpif_netdev_flow_dump_start,
+    dpif_netdev_flow_dump_create,
+    dpif_netdev_flow_dump_destroy,
+    dpif_netdev_flow_dump_thread_create,
+    dpif_netdev_flow_dump_thread_destroy,
     dpif_netdev_flow_dump_next,
-    NULL,
-    dpif_netdev_flow_dump_done,
-    dpif_netdev_flow_dump_state_uninit,
     dpif_netdev_execute,
     NULL,                       /* operate */
     dpif_netdev_recv_set,