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(a: lhash, b: fhash, c: 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: &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(dev: 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, saddr: &ip6h->saddr, sport, daddr: &ip6h->daddr, dport, |
158 | refcounted, ehashfn: inet6_ehashfn); |
159 | if (IS_ERR(ptr: sk)) |
160 | return NULL; |
161 | if (sk) |
162 | return sk; |
163 | |
164 | return __inet6_lookup(net, hashinfo, skb, |
165 | doff, saddr: &ip6h->saddr, sport, |
166 | daddr: &ip6h->daddr, ntohs(dport), |
167 | dif: 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(net1: sock_net(sk), net2: net) || |
185 | sk->sk_family != AF_INET6 || |
186 | sk->sk_portpair != ports || |
187 | !ipv6_addr_equal(a1: &sk->sk_v6_daddr, a2: saddr) || |
188 | !ipv6_addr_equal(a1: &sk->sk_v6_rcv_saddr, a2: 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 | |