|  | /* SPDX-License-Identifier: GPL-2.0-only */ | 
|  | /*  OpenVPN data channel offload | 
|  | * | 
|  | *  Copyright (C) 2012-2025 OpenVPN, Inc. | 
|  | * | 
|  | *  Author:	James Yonan <james@openvpn.net> | 
|  | *		Antonio Quartulli <antonio@openvpn.net> | 
|  | */ | 
|  |  | 
|  | #ifndef _NET_OVPN_OVPNBIND_H_ | 
|  | #define _NET_OVPN_OVPNBIND_H_ | 
|  |  | 
|  | #include <net/ip.h> | 
|  | #include <linux/in.h> | 
|  | #include <linux/in6.h> | 
|  | #include <linux/rcupdate.h> | 
|  | #include <linux/skbuff.h> | 
|  | #include <linux/spinlock.h> | 
|  |  | 
|  | struct ovpn_peer; | 
|  |  | 
|  | /** | 
|  | * union ovpn_sockaddr - basic transport layer address | 
|  | * @in4: IPv4 address | 
|  | * @in6: IPv6 address | 
|  | */ | 
|  | union ovpn_sockaddr { | 
|  | struct sockaddr_in in4; | 
|  | struct sockaddr_in6 in6; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * struct ovpn_bind - remote peer binding | 
|  | * @remote: the remote peer sockaddress | 
|  | * @local: local endpoint used to talk to the peer | 
|  | * @local.ipv4: local IPv4 used to talk to the peer | 
|  | * @local.ipv6: local IPv6 used to talk to the peer | 
|  | * @rcu: used to schedule RCU cleanup job | 
|  | */ | 
|  | struct ovpn_bind { | 
|  | union ovpn_sockaddr remote;  /* remote sockaddr */ | 
|  |  | 
|  | union { | 
|  | struct in_addr ipv4; | 
|  | struct in6_addr ipv6; | 
|  | } local; | 
|  |  | 
|  | struct rcu_head rcu; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * ovpn_bind_skb_src_match - match packet source with binding | 
|  | * @bind: the binding to match | 
|  | * @skb: the packet to match | 
|  | * | 
|  | * Return: true if the packet source matches the remote peer sockaddr | 
|  | * in the binding | 
|  | */ | 
|  | static inline bool ovpn_bind_skb_src_match(const struct ovpn_bind *bind, | 
|  | const struct sk_buff *skb) | 
|  | { | 
|  | const union ovpn_sockaddr *remote; | 
|  |  | 
|  | if (unlikely(!bind)) | 
|  | return false; | 
|  |  | 
|  | remote = &bind->remote; | 
|  |  | 
|  | switch (skb->protocol) { | 
|  | case htons(ETH_P_IP): | 
|  | if (unlikely(remote->in4.sin_family != AF_INET)) | 
|  | return false; | 
|  |  | 
|  | if (unlikely(remote->in4.sin_addr.s_addr != ip_hdr(skb)->saddr)) | 
|  | return false; | 
|  |  | 
|  | if (unlikely(remote->in4.sin_port != udp_hdr(skb)->source)) | 
|  | return false; | 
|  | break; | 
|  | case htons(ETH_P_IPV6): | 
|  | if (unlikely(remote->in6.sin6_family != AF_INET6)) | 
|  | return false; | 
|  |  | 
|  | if (unlikely(!ipv6_addr_equal(&remote->in6.sin6_addr, | 
|  | &ipv6_hdr(skb)->saddr))) | 
|  | return false; | 
|  |  | 
|  | if (unlikely(remote->in6.sin6_port != udp_hdr(skb)->source)) | 
|  | return false; | 
|  | break; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *sa); | 
|  | void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *bind); | 
|  |  | 
|  | #endif /* _NET_OVPN_OVPNBIND_H_ */ |