From: Jesse Gross Date: Mon, 12 Jul 2010 20:13:17 +0000 (-0700) Subject: datapath: Add loop checking. X-Git-Tag: v1.1.0pre1~187 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=ecbb6953b37f81f5ea1979aa4e5d8e11df0929ad;p=sliver-openvswitch.git datapath: Add loop checking. New types of vports such as patch and GRE make it possible to connect multiple (or the same) datapaths together, which introduces the possibility of loops on a single machine. Local loops are even worse than network loops because they will quickly crash the machine once the kernel stack is exhausted. This adds loop checking within the OVS datapath that will drop packets that have been seen more than 5 times. CC: Paul Ingram --- diff --git a/datapath/vport.c b/datapath/vport.c index 38c71476e..13039505c 100644 --- a/datapath/vport.c +++ b/datapath/vport.c @@ -34,6 +34,17 @@ static int n_vport_types; static struct hlist_head *dev_table; #define VPORT_HASH_BUCKETS 1024 +/* We limit the number of times that we pass through vport_send() to + * avoid blowing out the stack in the event that we have a loop. There is + * a separate counter for each CPU for both interrupt and non-interrupt + * context in order to keep the limit deterministic for a given packet. */ +struct percpu_loop_counter { + int count[2]; +}; + +static struct percpu_loop_counter *vport_loop_counter; +#define VPORT_MAX_LOOPS 5 + /* Both RTNL lock and vport_mutex need to be held when updating dev_table. * * If you use vport_locate and then perform some operations, you need to hold @@ -107,6 +118,12 @@ vport_init(void) goto error_dev_table; } + vport_loop_counter = alloc_percpu(struct percpu_loop_counter); + if (!vport_loop_counter) { + err = -ENOMEM; + goto error_ops_list; + } + for (i = 0; i < ARRAY_SIZE(base_vport_ops_list); i++) { struct vport_ops *new_ops = base_vport_ops_list[i]; @@ -125,6 +142,8 @@ vport_init(void) return 0; +error_ops_list: + kfree(vport_ops_list); error_dev_table: kfree(dev_table); error: @@ -170,6 +189,7 @@ vport_exit(void) vport_ops_list[i]->exit(); } + free_percpu(vport_loop_counter); kfree(vport_ops_list); kfree(dev_table); } @@ -1267,9 +1287,26 @@ vport_receive(struct vport *vport, struct sk_buff *skb) int vport_send(struct vport *vport, struct sk_buff *skb) { + int *loop_count; int sent; - sent = vport->ops->send(vport, skb); + loop_count = &per_cpu_ptr(vport_loop_counter, get_cpu())->count[!!in_interrupt()]; + (*loop_count)++; + + if (likely(*loop_count <= VPORT_MAX_LOOPS)) { + sent = vport->ops->send(vport, skb); + } else { + if (net_ratelimit()) + printk(KERN_WARNING "%s: dropping packet that has looped more than %d times\n", + dp_name(vport_get_dp_port(vport)->dp), VPORT_MAX_LOOPS); + + sent = 0; + kfree_skb(skb); + vport_record_error(vport, VPORT_E_TX_DROPPED); + } + + (*loop_count)--; + put_cpu(); if (vport->ops->flags & VPORT_F_GEN_STATS && sent > 0) { struct vport_percpu_stats *stats;