mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Todd Freed <toddfreed@meta.com>
To: <netdev@vger.kernel.org>, <toddfreed@meta.com>
Cc: "David S. Miller" <davem@davemloft.net>,
	"Andrew Lunn" <andrew+netdev@lunn.ch>,
	"Björn Töpel" <bjorn@kernel.org>,
	bpf@vger.kernel.org, "Daniel Borkmann" <daniel@iogearbox.net>,
	"Daniel Zahka" <daniel.zahka@gmail.com>,
	"Donald Hunter" <donald.hunter@gmail.com>,
	"Eric Dumazet" <edumazet@kernel.org>,
	"Jakub Kicinski" <kuba@kernel.org>,
	"Jamal Hadi Salim" <jhs@mojatatu.com>,
	"Jiri Pirko" <jiri@resnulli.us>,
	"John Fastabend" <john.fastabend@gmail.com>,
	linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-rdma@vger.kernel.org,
	"Martin KaFai Lau" <martin.lau@linux.dev>,
	"Paolo Abeni" <pabeni@redhat.com>,
	"Saeed Mahameed" <saeedm@nvidia.com>,
	"Simon Horman" <horms@kernel.org>,
	"Tariq Toukan" <tariqt@nvidia.com>,
	"Wei Wang" <weiwan@google.com>,
	"Willem de Bruijn" <willemdebruijn.kernel@gmail.com>
Subject: [PATCH net-next v1 09/15] net/sched: add act_psp for psp transport mode
Date: Tue, 10 Nov 2026 16:55:27 -0800	[thread overview]
Message-ID: <20261111005533.6436-10-toddfreed@meta.com> (raw)
In-Reply-To: <20261111005533.6436-1-toddfreed@meta.com>

adds tc action act_psp for psp transport mode encapsulation and
encryption.

encap attaches a psp tx association to the skb as a METADATA_PSP dst.
The packet must then be transmitted on the association's PSP device's
main netdev, which encrypts it: encap on that netdev's egress, or a
redirect there from another netdev's egress. Any other device drops it.

encap on ingress is hardware offload only, like tunnel mode: in
software, the action drops the packet. an ingress-encapped packet would
go through the stack with only its METADATA_PSP dst to keep it from
leaving in clear text, and many paths replace the dst: routing,
netfilter, bpf redirects, tc actions on the way out.

on egress, encap sets skb->decrypted, so that sk_validate_xmit_skb()
drops the packet if it loses the METADATA_PSP dst on the way. mirred
and bpf don't send such a packet back to ingress (previous patches).

Some kinds of packets are not supported and will be dropped,

 - ipv4 fragments. encap inserts udp+psp between the l3 and l4 headers,
   so transport mode can't carry fragments. tunnel mode psp, which a
   later series adds, does support them.
 - ipv6 packets with extension headers, including fragments. some
   extension headers could be supported in the future.
 - packets already encapsulated for a tunnel (skb->encapsulation). some
   tunnel types could be supported in the future.
 - packets which are too large. on transmit, packets which won't fit the
   device's mtu once encapsulated are dropped (see the METADATA_PSP
   patch). the traffic's mtu needs to leave room for the psp encap.
 - packets with another encryption offload: already marked decrypted,
   or of a psp or ktls offload socket, for which sk_validate_xmit_skb()
   would apply the socket's validator instead of checking the
   association. a socket can gain an offload while its packet is queued,
   so sk_validate_xmit_skb() also drops a packet with a METADATA_PSP dst
   whose socket has a validator.

decap removes the PSP Rx state of a packet the device decrypted. to
match packets of only one association, a rule should match the psp
header fields corresponding to the association, e.g. psp_spi / psp_ver /
psp_dev. those flower matches are added in a subsequent patch.

