[Openvpn-devel] [PATCH ovpn net-next v3 1/4] ovpn: support operations on interfaces in foreign netns
Marco Baffo <[email protected]> Wed, 29 Jul 2026 17:38:12 +0200
| Newsgroups | net.sourceforge.lists.openvpn-devel |
|---|---|
| Message-ID | <[email protected]> |
ovpn currently looks up the target interface in the network namespace associated with the netlink socket. This prevents a userspace process from controlling an ovpn interface located in another namespace. Add the optional OVPN_A_TARGET_NETNSID attribute. When present, it identifies the namespace containing the target interface relative to the namespace of the requesting Netlink socket. Resolve the ID and perform the interface lookup there. When absent, preserve the existing lookup behavior. Allow the attribute in all peer and key request policies, including peer dump requests. Signed-off-by: Marco Baffo <[email protected]> --- Changes in v3: - None. Changes in v2: - Improved Doc. Documentation/netlink/specs/ovpn.yaml | 27 ++++++++++++++++ drivers/net/ovpn/netlink-gen.c | 45 ++++++++++++++++----------- drivers/net/ovpn/netlink.c | 17 ++++++++++ include/uapi/linux/ovpn.h | 1 + 4 files changed, 72 insertions(+), 18 deletions(-) diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml index b0c782e59a32..1f0d4d6037c0 100644 --- a/Documentation/netlink/specs/ovpn.yaml +++ b/Documentation/netlink/specs/ovpn.yaml @@ -337,12 +337,20 @@ attribute-sets: type: nest doc: Peer specific cipher configuration nested-attributes: keyconf + - + name: target-netnsid + type: s32 + doc: >- + ID of the network namespace containing the ovpn interface, relative to + the network namespace of the requesting Netlink socket - name: ovpn-peer-new-input subset-of: ovpn attributes: - name: ifindex + - + name: target-netnsid - name: peer nested-attributes: peer-new-input @@ -352,6 +360,8 @@ attribute-sets: attributes: - name: ifindex + - + name: target-netnsid - name: peer nested-attributes: peer-set-input @@ -361,6 +371,8 @@ attribute-sets: attributes: - name: ifindex + - + name: target-netnsid - name: peer nested-attributes: peer-del-input @@ -370,6 +382,8 @@ attribute-sets: attributes: - name: ifindex + - + name: target-netnsid - name: keyconf nested-attributes: keyconf-get @@ -379,6 +393,8 @@ attribute-sets: attributes: - name: ifindex + - + name: target-netnsid - name: keyconf nested-attributes: keyconf-swap-input @@ -388,6 +404,8 @@ attribute-sets: attributes: - name: ifindex + - + name: target-netnsid - name: keyconf nested-attributes: keyconf-del-input @@ -405,6 +423,7 @@ operations: request: attributes: - ifindex + - target-netnsid - peer - name: peer-set @@ -417,6 +436,7 @@ operations: request: attributes: - ifindex + - target-netnsid - peer - name: peer-get @@ -429,6 +449,7 @@ operations: request: attributes: - ifindex + - target-netnsid - peer reply: attributes: @@ -437,6 +458,7 @@ operations: request: attributes: - ifindex + - target-netnsid reply: attributes: - peer @@ -451,6 +473,7 @@ operations: request: attributes: - ifindex + - target-netnsid - peer - name: peer-del-ntf @@ -469,6 +492,7 @@ operations: request: attributes: - ifindex + - target-netnsid - keyconf - name: key-get @@ -481,6 +505,7 @@ operations: request: attributes: - ifindex + - target-netnsid - keyconf reply: attributes: @@ -496,6 +521,7 @@ operations: request: attributes: - ifindex + - target-netnsid - keyconf - name: key-swap-ntf @@ -515,6 +541,7 @@ operations: request: attributes: - ifindex + - target-netnsid - keyconf - diff --git a/drivers/net/ovpn/netlink-gen.c b/drivers/net/ovpn/netlink-gen.c index 2147cec7c2c5..3f513e3675a3 100644 --- a/drivers/net/ovpn/netlink-gen.c +++ b/drivers/net/ovpn/netlink-gen.c @@ -118,55 +118,64 @@ const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = { }; /* OVPN_CMD_PEER_NEW - do */ -static const struct nla_policy ovpn_peer_new_nl_policy[OVPN_A_PEER + 1] = { +static const struct nla_policy ovpn_peer_new_nl_policy[OVPN_A_TARGET_NETNSID + 1] = { [OVPN_A_IFINDEX] = { .type = NLA_U32, }, + [OVPN_A_TARGET_NETNSID] = { .type = NLA_S32, }, [OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_new_input_nl_policy), }; /* OVPN_CMD_PEER_SET - do */ -static const struct nla_policy ovpn_peer_set_nl_policy[OVPN_A_PEER + 1] = { +static const struct nla_policy ovpn_peer_set_nl_policy[OVPN_A_TARGET_NETNSID + 1] = { [OVPN_A_IFINDEX] = { .type = NLA_U32, }, + [OVPN_A_TARGET_NETNSID] = { .type = NLA_S32, }, [OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_set_input_nl_policy), }; /* OVPN_CMD_PEER_GET - do */ -static const struct nla_policy ovpn_peer_get_do_nl_policy[OVPN_A_PEER + 1] = { +static const struct nla_policy ovpn_peer_get_do_nl_policy[OVPN_A_TARGET_NETNSID + 1] = { [OVPN_A_IFINDEX] = { .type = NLA_U32, }, + [OVPN_A_TARGET_NETNSID] = { .type = NLA_S32, }, [OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy), }; /* OVPN_CMD_PEER_GET - dump */ -static const struct nla_policy ovpn_peer_get_dump_nl_policy[OVPN_A_IFINDEX + 1] = { +static const struct nla_policy ovpn_peer_get_dump_nl_policy[OVPN_A_TARGET_NETNSID + 1] = { [OVPN_A_IFINDEX] = { .type = NLA_U32, }, + [OVPN_A_TARGET_NETNSID] = { .type = NLA_S32, }, }; /* OVPN_CMD_PEER_DEL - do */ -static const struct nla_policy ovpn_peer_del_nl_policy[OVPN_A_PEER + 1] = { +static const struct nla_policy ovpn_peer_del_nl_policy[OVPN_A_TARGET_NETNSID + 1] = { [OVPN_A_IFINDEX] = { .type = NLA_U32, }, + [OVPN_A_TARGET_NETNSID] = { .type = NLA_S32, }, [OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_del_input_nl_policy), }; /* OVPN_CMD_KEY_NEW - do */ -static const struct nla_policy ovpn_key_new_nl_policy[OVPN_A_KEYCONF + 1] = { +static const struct nla_policy ovpn_key_new_nl_policy[OVPN_A_TARGET_NETNSID + 1] = { [OVPN_A_IFINDEX] = { .type = NLA_U32, }, + [OVPN_A_TARGET_NETNSID] = { .type = NLA_S32, }, [OVPN_A_KEYCONF] = NLA_POLICY_NESTED(ovpn_keyconf_nl_policy), }; /* OVPN_CMD_KEY_GET - do */ -static const struct nla_policy ovpn_key_get_nl_policy[OVPN_A_KEYCONF + 1] = { +static const struct nla_policy ovpn_key_get_nl_policy[OVPN_A_TARGET_NETNSID + 1] = { [OVPN_A_IFINDEX] = { .type = NLA_U32, }, + [OVPN_A_TARGET_NETNSID] = { .type = NLA_S32, }, [OVPN_A_KEYCONF] = NLA_POLICY_NESTED(ovpn_keyconf_get_nl_policy), }; /* OVPN_CMD_KEY_SWAP - do */ -static const struct nla_policy ovpn_key_swap_nl_policy[OVPN_A_KEYCONF + 1] = { +static const struct nla_policy ovpn_key_swap_nl_policy[OVPN_A_TARGET_NETNSID + 1] = { [OVPN_A_IFINDEX] = { .type = NLA_U32, }, + [OVPN_A_TARGET_NETNSID] = { .type = NLA_S32, }, [OVPN_A_KEYCONF] = NLA_POLICY_NESTED(ovpn_keyconf_swap_input_nl_policy), }; /* OVPN_CMD_KEY_DEL - do */ -static const struct nla_policy ovpn_key_del_nl_policy[OVPN_A_KEYCONF + 1] = { +static const struct nla_policy ovpn_key_del_nl_policy[OVPN_A_TARGET_NETNSID + 1] = { [OVPN_A_IFINDEX] = { .type = NLA_U32, }, + [OVPN_A_TARGET_NETNSID] = { .type = NLA_S32, }, [OVPN_A_KEYCONF] = NLA_POLICY_NESTED(ovpn_keyconf_del_input_nl_policy), }; @@ -178,7 +187,7 @@ static const struct genl_split_ops ovpn_nl_ops[] = { .doit = ovpn_nl_peer_new_doit, .post_doit = ovpn_nl_post_doit, .policy = ovpn_peer_new_nl_policy, - .maxattr = OVPN_A_PEER, + .maxattr = OVPN_A_TARGET_NETNSID, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { @@ -187,7 +196,7 @@ static const struct genl_split_ops ovpn_nl_ops[] = { .doit = ovpn_nl_peer_set_doit, .post_doit = ovpn_nl_post_doit, .policy = ovpn_peer_set_nl_policy, - .maxattr = OVPN_A_PEER, + .maxattr = OVPN_A_TARGET_NETNSID, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { @@ -196,14 +205,14 @@ static const struct genl_split_ops ovpn_nl_ops[] = { .doit = ovpn_nl_peer_get_doit, .post_doit = ovpn_nl_post_doit, .policy = ovpn_peer_get_do_nl_policy, - .maxattr = OVPN_A_PEER, + .maxattr = OVPN_A_TARGET_NETNSID, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { .cmd = OVPN_CMD_PEER_GET, .dumpit = ovpn_nl_peer_get_dumpit, .policy = ovpn_peer_get_dump_nl_policy, - .maxattr = OVPN_A_IFINDEX, + .maxattr = OVPN_A_TARGET_NETNSID, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP, }, { @@ -212,7 +221,7 @@ static const struct genl_split_ops ovpn_nl_ops[] = { .doit = ovpn_nl_peer_del_doit, .post_doit = ovpn_nl_post_doit, .policy = ovpn_peer_del_nl_policy, - .maxattr = OVPN_A_PEER, + .maxattr = OVPN_A_TARGET_NETNSID, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { @@ -221,7 +230,7 @@ static const struct genl_split_ops ovpn_nl_ops[] = { .doit = ovpn_nl_key_new_doit, .post_doit = ovpn_nl_post_doit, .policy = ovpn_key_new_nl_policy, - .maxattr = OVPN_A_KEYCONF, + .maxattr = OVPN_A_TARGET_NETNSID, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { @@ -230,7 +239,7 @@ static const struct genl_split_ops ovpn_nl_ops[] = { .doit = ovpn_nl_key_get_doit, .post_doit = ovpn_nl_post_doit, .policy = ovpn_key_get_nl_policy, - .maxattr = OVPN_A_KEYCONF, + .maxattr = OVPN_A_TARGET_NETNSID, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { @@ -239,7 +248,7 @@ static const struct genl_split_ops ovpn_nl_ops[] = { .doit = ovpn_nl_key_swap_doit, .post_doit = ovpn_nl_post_doit, .policy = ovpn_key_swap_nl_policy, - .maxattr = OVPN_A_KEYCONF, + .maxattr = OVPN_A_TARGET_NETNSID, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { @@ -248,7 +257,7 @@ static const struct genl_split_ops ovpn_nl_ops[] = { .doit = ovpn_nl_key_del_doit, .post_doit = ovpn_nl_post_doit, .policy = ovpn_key_del_nl_policy, - .maxattr = OVPN_A_KEYCONF, + .maxattr = OVPN_A_TARGET_NETNSID, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, }; diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index 4c66c1ec497e..b70ecfaf46c8 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -38,6 +38,7 @@ ovpn_get_dev_from_attrs(struct net *net, const struct genl_info *info, { struct ovpn_priv *ovpn; struct net_device *dev; + struct net *target_net = NULL; int ifindex; if (GENL_REQ_ATTR_CHECK(info, OVPN_A_IFINDEX)) @@ -45,12 +46,26 @@ ovpn_get_dev_from_attrs(struct net *net, const struct genl_info *info, ifindex = nla_get_u32(info->attrs[OVPN_A_IFINDEX]); + if (info->attrs[OVPN_A_TARGET_NETNSID]) { + /* Target netns IDs are relative to the requesting Netlink socket */ + target_net = get_net_ns_by_id(net, nla_get_s32(info->attrs[OVPN_A_TARGET_NETNSID])); + if (!target_net) { + NL_SET_ERR_MSG_MOD(info->extack, + "invalid target network namespace ID"); + NL_SET_BAD_ATTR(info->extack, + info->attrs[OVPN_A_TARGET_NETNSID]); + return ERR_PTR(-EINVAL); + } + net = target_net; + } + rcu_read_lock(); dev = dev_get_by_index_rcu(net, ifindex); if (!dev) { rcu_read_unlock(); NL_SET_ERR_MSG_MOD(info->extack, "ifindex does not match any interface"); + put_net(target_net); return ERR_PTR(-ENODEV); } @@ -59,12 +74,14 @@ ovpn_get_dev_from_attrs(struct net *net, const struct genl_info *info, NL_SET_ERR_MSG_MOD(info->extack, "specified interface is not ovpn"); NL_SET_BAD_ATTR(info->extack, info->attrs[OVPN_A_IFINDEX]); + put_net(target_net); return ERR_PTR(-EINVAL); } ovpn = netdev_priv(dev); netdev_hold(dev, tracker, GFP_ATOMIC); rcu_read_unlock(); + put_net(target_net); return ovpn; } diff --git a/include/uapi/linux/ovpn.h b/include/uapi/linux/ovpn.h index 06690090a1a9..3fdf24182df4 100644 --- a/include/uapi/linux/ovpn.h +++ b/include/uapi/linux/ovpn.h @@ -85,6 +85,7 @@ enum { OVPN_A_IFINDEX = 1, OVPN_A_PEER, OVPN_A_KEYCONF, + OVPN_A_TARGET_NETNSID, __OVPN_A_MAX, OVPN_A_MAX = (__OVPN_A_MAX - 1) -- 2.43.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel