Wednesday, May 19, 2004

Cisco's IOS Source Code (ipv6_tcp.c)

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

No comments: