xref: /linux/include/net/psp/functions.h (revision e97269257fe437910cddc7c642a636ca3cf9fb1d)
100c94ca2SJakub Kicinski /* SPDX-License-Identifier: GPL-2.0-only */
200c94ca2SJakub Kicinski 
300c94ca2SJakub Kicinski #ifndef __NET_PSP_HELPERS_H
400c94ca2SJakub Kicinski #define __NET_PSP_HELPERS_H
500c94ca2SJakub Kicinski 
6659a2899SJakub Kicinski #include <linux/skbuff.h>
76b46ca26SJakub Kicinski #include <linux/rcupdate.h>
8*e9726925SJakub Kicinski #include <linux/udp.h>
9659a2899SJakub Kicinski #include <net/sock.h>
106b46ca26SJakub Kicinski #include <net/tcp.h>
1100c94ca2SJakub Kicinski #include <net/psp/types.h>
1200c94ca2SJakub Kicinski 
13ed8a507bSJakub Kicinski struct inet_timewait_sock;
14ed8a507bSJakub Kicinski 
1500c94ca2SJakub Kicinski /* Driver-facing API */
1600c94ca2SJakub Kicinski struct psp_dev *
1700c94ca2SJakub Kicinski psp_dev_create(struct net_device *netdev, struct psp_dev_ops *psd_ops,
1800c94ca2SJakub Kicinski 	       struct psp_dev_caps *psd_caps, void *priv_ptr);
1900c94ca2SJakub Kicinski void psp_dev_unregister(struct psp_dev *psd);
2000c94ca2SJakub Kicinski 
21ed8a507bSJakub Kicinski /* Kernel-facing API */
226b46ca26SJakub Kicinski void psp_assoc_put(struct psp_assoc *pas);
236b46ca26SJakub Kicinski 
246b46ca26SJakub Kicinski static inline void *psp_assoc_drv_data(struct psp_assoc *pas)
256b46ca26SJakub Kicinski {
266b46ca26SJakub Kicinski 	return pas->drv_data;
276b46ca26SJakub Kicinski }
286b46ca26SJakub Kicinski 
29659a2899SJakub Kicinski #if IS_ENABLED(CONFIG_INET_PSP)
306b46ca26SJakub Kicinski unsigned int psp_key_size(u32 version);
316b46ca26SJakub Kicinski void psp_sk_assoc_free(struct sock *sk);
326b46ca26SJakub Kicinski void psp_twsk_init(struct inet_timewait_sock *tw, const struct sock *sk);
336b46ca26SJakub Kicinski void psp_twsk_assoc_free(struct inet_timewait_sock *tw);
346b46ca26SJakub Kicinski void psp_reply_set_decrypted(struct sk_buff *skb);
356b46ca26SJakub Kicinski 
366b46ca26SJakub Kicinski static inline struct psp_assoc *psp_sk_assoc(const struct sock *sk)
376b46ca26SJakub Kicinski {
386b46ca26SJakub Kicinski 	return rcu_dereference_check(sk->psp_assoc, lockdep_sock_is_held(sk));
396b46ca26SJakub Kicinski }
40659a2899SJakub Kicinski 
41659a2899SJakub Kicinski static inline void
42659a2899SJakub Kicinski psp_enqueue_set_decrypted(struct sock *sk, struct sk_buff *skb)
43659a2899SJakub Kicinski {
446b46ca26SJakub Kicinski 	struct psp_assoc *pas;
456b46ca26SJakub Kicinski 
466b46ca26SJakub Kicinski 	pas = psp_sk_assoc(sk);
476b46ca26SJakub Kicinski 	if (pas && pas->tx.spi)
486b46ca26SJakub Kicinski 		skb->decrypted = 1;
49659a2899SJakub Kicinski }
50659a2899SJakub Kicinski 
51659a2899SJakub Kicinski static inline unsigned long
52659a2899SJakub Kicinski __psp_skb_coalesce_diff(const struct sk_buff *one, const struct sk_buff *two,
53659a2899SJakub Kicinski 			unsigned long diffs)
54659a2899SJakub Kicinski {
556b46ca26SJakub Kicinski 	struct psp_skb_ext *a, *b;
566b46ca26SJakub Kicinski 
576b46ca26SJakub Kicinski 	a = skb_ext_find(one, SKB_EXT_PSP);
586b46ca26SJakub Kicinski 	b = skb_ext_find(two, SKB_EXT_PSP);
596b46ca26SJakub Kicinski 
606b46ca26SJakub Kicinski 	diffs |= (!!a) ^ (!!b);
616b46ca26SJakub Kicinski 	if (!diffs && unlikely(a))
626b46ca26SJakub Kicinski 		diffs |= memcmp(a, b, sizeof(*a));
63659a2899SJakub Kicinski 	return diffs;
64659a2899SJakub Kicinski }
65659a2899SJakub Kicinski 
666b46ca26SJakub Kicinski static inline bool
676b46ca26SJakub Kicinski psp_is_allowed_nondata(struct sk_buff *skb, struct psp_assoc *pas)
686b46ca26SJakub Kicinski {
696b46ca26SJakub Kicinski 	bool fin = !!(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN);
706b46ca26SJakub Kicinski 	u32 end_seq = TCP_SKB_CB(skb)->end_seq;
716b46ca26SJakub Kicinski 	u32 seq = TCP_SKB_CB(skb)->seq;
726b46ca26SJakub Kicinski 	bool pure_fin;
736b46ca26SJakub Kicinski 
746b46ca26SJakub Kicinski 	pure_fin = fin && end_seq - seq == 1;
756b46ca26SJakub Kicinski 
766b46ca26SJakub Kicinski 	return seq == end_seq || (pure_fin && seq == pas->upgrade_seq);
776b46ca26SJakub Kicinski }
786b46ca26SJakub Kicinski 
796b46ca26SJakub Kicinski static inline bool
806b46ca26SJakub Kicinski psp_pse_matches_pas(struct psp_skb_ext *pse, struct psp_assoc *pas)
816b46ca26SJakub Kicinski {
826b46ca26SJakub Kicinski 	return pse && pas->rx.spi == pse->spi &&
836b46ca26SJakub Kicinski 	       pas->generation == pse->generation &&
846b46ca26SJakub Kicinski 	       pas->version == pse->version &&
856b46ca26SJakub Kicinski 	       pas->dev_id == pse->dev_id;
866b46ca26SJakub Kicinski }
876b46ca26SJakub Kicinski 
886b46ca26SJakub Kicinski static inline enum skb_drop_reason
896b46ca26SJakub Kicinski __psp_sk_rx_policy_check(struct sk_buff *skb, struct psp_assoc *pas)
906b46ca26SJakub Kicinski {
916b46ca26SJakub Kicinski 	struct psp_skb_ext *pse = skb_ext_find(skb, SKB_EXT_PSP);
926b46ca26SJakub Kicinski 
936b46ca26SJakub Kicinski 	if (!pas)
946b46ca26SJakub Kicinski 		return pse ? SKB_DROP_REASON_PSP_INPUT : 0;
956b46ca26SJakub Kicinski 
966b46ca26SJakub Kicinski 	if (likely(psp_pse_matches_pas(pse, pas))) {
976b46ca26SJakub Kicinski 		if (unlikely(!pas->peer_tx))
986b46ca26SJakub Kicinski 			pas->peer_tx = 1;
996b46ca26SJakub Kicinski 
1006b46ca26SJakub Kicinski 		return 0;
1016b46ca26SJakub Kicinski 	}
1026b46ca26SJakub Kicinski 
1036b46ca26SJakub Kicinski 	if (!pse) {
1046b46ca26SJakub Kicinski 		if (!pas->tx.spi ||
1056b46ca26SJakub Kicinski 		    (!pas->peer_tx && psp_is_allowed_nondata(skb, pas)))
1066b46ca26SJakub Kicinski 			return 0;
1076b46ca26SJakub Kicinski 	}
1086b46ca26SJakub Kicinski 
1096b46ca26SJakub Kicinski 	return SKB_DROP_REASON_PSP_INPUT;
1106b46ca26SJakub Kicinski }
1116b46ca26SJakub Kicinski 
112659a2899SJakub Kicinski static inline enum skb_drop_reason
113659a2899SJakub Kicinski psp_sk_rx_policy_check(struct sock *sk, struct sk_buff *skb)
114659a2899SJakub Kicinski {
1156b46ca26SJakub Kicinski 	return __psp_sk_rx_policy_check(skb, psp_sk_assoc(sk));
116659a2899SJakub Kicinski }
117659a2899SJakub Kicinski 
118659a2899SJakub Kicinski static inline enum skb_drop_reason
119659a2899SJakub Kicinski psp_twsk_rx_policy_check(struct inet_timewait_sock *tw, struct sk_buff *skb)
120659a2899SJakub Kicinski {
1216b46ca26SJakub Kicinski 	return __psp_sk_rx_policy_check(skb, rcu_dereference(tw->psp_assoc));
1226b46ca26SJakub Kicinski }
1236b46ca26SJakub Kicinski 
1246b46ca26SJakub Kicinski static inline struct psp_assoc *psp_sk_get_assoc_rcu(struct sock *sk)
1256b46ca26SJakub Kicinski {
1266b46ca26SJakub Kicinski 	struct inet_timewait_sock *tw;
1276b46ca26SJakub Kicinski 	struct psp_assoc *pas;
1286b46ca26SJakub Kicinski 	int state;
1296b46ca26SJakub Kicinski 
1306b46ca26SJakub Kicinski 	state = 1 << READ_ONCE(sk->sk_state);
1316b46ca26SJakub Kicinski 	if (!sk_is_inet(sk) || state & TCPF_NEW_SYN_RECV)
1326b46ca26SJakub Kicinski 		return NULL;
1336b46ca26SJakub Kicinski 
1346b46ca26SJakub Kicinski 	tw = inet_twsk(sk);
1356b46ca26SJakub Kicinski 	pas = state & TCPF_TIME_WAIT ? rcu_dereference(tw->psp_assoc) :
1366b46ca26SJakub Kicinski 				       rcu_dereference(sk->psp_assoc);
1376b46ca26SJakub Kicinski 	return pas;
138659a2899SJakub Kicinski }
139659a2899SJakub Kicinski 
140659a2899SJakub Kicinski static inline struct psp_assoc *psp_skb_get_assoc_rcu(struct sk_buff *skb)
141659a2899SJakub Kicinski {
1426b46ca26SJakub Kicinski 	if (!skb->decrypted || !skb->sk)
143659a2899SJakub Kicinski 		return NULL;
1446b46ca26SJakub Kicinski 
1456b46ca26SJakub Kicinski 	return psp_sk_get_assoc_rcu(skb->sk);
146659a2899SJakub Kicinski }
147*e9726925SJakub Kicinski 
148*e9726925SJakub Kicinski static inline unsigned int psp_sk_overhead(const struct sock *sk)
149*e9726925SJakub Kicinski {
150*e9726925SJakub Kicinski 	int psp_encap = sizeof(struct udphdr) + PSP_HDR_SIZE + PSP_TRL_SIZE;
151*e9726925SJakub Kicinski 	bool has_psp = rcu_access_pointer(sk->psp_assoc);
152*e9726925SJakub Kicinski 
153*e9726925SJakub Kicinski 	return has_psp ? psp_encap : 0;
154*e9726925SJakub Kicinski }
155659a2899SJakub Kicinski #else
156659a2899SJakub Kicinski static inline void psp_sk_assoc_free(struct sock *sk) { }
157659a2899SJakub Kicinski static inline void
158659a2899SJakub Kicinski psp_twsk_init(struct inet_timewait_sock *tw, const struct sock *sk) { }
159659a2899SJakub Kicinski static inline void psp_twsk_assoc_free(struct inet_timewait_sock *tw) { }
160659a2899SJakub Kicinski static inline void
161659a2899SJakub Kicinski psp_reply_set_decrypted(struct sk_buff *skb) { }
162659a2899SJakub Kicinski 
1636b46ca26SJakub Kicinski static inline struct psp_assoc *psp_sk_assoc(const struct sock *sk)
1646b46ca26SJakub Kicinski {
1656b46ca26SJakub Kicinski 	return NULL;
1666b46ca26SJakub Kicinski }
1676b46ca26SJakub Kicinski 
168659a2899SJakub Kicinski static inline void
169659a2899SJakub Kicinski psp_enqueue_set_decrypted(struct sock *sk, struct sk_buff *skb) { }
170659a2899SJakub Kicinski 
171659a2899SJakub Kicinski static inline unsigned long
172659a2899SJakub Kicinski __psp_skb_coalesce_diff(const struct sk_buff *one, const struct sk_buff *two,
173659a2899SJakub Kicinski 			unsigned long diffs)
174659a2899SJakub Kicinski {
175659a2899SJakub Kicinski 	return diffs;
176659a2899SJakub Kicinski }
177659a2899SJakub Kicinski 
178659a2899SJakub Kicinski static inline enum skb_drop_reason
179659a2899SJakub Kicinski psp_sk_rx_policy_check(struct sock *sk, struct sk_buff *skb)
180659a2899SJakub Kicinski {
181659a2899SJakub Kicinski 	return 0;
182659a2899SJakub Kicinski }
183659a2899SJakub Kicinski 
184659a2899SJakub Kicinski static inline enum skb_drop_reason
185659a2899SJakub Kicinski psp_twsk_rx_policy_check(struct inet_timewait_sock *tw, struct sk_buff *skb)
186659a2899SJakub Kicinski {
187659a2899SJakub Kicinski 	return 0;
188659a2899SJakub Kicinski }
189659a2899SJakub Kicinski 
190659a2899SJakub Kicinski static inline struct psp_assoc *psp_skb_get_assoc_rcu(struct sk_buff *skb)
191659a2899SJakub Kicinski {
192659a2899SJakub Kicinski 	return NULL;
193659a2899SJakub Kicinski }
194*e9726925SJakub Kicinski 
195*e9726925SJakub Kicinski static inline unsigned int psp_sk_overhead(const struct sock *sk)
196*e9726925SJakub Kicinski {
197*e9726925SJakub Kicinski 	return 0;
198*e9726925SJakub Kicinski }
199659a2899SJakub Kicinski #endif
200659a2899SJakub Kicinski 
201659a2899SJakub Kicinski static inline unsigned long
202659a2899SJakub Kicinski psp_skb_coalesce_diff(const struct sk_buff *one, const struct sk_buff *two)
203659a2899SJakub Kicinski {
204659a2899SJakub Kicinski 	return __psp_skb_coalesce_diff(one, two, 0);
205659a2899SJakub Kicinski }
206ed8a507bSJakub Kicinski 
20700c94ca2SJakub Kicinski #endif /* __NET_PSP_HELPERS_H */
208