aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/af_inet.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/af_inet.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/af_inet.c')
-rw-r--r--net/ipv4/af_inet.c17
1 files changed, 12 insertions, 5 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 0413af3e2285..963a621e75c7 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -153,7 +153,7 @@ void inet_sock_destruct(struct sock *sk)
153 WARN_ON(sk->sk_wmem_queued); 153 WARN_ON(sk->sk_wmem_queued);
154 WARN_ON(sk->sk_forward_alloc); 154 WARN_ON(sk->sk_forward_alloc);
155 155
156 kfree(inet->opt); 156 kfree(rcu_dereference_protected(inet->inet_opt, 1));
157 dst_release(rcu_dereference_check(sk->sk_dst_cache, 1)); 157 dst_release(rcu_dereference_check(sk->sk_dst_cache, 1));
158 sk_refcnt_debug_dec(sk); 158 sk_refcnt_debug_dec(sk);
159} 159}
@@ -1106,9 +1106,12 @@ static int inet_sk_reselect_saddr(struct sock *sk)
1106 struct flowi4 fl4; 1106 struct flowi4 fl4;
1107 struct rtable *rt; 1107 struct rtable *rt;
1108 __be32 new_saddr; 1108 __be32 new_saddr;
1109 struct ip_options_rcu *inet_opt;
1109 1110
1110 if (inet->opt && inet->opt->srr) 1111 inet_opt = rcu_dereference_protected(inet->inet_opt,
1111 daddr = inet->opt->faddr; 1112 sock_owned_by_user(sk));
1113 if (inet_opt && inet_opt->opt.srr)
1114 daddr = inet_opt->opt.faddr;
1112 1115
1113 /* Query new route. */ 1116 /* Query new route. */
1114 rt = ip_route_connect(&fl4, daddr, 0, RT_CONN_FLAGS(sk), 1117 rt = ip_route_connect(&fl4, daddr, 0, RT_CONN_FLAGS(sk),
@@ -1148,6 +1151,7 @@ int inet_sk_rebuild_header(struct sock *sk)
1148 struct inet_sock *inet = inet_sk(sk); 1151 struct inet_sock *inet = inet_sk(sk);
1149 struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0); 1152 struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0);
1150 __be32 daddr; 1153 __be32 daddr;
1154 struct ip_options_rcu *inet_opt;
1151 int err; 1155 int err;
1152 1156
1153 /* Route is OK, nothing to do. */ 1157 /* Route is OK, nothing to do. */
@@ -1155,9 +1159,12 @@ int inet_sk_rebuild_header(struct sock *sk)
1155 return 0; 1159 return 0;
1156 1160
1157 /* Reroute. */ 1161 /* Reroute. */
1162 rcu_read_lock();
1163 inet_opt = rcu_dereference(inet->inet_opt);
1158 daddr = inet->inet_daddr; 1164 daddr = inet->inet_daddr;
1159 if (inet->opt && inet->opt->srr) 1165 if (inet_opt && inet_opt->opt.srr)
1160 daddr = inet->opt->faddr; 1166 daddr = inet_opt->opt.faddr;
1167 rcu_read_unlock();
1161 rt = ip_route_output_ports(sock_net(sk), sk, daddr, inet->inet_saddr, 1168 rt = ip_route_output_ports(sock_net(sk), sk, daddr, inet->inet_saddr,
1162 inet->inet_dport, inet->inet_sport, 1169 inet->inet_dport, inet->inet_sport,
1163 sk->sk_protocol, RT_CONN_FLAGS(sk), 1170 sk->sk_protocol, RT_CONN_FLAGS(sk),