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