12874c5fdSThomas Gleixner /* SPDX-License-Identifier: GPL-2.0-or-later */
2505cbfc5SArnaldo Carvalho de Melo /*
3505cbfc5SArnaldo Carvalho de Melo * INET An implementation of the TCP/IP protocol suite for the LINUX
4505cbfc5SArnaldo Carvalho de Melo * operating system. INET is implemented using the BSD Socket
5505cbfc5SArnaldo Carvalho de Melo * interface as the means of communication with the user level.
6505cbfc5SArnaldo Carvalho de Melo *
7505cbfc5SArnaldo Carvalho de Melo * Authors: Lotsa people, from code originally in tcp
8505cbfc5SArnaldo Carvalho de Melo */
9505cbfc5SArnaldo Carvalho de Melo
10505cbfc5SArnaldo Carvalho de Melo #ifndef _INET6_HASHTABLES_H
11505cbfc5SArnaldo Carvalho de Melo #define _INET6_HASHTABLES_H
12505cbfc5SArnaldo Carvalho de Melo
135324a040SArnaldo Carvalho de Melo
14dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
155324a040SArnaldo Carvalho de Melo #include <linux/in6.h>
165324a040SArnaldo Carvalho de Melo #include <linux/ipv6.h>
17505cbfc5SArnaldo Carvalho de Melo #include <linux/types.h>
18b3da2cf3SDavid S. Miller #include <linux/jhash.h>
19b3da2cf3SDavid S. Miller
20b3da2cf3SDavid S. Miller #include <net/inet_sock.h>
21505cbfc5SArnaldo Carvalho de Melo
225324a040SArnaldo Carvalho de Melo #include <net/ipv6.h>
230b441916SPavel Emelyanov #include <net/netns/hash.h>
245324a040SArnaldo Carvalho de Melo
25505cbfc5SArnaldo Carvalho de Melo struct inet_hashinfo;
26505cbfc5SArnaldo Carvalho de Melo
__inet6_ehashfn(const u32 lhash,const u16 lport,const u32 fhash,const __be16 fport,const u32 initval)27b50026b5SHannes Frederic Sowa static inline unsigned int __inet6_ehashfn(const u32 lhash,
28b50026b5SHannes Frederic Sowa const u16 lport,
29b50026b5SHannes Frederic Sowa const u32 fhash,
30b50026b5SHannes Frederic Sowa const __be16 fport,
31b50026b5SHannes Frederic Sowa const u32 initval)
325324a040SArnaldo Carvalho de Melo {
33b50026b5SHannes Frederic Sowa const u32 ports = (((u32)lport) << 16) | (__force u32)fport;
34b50026b5SHannes Frederic Sowa return jhash_3words(lhash, fhash, ports, initval);
355324a040SArnaldo Carvalho de Melo }
365324a040SArnaldo Carvalho de Melo
375324a040SArnaldo Carvalho de Melo /*
385324a040SArnaldo Carvalho de Melo * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
395324a040SArnaldo Carvalho de Melo * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
405324a040SArnaldo Carvalho de Melo *
415324a040SArnaldo Carvalho de Melo * The sockhash lock must be held as a reader here.
425324a040SArnaldo Carvalho de Melo */
431fd51155SJoe Perches struct sock *__inet6_lookup_established(struct net *net,
44d86e0dacSPavel Emelyanov struct inet_hashinfo *hashinfo,
455324a040SArnaldo Carvalho de Melo const struct in6_addr *saddr,
46d2ecd9ccSAl Viro const __be16 sport,
475324a040SArnaldo Carvalho de Melo const struct in6_addr *daddr,
484297a0efSDavid Ahern const u16 hnum, const int dif,
494297a0efSDavid Ahern const int sdif);
505324a040SArnaldo Carvalho de Melo
510f495f76SLorenz Bauer typedef u32 (inet6_ehashfn_t)(const struct net *net,
520f495f76SLorenz Bauer const struct in6_addr *laddr, const u16 lport,
530f495f76SLorenz Bauer const struct in6_addr *faddr, const __be16 fport);
540f495f76SLorenz Bauer
550f495f76SLorenz Bauer inet6_ehashfn_t inet6_ehashfn;
560f495f76SLorenz Bauer
570f495f76SLorenz Bauer INDIRECT_CALLABLE_DECLARE(inet6_ehashfn_t udp6_ehashfn);
580f495f76SLorenz Bauer
59ce796e60SLorenz Bauer struct sock *inet6_lookup_reuseport(struct net *net, struct sock *sk,
60ce796e60SLorenz Bauer struct sk_buff *skb, int doff,
61ce796e60SLorenz Bauer const struct in6_addr *saddr,
62ce796e60SLorenz Bauer __be16 sport,
63ce796e60SLorenz Bauer const struct in6_addr *daddr,
640f495f76SLorenz Bauer unsigned short hnum,
650f495f76SLorenz Bauer inet6_ehashfn_t *ehashfn);
66ce796e60SLorenz Bauer
671fd51155SJoe Perches struct sock *inet6_lookup_listener(struct net *net,
68d86e0dacSPavel Emelyanov struct inet_hashinfo *hashinfo,
69a583636aSCraig Gallek struct sk_buff *skb, int doff,
705ba24953STom Herbert const struct in6_addr *saddr,
715ba24953STom Herbert const __be16 sport,
725324a040SArnaldo Carvalho de Melo const struct in6_addr *daddr,
734297a0efSDavid Ahern const unsigned short hnum,
744297a0efSDavid Ahern const int dif, const int sdif);
755324a040SArnaldo Carvalho de Melo
766c886db2SLorenz Bauer struct sock *inet6_lookup_run_sk_lookup(struct net *net,
776c886db2SLorenz Bauer int protocol,
786c886db2SLorenz Bauer struct sk_buff *skb, int doff,
796c886db2SLorenz Bauer const struct in6_addr *saddr,
806c886db2SLorenz Bauer const __be16 sport,
816c886db2SLorenz Bauer const struct in6_addr *daddr,
826c886db2SLorenz Bauer const u16 hnum, const int dif,
836c886db2SLorenz Bauer inet6_ehashfn_t *ehashfn);
846c886db2SLorenz Bauer
__inet6_lookup(struct net * net,struct inet_hashinfo * hashinfo,struct sk_buff * skb,int doff,const struct in6_addr * saddr,const __be16 sport,const struct in6_addr * daddr,const u16 hnum,const int dif,const int sdif,bool * refcounted)85d86e0dacSPavel Emelyanov static inline struct sock *__inet6_lookup(struct net *net,
86d86e0dacSPavel Emelyanov struct inet_hashinfo *hashinfo,
87a583636aSCraig Gallek struct sk_buff *skb, int doff,
885324a040SArnaldo Carvalho de Melo const struct in6_addr *saddr,
89d2ecd9ccSAl Viro const __be16 sport,
905324a040SArnaldo Carvalho de Melo const struct in6_addr *daddr,
915324a040SArnaldo Carvalho de Melo const u16 hnum,
924297a0efSDavid Ahern const int dif, const int sdif,
933b24d854SEric Dumazet bool *refcounted)
945324a040SArnaldo Carvalho de Melo {
95d86e0dacSPavel Emelyanov struct sock *sk = __inet6_lookup_established(net, hashinfo, saddr,
964297a0efSDavid Ahern sport, daddr, hnum,
974297a0efSDavid Ahern dif, sdif);
983b24d854SEric Dumazet *refcounted = true;
995324a040SArnaldo Carvalho de Melo if (sk)
1005324a040SArnaldo Carvalho de Melo return sk;
1013b24d854SEric Dumazet *refcounted = false;
102a583636aSCraig Gallek return inet6_lookup_listener(net, hashinfo, skb, doff, saddr, sport,
1034297a0efSDavid Ahern daddr, hnum, dif, sdif);
1045324a040SArnaldo Carvalho de Melo }
1055324a040SArnaldo Carvalho de Melo
1069c02bec9SLorenz Bauer static inline
inet6_steal_sock(struct net * net,struct sk_buff * skb,int doff,const struct in6_addr * saddr,const __be16 sport,const struct in6_addr * daddr,const __be16 dport,bool * refcounted,inet6_ehashfn_t * ehashfn)1079c02bec9SLorenz Bauer struct sock *inet6_steal_sock(struct net *net, struct sk_buff *skb, int doff,
1089c02bec9SLorenz Bauer const struct in6_addr *saddr, const __be16 sport,
1099c02bec9SLorenz Bauer const struct in6_addr *daddr, const __be16 dport,
1109c02bec9SLorenz Bauer bool *refcounted, inet6_ehashfn_t *ehashfn)
1119c02bec9SLorenz Bauer {
1129c02bec9SLorenz Bauer struct sock *sk, *reuse_sk;
1139c02bec9SLorenz Bauer bool prefetched;
1149c02bec9SLorenz Bauer
1159c02bec9SLorenz Bauer sk = skb_steal_sock(skb, refcounted, &prefetched);
1169c02bec9SLorenz Bauer if (!sk)
1179c02bec9SLorenz Bauer return NULL;
1189c02bec9SLorenz Bauer
119*8897562fSLorenz Bauer if (!prefetched || !sk_fullsock(sk))
1209c02bec9SLorenz Bauer return sk;
1219c02bec9SLorenz Bauer
1229c02bec9SLorenz Bauer if (sk->sk_protocol == IPPROTO_TCP) {
1239c02bec9SLorenz Bauer if (sk->sk_state != TCP_LISTEN)
1249c02bec9SLorenz Bauer return sk;
1259c02bec9SLorenz Bauer } else if (sk->sk_protocol == IPPROTO_UDP) {
1269c02bec9SLorenz Bauer if (sk->sk_state != TCP_CLOSE)
1279c02bec9SLorenz Bauer return sk;
1289c02bec9SLorenz Bauer } else {
1299c02bec9SLorenz Bauer return sk;
1309c02bec9SLorenz Bauer }
1319c02bec9SLorenz Bauer
1329c02bec9SLorenz Bauer reuse_sk = inet6_lookup_reuseport(net, sk, skb, doff,
1339c02bec9SLorenz Bauer saddr, sport, daddr, ntohs(dport),
1349c02bec9SLorenz Bauer ehashfn);
1359c02bec9SLorenz Bauer if (!reuse_sk)
1369c02bec9SLorenz Bauer return sk;
1379c02bec9SLorenz Bauer
1389c02bec9SLorenz Bauer /* We've chosen a new reuseport sock which is never refcounted. This
1399c02bec9SLorenz Bauer * implies that sk also isn't refcounted.
1409c02bec9SLorenz Bauer */
1419c02bec9SLorenz Bauer WARN_ON_ONCE(*refcounted);
1429c02bec9SLorenz Bauer
1439c02bec9SLorenz Bauer return reuse_sk;
1449c02bec9SLorenz Bauer }
1459c02bec9SLorenz Bauer
__inet6_lookup_skb(struct inet_hashinfo * hashinfo,struct sk_buff * skb,int doff,const __be16 sport,const __be16 dport,int iif,int sdif,bool * refcounted)1469a1f27c4SArnaldo Carvalho de Melo static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo,
147a583636aSCraig Gallek struct sk_buff *skb, int doff,
1489a1f27c4SArnaldo Carvalho de Melo const __be16 sport,
149870c3151SEric Dumazet const __be16 dport,
1504297a0efSDavid Ahern int iif, int sdif,
1513b24d854SEric Dumazet bool *refcounted)
1529a1f27c4SArnaldo Carvalho de Melo {
1539c02bec9SLorenz Bauer struct net *net = dev_net(skb_dst(skb)->dev);
1549c02bec9SLorenz Bauer const struct ipv6hdr *ip6h = ipv6_hdr(skb);
1559c02bec9SLorenz Bauer struct sock *sk;
15623542618SKOVACS Krisztian
1579c02bec9SLorenz Bauer sk = inet6_steal_sock(net, skb, doff, &ip6h->saddr, sport, &ip6h->daddr, dport,
1589c02bec9SLorenz Bauer refcounted, inet6_ehashfn);
1599c02bec9SLorenz Bauer if (IS_ERR(sk))
1609c02bec9SLorenz Bauer return NULL;
161c7109986SEric Dumazet if (sk)
16223542618SKOVACS Krisztian return sk;
163c7109986SEric Dumazet
1649c02bec9SLorenz Bauer return __inet6_lookup(net, hashinfo, skb,
1659c02bec9SLorenz Bauer doff, &ip6h->saddr, sport,
1669c02bec9SLorenz Bauer &ip6h->daddr, ntohs(dport),
1674297a0efSDavid Ahern iif, sdif, refcounted);
1689a1f27c4SArnaldo Carvalho de Melo }
1699a1f27c4SArnaldo Carvalho de Melo
1701fd51155SJoe Perches struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo,
171a583636aSCraig Gallek struct sk_buff *skb, int doff,
172d2ecd9ccSAl Viro const struct in6_addr *saddr, const __be16 sport,
173d2ecd9ccSAl Viro const struct in6_addr *daddr, const __be16 dport,
174505cbfc5SArnaldo Carvalho de Melo const int dif);
175496611d7SCraig Gallek
176496611d7SCraig Gallek int inet6_hash(struct sock *sk);
17725de4668SWANG Cong
inet6_match(struct net * net,const struct sock * sk,const struct in6_addr * saddr,const struct in6_addr * daddr,const __portpair ports,const int dif,const int sdif)1785d368f03SEric Dumazet static inline bool inet6_match(struct net *net, const struct sock *sk,
1795d368f03SEric Dumazet const struct in6_addr *saddr,
1805d368f03SEric Dumazet const struct in6_addr *daddr,
1815d368f03SEric Dumazet const __portpair ports,
1825d368f03SEric Dumazet const int dif, const int sdif)
1835d368f03SEric Dumazet {
1845d368f03SEric Dumazet if (!net_eq(sock_net(sk), net) ||
1855d368f03SEric Dumazet sk->sk_family != AF_INET6 ||
1865d368f03SEric Dumazet sk->sk_portpair != ports ||
1875d368f03SEric Dumazet !ipv6_addr_equal(&sk->sk_v6_daddr, saddr) ||
1885d368f03SEric Dumazet !ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
1895d368f03SEric Dumazet return false;
1905d368f03SEric Dumazet
191944fd1aeSMike Manning /* READ_ONCE() paired with WRITE_ONCE() in sock_bindtoindex_locked() */
192944fd1aeSMike Manning return inet_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if), dif,
193944fd1aeSMike Manning sdif);
1945d368f03SEric Dumazet }
1955d368f03SEric Dumazet #endif /* IS_ENABLED(CONFIG_IPV6) */
19625de4668SWANG Cong
197505cbfc5SArnaldo Carvalho de Melo #endif /* _INET6_HASHTABLES_H */
198