aboutsummaryrefslogtreecommitdiffstats
path: root/net/dccp/ipv4.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/dccp/ipv4.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/dccp/ipv4.c')
-rw-r--r--net/dccp/ipv4.c16
1 files changed, 10 insertions, 6 deletions
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index b92ab655d44e..cbbcc6c036e0 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -48,6 +48,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
48 struct flowi4 fl4; 48 struct flowi4 fl4;
49 struct rtable *rt; 49 struct rtable *rt;
50 int err; 50 int err;
51 struct ip_options_rcu *inet_opt;
51 52
52 dp->dccps_role = DCCP_ROLE_CLIENT; 53 dp->dccps_role = DCCP_ROLE_CLIENT;
53 54
@@ -58,10 +59,13 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
58 return -EAFNOSUPPORT; 59 return -EAFNOSUPPORT;
59 60
60 nexthop = daddr = usin->sin_addr.s_addr; 61 nexthop = daddr = usin->sin_addr.s_addr;
61 if (inet->opt != NULL && inet->opt->srr) { 62
63 inet_opt = rcu_dereference_protected(inet->inet_opt,
64 sock_owned_by_user(sk));
65 if (inet_opt != NULL && inet_opt->opt.srr) {
62 if (daddr == 0) 66 if (daddr == 0)
63 return -EINVAL; 67 return -EINVAL;
64 nexthop = inet->opt->faddr; 68 nexthop = inet_opt->opt.faddr;
65 } 69 }
66 70
67 orig_sport = inet->inet_sport; 71 orig_sport = inet->inet_sport;
@@ -78,7 +82,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
78 return -ENETUNREACH; 82 return -ENETUNREACH;
79 } 83 }
80 84
81 if (inet->opt == NULL || !inet->opt->srr) 85 if (inet_opt == NULL || !inet_opt->opt.srr)
82 daddr = rt->rt_dst; 86 daddr = rt->rt_dst;
83 87
84 if (inet->inet_saddr == 0) 88 if (inet->inet_saddr == 0)
@@ -89,8 +93,8 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
89 inet->inet_daddr = daddr; 93 inet->inet_daddr = daddr;
90 94
91 inet_csk(sk)->icsk_ext_hdr_len = 0; 95 inet_csk(sk)->icsk_ext_hdr_len = 0;
92 if (inet->opt != NULL) 96 if (inet_opt)
93 inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen; 97 inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen;
94 /* 98 /*
95 * Socket identity is still unknown (sport may be zero). 99 * Socket identity is still unknown (sport may be zero).
96 * However we set state to DCCP_REQUESTING and not releasing socket 100 * However we set state to DCCP_REQUESTING and not releasing socket
@@ -405,7 +409,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb,
405 newinet->inet_daddr = ireq->rmt_addr; 409 newinet->inet_daddr = ireq->rmt_addr;
406 newinet->inet_rcv_saddr = ireq->loc_addr; 410 newinet->inet_rcv_saddr = ireq->loc_addr;
407 newinet->inet_saddr = ireq->loc_addr; 411 newinet->inet_saddr = ireq->loc_addr;
408 newinet->opt = ireq->opt; 412 newinet->inet_opt = ireq->opt;
409 ireq->opt = NULL; 413 ireq->opt = NULL;
410 newinet->mc_index = inet_iif(skb); 414 newinet->mc_index = inet_iif(skb);
411 newinet->mc_ttl = ip_hdr(skb)->ttl; 415 newinet->mc_ttl = ip_hdr(skb)->ttl;