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 * Authors: Lotsa people, from code originally in tcp 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * as published by the Free Software Foundation; either version 11 * 2 of the License, or (at your option) any later version. 12 */ 13 14 #ifndef _INET6_HASHTABLES_H 15 #define _INET6_HASHTABLES_H 16 17 #include <linux/config.h> 18 19 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) 20 #include <linux/in6.h> 21 #include <linux/ipv6.h> 22 #include <linux/types.h> 23 24 #include <net/ipv6.h> 25 26 struct inet_hashinfo; 27 28 /* I have no idea if this is a good hash for v6 or not. -DaveM */ 29 static inline unsigned int inet6_ehashfn(const struct in6_addr *laddr, const u16 lport, 30 const struct in6_addr *faddr, const u16 fport) 31 { 32 unsigned int hashent = (lport ^ fport); 33 34 hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]); 35 hashent ^= hashent >> 16; 36 hashent ^= hashent >> 8; 37 return hashent; 38 } 39 40 static inline int inet6_sk_ehashfn(const struct sock *sk) 41 { 42 const struct inet_sock *inet = inet_sk(sk); 43 const struct ipv6_pinfo *np = inet6_sk(sk); 44 const struct in6_addr *laddr = &np->rcv_saddr; 45 const struct in6_addr *faddr = &np->daddr; 46 const __u16 lport = inet->num; 47 const __u16 fport = inet->dport; 48 return inet6_ehashfn(laddr, lport, faddr, fport); 49 } 50 51 static inline void __inet6_hash(struct inet_hashinfo *hashinfo, 52 struct sock *sk) 53 { 54 struct hlist_head *list; 55 rwlock_t *lock; 56 57 BUG_TRAP(sk_unhashed(sk)); 58 59 if (sk->sk_state == TCP_LISTEN) { 60 list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)]; 61 lock = &hashinfo->lhash_lock; 62 inet_listen_wlock(hashinfo); 63 } else { 64 unsigned int hash; 65 sk->sk_hash = hash = inet6_sk_ehashfn(sk); 66 hash &= (hashinfo->ehash_size - 1); 67 list = &hashinfo->ehash[hash].chain; 68 lock = &hashinfo->ehash[hash].lock; 69 write_lock(lock); 70 } 71 72 __sk_add_node(sk, list); 73 sock_prot_inc_use(sk->sk_prot); 74 write_unlock(lock); 75 } 76 77 /* 78 * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so 79 * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM 80 * 81 * The sockhash lock must be held as a reader here. 82 */ 83 static inline struct sock * 84 __inet6_lookup_established(struct inet_hashinfo *hashinfo, 85 const struct in6_addr *saddr, 86 const u16 sport, 87 const struct in6_addr *daddr, 88 const u16 hnum, 89 const int dif) 90 { 91 struct sock *sk; 92 const struct hlist_node *node; 93 const __u32 ports = INET_COMBINED_PORTS(sport, hnum); 94 /* Optimize here for direct hit, only listening connections can 95 * have wildcards anyways. 96 */ 97 unsigned int hash = inet6_ehashfn(daddr, hnum, saddr, sport); 98 struct inet_ehash_bucket *head = inet_ehash_bucket(hashinfo, hash); 99 100 prefetch(head->chain.first); 101 read_lock(&head->lock); 102 sk_for_each(sk, node, &head->chain) { 103 /* For IPV6 do the cheaper port and family tests first. */ 104 if (INET6_MATCH(sk, hash, saddr, daddr, ports, dif)) 105 goto hit; /* You sunk my battleship! */ 106 } 107 /* Must check for a TIME_WAIT'er before going to listener hash. */ 108 sk_for_each(sk, node, &(head + hashinfo->ehash_size)->chain) { 109 const struct inet_timewait_sock *tw = inet_twsk(sk); 110 111 if(*((__u32 *)&(tw->tw_dport)) == ports && 112 sk->sk_family == PF_INET6) { 113 const struct inet6_timewait_sock *tw6 = inet6_twsk(sk); 114 115 if (ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) && 116 ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) && 117 (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dif)) 118 goto hit; 119 } 120 } 121 read_unlock(&head->lock); 122 return NULL; 123 124 hit: 125 sock_hold(sk); 126 read_unlock(&head->lock); 127 return sk; 128 } 129 130 extern struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, 131 const struct in6_addr *daddr, 132 const unsigned short hnum, 133 const int dif); 134 135 static inline struct sock *__inet6_lookup(struct inet_hashinfo *hashinfo, 136 const struct in6_addr *saddr, 137 const u16 sport, 138 const struct in6_addr *daddr, 139 const u16 hnum, 140 const int dif) 141 { 142 struct sock *sk = __inet6_lookup_established(hashinfo, saddr, sport, 143 daddr, hnum, dif); 144 if (sk) 145 return sk; 146 147 return inet6_lookup_listener(hashinfo, daddr, hnum, dif); 148 } 149 150 extern struct sock *inet6_lookup(struct inet_hashinfo *hashinfo, 151 const struct in6_addr *saddr, const u16 sport, 152 const struct in6_addr *daddr, const u16 dport, 153 const int dif); 154 #endif /* defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) */ 155 #endif /* _INET6_HASHTABLES_H */ 156