Commit 5324a040 authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by David S. Miller

[INET6_HASHTABLES]: Move inet6_lookup functions to net/ipv6/inet6_hashtables.c

Doing this we allow tcp_diag to support IPV6 even if tcp_diag is compiled
statically and IPV6 is compiled as a module, removing the previous restriction
while not building any IPV6 code if it is not selected.

Now to work on the tcpdiag_register infrastructure and then to rename the whole
thing to inetdiag, reflecting its by then completely generic nature.
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 505cbfc5
...@@ -14,13 +14,117 @@ ...@@ -14,13 +14,117 @@
#ifndef _INET6_HASHTABLES_H #ifndef _INET6_HASHTABLES_H
#define _INET6_HASHTABLES_H #define _INET6_HASHTABLES_H
#include <linux/config.h>
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
#include <linux/in6.h>
#include <linux/ipv6.h>
#include <linux/types.h> #include <linux/types.h>
struct in6_addr; #include <net/ipv6.h>
struct inet_hashinfo; struct inet_hashinfo;
/* I have no idea if this is a good hash for v6 or not. -DaveM */
static inline int inet6_ehashfn(const struct in6_addr *laddr, const u16 lport,
const struct in6_addr *faddr, const u16 fport,
const int ehash_size)
{
int hashent = (lport ^ fport);
hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]);
hashent ^= hashent >> 16;
hashent ^= hashent >> 8;
return (hashent & (ehash_size - 1));
}
static inline int inet6_sk_ehashfn(const struct sock *sk, const int ehash_size)
{
const struct inet_sock *inet = inet_sk(sk);
const struct ipv6_pinfo *np = inet6_sk(sk);
const struct in6_addr *laddr = &np->rcv_saddr;
const struct in6_addr *faddr = &np->daddr;
const __u16 lport = inet->num;
const __u16 fport = inet->dport;
return inet6_ehashfn(laddr, lport, faddr, fport, ehash_size);
}
/*
* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
* we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
*
* The sockhash lock must be held as a reader here.
*/
static inline struct sock *
__inet6_lookup_established(struct inet_hashinfo *hashinfo,
const struct in6_addr *saddr,
const u16 sport,
const struct in6_addr *daddr,
const u16 hnum,
const int dif)
{
struct sock *sk;
const struct hlist_node *node;
const __u32 ports = INET_COMBINED_PORTS(sport, hnum);
/* Optimize here for direct hit, only listening connections can
* have wildcards anyways.
*/
const int hash = inet6_ehashfn(daddr, hnum, saddr, sport,
hashinfo->ehash_size);
struct inet_ehash_bucket *head = &hashinfo->ehash[hash];
read_lock(&head->lock);
sk_for_each(sk, node, &head->chain) {
/* For IPV6 do the cheaper port and family tests first. */
if (INET6_MATCH(sk, saddr, daddr, ports, dif))
goto hit; /* You sunk my battleship! */
}
/* Must check for a TIME_WAIT'er before going to listener hash. */
sk_for_each(sk, node, &(head + hashinfo->ehash_size)->chain) {
const struct inet_timewait_sock *tw = inet_twsk(sk);
if(*((__u32 *)&(tw->tw_dport)) == ports &&
sk->sk_family == PF_INET6) {
const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk);
if (ipv6_addr_equal(&tcp6tw->tw_v6_daddr, saddr) &&
ipv6_addr_equal(&tcp6tw->tw_v6_rcv_saddr, daddr) &&
(!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dif))
goto hit;
}
}
read_unlock(&head->lock);
return NULL;
hit:
sock_hold(sk);
read_unlock(&head->lock);
return sk;
}
extern struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo,
const struct in6_addr *daddr,
const unsigned short hnum,
const int dif);
static inline struct sock *__inet6_lookup(struct inet_hashinfo *hashinfo,
const struct in6_addr *saddr,
const u16 sport,
const struct in6_addr *daddr,
const u16 hnum,
const int dif)
{
struct sock *sk = __inet6_lookup_established(hashinfo, saddr, sport,
daddr, hnum, dif);
if (sk)
return sk;
return inet6_lookup_listener(hashinfo, daddr, hnum, dif);
}
extern struct sock *inet6_lookup(struct inet_hashinfo *hashinfo, extern struct sock *inet6_lookup(struct inet_hashinfo *hashinfo,
const struct in6_addr *saddr, const u16 sport, const struct in6_addr *saddr, const u16 sport,
const struct in6_addr *daddr, const u16 dport, const struct in6_addr *daddr, const u16 dport,
const int dif); const int dif);
#endif /* defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) */
#endif /* _INET6_HASHTABLES_H */ #endif /* _INET6_HASHTABLES_H */
...@@ -419,9 +419,7 @@ config IP_TCPDIAG ...@@ -419,9 +419,7 @@ config IP_TCPDIAG
---help--- ---help---
Support for TCP socket monitoring interface used by native Linux Support for TCP socket monitoring interface used by native Linux
tools such as ss. ss is included in iproute2, currently downloadable tools such as ss. ss is included in iproute2, currently downloadable
at <http://developer.osdl.org/dev/iproute2>. If you want IPv6 or DCCP at <http://developer.osdl.org/dev/iproute2>.
support and have selected IPv6 or DCCP as a module, you need to build
this as a module too.
If unsure, say Y. If unsure, say Y.
......
...@@ -23,3 +23,5 @@ obj-$(CONFIG_NETFILTER) += netfilter/ ...@@ -23,3 +23,5 @@ obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
obj-y += exthdrs_core.o obj-y += exthdrs_core.o
obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* Generic INET6 transport hashtables
*
* Authors: Lotsa people, from code originally in tcp
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
#include <net/inet6_hashtables.h>
struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo,
const struct in6_addr *daddr,
const unsigned short hnum, const int dif)
{
struct sock *sk;
const struct hlist_node *node;
struct sock *result = NULL;
int score, hiscore = 0;
read_lock(&hashinfo->lhash_lock);
sk_for_each(sk, node, &hashinfo->listening_hash[inet_lhashfn(hnum)]) {
if (inet_sk(sk)->num == hnum && sk->sk_family == PF_INET6) {
const struct ipv6_pinfo *np = inet6_sk(sk);
score = 1;
if (!ipv6_addr_any(&np->rcv_saddr)) {
if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
continue;
score++;
}
if (sk->sk_bound_dev_if) {
if (sk->sk_bound_dev_if != dif)
continue;
score++;
}
if (score == 3) {
result = sk;
break;
}
if (score > hiscore) {
hiscore = score;
result = sk;
}
}
}
if (result)
sock_hold(result);
read_unlock(&hashinfo->lhash_lock);
return result;
}
EXPORT_SYMBOL_GPL(inet6_lookup_listener);
struct sock *inet6_lookup(struct inet_hashinfo *hashinfo,
const struct in6_addr *saddr, const u16 sport,
const struct in6_addr *daddr, const u16 dport,
const int dif)
{
struct sock *sk;
local_bh_disable();
sk = __inet6_lookup(hashinfo, saddr, sport, daddr, ntohs(dport), dif);
local_bh_enable();
return sk;
}
EXPORT_SYMBOL_GPL(inet6_lookup);
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <net/tcp.h> #include <net/tcp.h>
#include <net/ndisc.h> #include <net/ndisc.h>
#include <net/inet6_hashtables.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/transp_v6.h> #include <net/transp_v6.h>
#include <net/addrconf.h> #include <net/addrconf.h>
...@@ -75,30 +76,6 @@ static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok); ...@@ -75,30 +76,6 @@ static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok);
static struct tcp_func ipv6_mapped; static struct tcp_func ipv6_mapped;
static struct tcp_func ipv6_specific; static struct tcp_func ipv6_specific;
/* I have no idea if this is a good hash for v6 or not. -DaveM */
static inline int inet6_ehashfn(const struct in6_addr *laddr, const u16 lport,
const struct in6_addr *faddr, const u16 fport,
const int ehash_size)
{
int hashent = (lport ^ fport);
hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]);
hashent ^= hashent>>16;
hashent ^= hashent>>8;
return (hashent & (ehash_size - 1));
}
static inline int inet6_sk_ehashfn(const struct sock *sk, const int ehash_size)
{
const struct inet_sock *inet = inet_sk(sk);
const struct ipv6_pinfo *np = inet6_sk(sk);
const struct in6_addr *laddr = &np->rcv_saddr;
const struct in6_addr *faddr = &np->daddr;
const __u16 lport = inet->num;
const __u16 fport = inet->dport;
return inet6_ehashfn(laddr, lport, faddr, fport, ehash_size);
}
static inline int tcp_v6_bind_conflict(const struct sock *sk, static inline int tcp_v6_bind_conflict(const struct sock *sk,
const struct inet_bind_bucket *tb) const struct inet_bind_bucket *tb)
{ {
...@@ -259,135 +236,6 @@ static void tcp_v6_hash(struct sock *sk) ...@@ -259,135 +236,6 @@ static void tcp_v6_hash(struct sock *sk)
} }
} }
static struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo,
const struct in6_addr *daddr,
const unsigned short hnum,
const int dif)
{
struct sock *sk;
struct hlist_node *node;
struct sock *result = NULL;
int score, hiscore;
hiscore=0;
read_lock(&hashinfo->lhash_lock);
sk_for_each(sk, node, &hashinfo->listening_hash[inet_lhashfn(hnum)]) {
if (inet_sk(sk)->num == hnum && sk->sk_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);
score = 1;
if (!ipv6_addr_any(&np->rcv_saddr)) {
if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
continue;
score++;
}
if (sk->sk_bound_dev_if) {
if (sk->sk_bound_dev_if != dif)
continue;
score++;
}
if (score == 3) {
result = sk;
break;
}
if (score > hiscore) {
hiscore = score;
result = sk;
}
}
}
if (result)
sock_hold(result);
read_unlock(&hashinfo->lhash_lock);
return result;
}
/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
* we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
*
* The sockhash lock must be held as a reader here.
*/
static inline struct sock *
__inet6_lookup_established(struct inet_hashinfo *hashinfo,
const struct in6_addr *saddr,
const u16 sport,
const struct in6_addr *daddr,
const u16 hnum,
const int dif)
{
struct sock *sk;
const struct hlist_node *node;
const __u32 ports = INET_COMBINED_PORTS(sport, hnum);
/* Optimize here for direct hit, only listening connections can
* have wildcards anyways.
*/
const int hash = inet6_ehashfn(daddr, hnum, saddr, sport,
hashinfo->ehash_size);
struct inet_ehash_bucket *head = &hashinfo->ehash[hash];
read_lock(&head->lock);
sk_for_each(sk, node, &head->chain) {
/* For IPV6 do the cheaper port and family tests first. */
if (INET6_MATCH(sk, saddr, daddr, ports, dif))
goto hit; /* You sunk my battleship! */
}
/* Must check for a TIME_WAIT'er before going to listener hash. */
sk_for_each(sk, node, &(head + hashinfo->ehash_size)->chain) {
const struct inet_timewait_sock *tw = inet_twsk(sk);
if(*((__u32 *)&(tw->tw_dport)) == ports &&
sk->sk_family == PF_INET6) {
const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk);
if (ipv6_addr_equal(&tcp6tw->tw_v6_daddr, saddr) &&
ipv6_addr_equal(&tcp6tw->tw_v6_rcv_saddr, daddr) &&
(!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dif))
goto hit;
}
}
read_unlock(&head->lock);
return NULL;
hit:
sock_hold(sk);
read_unlock(&head->lock);
return sk;
}
static inline struct sock *__inet6_lookup(struct inet_hashinfo *hashinfo,
const struct in6_addr *saddr,
const u16 sport,
const struct in6_addr *daddr,
const u16 hnum,
const int dif)
{
struct sock *sk = __inet6_lookup_established(hashinfo, saddr, sport,
daddr, hnum, dif);
if (sk)
return sk;
return inet6_lookup_listener(hashinfo, daddr, hnum, dif);
}
inline struct sock *inet6_lookup(struct inet_hashinfo *hashinfo,
const struct in6_addr *saddr, const u16 sport,
const struct in6_addr *daddr, const u16 dport,
const int dif)
{
struct sock *sk;
local_bh_disable();
sk = __inet6_lookup(hashinfo, saddr, sport, daddr, ntohs(dport), dif);
local_bh_enable();
return sk;
}
EXPORT_SYMBOL_GPL(inet6_lookup);
/* /*
* Open request hash tables. * Open request hash tables.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment