Before this patch, if a packet came in on a port which userspace
doesn't know about, it would be silently dropped without installing
a drop flow.  Historically, this has been fine because this
situation could only occur during transient reconfiguration
periods.  However, in future, this could occur when the tunneling
code decides to reject a packet due to invalid headers.  In this
case, it's preferable to drop the packet in the kernel to avoid a
high bandwidth stream of invalid packets DoSing the switch.

Signed-off-by: Ethan Jackson <et...@nicira.com>
---
 lib/ofpbuf.c           |    1 +
 lib/ofpbuf.h           |    2 ++
 ofproto/ofproto-dpif.c |   77 ++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c
index 6484ab3..1c2e378 100644
--- a/lib/ofpbuf.c
+++ b/lib/ofpbuf.c
@@ -31,6 +31,7 @@ ofpbuf_use__(struct ofpbuf *b, void *base, size_t allocated,
     b->size = 0;
     b->l2 = b->l3 = b->l4 = b->l7 = NULL;
     list_poison(&b->list_node);
+    memset(&b->hmap_node, 0, sizeof b->hmap_node);
     b->private_p = NULL;
 }
 
diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h
index 520455d..c562721 100644
--- a/lib/ofpbuf.h
+++ b/lib/ofpbuf.h
@@ -19,6 +19,7 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include "hmap.h"
 #include "list.h"
 #include "util.h"
 
@@ -48,6 +49,7 @@ struct ofpbuf {
     void *l7;                   /* Application data. */
 
     struct list list_node;      /* Private list element for use by owner. */
+    struct hmap_node hmap_node; /* Private hmap node for use by owner. */
     void *private_p;            /* Private pointer for use by owner. */
 };
 
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 0fdcaa7..2d7db24 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -626,11 +626,17 @@ struct dpif_backer {
     /* Facet revalidation flags applying to facets which use this backer. */
     enum revalidate_reason need_revalidate; /* Revalidate every facet. */
     struct tag_set revalidate_set; /* Revalidate only matching facets. */
+
+    /* Set of odp flow keys which have drop flows installed in the kernel.
+     * These are datapath flows which have no associated ofproto, if they did
+     * we would use facets. */
+    struct hmap drop_keys;
 };
 
 /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
 static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
 
+static void drop_key_clear(struct dpif_backer *);
 static struct ofport_dpif *
 odp_port_to_ofport(const struct dpif_backer *, uint32_t odp_port);
 
@@ -844,6 +850,12 @@ type_run(const char *type)
         case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
         }
 
+        if (backer->need_revalidate) {
+            /* Clear the drop_keys in case we should now be accepting some
+             * formally dropped flows. */
+            drop_key_clear(backer);
+        }
+
         HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
             struct facet *facet;
 
@@ -862,7 +874,6 @@ type_run(const char *type)
                 }
             }
         }
-
     }
 
     if (timer_expired(&backer->next_expiration)) {
@@ -1070,6 +1081,7 @@ open_dpif_backer(const char *type, struct dpif_backer 
**backerp)
     backer->type = xstrdup(type);
     backer->refcount = 1;
     hmap_init(&backer->odp_to_ofport_map);
+    hmap_init(&backer->drop_keys);
     timer_set_duration(&backer->next_expiration, 1000);
     backer->need_revalidate = 0;
     tag_set_init(&backer->revalidate_set);
@@ -3474,6 +3486,46 @@ handle_flow_miss(struct flow_miss *miss, struct 
flow_miss_op *ops,
     handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
 }
 
+static struct ofpbuf *
+drop_key_lookup(const struct dpif_backer *backer, const struct nlattr *key,
+                size_t key_len)
+{
+    struct ofpbuf *drop_key;
+
+    HMAP_FOR_EACH_WITH_HASH (drop_key, hmap_node, hash_bytes(key, key_len, 0),
+                             &backer->drop_keys) {
+        if (drop_key->size == key_len
+            && !memcmp(drop_key->data, key, key_len)) {
+            return drop_key;
+        }
+    }
+    return NULL;
+}
+
+static void
+drop_key_clear(struct dpif_backer *backer)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 15);
+    struct ofpbuf *drop_key, *next;
+
+    HMAP_FOR_EACH_SAFE (drop_key, next, hmap_node, &backer->drop_keys) {
+        int error;
+
+        error = dpif_flow_del(backer->dpif, drop_key->data, drop_key->size,
+                              NULL);
+        if (error && !VLOG_DROP_WARN(&rl)) {
+            struct ds ds = DS_EMPTY_INITIALIZER;
+            odp_flow_key_format(drop_key->data, drop_key->size, &ds);
+            VLOG_WARN("Failed to delete drop key (%s) (%s)", strerror(error),
+                      ds_cstr(&ds));
+            ds_destroy(&ds);
+        }
+
+        hmap_remove(&backer->drop_keys, &drop_key->hmap_node);
+        ofpbuf_delete(drop_key);
+    }
+}
+
 /* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key'
  * respectively), populates 'flow' with the result of odp_flow_key_to_flow().
  * Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as
@@ -3597,6 +3649,7 @@ handle_miss_upcalls(struct dpif_backer *backer, struct 
dpif_upcall *upcalls,
         struct flow_miss *miss = &misses[n_misses];
         struct flow_miss *existing_miss;
         struct ofproto_dpif *ofproto;
+        struct ofpbuf *drop_key;
         uint32_t odp_in_port;
         struct flow flow;
         uint32_t hash;
@@ -3609,9 +3662,21 @@ handle_miss_upcalls(struct dpif_backer *backer, struct 
dpif_upcall *upcalls,
             /* Received packet on port for which we couldn't associate
              * an ofproto.  This can happen if a port is removed while
              * traffic is being received.  Print a rate-limited message
-             * in case it happens frequently. */
+             * in case it happens frequently.  Install a drop flow so
+             * that future packets of the flow are inexpensively dropped
+             * in the kernel. */
             VLOG_INFO_RL(&rl, "received packet on unassociated port %"PRIu32,
                          flow.in_port);
+
+            drop_key = drop_key_lookup(backer, upcall->key, upcall->key_len);
+            if (!drop_key) {
+                drop_key = ofpbuf_clone_data(upcall->key, upcall->key_len);
+                hmap_insert(&backer->drop_keys, &drop_key->hmap_node,
+                            hash_bytes(drop_key->data, drop_key->size, 0));
+                dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+                              drop_key->data, drop_key->size, NULL, 0, NULL);
+            }
+            continue;
         }
         if (error) {
             continue;
@@ -3801,6 +3866,10 @@ expire(struct dpif_backer *backer)
     struct ofproto_dpif *ofproto;
     int max_idle = INT32_MAX;
 
+    /* Periodically clear out the drop keys in an effort to keep them
+     * relatively few. */
+    drop_key_clear(backer);
+
     /* Update stats for each flow in the backer. */
     update_stats(backer);
 
@@ -3921,6 +3990,10 @@ update_stats(struct dpif_backer *backer)
         struct ofproto_dpif *ofproto;
         uint32_t key_hash;
 
+        if (drop_key_lookup(backer, key, key_len)) {
+            continue;
+        }
+
         if (ofproto_receive(backer, NULL, key, key_len, &flow, NULL, &ofproto,
                             NULL, NULL)) {
             continue;
-- 
1.7.9.5

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to