patch-2.4.21 linux-2.4.21/net/ipv6/tcp_ipv6.c
Next file: linux-2.4.21/net/ipv6/udp.c
Previous file: linux-2.4.21/net/ipv6/sysctl_net_ipv6.c
Back to the patch index
Back to the overall index
- Lines: 167
- Date:
2003-06-13 07:51:39.000000000 -0700
- Orig file:
linux-2.4.20/net/ipv6/tcp_ipv6.c
- Orig date:
2002-11-28 15:53:15.000000000 -0800
diff -urN linux-2.4.20/net/ipv6/tcp_ipv6.c linux-2.4.21/net/ipv6/tcp_ipv6.c
@@ -14,6 +14,9 @@
*
* Fixes:
* Hideaki YOSHIFUJI : sin6_scope_id support
+ * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
+ * Alexey Kuznetsov allow both IPv4 and IPv6 sockets to bind
+ * a single port at the same time.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,7 +24,6 @@
* 2 of the License, or (at your option) any later version.
*/
-#define __NO_VERSION__
#include <linux/module.h>
#include <linux/config.h>
#include <linux/errno.h>
@@ -34,6 +36,7 @@
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/init.h>
+#include <linux/jhash.h>
#include <linux/ipsec.h>
#include <linux/ipv6.h>
@@ -148,14 +151,23 @@
!sk2->reuse ||
sk2->state == TCP_LISTEN) {
/* NOTE: IPv6 tw bucket have different format */
- if (!sk2->rcv_saddr ||
- addr_type == IPV6_ADDR_ANY ||
- !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
- sk2->state != TCP_TIME_WAIT ?
- &sk2->net_pinfo.af_inet6.rcv_saddr :
- &((struct tcp_tw_bucket*)sk)->v6_rcv_saddr) ||
- (addr_type==IPV6_ADDR_MAPPED && sk2->family==AF_INET &&
- sk->rcv_saddr==sk2->rcv_saddr))
+ if ((!sk2->rcv_saddr && !ipv6_only_sock(sk)) ||
+ (sk2->family == AF_INET6 &&
+ ipv6_addr_any(&sk2->net_pinfo.af_inet6.rcv_saddr) &&
+ !(ipv6_only_sock(sk2) && addr_type == IPV6_ADDR_MAPPED)) ||
+ (addr_type == IPV6_ADDR_ANY &&
+ (!ipv6_only_sock(sk) ||
+ !(sk2->family == AF_INET6 ? ipv6_addr_type(&sk2->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_MAPPED : 1))) ||
+ (sk2->family == AF_INET6 &&
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+ sk2->state != TCP_TIME_WAIT ?
+ &sk2->net_pinfo.af_inet6.rcv_saddr :
+ &((struct tcp_tw_bucket*)sk)->v6_rcv_saddr)) ||
+ (addr_type == IPV6_ADDR_MAPPED &&
+ !ipv6_only_sock(sk2) &&
+ (!sk2->rcv_saddr ||
+ !sk->rcv_saddr ||
+ sk->rcv_saddr == sk2->rcv_saddr)))
break;
}
}
@@ -357,12 +369,24 @@
* Open request hash tables.
*/
-static __inline__ unsigned tcp_v6_synq_hash(struct in6_addr *raddr, u16 rport)
+static u32 tcp_v6_synq_hash(struct in6_addr *raddr, u16 rport, u32 rnd)
{
- unsigned h = raddr->s6_addr32[3] ^ rport;
- h ^= h>>16;
- h ^= h>>8;
- return h&(TCP_SYNQ_HSIZE-1);
+ u32 a, b, c;
+
+ a = raddr->s6_addr32[0];
+ b = raddr->s6_addr32[1];
+ c = raddr->s6_addr32[2];
+
+ a += JHASH_GOLDEN_RATIO;
+ b += JHASH_GOLDEN_RATIO;
+ c += rnd;
+ __jhash_mix(a, b, c);
+
+ a += raddr->s6_addr32[3];
+ b += (u32) rport;
+ __jhash_mix(a, b, c);
+
+ return c & (TCP_SYNQ_HSIZE - 1);
}
static struct open_request *tcp_v6_search_req(struct tcp_opt *tp,
@@ -375,7 +399,7 @@
struct tcp_listen_opt *lopt = tp->listen_opt;
struct open_request *req, **prev;
- for (prev = &lopt->syn_table[tcp_v6_synq_hash(raddr, rport)];
+ for (prev = &lopt->syn_table[tcp_v6_synq_hash(raddr, rport, lopt->hash_rnd)];
(req = *prev) != NULL;
prev = &req->dl_next) {
if (req->rmt_port == rport &&
@@ -602,6 +626,9 @@
SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
+ if (__ipv6_only_sock(sk))
+ return -ENETUNREACH;
+
sin.sin_family = AF_INET;
sin.sin_port = usin->sin6_port;
sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
@@ -761,7 +788,7 @@
dst = ip6_route_output(sk, &fl);
} else
- dst_clone(dst);
+ dst_hold(dst);
if (dst->error) {
sk->err_soft = -dst->error;
@@ -922,7 +949,7 @@
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
if (skb->ip_summed == CHECKSUM_HW) {
- th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0);
+ th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0);
skb->csum = offsetof(struct tcphdr, check);
} else {
th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
@@ -1121,7 +1148,7 @@
{
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
struct tcp_listen_opt *lopt = tp->listen_opt;
- unsigned h = tcp_v6_synq_hash(&req->af.v6_req.rmt_addr, req->rmt_port);
+ u32 h = tcp_v6_synq_hash(&req->af.v6_req.rmt_addr, req->rmt_port, lopt->hash_rnd);
req->sk = NULL;
req->expires = jiffies + TCP_TIMEOUT_INIT;
@@ -1412,9 +1439,6 @@
*/
static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
{
-#ifdef CONFIG_FILTER
- struct sk_filter *filter;
-#endif
struct sk_buff *opt_skb = NULL;
/* Imagine: socket is IPv6. IPv4 packet arrives,
@@ -1428,12 +1452,6 @@
if (skb->protocol == htons(ETH_P_IP))
return tcp_v4_do_rcv(sk, skb);
-#ifdef CONFIG_FILTER
- filter = sk->filter;
- if (filter && sk_filter(skb, filter))
- goto discard;
-#endif /* CONFIG_FILTER */
-
/*
* socket locking is here for SMP purposes as backlog rcv
* is currently called with bh processing disabled.
@@ -1586,6 +1604,9 @@
if(sk->state == TCP_TIME_WAIT)
goto do_time_wait;
+ if (sk_filter(sk, skb, 0))
+ goto discard_and_relse;
+
skb->dev = NULL;
bh_lock_sock(sk);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)