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