diff options
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | include/net/net_namespace.h | 4 | ||||
-rw-r--r-- | include/uapi/linux/Kbuild | 1 | ||||
-rw-r--r-- | include/uapi/linux/net_namespace.h | 23 | ||||
-rw-r--r-- | include/uapi/linux/rtnetlink.h | 5 | ||||
-rw-r--r-- | net/core/net_namespace.c | 211 |
6 files changed, 245 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 9de900572633..9b91d9f0257e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -6578,6 +6578,7 @@ F: include/linux/netdevice.h | |||
6578 | F: include/uapi/linux/in.h | 6578 | F: include/uapi/linux/in.h |
6579 | F: include/uapi/linux/net.h | 6579 | F: include/uapi/linux/net.h |
6580 | F: include/uapi/linux/netdevice.h | 6580 | F: include/uapi/linux/netdevice.h |
6581 | F: include/uapi/linux/net_namespace.h | ||
6581 | F: tools/net/ | 6582 | F: tools/net/ |
6582 | F: tools/testing/selftests/net/ | 6583 | F: tools/testing/selftests/net/ |
6583 | F: lib/random32.c | 6584 | F: lib/random32.c |
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 2e8756b8c775..36faf4990c4b 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h | |||
@@ -60,6 +60,7 @@ struct net { | |||
60 | struct list_head exit_list; /* Use only net_mutex */ | 60 | struct list_head exit_list; /* Use only net_mutex */ |
61 | 61 | ||
62 | struct user_namespace *user_ns; /* Owning user namespace */ | 62 | struct user_namespace *user_ns; /* Owning user namespace */ |
63 | struct idr netns_ids; | ||
63 | 64 | ||
64 | struct ns_common ns; | 65 | struct ns_common ns; |
65 | 66 | ||
@@ -290,6 +291,9 @@ static inline struct net *read_pnet(struct net * const *pnet) | |||
290 | #define __net_initconst __initconst | 291 | #define __net_initconst __initconst |
291 | #endif | 292 | #endif |
292 | 293 | ||
294 | int peernet2id(struct net *net, struct net *peer); | ||
295 | struct net *get_net_ns_by_id(struct net *net, int id); | ||
296 | |||
293 | struct pernet_operations { | 297 | struct pernet_operations { |
294 | struct list_head list; | 298 | struct list_head list; |
295 | int (*init)(struct net *net); | 299 | int (*init)(struct net *net); |
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 00b100023c47..14b7b6e44c77 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild | |||
@@ -283,6 +283,7 @@ header-y += net.h | |||
283 | header-y += netlink_diag.h | 283 | header-y += netlink_diag.h |
284 | header-y += netlink.h | 284 | header-y += netlink.h |
285 | header-y += netrom.h | 285 | header-y += netrom.h |
286 | header-y += net_namespace.h | ||
286 | header-y += net_tstamp.h | 287 | header-y += net_tstamp.h |
287 | header-y += nfc.h | 288 | header-y += nfc.h |
288 | header-y += nfs2.h | 289 | header-y += nfs2.h |
diff --git a/include/uapi/linux/net_namespace.h b/include/uapi/linux/net_namespace.h new file mode 100644 index 000000000000..778cd2c3ebf4 --- /dev/null +++ b/include/uapi/linux/net_namespace.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* Copyright (c) 2015 6WIND S.A. | ||
2 | * Author: Nicolas Dichtel <nicolas.dichtel@6wind.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | */ | ||
8 | #ifndef _UAPI_LINUX_NET_NAMESPACE_H_ | ||
9 | #define _UAPI_LINUX_NET_NAMESPACE_H_ | ||
10 | |||
11 | /* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ | ||
12 | enum { | ||
13 | NETNSA_NONE, | ||
14 | #define NETNSA_NSID_NOT_ASSIGNED -1 | ||
15 | NETNSA_NSID, | ||
16 | NETNSA_PID, | ||
17 | NETNSA_FD, | ||
18 | __NETNSA_MAX, | ||
19 | }; | ||
20 | |||
21 | #define NETNSA_MAX (__NETNSA_MAX - 1) | ||
22 | |||
23 | #endif /* _UAPI_LINUX_NET_NAMESPACE_H_ */ | ||
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index a1d18593f41e..5cc5d66bf519 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h | |||
@@ -132,6 +132,11 @@ enum { | |||
132 | RTM_GETMDB = 86, | 132 | RTM_GETMDB = 86, |
133 | #define RTM_GETMDB RTM_GETMDB | 133 | #define RTM_GETMDB RTM_GETMDB |
134 | 134 | ||
135 | RTM_NEWNSID = 88, | ||
136 | #define RTM_NEWNSID RTM_NEWNSID | ||
137 | RTM_GETNSID = 90, | ||
138 | #define RTM_GETNSID RTM_GETNSID | ||
139 | |||
135 | __RTM_MAX, | 140 | __RTM_MAX, |
136 | #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) | 141 | #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) |
137 | }; | 142 | }; |
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index ce780c722e48..9d1a4cac83b6 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c | |||
@@ -15,6 +15,10 @@ | |||
15 | #include <linux/file.h> | 15 | #include <linux/file.h> |
16 | #include <linux/export.h> | 16 | #include <linux/export.h> |
17 | #include <linux/user_namespace.h> | 17 | #include <linux/user_namespace.h> |
18 | #include <linux/net_namespace.h> | ||
19 | #include <linux/rtnetlink.h> | ||
20 | #include <net/sock.h> | ||
21 | #include <net/netlink.h> | ||
18 | #include <net/net_namespace.h> | 22 | #include <net/net_namespace.h> |
19 | #include <net/netns/generic.h> | 23 | #include <net/netns/generic.h> |
20 | 24 | ||
@@ -144,6 +148,77 @@ static void ops_free_list(const struct pernet_operations *ops, | |||
144 | } | 148 | } |
145 | } | 149 | } |
146 | 150 | ||
151 | static int alloc_netid(struct net *net, struct net *peer, int reqid) | ||
152 | { | ||
153 | int min = 0, max = 0; | ||
154 | |||
155 | ASSERT_RTNL(); | ||
156 | |||
157 | if (reqid >= 0) { | ||
158 | min = reqid; | ||
159 | max = reqid + 1; | ||
160 | } | ||
161 | |||
162 | return idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL); | ||
163 | } | ||
164 | |||
165 | /* This function is used by idr_for_each(). If net is equal to peer, the | ||
166 | * function returns the id so that idr_for_each() stops. Because we cannot | ||
167 | * returns the id 0 (idr_for_each() will not stop), we return the magic value | ||
168 | * NET_ID_ZERO (-1) for it. | ||
169 | */ | ||
170 | #define NET_ID_ZERO -1 | ||
171 | static int net_eq_idr(int id, void *net, void *peer) | ||
172 | { | ||
173 | if (net_eq(net, peer)) | ||
174 | return id ? : NET_ID_ZERO; | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static int __peernet2id(struct net *net, struct net *peer, bool alloc) | ||
179 | { | ||
180 | int id = idr_for_each(&net->netns_ids, net_eq_idr, peer); | ||
181 | |||
182 | ASSERT_RTNL(); | ||
183 | |||
184 | /* Magic value for id 0. */ | ||
185 | if (id == NET_ID_ZERO) | ||
186 | return 0; | ||
187 | if (id > 0) | ||
188 | return id; | ||
189 | |||
190 | if (alloc) | ||
191 | return alloc_netid(net, peer, -1); | ||
192 | |||
193 | return -ENOENT; | ||
194 | } | ||
195 | |||
196 | /* This function returns the id of a peer netns. If no id is assigned, one will | ||
197 | * be allocated and returned. | ||
198 | */ | ||
199 | int peernet2id(struct net *net, struct net *peer) | ||
200 | { | ||
201 | int id = __peernet2id(net, peer, true); | ||
202 | |||
203 | return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED; | ||
204 | } | ||
205 | |||
206 | struct net *get_net_ns_by_id(struct net *net, int id) | ||
207 | { | ||
208 | struct net *peer; | ||
209 | |||
210 | if (id < 0) | ||
211 | return NULL; | ||
212 | |||
213 | rcu_read_lock(); | ||
214 | peer = idr_find(&net->netns_ids, id); | ||
215 | if (peer) | ||
216 | get_net(peer); | ||
217 | rcu_read_unlock(); | ||
218 | |||
219 | return peer; | ||
220 | } | ||
221 | |||
147 | /* | 222 | /* |
148 | * setup_net runs the initializers for the network namespace object. | 223 | * setup_net runs the initializers for the network namespace object. |
149 | */ | 224 | */ |
@@ -158,6 +233,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) | |||
158 | atomic_set(&net->passive, 1); | 233 | atomic_set(&net->passive, 1); |
159 | net->dev_base_seq = 1; | 234 | net->dev_base_seq = 1; |
160 | net->user_ns = user_ns; | 235 | net->user_ns = user_ns; |
236 | idr_init(&net->netns_ids); | ||
161 | 237 | ||
162 | #ifdef NETNS_REFCNT_DEBUG | 238 | #ifdef NETNS_REFCNT_DEBUG |
163 | atomic_set(&net->use_count, 0); | 239 | atomic_set(&net->use_count, 0); |
@@ -288,6 +364,14 @@ static void cleanup_net(struct work_struct *work) | |||
288 | list_for_each_entry(net, &net_kill_list, cleanup_list) { | 364 | list_for_each_entry(net, &net_kill_list, cleanup_list) { |
289 | list_del_rcu(&net->list); | 365 | list_del_rcu(&net->list); |
290 | list_add_tail(&net->exit_list, &net_exit_list); | 366 | list_add_tail(&net->exit_list, &net_exit_list); |
367 | for_each_net(tmp) { | ||
368 | int id = __peernet2id(tmp, net, false); | ||
369 | |||
370 | if (id >= 0) | ||
371 | idr_remove(&tmp->netns_ids, id); | ||
372 | } | ||
373 | idr_destroy(&net->netns_ids); | ||
374 | |||
291 | } | 375 | } |
292 | rtnl_unlock(); | 376 | rtnl_unlock(); |
293 | 377 | ||
@@ -402,6 +486,130 @@ static struct pernet_operations __net_initdata net_ns_ops = { | |||
402 | .exit = net_ns_net_exit, | 486 | .exit = net_ns_net_exit, |
403 | }; | 487 | }; |
404 | 488 | ||
489 | static struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = { | ||
490 | [NETNSA_NONE] = { .type = NLA_UNSPEC }, | ||
491 | [NETNSA_NSID] = { .type = NLA_S32 }, | ||
492 | [NETNSA_PID] = { .type = NLA_U32 }, | ||
493 | [NETNSA_FD] = { .type = NLA_U32 }, | ||
494 | }; | ||
495 | |||
496 | static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh) | ||
497 | { | ||
498 | struct net *net = sock_net(skb->sk); | ||
499 | struct nlattr *tb[NETNSA_MAX + 1]; | ||
500 | struct net *peer; | ||
501 | int nsid, err; | ||
502 | |||
503 | err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, | ||
504 | rtnl_net_policy); | ||
505 | if (err < 0) | ||
506 | return err; | ||
507 | if (!tb[NETNSA_NSID]) | ||
508 | return -EINVAL; | ||
509 | nsid = nla_get_s32(tb[NETNSA_NSID]); | ||
510 | |||
511 | if (tb[NETNSA_PID]) | ||
512 | peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID])); | ||
513 | else if (tb[NETNSA_FD]) | ||
514 | peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD])); | ||
515 | else | ||
516 | return -EINVAL; | ||
517 | if (IS_ERR(peer)) | ||
518 | return PTR_ERR(peer); | ||
519 | |||
520 | if (__peernet2id(net, peer, false) >= 0) { | ||
521 | err = -EEXIST; | ||
522 | goto out; | ||
523 | } | ||
524 | |||
525 | err = alloc_netid(net, peer, nsid); | ||
526 | if (err > 0) | ||
527 | err = 0; | ||
528 | out: | ||
529 | put_net(peer); | ||
530 | return err; | ||
531 | } | ||
532 | |||
533 | static int rtnl_net_get_size(void) | ||
534 | { | ||
535 | return NLMSG_ALIGN(sizeof(struct rtgenmsg)) | ||
536 | + nla_total_size(sizeof(s32)) /* NETNSA_NSID */ | ||
537 | ; | ||
538 | } | ||
539 | |||
540 | static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags, | ||
541 | int cmd, struct net *net, struct net *peer) | ||
542 | { | ||
543 | struct nlmsghdr *nlh; | ||
544 | struct rtgenmsg *rth; | ||
545 | int id; | ||
546 | |||
547 | ASSERT_RTNL(); | ||
548 | |||
549 | nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rth), flags); | ||
550 | if (!nlh) | ||
551 | return -EMSGSIZE; | ||
552 | |||
553 | rth = nlmsg_data(nlh); | ||
554 | rth->rtgen_family = AF_UNSPEC; | ||
555 | |||
556 | id = __peernet2id(net, peer, false); | ||
557 | if (id < 0) | ||
558 | id = NETNSA_NSID_NOT_ASSIGNED; | ||
559 | if (nla_put_s32(skb, NETNSA_NSID, id)) | ||
560 | goto nla_put_failure; | ||
561 | |||
562 | nlmsg_end(skb, nlh); | ||
563 | return 0; | ||
564 | |||
565 | nla_put_failure: | ||
566 | nlmsg_cancel(skb, nlh); | ||
567 | return -EMSGSIZE; | ||
568 | } | ||
569 | |||
570 | static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh) | ||
571 | { | ||
572 | struct net *net = sock_net(skb->sk); | ||
573 | struct nlattr *tb[NETNSA_MAX + 1]; | ||
574 | struct sk_buff *msg; | ||
575 | int err = -ENOBUFS; | ||
576 | struct net *peer; | ||
577 | |||
578 | err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, | ||
579 | rtnl_net_policy); | ||
580 | if (err < 0) | ||
581 | return err; | ||
582 | if (tb[NETNSA_PID]) | ||
583 | peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID])); | ||
584 | else if (tb[NETNSA_FD]) | ||
585 | peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD])); | ||
586 | else | ||
587 | return -EINVAL; | ||
588 | |||
589 | if (IS_ERR(peer)) | ||
590 | return PTR_ERR(peer); | ||
591 | |||
592 | msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL); | ||
593 | if (!msg) { | ||
594 | err = -ENOMEM; | ||
595 | goto out; | ||
596 | } | ||
597 | |||
598 | err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0, | ||
599 | RTM_GETNSID, net, peer); | ||
600 | if (err < 0) | ||
601 | goto err_out; | ||
602 | |||
603 | err = rtnl_unicast(msg, net, NETLINK_CB(skb).portid); | ||
604 | goto out; | ||
605 | |||
606 | err_out: | ||
607 | nlmsg_free(msg); | ||
608 | out: | ||
609 | put_net(peer); | ||
610 | return err; | ||
611 | } | ||
612 | |||
405 | static int __init net_ns_init(void) | 613 | static int __init net_ns_init(void) |
406 | { | 614 | { |
407 | struct net_generic *ng; | 615 | struct net_generic *ng; |
@@ -435,6 +643,9 @@ static int __init net_ns_init(void) | |||
435 | 643 | ||
436 | register_pernet_subsys(&net_ns_ops); | 644 | register_pernet_subsys(&net_ns_ops); |
437 | 645 | ||
646 | rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL, NULL); | ||
647 | rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, NULL, NULL); | ||
648 | |||
438 | return 0; | 649 | return 0; |
439 | } | 650 | } |
440 | 651 | ||