Code:
/*------------------------------------------------------------------ 
* ipv6_tcp.c -- IP version 6 support functions for TCP 
* 
* June 1996, Kirk Lougheed 
* 
* This should be the only file in the TCP sources that 
* explicitly references an IPv6 header or IPv6 addresses. 
* 
* Copyright (c) 1996-2003 by cisco Systems, Inc. 
* All rights reserved. 
*------------------------------------------------------------------ 
*/ 

#include "master.h" 
#include "address.h" 
#include "packet.h" 
#include "interface_private.h" 
#include "../ipv6/ipv6.h" 
#include "../ipv6/ipv6_private.h" 
#include "../ipv6/ipv6_stats.h" 
#include "../ipv6/ipv6_idb.h" 
#include "icmp6.h" 
#include "ipv6_debug.h" 
#include "../tcp/tcp.h" 
#include "../tcp/tcpinternal.h" 
#include "../tcp/tcp_debug.h" 


/* 
* ipv6_receive_tcp_header 
* 
* Pass a TCP segment to the main TCP layer. 
* We checksum it and set up private pointers. 
*/ 
void 
ipv6_receive_tcp_header (paktype *pak) 
{ 
ip6_hdr_t *ip; 
tcptype *tcp; 
ipv6addr_listtype *ifa; 

/* 
* Checksum the TCP packet. 
* Errors are counted by the IPv4 code. 
*/ 
ip = (ip6_hdr_t *)pak->network_start; 
tcp = (tcptype *)pak->transport_start; 

/* 
* Don't accept TCP connections to multicast or anycast addresses 
*/ 
ifa = ipv6_address_find(pak->if_input, &ip->ip6_dst); 
if ((ifa && (ifa->flags & IPV6_IFF_ANYCAST)) || 

IN6_IS_ADDR_MULTICAST(&ip->ip6_dst)) { 
if (tcp_debug) 

buginf("\nTCP: anycast or multicast address <%P,%d> <%P,%d>", 


&ip->ip6_src, tcp->sourceport, 

&ip->ip6_dst, tcp->destinationport); 

retbuffer(pak); 

return; 
} 

if (ipv6_checksum(ip, tcp, IPPROTO_TCP)) { 
if (tcp_debug) 
buginf("\nTCP: checksum failure <%P,%d> <%P,%d>", 


&ip->ip6_src, tcp->sourceport, 

&ip->ip6_dst, tcp->destinationport); 

tcp_traffic.checksumerr++; 

ipv6_stats_update_idb(IPV6_STATS_TCP_CHECKSUM_ERROR, pak->if_input); 

retbuffer(pak); 

return; 
} 

/* 
* Setup TCP's private pointers 
* pak->length is total length less IP header bytes (including extensions) 
*/ 
pak->dataptr = (uchar *)tcp; 
pak->length = ip->ip6_plen + IPV6_HEADERBYTES - ((int)tcp - (int)ip); 

/* 
* Hand TCP segment off to TCP 
*/ 
ipv6_stats_update_idb(IPV6_STATS_TCP_INPUT, pak->if_input); 

if (gettcpack(tcp)) { 
ipv6_reachable(&ip->ip6_src); 
} 
tcp_inputsegment(pak); 
} 


