diff options
Diffstat (limited to 'net/ipv4/ip_sockglue.c')
-rw-r--r-- | net/ipv4/ip_sockglue.c | 45 |
1 files changed, 34 insertions, 11 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index ce231780a2b1..6c40a8c46e79 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -239,7 +239,16 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) | |||
239 | sent to multicast group to reach destination designated router. | 239 | sent to multicast group to reach destination designated router. |
240 | */ | 240 | */ |
241 | struct ip_ra_chain *ip_ra_chain; | 241 | struct ip_ra_chain *ip_ra_chain; |
242 | DEFINE_RWLOCK(ip_ra_lock); | 242 | static DEFINE_SPINLOCK(ip_ra_lock); |
243 | |||
244 | |||
245 | static void ip_ra_destroy_rcu(struct rcu_head *head) | ||
246 | { | ||
247 | struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu); | ||
248 | |||
249 | sock_put(ra->saved_sk); | ||
250 | kfree(ra); | ||
251 | } | ||
243 | 252 | ||
244 | int ip_ra_control(struct sock *sk, unsigned char on, | 253 | int ip_ra_control(struct sock *sk, unsigned char on, |
245 | void (*destructor)(struct sock *)) | 254 | void (*destructor)(struct sock *)) |
@@ -251,35 +260,42 @@ int ip_ra_control(struct sock *sk, unsigned char on, | |||
251 | 260 | ||
252 | new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; | 261 | new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; |
253 | 262 | ||
254 | write_lock_bh(&ip_ra_lock); | 263 | spin_lock_bh(&ip_ra_lock); |
255 | for (rap = &ip_ra_chain; (ra = *rap) != NULL; rap = &ra->next) { | 264 | for (rap = &ip_ra_chain; (ra = *rap) != NULL; rap = &ra->next) { |
256 | if (ra->sk == sk) { | 265 | if (ra->sk == sk) { |
257 | if (on) { | 266 | if (on) { |
258 | write_unlock_bh(&ip_ra_lock); | 267 | spin_unlock_bh(&ip_ra_lock); |
259 | kfree(new_ra); | 268 | kfree(new_ra); |
260 | return -EADDRINUSE; | 269 | return -EADDRINUSE; |
261 | } | 270 | } |
262 | *rap = ra->next; | 271 | /* dont let ip_call_ra_chain() use sk again */ |
263 | write_unlock_bh(&ip_ra_lock); | 272 | ra->sk = NULL; |
273 | rcu_assign_pointer(*rap, ra->next); | ||
274 | spin_unlock_bh(&ip_ra_lock); | ||
264 | 275 | ||
265 | if (ra->destructor) | 276 | if (ra->destructor) |
266 | ra->destructor(sk); | 277 | ra->destructor(sk); |
267 | sock_put(sk); | 278 | /* |
268 | kfree(ra); | 279 | * Delay sock_put(sk) and kfree(ra) after one rcu grace |
280 | * period. This guarantee ip_call_ra_chain() dont need | ||
281 | * to mess with socket refcounts. | ||
282 | */ | ||
283 | ra->saved_sk = sk; | ||
284 | call_rcu(&ra->rcu, ip_ra_destroy_rcu); | ||
269 | return 0; | 285 | return 0; |
270 | } | 286 | } |
271 | } | 287 | } |
272 | if (new_ra == NULL) { | 288 | if (new_ra == NULL) { |
273 | write_unlock_bh(&ip_ra_lock); | 289 | spin_unlock_bh(&ip_ra_lock); |
274 | return -ENOBUFS; | 290 | return -ENOBUFS; |
275 | } | 291 | } |
276 | new_ra->sk = sk; | 292 | new_ra->sk = sk; |
277 | new_ra->destructor = destructor; | 293 | new_ra->destructor = destructor; |
278 | 294 | ||
279 | new_ra->next = ra; | 295 | new_ra->next = ra; |
280 | *rap = new_ra; | 296 | rcu_assign_pointer(*rap, new_ra); |
281 | sock_hold(sk); | 297 | sock_hold(sk); |
282 | write_unlock_bh(&ip_ra_lock); | 298 | spin_unlock_bh(&ip_ra_lock); |
283 | 299 | ||
284 | return 0; | 300 | return 0; |
285 | } | 301 | } |
@@ -449,7 +465,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
449 | (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | | 465 | (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | |
450 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | | 466 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | |
451 | (1<<IP_PASSSEC) | (1<<IP_TRANSPARENT) | | 467 | (1<<IP_PASSSEC) | (1<<IP_TRANSPARENT) | |
452 | (1<<IP_MINTTL))) || | 468 | (1<<IP_MINTTL) | (1<<IP_NODEFRAG))) || |
453 | optname == IP_MULTICAST_TTL || | 469 | optname == IP_MULTICAST_TTL || |
454 | optname == IP_MULTICAST_ALL || | 470 | optname == IP_MULTICAST_ALL || |
455 | optname == IP_MULTICAST_LOOP || | 471 | optname == IP_MULTICAST_LOOP || |
@@ -572,6 +588,13 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
572 | } | 588 | } |
573 | inet->hdrincl = val ? 1 : 0; | 589 | inet->hdrincl = val ? 1 : 0; |
574 | break; | 590 | break; |
591 | case IP_NODEFRAG: | ||
592 | if (sk->sk_type != SOCK_RAW) { | ||
593 | err = -ENOPROTOOPT; | ||
594 | break; | ||
595 | } | ||
596 | inet->nodefrag = val ? 1 : 0; | ||
597 | break; | ||
575 | case IP_MTU_DISCOVER: | 598 | case IP_MTU_DISCOVER: |
576 | if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_PROBE) | 599 | if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_PROBE) |
577 | goto e_inval; | 600 | goto e_inval; |