Allow changing the transit namespace
v4 cover Allow changing the transit namespace
Hello list,
This series is simply v3 [1] rebased on master.
Julian
[1] https://lists.zx2c4.com/pipermail/wireguard/2018-September/003358.html
Julian Orth (12):
device: protect socket_init with device_update_lock
netlink: check for CAP_NET_ADMIN manually
netlink: allow specifying the device namespace
netlink: restrict access to the UDP socket
device: rename creating_net to transit_net
device: store a copy of the device net
socket: allow modification of transit_net
netlink: allow modification of transit net
tools: add framework for shared options
tools: allow specifying the device namespace
tools: allow modification of transit net
tests: add test for transit-net
src/device.c | 44 +++++++----- src/device.h | 6 +- src/netlink.c | 150 ++++++++++++++++++++++++++++++++-------- src/socket.c | 18 ++--- src/socket.h | 6 +- src/tests/netns.sh | 40 +++++++++++ src/tools/config.c | 8 +++ src/tools/containers.h | 22 +++++- src/tools/genkey.c | 3 +- src/tools/ipc.c | 26 +++++-- src/tools/ipc.h | 7 +- src/tools/man/wg.8 | 9 ++- src/tools/netns.c | 62 +++++++++++++++++ src/tools/netns.h | 18 +++++ src/tools/pubkey.c | 3 +- src/tools/set.c | 6 +- src/tools/setconf.c | 4 +- src/tools/show.c | 35 +++++++--- src/tools/showconf.c | 4 +- src/tools/subcommands.h | 14 ++-- src/tools/wg.c | 64 +++++++++++++++-- src/uapi/wireguard.h | 39 ++++++++++- 22 files changed, 483 insertions(+), 105 deletions(-) create mode 100644 src/tools/netns.c create mode 100644 src/tools/netns.h
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 1/12 device: protect socket_init with device_update_lock
set_port in netlink.c races with open in device.c. This can cause the following code flow:
* thread 1: set_port: device is not up
* thread 2: device is opened
* thread 2: open: called and calls socket_init with the original port
* thread 1: set_port: sets incoming_port to the new port and returns
incoming_port is then inconsistent. While this is not particularly critical, it will become more critial when ste_port also sets the transit namespace.
--- src/device.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
src/device.c +4 -3
@@ -53,17 +53,18 @@ static int open(struct net_device *dev)#endif #endif +mutex_lock (&wg->device_update_lock); ret =wg_socket_init (wg, wg->incoming_port);if (ret <0 ) -return ret; -mutex_lock (&wg->device_update_lock); + goto out; list_for_each_entry (peer, &wg->peer_list, peer_list) {wg_packet_send_staged_packets (peer);if (peer->persistent_keepalive_interval)wg_packet_send_keepalive (peer); } +out:mutex_unlock (&wg->device_update_lock); -return 0 ; +return ret; }#if defined(CONFIG_PM_SLEEP ) && !defined(CONFIG_ANDROID )
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
Re: [PATCH v4 01/12] device: protect socket_init with device_update_lock
set_device takes rtnl_lock(), and ndo_open is called while holding the rtnl lock too. (Try adding BUG_ON(!rtnl_is_locked()); there.)
_______________________________________________
WireGuard mailing list
[email protected]
https://lists.zx2c4.com/mailman/listinfo/wireguard
Re: [PATCH v4 01/12] device: protect socket_init with device_update_lock
Quoted 2 lines · Jason A. Donenfeld
set_devicetakesrtnl_lock(), andndo_openis called while holding the rtnl lock too. (Try addingBUG_ON(!rtnl_is_locked());there.)
I've confirmed this and will remove this patch from the next version.
_______________________________________________
WireGuard mailing list
[email protected]
https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 2/12 netlink: check for CAP_NET_ADMIN manually
In reply to Julian Orth--- src/netlink.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
src/netlink.c +12 -3
@@ -169,6 +169,7 @@ err:static int get_device_start (struct netlink_callback *cb) {struct nlattr **attrs =genl_family_attrbuf (&genl_family); +struct net *dev_net =sock_net (cb->skb->sk);struct wireguard_device *wg;int ret;
@@ -176,6 +177,8 @@ static int get_device_start(struct netlink_callback *cb) genl_family.maxattr, device_policy, NULL);if (ret <0 )return ret; +if (!netlink_ns_capable (cb->skb, dev_net->user_ns,CAP_NET_ADMIN )) +return -EPERM ; cb->args[2 ] = (long )kzalloc (sizeof (struct allowedips_cursor ),GFP_KERNEL );if (unlikely (!cb->args[2 ]))
@@ -477,9 +480,17 @@ out:static int set_device (struct sk_buff *skb,struct genl_info *info) { -struct wireguard_device *wg =lookup_interface (info->attrs, skb); +struct net *dev_net =sock_net (skb->sk); +struct wireguard_device *wg;int ret; +if (!netlink_ns_capable (skb, dev_net->user_ns,CAP_NET_ADMIN )) { + ret = -EPERM ; + goto out_nodev; + } + + wg =lookup_interface (info->attrs, skb); +if (IS_ERR (wg)) { ret =PTR_ERR (wg); goto out_nodev;
@@ -581,12 +592,10 @@ struct genl_ops genl_ops[] = { .dumpit = get_device_dump, .done = get_device_done, .policy = device_policy, - .flags = GENL_UNS_ADMIN_PERM }, { .cmd = WG_CMD_SET_DEVICE, .doit = set_device, .policy = device_policy, - .flags = GENL_UNS_ADMIN_PERM } };
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 3/12 netlink: allow specifying the device namespace
In reply to Julian OrthThis commit adds two new attributes of which at most one may be provided:
* WGDEVICE_A_DEV_NETNS_PID: NLA_U32
* WGDEVICE_A_DEV_NETNS_FD: NLA_U32
The Wireguard device is then looked up in this namespace instead of the namespace of the netlink socket.
--- src/netlink.c | 82 ++++++++++++++++++++++++++++++++++---------- src/uapi/wireguard.h | 26 ++++++++++++-- 2 files changed, 87 insertions(+), 21 deletions(-)
src/netlink.c +64 -18
@@ -24,7 +24,9 @@ static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = { [WGDEVICE_A_FLAGS] = { .type = NLA_U32 }, [WGDEVICE_A_LISTEN_PORT] = { .type = NLA_U16 }, [WGDEVICE_A_FWMARK] = { .type = NLA_U32 }, - [WGDEVICE_A_PEERS ] = { .type =NLA_NESTED } + [WGDEVICE_A_PEERS] = { .type = NLA_NESTED }, + [WGDEVICE_A_DEV_NETNS_PID] = { .type = NLA_U32 }, + [WGDEVICE_A_DEV_NETNS_FD] = { .type = NLA_U32 }, };static const struct nla_policy peer_policy[WGPEER_A_MAX +1 ] = {
@@ -47,17 +49,17 @@ static const struct nla_policy allowedip_policy[WGALLOWEDIP_A_MAX + 1] = { };static struct wireguard_device *lookup_interface (struct nlattr **attrs, -struct sk_buff *skb) +struct net *net) {struct net_device *dev =NULL ;if (!attrs[WGDEVICE_A_IFINDEX ] == !attrs[WGDEVICE_A_IFNAME ])return ERR_PTR (-EBADR );if (attrs[WGDEVICE_A_IFINDEX ]) - dev =dev_get_by_index (sock_net (skb->sk), + dev =dev_get_by_index (net,nla_get_u32 (attrs[WGDEVICE_A_IFINDEX ]));else if (attrs[WGDEVICE_A_IFNAME ]) - dev =dev_get_by_name (sock_net (skb->sk), + dev =dev_get_by_name (net,nla_data (attrs[WGDEVICE_A_IFNAME ]));if (!dev)return ERR_PTR (-ENODEV );
@@ -166,10 +168,22 @@ err:return -EMSGSIZE ; } +static struct net *get_attr_net (struct nlattr *net_pid,struct nlattr *net_fd) +{ +if (net_pid && net_fd) +return ERR_PTR (-EINVAL ); +if (net_pid) +return get_net_ns_by_pid (nla_get_u32 (net_pid)); +if (net_fd) +return get_net_ns_by_fd (nla_get_u32 (net_fd)); +return NULL ; +} +static int get_device_start (struct netlink_callback *cb) {struct nlattr **attrs =genl_family_attrbuf (&genl_family); -struct net *dev_net =sock_net (cb->skb->sk); +struct net *owned_dev_net =NULL , *dev_net; +struct allowedips_cursor *cursor =NULL ;struct wireguard_device *wg;int ret;
@@ -177,20 +191,42 @@ static int get_device_start(struct netlink_callback *cb) genl_family.maxattr, device_policy, NULL);if (ret <0 )return ret; -if (!netlink_ns_capable (cb->skb, dev_net->user_ns,CAP_NET_ADMIN )) -return -EPERM ; - cb->args[2 ] = (long )kzalloc (sizeof (struct allowedips_cursor ), -GFP_KERNEL ); -if (unlikely (!cb->args[2 ])) -return -ENOMEM ; - wg =lookup_interface (attrs, cb->skb); + + owned_dev_net =get_attr_net (attrs[WGDEVICE_A_DEV_NETNS_PID ], + attrs[WGDEVICE_A_DEV_NETNS_FD ]); +if (IS_ERR (owned_dev_net)) { + ret =PTR_ERR (owned_dev_net); + owned_dev_net =NULL ; + goto out; + } + dev_net = owned_dev_net ? :sock_net (cb->skb->sk); +if (!netlink_ns_capable (cb->skb, dev_net->user_ns,CAP_NET_ADMIN )) { + ret = -EPERM ; + goto out; + } + + cursor =kzalloc (sizeof (*cursor),GFP_KERNEL ); +if (unlikely (!cursor)) { + ret = -ENOMEM ; + goto out; + } + + wg =lookup_interface (attrs, dev_net);if (IS_ERR (wg)) { -kfree ((void *)cb->args[2 ]); - cb->args[2 ] =0 ; -return PTR_ERR (wg); + ret =PTR_ERR (wg); + goto out; } + cb->args[0 ] = (long )wg; -return 0 ; + cb->args[2 ] = (long )cursor; + cursor =NULL ; + +out: +if (cursor) +kfree (cursor); +if (owned_dev_net) +put_net (owned_dev_net); +return ret; }static int get_device_dump (struct sk_buff *skb,struct netlink_callback *cb)
@@ -480,16 +516,23 @@ out:static int set_device (struct sk_buff *skb,struct genl_info *info) { -struct net *dev_net =sock_net (skb->sk); +struct net *owned_dev_net, *dev_net;struct wireguard_device *wg;int ret; + owned_dev_net =get_attr_net (info->attrs[WGDEVICE_A_DEV_NETNS_PID ], + info->attrs[WGDEVICE_A_DEV_NETNS_FD ]); +if (IS_ERR (owned_dev_net)) { + ret =PTR_ERR (owned_dev_net); + goto out_nonet; + } + dev_net = owned_dev_net ? :sock_net (skb->sk);if (!netlink_ns_capable (skb, dev_net->user_ns,CAP_NET_ADMIN )) { ret = -EPERM ; goto out_nodev; } - wg =lookup_interface (info->attrs, skb); + wg =lookup_interface (info->attrs, dev_net);if (IS_ERR (wg)) { ret =PTR_ERR (wg);
@@ -572,6 +615,9 @@ out:rtnl_unlock ();dev_put (wg->dev); out_nodev: +if (owned_dev_net) +put_net (owned_dev_net); +out_nonet:if (info->attrs[WGDEVICE_A_PRIVATE_KEY ])memzero_explicit (nla_data (info->attrs[WGDEVICE_A_PRIVATE_KEY ]),nla_len (info->attrs[WGDEVICE_A_PRIVATE_KEY ]));
src/uapi/wireguard.h +23 -3
@@ -20,6 +20,16 @@* WGDEVICE_A_IFINDEX: NLA_U32 * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1 * +* At most one of the following may be provided: +* +* WGDEVICE_A_DEV_NETNS_PID: NLA_U32 +* WGDEVICE_A_DEV_NETNS_FD: NLA_U32 +* +* If they are provided, the Wireguard device will be looked up in this network +* namespace. Otherwise it is looked up in the network namespace of the netlink +* socket. The caller must have CAP_NET_ADMIN in the namespace of the Wireguard +* device. +* * The kernel will then return several messages (NLM_F_MULTI) containing the * following tree of nested items: *
@@ -72,9 +82,15 @@* WG_CMD_SET_DEVICE * ----------------- * -* May only be called via NLM_F_REQUEST. The command should contain the -* following tree of nested items, containing one but not both of -* WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME: +* May only be called via NLM_F_REQUEST. The command must contain the following +* tree of nested items. Exactly one of WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME +* must be provided. All other top-level items are optional. At most one of +* WGDEVICE_A_DEV_NETNS_PID and WGDEVICE_A_DEV_NETNS_FD may be provided. +* +* If WGDEVICE_A_DEV_NETNS_PID/FD is provided, the Wireguard device is looked up +* in this network namespace. Otherwise it is looked up in the network namespace +* of the netlink socket. The caller must have CAP_NET_ADMIN in the namespace of +* the Wireguard device. * * WGDEVICE_A_IFINDEX: NLA_U32 * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
@@ -82,6 +98,8 @@* peers should be removed prior to adding the list below. * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove * WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly +* WGDEVICE_A_DEV_NETNS_PID: NLA_U32 +* WGDEVICE_A_DEV_NETNS_FD: NLA_U32 * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable * WGDEVICE_A_PEERS: NLA_NESTED * 0: NLA_NESTED
@@ -154,6 +172,8 @@ enum wgdevice_attribute {WGDEVICE_A_LISTEN_PORT ,WGDEVICE_A_FWMARK ,WGDEVICE_A_PEERS , +WGDEVICE_A_DEV_NETNS_PID , +WGDEVICE_A_DEV_NETNS_FD , __WGDEVICE_A_LAST };#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 4/12 netlink: restrict access to the UDP socket
In reply to Julian OrthTo interact with the UDP socket the caller must either be in the network namespace of the socket or have CAP_NET_ADMIN in that network namespace.
--- src/netlink.c | 21 ++++++++++++++++++--- src/uapi/wireguard.h | 7 +++++++ 2 files changed, 25 insertions(+), 3 deletions(-)
src/netlink.c +18 -3
@@ -179,6 +179,14 @@ static struct net *get_attr_net(struct nlattr *net_pid, struct nlattr *net_fd)return NULL ; } +static int test_socket_net_capable (struct net *net) +{ +if (net != current->nsproxy->net_ns && + !ns_capable (net->user_ns,CAP_NET_ADMIN )) +return -EPERM ; +return 0 ; +} +static int get_device_start (struct netlink_callback *cb) {struct nlattr **attrs =genl_family_attrbuf (&genl_family);
@@ -256,9 +264,12 @@ static int get_device_dump(struct sk_buff *skb, struct netlink_callback *cb)genl_dump_check_consistent (cb, hdr);if (!last_peer_cursor) { -if (nla_put_u16 (skb,WGDEVICE_A_LISTEN_PORT , - wg->incoming_port) || -nla_put_u32 (skb,WGDEVICE_A_FWMARK , wg->fwmark) || +if (test_socket_net_capable (wg->creating_net) ==0 ) { +if (nla_put_u16 (skb,WGDEVICE_A_LISTEN_PORT , + wg->incoming_port)) + goto out; + } +if (nla_put_u32 (skb,WGDEVICE_A_FWMARK , wg->fwmark) ||nla_put_u32 (skb,WGDEVICE_A_IFINDEX , wg->dev->ifindex) ||nla_put_string (skb,WGDEVICE_A_IFNAME , wg->dev->name)) goto out;
@@ -345,7 +356,11 @@ static int get_device_done(struct netlink_callback *cb)static int set_port (struct wireguard_device *wg,u16 port) {struct wireguard_peer *peer; +int ret; + ret =test_socket_net_capable (wg->creating_net); +if (ret) +return ret;if (wg->incoming_port == port)return 0 ;list_for_each_entry (peer, &wg->peer_list, peer_list)
src/uapi/wireguard.h +7 -0
@@ -30,6 +30,9 @@* socket. The caller must have CAP_NET_ADMIN in the namespace of the Wireguard * device. * +* If the caller is not in the transit namespace and does not have CAP_NET_ADMIN +* in the transit namespace, then the WGDEVICE_A_LISTEN_PORT is not returned. +* * The kernel will then return several messages (NLM_F_MULTI) containing the * following tree of nested items: *
@@ -92,6 +95,10 @@* of the netlink socket. The caller must have CAP_NET_ADMIN in the namespace of * the Wireguard device. * +* If WGDEVICE_A_LISTEN_PORT is provided and the calling process is not in the +* transit namespace, then the calling process must have CAP_NET_ADMIN the +* transit namespace. +* * WGDEVICE_A_IFINDEX: NLA_U32 * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1 * WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 5/12 device: rename creating_net to transit_net
In reply to Julian Orth--- src/device.c | 20 ++++++++++---------- src/device.h | 4 ++-- src/netlink.c | 4 ++-- src/socket.c | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-)
src/device.c +10 -10
@@ -243,8 +243,8 @@ static void destruct(struct net_device *dev)skb_queue_purge (&wg->incoming_handshakes);free_percpu (dev->tstats);free_percpu (wg->incoming_handshakes_worker); -if (wg->have_creating_net_ref) -put_net (wg->creating_net); +if (wg->have_transit_net_ref) +put_net (wg->transit_net);mutex_unlock (&wg->device_update_lock);pr_debug ("%s: Interface deleted\n" , dev->name);
@@ -296,7 +296,7 @@ static int newlink(struct net *src_net, struct net_device *dev,int ret = -ENOMEM ;struct wireguard_device *wg =netdev_priv (dev); - wg->creating_net = src_net; + wg->transit_net = src_net;init_rwsem (&wg->static_identity.lock);mutex_init (&wg->socket_update_lock);mutex_init (&wg->device_update_lock);
@@ -397,13 +397,13 @@ static int netdevice_notification(struct notifier_block *nb,if (action !=NETDEV_REGISTER || dev->netdev_ops != &netdev_ops)return 0 ; -if (dev_net (dev) == wg->creating_net && wg->have_creating_net_ref) { -put_net (wg->creating_net); - wg->have_creating_net_ref = false; - }else if (dev_net (dev) != wg->creating_net && - !wg->have_creating_net_ref) { - wg->have_creating_net_ref = true; -get_net (wg->creating_net); +if (dev_net (dev) == wg->transit_net && wg->have_transit_net_ref) { +put_net (wg->transit_net); + wg->have_transit_net_ref = false; + }else if (dev_net (dev) != wg->transit_net && + !wg->have_transit_net_ref) { + wg->have_transit_net_ref = true; +get_net (wg->transit_net); }return 0 ; }
src/device.h +2 -2
@@ -40,7 +40,7 @@ struct wireguard_device {struct net_device *dev;struct crypt_queue encrypt_queue, decrypt_queue;struct sock __rcu *sock4, *sock6; -struct net *creating_net; +struct net *transit_net;struct noise_static_identity static_identity;struct workqueue_struct *handshake_receive_wq, *handshake_send_wq;struct workqueue_struct *packet_crypt_wq;
@@ -56,7 +56,7 @@ struct wireguard_device {unsigned int num_peers, device_update_gen;u32 fwmark;u16 incoming_port; -bool have_creating_net_ref; +bool have_transit_net_ref; };int wg_device_init (void );
src/netlink.c +2 -2
@@ -264,7 +264,7 @@ static int get_device_dump(struct sk_buff *skb, struct netlink_callback *cb)genl_dump_check_consistent (cb, hdr);if (!last_peer_cursor) { -if (test_socket_net_capable (wg->creating_net) ==0 ) { +if (test_socket_net_capable (wg->transit_net) ==0 ) {if (nla_put_u16 (skb,WGDEVICE_A_LISTEN_PORT , wg->incoming_port)) goto out;
@@ -358,7 +358,7 @@ static int set_port(struct wireguard_device *wg, u16 port)struct wireguard_peer *peer;int ret; - ret =test_socket_net_capable (wg->creating_net); + ret =test_socket_net_capable (wg->transit_net);if (ret)return ret;if (wg->incoming_port == port)
src/socket.c +4 -4
@@ -381,18 +381,18 @@ int wg_socket_init(struct wireguard_device *wg, u16 port) retry: #endif - ret = udp_sock_create(wg->creating_net, &port4, &new4); + ret = udp_sock_create(wg->transit_net, &port4, &new4); if (ret < 0) { pr_err("%s: Could not create IPv4 socket\n", wg->dev->name); return ret; } set_sock_opts(new4); - setup_udp_tunnel_sock(wg->creating_net, new4, &cfg); + setup_udp_tunnel_sock(wg->transit_net, new4, &cfg); #if IS_ENABLED(CONFIG_IPV6) if (ipv6_mod_enabled()) { port6.local_udp_port = inet_sk(new4->sk)->inet_sport; - ret = udp_sock_create(wg->creating_net, &port6, &new6); + ret = udp_sock_create(wg->transit_net, &port6, &new6); if (ret < 0) { udp_tunnel_sock_release(new4); if (ret == -EADDRINUSE && !port && retries++ < 100)
@@ -402,7 +402,7 @@ retry: return ret; }set_sock_opts (new6); -setup_udp_tunnel_sock (wg->creating_net, new6, &cfg); +setup_udp_tunnel_sock (wg->transit_net, new6, &cfg); }#endif
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 6/12 device: store a copy of the device net
In reply to Julian OrthThis eliminates the need for have_transit_net_ref because have_transit_net_ref == true if and only if dev_net != transit_net.
--- src/device.c | 27 +++++++++++++++++---------- src/device.h | 4 +++- 2 files changed, 20 insertions(+), 11 deletions(-)
src/device.c +17 -10
@@ -243,7 +243,7 @@ static void destruct(struct net_device *dev)skb_queue_purge (&wg->incoming_handshakes);free_percpu (dev->tstats);free_percpu (wg->incoming_handshakes_worker); -if (wg->have_transit_net_ref) +if (wg->transit_net != wg->dev_net)put_net (wg->transit_net);mutex_unlock (&wg->device_update_lock);
@@ -296,7 +296,9 @@ static int newlink(struct net *src_net, struct net_device *dev,int ret = -ENOMEM ;struct wireguard_device *wg =netdev_priv (dev); - wg->transit_net = src_net; + wg->dev_net =NULL ; + wg->transit_net =NULL ; +wg_device_set_nets (wg,dev_net (dev), src_net);init_rwsem (&wg->static_identity.lock);mutex_init (&wg->socket_update_lock);mutex_init (&wg->device_update_lock);
@@ -397,14 +399,8 @@ static int netdevice_notification(struct notifier_block *nb,if (action !=NETDEV_REGISTER || dev->netdev_ops != &netdev_ops)return 0 ; -if (dev_net (dev) == wg->transit_net && wg->have_transit_net_ref) { -put_net (wg->transit_net); - wg->have_transit_net_ref = false; - }else if (dev_net (dev) != wg->transit_net && - !wg->have_transit_net_ref) { - wg->have_transit_net_ref = true; -get_net (wg->transit_net); - } +wg_device_set_nets (wg,dev_net (dev), wg->transit_net); +return 0 ; }
@@ -450,3 +446,14 @@ void wg_device_uninit(void)#endif rcu_barrier_bh (); } + +void wg_device_set_nets (struct wireguard_device *wg,struct net *dev_net, +struct net *transit_net) +{ +if (wg->transit_net != wg->dev_net) +put_net (wg->transit_net); + wg->dev_net = dev_net; + wg->transit_net = transit_net; +if (wg->transit_net != wg->dev_net) +get_net (wg->transit_net); +}
src/device.h +3 -1
@@ -41,6 +41,7 @@ struct wireguard_device {struct crypt_queue encrypt_queue, decrypt_queue;struct sock __rcu *sock4, *sock6;struct net *transit_net; +struct net *dev_net;struct noise_static_identity static_identity;struct workqueue_struct *handshake_receive_wq, *handshake_send_wq;struct workqueue_struct *packet_crypt_wq;
@@ -56,10 +57,11 @@ struct wireguard_device {unsigned int num_peers, device_update_gen;u32 fwmark; u16 incoming_port; - bool have_transit_net_ref; };int wg_device_init (void );void wg_device_uninit (void ); +void wg_device_set_nets (struct wireguard_device *wg,struct net *dev_net, +struct net *transit_net);#endif /* _WG_DEVICE_H */
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 7/12 socket: allow modification of transit_net
In reply to Julian Orth--- src/device.c | 8 +++++--- src/netlink.c | 2 +- src/socket.c | 18 ++++++++++-------- src/socket.h | 6 +++--- 4 files changed, 19 insertions(+), 15 deletions(-)
src/device.c +5 -3
@@ -54,7 +54,7 @@ static int open(struct net_device *dev)#endif mutex_lock (&wg->device_update_lock); - ret =wg_socket_init (wg, wg->incoming_port); + ret =wg_socket_init (wg, wg->transit_net, wg->incoming_port);if (ret <0 ) goto out; list_for_each_entry (peer, &wg->peer_list, peer_list) {
@@ -112,7 +112,7 @@ static int stop(struct net_device *dev) }mutex_unlock (&wg->device_update_lock);skb_queue_purge (&wg->incoming_handshakes); -wg_socket_reinit (wg,NULL ,NULL ); +wg_socket_reinit (wg,NULL ,NULL ,NULL );return 0 ; }
@@ -228,7 +228,7 @@ static void destruct(struct net_device *dev)rtnl_unlock ();mutex_lock (&wg->device_update_lock); wg->incoming_port =0 ; -wg_socket_reinit (wg,NULL ,NULL ); +wg_socket_reinit (wg,NULL ,NULL ,NULL );wg_allowedips_free (&wg->peer_allowedips, &wg->device_update_lock);/* The final references are cleared in the below calls to destroy_workqueue. */ wg_peer_remove_all (wg);
@@ -399,7 +399,9 @@ static int netdevice_notification(struct notifier_block *nb,if (action !=NETDEV_REGISTER || dev->netdev_ops != &netdev_ops)return 0 ; +mutex_lock (&wg->device_update_lock);wg_device_set_nets (wg,dev_net (dev), wg->transit_net); +mutex_unlock (&wg->device_update_lock);return 0 ; }
src/netlink.c +1 -1
@@ -369,7 +369,7 @@ static int set_port(struct wireguard_device *wg, u16 port) wg->incoming_port = port;return 0 ; } -return wg_socket_init (wg, port); +return wg_socket_init (wg, wg->transit_net, port); }static int set_allowedip (struct wireguard_peer *peer,struct nlattr **attrs)
src/socket.c +10 -8
@@ -351,7 +351,7 @@ static void set_sock_opts(struct socket *sock)sk_set_memalloc (sock->sk); } -int wg_socket_init (struct wireguard_device *wg,u16 port) +int wg_socket_init (struct wireguard_device *wg,struct net *net,u16 port) {int ret;struct udp_tunnel_sock_cfg cfg = {
@@ -381,18 +381,18 @@ int wg_socket_init(struct wireguard_device *wg, u16 port) retry: #endif - ret = udp_sock_create(wg->transit_net, &port4, &new4); + ret = udp_sock_create(net, &port4, &new4); if (ret < 0) { pr_err("%s: Could not create IPv4 socket\n", wg->dev->name); return ret; } set_sock_opts(new4); - setup_udp_tunnel_sock(wg->transit_net, new4, &cfg); + setup_udp_tunnel_sock(net, new4, &cfg); #if IS_ENABLED(CONFIG_IPV6) if (ipv6_mod_enabled()) { port6.local_udp_port = inet_sk(new4->sk)->inet_sport; - ret = udp_sock_create(wg->transit_net, &port6, &new6); + ret = udp_sock_create(net, &port6, &new6); if (ret < 0) { udp_tunnel_sock_release(new4); if (ret == -EADDRINUSE && !port && retries++ < 100)
@@ -402,16 +402,16 @@ retry: return ret; }set_sock_opts (new6); -setup_udp_tunnel_sock (wg->transit_net, new6, &cfg); +setup_udp_tunnel_sock (net, new6, &cfg); }#endif -wg_socket_reinit (wg, new4 ? new4->sk :NULL , new6 ? new6->sk :NULL ); +wg_socket_reinit (wg, net, new4 ? new4->sk :NULL , new6 ? new6->sk :NULL );return 0 ; } -void wg_socket_reinit (struct wireguard_device *wg,struct sock *new4, -struct sock *new6) +void wg_socket_reinit (struct wireguard_device *wg,struct net *net, +struct sock *new4,struct sock *new6) {struct sock *old4, *old6;
@@ -424,6 +424,8 @@ void wg_socket_reinit(struct wireguard_device *wg, struct sock *new4,rcu_assign_pointer (wg->sock6, new6);if (new4) wg->incoming_port =ntohs (inet_sk (new4)->inet_sport); +if (net && wg->transit_net != net) +wg_device_set_nets (wg, wg->dev_net, net);mutex_unlock (&wg->socket_update_lock);synchronize_rcu_bh ();synchronize_net ();
src/socket.h +3 -3
@@ -11,9 +11,9 @@#include <linux/if_vlan.h> #include <linux/if_ether.h> -int wg_socket_init (struct wireguard_device *wg,u16 port); -void wg_socket_reinit (struct wireguard_device *wg,struct sock *new4, -struct sock *new6); +int wg_socket_init (struct wireguard_device *wg,struct net *net,u16 port); +void wg_socket_reinit (struct wireguard_device *wg,struct net *net, +struct sock *new4,struct sock *new6);int wg_socket_send_buffer_to_peer (struct wireguard_peer *peer,void *data,size_t len,u8 ds); int wg_socket_send_skb_to_peer(struct wireguard_peer *peer, struct sk_buff *skb,
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 8/12 netlink: allow modification of transit net
In reply to Julian OrthThis commit adds two new attributes of which at most one may be provided:
* WGDEVICE_A_TRANSIT_NETNS_PID: NLA_U32
* WGDEVICE_A_TRANSIT_NETNS_FD: NLA_U32
The transit namespace is then set to this namespace. The caller must either be in this namespace or have CAP_NET_ADMIN in it.
--- src/netlink.c | 48 +++++++++++++++++++++++++++++++------------- src/uapi/wireguard.h | 14 +++++++++---- 2 files changed, 44 insertions(+), 18 deletions(-)
src/netlink.c +34 -14
@@ -27,6 +27,8 @@ static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = { [WGDEVICE_A_PEERS] = { .type = NLA_NESTED }, [WGDEVICE_A_DEV_NETNS_PID] = { .type = NLA_U32 }, [WGDEVICE_A_DEV_NETNS_FD] = { .type = NLA_U32 }, + [WGDEVICE_A_TRANSIT_NETNS_PID] = { .type = NLA_U32 }, + [WGDEVICE_A_TRANSIT_NETNS_FD] = { .type = NLA_U32 }, };static const struct nla_policy peer_policy[WGPEER_A_MAX +1 ] = {
@@ -353,23 +355,44 @@ static int get_device_done(struct netlink_callback *cb)return 0 ; } -static int set_port (struct wireguard_device *wg,u16 port) +static int set_socket (struct wireguard_device *wg,struct nlattr **attrs) {struct wireguard_peer *peer; -int ret; +struct nlattr *port_attr = attrs[WGDEVICE_A_LISTEN_PORT ]; +u16 port; +struct net *net =NULL ; +int ret =0 ; + + net =get_attr_net (attrs[WGDEVICE_A_TRANSIT_NETNS_PID ], + attrs[WGDEVICE_A_TRANSIT_NETNS_FD ]); +if (IS_ERR (net)) +return PTR_ERR (net); +if (port_attr) + port =nla_get_u16 (port_attr); +else + port = wg->incoming_port; - ret =test_socket_net_capable (wg->transit_net); + ret =test_socket_net_capable (net ? : wg->transit_net);if (ret) -return ret; -if (wg->incoming_port == port) -return 0 ; + goto out; + +if (wg->incoming_port == port && (!net || wg->transit_net == net)) + goto out; + list_for_each_entry (peer, &wg->peer_list, peer_list)wg_socket_clear_peer_endpoint_src (peer);if (!netif_running (wg->dev)) { wg->incoming_port = port; -return 0 ; +if (net) +wg_device_set_nets (wg, wg->dev_net, net); + goto out; } -return wg_socket_init (wg, wg->transit_net, port); + ret =wg_socket_init (wg, net ? : wg->transit_net, port); + +out: +if (net) +put_net (net); +return ret; }static int set_allowedip (struct wireguard_peer *peer,struct nlattr **attrs)
@@ -566,12 +589,9 @@ static int set_device(struct sk_buff *skb, struct genl_info *info)wg_socket_clear_peer_endpoint_src (peer); } -if (info->attrs[WGDEVICE_A_LISTEN_PORT ]) { - ret =set_port (wg, -nla_get_u16 (info->attrs[WGDEVICE_A_LISTEN_PORT ])); -if (ret) - goto out; - } + ret =set_socket (wg, info->attrs); +if (ret) + goto out;if (info->attrs[WGDEVICE_A_FLAGS ] && nla_get_u32(info->attrs[WGDEVICE_A_FLAGS]) &
src/uapi/wireguard.h +10 -4
@@ -88,16 +88,18 @@* May only be called via NLM_F_REQUEST. The command must contain the following * tree of nested items. Exactly one of WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME * must be provided. All other top-level items are optional. At most one of -* WGDEVICE_A_DEV_NETNS_PID and WGDEVICE_A_DEV_NETNS_FD may be provided. +* WGDEVICE_A_TRANSIT_NETNS_PID and WGDEVICE_A_TRANSIT_NETNS_FD may be provided. +* At most one of WGDEVICE_A_DEV_NETNS_PID and WGDEVICE_A_DEV_NETNS_FD may be +* provided. * * If WGDEVICE_A_DEV_NETNS_PID/FD is provided, the Wireguard device is looked up * in this network namespace. Otherwise it is looked up in the network namespace * of the netlink socket. The caller must have CAP_NET_ADMIN in the namespace of * the Wireguard device. * -* If WGDEVICE_A_LISTEN_PORT is provided and the calling process is not in the -* transit namespace, then the calling process must have CAP_NET_ADMIN the -* transit namespace. +* If WGDEVICE_A_TRANSIT_NETNS_PID/FD and/or WGDEVICE_A_LISTEN_PORT are provided +* and the calling process is not in the transit namespace, then the calling +* process must have CAP_NET_ADMIN the transit namespace. * * WGDEVICE_A_IFINDEX: NLA_U32 * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
@@ -107,6 +109,8 @@* WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly * WGDEVICE_A_DEV_NETNS_PID: NLA_U32 * WGDEVICE_A_DEV_NETNS_FD: NLA_U32 +* WGDEVICE_A_TRANSIT_NETNS_PID: NLA_U32 +* WGDEVICE_A_TRANSIT_NETNS_FD: NLA_U32 * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable * WGDEVICE_A_PEERS: NLA_NESTED * 0: NLA_NESTED
@@ -181,6 +185,8 @@ enum wgdevice_attribute {WGDEVICE_A_PEERS ,WGDEVICE_A_DEV_NETNS_PID ,WGDEVICE_A_DEV_NETNS_FD , +WGDEVICE_A_TRANSIT_NETNS_PID , +WGDEVICE_A_TRANSIT_NETNS_FD , __WGDEVICE_A_LAST };#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
Re: [PATCH v4 08/12] netlink: allow modification of transit net
Quoted 2 lines · Julian Orth
+static int
set_socket(struct wireguard_device *wg, struct nlattr **attrs)
{
[...]
src/netlink.c static int get_device_done(struct netlink_callback *cb) · line 375
Quoted 3 lines
src/netlink.c +1
-1- ret =test_socket_net_capable (wg->transit_net); + ret =test_socket_net_capable (net ? : wg->transit_net);
This is wrong. The capability is checked even if the user hasn't provided any of the socket attributes. Instead we must check for the capability iff at least one of the three attributes is provided. It's not sufficient to move the check below
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (wg->incoming_port == port && (!net || wg->transit_net == net)) + goto out;
because this would allow an unprivileged caller to find the port and transit namespace by trial and error.
I'll fix this in the next version.
_______________________________________________
WireGuard mailing list
[email protected]
https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 9/12 tools: add framework for shared options
In reply to Julian Orth--- src/tools/containers.h | 3 +++ src/tools/genkey.c | 3 ++- src/tools/pubkey.c | 3 ++- src/tools/set.c | 3 ++- src/tools/setconf.c | 3 ++- src/tools/show.c | 3 ++- src/tools/showconf.c | 3 ++- src/tools/subcommands.h | 14 ++++++----- src/tools/wg.c | 56 +++++++++++++++++++++++++++++++++++------ 9 files changed, 72 insertions(+), 19 deletions(-)
src/tools/containers.h +3 -0
@@ -15,6 +15,9 @@#include "../uapi/wireguard.h" +struct wgoptions { +}; +struct wgallowedip {uint16_t family;union {
src/tools/genkey.c +2 -1
@@ -52,11 +52,12 @@ static inline ssize_t get_random_bytes(uint8_t *out, size_t len)return ret; } -int genkey_main (int argc,char *argv[]) +int genkey_main (int argc,char *argv[],struct wgoptions *options) {uint8_t key[WG_KEY_LEN ];char base64[WG_KEY_LEN_BASE64 ];struct stat stat; + (void )options;if (argc !=1 ) {fprintf (stderr,"Usage: %s %s\n" ,PROG_NAME , argv[0 ]);
src/tools/pubkey.c +2 -1
@@ -11,11 +11,12 @@#include "encoding.h" #include "subcommands.h" -int pubkey_main (int argc,char *argv[]) +int pubkey_main (int argc,char *argv[],struct wgoptions *options) {uint8_t key[WG_KEY_LEN ];char base64[WG_KEY_LEN_BASE64 ];int trailing_char; + (void )options;if (argc !=1 ) {fprintf (stderr,"Usage: %s %s\n" ,PROG_NAME , argv[0 ]);
src/tools/set.c +2 -1
@@ -12,10 +12,11 @@#include "ipc.h" #include "subcommands.h" -int set_main (int argc,char *argv[]) +int set_main (int argc,char *argv[],struct wgoptions *options) {struct wgdevice *device =NULL ;int ret =1 ; + (void )options;if (argc <3 ) {fprintf (stderr,"Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n" ,PROG_NAME , argv[0 ]);
src/tools/setconf.c +2 -1
@@ -13,7 +13,7 @@#include "ipc.h" #include "subcommands.h" -int setconf_main (int argc,char *argv[]) +int setconf_main (int argc,char *argv[],struct wgoptions *options) {struct wgdevice *device =NULL ;struct config_ctx ctx;
@@ -21,6 +21,7 @@ int setconf_main(int argc, char *argv[])char *config_buffer =NULL ;size_t config_buffer_len =0 ;int ret =1 ; + (void )options;if (argc !=3 ) {fprintf (stderr,"Usage: %s %s <interface> <configuration filename>\n" ,PROG_NAME , argv[0 ]);
src/tools/show.c +2 -1
@@ -377,10 +377,11 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_intreturn true; } -int show_main (int argc,char *argv[]) +int show_main (int argc,char *argv[],struct wgoptions *options) {int ret =0 ; + (void )options;COMMAND_NAME = argv[0 ];if (argc >3 ) {
src/tools/showconf.c +2 -1
@@ -18,7 +18,7 @@#include "ipc.h" #include "subcommands.h" -int showconf_main (int argc,char *argv[]) +int showconf_main (int argc,char *argv[],struct wgoptions *options) {char base64[WG_KEY_LEN_BASE64 ];char ip[INET6_ADDRSTRLEN ];
@@ -26,6 +26,7 @@ int showconf_main(int argc, char *argv[])struct wgpeer *peer;struct wgallowedip *allowedip;int ret =1 ; + (void )options;if (argc !=2 ) {fprintf (stderr,"Usage: %s %s <interface>\n" ,PROG_NAME , argv[0 ]);
src/tools/subcommands.h +8 -6
@@ -6,12 +6,14 @@#ifndef SUBCOMMANDS_H #define SUBCOMMANDS_H +#include "containers.h" +extern const char *PROG_NAME ; -int show_main (int argc,char *argv[]); -int showconf_main (int argc,char *argv[]); -int set_main (int argc,char *argv[]); -int setconf_main (int argc,char *argv[]); -int genkey_main (int argc,char *argv[]); -int pubkey_main (int argc,char *argv[]); +int show_main (int argc,char *argv[],struct wgoptions *); +int showconf_main (int argc,char *argv[],struct wgoptions *); +int set_main (int argc,char *argv[],struct wgoptions *); +int setconf_main (int argc,char *argv[],struct wgoptions *); +int genkey_main (int argc,char *argv[],struct wgoptions *); +int pubkey_main (int argc,char *argv[],struct wgoptions *);#endif
src/tools/wg.c +49 -7
89 lines not on this page · raw message
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 10/12 tools: allow specifying the device namespace
In reply to Julian OrthThe user can now use
wg --netns <pid|file-path> <subcommand>
to specify the network namespace in which wg should act. This sets the attribute WGDEVICE_A_DEV_NETNS_PID or WGDEVICE_A_DEV_NETNS_FD.
In the case of
wg --netns <pid|file-path> show all
we have to try to enter the network namespace because the kernel interface does not allow us to list devices in a network namespace referenced by pid or fd. Since entering a network namespace requires CAP_SYS_ADMIN in the current user namespace and the target user namespace, this is almost useless. TODO: Add the missing functionality to the kernel.
--- src/tools/containers.h | 14 ++++++++++ src/tools/ipc.c | 22 +++++++++++---- src/tools/ipc.h | 7 +++-- src/tools/netns.c | 62 ++++++++++++++++++++++++++++++++++++++++++ src/tools/netns.h | 18 ++++++++++++ src/tools/set.c | 3 +- src/tools/setconf.c | 3 +- src/tools/show.c | 36 +++++++++++++++++------- src/tools/showconf.c | 3 +- src/tools/wg.c | 14 ++++++++-- 10 files changed, 155 insertions(+), 27 deletions(-) create mode 100644 src/tools/netns.c create mode 100644 src/tools/netns.h
src/tools/containers.h +14 -0
@@ -15,7 +15,21 @@#include "../uapi/wireguard.h" +struct wgnetns { +uint32_t flags; + +uint32_t pid; +int fd; +}; + +enum { +WGNETNS_HAS_PID =1U <<0 , +WGNETNS_HAS_FD =1U <<1 , +}; + +struct wgoptions { +struct wgnetns dev_netns; };struct wgallowedip {
src/tools/ipc.c +16 -6
@@ -544,7 +544,7 @@ cleanup:return ret; } -static int kernel_set_device (struct wgdevice *dev) +static int kernel_set_device (struct wgnetns *dev_netns,struct wgdevice *dev) {int ret =0 ;size_t i, j;
@@ -573,6 +573,10 @@ again:mnl_attr_put_u32 (nlh,WGDEVICE_A_FWMARK , dev->fwmark);if (dev->flags &WGDEVICE_REPLACE_PEERS ) flags |=WGDEVICE_F_REPLACE_PEERS ; +if (dev_netns->flags &WGNETNS_HAS_PID ) +mnl_attr_put_u32 (nlh,WGDEVICE_A_DEV_NETNS_PID , dev_netns->pid); +if (dev_netns->flags &WGNETNS_HAS_FD ) +mnl_attr_put_u32 (nlh,WGDEVICE_A_DEV_NETNS_FD , (uint32_t )dev_netns->fd);if (flags)mnl_attr_put_u32 (nlh,WGDEVICE_A_FLAGS , flags); }
@@ -879,7 +883,8 @@ static void coalesce_peers(struct wgdevice *device) } } -static int kernel_get_device (struct wgdevice **device,const char *interface) +static int kernel_get_device (struct wgnetns *dev_netns, +struct wgdevice **device,const char *interface) {int ret =0 ;struct nlmsghdr *nlh;
@@ -899,6 +904,10 @@ try_again: nlh =mnlg_msg_prepare (nlg,WG_CMD_GET_DEVICE ,NLM_F_REQUEST |NLM_F_ACK |NLM_F_DUMP );mnl_attr_put_strz (nlh,WGDEVICE_A_IFNAME , interface); +if (dev_netns->flags &WGNETNS_HAS_PID ) +mnl_attr_put_u32 (nlh,WGDEVICE_A_DEV_NETNS_PID , dev_netns->pid); +if (dev_netns->flags &WGNETNS_HAS_FD ) +mnl_attr_put_u32 (nlh,WGDEVICE_A_DEV_NETNS_FD , (uint32_t )dev_netns->fd);if (mnlg_socket_send (nlg, nlh) <0 ) { ret = -errno; goto out;
@@ -953,23 +962,24 @@ cleanup:return buffer.buffer; } -int ipc_get_device (struct wgdevice **dev,const char *interface) +int ipc_get_device (struct wgnetns *dev_netns,struct wgdevice **dev, +const char *interface) {#ifdef __linux__if (userspace_has_wireguard_interface (interface))return userspace_get_device (dev, interface); -return kernel_get_device (dev, interface); +return kernel_get_device (dev_netns, dev, interface);#else return userspace_get_device (dev, interface);#endif } -int ipc_set_device (struct wgdevice *dev) +int ipc_set_device (struct wgnetns *dev_netns,struct wgdevice *dev) {#ifdef __linux__if (userspace_has_wireguard_interface (dev->name))return userspace_set_device (dev); -return kernel_set_device (dev); +return kernel_set_device (dev_netns, dev);#else return userspace_set_device (dev);#endif
src/tools/ipc.h +5 -2
@@ -8,10 +8,13 @@#include <stdbool.h> +#include "containers.h" +struct wgdevice ; -int ipc_set_device (struct wgdevice *dev); -int ipc_get_device (struct wgdevice **dev,const char *interface); +int ipc_set_device (struct wgnetns *dev_netns,struct wgdevice *dev); +int ipc_get_device (struct wgnetns *dev_netns,struct wgdevice **dev, +const char *interface);char *ipc_list_devices (void );#endif
src/tools/netns.c new file +62 -0
62 lines not on this page · raw message
src/tools/netns.h new file +18 -0
@@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 +* +* Copyright (C) 2018 Julian orth <[email protected]>. All Rights Reserved. +*/ + +#ifndef NETNS_H +#define NETNS_H + +#include <stdbool.h> + +#include "containers.h" + +bool netns_enter (struct wgnetns *netns); +bool netns_parse (struct wgnetns *netns,const char *arg); + +extern struct wgnetns netns_current; + +#endif
src/tools/set.c +1 -2
@@ -16,7 +16,6 @@ int set_main(int argc, char *argv[], struct wgoptions *options) {struct wgdevice *device =NULL ;int ret =1 ; - (void )options;if (argc <3 ) {fprintf (stderr,"Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n" ,PROG_NAME , argv[0 ]);
@@ -29,7 +28,7 @@ int set_main(int argc, char *argv[], struct wgoptions *options)strncpy (device->name, argv[1 ],IFNAMSIZ -1 ); device->name[IFNAMSIZ -1 ] ='\0' ; -if (ipc_set_device (device) !=0 ) { +if (ipc_set_device (&options->dev_netns, device) !=0 ) {perror ("Unable to modify interface" ); goto cleanup; }
src/tools/setconf.c +1 -2
@@ -21,7 +21,6 @@ int setconf_main(int argc, char *argv[], struct wgoptions *options)char *config_buffer =NULL ;size_t config_buffer_len =0 ;int ret =1 ; - (void )options;if (argc !=3 ) {fprintf (stderr,"Usage: %s %s <interface> <configuration filename>\n" ,PROG_NAME , argv[0 ]);
@@ -51,7 +50,7 @@ int setconf_main(int argc, char *argv[], struct wgoptions *options)strncpy (device->name, argv[1 ],IFNAMSIZ -1 ); device->name[IFNAMSIZ -1 ] ='\0' ; -if (ipc_set_device (device) !=0 ) { +if (ipc_set_device (&options->dev_netns, device) !=0 ) {perror ("Unable to modify interface" ); goto cleanup; }
src/tools/show.c +26 -10
@@ -23,6 +23,7 @@#include "terminal.h" #include "encoding.h" #include "subcommands.h" +#include "netns.h" static int peer_cmp (const void *first,const void *second) {
@@ -377,17 +378,14 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_intreturn true; } -int show_main (int argc,char *argv[],struct wgoptions *options) +static int show_all (int argc,char *argv[],struct wgoptions *options) {int ret =0 ; - (void )options; -COMMAND_NAME = argv[0 ]; - -if (argc >3 ) { -show_usage (); +// The kernel interface used by ipc_list_devices does not allow us to +// list devices in a namespace referenced via pid or fd. +if (!netns_enter (&options->dev_netns))return 1 ; - }if (argc ==1 || !strcmp (argv[1 ],"all" )) {char *interfaces =ipc_list_devices (), *interface;
@@ -401,7 +399,7 @@ int show_main(int argc, char *argv[], struct wgoptions *options)for (size_t len =0 ; (len =strlen (interface)); interface += len +1 ) {struct wgdevice *device =NULL ; -if (ipc_get_device (&device, interface) <0 ) { +if (ipc_get_device (&netns_current, &device, interface) <0 ) {fprintf (stderr,"Unable to access interface %s: %s\n" , interface,strerror (errno));continue ; }
@@ -436,12 +434,30 @@ int show_main(int argc, char *argv[], struct wgoptions *options)for (size_t len =0 ; (len =strlen (interface)); interface += len +1 )printf ("%s%c" , interface,strlen (interface + len +1 ) ?' ' :'\n' );free (interfaces); - }else if (argc ==2 && (!strcmp (argv[1 ],"-h" ) || !strcmp (argv[1 ],"--help" ) || !strcmp (argv[1 ],"help" ))) + } + +return ret; +} + +int show_main (int argc,char *argv[],struct wgoptions *options) +{ +int ret =0 ; + +COMMAND_NAME = argv[0 ]; + +if (argc >3 ) { +show_usage (); +return 1 ; + } + +if (argc ==1 || !strcmp (argv[1 ],"all" ) || !strcmp (argv[1 ],"interfaces" )) + ret =show_all (argc, argv, options); +else if (argc ==2 && (!strcmp (argv[1 ],"-h" ) || !strcmp (argv[1 ],"--help" ) || !strcmp (argv[1 ],"help" )))show_usage ();else {struct wgdevice *device =NULL ; -if (ipc_get_device (&device, argv[1 ]) <0 ) { +if (ipc_get_device (&options->dev_netns, &device, argv[1 ]) <0 ) {perror ("Unable to access interface" );return 1 ; }
src/tools/showconf.c +1 -2
@@ -26,14 +26,13 @@ int showconf_main(int argc, char *argv[], struct wgoptions *options)struct wgpeer *peer;struct wgallowedip *allowedip;int ret =1 ; - (void )options;if (argc !=2 ) {fprintf (stderr,"Usage: %s %s <interface>\n" ,PROG_NAME , argv[0 ]);return 1 ; } -if (ipc_get_device (&device, argv[1 ])) { +if (ipc_get_device (&options->dev_netns, &device, argv[1 ])) {perror ("Unable to access interface" ); goto cleanup; }
src/tools/wg.c +11 -3
@@ -11,6 +11,7 @@#include "subcommands.h" #include "containers.h" +#include "netns.h" const char *PROG_NAME ;
@@ -46,21 +47,28 @@ static bool parse_options(int argc, char *argv[], struct wgoptions *options) .name = "help", .val = 'h', }, + { + .name = "netns", + .has_arg = 1, + .val = 'n', + }, { 0 } }; - (void )options;setenv ("POSIXLY_CORRECT" ,"" ,0 ); -while ((ch =getopt_long (argc, argv,"h" , opts,NULL )) !=-1 ) { +while ((ch =getopt_long (argc, argv,"hn:" , opts,NULL )) !=-1 ) {switch (ch) {case '?' :return false;case 'h' :show_usage (stdout);exit (0 ); +case 'n' : +netns_parse (&options->dev_netns, optarg); +break ; } }
@@ -69,7 +77,7 @@ static bool parse_options(int argc, char *argv[], struct wgoptions *options)int main (int argc,char *argv[]) { -struct wgoptions options = { }; +struct wgoptions options = {0 };PROG_NAME = argv[0 ];
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 11/12 tools: allow modification of transit net
In reply to Julian OrthThe command is
wg set <device> [...] transit-netns <pid|file-path> [...]
For example:
wg set wg0 transit-netns 1
wg set wg0 transit-netns /proc/1/ns/net
--- src/tools/config.c | 8 ++++++++ src/tools/containers.h | 5 ++++- src/tools/ipc.c | 4 ++++ src/tools/man/wg.8 | 9 +++++++-- src/tools/set.c | 2 +- 5 files changed, 24 insertions(+), 4 deletions(-)
src/tools/config.c +8 -0
@@ -19,6 +19,7 @@#include "containers.h" #include "ipc.h" #include "encoding.h" +#include "netns.h" #define COMMENT_CHAR '#'
@@ -392,6 +393,8 @@ static bool process_line(struct config_ctx *ctx, const char *line)if (ctx->is_device_section) {if (key_match ("ListenPort" )) ret =parse_port (&ctx->device->listen_port, &ctx->device->flags, value); +else if (key_match ("TransitNetns" )) + ret =netns_parse (&ctx->device->transit_netns, value);else if (key_match ("FwMark" )) ret =parse_fwmark (&ctx->device->fwmark, &ctx->device->flags, value);else if (key_match ("PrivateKey" )) {
@@ -525,6 +528,11 @@ struct wgdevice *config_read_cmd(char *argv[], int argc) goto error; argv +=2 ; argc -=2 ; + }else if (!strcmp (argv[0 ],"transit-netns" ) && argc >=2 && !peer) { +if (!netns_parse (&device->transit_netns, argv[1 ])) + goto error; + argv +=2 ; + argc -=2 ; }else if (!strcmp (argv[0 ],"fwmark" ) && argc >=2 && !peer) {if (!parse_fwmark (&device->fwmark, &device->flags, argv[1 ])) goto error;
src/tools/containers.h +4 -1
@@ -75,7 +75,9 @@ enum {WGDEVICE_HAS_PRIVATE_KEY =1U <<1 ,WGDEVICE_HAS_PUBLIC_KEY =1U <<2 ,WGDEVICE_HAS_LISTEN_PORT =1U <<3 , -WGDEVICE_HAS_FWMARK =1U <<4 +WGDEVICE_HAS_FWMARK =1U <<4 , +WGDEVICE_HAS_TRANSIT_NETNS_PID =1U <<5 , + WGDEVICE_HAS_TRANSIT_NETNS_FD = 1U << 6, }; struct wgdevice {
@@ -89,6 +91,7 @@ struct wgdevice {uint32_t fwmark;uint16_t listen_port; +struct wgnetns transit_netns;struct wgpeer *first_peer, *last_peer; };
src/tools/ipc.c +4 -0
@@ -569,6 +569,10 @@ again:mnl_attr_put (nlh,WGDEVICE_A_PRIVATE_KEY ,sizeof (dev->private_key), dev->private_key);if (dev->flags &WGDEVICE_HAS_LISTEN_PORT )mnl_attr_put_u16 (nlh,WGDEVICE_A_LISTEN_PORT , dev->listen_port); +if (dev->transit_netns.flags &WGNETNS_HAS_PID ) +mnl_attr_put_u32 (nlh,WGDEVICE_A_TRANSIT_NETNS_PID , dev->transit_netns.pid); +if (dev->transit_netns.flags &WGNETNS_HAS_FD ) +mnl_attr_put_u32 (nlh,WGDEVICE_A_TRANSIT_NETNS_FD , (uint32_t )dev->transit_netns.fd);if (dev->flags &WGDEVICE_HAS_FWMARK )mnl_attr_put_u32 (nlh,WGDEVICE_A_FWMARK , dev->fwmark);if (dev->flags &WGDEVICE_REPLACE_PEERS )
src/tools/man/wg.8 +7 -2
@@ -55,12 +55,17 @@ transfer-rx, transfer-tx, persistent-keepalive. Shows the current configuration of \fI<interface>\fP in the format described by \fICONFIGURATION FILE FORMAT\fP below. .TP -\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI<ip1>/<cidr1>\fP[,\fI<ip2>/<cidr2>\fP]...] ]... +\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fItransit-netns\fP \fI<pid|file-path>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI<ip1>/<cidr1>\fP[,\fI<ip2>/<cidr2>\fP]...] ]... Sets configuration values for the specified \fI<interface>\fP. Multiple \fIpeer\fPs may be specified, and if the \fIremove\fP argument is given for a peer, that peer is removed, not configured. If \fIlisten-port\fP is not specified, the port will be chosen randomly when the -interface comes up. Both \fIprivate-key\fP and \fIpreshared-key\fP must +interface comes up. If transit-netns is not specified, the network namespace +through which encrypted packets are routed is the one in which the device +was created. Otherwise the network namespace through which encrypted packets are +routed is the one specified by the argument. If the argument is an unsigned +32-bit integer, it is interpeted as a process id, otherwise it is interpreted as +a file path. Both \fIprivate-key\fP and \fIpreshared-key\fP must be a files, because command line arguments are not considered private on most systems but if you are using .BR bash (1),
src/tools/set.c +1 -1
@@ -18,7 +18,7 @@ int set_main(int argc, char *argv[], struct wgoptions *options)int ret =1 ;if (argc <3 ) { -fprintf (stderr,"Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n" ,PROG_NAME , argv[0 ]); +fprintf (stderr,"Usage: %s %s <interface> [listen-port <port>] [transit-netns <pid|file path>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n" ,PROG_NAME , argv[0 ]);return 1 ; }
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard
PATCH v4 12/12 tests: add test for transit-net
In reply to Julian Orth--- src/tests/netns.sh | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
src/tests/netns.sh +40 -0
@@ -222,6 +222,46 @@ n1 wg set wg0 peer "$more_specific_key" remove ip1 link del wg0 ip2 link del wg0 +# Test using transit namespace. We now change the topology to this with transit-netns of $ns1 wg0 = $ns0 +# ┌──────────────────────┐ ┌───────────────────────┐ ┌────────────────────────────────────────┐ +# │ $ns1 namespace │ │ $ns0 namespace │ │ $ns2 namespace │ +# │ │ │ │ │ │ +# │ ┌─────┐ │ │ ┌──────┐ │ │ ┌─────┐ ┌─────┐ │ +# │ │ wg0 │ │ │ │vethrs│──────────────┼─────┼──│veths│────────────│ wg0 │ │ +# │ ├─────┴──────────┐ │ │ ├──────┴────────────┐ │ │ ├─────┴──────────┐ ├─────┴──────────┐ │ +# │ │192.168.241.1/24│ │ │ │10.0.0.1/24 │ │ │ │10.0.0.100/24 │ │192.168.241.2/24│ │ +# │ │fd00::1/24 │ │ │ │SNAT:192.168.1.0/24│ │ │ │ │ │fd00::2/24 │ │ +# │ └────────────────┘ │ │ └───────────────────┘ │ │ └────────────────┘ └────────────────┘ │ +# └──────────────────────┘ └───────────────────────┘ └────────────────────────────────────────┘ + +ip1 link add dev wg0 type wireguard +ip2 link add dev wg0 type wireguard +configure_peers +n1 wg set wg0 transit-netns /run/netns/$netns0 + +ip0 link add vethrs type veth peer name veths +ip0 link set veths netns $netns2 +ip0 link set vethrs up +ip0 addr add 10.0.0.1/24 dev vethrs +ip2 addr add 10.0.0.100/24 dev veths +ip1 route add default dev wg0 +ip2 link set veths up +waitiface $netns0 vethrs +waitiface $netns2 veths + +n1 wg set wg0 peer "$pub2" endpoint 10.0.0.100:2 persistent-keepalive 1 +n1 ping -W 1 -c 1 192.168.241.2 +n2 ping -W 1 -c 1 192.168.241.1 +[[ $(n2 wg show wg0 endpoints) == "$pub1 10.0.0.1:1" ]] +# Demonstrate n2 can still send packets to n1, since persistent-keepalive will prevent connection tracking entry from expiring (to see entries: `n0 conntrack -L`). +pp sleep 3 +n2 ping -W 1 -c 1 192.168.241.1 + +ip0 link del vethrs + +ip1 link del wg0 +ip2 link del wg0 + # Test using NAT. We now change the topology to this: # ┌────────────────────────────────────────┐ ┌────────────────────────────────────────────────┐ ┌────────────────────────────────────────┐ # │ $ns1 namespace │ │ $ns0 namespace │ │ $ns2 namespace │
Signature · 6 lines
2.19.0 _______________________________________________ WireGuard mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/wireguard