diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/addrconf.c | 4 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 7 | ||||
-rw-r--r-- | net/ipv6/exthdrs.c | 2 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_fib.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_fw.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_input.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 2 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 2 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 2 | ||||
-rw-r--r-- | net/ipv6/proc.c | 59 | ||||
-rw-r--r-- | net/ipv6/raw.c | 2 | ||||
-rw-r--r-- | net/ipv6/reassembly.c | 2 | ||||
-rw-r--r-- | net/ipv6/route.c | 43 | ||||
-rw-r--r-- | net/ipv6/sit.c | 2 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 423 | ||||
-rw-r--r-- | net/ipv6/udp.c | 39 |
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, |