mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 23:59:33 -04:00
psp: add new netlink cmd for dev-assoc and dev-disassoc
The main purpose of this cmd is to be able to associate a non-psp-capable device (e.g. veth or netkit) with a psp device. One use case is if we create a pair of veth/netkit, and assign 1 end inside a netns, while leaving the other end within the default netns, with a real PSP device, e.g. netdevsim or a physical PSP-capable NIC. With this command, we could associate the veth/netkit inside the netns with PSP device, so the virtual device could act as PSP-capable device to initiate PSP connections, and performs PSP encryption/decryption on the real PSP device. Signed-off-by: Wei Wang <weibunny@fb.com> Reviewed-by: Daniel Zahka <daniel.zahka@gmail.com> Link: https://patch.msgid.link/20260608233118.2694144-3-weibunny.kernel@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -13,6 +13,17 @@ definitions:
|
|||||||
hdr0-aes-gmac-128, hdr0-aes-gmac-256]
|
hdr0-aes-gmac-128, hdr0-aes-gmac-256]
|
||||||
|
|
||||||
attribute-sets:
|
attribute-sets:
|
||||||
|
-
|
||||||
|
name: assoc-dev-info
|
||||||
|
attributes:
|
||||||
|
-
|
||||||
|
name: ifindex
|
||||||
|
doc: ifindex of an associated network device.
|
||||||
|
type: u32
|
||||||
|
-
|
||||||
|
name: nsid
|
||||||
|
doc: Network namespace ID of the associated device.
|
||||||
|
type: s32
|
||||||
-
|
-
|
||||||
name: dev
|
name: dev
|
||||||
attributes:
|
attributes:
|
||||||
@@ -24,7 +35,9 @@ attribute-sets:
|
|||||||
min: 1
|
min: 1
|
||||||
-
|
-
|
||||||
name: ifindex
|
name: ifindex
|
||||||
doc: ifindex of the main netdevice linked to the PSP device.
|
doc: |
|
||||||
|
ifindex of the main netdevice linked to the PSP device,
|
||||||
|
or the ifindex to associate with the PSP device.
|
||||||
type: u32
|
type: u32
|
||||||
-
|
-
|
||||||
name: psp-versions-cap
|
name: psp-versions-cap
|
||||||
@@ -38,6 +51,28 @@ attribute-sets:
|
|||||||
type: u32
|
type: u32
|
||||||
enum: version
|
enum: version
|
||||||
enum-as-flags: true
|
enum-as-flags: true
|
||||||
|
-
|
||||||
|
name: assoc-list
|
||||||
|
doc: List of associated virtual devices.
|
||||||
|
type: nest
|
||||||
|
nested-attributes: assoc-dev-info
|
||||||
|
multi-attr: true
|
||||||
|
-
|
||||||
|
name: nsid
|
||||||
|
doc: |
|
||||||
|
Network namespace ID for the device to associate/disassociate.
|
||||||
|
Optional for dev-assoc and dev-disassoc; if not present, the
|
||||||
|
device is looked up in the caller's network namespace.
|
||||||
|
type: s32
|
||||||
|
-
|
||||||
|
name: by-association
|
||||||
|
doc: |
|
||||||
|
Flag indicating the PSP device is an associated device from a
|
||||||
|
different network namespace.
|
||||||
|
Present when in associated namespace, absent when in primary/host
|
||||||
|
namespace.
|
||||||
|
type: flag
|
||||||
|
|
||||||
-
|
-
|
||||||
name: assoc
|
name: assoc
|
||||||
attributes:
|
attributes:
|
||||||
@@ -170,6 +205,8 @@ operations:
|
|||||||
- ifindex
|
- ifindex
|
||||||
- psp-versions-cap
|
- psp-versions-cap
|
||||||
- psp-versions-ena
|
- psp-versions-ena
|
||||||
|
- assoc-list
|
||||||
|
- by-association
|
||||||
pre: psp-device-get-locked
|
pre: psp-device-get-locked
|
||||||
post: psp-device-unlock
|
post: psp-device-unlock
|
||||||
dump:
|
dump:
|
||||||
@@ -281,6 +318,36 @@ operations:
|
|||||||
post: psp-device-unlock
|
post: psp-device-unlock
|
||||||
dump:
|
dump:
|
||||||
reply: *stats-all
|
reply: *stats-all
|
||||||
|
-
|
||||||
|
name: dev-assoc
|
||||||
|
doc: Associate a network device with a PSP device.
|
||||||
|
attribute-set: dev
|
||||||
|
flags: [admin-perm]
|
||||||
|
do:
|
||||||
|
request:
|
||||||
|
attributes:
|
||||||
|
- id
|
||||||
|
- ifindex
|
||||||
|
- nsid
|
||||||
|
reply:
|
||||||
|
attributes: []
|
||||||
|
pre: psp-device-get-locked
|
||||||
|
post: psp-device-unlock
|
||||||
|
-
|
||||||
|
name: dev-disassoc
|
||||||
|
doc: Disassociate a network device from a PSP device.
|
||||||
|
attribute-set: dev
|
||||||
|
flags: [admin-perm]
|
||||||
|
do:
|
||||||
|
request:
|
||||||
|
attributes:
|
||||||
|
- id
|
||||||
|
- ifindex
|
||||||
|
- nsid
|
||||||
|
reply:
|
||||||
|
attributes: []
|
||||||
|
pre: psp-device-get-locked
|
||||||
|
post: psp-device-unlock
|
||||||
|
|
||||||
mcast-groups:
|
mcast-groups:
|
||||||
list:
|
list:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/refcount.h>
|
#include <linux/refcount.h>
|
||||||
|
#include <net/net_trackers.h>
|
||||||
|
|
||||||
struct netlink_ext_ack;
|
struct netlink_ext_ack;
|
||||||
|
|
||||||
@@ -43,9 +44,29 @@ struct psp_dev_config {
|
|||||||
u32 versions;
|
u32 versions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Max number of devices that can be associated with a single PSP device.
|
||||||
|
* Each entry consumes ~24 bytes in the netlink dev-get response, and the
|
||||||
|
* response must fit in GENLMSG_DEFAULT_SIZE (~3.7KB).
|
||||||
|
*/
|
||||||
|
#define PSP_ASSOC_DEV_MAX 128
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct psp_assoc_dev - wrapper for associated net_device
|
||||||
|
* @dev_list: list node for psp_dev::assoc_dev_list
|
||||||
|
* @assoc_dev: the associated net_device
|
||||||
|
* @dev_tracker: tracker for the net_device reference
|
||||||
|
*/
|
||||||
|
struct psp_assoc_dev {
|
||||||
|
struct list_head dev_list;
|
||||||
|
struct net_device *assoc_dev;
|
||||||
|
netdevice_tracker dev_tracker;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct psp_dev - PSP device struct
|
* struct psp_dev - PSP device struct
|
||||||
* @main_netdev: original netdevice of this PSP device
|
* @main_netdev: original netdevice of this PSP device
|
||||||
|
* @assoc_dev_list: list of psp_assoc_dev entries associated with this PSP device
|
||||||
|
* @assoc_dev_cnt: number of entries in @assoc_dev_list
|
||||||
* @ops: driver callbacks
|
* @ops: driver callbacks
|
||||||
* @caps: device capabilities
|
* @caps: device capabilities
|
||||||
* @drv_priv: driver priv pointer
|
* @drv_priv: driver priv pointer
|
||||||
@@ -67,6 +88,8 @@ struct psp_dev_config {
|
|||||||
*/
|
*/
|
||||||
struct psp_dev {
|
struct psp_dev {
|
||||||
struct net_device *main_netdev;
|
struct net_device *main_netdev;
|
||||||
|
struct list_head assoc_dev_list;
|
||||||
|
int assoc_dev_cnt;
|
||||||
|
|
||||||
struct psp_dev_ops *ops;
|
struct psp_dev_ops *ops;
|
||||||
struct psp_dev_caps *caps;
|
struct psp_dev_caps *caps;
|
||||||
|
|||||||
@@ -17,11 +17,22 @@ enum psp_version {
|
|||||||
PSP_VERSION_HDR0_AES_GMAC_256,
|
PSP_VERSION_HDR0_AES_GMAC_256,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PSP_A_ASSOC_DEV_INFO_IFINDEX = 1,
|
||||||
|
PSP_A_ASSOC_DEV_INFO_NSID,
|
||||||
|
|
||||||
|
__PSP_A_ASSOC_DEV_INFO_MAX,
|
||||||
|
PSP_A_ASSOC_DEV_INFO_MAX = (__PSP_A_ASSOC_DEV_INFO_MAX - 1)
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
PSP_A_DEV_ID = 1,
|
PSP_A_DEV_ID = 1,
|
||||||
PSP_A_DEV_IFINDEX,
|
PSP_A_DEV_IFINDEX,
|
||||||
PSP_A_DEV_PSP_VERSIONS_CAP,
|
PSP_A_DEV_PSP_VERSIONS_CAP,
|
||||||
PSP_A_DEV_PSP_VERSIONS_ENA,
|
PSP_A_DEV_PSP_VERSIONS_ENA,
|
||||||
|
PSP_A_DEV_ASSOC_LIST,
|
||||||
|
PSP_A_DEV_NSID,
|
||||||
|
PSP_A_DEV_BY_ASSOCIATION,
|
||||||
|
|
||||||
__PSP_A_DEV_MAX,
|
__PSP_A_DEV_MAX,
|
||||||
PSP_A_DEV_MAX = (__PSP_A_DEV_MAX - 1)
|
PSP_A_DEV_MAX = (__PSP_A_DEV_MAX - 1)
|
||||||
@@ -74,6 +85,8 @@ enum {
|
|||||||
PSP_CMD_RX_ASSOC,
|
PSP_CMD_RX_ASSOC,
|
||||||
PSP_CMD_TX_ASSOC,
|
PSP_CMD_TX_ASSOC,
|
||||||
PSP_CMD_GET_STATS,
|
PSP_CMD_GET_STATS,
|
||||||
|
PSP_CMD_DEV_ASSOC,
|
||||||
|
PSP_CMD_DEV_DISASSOC,
|
||||||
|
|
||||||
__PSP_CMD_MAX,
|
__PSP_CMD_MAX,
|
||||||
PSP_CMD_MAX = (__PSP_CMD_MAX - 1)
|
PSP_CMD_MAX = (__PSP_CMD_MAX - 1)
|
||||||
|
|||||||
@@ -53,6 +53,20 @@ static const struct nla_policy psp_get_stats_nl_policy[PSP_A_STATS_DEV_ID + 1] =
|
|||||||
[PSP_A_STATS_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
|
[PSP_A_STATS_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* PSP_CMD_DEV_ASSOC - do */
|
||||||
|
static const struct nla_policy psp_dev_assoc_nl_policy[PSP_A_DEV_NSID + 1] = {
|
||||||
|
[PSP_A_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||||
|
[PSP_A_DEV_IFINDEX] = { .type = NLA_U32, },
|
||||||
|
[PSP_A_DEV_NSID] = { .type = NLA_S32, },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* PSP_CMD_DEV_DISASSOC - do */
|
||||||
|
static const struct nla_policy psp_dev_disassoc_nl_policy[PSP_A_DEV_NSID + 1] = {
|
||||||
|
[PSP_A_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||||
|
[PSP_A_DEV_IFINDEX] = { .type = NLA_U32, },
|
||||||
|
[PSP_A_DEV_NSID] = { .type = NLA_S32, },
|
||||||
|
};
|
||||||
|
|
||||||
/* Ops table for psp */
|
/* Ops table for psp */
|
||||||
static const struct genl_split_ops psp_nl_ops[] = {
|
static const struct genl_split_ops psp_nl_ops[] = {
|
||||||
{
|
{
|
||||||
@@ -119,6 +133,24 @@ static const struct genl_split_ops psp_nl_ops[] = {
|
|||||||
.dumpit = psp_nl_get_stats_dumpit,
|
.dumpit = psp_nl_get_stats_dumpit,
|
||||||
.flags = GENL_CMD_CAP_DUMP,
|
.flags = GENL_CMD_CAP_DUMP,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.cmd = PSP_CMD_DEV_ASSOC,
|
||||||
|
.pre_doit = psp_device_get_locked,
|
||||||
|
.doit = psp_nl_dev_assoc_doit,
|
||||||
|
.post_doit = psp_device_unlock,
|
||||||
|
.policy = psp_dev_assoc_nl_policy,
|
||||||
|
.maxattr = PSP_A_DEV_NSID,
|
||||||
|
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.cmd = PSP_CMD_DEV_DISASSOC,
|
||||||
|
.pre_doit = psp_device_get_locked,
|
||||||
|
.doit = psp_nl_dev_disassoc_doit,
|
||||||
|
.post_doit = psp_device_unlock,
|
||||||
|
.policy = psp_dev_disassoc_nl_policy,
|
||||||
|
.maxattr = PSP_A_DEV_NSID,
|
||||||
|
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct genl_multicast_group psp_nl_mcgrps[] = {
|
static const struct genl_multicast_group psp_nl_mcgrps[] = {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ int psp_nl_rx_assoc_doit(struct sk_buff *skb, struct genl_info *info);
|
|||||||
int psp_nl_tx_assoc_doit(struct sk_buff *skb, struct genl_info *info);
|
int psp_nl_tx_assoc_doit(struct sk_buff *skb, struct genl_info *info);
|
||||||
int psp_nl_get_stats_doit(struct sk_buff *skb, struct genl_info *info);
|
int psp_nl_get_stats_doit(struct sk_buff *skb, struct genl_info *info);
|
||||||
int psp_nl_get_stats_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
|
int psp_nl_get_stats_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
|
||||||
|
int psp_nl_dev_assoc_doit(struct sk_buff *skb, struct genl_info *info);
|
||||||
|
int psp_nl_dev_disassoc_doit(struct sk_buff *skb, struct genl_info *info);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
PSP_NLGRP_MGMT,
|
PSP_NLGRP_MGMT,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ extern struct mutex psp_devs_lock;
|
|||||||
|
|
||||||
void psp_dev_free(struct psp_dev *psd);
|
void psp_dev_free(struct psp_dev *psd);
|
||||||
int psp_dev_check_access(struct psp_dev *psd, struct net *net, bool admin);
|
int psp_dev_check_access(struct psp_dev *psd, struct net *net, bool admin);
|
||||||
|
bool psp_has_assoc_dev_in_ns(struct psp_dev *psd, struct net *net);
|
||||||
|
|
||||||
void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd);
|
void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd);
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ int psp_dev_check_access(struct psp_dev *psd, struct net *net, bool admin)
|
|||||||
{
|
{
|
||||||
if (dev_net(psd->main_netdev) == net)
|
if (dev_net(psd->main_netdev) == net)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (!admin && psp_has_assoc_dev_in_ns(psd, net))
|
||||||
|
return 0;
|
||||||
|
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +78,7 @@ psp_dev_create(struct net_device *netdev,
|
|||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
psd->main_netdev = netdev;
|
psd->main_netdev = netdev;
|
||||||
|
INIT_LIST_HEAD(&psd->assoc_dev_list);
|
||||||
psd->ops = psd_ops;
|
psd->ops = psd_ops;
|
||||||
psd->caps = psd_caps;
|
psd->caps = psd_caps;
|
||||||
psd->drv_priv = priv_ptr;
|
psd->drv_priv = priv_ptr;
|
||||||
@@ -125,6 +130,7 @@ void psp_dev_free(struct psp_dev *psd)
|
|||||||
*/
|
*/
|
||||||
void psp_dev_unregister(struct psp_dev *psd)
|
void psp_dev_unregister(struct psp_dev *psd)
|
||||||
{
|
{
|
||||||
|
struct psp_assoc_dev *entry, *entry_tmp;
|
||||||
struct psp_assoc *pas, *next;
|
struct psp_assoc *pas, *next;
|
||||||
|
|
||||||
mutex_lock(&psp_devs_lock);
|
mutex_lock(&psp_devs_lock);
|
||||||
@@ -144,6 +150,15 @@ void psp_dev_unregister(struct psp_dev *psd)
|
|||||||
list_for_each_entry_safe(pas, next, &psd->stale_assocs, assocs_list)
|
list_for_each_entry_safe(pas, next, &psd->stale_assocs, assocs_list)
|
||||||
psp_dev_tx_key_del(psd, pas);
|
psp_dev_tx_key_del(psd, pas);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(entry, entry_tmp, &psd->assoc_dev_list,
|
||||||
|
dev_list) {
|
||||||
|
list_del(&entry->dev_list);
|
||||||
|
rcu_assign_pointer(entry->assoc_dev->psp_dev, NULL);
|
||||||
|
netdev_put(entry->assoc_dev, &entry->dev_tracker);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
psd->assoc_dev_cnt = 0;
|
||||||
|
|
||||||
rcu_assign_pointer(psd->main_netdev->psp_dev, NULL);
|
rcu_assign_pointer(psd->main_netdev->psp_dev, NULL);
|
||||||
|
|
||||||
psd->ops = NULL;
|
psd->ops = NULL;
|
||||||
|
|||||||
325
net/psp/psp_nl.c
325
net/psp/psp_nl.c
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
#include <linux/ethtool.h>
|
#include <linux/ethtool.h>
|
||||||
|
#include <linux/net_namespace.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/xarray.h>
|
#include <linux/xarray.h>
|
||||||
#include <net/genetlink.h>
|
#include <net/genetlink.h>
|
||||||
@@ -38,6 +39,74 @@ static int psp_nl_reply_send(struct sk_buff *rsp, struct genl_info *info)
|
|||||||
return genlmsg_reply(rsp, info);
|
return genlmsg_reply(rsp, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* psp_nl_multicast_per_ns() - multicast a notification to each unique netns
|
||||||
|
* @psd: PSP device (must be locked)
|
||||||
|
* @group: multicast group
|
||||||
|
* @build_ntf: callback to build an skb for a given netns, or NULL on failure
|
||||||
|
* @ctx: opaque context passed to @build_ntf
|
||||||
|
*
|
||||||
|
* Iterates all unique network namespaces from the associated device list
|
||||||
|
* plus the main device's netns. For each unique netns, calls @build_ntf
|
||||||
|
* to construct a notification skb and multicasts it.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
psp_nl_multicast_per_ns(struct psp_dev *psd, unsigned int group,
|
||||||
|
struct sk_buff *(*build_ntf)(struct psp_dev *,
|
||||||
|
struct net *,
|
||||||
|
void *),
|
||||||
|
void *ctx)
|
||||||
|
{
|
||||||
|
struct psp_assoc_dev *entry;
|
||||||
|
struct xarray sent_nets;
|
||||||
|
struct net *main_net;
|
||||||
|
struct sk_buff *ntf;
|
||||||
|
|
||||||
|
main_net = dev_net(psd->main_netdev);
|
||||||
|
xa_init(&sent_nets);
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
|
||||||
|
struct net *assoc_net = dev_net(entry->assoc_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (net_eq(assoc_net, main_net))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = xa_insert(&sent_nets, (unsigned long)assoc_net, assoc_net,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (ret == -EBUSY)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ntf = build_ntf(psd, assoc_net, ctx);
|
||||||
|
if (!ntf)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
genlmsg_multicast_netns(&psp_nl_family, assoc_net, ntf, 0,
|
||||||
|
group, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
xa_destroy(&sent_nets);
|
||||||
|
|
||||||
|
/* Send to main device netns */
|
||||||
|
ntf = build_ntf(psd, main_net, ctx);
|
||||||
|
if (!ntf)
|
||||||
|
return;
|
||||||
|
genlmsg_multicast_netns(&psp_nl_family, main_net, ntf, 0, group,
|
||||||
|
GFP_KERNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sk_buff *psp_nl_clone_ntf(struct psp_dev *psd, struct net *net,
|
||||||
|
void *ctx)
|
||||||
|
{
|
||||||
|
return skb_clone(ctx, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void psp_nl_multicast_all_ns(struct psp_dev *psd, struct sk_buff *ntf,
|
||||||
|
unsigned int group)
|
||||||
|
{
|
||||||
|
psp_nl_multicast_per_ns(psd, group, psp_nl_clone_ntf, ntf);
|
||||||
|
nlmsg_consume(ntf);
|
||||||
|
}
|
||||||
|
|
||||||
/* Device stuff */
|
/* Device stuff */
|
||||||
|
|
||||||
static struct psp_dev *
|
static struct psp_dev *
|
||||||
@@ -91,6 +160,38 @@ int psp_device_get_locked(const struct genl_split_ops *ops,
|
|||||||
return __psp_device_get_locked(ops, skb, info, false);
|
return __psp_device_get_locked(ops, skb, info, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct net *psp_nl_resolve_assoc_dev_ns(struct psp_dev *psd,
|
||||||
|
struct genl_info *info)
|
||||||
|
{
|
||||||
|
struct net *net;
|
||||||
|
int nsid;
|
||||||
|
|
||||||
|
if (GENL_REQ_ATTR_CHECK(info, PSP_A_DEV_IFINDEX))
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
if (info->attrs[PSP_A_DEV_NSID]) {
|
||||||
|
/* Only callers in the main netns may specify nsid */
|
||||||
|
if (dev_net(psd->main_netdev) != genl_info_net(info)) {
|
||||||
|
NL_SET_BAD_ATTR(info->extack,
|
||||||
|
info->attrs[PSP_A_DEV_NSID]);
|
||||||
|
return ERR_PTR(-EPERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsid = nla_get_s32(info->attrs[PSP_A_DEV_NSID]);
|
||||||
|
|
||||||
|
net = get_net_ns_by_id(genl_info_net(info), nsid);
|
||||||
|
if (!net) {
|
||||||
|
NL_SET_BAD_ATTR(info->extack,
|
||||||
|
info->attrs[PSP_A_DEV_NSID]);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
net = get_net(genl_info_net(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
return net;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
|
psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||||
struct genl_info *info)
|
struct genl_info *info)
|
||||||
@@ -103,11 +204,67 @@ psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
|
|||||||
sockfd_put(socket);
|
sockfd_put(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool psp_has_assoc_dev_in_ns(struct psp_dev *psd, struct net *net)
|
||||||
|
{
|
||||||
|
struct psp_assoc_dev *entry;
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
|
||||||
|
if (dev_net(entry->assoc_dev) == net)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int psp_nl_fill_assoc_dev_list(struct psp_dev *psd, struct sk_buff *rsp,
|
||||||
|
struct net *cur_net,
|
||||||
|
struct net *filter_net)
|
||||||
|
{
|
||||||
|
struct psp_assoc_dev *entry;
|
||||||
|
struct net *dev_net_ns;
|
||||||
|
struct nlattr *nest;
|
||||||
|
int nsid;
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
|
||||||
|
dev_net_ns = dev_net(entry->assoc_dev);
|
||||||
|
|
||||||
|
if (filter_net && dev_net_ns != filter_net)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* When filtering by namespace, all devices are in the caller's
|
||||||
|
* namespace so nsid is always NETNSA_NSID_NOT_ASSIGNED (-1).
|
||||||
|
* Otherwise, calculate the nsid relative to cur_net.
|
||||||
|
*/
|
||||||
|
nsid = filter_net ? NETNSA_NSID_NOT_ASSIGNED :
|
||||||
|
peernet2id_alloc(cur_net, dev_net_ns,
|
||||||
|
GFP_KERNEL);
|
||||||
|
|
||||||
|
nest = nla_nest_start(rsp, PSP_A_DEV_ASSOC_LIST);
|
||||||
|
if (!nest)
|
||||||
|
return -EMSGSIZE;
|
||||||
|
|
||||||
|
if (nla_put_u32(rsp, PSP_A_ASSOC_DEV_INFO_IFINDEX,
|
||||||
|
entry->assoc_dev->ifindex) ||
|
||||||
|
nla_put_s32(rsp, PSP_A_ASSOC_DEV_INFO_NSID, nsid)) {
|
||||||
|
nla_nest_cancel(rsp, nest);
|
||||||
|
return -EMSGSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nla_nest_end(rsp, nest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
|
psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
|
||||||
const struct genl_info *info)
|
const struct genl_info *info)
|
||||||
{
|
{
|
||||||
|
struct net *cur_net;
|
||||||
void *hdr;
|
void *hdr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
cur_net = genl_info_net(info);
|
||||||
|
|
||||||
hdr = genlmsg_iput(rsp, info);
|
hdr = genlmsg_iput(rsp, info);
|
||||||
if (!hdr)
|
if (!hdr)
|
||||||
@@ -119,6 +276,22 @@ psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
|
|||||||
nla_put_u32(rsp, PSP_A_DEV_PSP_VERSIONS_ENA, psd->config.versions))
|
nla_put_u32(rsp, PSP_A_DEV_PSP_VERSIONS_ENA, psd->config.versions))
|
||||||
goto err_cancel_msg;
|
goto err_cancel_msg;
|
||||||
|
|
||||||
|
if (cur_net == dev_net(psd->main_netdev)) {
|
||||||
|
/* Primary device - dump assoc list */
|
||||||
|
err = psp_nl_fill_assoc_dev_list(psd, rsp, cur_net, NULL);
|
||||||
|
if (err)
|
||||||
|
goto err_cancel_msg;
|
||||||
|
} else {
|
||||||
|
/* In netns: set by-association flag and dump filtered
|
||||||
|
* assoc list containing only devices in cur_net
|
||||||
|
*/
|
||||||
|
if (nla_put_flag(rsp, PSP_A_DEV_BY_ASSOCIATION))
|
||||||
|
goto err_cancel_msg;
|
||||||
|
err = psp_nl_fill_assoc_dev_list(psd, rsp, cur_net, cur_net);
|
||||||
|
if (err)
|
||||||
|
goto err_cancel_msg;
|
||||||
|
}
|
||||||
|
|
||||||
genlmsg_end(rsp, hdr);
|
genlmsg_end(rsp, hdr);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -127,27 +300,34 @@ psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
|
|||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd)
|
static struct sk_buff *psp_nl_build_dev_ntf(struct psp_dev *psd,
|
||||||
|
struct net *net, void *ctx)
|
||||||
{
|
{
|
||||||
|
u32 cmd = *(u32 *)ctx;
|
||||||
struct genl_info info;
|
struct genl_info info;
|
||||||
struct sk_buff *ntf;
|
struct sk_buff *ntf;
|
||||||
|
|
||||||
if (!genl_has_listeners(&psp_nl_family, dev_net(psd->main_netdev),
|
if (!genl_has_listeners(&psp_nl_family, net, PSP_NLGRP_MGMT))
|
||||||
PSP_NLGRP_MGMT))
|
return NULL;
|
||||||
return;
|
|
||||||
|
|
||||||
ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||||
if (!ntf)
|
if (!ntf)
|
||||||
return;
|
return NULL;
|
||||||
|
|
||||||
genl_info_init_ntf(&info, &psp_nl_family, cmd);
|
genl_info_init_ntf(&info, &psp_nl_family, cmd);
|
||||||
|
genl_info_net_set(&info, net);
|
||||||
if (psp_nl_dev_fill(psd, ntf, &info)) {
|
if (psp_nl_dev_fill(psd, ntf, &info)) {
|
||||||
nlmsg_free(ntf);
|
nlmsg_free(ntf);
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
genlmsg_multicast_netns(&psp_nl_family, dev_net(psd->main_netdev), ntf,
|
return ntf;
|
||||||
0, PSP_NLGRP_MGMT, GFP_KERNEL);
|
}
|
||||||
|
|
||||||
|
void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd)
|
||||||
|
{
|
||||||
|
psp_nl_multicast_per_ns(psd, PSP_NLGRP_MGMT,
|
||||||
|
psp_nl_build_dev_ntf, &cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
int psp_nl_dev_get_doit(struct sk_buff *req, struct genl_info *info)
|
int psp_nl_dev_get_doit(struct sk_buff *req, struct genl_info *info)
|
||||||
@@ -281,8 +461,9 @@ int psp_nl_key_rotate_doit(struct sk_buff *skb, struct genl_info *info)
|
|||||||
psd->stats.rotations++;
|
psd->stats.rotations++;
|
||||||
|
|
||||||
nlmsg_end(ntf, (struct nlmsghdr *)ntf->data);
|
nlmsg_end(ntf, (struct nlmsghdr *)ntf->data);
|
||||||
genlmsg_multicast_netns(&psp_nl_family, dev_net(psd->main_netdev), ntf,
|
|
||||||
0, PSP_NLGRP_USE, GFP_KERNEL);
|
psp_nl_multicast_all_ns(psd, ntf, PSP_NLGRP_USE);
|
||||||
|
|
||||||
return psp_nl_reply_send(rsp, info);
|
return psp_nl_reply_send(rsp, info);
|
||||||
|
|
||||||
err_free_ntf:
|
err_free_ntf:
|
||||||
@@ -292,6 +473,130 @@ int psp_nl_key_rotate_doit(struct sk_buff *skb, struct genl_info *info)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int psp_nl_dev_assoc_doit(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
{
|
||||||
|
struct psp_dev *psd = info->user_ptr[0];
|
||||||
|
struct psp_assoc_dev *psp_assoc_dev;
|
||||||
|
struct net_device *assoc_dev;
|
||||||
|
struct sk_buff *rsp;
|
||||||
|
u32 assoc_ifindex;
|
||||||
|
struct net *net;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (psd->assoc_dev_cnt >= PSP_ASSOC_DEV_MAX) {
|
||||||
|
NL_SET_ERR_MSG(info->extack,
|
||||||
|
"Maximum number of associated devices reached");
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
net = psp_nl_resolve_assoc_dev_ns(psd, info);
|
||||||
|
if (IS_ERR(net))
|
||||||
|
return PTR_ERR(net);
|
||||||
|
|
||||||
|
psp_assoc_dev = kzalloc_obj(*psp_assoc_dev);
|
||||||
|
if (!psp_assoc_dev) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto err_put_net;
|
||||||
|
}
|
||||||
|
|
||||||
|
assoc_ifindex = nla_get_u32(info->attrs[PSP_A_DEV_IFINDEX]);
|
||||||
|
assoc_dev = netdev_get_by_index(net, assoc_ifindex,
|
||||||
|
&psp_assoc_dev->dev_tracker,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!assoc_dev) {
|
||||||
|
NL_SET_BAD_ATTR(info->extack, info->attrs[PSP_A_DEV_IFINDEX]);
|
||||||
|
err = -ENODEV;
|
||||||
|
goto err_free_assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if device is already associated with a PSP device */
|
||||||
|
if (cmpxchg(&assoc_dev->psp_dev, NULL, RCU_INITIALIZER(psd))) {
|
||||||
|
NL_SET_ERR_MSG(info->extack,
|
||||||
|
"Device already associated with a PSP device");
|
||||||
|
err = -EBUSY;
|
||||||
|
goto err_put_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
psp_assoc_dev->assoc_dev = assoc_dev;
|
||||||
|
rsp = psp_nl_reply_new(info);
|
||||||
|
if (!rsp) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto err_clean_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_tail(&psp_assoc_dev->dev_list, &psd->assoc_dev_list);
|
||||||
|
psd->assoc_dev_cnt++;
|
||||||
|
|
||||||
|
put_net(net);
|
||||||
|
|
||||||
|
psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF);
|
||||||
|
|
||||||
|
return psp_nl_reply_send(rsp, info);
|
||||||
|
|
||||||
|
err_clean_ptr:
|
||||||
|
rcu_assign_pointer(assoc_dev->psp_dev, NULL);
|
||||||
|
err_put_dev:
|
||||||
|
netdev_put(assoc_dev, &psp_assoc_dev->dev_tracker);
|
||||||
|
err_free_assoc:
|
||||||
|
kfree(psp_assoc_dev);
|
||||||
|
err_put_net:
|
||||||
|
put_net(net);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int psp_nl_dev_disassoc_doit(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
{
|
||||||
|
struct psp_assoc_dev *entry, *found = NULL;
|
||||||
|
struct psp_dev *psd = info->user_ptr[0];
|
||||||
|
struct sk_buff *rsp;
|
||||||
|
u32 assoc_ifindex;
|
||||||
|
struct net *net;
|
||||||
|
|
||||||
|
net = psp_nl_resolve_assoc_dev_ns(psd, info);
|
||||||
|
if (IS_ERR(net))
|
||||||
|
return PTR_ERR(net);
|
||||||
|
|
||||||
|
assoc_ifindex = nla_get_u32(info->attrs[PSP_A_DEV_IFINDEX]);
|
||||||
|
|
||||||
|
/* Search the association list by ifindex and netns */
|
||||||
|
list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
|
||||||
|
if (entry->assoc_dev->ifindex == assoc_ifindex &&
|
||||||
|
dev_net(entry->assoc_dev) == net) {
|
||||||
|
found = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
put_net(net);
|
||||||
|
NL_SET_BAD_ATTR(info->extack, info->attrs[PSP_A_DEV_IFINDEX]);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
rsp = psp_nl_reply_new(info);
|
||||||
|
if (!rsp) {
|
||||||
|
put_net(net);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
put_net(net);
|
||||||
|
|
||||||
|
/* Notify before removal so listeners in the disassociated namespace
|
||||||
|
* still receive the notification.
|
||||||
|
*/
|
||||||
|
psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF);
|
||||||
|
|
||||||
|
/* Remove from the association list */
|
||||||
|
list_del(&found->dev_list);
|
||||||
|
psd->assoc_dev_cnt--;
|
||||||
|
rcu_assign_pointer(found->assoc_dev->psp_dev, NULL);
|
||||||
|
netdev_put(found->assoc_dev, &found->dev_tracker);
|
||||||
|
kfree(found);
|
||||||
|
|
||||||
|
return psp_nl_reply_send(rsp, info);
|
||||||
|
}
|
||||||
|
|
||||||
/* Key etc. */
|
/* Key etc. */
|
||||||
|
|
||||||
int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
|
int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
|
||||||
|
|||||||
Reference in New Issue
Block a user