psp: base PSP device support
Add a netlink family for PSP and allow drivers to register support. The "PSP device" is its own object. This allows us to perform more flexible reference counting / lifetime control than if PSP information was part of net_device. In the future we should also be able to "delegate" PSP access to software devices, such as *vlan, veth or netkit more easily. Reviewed-by: Willem de Bruijn <willemb@google.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Daniel Zahka <daniel.zahka@gmail.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Link: https://patch.msgid.link/20250917000954.859376-3-daniel.zahka@gmail.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
a9266275fd
commit
00c94ca2b9
|
@ -0,0 +1,96 @@
|
|||
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
---
|
||||
name: psp
|
||||
|
||||
doc:
|
||||
PSP Security Protocol Generic Netlink family.
|
||||
|
||||
definitions:
|
||||
-
|
||||
type: enum
|
||||
name: version
|
||||
entries: [hdr0-aes-gcm-128, hdr0-aes-gcm-256,
|
||||
hdr0-aes-gmac-128, hdr0-aes-gmac-256]
|
||||
|
||||
attribute-sets:
|
||||
-
|
||||
name: dev
|
||||
attributes:
|
||||
-
|
||||
name: id
|
||||
doc: PSP device ID.
|
||||
type: u32
|
||||
checks:
|
||||
min: 1
|
||||
-
|
||||
name: ifindex
|
||||
doc: ifindex of the main netdevice linked to the PSP device.
|
||||
type: u32
|
||||
-
|
||||
name: psp-versions-cap
|
||||
doc: Bitmask of PSP versions supported by the device.
|
||||
type: u32
|
||||
enum: version
|
||||
enum-as-flags: true
|
||||
-
|
||||
name: psp-versions-ena
|
||||
doc: Bitmask of currently enabled (accepted on Rx) PSP versions.
|
||||
type: u32
|
||||
enum: version
|
||||
enum-as-flags: true
|
||||
|
||||
operations:
|
||||
list:
|
||||
-
|
||||
name: dev-get
|
||||
doc: Get / dump information about PSP capable devices on the system.
|
||||
attribute-set: dev
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
- id
|
||||
reply: &dev-all
|
||||
attributes:
|
||||
- id
|
||||
- ifindex
|
||||
- psp-versions-cap
|
||||
- psp-versions-ena
|
||||
pre: psp-device-get-locked
|
||||
post: psp-device-unlock
|
||||
dump:
|
||||
reply: *dev-all
|
||||
-
|
||||
name: dev-add-ntf
|
||||
doc: Notification about device appearing.
|
||||
notify: dev-get
|
||||
mcgrp: mgmt
|
||||
-
|
||||
name: dev-del-ntf
|
||||
doc: Notification about device disappearing.
|
||||
notify: dev-get
|
||||
mcgrp: mgmt
|
||||
-
|
||||
name: dev-set
|
||||
doc: Set the configuration of a PSP device.
|
||||
attribute-set: dev
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
- id
|
||||
- psp-versions-ena
|
||||
reply:
|
||||
attributes: []
|
||||
pre: psp-device-get-locked
|
||||
post: psp-device-unlock
|
||||
-
|
||||
name: dev-change-ntf
|
||||
doc: Notification about device configuration being changed.
|
||||
notify: dev-get
|
||||
mcgrp: mgmt
|
||||
|
||||
mcast-groups:
|
||||
list:
|
||||
-
|
||||
name: mgmt
|
||||
|
||||
...
|
|
@ -1906,6 +1906,7 @@ enum netdev_reg_state {
|
|||
* device struct
|
||||
* @mpls_ptr: mpls_dev struct pointer
|
||||
* @mctp_ptr: MCTP specific data
|
||||
* @psp_dev: PSP crypto device registered for this netdev
|
||||
*
|
||||
* @dev_addr: Hw address (before bcast,
|
||||
* because most packets are unicast)
|
||||
|
@ -2310,6 +2311,9 @@ struct net_device {
|
|||
#if IS_ENABLED(CONFIG_MCTP)
|
||||
struct mctp_dev __rcu *mctp_ptr;
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_INET_PSP)
|
||||
struct psp_dev __rcu *psp_dev;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Cache lines mostly used on receive path (including eth_type_trans())
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __NET_PSP_ALL_H
|
||||
#define __NET_PSP_ALL_H
|
||||
|
||||
#include <uapi/linux/psp.h>
|
||||
#include <net/psp/functions.h>
|
||||
#include <net/psp/types.h>
|
||||
|
||||
/* Do not add any code here. Put it in the sub-headers instead. */
|
||||
|
||||
#endif /* __NET_PSP_ALL_H */
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __NET_PSP_HELPERS_H
|
||||
#define __NET_PSP_HELPERS_H
|
||||
|
||||
#include <net/psp/types.h>
|
||||
|
||||
/* Driver-facing API */
|
||||
struct psp_dev *
|
||||
psp_dev_create(struct net_device *netdev, struct psp_dev_ops *psd_ops,
|
||||
struct psp_dev_caps *psd_caps, void *priv_ptr);
|
||||
void psp_dev_unregister(struct psp_dev *psd);
|
||||
|
||||
#endif /* __NET_PSP_HELPERS_H */
|
|
@ -0,0 +1,100 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __NET_PSP_H
|
||||
#define __NET_PSP_H
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/refcount.h>
|
||||
|
||||
struct netlink_ext_ack;
|
||||
|
||||
#define PSP_DEFAULT_UDP_PORT 1000
|
||||
|
||||
struct psphdr {
|
||||
u8 nexthdr;
|
||||
u8 hdrlen;
|
||||
u8 crypt_offset;
|
||||
u8 verfl;
|
||||
__be32 spi;
|
||||
__be64 iv;
|
||||
__be64 vc[]; /* optional */
|
||||
};
|
||||
|
||||
#define PSP_SPI_KEY_ID GENMASK(30, 0)
|
||||
#define PSP_SPI_KEY_PHASE BIT(31)
|
||||
|
||||
#define PSPHDR_CRYPT_OFFSET GENMASK(5, 0)
|
||||
|
||||
#define PSPHDR_VERFL_SAMPLE BIT(7)
|
||||
#define PSPHDR_VERFL_DROP BIT(6)
|
||||
#define PSPHDR_VERFL_VERSION GENMASK(5, 2)
|
||||
#define PSPHDR_VERFL_VIRT BIT(1)
|
||||
#define PSPHDR_VERFL_ONE BIT(0)
|
||||
|
||||
#define PSP_HDRLEN_NOOPT ((sizeof(struct psphdr) - 8) / 8)
|
||||
|
||||
/**
|
||||
* struct psp_dev_config - PSP device configuration
|
||||
* @versions: PSP versions enabled on the device
|
||||
*/
|
||||
struct psp_dev_config {
|
||||
u32 versions;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct psp_dev - PSP device struct
|
||||
* @main_netdev: original netdevice of this PSP device
|
||||
* @ops: driver callbacks
|
||||
* @caps: device capabilities
|
||||
* @drv_priv: driver priv pointer
|
||||
* @lock: instance lock, protects all fields
|
||||
* @refcnt: reference count for the instance
|
||||
* @id: instance id
|
||||
* @config: current device configuration
|
||||
*
|
||||
* @rcu: RCU head for freeing the structure
|
||||
*/
|
||||
struct psp_dev {
|
||||
struct net_device *main_netdev;
|
||||
|
||||
struct psp_dev_ops *ops;
|
||||
struct psp_dev_caps *caps;
|
||||
void *drv_priv;
|
||||
|
||||
struct mutex lock;
|
||||
refcount_t refcnt;
|
||||
|
||||
u32 id;
|
||||
|
||||
struct psp_dev_config config;
|
||||
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct psp_dev_caps - PSP device capabilities
|
||||
*/
|
||||
struct psp_dev_caps {
|
||||
/**
|
||||
* @versions: mask of supported PSP versions
|
||||
* Set this field to 0 to indicate PSP is not supported at all.
|
||||
*/
|
||||
u32 versions;
|
||||
};
|
||||
|
||||
#define PSP_MAX_KEY 32
|
||||
|
||||
/**
|
||||
* struct psp_dev_ops - netdev driver facing PSP callbacks
|
||||
*/
|
||||
struct psp_dev_ops {
|
||||
/**
|
||||
* @set_config: set configuration of a PSP device
|
||||
* Driver can inspect @psd->config for the previous configuration.
|
||||
* Core will update @psd->config with @config on success.
|
||||
*/
|
||||
int (*set_config)(struct psp_dev *psd, struct psp_dev_config *conf,
|
||||
struct netlink_ext_ack *extack);
|
||||
};
|
||||
|
||||
#endif /* __NET_PSP_H */
|
|
@ -0,0 +1,42 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/* Do not edit directly, auto-generated from: */
|
||||
/* Documentation/netlink/specs/psp.yaml */
|
||||
/* YNL-GEN uapi header */
|
||||
|
||||
#ifndef _UAPI_LINUX_PSP_H
|
||||
#define _UAPI_LINUX_PSP_H
|
||||
|
||||
#define PSP_FAMILY_NAME "psp"
|
||||
#define PSP_FAMILY_VERSION 1
|
||||
|
||||
enum psp_version {
|
||||
PSP_VERSION_HDR0_AES_GCM_128,
|
||||
PSP_VERSION_HDR0_AES_GCM_256,
|
||||
PSP_VERSION_HDR0_AES_GMAC_128,
|
||||
PSP_VERSION_HDR0_AES_GMAC_256,
|
||||
};
|
||||
|
||||
enum {
|
||||
PSP_A_DEV_ID = 1,
|
||||
PSP_A_DEV_IFINDEX,
|
||||
PSP_A_DEV_PSP_VERSIONS_CAP,
|
||||
PSP_A_DEV_PSP_VERSIONS_ENA,
|
||||
|
||||
__PSP_A_DEV_MAX,
|
||||
PSP_A_DEV_MAX = (__PSP_A_DEV_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
PSP_CMD_DEV_GET = 1,
|
||||
PSP_CMD_DEV_ADD_NTF,
|
||||
PSP_CMD_DEV_DEL_NTF,
|
||||
PSP_CMD_DEV_SET,
|
||||
PSP_CMD_DEV_CHANGE_NTF,
|
||||
|
||||
__PSP_CMD_MAX,
|
||||
PSP_CMD_MAX = (__PSP_CMD_MAX - 1)
|
||||
};
|
||||
|
||||
#define PSP_MCGRP_MGMT "mgmt"
|
||||
|
||||
#endif /* _UAPI_LINUX_PSP_H */
|
|
@ -82,6 +82,7 @@ config NET_CRC32C
|
|||
menu "Networking options"
|
||||
|
||||
source "net/packet/Kconfig"
|
||||
source "net/psp/Kconfig"
|
||||
source "net/unix/Kconfig"
|
||||
source "net/tls/Kconfig"
|
||||
source "net/xfrm/Kconfig"
|
||||
|
|
|
@ -18,6 +18,7 @@ obj-$(CONFIG_INET) += ipv4/
|
|||
obj-$(CONFIG_TLS) += tls/
|
||||
obj-$(CONFIG_XFRM) += xfrm/
|
||||
obj-$(CONFIG_UNIX) += unix/
|
||||
obj-$(CONFIG_INET_PSP) += psp/
|
||||
obj-y += ipv6/
|
||||
obj-$(CONFIG_PACKET) += packet/
|
||||
obj-$(CONFIG_NET_KEY) += key/
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# PSP configuration
|
||||
#
|
||||
config INET_PSP
|
||||
bool "PSP Security Protocol support"
|
||||
depends on INET
|
||||
help
|
||||
Enable kernel support for the PSP protocol.
|
||||
For more information see:
|
||||
https://raw.githubusercontent.com/google/psp/main/doc/PSP_Arch_Spec.pdf
|
||||
|
||||
If unsure, say N.
|
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_INET_PSP) += psp.o
|
||||
|
||||
psp-y := psp_main.o psp_nl.o psp-nl-gen.o
|
|
@ -0,0 +1,65 @@
|
|||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
/* Do not edit directly, auto-generated from: */
|
||||
/* Documentation/netlink/specs/psp.yaml */
|
||||
/* YNL-GEN kernel source */
|
||||
|
||||
#include <net/netlink.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#include "psp-nl-gen.h"
|
||||
|
||||
#include <uapi/linux/psp.h>
|
||||
|
||||
/* PSP_CMD_DEV_GET - do */
|
||||
static const struct nla_policy psp_dev_get_nl_policy[PSP_A_DEV_ID + 1] = {
|
||||
[PSP_A_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||
};
|
||||
|
||||
/* PSP_CMD_DEV_SET - do */
|
||||
static const struct nla_policy psp_dev_set_nl_policy[PSP_A_DEV_PSP_VERSIONS_ENA + 1] = {
|
||||
[PSP_A_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||
[PSP_A_DEV_PSP_VERSIONS_ENA] = NLA_POLICY_MASK(NLA_U32, 0xf),
|
||||
};
|
||||
|
||||
/* Ops table for psp */
|
||||
static const struct genl_split_ops psp_nl_ops[] = {
|
||||
{
|
||||
.cmd = PSP_CMD_DEV_GET,
|
||||
.pre_doit = psp_device_get_locked,
|
||||
.doit = psp_nl_dev_get_doit,
|
||||
.post_doit = psp_device_unlock,
|
||||
.policy = psp_dev_get_nl_policy,
|
||||
.maxattr = PSP_A_DEV_ID,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = PSP_CMD_DEV_GET,
|
||||
.dumpit = psp_nl_dev_get_dumpit,
|
||||
.flags = GENL_CMD_CAP_DUMP,
|
||||
},
|
||||
{
|
||||
.cmd = PSP_CMD_DEV_SET,
|
||||
.pre_doit = psp_device_get_locked,
|
||||
.doit = psp_nl_dev_set_doit,
|
||||
.post_doit = psp_device_unlock,
|
||||
.policy = psp_dev_set_nl_policy,
|
||||
.maxattr = PSP_A_DEV_PSP_VERSIONS_ENA,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct genl_multicast_group psp_nl_mcgrps[] = {
|
||||
[PSP_NLGRP_MGMT] = { "mgmt", },
|
||||
};
|
||||
|
||||
struct genl_family psp_nl_family __ro_after_init = {
|
||||
.name = PSP_FAMILY_NAME,
|
||||
.version = PSP_FAMILY_VERSION,
|
||||
.netnsok = true,
|
||||
.parallel_ops = true,
|
||||
.module = THIS_MODULE,
|
||||
.split_ops = psp_nl_ops,
|
||||
.n_split_ops = ARRAY_SIZE(psp_nl_ops),
|
||||
.mcgrps = psp_nl_mcgrps,
|
||||
.n_mcgrps = ARRAY_SIZE(psp_nl_mcgrps),
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/* Do not edit directly, auto-generated from: */
|
||||
/* Documentation/netlink/specs/psp.yaml */
|
||||
/* YNL-GEN kernel header */
|
||||
|
||||
#ifndef _LINUX_PSP_GEN_H
|
||||
#define _LINUX_PSP_GEN_H
|
||||
|
||||
#include <net/netlink.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#include <uapi/linux/psp.h>
|
||||
|
||||
int psp_device_get_locked(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb, struct genl_info *info);
|
||||
void
|
||||
psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
|
||||
int psp_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int psp_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
|
||||
int psp_nl_dev_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
|
||||
enum {
|
||||
PSP_NLGRP_MGMT,
|
||||
};
|
||||
|
||||
extern struct genl_family psp_nl_family;
|
||||
|
||||
#endif /* _LINUX_PSP_GEN_H */
|
|
@ -0,0 +1,31 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __PSP_PSP_H
|
||||
#define __PSP_PSP_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <net/netns/generic.h>
|
||||
#include <net/psp.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
extern struct xarray psp_devs;
|
||||
extern struct mutex psp_devs_lock;
|
||||
|
||||
void psp_dev_destroy(struct psp_dev *psd);
|
||||
int psp_dev_check_access(struct psp_dev *psd, struct net *net);
|
||||
|
||||
void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd);
|
||||
|
||||
static inline void psp_dev_get(struct psp_dev *psd)
|
||||
{
|
||||
refcount_inc(&psd->refcnt);
|
||||
}
|
||||
|
||||
static inline void psp_dev_put(struct psp_dev *psd)
|
||||
{
|
||||
if (refcount_dec_and_test(&psd->refcnt))
|
||||
psp_dev_destroy(psd);
|
||||
}
|
||||
|
||||
#endif /* __PSP_PSP_H */
|
|
@ -0,0 +1,139 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/xarray.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/psp.h>
|
||||
|
||||
#include "psp.h"
|
||||
#include "psp-nl-gen.h"
|
||||
|
||||
DEFINE_XARRAY_ALLOC1(psp_devs);
|
||||
struct mutex psp_devs_lock;
|
||||
|
||||
/**
|
||||
* DOC: PSP locking
|
||||
*
|
||||
* psp_devs_lock protects the psp_devs xarray.
|
||||
* Ordering is take the psp_devs_lock and then the instance lock.
|
||||
* Each instance is protected by RCU, and has a refcount.
|
||||
* When driver unregisters the instance gets flushed, but struct sticks around.
|
||||
*/
|
||||
|
||||
/**
|
||||
* psp_dev_check_access() - check if user in a given net ns can access PSP dev
|
||||
* @psd: PSP device structure user is trying to access
|
||||
* @net: net namespace user is in
|
||||
*
|
||||
* Return: 0 if PSP device should be visible in @net, errno otherwise.
|
||||
*/
|
||||
int psp_dev_check_access(struct psp_dev *psd, struct net *net)
|
||||
{
|
||||
if (dev_net(psd->main_netdev) == net)
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* psp_dev_create() - create and register PSP device
|
||||
* @netdev: main netdevice
|
||||
* @psd_ops: driver callbacks
|
||||
* @psd_caps: device capabilities
|
||||
* @priv_ptr: back-pointer to driver private data
|
||||
*
|
||||
* Return: pointer to allocated PSP device, or ERR_PTR.
|
||||
*/
|
||||
struct psp_dev *
|
||||
psp_dev_create(struct net_device *netdev,
|
||||
struct psp_dev_ops *psd_ops, struct psp_dev_caps *psd_caps,
|
||||
void *priv_ptr)
|
||||
{
|
||||
struct psp_dev *psd;
|
||||
static u32 last_id;
|
||||
int err;
|
||||
|
||||
if (WARN_ON(!psd_caps->versions ||
|
||||
!psd_ops->set_config))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
psd = kzalloc(sizeof(*psd), GFP_KERNEL);
|
||||
if (!psd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
psd->main_netdev = netdev;
|
||||
psd->ops = psd_ops;
|
||||
psd->caps = psd_caps;
|
||||
psd->drv_priv = priv_ptr;
|
||||
|
||||
mutex_init(&psd->lock);
|
||||
refcount_set(&psd->refcnt, 1);
|
||||
|
||||
mutex_lock(&psp_devs_lock);
|
||||
err = xa_alloc_cyclic(&psp_devs, &psd->id, psd, xa_limit_16b,
|
||||
&last_id, GFP_KERNEL);
|
||||
if (err) {
|
||||
mutex_unlock(&psp_devs_lock);
|
||||
kfree(psd);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
mutex_lock(&psd->lock);
|
||||
mutex_unlock(&psp_devs_lock);
|
||||
|
||||
psp_nl_notify_dev(psd, PSP_CMD_DEV_ADD_NTF);
|
||||
|
||||
rcu_assign_pointer(netdev->psp_dev, psd);
|
||||
|
||||
mutex_unlock(&psd->lock);
|
||||
|
||||
return psd;
|
||||
}
|
||||
EXPORT_SYMBOL(psp_dev_create);
|
||||
|
||||
void psp_dev_destroy(struct psp_dev *psd)
|
||||
{
|
||||
mutex_lock(&psp_devs_lock);
|
||||
xa_erase(&psp_devs, psd->id);
|
||||
mutex_unlock(&psp_devs_lock);
|
||||
|
||||
mutex_destroy(&psd->lock);
|
||||
kfree_rcu(psd, rcu);
|
||||
}
|
||||
|
||||
/**
|
||||
* psp_dev_unregister() - unregister PSP device
|
||||
* @psd: PSP device structure
|
||||
*/
|
||||
void psp_dev_unregister(struct psp_dev *psd)
|
||||
{
|
||||
mutex_lock(&psp_devs_lock);
|
||||
mutex_lock(&psd->lock);
|
||||
|
||||
psp_nl_notify_dev(psd, PSP_CMD_DEV_DEL_NTF);
|
||||
|
||||
/* Wait until psp_dev_destroy() to call xa_erase() to prevent a
|
||||
* different psd from being added to the xarray with this id, while
|
||||
* there are still references to this psd being held.
|
||||
*/
|
||||
xa_store(&psp_devs, psd->id, NULL, GFP_KERNEL);
|
||||
mutex_unlock(&psp_devs_lock);
|
||||
|
||||
rcu_assign_pointer(psd->main_netdev->psp_dev, NULL);
|
||||
|
||||
psd->ops = NULL;
|
||||
psd->drv_priv = NULL;
|
||||
|
||||
mutex_unlock(&psd->lock);
|
||||
|
||||
psp_dev_put(psd);
|
||||
}
|
||||
EXPORT_SYMBOL(psp_dev_unregister);
|
||||
|
||||
static int __init psp_init(void)
|
||||
{
|
||||
mutex_init(&psp_devs_lock);
|
||||
|
||||
return genl_register_family(&psp_nl_family);
|
||||
}
|
||||
|
||||
subsys_initcall(psp_init);
|
|
@ -0,0 +1,223 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/xarray.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <net/psp.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "psp-nl-gen.h"
|
||||
#include "psp.h"
|
||||
|
||||
/* Netlink helpers */
|
||||
|
||||
static struct sk_buff *psp_nl_reply_new(struct genl_info *info)
|
||||
{
|
||||
struct sk_buff *rsp;
|
||||
void *hdr;
|
||||
|
||||
rsp = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!rsp)
|
||||
return NULL;
|
||||
|
||||
hdr = genlmsg_iput(rsp, info);
|
||||
if (!hdr) {
|
||||
nlmsg_free(rsp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rsp;
|
||||
}
|
||||
|
||||
static int psp_nl_reply_send(struct sk_buff *rsp, struct genl_info *info)
|
||||
{
|
||||
/* Note that this *only* works with a single message per skb! */
|
||||
nlmsg_end(rsp, (struct nlmsghdr *)rsp->data);
|
||||
|
||||
return genlmsg_reply(rsp, info);
|
||||
}
|
||||
|
||||
/* Device stuff */
|
||||
|
||||
static struct psp_dev *
|
||||
psp_device_get_and_lock(struct net *net, struct nlattr *dev_id)
|
||||
{
|
||||
struct psp_dev *psd;
|
||||
int err;
|
||||
|
||||
mutex_lock(&psp_devs_lock);
|
||||
psd = xa_load(&psp_devs, nla_get_u32(dev_id));
|
||||
if (!psd) {
|
||||
mutex_unlock(&psp_devs_lock);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
mutex_lock(&psd->lock);
|
||||
mutex_unlock(&psp_devs_lock);
|
||||
|
||||
err = psp_dev_check_access(psd, net);
|
||||
if (err) {
|
||||
mutex_unlock(&psd->lock);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return psd;
|
||||
}
|
||||
|
||||
int psp_device_get_locked(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
if (GENL_REQ_ATTR_CHECK(info, PSP_A_DEV_ID))
|
||||
return -EINVAL;
|
||||
|
||||
info->user_ptr[0] = psp_device_get_and_lock(genl_info_net(info),
|
||||
info->attrs[PSP_A_DEV_ID]);
|
||||
return PTR_ERR_OR_ZERO(info->user_ptr[0]);
|
||||
}
|
||||
|
||||
void
|
||||
psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct psp_dev *psd = info->user_ptr[0];
|
||||
|
||||
mutex_unlock(&psd->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
|
||||
const struct genl_info *info)
|
||||
{
|
||||
void *hdr;
|
||||
|
||||
hdr = genlmsg_iput(rsp, info);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_u32(rsp, PSP_A_DEV_ID, psd->id) ||
|
||||
nla_put_u32(rsp, PSP_A_DEV_IFINDEX, psd->main_netdev->ifindex) ||
|
||||
nla_put_u32(rsp, PSP_A_DEV_PSP_VERSIONS_CAP, psd->caps->versions) ||
|
||||
nla_put_u32(rsp, PSP_A_DEV_PSP_VERSIONS_ENA, psd->config.versions))
|
||||
goto err_cancel_msg;
|
||||
|
||||
genlmsg_end(rsp, hdr);
|
||||
return 0;
|
||||
|
||||
err_cancel_msg:
|
||||
genlmsg_cancel(rsp, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd)
|
||||
{
|
||||
struct genl_info info;
|
||||
struct sk_buff *ntf;
|
||||
|
||||
if (!genl_has_listeners(&psp_nl_family, dev_net(psd->main_netdev),
|
||||
PSP_NLGRP_MGMT))
|
||||
return;
|
||||
|
||||
ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!ntf)
|
||||
return;
|
||||
|
||||
genl_info_init_ntf(&info, &psp_nl_family, cmd);
|
||||
if (psp_nl_dev_fill(psd, ntf, &info)) {
|
||||
nlmsg_free(ntf);
|
||||
return;
|
||||
}
|
||||
|
||||
genlmsg_multicast_netns(&psp_nl_family, dev_net(psd->main_netdev), ntf,
|
||||
0, PSP_NLGRP_MGMT, GFP_KERNEL);
|
||||
}
|
||||
|
||||
int psp_nl_dev_get_doit(struct sk_buff *req, struct genl_info *info)
|
||||
{
|
||||
struct psp_dev *psd = info->user_ptr[0];
|
||||
struct sk_buff *rsp;
|
||||
int err;
|
||||
|
||||
rsp = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!rsp)
|
||||
return -ENOMEM;
|
||||
|
||||
err = psp_nl_dev_fill(psd, rsp, info);
|
||||
if (err)
|
||||
goto err_free_msg;
|
||||
|
||||
return genlmsg_reply(rsp, info);
|
||||
|
||||
err_free_msg:
|
||||
nlmsg_free(rsp);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
psp_nl_dev_get_dumpit_one(struct sk_buff *rsp, struct netlink_callback *cb,
|
||||
struct psp_dev *psd)
|
||||
{
|
||||
if (psp_dev_check_access(psd, sock_net(rsp->sk)))
|
||||
return 0;
|
||||
|
||||
return psp_nl_dev_fill(psd, rsp, genl_info_dump(cb));
|
||||
}
|
||||
|
||||
int psp_nl_dev_get_dumpit(struct sk_buff *rsp, struct netlink_callback *cb)
|
||||
{
|
||||
struct psp_dev *psd;
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&psp_devs_lock);
|
||||
xa_for_each_start(&psp_devs, cb->args[0], psd, cb->args[0]) {
|
||||
mutex_lock(&psd->lock);
|
||||
err = psp_nl_dev_get_dumpit_one(rsp, cb, psd);
|
||||
mutex_unlock(&psd->lock);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&psp_devs_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int psp_nl_dev_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct psp_dev *psd = info->user_ptr[0];
|
||||
struct psp_dev_config new_config;
|
||||
struct sk_buff *rsp;
|
||||
int err;
|
||||
|
||||
memcpy(&new_config, &psd->config, sizeof(new_config));
|
||||
|
||||
if (info->attrs[PSP_A_DEV_PSP_VERSIONS_ENA]) {
|
||||
new_config.versions =
|
||||
nla_get_u32(info->attrs[PSP_A_DEV_PSP_VERSIONS_ENA]);
|
||||
if (new_config.versions & ~psd->caps->versions) {
|
||||
NL_SET_ERR_MSG(info->extack, "Requested PSP versions not supported by the device");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
NL_SET_ERR_MSG(info->extack, "No settings present");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rsp = psp_nl_reply_new(info);
|
||||
if (!rsp)
|
||||
return -ENOMEM;
|
||||
|
||||
if (memcmp(&new_config, &psd->config, sizeof(new_config))) {
|
||||
err = psd->ops->set_config(psd, &new_config, info->extack);
|
||||
if (err)
|
||||
goto err_free_rsp;
|
||||
|
||||
memcpy(&psd->config, &new_config, sizeof(new_config));
|
||||
}
|
||||
|
||||
psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF);
|
||||
|
||||
return psp_nl_reply_send(rsp, info);
|
||||
|
||||
err_free_rsp:
|
||||
nlmsg_free(rsp);
|
||||
return err;
|
||||
}
|
|
@ -31,6 +31,7 @@ CFLAGS_ovpn:=$(call get_hdr_inc,_LINUX_OVPN_H,ovpn.h)
|
|||
CFLAGS_ovs_datapath:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h)
|
||||
CFLAGS_ovs_flow:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h)
|
||||
CFLAGS_ovs_vport:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h)
|
||||
CFLAGS_psp:=$(call get_hdr_inc,_LINUX_PSP_H,psp.h)
|
||||
CFLAGS_rt-addr:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
|
||||
$(call get_hdr_inc,__LINUX_IF_ADDR_H,if_addr.h)
|
||||
CFLAGS_rt-link:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
|
||||
|
|
Loading…
Reference in New Issue