aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorNicolas Dichtel <nicolas.dichtel@6wind.com>2015-01-15 09:11:15 -0500
committerDavid S. Miller <davem@davemloft.net>2015-01-19 14:21:18 -0500
commit0c7aecd4bde4b7302cd41986d3a29e4f0b0ed218 (patch)
tree2c4ba6eda5392449a2c9019aafd061324e242dbc /net/core
parent4de8b413700e78560388eb14c4bbc67aff62da6d (diff)
netns: add rtnl cmd to add and get peer netns ids
With this patch, a user can define an id for a peer netns by providing a FD or a PID. These ids are local to the netns where it is added (ie valid only into this netns). The main function (ie the one exported to other module), peernet2id(), allows to get the id of a peer netns. If no id has been assigned by the user, this function allocates one. These ids will be used in netlink messages to point to a peer netns, for example in case of a x-netns interface. Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/net_namespace.c211
1 files changed, 211 insertions, 0 deletions
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
151static 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
171static 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
178static 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 */
199int 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
206struct 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
489static 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
496static 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;
528out:
529 put_net(peer);
530 return err;
531}
532
533static 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
540static 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
565nla_put_failure:
566 nlmsg_cancel(skb, nlh);
567 return -EMSGSIZE;
568}
569
570static 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
606err_out:
607 nlmsg_free(msg);
608out:
609 put_net(peer);
610 return err;
611}
612
405static int __init net_ns_init(void) 613static 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