diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-04-21 05:45:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-04-28 16:16:35 -0400 |
commit | f6d8bd051c391c1c0458a30b2a7abcd939329259 (patch) | |
tree | 1dc4daecdeb0b42c2c6b59d7d6b41e091c11db5f /net/ipv4/icmp.c | |
parent | 0a14842f5a3c0e88a1e59fac5c3025db39721f74 (diff) |
inet: add RCU protection to inet->opt
We lack proper synchronization to manipulate inet->opt ip_options
Problem is ip_make_skb() calls ip_setup_cork() and
ip_setup_cork() possibly makes a copy of ipc->opt (struct ip_options),
without any protection against another thread manipulating inet->opt.
Another thread can change inet->opt pointer and free old one under us.
Use RCU to protect inet->opt (changed to inet->inet_opt).
Instead of handling atomic refcounts, just copy ip_options when
necessary, to avoid cache line dirtying.
We cant insert an rcu_head in struct ip_options since its included in
skb->cb[], so this patch is large because I had to introduce a new
ip_options_rcu structure.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/icmp.c')
-rw-r--r-- | net/ipv4/icmp.c | 23 |
1 files changed, 11 insertions, 12 deletions
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 74e35e5736e2..cfeca3c2152d 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c | |||
@@ -108,8 +108,7 @@ struct icmp_bxm { | |||
108 | __be32 times[3]; | 108 | __be32 times[3]; |
109 | } data; | 109 | } data; |
110 | int head_len; | 110 | int head_len; |
111 | struct ip_options replyopts; | 111 | struct ip_options_data replyopts; |
112 | unsigned char optbuf[40]; | ||
113 | }; | 112 | }; |
114 | 113 | ||
115 | /* An array of errno for error messages from dest unreach. */ | 114 | /* An array of errno for error messages from dest unreach. */ |
@@ -333,7 +332,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) | |||
333 | struct inet_sock *inet; | 332 | struct inet_sock *inet; |
334 | __be32 daddr; | 333 | __be32 daddr; |
335 | 334 | ||
336 | if (ip_options_echo(&icmp_param->replyopts, skb)) | 335 | if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb)) |
337 | return; | 336 | return; |
338 | 337 | ||
339 | sk = icmp_xmit_lock(net); | 338 | sk = icmp_xmit_lock(net); |
@@ -347,10 +346,10 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) | |||
347 | daddr = ipc.addr = rt->rt_src; | 346 | daddr = ipc.addr = rt->rt_src; |
348 | ipc.opt = NULL; | 347 | ipc.opt = NULL; |
349 | ipc.tx_flags = 0; | 348 | ipc.tx_flags = 0; |
350 | if (icmp_param->replyopts.optlen) { | 349 | if (icmp_param->replyopts.opt.opt.optlen) { |
351 | ipc.opt = &icmp_param->replyopts; | 350 | ipc.opt = &icmp_param->replyopts.opt; |
352 | if (ipc.opt->srr) | 351 | if (ipc.opt->opt.srr) |
353 | daddr = icmp_param->replyopts.faddr; | 352 | daddr = icmp_param->replyopts.opt.opt.faddr; |
354 | } | 353 | } |
355 | { | 354 | { |
356 | struct flowi4 fl4 = { | 355 | struct flowi4 fl4 = { |
@@ -379,8 +378,8 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, | |||
379 | struct icmp_bxm *param) | 378 | struct icmp_bxm *param) |
380 | { | 379 | { |
381 | struct flowi4 fl4 = { | 380 | struct flowi4 fl4 = { |
382 | .daddr = (param->replyopts.srr ? | 381 | .daddr = (param->replyopts.opt.opt.srr ? |
383 | param->replyopts.faddr : iph->saddr), | 382 | param->replyopts.opt.opt.faddr : iph->saddr), |
384 | .saddr = saddr, | 383 | .saddr = saddr, |
385 | .flowi4_tos = RT_TOS(tos), | 384 | .flowi4_tos = RT_TOS(tos), |
386 | .flowi4_proto = IPPROTO_ICMP, | 385 | .flowi4_proto = IPPROTO_ICMP, |
@@ -581,7 +580,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) | |||
581 | IPTOS_PREC_INTERNETCONTROL) : | 580 | IPTOS_PREC_INTERNETCONTROL) : |
582 | iph->tos; | 581 | iph->tos; |
583 | 582 | ||
584 | if (ip_options_echo(&icmp_param.replyopts, skb_in)) | 583 | if (ip_options_echo(&icmp_param.replyopts.opt.opt, skb_in)) |
585 | goto out_unlock; | 584 | goto out_unlock; |
586 | 585 | ||
587 | 586 | ||
@@ -597,7 +596,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) | |||
597 | icmp_param.offset = skb_network_offset(skb_in); | 596 | icmp_param.offset = skb_network_offset(skb_in); |
598 | inet_sk(sk)->tos = tos; | 597 | inet_sk(sk)->tos = tos; |
599 | ipc.addr = iph->saddr; | 598 | ipc.addr = iph->saddr; |
600 | ipc.opt = &icmp_param.replyopts; | 599 | ipc.opt = &icmp_param.replyopts.opt; |
601 | ipc.tx_flags = 0; | 600 | ipc.tx_flags = 0; |
602 | 601 | ||
603 | rt = icmp_route_lookup(net, skb_in, iph, saddr, tos, | 602 | rt = icmp_route_lookup(net, skb_in, iph, saddr, tos, |
@@ -613,7 +612,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) | |||
613 | room = dst_mtu(&rt->dst); | 612 | room = dst_mtu(&rt->dst); |
614 | if (room > 576) | 613 | if (room > 576) |
615 | room = 576; | 614 | room = 576; |
616 | room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen; | 615 | room -= sizeof(struct iphdr) + icmp_param.replyopts.opt.opt.optlen; |
617 | room -= sizeof(struct icmphdr); | 616 | room -= sizeof(struct icmphdr); |
618 | 617 | ||
619 | icmp_param.data_len = skb_in->len - icmp_param.offset; | 618 | icmp_param.data_len = skb_in->len - icmp_param.offset; |