Commit | Line | Data |
---|---|---|
8129765a ACM |
1 | /* |
2 | * INET An implementation of the TCP/IP protocol suite for the LINUX | |
3 | * operating system. INET is implemented using the BSD Socket | |
4 | * interface as the means of communication with the user level. | |
5 | * | |
6 | * Support for INET6 connection oriented protocols. | |
7 | * | |
8 | * Authors: See the TCPv6 sources | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or | |
11 | * modify it under the terms of the GNU General Public License | |
12 | * as published by the Free Software Foundation; either version | |
13 | * 2 of the License, or(at your option) any later version. | |
14 | */ | |
15 | ||
16 | #include <linux/config.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/in6.h> | |
19 | #include <linux/ipv6.h> | |
20 | #include <linux/jhash.h> | |
21 | ||
22 | #include <net/addrconf.h> | |
23 | #include <net/inet_connection_sock.h> | |
24 | #include <net/sock.h> | |
25 | ||
26 | /* | |
27 | * request_sock (formerly open request) hash tables. | |
28 | */ | |
29 | static u32 inet6_synq_hash(const struct in6_addr *raddr, const u16 rport, | |
30 | const u32 rnd, const u16 synq_hsize) | |
31 | { | |
32 | u32 a = raddr->s6_addr32[0]; | |
33 | u32 b = raddr->s6_addr32[1]; | |
34 | u32 c = raddr->s6_addr32[2]; | |
35 | ||
36 | a += JHASH_GOLDEN_RATIO; | |
37 | b += JHASH_GOLDEN_RATIO; | |
38 | c += rnd; | |
39 | __jhash_mix(a, b, c); | |
40 | ||
41 | a += raddr->s6_addr32[3]; | |
42 | b += (u32)rport; | |
43 | __jhash_mix(a, b, c); | |
44 | ||
45 | return c & (synq_hsize - 1); | |
46 | } | |
47 | ||
48 | struct request_sock *inet6_csk_search_req(const struct sock *sk, | |
49 | struct request_sock ***prevp, | |
50 | const __u16 rport, | |
51 | const struct in6_addr *raddr, | |
52 | const struct in6_addr *laddr, | |
53 | const int iif) | |
54 | { | |
55 | const struct inet_connection_sock *icsk = inet_csk(sk); | |
56 | struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; | |
57 | struct request_sock *req, **prev; | |
58 | ||
59 | for (prev = &lopt->syn_table[inet6_synq_hash(raddr, rport, | |
60 | lopt->hash_rnd, | |
61 | lopt->nr_table_entries)]; | |
62 | (req = *prev) != NULL; | |
63 | prev = &req->dl_next) { | |
64 | const struct tcp6_request_sock *treq = tcp6_rsk(req); | |
65 | ||
66 | if (inet_rsk(req)->rmt_port == rport && | |
67 | req->rsk_ops->family == AF_INET6 && | |
68 | ipv6_addr_equal(&treq->rmt_addr, raddr) && | |
69 | ipv6_addr_equal(&treq->loc_addr, laddr) && | |
70 | (!treq->iif || treq->iif == iif)) { | |
71 | BUG_TRAP(req->sk == NULL); | |
72 | *prevp = prev; | |
73 | return req; | |
74 | } | |
75 | } | |
76 | ||
77 | return NULL; | |
78 | } | |
79 | ||
80 | EXPORT_SYMBOL_GPL(inet6_csk_search_req); | |
81 | ||
82 | void inet6_csk_reqsk_queue_hash_add(struct sock *sk, | |
83 | struct request_sock *req, | |
84 | const unsigned long timeout) | |
85 | { | |
86 | struct inet_connection_sock *icsk = inet_csk(sk); | |
87 | struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; | |
88 | const u32 h = inet6_synq_hash(&tcp6_rsk(req)->rmt_addr, | |
89 | inet_rsk(req)->rmt_port, | |
90 | lopt->hash_rnd, lopt->nr_table_entries); | |
91 | ||
92 | reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout); | |
93 | inet_csk_reqsk_queue_added(sk, timeout); | |
94 | } | |
95 | ||
96 | EXPORT_SYMBOL_GPL(inet6_csk_reqsk_queue_hash_add); |