xref: /linux/include/net/psp/functions.h (revision f8d2f8205be8cceef2dd3c0e68e7af3c5f83c75c)
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>
8e9726925SJakub 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);
20fc724515SRaed Salem bool psp_dev_encapsulate(struct net *net, struct sk_buff *skb, __be32 spi,
21fc724515SRaed Salem 			 u8 ver, __be16 sport);
220eddb802SRaed Salem int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv);
2300c94ca2SJakub Kicinski 
24ed8a507bSJakub Kicinski /* Kernel-facing API */
256b46ca26SJakub Kicinski void psp_assoc_put(struct psp_assoc *pas);
266b46ca26SJakub Kicinski 
276b46ca26SJakub Kicinski static inline void *psp_assoc_drv_data(struct psp_assoc *pas)
286b46ca26SJakub Kicinski {
296b46ca26SJakub Kicinski 	return pas->drv_data;
306b46ca26SJakub Kicinski }
316b46ca26SJakub Kicinski 
32659a2899SJakub Kicinski #if IS_ENABLED(CONFIG_INET_PSP)
336b46ca26SJakub Kicinski unsigned int psp_key_size(u32 version);
346b46ca26SJakub Kicinski void psp_sk_assoc_free(struct sock *sk);
356b46ca26SJakub Kicinski void psp_twsk_init(struct inet_timewait_sock *tw, const struct sock *sk);
366b46ca26SJakub Kicinski void psp_twsk_assoc_free(struct inet_timewait_sock *tw);
376b46ca26SJakub Kicinski void psp_reply_set_decrypted(struct sk_buff *skb);
386b46ca26SJakub Kicinski 
396b46ca26SJakub Kicinski static inline struct psp_assoc *psp_sk_assoc(const struct sock *sk)
406b46ca26SJakub Kicinski {
416b46ca26SJakub Kicinski 	return rcu_dereference_check(sk->psp_assoc, lockdep_sock_is_held(sk));
426b46ca26SJakub Kicinski }
43659a2899SJakub Kicinski 
44659a2899SJakub Kicinski static inline void
45659a2899SJakub Kicinski psp_enqueue_set_decrypted(struct sock *sk, struct sk_buff *skb)
46659a2899SJakub Kicinski {
476b46ca26SJakub Kicinski 	struct psp_assoc *pas;
486b46ca26SJakub Kicinski 
496b46ca26SJakub Kicinski 	pas = psp_sk_assoc(sk);
506b46ca26SJakub Kicinski 	if (pas && pas->tx.spi)
516b46ca26SJakub Kicinski 		skb->decrypted = 1;
52659a2899SJakub Kicinski }
53659a2899SJakub Kicinski 
54659a2899SJakub Kicinski static inline unsigned long
55659a2899SJakub Kicinski __psp_skb_coalesce_diff(const struct sk_buff *one, const struct sk_buff *two,
56659a2899SJakub Kicinski 			unsigned long diffs)
57659a2899SJakub Kicinski {
586b46ca26SJakub Kicinski 	struct psp_skb_ext *a, *b;
596b46ca26SJakub Kicinski 
606b46ca26SJakub Kicinski 	a = skb_ext_find(one, SKB_EXT_PSP);
616b46ca26SJakub Kicinski 	b = skb_ext_find(two, SKB_EXT_PSP);
626b46ca26SJakub Kicinski 
636b46ca26SJakub Kicinski 	diffs |= (!!a) ^ (!!b);
646b46ca26SJakub Kicinski 	if (!diffs && unlikely(a))
656b46ca26SJakub Kicinski 		diffs |= memcmp(a, b, sizeof(*a));
66659a2899SJakub Kicinski 	return diffs;
67659a2899SJakub Kicinski }
68659a2899SJakub Kicinski 
696b46ca26SJakub Kicinski static inline bool
706b46ca26SJakub Kicinski psp_is_allowed_nondata(struct sk_buff *skb, struct psp_assoc *pas)
716b46ca26SJakub Kicinski {
726b46ca26SJakub Kicinski 	bool fin = !!(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN);
736b46ca26SJakub Kicinski 	u32 end_seq = TCP_SKB_CB(skb)->end_seq;
746b46ca26SJakub Kicinski 	u32 seq = TCP_SKB_CB(skb)->seq;
756b46ca26SJakub Kicinski 	bool pure_fin;
766b46ca26SJakub Kicinski 
776b46ca26SJakub Kicinski 	pure_fin = fin && end_seq - seq == 1;
786b46ca26SJakub Kicinski 
796b46ca26SJakub Kicinski 	return seq == end_seq || (pure_fin && seq == pas->upgrade_seq);
806b46ca26SJakub Kicinski }
816b46ca26SJakub Kicinski 
826b46ca26SJakub Kicinski static inline bool
836b46ca26SJakub Kicinski psp_pse_matches_pas(struct psp_skb_ext *pse, struct psp_assoc *pas)
846b46ca26SJakub Kicinski {
856b46ca26SJakub Kicinski 	return pse && pas->rx.spi == pse->spi &&
866b46ca26SJakub Kicinski 	       pas->generation == pse->generation &&
876b46ca26SJakub Kicinski 	       pas->version == pse->version &&
886b46ca26SJakub Kicinski 	       pas->dev_id == pse->dev_id;
896b46ca26SJakub Kicinski }
906b46ca26SJakub Kicinski 
916b46ca26SJakub Kicinski static inline enum skb_drop_reason
926b46ca26SJakub Kicinski __psp_sk_rx_policy_check(struct sk_buff *skb, struct psp_assoc *pas)
936b46ca26SJakub Kicinski {
946b46ca26SJakub Kicinski 	struct psp_skb_ext *pse = skb_ext_find(skb, SKB_EXT_PSP);
956b46ca26SJakub Kicinski 
966b46ca26SJakub Kicinski 	if (!pas)
976b46ca26SJakub Kicinski 		return pse ? SKB_DROP_REASON_PSP_INPUT : 0;
986b46ca26SJakub Kicinski 
996b46ca26SJakub Kicinski 	if (likely(psp_pse_matches_pas(pse, pas))) {
1006b46ca26SJakub Kicinski 		if (unlikely(!pas->peer_tx))
1016b46ca26SJakub Kicinski 			pas->peer_tx = 1;
1026b46ca26SJakub Kicinski 
1036b46ca26SJakub Kicinski 		return 0;
1046b46ca26SJakub Kicinski 	}
1056b46ca26SJakub Kicinski 
1066b46ca26SJakub Kicinski 	if (!pse) {
1076b46ca26SJakub Kicinski 		if (!pas->tx.spi ||
1086b46ca26SJakub Kicinski 		    (!pas->peer_tx && psp_is_allowed_nondata(skb, pas)))
1096b46ca26SJakub Kicinski 			return 0;
1106b46ca26SJakub Kicinski 	}
1116b46ca26SJakub Kicinski 
1126b46ca26SJakub Kicinski 	return SKB_DROP_REASON_PSP_INPUT;
1136b46ca26SJakub Kicinski }
1146b46ca26SJakub Kicinski 
115659a2899SJakub Kicinski static inline enum skb_drop_reason
116659a2899SJakub Kicinski psp_sk_rx_policy_check(struct sock *sk, struct sk_buff *skb)
117659a2899SJakub Kicinski {
1186b46ca26SJakub Kicinski 	return __psp_sk_rx_policy_check(skb, psp_sk_assoc(sk));
119659a2899SJakub Kicinski }
120659a2899SJakub Kicinski 
121659a2899SJakub Kicinski static inline enum skb_drop_reason
122659a2899SJakub Kicinski psp_twsk_rx_policy_check(struct inet_timewait_sock *tw, struct sk_buff *skb)
123659a2899SJakub Kicinski {
1246b46ca26SJakub Kicinski 	return __psp_sk_rx_policy_check(skb, rcu_dereference(tw->psp_assoc));
1256b46ca26SJakub Kicinski }
1266b46ca26SJakub Kicinski 
127*f8d2f820SDaniel Zahka static inline struct psp_assoc *psp_sk_get_assoc_rcu(const struct sock *sk)
1286b46ca26SJakub Kicinski {
1296b46ca26SJakub Kicinski 	struct inet_timewait_sock *tw;
1306b46ca26SJakub Kicinski 	struct psp_assoc *pas;
1316b46ca26SJakub Kicinski 	int state;
1326b46ca26SJakub Kicinski 
1336b46ca26SJakub Kicinski 	state = 1 << READ_ONCE(sk->sk_state);
1346b46ca26SJakub Kicinski 	if (!sk_is_inet(sk) || state & TCPF_NEW_SYN_RECV)
1356b46ca26SJakub Kicinski 		return NULL;
1366b46ca26SJakub Kicinski 
1376b46ca26SJakub Kicinski 	tw = inet_twsk(sk);
1386b46ca26SJakub Kicinski 	pas = state & TCPF_TIME_WAIT ? rcu_dereference(tw->psp_assoc) :
1396b46ca26SJakub Kicinski 				       rcu_dereference(sk->psp_assoc);
1406b46ca26SJakub Kicinski 	return pas;
141659a2899SJakub Kicinski }
142659a2899SJakub Kicinski 
143659a2899SJakub Kicinski static inline struct psp_assoc *psp_skb_get_assoc_rcu(struct sk_buff *skb)
144659a2899SJakub Kicinski {
1456b46ca26SJakub Kicinski 	if (!skb->decrypted || !skb->sk)
146659a2899SJakub Kicinski 		return NULL;
1476b46ca26SJakub Kicinski 
1486b46ca26SJakub Kicinski 	return psp_sk_get_assoc_rcu(skb->sk);
149659a2899SJakub Kicinski }
150e9726925SJakub Kicinski 
151e9726925SJakub Kicinski static inline unsigned int psp_sk_overhead(const struct sock *sk)
152e9726925SJakub Kicinski {
153e9726925SJakub Kicinski 	int psp_encap = sizeof(struct udphdr) + PSP_HDR_SIZE + PSP_TRL_SIZE;
154e9726925SJakub Kicinski 	bool has_psp = rcu_access_pointer(sk->psp_assoc);
155e9726925SJakub Kicinski 
156e9726925SJakub Kicinski 	return has_psp ? psp_encap : 0;
157e9726925SJakub Kicinski }
158659a2899SJakub Kicinski #else
159659a2899SJakub Kicinski static inline void psp_sk_assoc_free(struct sock *sk) { }
160659a2899SJakub Kicinski static inline void
161659a2899SJakub Kicinski psp_twsk_init(struct inet_timewait_sock *tw, const struct sock *sk) { }
162659a2899SJakub Kicinski static inline void psp_twsk_assoc_free(struct inet_timewait_sock *tw) { }
163659a2899SJakub Kicinski static inline void
164659a2899SJakub Kicinski psp_reply_set_decrypted(struct sk_buff *skb) { }
165659a2899SJakub Kicinski 
1666b46ca26SJakub Kicinski static inline struct psp_assoc *psp_sk_assoc(const struct sock *sk)
1676b46ca26SJakub Kicinski {
1686b46ca26SJakub Kicinski 	return NULL;
1696b46ca26SJakub Kicinski }
1706b46ca26SJakub Kicinski 
171659a2899SJakub Kicinski static inline void
172659a2899SJakub Kicinski psp_enqueue_set_decrypted(struct sock *sk, struct sk_buff *skb) { }
173659a2899SJakub Kicinski 
174659a2899SJakub Kicinski static inline unsigned long
175659a2899SJakub Kicinski __psp_skb_coalesce_diff(const struct sk_buff *one, const struct sk_buff *two,
176659a2899SJakub Kicinski 			unsigned long diffs)
177659a2899SJakub Kicinski {
178659a2899SJakub Kicinski 	return diffs;
179659a2899SJakub Kicinski }
180659a2899SJakub Kicinski 
181659a2899SJakub Kicinski static inline enum skb_drop_reason
182659a2899SJakub Kicinski psp_sk_rx_policy_check(struct sock *sk, struct sk_buff *skb)
183659a2899SJakub Kicinski {
184659a2899SJakub Kicinski 	return 0;
185659a2899SJakub Kicinski }
186659a2899SJakub Kicinski 
187659a2899SJakub Kicinski static inline enum skb_drop_reason
188659a2899SJakub Kicinski psp_twsk_rx_policy_check(struct inet_timewait_sock *tw, struct sk_buff *skb)
189659a2899SJakub Kicinski {
190659a2899SJakub Kicinski 	return 0;
191659a2899SJakub Kicinski }
192659a2899SJakub Kicinski 
193659a2899SJakub Kicinski static inline struct psp_assoc *psp_skb_get_assoc_rcu(struct sk_buff *skb)
194659a2899SJakub Kicinski {
195659a2899SJakub Kicinski 	return NULL;
196659a2899SJakub Kicinski }
197e9726925SJakub Kicinski 
198e9726925SJakub Kicinski static inline unsigned int psp_sk_overhead(const struct sock *sk)
199e9726925SJakub Kicinski {
200e9726925SJakub Kicinski 	return 0;
201e9726925SJakub Kicinski }
202659a2899SJakub Kicinski #endif
203659a2899SJakub Kicinski 
204659a2899SJakub Kicinski static inline unsigned long
205659a2899SJakub Kicinski psp_skb_coalesce_diff(const struct sk_buff *one, const struct sk_buff *two)
206659a2899SJakub Kicinski {
207659a2899SJakub Kicinski 	return __psp_skb_coalesce_diff(one, two, 0);
208659a2899SJakub Kicinski }
209ed8a507bSJakub Kicinski 
21000c94ca2SJakub Kicinski #endif /* __NET_PSP_HELPERS_H */
211