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