diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 423 |
1 files changed, 215 insertions, 208 deletions
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; } |