Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Nicolas Dichtel and committed by
David S. Miller
0c7aecd4 4de8b413

+245
+1
MAINTAINERS
··· 6578 6578 F: include/uapi/linux/in.h 6579 6579 F: include/uapi/linux/net.h 6580 6580 F: include/uapi/linux/netdevice.h 6581 + F: include/uapi/linux/net_namespace.h 6581 6582 F: tools/net/ 6582 6583 F: tools/testing/selftests/net/ 6583 6584 F: lib/random32.c
+4
include/net/net_namespace.h
··· 60 60 struct list_head exit_list; /* Use only net_mutex */ 61 61 62 62 struct user_namespace *user_ns; /* Owning user namespace */ 63 + struct idr netns_ids; 63 64 64 65 struct ns_common ns; 65 66 ··· 290 289 #define __net_initdata __initdata 291 290 #define __net_initconst __initconst 292 291 #endif 292 + 293 + int peernet2id(struct net *net, struct net *peer); 294 + struct net *get_net_ns_by_id(struct net *net, int id); 293 295 294 296 struct pernet_operations { 295 297 struct list_head list;
+1
include/uapi/linux/Kbuild
··· 283 283 header-y += netlink_diag.h 284 284 header-y += netlink.h 285 285 header-y += netrom.h 286 + header-y += net_namespace.h 286 287 header-y += net_tstamp.h 287 288 header-y += nfc.h 288 289 header-y += nfs2.h
+23
include/uapi/linux/net_namespace.h
··· 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_ */
+5
include/uapi/linux/rtnetlink.h
··· 132 132 RTM_GETMDB = 86, 133 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 140 __RTM_MAX, 136 141 #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) 137 142 };
+211
net/core/net_namespace.c
··· 15 15 #include <linux/file.h> 16 16 #include <linux/export.h> 17 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 22 #include <net/net_namespace.h> 19 23 #include <net/netns/generic.h> 20 24 ··· 148 144 } 149 145 } 150 146 147 + static int alloc_netid(struct net *net, struct net *peer, int reqid) 148 + { 149 + int min = 0, max = 0; 150 + 151 + ASSERT_RTNL(); 152 + 153 + if (reqid >= 0) { 154 + min = reqid; 155 + max = reqid + 1; 156 + } 157 + 158 + return idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL); 159 + } 160 + 161 + /* This function is used by idr_for_each(). If net is equal to peer, the 162 + * function returns the id so that idr_for_each() stops. Because we cannot 163 + * returns the id 0 (idr_for_each() will not stop), we return the magic value 164 + * NET_ID_ZERO (-1) for it. 165 + */ 166 + #define NET_ID_ZERO -1 167 + static int net_eq_idr(int id, void *net, void *peer) 168 + { 169 + if (net_eq(net, peer)) 170 + return id ? : NET_ID_ZERO; 171 + return 0; 172 + } 173 + 174 + static int __peernet2id(struct net *net, struct net *peer, bool alloc) 175 + { 176 + int id = idr_for_each(&net->netns_ids, net_eq_idr, peer); 177 + 178 + ASSERT_RTNL(); 179 + 180 + /* Magic value for id 0. */ 181 + if (id == NET_ID_ZERO) 182 + return 0; 183 + if (id > 0) 184 + return id; 185 + 186 + if (alloc) 187 + return alloc_netid(net, peer, -1); 188 + 189 + return -ENOENT; 190 + } 191 + 192 + /* This function returns the id of a peer netns. If no id is assigned, one will 193 + * be allocated and returned. 194 + */ 195 + int peernet2id(struct net *net, struct net *peer) 196 + { 197 + int id = __peernet2id(net, peer, true); 198 + 199 + return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED; 200 + } 201 + 202 + struct net *get_net_ns_by_id(struct net *net, int id) 203 + { 204 + struct net *peer; 205 + 206 + if (id < 0) 207 + return NULL; 208 + 209 + rcu_read_lock(); 210 + peer = idr_find(&net->netns_ids, id); 211 + if (peer) 212 + get_net(peer); 213 + rcu_read_unlock(); 214 + 215 + return peer; 216 + } 217 + 151 218 /* 152 219 * setup_net runs the initializers for the network namespace object. 153 220 */ ··· 233 158 atomic_set(&net->passive, 1); 234 159 net->dev_base_seq = 1; 235 160 net->user_ns = user_ns; 161 + idr_init(&net->netns_ids); 236 162 237 163 #ifdef NETNS_REFCNT_DEBUG 238 164 atomic_set(&net->use_count, 0); ··· 364 288 list_for_each_entry(net, &net_kill_list, cleanup_list) { 365 289 list_del_rcu(&net->list); 366 290 list_add_tail(&net->exit_list, &net_exit_list); 291 + for_each_net(tmp) { 292 + int id = __peernet2id(tmp, net, false); 293 + 294 + if (id >= 0) 295 + idr_remove(&tmp->netns_ids, id); 296 + } 297 + idr_destroy(&net->netns_ids); 298 + 367 299 } 368 300 rtnl_unlock(); 369 301 ··· 486 402 .exit = net_ns_net_exit, 487 403 }; 488 404 405 + static struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = { 406 + [NETNSA_NONE] = { .type = NLA_UNSPEC }, 407 + [NETNSA_NSID] = { .type = NLA_S32 }, 408 + [NETNSA_PID] = { .type = NLA_U32 }, 409 + [NETNSA_FD] = { .type = NLA_U32 }, 410 + }; 411 + 412 + static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh) 413 + { 414 + struct net *net = sock_net(skb->sk); 415 + struct nlattr *tb[NETNSA_MAX + 1]; 416 + struct net *peer; 417 + int nsid, err; 418 + 419 + err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, 420 + rtnl_net_policy); 421 + if (err < 0) 422 + return err; 423 + if (!tb[NETNSA_NSID]) 424 + return -EINVAL; 425 + nsid = nla_get_s32(tb[NETNSA_NSID]); 426 + 427 + if (tb[NETNSA_PID]) 428 + peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID])); 429 + else if (tb[NETNSA_FD]) 430 + peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD])); 431 + else 432 + return -EINVAL; 433 + if (IS_ERR(peer)) 434 + return PTR_ERR(peer); 435 + 436 + if (__peernet2id(net, peer, false) >= 0) { 437 + err = -EEXIST; 438 + goto out; 439 + } 440 + 441 + err = alloc_netid(net, peer, nsid); 442 + if (err > 0) 443 + err = 0; 444 + out: 445 + put_net(peer); 446 + return err; 447 + } 448 + 449 + static int rtnl_net_get_size(void) 450 + { 451 + return NLMSG_ALIGN(sizeof(struct rtgenmsg)) 452 + + nla_total_size(sizeof(s32)) /* NETNSA_NSID */ 453 + ; 454 + } 455 + 456 + static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags, 457 + int cmd, struct net *net, struct net *peer) 458 + { 459 + struct nlmsghdr *nlh; 460 + struct rtgenmsg *rth; 461 + int id; 462 + 463 + ASSERT_RTNL(); 464 + 465 + nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rth), flags); 466 + if (!nlh) 467 + return -EMSGSIZE; 468 + 469 + rth = nlmsg_data(nlh); 470 + rth->rtgen_family = AF_UNSPEC; 471 + 472 + id = __peernet2id(net, peer, false); 473 + if (id < 0) 474 + id = NETNSA_NSID_NOT_ASSIGNED; 475 + if (nla_put_s32(skb, NETNSA_NSID, id)) 476 + goto nla_put_failure; 477 + 478 + nlmsg_end(skb, nlh); 479 + return 0; 480 + 481 + nla_put_failure: 482 + nlmsg_cancel(skb, nlh); 483 + return -EMSGSIZE; 484 + } 485 + 486 + static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh) 487 + { 488 + struct net *net = sock_net(skb->sk); 489 + struct nlattr *tb[NETNSA_MAX + 1]; 490 + struct sk_buff *msg; 491 + int err = -ENOBUFS; 492 + struct net *peer; 493 + 494 + err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, 495 + rtnl_net_policy); 496 + if (err < 0) 497 + return err; 498 + if (tb[NETNSA_PID]) 499 + peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID])); 500 + else if (tb[NETNSA_FD]) 501 + peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD])); 502 + else 503 + return -EINVAL; 504 + 505 + if (IS_ERR(peer)) 506 + return PTR_ERR(peer); 507 + 508 + msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL); 509 + if (!msg) { 510 + err = -ENOMEM; 511 + goto out; 512 + } 513 + 514 + err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0, 515 + RTM_GETNSID, net, peer); 516 + if (err < 0) 517 + goto err_out; 518 + 519 + err = rtnl_unicast(msg, net, NETLINK_CB(skb).portid); 520 + goto out; 521 + 522 + err_out: 523 + nlmsg_free(msg); 524 + out: 525 + put_net(peer); 526 + return err; 527 + } 528 + 489 529 static int __init net_ns_init(void) 490 530 { 491 531 struct net_generic *ng; ··· 642 434 mutex_unlock(&net_mutex); 643 435 644 436 register_pernet_subsys(&net_ns_ops); 437 + 438 + rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL, NULL); 439 + rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, NULL, NULL); 645 440 646 441 return 0; 647 442 }