1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 /* 3 * INET An implementation of the TCP/IP protocol suite for the LINUX 4 * operating system. INET is implemented using the BSD Socket 5 * interface as the means of communication with the user level. 6 * 7 * Authors: Lotsa people, from code originally in tcp 8 */ 9 10 #ifndef _INET6_HASHTABLES_H 11 #define _INET6_HASHTABLES_H 12 13 14 #if IS_ENABLED(CONFIG_IPV6) 15 #include <linux/in6.h> 16 #include <linux/ipv6.h> 17 #include <linux/types.h> 18 #include <linux/jhash.h> 19 20 #include <net/inet_sock.h> 21 22 #include <net/ipv6.h> 23 #include <net/netns/hash.h> 24 25 struct inet_hashinfo; 26 27 static inline unsigned int __inet6_ehashfn(const u32 lhash, 28 const u16 lport, 29 const u32 fhash, 30 const __be16 fport, 31 const u32 initval) 32 { 33 const u32 ports = (((u32)lport) << 16) | (__force u32)fport; 34 return jhash_3words(lhash, fhash, ports, initval); 35 } 36 37 /* 38 * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so 39 * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM 40 * 41 * The sockhash lock must be held as a reader here. 42 */ 43 struct sock *__inet6_lookup_established(const struct net *net, 44 const struct in6_addr *saddr, 45 const __be16 sport, 46 const struct in6_addr *daddr, 47 const u16 hnum, const int dif, 48 const int sdif); 49 50 typedef u32 (inet6_ehashfn_t)(const struct net *net, 51 const struct in6_addr *laddr, const u16 lport, 52 const struct in6_addr *faddr, const __be16 fport); 53 54 inet6_ehashfn_t inet6_ehashfn; 55 56 INDIRECT_CALLABLE_DECLARE(inet6_ehashfn_t udp6_ehashfn); 57 58 struct sock *inet6_lookup_reuseport(const struct net *net, struct sock *sk, 59 struct sk_buff *skb, int doff, 60 const struct in6_addr *saddr, 61 __be16 sport, 62 const struct in6_addr *daddr, 63 unsigned short hnum, 64 inet6_ehashfn_t *ehashfn); 65 66 struct sock *inet6_lookup_listener(const struct net *net, 67 struct sk_buff *skb, int doff, 68 const struct in6_addr *saddr, 69 const __be16 sport, 70 const struct in6_addr *daddr, 71 const unsigned short hnum, 72 const int dif, const int sdif); 73 74 struct sock *inet6_lookup_run_sk_lookup(const struct net *net, 75 int protocol, 76 struct sk_buff *skb, int doff, 77 const struct in6_addr *saddr, 78 const __be16 sport, 79 const struct in6_addr *daddr, 80 const u16 hnum, const int dif, 81 inet6_ehashfn_t *ehashfn); 82 83 static inline struct sock *__inet6_lookup(const struct net *net, 84 struct sk_buff *skb, int doff, 85 const struct in6_addr *saddr, 86 const __be16 sport, 87 const struct in6_addr *daddr, 88 const u16 hnum, 89 const int dif, const int sdif, 90 bool *refcounted) 91 { 92 struct sock *sk = __inet6_lookup_established(net, saddr, sport, 93 daddr, hnum, 94 dif, sdif); 95 *refcounted = true; 96 if (sk) 97 return sk; 98 *refcounted = false; 99 return inet6_lookup_listener(net, skb, doff, saddr, sport, 100 daddr, hnum, dif, sdif); 101 } 102 103 static inline 104 struct sock *inet6_steal_sock(struct net *net, struct sk_buff *skb, int doff, 105 const struct in6_addr *saddr, const __be16 sport, 106 const struct in6_addr *daddr, const __be16 dport, 107 bool *refcounted, inet6_ehashfn_t *ehashfn) 108 { 109 struct sock *sk, *reuse_sk; 110 bool prefetched; 111 112 sk = skb_steal_sock(skb, refcounted, &prefetched); 113 if (!sk) 114 return NULL; 115 116 if (!prefetched || !sk_fullsock(sk)) 117 return sk; 118 119 if (sk->sk_protocol == IPPROTO_TCP) { 120 if (sk->sk_state != TCP_LISTEN) 121 return sk; 122 } else if (sk->sk_protocol == IPPROTO_UDP) { 123 if (sk->sk_state != TCP_CLOSE) 124 return sk; 125 } else { 126 return sk; 127 } 128 129 reuse_sk = inet6_lookup_reuseport(net, sk, skb, doff, 130 saddr, sport, daddr, ntohs(dport), 131 ehashfn); 132 if (!reuse_sk) 133 return sk; 134 135 /* We've chosen a new reuseport sock which is never refcounted. This 136 * implies that sk also isn't refcounted. 137 */ 138 WARN_ON_ONCE(*refcounted); 139 140 return reuse_sk; 141 } 142 143 static inline struct sock *__inet6_lookup_skb(struct sk_buff *skb, int doff, 144 const __be16 sport, 145 const __be16 dport, 146 int iif, int sdif, 147 bool *refcounted) 148 { 149 struct net *net = skb_dst_dev_net_rcu(skb); 150 const struct ipv6hdr *ip6h = ipv6_hdr(skb); 151 struct sock *sk; 152 153 sk = inet6_steal_sock(net, skb, doff, &ip6h->saddr, sport, &ip6h->daddr, dport, 154 refcounted, inet6_ehashfn); 155 if (IS_ERR(sk)) 156 return NULL; 157 if (sk) 158 return sk; 159 160 return __inet6_lookup(net, skb, doff, &ip6h->saddr, sport, 161 &ip6h->daddr, ntohs(dport), 162 iif, sdif, refcounted); 163 } 164 165 struct sock *inet6_lookup(const struct net *net, struct sk_buff *skb, int doff, 166 const struct in6_addr *saddr, const __be16 sport, 167 const struct in6_addr *daddr, const __be16 dport, 168 const int dif); 169 170 static inline bool inet6_match(const struct net *net, const struct sock *sk, 171 const struct in6_addr *saddr, 172 const struct in6_addr *daddr, 173 const __portpair ports, 174 const int dif, const int sdif) 175 { 176 if (!net_eq(sock_net(sk), net) || 177 sk->sk_family != AF_INET6 || 178 sk->sk_portpair != ports || 179 !ipv6_addr_equal(&sk->sk_v6_daddr, saddr) || 180 !ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) 181 return false; 182 183 /* READ_ONCE() paired with WRITE_ONCE() in sock_bindtoindex_locked() */ 184 return inet_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if), dif, 185 sdif); 186 } 187 #endif /* IS_ENABLED(CONFIG_IPV6) */ 188 189 #endif /* _INET6_HASHTABLES_H */ 190