Signed-off-by: Todd Freed <toddfreed@meta.com>
---
 MAINTAINERS                        |   3 +
 include/net/tc_act/tc_psp.h        |  23 ++
 include/uapi/linux/pkt_cls.h       |   1 +
 include/uapi/linux/tc_act/tc_psp.h |  28 ++
 net/core/dev.c                     |  13 +-
 net/sched/Kconfig                  |  13 +
 net/sched/Makefile                 |   1 +
 net/sched/act_psp.c                | 439 +++++++++++++++++++++++++++++
 8 files changed, 519 insertions(+), 2 deletions(-)
 create mode 100644 include/net/tc_act/tc_psp.h
 create mode 100644 include/uapi/linux/tc_act/tc_psp.h
 create mode 100644 net/sched/act_psp.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 76fb13c8730b..a8e45efd8baf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22533,8 +22533,11 @@ F:	Documentation/netlink/specs/psp.yaml
 F:	Documentation/networking/psp.rst
 F:	include/net/psp/
 F:	include/net/psp.h
+F:	include/net/tc_act/tc_psp.h
 F:	include/uapi/linux/psp.h
+F:	include/uapi/linux/tc_act/tc_psp.h
 F:	net/psp/
+F:	net/sched/act_psp.c
 K:	struct\ psp(_assoc|_dev|hdr)\b
 
 PSTORE FILESYSTEM
diff --git a/include/net/tc_act/tc_psp.h b/include/net/tc_act/tc_psp.h
new file mode 100644
index 000000000000..8640d7685475
--- /dev/null
+++ b/include/net/tc_act/tc_psp.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __NET_TC_PSP_H
+#define __NET_TC_PSP_H
+
+#include <net/act_api.h>
+#include <net/dst_metadata.h>
+#include <linux/tc_act/tc_psp.h>
+
+struct tcf_psp_params {
+	struct rcu_head		rcu;
+	int			action;
+	int			psp_action;
+	struct metadata_dst	*md;
+};
+
+struct tcf_psp {
+	struct tc_action		common;
+	struct tcf_psp_params __rcu	*params;
+};
+
+#define to_psp(a) ((struct tcf_psp *)a)
+
+#endif /* __NET_TC_PSP_H */
diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
index 28d94b11d1aa..14cff8f5da74 100644
--- a/include/uapi/linux/pkt_cls.h
+++ b/include/uapi/linux/pkt_cls.h
@@ -139,6 +139,7 @@ enum tca_id {
 	TCA_ID_MPLS,
 	TCA_ID_CT,
 	TCA_ID_GATE,
+	TCA_ID_PSP,
 	/* other actions go here */
 	__TCA_ID_MAX = 255
 };