/* 
* ipv6_tcp_write 
* Write a TCP datagram to network using IPv6 network layer. 
* 
* Warning: the code that sends a RST about connectionless packets 
* calls us with a NULL tcb pointer. 
*/ 
void 
ipv6_tcp_write (tcbtype * tcb, paktype *pak, 


addrtype *destination, addrtype *source) 
{ 
ip6_hdr_t *ip; 
tcptype *tcp; 
int bytes; 

/* 
* Put on standard IPV6 header. 
* Figure out extension headers some other time. 
*/ 
ip = (ip6_hdr_t *)pak->network_start; 
ipv6_init_header(ip, IPV6_PRIORITY_INTERACTIVE, IPV6_DEFAULT_FLOW, 


pak->length - IPV6_HEADERBYTES, IPPROTO_TCP, 


ipv6_hop_limit, 

(in6_addr_t *)&source->ipv6_addr, 


(in6_addr_t *)&destination->ipv6_addr); 

/* 
* Compute TCP header checksum 
*/ 
tcp = (tcptype *)pak->transport_start; 
tcp->checksum = 0; 
tcp->checksum = ipv6_checksum(ip, tcp, IPPROTO_TCP); 

/* 
* Do connection based accounting and fiddling. 
*/ 
if (tcb) { 
/* 

* Do some bean counting 
*/ 

if (pak->us_retransno != 0) { 
tcp_traffic.retrans++; 

tcb->pakoutretrans++; 
ipv6_stats_update_idb(IPV6_STATS_TCP_RETRANSMITTED, 




pak->if_output); 
if (tcppkt_debug) 


tcp_print(tcb, pak, tcp, 'R'); 
} else { 
bytes = pak->length - tcp_ipbytes(tcb) + (tcp->dataoffset << 2); 
if (bytes > 0) { 

tcb->bytesoutcount += bytes; 

tcb->pakoutdata++; 

} 
tcb->pakoutcount++; 

if (tcppkt_debug) 

tcp_print(tcb, pak, tcp, 'O'); 

} 

/* 

* We're sending an ACK, so stop the ACK timer 

*/ 
tcp_stoptimer(tcb, ACKTIMEOUT); 
} 

/* 
* Write datagram to network. 
* Routing is done by IPv6. 
*/ 
pak->if_output = NULL; 
ipv6_write(pak, IPV6_STATS_TCP_OUTPUT); 
} 


/* 
* ipv6_tcp_icmp_received 
* 
* We received an ICMP error message about some datagram. 
* Check if it is for a TCP packet of ours belonging to an 
* active socket. Return TRUE if we absorbed it. 
*/ 
boolean 
ipv6_tcp_icmp_received (paktype *pak, ip6_hdr_t *ip, icmp6_hdr_t *icmp) 
{ 
tcptype *tcp; 
tcbtype *tcb; 
addrtype address; 

/* 
* We are looking for a returned TCP packet inside this ICMP message. 
*/ 
tcp = ipv6_icmp_find_ulp(pak, NULL, IPPROTO_TCP); 
if (!tcp) { 
return (FALSE); 
} 

/* 
* Check that enough of the originating packet was returned 
* that we can correctly examine the tcp sport and dport. 
* We need everything up to (but not including) the "window" field. 
*/ 
if (ip->ip6_plen + IPV6_HEADERBYTES - 

((char *)tcp - (char *)pak->network_start) 

< FIELDOFFSET(tcptype, window)) { 

ICMPV6_DEBUG("Not enough data returned - dropping packet"); 
return (FALSE); 
} 

/* 
* Find a matching TCB. 
*/ 
ipv6_addrtype_create(&ip->ip6_src, &address); 
tcb = find_tcb(GETSHORT(&tcp->destinationport), GETSHORT(&tcp->sourceport), 


&address, NULL, FALSE, FALSE, 0); 
if (tcb) { 
if (icmp->icmp6_type == ICMP6_PACKET_TOO_BIG) { 
tcp_pmtu_response(tcb, icmp->icmp6_mtu); 

} else if (tcb->state != ESTAB && tcb->state != CLOSEWAIT) { 
/* 
* Throw away TCBs that applications either don't know 
* about or don't care anymore. 
*/ 
if ((tcb->flags & TCB_GENTCBS && tcb->state == SYNRCVD) &#0124;&#0124; 
tcb->flags & TCB_APP_CLOSED) { 
tcp_async_cleanup(tcb, UNREACHABLE); 
} else { 
/* 
* Application is expected to close the connection 
* upon notification of the state change. 
*/ 
tcp_deallocatetcb(tcb, UNREACHABLE); 
} 
} 
} 
retbuffer(pak); 
return (TRUE); 
}