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(struct net *net, 44 struct inet_hashinfo *hashinfo, 45 const struct in6_addr *saddr, 46 const __be16 sport, 47 const struct in6_addr *daddr, 48 const u16 hnum, const int dif, 49 const int sdif); 50 51 typedef u32 (inet6_ehashfn_t)(const struct net *net, 52 const struct in6_addr *laddr, const u16 lport, 53 const struct in6_addr *faddr, const __be16 fport); 54 55 inet6_ehashfn_t inet6_ehashfn; 56 57 INDIRECT_CALLABLE_DECLARE(inet6_ehashfn_t udp6_ehashfn); 58 59 struct sock *inet6_lookup_reuseport(struct net *net, struct sock *sk, 60 struct sk_buff *skb, int doff, 61 const struct in6_addr *saddr, 62 __be16 sport, 63 const struct in6_addr *daddr, 64 unsigned short hnum, 65 inet6_ehashfn_t *ehashfn); 66 67 struct sock *inet6_lookup_listener(struct net *net, 68 struct inet_hashinfo *hashinfo, 69 struct sk_buff *skb, int doff, 70 const struct in6_addr *saddr, 71 const __be16 sport, 72 const struct in6_addr *daddr, 73 const unsigned short hnum, 74 const int dif, const int sdif); 75 76 struct sock *inet6_lookup_run_sk_lookup(struct net *net, 77 int protocol, 78 struct sk_buff *skb, int doff, 79 const struct in6_addr *saddr, 80 const __be16 sport, 81 const struct in6_addr *daddr, 82 const u16 hnum, const int dif, 83 inet6_ehashfn_t *ehashfn); 84 85 static inline struct sock *__inet6_lookup(struct net *net, 86 struct inet_hashinfo *hashinfo, 87 struct sk_buff *skb, int doff, 88 const struct in6_addr *saddr, 89 const __be16 sport, 90 const struct in6_addr *daddr, 91 const u16 hnum, 92 const int dif, const int sdif, 93 bool *refcounted) 94 { 95 struct sock *sk = __inet6_lookup_established(net, hashinfo, saddr, 96 sport, daddr, hnum, 97 dif, sdif); 98 *refcounted = true; 99 if (sk) 100 return sk; 101 *refcounted = false; 102 return inet6_lookup_listener(net, hashinfo, skb, doff, saddr, sport, 103 daddr, hnum, dif, sdif); 104 } 105 106 static inline 107 struct sock *inet6_steal_sock(struct net *net, struct sk_buff *skb, int doff, 108 const struct in6_addr *saddr, const __be16 sport, 109 const struct in6_addr *daddr, const __be16 dport, 110 bool *refcounted, inet6_ehashfn_t *ehashfn) 111 { 112 struct sock *sk, *reuse_sk; 113 bool prefetched; 114 115 sk = skb_steal_sock(skb, refcounted, &prefetched); 116 if (!sk) 117 return NULL; 118 119 if (!prefetched || !sk_fullsock(sk)) 120 return sk; 121 122 if (sk->sk_protocol == IPPROTO_TCP) { 123 if (sk->sk_state != TCP_LISTEN) 124 return sk; 125 } else if (sk->sk_protocol == IPPROTO_UDP) { 126 if (sk->sk_state != TCP_CLOSE) 127 return sk; 128 } else { 129 return sk; 130 } 131 132 reuse_sk = inet6_lookup_reuseport(net, sk, skb, doff, 133 saddr, sport, daddr, ntohs(dport), 134 ehashfn); 135 if (!reuse_sk) 136 return sk; 137 138 /* We've chosen a new reuseport sock which is never refcounted. This 139 * implies that sk also isn't refcounted. 140 */ 141 WARN_ON_ONCE(*refcounted); 142 143 return reuse_sk; 144 } 145 146 static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo, 147 struct sk_buff *skb, int doff, 148 const __be16 sport, 149 const __be16 dport, 150 int iif, int sdif, 151 bool *refcounted) 152 { 153 struct net *net = dev_net(skb_dst(skb)->dev); 154 const struct ipv6hdr *ip6h = ipv6_hdr(skb); 155 struct sock *sk; 156 157 sk = inet6_steal_sock(net, skb, doff, &ip6h->saddr, sport, &ip6h->daddr, dport, 158 refcounted, inet6_ehashfn); 159 if (IS_ERR(sk)) 160 return NULL; 161 if (sk) 162 return sk; 163 164 return __inet6_lookup(net, hashinfo, skb, 165 doff, &ip6h->saddr, sport, 166 &ip6h->daddr, ntohs(dport), 167 iif, sdif, refcounted); 168 } 169 170 struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo, 171 struct sk_buff *skb, int doff, 172 const struct in6_addr *saddr, const __be16 sport, 173 const struct in6_addr *daddr, const __be16 dport, 174 const int dif); 175 176 int inet6_hash(struct sock *sk); 177 178 static inline bool inet6_match(struct net *net, const struct sock *sk, 179 const struct in6_addr *saddr, 180 const struct in6_addr *daddr, 181 const __portpair ports, 182 const int dif, const int sdif) 183 { 184 if (!net_eq(sock_net(sk), net) || 185 sk->sk_family != AF_INET6 || 186 sk->sk_portpair != ports || 187 !ipv6_addr_equal(&sk->sk_v6_daddr, saddr) || 188 !ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) 189 return false; 190 191 /* READ_ONCE() paired with WRITE_ONCE() in sock_bindtoindex_locked() */ 192 return inet_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if), dif, 193 sdif); 194 } 195 #endif /* IS_ENABLED(CONFIG_IPV6) */ 196 197 #endif /* _INET6_HASHTABLES_H */ 198