summaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/addrconf.c4
-rw-r--r--net/ipv6/af_inet6.c7
-rw-r--r--net/ipv6/exthdrs.c2
-rw-r--r--net/ipv6/icmp.c2
-rw-r--r--net/ipv6/ip6_fib.c2
-rw-r--r--net/ipv6/ip6_fw.c2
-rw-r--r--net/ipv6/ip6_input.c2
-rw-r--r--net/ipv6/ip6_output.c2
-rw-r--r--net/ipv6/ipv6_sockglue.c2
-rw-r--r--net/ipv6/ndisc.c2
-rw-r--r--net/ipv6/proc.c59
-rw-r--r--net/ipv6/raw.c2
-rw-r--r--net/ipv6/reassembly.c2
-rw-r--r--net/ipv6/route.c43
-rw-r--r--net/ipv6/sit.c2
-rw-r--r--net/ipv6/tcp_ipv6.c423
-rw-r--r--net/ipv6/udp.c39
17 files changed, 334 insertions, 263 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index c4faba4b7..4a4060601 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: addrconf.c,v 1.32 1997/12/27 20:41:18 kuznet Exp $
+ * $Id: addrconf.c,v 1.37 1998/03/08 20:52:46 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -1753,6 +1753,8 @@ static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf
t->sysctl_header = register_sysctl_table(t->addrconf_root_dir, 0);
if (t->sysctl_header == NULL)
kfree(t);
+ else
+ p->sysctl = t;
}
static void addrconf_sysctl_unregister(struct ipv6_devconf *p)
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index b0a0eb702..bc5ba892a 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/af_inet.c
*
- * $Id: af_inet6.c,v 1.24 1997/12/13 21:53:08 kuznet Exp $
+ * $Id: af_inet6.c,v 1.28 1998/03/08 05:56:49 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -71,7 +71,7 @@ static int inet6_create(struct socket *sock, int protocol)
struct sock *sk;
struct proto *prot;
- sk = sk_alloc(AF_INET6, GFP_KERNEL);
+ sk = sk_alloc(AF_INET6, GFP_KERNEL, 1);
if (sk == NULL)
goto do_oom;
@@ -139,8 +139,7 @@ static int inet6_create(struct socket *sock, int protocol)
* creation time automatically shares.
*/
sk->dummy_th.source = ntohs(sk->num);
- if(sk->prot->hash)
- sk->prot->hash(sk);
+ sk->prot->hash(sk);
add_to_prot_sklist(sk);
}
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 6b7508666..af29057ec 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: exthdrs.c,v 1.4 1997/03/18 18:24:29 davem Exp $
+ * $Id: exthdrs.c,v 1.5 1998/02/12 07:43:39 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index b84dc9268..96867403b 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: icmp.c,v 1.12 1997/12/13 21:53:10 kuznet Exp $
+ * $Id: icmp.c,v 1.13 1998/02/12 07:43:41 davem Exp $
*
* Based on net/ipv4/icmp.c
*
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 15ce420ac..9fce1acca 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: ip6_fib.c,v 1.10 1997/12/13 21:53:10 kuznet Exp $
+ * $Id: ip6_fib.c,v 1.11 1998/03/08 05:56:50 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/ipv6/ip6_fw.c b/net/ipv6/ip6_fw.c
index 7316a30f1..3c3a0cfc5 100644
--- a/net/ipv6/ip6_fw.c
+++ b/net/ipv6/ip6_fw.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: ip6_fw.c,v 1.8 1997/12/13 21:53:11 kuznet Exp $
+ * $Id: ip6_fw.c,v 1.9 1998/02/12 07:43:42 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index ead32047a..71ad7e1a0 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -6,7 +6,7 @@
* Pedro Roque <roque@di.fc.ul.pt>
* Ian P. Morris <I.P.Morris@soton.ac.uk>
*
- * $Id: ip6_input.c,v 1.7 1997/09/20 20:48:27 davem Exp $
+ * $Id: ip6_input.c,v 1.8 1998/02/12 07:43:43 davem Exp $
*
* Based in linux/net/ipv4/ip_input.c
*
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 67b81d041..13029e175 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: ip6_output.c,v 1.7 1997/12/29 19:52:46 kuznet Exp $
+ * $Id: ip6_output.c,v 1.9 1998/03/08 05:56:50 davem Exp $
*
* Based on linux/net/ipv4/ip_output.c
*
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index f2ef3fd76..c6714eea3 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -7,7 +7,7 @@
*
* Based on linux/net/ipv4/ip_sockglue.c
*
- * $Id: ipv6_sockglue.c,v 1.16 1997/12/13 21:53:13 kuznet Exp $
+ * $Id: ipv6_sockglue.c,v 1.17 1998/03/08 05:56:51 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 3fb0680bc..ce37117a3 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -132,7 +132,7 @@ struct neigh_table nd_tbl =
pndisc_destructor,
pndisc_redo,
{ NULL, NULL, &nd_tbl, 0, NULL, NULL,
- 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 0, 64 },
+ 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 0 },
30*HZ, 128, 512, 1024,
};
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
index b9b811e35..b87d4696b 100644
--- a/net/ipv6/proc.c
+++ b/net/ipv6/proc.c
@@ -7,7 +7,7 @@
* PROC file system. This is very similar to the IPv4 version,
* except it reports the sockets in the INET6 address family.
*
- * Version: $Id: proc.c,v 1.4 1997/04/20 22:50:44 schenk Exp $
+ * Version: $Id: proc.c,v 1.6 1998/03/13 08:02:19 davem Exp $
*
* Authors: David S. Miller (davem@caip.rutgers.edu)
*
@@ -21,6 +21,7 @@
#include <linux/net.h>
#include <linux/in6.h>
#include <net/sock.h>
+#include <net/tcp.h>
#include <net/transp_v6.h>
/* This is the main implementation workhorse of all these routines. */
@@ -52,21 +53,35 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta
SOCKHASH_LOCK();
sp = pro->sklist_next;
while(sp != (struct sock *)pro) {
+ struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp;
+ int tw_bucket = 0;
+
pos += 149;
if(pos < offset)
goto next;
tp = &(sp->tp_pinfo.af_tcp);
- dest = &sp->net_pinfo.af_inet6.daddr;
- src = &sp->net_pinfo.af_inet6.rcv_saddr;
+ if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
+ tw_bucket = 1;
+ dest = &tw->v6_daddr;
+ src = &tw->v6_rcv_saddr;
+ } else {
+ dest = &sp->net_pinfo.af_inet6.daddr;
+ src = &sp->net_pinfo.af_inet6.rcv_saddr;
+ }
destp = ntohs(sp->dummy_th.dest);
srcp = ntohs(sp->dummy_th.source);
-
- timer_active1 = del_timer(&tp->retransmit_timer);
- timer_active2 = del_timer(&sp->timer);
- if(!timer_active1) tp->retransmit_timer.expires = 0;
- if(!timer_active2) sp->timer.expires = 0;
- timer_active = 0;
- timer_expires = (unsigned) -1;
+ if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
+ timer_active1 = timer_active2 = 0;
+ timer_active = 3;
+ timer_expires = tw->timer.expires;
+ } else {
+ timer_active1 = del_timer(&tp->retransmit_timer);
+ timer_active2 = del_timer(&sp->timer);
+ if(!timer_active1) tp->retransmit_timer.expires = 0;
+ if(!timer_active2) sp->timer.expires = 0;
+ timer_active = 0;
+ timer_expires = (unsigned) -1;
+ }
if(timer_active1 && tp->retransmit_timer.expires < timer_expires) {
timer_active = timer_active1;
timer_expires = tp->retransmit_timer.expires;
@@ -75,6 +90,8 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta
timer_active = timer_active2;
timer_expires = sp->timer.expires;
}
+ if(timer_active == 0)
+ timer_expires = jiffies;
sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
"%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
i,
@@ -83,13 +100,23 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta
dest->s6_addr32[0], dest->s6_addr32[1],
dest->s6_addr32[2], dest->s6_addr32[3], destp,
sp->state,
- format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc),
- format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc),
+ (tw_bucket ?
+ 0 :
+ (format == 0) ?
+ tp->write_seq-tp->snd_una :
+ atomic_read(&sp->wmem_alloc)),
+ (tw_bucket ?
+ 0 :
+ (format == 0) ?
+ tp->rcv_nxt-tp->copied_seq :
+ atomic_read(&sp->rmem_alloc)),
timer_active, timer_expires-jiffies,
- tp->retransmits,
- sp->socket ? sp->socket->inode->i_uid:0,
- timer_active?sp->timeout:0,
- sp->socket ? sp->socket->inode->i_ino:0);
+ (tw_bucket ? 0 : tp->retransmits),
+ ((!tw_bucket && sp->socket) ?
+ sp->socket->inode->i_uid : 0),
+ (!tw_bucket && timer_active) ? sp->timeout : 0,
+ ((!tw_bucket && sp->socket) ?
+ sp->socket->inode->i_ino : 0));
if(timer_active1) add_timer(&tp->retransmit_timer);
if(timer_active2) add_timer(&sp->timer);
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 4ee1b13ad..5b182b7ef 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/raw.c
*
- * $Id: raw.c,v 1.16 1997/12/29 19:52:48 kuznet Exp $
+ * $Id: raw.c,v 1.18 1998/03/08 05:56:54 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index aa027da14..55fecc676 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: reassembly.c,v 1.8 1997/12/29 19:52:50 kuznet Exp $
+ * $Id: reassembly.c,v 1.9 1998/02/12 07:43:48 davem Exp $
*
* Based on: net/ipv4/ip_fragment.c
*
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 28ee43e78..5188de864 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: route.c,v 1.19 1997/12/13 21:53:16 kuznet Exp $
+ * $Id: route.c,v 1.25 1998/03/15 03:31:47 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -85,18 +85,18 @@ struct dst_ops ip6_dst_ops = {
};
struct rt6_info ip6_null_entry = {
- {{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL,
+ {{NULL, ATOMIC_INIT(1), ATOMIC_INIT(1), NULL,
-1, 0, 0, 0, 0, 0, 0, 0, 0,
-ENETUNREACH, NULL, NULL,
ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}},
NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0U,
- 0, 255, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
+ 255, 0, {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0}
};
struct fib6_node ip6_routing_table = {
NULL, NULL, NULL, NULL,
&ip6_null_entry,
- 0, RTN_ROOT|RTN_TL_ROOT, 0
+ 0, RTN_ROOT|RTN_TL_ROOT|RTN_RTINFO, 0
};
#ifdef CONFIG_RT6_POLICY
@@ -709,14 +709,14 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err)
if (rt == NULL) {
RDBG(("dalloc fails, "));
*err = -ENOMEM;
- goto out;
+ return NULL;
}
rt->u.dst.obsolete = -1;
rt->rt6i_expires = rtmsg->rtmsg_info;
addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
-
+
if (addr_type & IPV6_ADDR_MULTICAST) {
RDBG(("MCAST, "));
rt->u.dst.input = ip6_mc_input;
@@ -743,6 +743,21 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err)
rt->rt6i_src.plen = rtmsg->rtmsg_src_len;
ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen);
+ /* We cannot add true routes via loopback here,
+ they would result in kernel looping; promote them to reject routes
+ */
+ if ((rtmsg->rtmsg_flags&RTF_REJECT) ||
+ (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) {
+ dev = dev_get("lo");
+ rt->u.dst.output = ip6_pkt_discard;
+ rt->u.dst.input = ip6_pkt_discard;
+ rt->u.dst.error = -ENETUNREACH;
+ rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
+ rt->rt6i_metric = rtmsg->rtmsg_metric;
+ rt->rt6i_dev = dev;
+ goto install_route;
+ }
+
if (rtmsg->rtmsg_flags & RTF_GATEWAY) {
struct in6_addr *gw_addr;
int gwa_type;
@@ -773,7 +788,7 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err)
}
dev = grt->rt6i_dev;
}
- if (dev == NULL) {
+ if (dev == NULL || (dev->flags&IFF_LOOPBACK)) {
*err = -EINVAL;
goto out;
}
@@ -805,6 +820,7 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err)
rt->rt6i_hoplimit = ipv6_get_hoplimit(dev);
rt->rt6i_flags = rtmsg->rtmsg_flags;
+install_route:
RDBG(("rt6ins(%p) ", rt));
rt6_lock();
@@ -1421,6 +1437,7 @@ int ipv6_route_ioctl(unsigned int cmd, void *arg)
int ip6_pkt_discard(struct sk_buff *skb)
{
ipv6_statistics.Ip6OutNoRoutes++;
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
kfree_skb(skb);
return 0;
}
@@ -1671,7 +1688,8 @@ static int inet6_rtm_to_rtmsg(struct rtmsg *r, struct rtattr **rta,
rtmsg->rtmsg_dst_len = r->rtm_dst_len;
rtmsg->rtmsg_src_len = r->rtm_src_len;
rtmsg->rtmsg_flags = RTF_UP;
- rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
+ if (r->rtm_type == RTN_UNREACHABLE)
+ rtmsg->rtmsg_flags |= RTF_REJECT;
if (rta[RTA_GATEWAY-1]) {
if (rta[RTA_GATEWAY-1]->rta_len != RTA_LENGTH(16))
@@ -1754,7 +1772,12 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
rtm->rtm_src_len = rt->rt6i_src.plen;
rtm->rtm_tos = 0;
rtm->rtm_table = RT_TABLE_MAIN;
- rtm->rtm_type = RTN_UNICAST;
+ if (rt->rt6i_flags&RTF_REJECT)
+ rtm->rtm_type = RTN_UNREACHABLE;
+ else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK))
+ rtm->rtm_type = RTN_LOCAL;
+ else
+ rtm->rtm_type = RTN_UNICAST;
rtm->rtm_flags = 0;
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
#ifdef CONFIG_RTNL_OLD_IFINFO
@@ -1795,6 +1818,8 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
if (rt->u.dst.rtt)
RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
mx->rta_len = skb->tail - (u8*)mx;
+ if (mx->rta_len == RTA_LENGTH(0))
+ skb_trim(skb, (u8*)mx - skb->data);
#endif
if (rt->u.dst.neighbour)
RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index f029942df..577b85d0f 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -6,7 +6,7 @@
* Pedro Roque <roque@di.fc.ul.pt>
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
*
- * $Id: sit.c,v 1.24 1997/12/13 21:53:17 kuznet Exp $
+ * $Id: sit.c,v 1.27 1998/03/08 05:56:57 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f7a080a0d..1d082c195 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: tcp_ipv6.c,v 1.44 1997/12/13 21:53:18 kuznet Exp $
+ * $Id: tcp_ipv6.c,v 1.60 1998/03/15 02:59:32 davem Exp $
*
* Based on:
* linux/net/ipv4/tcp.c
@@ -44,7 +44,6 @@
#define ICMP_PARANOIA
-extern int sysctl_tcp_sack;
extern int sysctl_tcp_timestamps;
extern int sysctl_tcp_window_scaling;
@@ -86,62 +85,69 @@ static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
/* Grrr, addr_type already calculated by caller, but I don't want
* to add some silly "cookie" argument to this method just for that.
+ * But it doesn't matter, the recalculation is in the rarest path
+ * this function ever takes.
*/
static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum)
{
- struct sock *sk2;
- int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
- int retval = 0, sk_reuse = sk->reuse;
+ struct tcp_bind_bucket *tb;
+ int result = 0;
SOCKHASH_LOCK();
- sk2 = tcp_bound_hash[tcp_sk_bhashfn(sk)];
- for(; sk2 != NULL; sk2 = sk2->bind_next) {
- if((sk2->num == snum) && (sk2 != sk)) {
- unsigned char state = sk2->state;
- int sk2_reuse = sk2->reuse;
- if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
- if((!sk2_reuse) ||
- (!sk_reuse) ||
- (state == TCP_LISTEN)) {
- retval = 1;
- break;
- }
- } else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
- &sk2->net_pinfo.af_inet6.rcv_saddr)) {
- if((!sk_reuse) ||
- (!sk2_reuse) ||
- (state == TCP_LISTEN)) {
- retval = 1;
- break;
+ for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+ (tb && (tb->port != snum));
+ tb = tb->next)
+ ;
+ if(tb && tb->owners) {
+ /* Fast path for reuse ports, see include/net/tcp.h for a very
+ * detailed description of why this works, and why it is worth
+ * the effort at all. -DaveM
+ */
+ if((tb->flags & TCPB_FLAG_FASTREUSE) &&
+ (sk->reuse != 0)) {
+ goto go_like_smoke;
+ } else {
+ struct sock *sk2;
+ int sk_reuse = sk->reuse;
+ int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+
+ /* We must walk the whole port owner list in this case. -DaveM */
+ for(sk2 = tb->owners; sk2; sk2 = sk2->bind_next) {
+ if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) {
+ if(addr_type == IPV6_ADDR_ANY ||
+ !sk2->rcv_saddr ||
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+ &sk2->net_pinfo.af_inet6.rcv_saddr))
+ break;
}
}
+ if(sk2 != NULL)
+ result = 1;
}
}
+ if((result == 0) &&
+ (tb == NULL) &&
+ (tcp_bucket_create(snum) == NULL))
+ result = 1;
+go_like_smoke:
SOCKHASH_UNLOCK();
-
- return retval;
+ return result;
}
static void tcp_v6_hash(struct sock *sk)
{
- unsigned char state;
-
- SOCKHASH_LOCK();
- state = sk->state;
- if(state != TCP_CLOSE) {
+ if(sk->state != TCP_CLOSE) {
struct sock **skp;
- if(state == TCP_LISTEN)
- skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
- else
- skp = &tcp_established_hash[tcp_v6_sk_hashfn(sk)];
+ SOCKHASH_LOCK();
+ skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
if((sk->next = *skp) != NULL)
(*skp)->pprev = &sk->next;
*skp = sk;
sk->pprev = skp;
tcp_sk_bindify(sk);
+ SOCKHASH_UNLOCK();
}
- SOCKHASH_UNLOCK();
}
static void tcp_v6_unhash(struct sock *sk)
@@ -153,6 +159,7 @@ static void tcp_v6_unhash(struct sock *sk)
*sk->pprev = sk->next;
sk->pprev = NULL;
tcp_sk_unbindify(sk);
+ tcp_reg_zap(sk);
}
SOCKHASH_UNLOCK();
}
@@ -163,29 +170,27 @@ static void tcp_v6_rehash(struct sock *sk)
SOCKHASH_LOCK();
state = sk->state;
- if(sk->pprev) {
+ if(sk->pprev != NULL) {
if(sk->next)
sk->next->pprev = sk->pprev;
*sk->pprev = sk->next;
sk->pprev = NULL;
- tcp_sk_unbindify(sk);
+ tcp_reg_zap(sk);
}
if(state != TCP_CLOSE) {
struct sock **skp;
- if(state == TCP_LISTEN) {
+ if(state == TCP_LISTEN)
skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
- } else {
- int hash = tcp_v6_sk_hashfn(sk);
- if(state == TCP_TIME_WAIT)
- hash += (TCP_HTABLE_SIZE/2);
- skp = &tcp_established_hash[hash];
- }
+ else
+ skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
+
if((sk->next = *skp) != NULL)
(*skp)->pprev = &sk->next;
*skp = sk;
sk->pprev = skp;
- tcp_sk_bindify(sk);
+ if(state == TCP_LISTEN)
+ tcp_sk_bindify(sk);
}
SOCKHASH_UNLOCK();
}
@@ -209,8 +214,12 @@ static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned shor
return result;
}
+/* Until this is verified... -DaveM */
+/* #define USE_QUICKSYNS */
+
/* 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
+ * It is assumed that this code only gets called from within NET_BH.
*/
static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
struct in6_addr *saddr, u16 sport,
@@ -218,30 +227,53 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
{
unsigned short hnum = ntohs(dport);
struct sock *sk;
- int hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
+ int hash;
+
+#ifdef USE_QUICKSYNS
+ /* Incomming connection short-cut. */
+ if (th && th->syn == 1 && th->ack == 0)
+ goto listener_shortcut;
+#endif
+
+ /* Check TCP register quick cache first. */
+ sk = TCP_RHASH(sport);
+ if(sk &&
+ sk->num == hnum && /* local port */
+ sk->family == AF_INET6 && /* address family */
+ sk->dummy_th.dest == sport && /* remote port */
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) &&
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+ goto hit;
/* Optimize here for direct hit, only listening connections can
- * have wildcards anyways. It is assumed that this code only
- * gets called from within NET_BH.
+ * have wildcards anyways.
*/
- for(sk = tcp_established_hash[hash]; sk; sk = sk->next)
+ hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
+ for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
/* For IPV6 do the cheaper port and family tests first. */
if(sk->num == hnum && /* local port */
sk->family == AF_INET6 && /* address family */
sk->dummy_th.dest == sport && /* remote port */
!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) &&
- !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr)) {
+ if (sk->state == TCP_ESTABLISHED)
+ TCP_RHASH(sport) = sk;
goto hit; /* You sunk my battleship! */
-
+ }
+ }
/* Must check for a TIME_WAIT'er before going to listener hash. */
for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
if(sk->num == hnum && /* local port */
sk->family == AF_INET6 && /* address family */
- sk->dummy_th.dest == sport && /* remote port */
- !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) &&
- !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
- goto hit;
-
+ sk->dummy_th.dest == sport) { /* remote port */
+ struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
+ if(!ipv6_addr_cmp(&tw->v6_daddr, saddr) &&
+ !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr))
+ goto hit;
+ }
+#ifdef USE_QUICKSYNS
+listener_shortcut:
+#endif
sk = tcp_v6_lookup_listener(daddr, hnum);
hit:
return sk;
@@ -275,6 +307,33 @@ static __u32 tcp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
skb->h.th->source);
}
+static int tcp_v6_unique_address(struct sock *sk)
+{
+ struct tcp_bind_bucket *tb;
+ unsigned short snum = sk->num;
+ int retval = 1;
+
+ /* Freeze the hash while we snoop around. */
+ SOCKHASH_LOCK();
+ tb = tcp_bound_hash[tcp_bhashfn(snum)];
+ for(; tb; tb = tb->next) {
+ if(tb->port == snum && tb->owners != NULL) {
+ /* Almost certainly the re-use port case, search the real hashes
+ * so it actually scales. (we hope that all ipv6 ftp servers will
+ * use passive ftp, I just cover this case for completeness)
+ */
+ sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr,
+ sk->dummy_th.dest,
+ &sk->net_pinfo.af_inet6.rcv_saddr, snum);
+ if((sk != NULL) && (sk->state != TCP_LISTEN))
+ retval = 0;
+ break;
+ }
+ }
+ SOCKHASH_UNLOCK();
+ return retval;
+}
+
static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{
@@ -390,7 +449,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
ipv6_addr_copy(&np->saddr, saddr);
}
- /* FIXME: Need to do tcp_v6_unique_address() here! -DaveM */
+ sk->dummy_th.dest = usin->sin6_port;
+ if (!tcp_v6_unique_address(sk))
+ return -EADDRNOTAVAIL;
/*
* Init variables
@@ -398,16 +459,15 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
lock_sock(sk);
- sk->dummy_th.dest = usin->sin6_port;
- sk->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3],
+ tp->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3],
np->daddr.s6_addr32[3],
sk->dummy_th.source,
sk->dummy_th.dest);
tp->snd_wnd = 0;
tp->snd_wl1 = 0;
- tp->snd_wl2 = sk->write_seq;
- tp->snd_una = sk->write_seq;
+ tp->snd_wl2 = tp->write_seq;
+ tp->snd_una = tp->write_seq;
tp->rcv_nxt = 0;
@@ -415,30 +475,35 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
release_sock(sk);
- buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL);
-
- if (buff == NULL)
+ buff = sock_wmalloc(sk, (MAX_SYN_SIZE + sizeof(struct sk_buff)),
+ 0, GFP_KERNEL);
+ if (buff == NULL) {
+ /* FIXME: Free route references etc??? */
return(-ENOMEM);
+ }
lock_sock(sk);
tcp_v6_build_header(sk, buff);
+ tp->tcp_header_len = sizeof(struct tcphdr) +
+ (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
+
/* build the tcp header */
th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
buff->h.th = th;
memcpy(th, (void *) &(sk->dummy_th), sizeof(*th));
- buff->seq = sk->write_seq++;
+ buff->seq = tp->write_seq++;
th->seq = htonl(buff->seq);
- tp->snd_nxt = sk->write_seq;
- buff->end_seq = sk->write_seq;
+ tp->snd_nxt = tp->write_seq;
+ buff->end_seq = tp->write_seq;
th->ack = 0;
th->syn = 1;
sk->mtu = dst->pmtu;
- sk->mss = sk->mtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
+ sk->mss = (sk->mtu - sizeof(struct ipv6hdr) - tp->tcp_header_len);
if (sk->mss < 1) {
printk(KERN_DEBUG "intial ipv6 sk->mss below 1\n");
@@ -457,8 +522,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
* Put in the TCP options to say MTU.
*/
- tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_sack,
- sysctl_tcp_timestamps,
+ tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_timestamps,
sysctl_tcp_window_scaling,tp->rcv_wscale);
th->doff = sizeof(*th)/4 + (tmp>>2);
buff->csum = 0;
@@ -467,9 +531,10 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
tcp_set_state(sk, TCP_SYN_SENT);
/* Socket identity change complete, no longer
- * in TCP_CLOSE, so rehash.
+ * in TCP_CLOSE, so enter ourselves into the
+ * hash tables.
*/
- sk->prot->rehash(sk);
+ sk->prot->hash(sk);
/* FIXME: should use dcache->rtt if availiable */
tp->rto = TCP_TIMEOUT_INIT;
@@ -482,12 +547,12 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
tp->packets_out++;
buff->when = jiffies;
skb1 = skb_clone(buff, GFP_KERNEL);
- skb_set_owner_w(skb1, sk);
-
- tcp_v6_xmit(skb1);
+ if(skb1 != NULL) {
+ skb_set_owner_w(skb1, sk);
+ tcp_v6_xmit(skb1);
+ }
/* Timer for repeating the SYN until an answer */
-
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
tcp_statistics.TcpActiveOpens++;
tcp_statistics.TcpOutSegs++;
@@ -499,6 +564,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
+ struct tcp_opt *tp;
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
int retval = -EINVAL;
@@ -530,7 +596,10 @@ static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
lock_sock(sk);
retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov,
msg->msg_flags);
-
+ /* Push out partial tail frames if needed. */
+ tp = &(sk->tp_pinfo.af_tcp);
+ if(tp->send_head && tcp_snd_test(sk, tp->send_head))
+ tcp_write_xmit(sk);
release_sock(sk);
out:
@@ -555,7 +624,7 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info,
sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source);
- if (sk == NULL) {
+ if (sk == NULL || sk->state == TCP_TIME_WAIT) {
/* XXX: Update ICMP error count */
return;
}
@@ -596,11 +665,14 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info,
ip6_dst_store(sk, dst);
}
- if (sk->dst_cache->error)
+ if (sk->dst_cache->error) {
sk->err_soft = sk->dst_cache->error;
- else
+ } else {
+ /* FIXME: Reset sk->mss, taking into account TCP option
+ * bytes for timestamps. -DaveM
+ */
sk->mtu = sk->dst_cache->pmtu;
-
+ }
if (sk->sock_readers) { /* remove later */
printk(KERN_DEBUG "tcp_v6_err: pmtu disc: socket locked.\n");
return;
@@ -713,11 +785,10 @@ static void tcp_v6_send_synack(struct sock *sk, struct open_request *req)
* match what happens under IPV4. Figure out the right thing to do.
*/
req->mss = min(sk->mss, req->mss);
-
- if (req->mss < 1) {
- printk(KERN_DEBUG "initial req->mss below 1\n");
- req->mss = 1;
- }
+ if(sk->user_mss)
+ req->mss = min(req->mss, sk->user_mss);
+ if(req->tstamp_ok == 0)
+ req->mss += TCPOLEN_TSTAMP_ALIGNED;
if (req->rcv_wnd == 0) {
__u8 rcv_wscale;
@@ -732,7 +803,7 @@ static void tcp_v6_send_synack(struct sock *sk, struct open_request *req)
}
th->window = htons(req->rcv_wnd);
- tmp = tcp_syn_build_options(skb, req->mss, req->sack_ok, req->tstamp_ok,
+ tmp = tcp_syn_build_options(skb, req->mss, req->tstamp_ok,
req->wscale_ok,req->rcv_wscale);
skb->csum = 0;
th->doff = (sizeof(*th) + tmp)>>2;
@@ -740,9 +811,13 @@ static void tcp_v6_send_synack(struct sock *sk, struct open_request *req)
&req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr,
csum_partial((char *)th, sizeof(*th)+tmp, skb->csum));
+ /* Actually we should not attach dst to socket in state LISTEN,
+ it results in stale destination per listen socket and
+ overflow of routing cache.
+ (IPv4 has the same flaw with more unpleasant consequences.)
+ */
ip6_dst_store(sk, dst);
ip6_xmit(sk, skb, &fl, req->af.v6_req.opt);
- dst_release(dst);
tcp_statistics.TcpOutSegs++;
}
@@ -801,14 +876,15 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
req->rcv_isn = skb->seq;
req->snt_isn = isn;
- tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
+ tp.tstamp_ok = tp.wscale_ok = tp.snd_wscale = 0;
tp.in_mss = 536;
tcp_parse_options(skb->h.th,&tp,0);
- if (tp.saw_tstamp)
- req->ts_recent = tp.rcv_tsval;
req->mss = tp.in_mss;
+ if (tp.saw_tstamp) {
+ req->mss -= TCPOLEN_TSTAMP_ALIGNED;
+ req->ts_recent = tp.rcv_tsval;
+ }
req->tstamp_ok = tp.tstamp_ok;
- req->sack_ok = tp.sack_ok;
req->snd_wscale = tp.snd_wscale;
req->wscale_ok = tp.wscale_ok;
req->rmt_port = skb->h.th->source;
@@ -879,92 +955,17 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
return newsk;
}
- newsk = sk_alloc(AF_INET6, GFP_ATOMIC);
+ newsk = tcp_create_openreq_child(sk, req, skb);
if (newsk == NULL) {
- if (dst)
- dst_release(dst);
+ dst_release(dst);
return NULL;
}
- memcpy(newsk, sk, sizeof(*newsk));
-
- /* Or else we die! -DaveM */
- newsk->sklist_next = NULL;
-
- newsk->opt = NULL;
newsk->dst_cache = NULL;
- skb_queue_head_init(&newsk->write_queue);
- skb_queue_head_init(&newsk->receive_queue);
- skb_queue_head_init(&newsk->out_of_order_queue);
- skb_queue_head_init(&newsk->error_queue);
-
- /*
- * Unused
- */
newtp = &(newsk->tp_pinfo.af_tcp);
- np = &newsk->net_pinfo.af_inet6;
-
- newtp->send_head = NULL;
- newtp->retrans_head = NULL;
-
- newtp->pending = 0;
-
- skb_queue_head_init(&newsk->back_log);
-
- newsk->prot->init(newsk);
-
- newtp->snd_cwnd_cnt = 0;
-#if 0 /* Don't mess up the initialization we did in the init routine! */
- newtp->snd_ssthresh = 0;
-#endif
- newtp->backoff = 0;
- newsk->proc = 0;
- newsk->done = 0;
- newsk->pair = NULL;
- atomic_set(&newsk->wmem_alloc, 0);
- atomic_set(&newsk->rmem_alloc, 0);
- newsk->localroute = sk->localroute;
-
- newsk->err = 0;
- newsk->shutdown = 0;
- newsk->ack_backlog = 0;
-
- newtp->fin_seq = req->rcv_isn;
- newsk->syn_seq = req->rcv_isn;
- newsk->state = TCP_SYN_RECV;
- newsk->timeout = 0;
-
- newsk->write_seq = req->snt_isn;
-
- newtp->snd_wnd = ntohs(skb->h.th->window);
- newtp->max_window = newtp->snd_wnd;
- newtp->snd_wl1 = req->rcv_isn;
- newtp->snd_wl2 = newsk->write_seq;
- newtp->snd_una = newsk->write_seq++;
- newtp->snd_nxt = newsk->write_seq;
-
- newsk->urg_data = 0;
- newtp->packets_out = 0;
- newtp->retransmits = 0;
- newsk->linger=0;
- newsk->destroy = 0;
- init_timer(&newsk->timer);
- newsk->timer.data = (unsigned long) newsk;
- newsk->timer.function = &net_timer;
-
- tcp_init_xmit_timers(newsk);
-
- newsk->dummy_th.source = sk->dummy_th.source;
- newsk->dummy_th.dest = req->rmt_port;
- newsk->sock_readers=0;
-
- newtp->rcv_nxt = req->rcv_isn + 1;
- newtp->rcv_wup = req->rcv_isn + 1;
- newsk->copied_seq = req->rcv_isn + 1;
-
- newsk->socket = NULL;
+ np = &newsk->net_pinfo.af_inet6;
ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr);
ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr);
ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr);
@@ -987,14 +988,22 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
ip6_dst_store(newsk, dst);
- newtp->sack_ok = req->sack_ok;
newtp->tstamp_ok = req->tstamp_ok;
- newtp->snd_wscale = req->snd_wscale;
+ newtp->window_clamp = req->window_clamp;
+ newtp->rcv_wnd = req->rcv_wnd;
newtp->wscale_ok = req->wscale_ok;
- newtp->ts_recent = req->ts_recent;
+ if (newtp->wscale_ok) {
+ newtp->snd_wscale = req->snd_wscale;
+ newtp->rcv_wscale = req->rcv_wscale;
+ } else {
+ newtp->snd_wscale = newtp->rcv_wscale = 0;
+ newtp->window_clamp = min(newtp->window_clamp,65535);
+ }
if (newtp->tstamp_ok) {
- newtp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: define the contant. */
- newsk->dummy_th.doff += 3;
+ newtp->ts_recent = req->ts_recent;
+ newtp->ts_recent_stamp = jiffies;
+ newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
+ newsk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2);
} else {
newtp->tcp_header_len = sizeof(struct tcphdr);
}
@@ -1006,7 +1015,6 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->mss = min(req->mss+sizeof(struct tcphdr)-newtp->tcp_header_len,
(newsk->mtu - sizeof(struct ipv6hdr) - newtp->tcp_header_len));
- /* XXX tp->window_clamp??? -DaveM */
newsk->daddr = LOOPBACK4_IPV6;
newsk->saddr = LOOPBACK4_IPV6;
@@ -1181,12 +1189,14 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
goto no_tcp_socket;
}
- skb->sk = sk;
skb->seq = ntohl(th->seq);
skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
skb->ack_seq = ntohl(th->ack_seq);
-
skb->used = 0;
+ if(sk->state == TCP_TIME_WAIT)
+ goto do_time_wait;
+
+ skb->sk = sk;
}
/*
@@ -1249,6 +1259,12 @@ discard_it:
kfree_skb(skb);
return 0;
+
+do_time_wait:
+ if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
+ skb, th, &(IPCB(skb)->opt), skb->len))
+ goto no_tcp_socket;
+ goto discard_it;
}
static int tcp_v6_rebuild_header(struct sock *sk, struct sk_buff *skb)
@@ -1384,51 +1400,34 @@ static struct tcp_func ipv6_mapped = {
sizeof(struct sockaddr_in6)
};
+/* NOTE: A lot of things set to zero explicitly by call to
+ * sk_alloc() so need not be done here.
+ */
static int tcp_v6_init_sock(struct sock *sk)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- skb_queue_head_init(&sk->out_of_order_queue);
+ skb_queue_head_init(&tp->out_of_order_queue);
tcp_init_xmit_timers(sk);
- tp->srtt = 0;
tp->rto = TCP_TIMEOUT_INIT; /*TCP_WRITE_TIME*/
tp->mdev = TCP_TIMEOUT_INIT;
-
- tp->ato = 0;
- tp->iat = (HZ/5) << 3;
-
- /* FIXME: right thing? */
- tp->rcv_wnd = 0;
tp->in_mss = 536;
- /* tp->rcv_wnd = 8192; */
- tp->tstamp_ok = 0;
- tp->sack_ok = 0;
- tp->wscale_ok = 0;
- tp->snd_wscale = 0;
- tp->sacks = 0;
- tp->saw_tstamp = 0;
- tp->syn_backlog = 0;
-
- /* start with only sending one packet at a time. */
+
+ /* See draft-stevens-tcpca-spec-01 for discussion of the
+ * initialization of these values.
+ */
tp->snd_cwnd = 1;
tp->snd_ssthresh = 0x7fffffff;
-
-
sk->priority = 1;
sk->state = TCP_CLOSE;
-
sk->max_ack_backlog = SOMAXCONN;
-
sk->mtu = 576;
sk->mss = 536;
-
sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
- /*
- * Speed up by setting some standard state for the dummy_th.
- */
+ /* Speed up by setting some standard state for the dummy_th. */
sk->dummy_th.ack=1;
sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
@@ -1442,6 +1441,7 @@ static int tcp_v6_init_sock(struct sock *sk)
static int tcp_v6_destroy_sock(struct sock *sk)
{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
struct sk_buff *skb;
tcp_clear_xmit_timers(sk);
@@ -1460,15 +1460,22 @@ static int tcp_v6_destroy_sock(struct sock *sk)
* Cleans up our, hopefuly empty, out_of_order_queue
*/
- while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL)
+ while((skb = skb_dequeue(&tp->out_of_order_queue)) != NULL)
kfree_skb(skb);
/*
* Release destination entry
*/
- dst_release(sk->dst_cache);
- sk->dst_cache = NULL;
+ dst_release(xchg(&sk->dst_cache,NULL));
+
+ /* Clean up a locked TCP bind bucket, this only happens if a
+ * port is allocated for a socket, but it never fully connects.
+ * In which case we will find num to be non-zero and daddr to
+ * be zero.
+ */
+ if(ipv6_addr_any(&(sk->net_pinfo.af_inet6.daddr)) && sk->num != 0)
+ tcp_bucket_unlock(sk);
return 0;
}
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index b99dc19e3..40e9b0233 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -7,7 +7,7 @@
*
* Based on linux/ipv4/udp.c
*
- * $Id: udp.c,v 1.21 1997/12/29 19:52:52 kuznet Exp $
+ * $Id: udp.c,v 1.24 1998/03/12 03:20:21 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -448,32 +448,43 @@ static struct sock *udp_v6_mcast_next(struct sock *sk,
return NULL;
}
+/*
+ * Note: called only from the BH handler context,
+ * so we don't need to lock the hashes.
+ */
static void udpv6_mcast_deliver(struct udphdr *uh,
struct in6_addr *saddr, struct in6_addr *daddr,
struct sk_buff *skb)
{
struct sock *sk, *sk2;
+ struct sk_buff *buff;
- SOCKHASH_LOCK();
sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
- if(sk) {
- sk2 = sk;
- while((sk2 = udp_v6_mcast_next(sk2->next,
- uh->dest, saddr,
- uh->source, daddr))) {
- struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
- if (buff && sock_queue_rcv_skb(sk2, buff) < 0) {
- buff->sk = NULL;
- kfree_skb(buff);
- }
+ if (!sk)
+ goto free_skb;
+
+ buff = NULL;
+ sk2 = sk;
+ while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, saddr,
+ uh->source, daddr))) {
+ if (!buff) {
+ buff = skb_clone(skb, GFP_ATOMIC);
+ if (!buff)
+ continue;
}
+ if (sock_queue_rcv_skb(sk2, buff) >= 0)
+ buff = NULL;
+ }
+ if (buff) {
+ buff->sk = NULL;
+ kfree_skb(buff);
}
- if(!sk || sock_queue_rcv_skb(sk, skb) < 0) {
+ if (sock_queue_rcv_skb(sk, skb) < 0) {
+ free_skb:
skb->sk = NULL;
kfree_skb(skb);
}
- SOCKHASH_UNLOCK();
}
int udpv6_rcv(struct sk_buff *skb, struct device *dev,