/*------------------------------------------------------------------ 
* 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) || 
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); 
}