diff options
-rw-r--r-- | include/net/sock.h | 13 | ||||
-rw-r--r-- | net/core/sock.c | 18 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 18 |
3 files changed, 33 insertions, 16 deletions
diff --git a/include/net/sock.h b/include/net/sock.h index fd9876087651..39112e75411c 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -850,6 +850,7 @@ extern struct sock *sk_alloc(struct net *net, int family, | |||
850 | gfp_t priority, | 850 | gfp_t priority, |
851 | struct proto *prot); | 851 | struct proto *prot); |
852 | extern void sk_free(struct sock *sk); | 852 | extern void sk_free(struct sock *sk); |
853 | extern void sk_release_kernel(struct sock *sk); | ||
853 | extern struct sock *sk_clone(const struct sock *sk, | 854 | extern struct sock *sk_clone(const struct sock *sk, |
854 | const gfp_t priority); | 855 | const gfp_t priority); |
855 | 856 | ||
@@ -1333,6 +1334,18 @@ static inline void sk_eat_skb(struct sock *sk, struct sk_buff *skb, int copied_e | |||
1333 | } | 1334 | } |
1334 | #endif | 1335 | #endif |
1335 | 1336 | ||
1337 | /* | ||
1338 | * Kernel sockets, f.e. rtnl or icmp_socket, are a part of a namespace. | ||
1339 | * They should not hold a referrence to a namespace in order to allow | ||
1340 | * to stop it. | ||
1341 | * Sockets after sk_change_net should be released using sk_release_kernel | ||
1342 | */ | ||
1343 | static inline void sk_change_net(struct sock *sk, struct net *net) | ||
1344 | { | ||
1345 | put_net(sk->sk_net); | ||
1346 | sk->sk_net = net; | ||
1347 | } | ||
1348 | |||
1336 | extern void sock_enable_timestamp(struct sock *sk); | 1349 | extern void sock_enable_timestamp(struct sock *sk); |
1337 | extern int sock_get_timestamp(struct sock *, struct timeval __user *); | 1350 | extern int sock_get_timestamp(struct sock *, struct timeval __user *); |
1338 | extern int sock_get_timestampns(struct sock *, struct timespec __user *); | 1351 | extern int sock_get_timestampns(struct sock *, struct timespec __user *); |
diff --git a/net/core/sock.c b/net/core/sock.c index 09cb3a74de7f..c71b645a78f0 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -987,6 +987,24 @@ void sk_free(struct sock *sk) | |||
987 | sk_prot_free(sk->sk_prot_creator, sk); | 987 | sk_prot_free(sk->sk_prot_creator, sk); |
988 | } | 988 | } |
989 | 989 | ||
990 | /* | ||
991 | * Last sock_put should drop referrence to sk->sk_net. It has already | ||
992 | * been dropped in sk_change_net. Taking referrence to stopping namespace | ||
993 | * is not an option. | ||
994 | * Take referrence to a socket to remove it from hash _alive_ and after that | ||
995 | * destroy it in the context of init_net. | ||
996 | */ | ||
997 | void sk_release_kernel(struct sock *sk) | ||
998 | { | ||
999 | if (sk == NULL || sk->sk_socket == NULL) | ||
1000 | return; | ||
1001 | |||
1002 | sock_hold(sk); | ||
1003 | sock_release(sk->sk_socket); | ||
1004 | sk->sk_net = get_net(&init_net); | ||
1005 | sock_put(sk); | ||
1006 | } | ||
1007 | |||
990 | struct sock *sk_clone(const struct sock *sk, const gfp_t priority) | 1008 | struct sock *sk_clone(const struct sock *sk, const gfp_t priority) |
991 | { | 1009 | { |
992 | struct sock *newsk; | 1010 | struct sock *newsk; |
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index e6b636d53633..524e826bb976 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -1372,8 +1372,7 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups, | |||
1372 | goto out_sock_release_nosk; | 1372 | goto out_sock_release_nosk; |
1373 | 1373 | ||
1374 | sk = sock->sk; | 1374 | sk = sock->sk; |
1375 | put_net(sk->sk_net); | 1375 | sk_change_net(sk, net); |
1376 | sk->sk_net = net; | ||
1377 | 1376 | ||
1378 | if (groups < 32) | 1377 | if (groups < 32) |
1379 | groups = 32; | 1378 | groups = 32; |
@@ -1421,20 +1420,7 @@ EXPORT_SYMBOL(netlink_kernel_create); | |||
1421 | void | 1420 | void |
1422 | netlink_kernel_release(struct sock *sk) | 1421 | netlink_kernel_release(struct sock *sk) |
1423 | { | 1422 | { |
1424 | /* | 1423 | sk_release_kernel(sk); |
1425 | * Last sock_put should drop referrence to sk->sk_net. It has already | ||
1426 | * been dropped in netlink_kernel_create. Taking referrence to stopping | ||
1427 | * namespace is not an option. | ||
1428 | * Take referrence to a socket to remove it from netlink lookup table | ||
1429 | * _alive_ and after that destroy it in the context of init_net. | ||
1430 | */ | ||
1431 | if (sk == NULL || sk->sk_socket == NULL) | ||
1432 | return; | ||
1433 | |||
1434 | sock_hold(sk); | ||
1435 | sock_release(sk->sk_socket); | ||
1436 | sk->sk_net = get_net(&init_net); | ||
1437 | sock_put(sk); | ||
1438 | } | 1424 | } |
1439 | EXPORT_SYMBOL(netlink_kernel_release); | 1425 | EXPORT_SYMBOL(netlink_kernel_release); |
1440 | 1426 | ||