diff --git a/include/uapi/linux/tc_act/tc_psp.h b/include/uapi/linux/tc_act/tc_psp.h
new file mode 100644
index 000000000000..e58ebcae77be
--- /dev/null
+++ b/include/uapi/linux/tc_act/tc_psp.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_TC_PSP_H
+#define __LINUX_TC_PSP_H
+
+#include <linux/pkt_cls.h>
+
+#define TCA_PSP_ACT_ENCAP	1	/* attach a PSP association */
+#define TCA_PSP_ACT_DECAP	2	/* drop PSP Rx state */
+
+struct tc_psp {
+	tc_gen;
+};
+
+enum {
+	TCA_PSP_UNSPEC,
+	TCA_PSP_TM,
+	TCA_PSP_PARMS,
+	TCA_PSP_PAD,
+	TCA_PSP_ACTION,		/* u32, TCA_PSP_ACT_* */
+	TCA_PSP_ASSOC_ID,	/* u32, encap only: Tx association ID from
+				 * the PSP netlink family (assoc-add)
+				 */
+	__TCA_PSP_MAX,
+};
+
+#define TCA_PSP_MAX (__TCA_PSP_MAX - 1)
+
+#endif
diff --git a/net/core/dev.c b/net/core/dev.c
index 2cca5f0d302f..353c0409b3c0 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4154,13 +4154,22 @@ static struct sk_buff *sk_validate_xmit_skb(struct sk_buff *skb,
 	sk_validate = NULL;
 	if (sk) {
 		if (sk_fullsock(sk))
-			sk_validate = sk->sk_validate_xmit_skb;
+			sk_validate = READ_ONCE(sk->sk_validate_xmit_skb);
 		else if (sk_is_inet(sk) && sk->sk_state == TCP_TIME_WAIT)
 			sk_validate = inet_twsk(sk)->tw_validate_xmit_skb;
 	}
 
 	if (sk_validate) {
-		skb = sk_validate(sk, dev, skb);
+		/* act_psp doesn't give an association to the packets of a
+		 * socket with its own offload, but the socket can gain one
+		 * while such a packet is queued
+		 */
+		if (unlikely(skb_psp_md(skb))) {
+			kfree_skb_reason(skb, SKB_DROP_REASON_PSP_OUTPUT);
+			skb = NULL;
+		} else {
+			skb = sk_validate(sk, dev, skb);
+		}
 	} else if (unlikely(skb_psp_md(skb))) {
 		int psp = psp_xmit_md(skb, dev);
 
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 6ddff028b81a..35f9d76c4cae 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -939,6 +939,19 @@ config NET_ACT_GATE
 	  To compile this code as a module, choose M here: the
 	  module will be called act_gate.
 
+config NET_ACT_PSP
+	tristate "PSP encryption tc action"
+	depends on NET_CLS_ACT && INET_PSP
+	help
+	  Say Y here to allow PSP associations, created through the PSP
+	  netlink family, to be attached to packets. PSP capable devices can
+	  then encrypt them on transmit. Enables transparent psp tunnels for
+	  forwarded traffic.
+
+	  If unsure, say N.
+	  To compile this code as a module, choose M here: the
+	  module will be called act_psp.
+
 config NET_IFE_SKBMARK
 	tristate "Support to encoding decoding skb mark on IFE action"
 	depends on NET_ACT_IFE
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 5078ea84e6ad..9fe8cc0abfdf 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_NET_IFE_SKBTCINDEX)	+= act_meta_skbtcindex.o
 obj-$(CONFIG_NET_ACT_TUNNEL_KEY)+= act_tunnel_key.o
 obj-$(CONFIG_NET_ACT_CT)	+= act_ct.o
 obj-$(CONFIG_NET_ACT_GATE)	+= act_gate.o
+obj-$(CONFIG_NET_ACT_PSP)	+= act_psp.o
 obj-$(CONFIG_NET_SCH_FIFO)	+= sch_fifo.o
 obj-$(CONFIG_NET_SCH_HTB)	+= sch_htb.o
 obj-$(CONFIG_NET_SCH_HFSC)	+= sch_hfsc.o
diff --git a/net/sched/act_psp.c b/net/sched/act_psp.c
new file mode 100644
index 000000000000..4ff54203c9f5
--- /dev/null
+++ b/net/sched/act_psp.c
@@ -0,0 +1,439 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * tc action that attaches a PSP association to packets.
+ *
+ * encap: attach a tx association referenced by its ID, as a METADATA_PSP dst.
+ *        The PSP device's netdev encrypts the packet on transmit. In
+ *        software, egress only: like tunnel mode, encap on ingress is
+ *        hardware offload only.
+ * decap: drop the PSP Rx state (SKB_EXT_PSP) of a decrypted packet. It
+ *        takes no association. a rule which should only take the packets
+ *        of one association matches its properties against psp packet header
+ *        fields (flower psp_spi / psp_ver / psp_dev).
+ */
+
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <net/dst.h>
+#include <net/inet_timewait_sock.h>
+#include <net/ip.h>
+#include <net/netlink.h>
+#include <net/pkt_cls.h>
+#include <net/pkt_sched.h>
+#include <net/psp/functions.h>
+#include <net/tc_act/tc_psp.h>
+
+static struct tc_action_ops act_psp_ops;
+
+/* The device inserts UDP + PSP between the IP header and the transport
+ * header, so the transport header must be right by the time the packet
+ * gets there. On a forwarded packet it may not be. Transport mode doesn't
+ * support IP fragments or IPv6 extension headers.
+ */
+static bool tcf_psp_set_transport_header(struct sk_buff *skb)
+{
+	unsigned int off = skb_network_offset(skb);
+	const struct iphdr *iph;
+	unsigned int hlen, len;
+
+	switch (skb->protocol) {
+	case htons(ETH_P_IP):
+		if (!pskb_may_pull(skb, off + sizeof(*iph)))
+			return false;
+		iph = ip_hdr(skb);
+		if (iph->version != 4 || iph->ihl < 5 || ip_is_fragment(iph))
+			return false;
+		hlen = iph->ihl * 4;
+		len = ntohs(iph->tot_len);
+		if (!pskb_may_pull(skb, off + hlen))
+			return false;
+		break;
+	case htons(ETH_P_IPV6):
+		if (!pskb_may_pull(skb, off + sizeof(struct ipv6hdr)))
+			return false;
+		if (ipv6_hdr(skb)->version != 6 ||
+		    ipv6_ext_hdr(ipv6_hdr(skb)->nexthdr))
+			return false;
+		hlen = sizeof(struct ipv6hdr);
+		len = hlen + ntohs(ipv6_hdr(skb)->payload_len);
+		break;
+	default:
+		return false;
+	}
+
+	/* The device takes the length of the payload from the packet: it must
+	 * hold the IP length, and L2 padding, e.g. of a minimum size frame,
+	 * must not be encrypted as payload. A GSO skb's header doesn't always
+	 * carry its length (BIG TCP).
+	 */
+	if (!skb_is_gso(skb)) {
+		if (len < hlen || skb->len - off < len)
+			return false;
+		if (pskb_trim_rcsum(skb, off + len))
+			return false;
+	}
+
+	skb_set_transport_header(skb, off + hlen);
+	return true;
+}
+
+/* The socket of the packet has its own encryption offload, kTLS or PSP:
+ * sk_validate_xmit_skb() then applies the socket's validator instead of
+ * checking the association.
+ */
+static bool tcf_psp_sk_offload(const struct sk_buff *skb)
+{
+#ifdef CONFIG_SOCK_VALIDATE_XMIT
+	struct sock *sk = skb->sk;
+
+	if (!sk)
+		return false;
+	if (sk_fullsock(sk))
+		return !!READ_ONCE(sk->sk_validate_xmit_skb);
+	return sk_is_inet(sk) && sk->sk_state == TCP_TIME_WAIT &&
+	       inet_twsk(sk)->tw_validate_xmit_skb;
+#else
+	return false;
+#endif
+}
+
+static int tcf_psp_drop(struct tcf_psp *p, struct sk_buff *skb,
+			enum skb_drop_reason reason)
+{
+	tcf_action_inc_drop_qstats(&p->common);
+	tcf_set_drop_reason(skb, reason);
+	return TC_ACT_SHOT;
+}
+
+static int tcf_psp_act(struct sk_buff *skb, const struct tc_action *a,
+		       struct tcf_result *res)
+{
+	struct tcf_psp *p = to_psp(a);
+	struct tcf_psp_params *params;
+
+	params = rcu_dereference_bh(p->params);
+
+	tcf_lastuse_update(&p->tcf_tm);
+	tcf_action_update_bstats(&p->common, skb);
+
+	switch (params->psp_action) {
+	case TCA_PSP_ACT_ENCAP:
+		/* Encap on ingress is hardware offload only, like tunnel
+		 * mode: the packet would go through the stack with nothing
+		 * but its dst, which routing, netfilter or a redirect may
+		 * replace, to keep it from leaving in clear text
+		 */
+		if (unlikely(skb_at_tc_ingress(skb)))
+			return tcf_psp_drop(p, skb, SKB_DROP_REASON_PSP_OUTPUT);
+		/* PSP on a tunnel isn't supported: the encap would overwrite
+		 * the inner headers which the device's tunnel offloads use
+		 */
+		if (unlikely(skb->encapsulation))
+			return tcf_psp_drop(p, skb, SKB_DROP_REASON_PSP_OUTPUT);
+		/* Don't mix the association with another encryption
+		 * offload: the packet is already marked decrypted, or its
+		 * socket has an offload of its own
+		 */
+		if (unlikely(skb_is_decrypted(skb) || tcf_psp_sk_offload(skb)))
+			return tcf_psp_drop(p, skb, SKB_DROP_REASON_PSP_OUTPUT);
+		if (unlikely(!tcf_psp_set_transport_header(skb)))
+			return tcf_psp_drop(p, skb, SKB_DROP_REASON_PSP_OUTPUT);
+		skb_dst_drop(skb);
+		skb_dst_set(skb, dst_clone(&params->md->dst));
+		/* Must be encrypted: sk_validate_xmit_skb() drops it unless
+		 * it still carries the association when it is transmitted
+		 */
+		skb->decrypted = 1;
+		break;
+	case TCA_PSP_ACT_DECAP:
+		/* Only a packet the device decrypted: on transmit,
+		 * skb->decrypted means "device, please encrypt", which decap
+		 * must not undo.
+		 */
+		if (!skb_ext_find(skb, SKB_EXT_PSP))
+			break;
+		skb_ext_del(skb, SKB_EXT_PSP);
+		skb->decrypted = 0;
+		break;
+	}
+
+	return params->action;
+}
+
+static const struct nla_policy psp_policy[TCA_PSP_MAX + 1] = {
+	[TCA_PSP_PARMS]		= NLA_POLICY_EXACT_LEN(sizeof(struct tc_psp)),
+	[TCA_PSP_ACTION]	= NLA_POLICY_RANGE(NLA_U32, TCA_PSP_ACT_ENCAP,
+						   TCA_PSP_ACT_DECAP),
+	[TCA_PSP_ASSOC_ID]	= NLA_POLICY_MIN(NLA_U32, 1),
+};
+
+static void tcf_psp_release_params_rcu(struct rcu_head *head)
+{
+	struct tcf_psp_params *params = container_of(head, typeof(*params),
+						     rcu);
+
+	if (params->md)
+		dst_release(&params->md->dst);
+	kfree(params);
+}
+
+static void tcf_psp_release_params(struct tcf_psp_params *params)
+{
+	if (params)
+		call_rcu(&params->rcu, tcf_psp_release_params_rcu);
+}
+
+static struct metadata_dst *
+tcf_psp_md_create(struct net *net, struct nlattr *id_attr,
+		  struct netlink_ext_ack *extack)
+{
+	struct metadata_dst *md;
+	struct psp_assoc *pas;
+
+	pas = psp_assoc_get_by_id(net, nla_get_u32(id_attr));
+	if (!pas) {
+		NL_SET_ERR_MSG_ATTR(extack, id_attr,
+				    "PSP association not found");
+		return ERR_PTR(-ENOENT);
+	}
+	if (!psp_assoc_is_tx(pas)) {
+		NL_SET_ERR_MSG_ATTR(extack, id_attr,
+				    "encap needs a Tx association (assoc-add)");
+		psp_assoc_put(pas);
+		return ERR_PTR(-EINVAL);
+	}
+
+	md = metadata_dst_alloc(0, METADATA_PSP, GFP_KERNEL);
+	if (!md) {
+		psp_assoc_put(pas);
+		return ERR_PTR(-ENOMEM);
+	}
+	/* The dst now owns the reference on the association */
+	md->u.psp_info.pas = pas;
+
+	return md;
+}
+
+static int tcf_psp_init(struct net *net, struct nlattr *nla,
+			struct nlattr *est, struct tc_action **a,
+			struct tcf_proto *tp, u32 flags,
+			struct netlink_ext_ack *extack)
+{
+	struct tc_action_net *tn = net_generic(net, act_psp_ops.net_id);
+	bool bind = flags & TCA_ACT_FLAGS_BIND;
+	struct nlattr *tb[TCA_PSP_MAX + 1];
+	struct tcf_psp_params *params_new;
+	struct metadata_dst *md = NULL;
+	struct tcf_chain *goto_ch = NULL;
+	struct tc_psp *parm;
+	bool exists = false;
+	int ret = 0, err;
+	struct tcf_psp *p;
+	u32 psp_action;
+	u32 index;
+
+	if (!nla) {
+		NL_SET_ERR_MSG(extack, "PSP action requires attributes");
+		return -EINVAL;
+	}
+
+	err = nla_parse_nested(tb, TCA_PSP_MAX, nla, psp_policy, extack);
+	if (err < 0)
+		return err;
+
+	if (!tb[TCA_PSP_PARMS]) {
+		NL_SET_ERR_MSG(extack, "Missing PSP action parameters");
+		return -EINVAL;
+	}
+
+	parm = nla_data(tb[TCA_PSP_PARMS]);
+	index = parm->index;
+	err = tcf_idr_check_alloc(tn, &index, a, bind);
+	if (err < 0)
+		return err;
+	exists = err;
+	if (exists && bind)
+		return ACT_P_BOUND;
+
+	if (NL_REQ_ATTR_CHECK(extack, nla, tb, TCA_PSP_ACTION)) {
+		NL_SET_ERR_MSG(extack, "Missing PSP action, encap or decap");
+		ret = -EINVAL;
+		goto err_out;
+	}
+	psp_action = nla_get_u32(tb[TCA_PSP_ACTION]);
+
+	switch (psp_action) {
+	case TCA_PSP_ACT_ENCAP:
+		if (NL_REQ_ATTR_CHECK(extack, nla, tb, TCA_PSP_ASSOC_ID)) {
+			NL_SET_ERR_MSG(extack, "Missing PSP association ID");
+			ret = -EINVAL;
+			goto err_out;
+		}
+		md = tcf_psp_md_create(net, tb[TCA_PSP_ASSOC_ID], extack);
+		if (IS_ERR(md)) {
+			ret = PTR_ERR(md);
+			md = NULL;
+			goto err_out;
+		}
+		break;
+	case TCA_PSP_ACT_DECAP:
+		if (tb[TCA_PSP_ASSOC_ID]) {
+			NL_SET_ERR_MSG_ATTR(extack, tb[TCA_PSP_ASSOC_ID],
+					    "decap takes no PSP association");
+			ret = -EINVAL;
+			goto err_out;
+		}
+		break;
+	}
+
+	if (!exists) {
+		ret = tcf_idr_create_from_flags(tn, index, est, a,
+						&act_psp_ops, bind, flags);
+		if (ret)
+			goto release_md;
+
+		ret = ACT_P_CREATED;
+	} else if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
+		NL_SET_ERR_MSG(extack, "PSP action already exists");
+		ret = -EEXIST;
+		goto release_md;
+	}
+
+	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
+	if (err < 0) {
+		ret = err;
+		exists = true;
+		goto release_md;
+	}
+	p = to_psp(*a);
+
+	params_new = kzalloc_obj(*params_new);
+	if (!params_new) {
+		ret = -ENOMEM;
+		exists = true;
+		goto put_chain;
+	}
+	params_new->action = parm->action;
+	params_new->psp_action = psp_action;
+	params_new->md = md;
+
+	spin_lock_bh(&p->tcf_lock);
+	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
+	params_new = rcu_replace_pointer(p->params, params_new,
+					 lockdep_is_held(&p->tcf_lock));
+	spin_unlock_bh(&p->tcf_lock);
+
+	tcf_psp_release_params(params_new);
+	if (goto_ch)
+		tcf_chain_put_by_act(goto_ch);
+
+	return ret;
+
+put_chain:
+	if (goto_ch)
+		tcf_chain_put_by_act(goto_ch);
+release_md:
+	if (md)
+		dst_release(&md->dst);
+err_out:
+	if (exists)
+		tcf_idr_release(*a, bind);
+	else
+		tcf_idr_cleanup(tn, index);
+	return ret;
+}
+
+static void tcf_psp_cleanup(struct tc_action *a)
+{
+	struct tcf_psp *p = to_psp(a);
+
+	tcf_psp_release_params(rcu_dereference_protected(p->params, 1));
+}
+
+static int tcf_psp_dump(struct sk_buff *skb, struct tc_action *a,
+			int bind, int ref)
+{
+	unsigned char *b = skb_tail_pointer(skb);
+	struct tcf_psp *p = to_psp(a);
+	struct tcf_psp_params *params;
+	struct tc_psp opt = {
+		.index    = p->tcf_index,
+		.refcnt   = refcount_read(&p->tcf_refcnt) - ref,
+		.bindcnt  = atomic_read(&p->tcf_bindcnt) - bind,
+	};
+	struct tcf_t t;
+
+	rcu_read_lock();
+	params = rcu_dereference(p->params);
+	opt.action = params->action;
+
+	if (nla_put(skb, TCA_PSP_PARMS, sizeof(opt), &opt) ||
+	    nla_put_u32(skb, TCA_PSP_ACTION, params->psp_action))
+		goto nla_put_failure;
+
+	if (params->md &&
+	    nla_put_u32(skb, TCA_PSP_ASSOC_ID, params->md->u.psp_info.pas->id))
+		goto nla_put_failure;
+
+	tcf_tm_dump(&t, &p->tcf_tm);
+	if (nla_put_64bit(skb, TCA_PSP_TM, sizeof(t), &t, TCA_PSP_PAD))
+		goto nla_put_failure;
+	rcu_read_unlock();
+
+	return skb->len;
+
+nla_put_failure:
+	rcu_read_unlock();
+	nlmsg_trim(skb, b);
+	return -1;
+}
+
+static struct tc_action_ops act_psp_ops = {
+	.kind		=	"psp",
+	.id		=	TCA_ID_PSP,
+	.owner		=	THIS_MODULE,
+	.act		=	tcf_psp_act,
+	.dump		=	tcf_psp_dump,
+	.init		=	tcf_psp_init,
+	.cleanup	=	tcf_psp_cleanup,
+	.size		=	sizeof(struct tcf_psp),
+};
+MODULE_ALIAS_NET_ACT("psp");
+
+static __net_init int psp_init_net(struct net *net)
+{
+	struct tc_action_net *tn = net_generic(net, act_psp_ops.net_id);
+
+	return tc_action_net_init(net, tn, &act_psp_ops);
+}
+
+static void __net_exit psp_exit_net(struct list_head *net_list)
+{
+	tc_action_net_exit(net_list, act_psp_ops.net_id);
+}
+
+static struct pernet_operations psp_net_ops = {
+	.init = psp_init_net,
+	.exit_batch = psp_exit_net,
+	.id   = &act_psp_ops.net_id,
+	.size = sizeof(struct tc_action_net),
+};
+
+static int __init psp_init_module(void)
+{
+	return tcf_register_action(&act_psp_ops, &psp_net_ops);
+}
+
+static void __exit psp_cleanup_module(void)
+{
+	tcf_unregister_action(&act_psp_ops, &psp_net_ops);
+}
+
+module_init(psp_init_module);
+module_exit(psp_cleanup_module);
+
+MODULE_DESCRIPTION("PSP encryption tc action");
+MODULE_LICENSE("GPL");
-- 
2.53.0-Meta


  parent reply	other threads:[~2026-11-11  0:56 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-11  0:55 [PATCH net-next v1 00/15] psp: introduce socket-less associations Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 01/15] psp: get netns from net_device instead of sock Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 02/15] psp: take the psp nexthdr from the packet on encap Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 03/15] psp: declare psp_assoc_set_tx() in psp.h Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 04/15] psp: add support for socket-less psp associations Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 05/15] net: add METADATA_PSP dst to carry a psp association on an skb Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 06/15] net/sched: act_mirred: don't send decrypted skbs from egress to ingress Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 07/15] bpf: don't redirect " Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 08/15] psp: only give time-wait sockets of psp connections a validator Todd Freed
2026-11-11  0:55 ` Todd Freed [this message]
2026-11-11  0:55 ` [PATCH net-next v1 10/15] net/sched: flower: add psp_spi, psp_ver, and psp_dev matches Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 11/15] selftests: drv-net: psp: test socket-less associations and the tc psp action Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 12/15] selftests: drv-net: psp: test that software encap on ingress drops Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 13/15] selftests: tc-testing: run dependsOn probes outside the test namespace Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 14/15] selftests: tc-testing: add tests for the psp action Todd Freed
2026-11-11  0:55 ` [PATCH net-next v1 15/15] Documentation: networking: psp: document socket-less associations Todd Freed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261111005533.6436-10-toddfreed@meta.com \
    --to=toddfreed@meta.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=bjorn@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel.zahka@gmail.com \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=donald.hunter@gmail.com \
    --cc=edumazet@kernel.org \
    --cc=horms@kernel.org \
    --cc=jhs@mojatatu.com \
    --cc=jiri@resnulli.us \
    --cc=john.fastabend@gmail.com \
    --cc=kuba@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=saeedm@nvidia.com \
    --cc=tariqt@nvidia.com \
    --cc=weiwan@google.com \
    --cc=willemdebruijn.kernel@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®