[IPV6]: Generalise tcp_v6_search_req & tcp_v6_synq_add
[linux-2.6] / net / ipv6 / inet6_connection_sock.c
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);