aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/icmp.c
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-04-21 05:45:37 -0400
committerDavid S. Miller <davem@davemloft.net>2011-04-28 16:16:35 -0400
commitf6d8bd051c391c1c0458a30b2a7abcd939329259 (patch)
tree1dc4daecdeb0b42c2c6b59d7d6b41e091c11db5f /net/ipv4/icmp.c
parent0a14842f5a3c0e88a1e59fac5c3025db39721f74 (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.c23
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;