Skip to content

Commit 0c7aecd

Browse files
NicolasDichteldavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4de8b41 commit 0c7aecd

File tree

6 files changed

+245
-0
lines changed

6 files changed

+245
-0
lines changed

MAINTAINERS

+1
Original file line numberDiff line numberDiff line change
@@ -6578,6 +6578,7 @@ F: include/linux/netdevice.h
65786578
F: include/uapi/linux/in.h
65796579
F: include/uapi/linux/net.h
65806580
F: include/uapi/linux/netdevice.h
6581+
F: include/uapi/linux/net_namespace.h
65816582
F: tools/net/
65826583
F: tools/testing/selftests/net/
65836584
F: lib/random32.c

include/net/net_namespace.h

+4
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ struct net {
6060
struct list_head exit_list; /* Use only net_mutex */
6161

6262
struct user_namespace *user_ns; /* Owning user namespace */
63+
struct idr netns_ids;
6364

6465
struct ns_common ns;
6566

@@ -290,6 +291,9 @@ static inline struct net *read_pnet(struct net * const *pnet)
290291
#define __net_initconst __initconst
291292
#endif
292293

294+
int peernet2id(struct net *net, struct net *peer);
295+
struct net *get_net_ns_by_id(struct net *net, int id);
296+
293297
struct pernet_operations {
294298
struct list_head list;
295299
int (*init)(struct net *net);

include/uapi/linux/Kbuild

+1
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ header-y += net.h
283283
header-y += netlink_diag.h
284284
header-y += netlink.h
285285
header-y += netrom.h
286+
header-y += net_namespace.h
286287
header-y += net_tstamp.h
287288
header-y += nfc.h
288289
header-y += nfs2.h

include/uapi/linux/net_namespace.h

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* Copyright (c) 2015 6WIND S.A.
2+
* Author: Nicolas Dichtel <[email protected]>
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_ */

include/uapi/linux/rtnetlink.h

+5
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ enum {
132132
RTM_GETMDB = 86,
133133
#define RTM_GETMDB RTM_GETMDB
134134

135+
RTM_NEWNSID = 88,
136+
#define RTM_NEWNSID RTM_NEWNSID
137+
RTM_GETNSID = 90,
138+
#define RTM_GETNSID RTM_GETNSID
139+
135140
__RTM_MAX,
136141
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
137142
};

net/core/net_namespace.c

+211
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
#include <linux/file.h>
1616
#include <linux/export.h>
1717
#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>
1822
#include <net/net_namespace.h>
1923
#include <net/netns/generic.h>
2024

@@ -144,6 +148,77 @@ static void ops_free_list(const struct pernet_operations *ops,
144148
}
145149
}
146150

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+
147222
/*
148223
* setup_net runs the initializers for the network namespace object.
149224
*/
@@ -158,6 +233,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
158233
atomic_set(&net->passive, 1);
159234
net->dev_base_seq = 1;
160235
net->user_ns = user_ns;
236+
idr_init(&net->netns_ids);
161237

162238
#ifdef NETNS_REFCNT_DEBUG
163239
atomic_set(&net->use_count, 0);
@@ -288,6 +364,14 @@ static void cleanup_net(struct work_struct *work)
288364
list_for_each_entry(net, &net_kill_list, cleanup_list) {
289365
list_del_rcu(&net->list);
290366
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+
291375
}
292376
rtnl_unlock();
293377

@@ -402,6 +486,130 @@ static struct pernet_operations __net_initdata net_ns_ops = {
402486
.exit = net_ns_net_exit,
403487
};
404488

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+
405613
static int __init net_ns_init(void)
406614
{
407615
struct net_generic *ng;
@@ -435,6 +643,9 @@ static int __init net_ns_init(void)
435643

436644
register_pernet_subsys(&net_ns_ops);
437645

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+
438649
return 0;
439650
}
440651

0 commit comments

Comments
 (0)