From: John Hodge Date: Wed, 19 Mar 2014 14:11:30 +0000 (+0800) Subject: Modules/IPStack - Add ICMPv6 (not tested), fix TCP packet caching X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=3bd7834dc2892fae3a24782a4f150629140574d2;p=tpg%2Facess2.git Modules/IPStack - Add ICMPv6 (not tested), fix TCP packet caching --- diff --git a/KernelLand/Modules/IPStack/Makefile b/KernelLand/Modules/IPStack/Makefile index 0d8f5821..ca0b9aa1 100644 --- a/KernelLand/Modules/IPStack/Makefile +++ b/KernelLand/Modules/IPStack/Makefile @@ -5,7 +5,7 @@ OBJ := main.o interface.o adapters.o OBJ += buffer.o OBJ += link.o hwaddr_cache.o OBJ += ipv4.o icmp.o arp.o -OBJ += ipv6.o +OBJ += ipv6.o icmpv6.o OBJ += firewall.o routing.o OBJ += udp.o tcp.o NAME := IPStack diff --git a/KernelLand/Modules/IPStack/icmpv6.c b/KernelLand/Modules/IPStack/icmpv6.c new file mode 100644 index 00000000..571323ed --- /dev/null +++ b/KernelLand/Modules/IPStack/icmpv6.c @@ -0,0 +1,147 @@ +/* + * Acess2 IP Stack + * + * icmpv6.c + * - Internet Control Message Protocol v6 + */ +#include "ipstack.h" +#include "ipv6.h" +#include "ipv4.h" // For the IP Checksum +#include "icmpv6.h" +#include "link.h" +#include "hwaddr_cache.h" +#include "include/adapters_int.h" + +// === GLOBALS === +void ICMPv6_GetPacket(tInterface *Interface, void *Address, int Length, const void *Buffer); + int ICMPv6_ND_GetOpt(size_t Length, const void *Buffer, Uint8 OptID, const void **Ptr); + +// === CODE === +void ICMPv6_Initialise(void) +{ + +} + +void ICMPv6_GetPacket(tInterface *Interface, void *Address, int Length, const void *Buffer) +{ + if( Length < sizeof(tICMPv6_Header)) + return ; + const tICMPv6_Header *hdr = Buffer; + + // Validate checksum + if( IPv4_Checksum(Buffer, Length) != 0 ) + return ; + + if( hdr->Type == ICMPV6_INFO_NEIGHBOUR_ADVERTISMENT ) + { + if( Length < sizeof(tICMPv6_Header) + sizeof(tICMPv6_NA) ) + return ; + // TODO: Check that IP.HopLimt == 255 + const tICMPv6_NA *na = (const void*)(hdr+1); + if( !(na->Flags & (1 << 1)) ) { + // Unsolicited, TODO + return ; + } + if( na->Flags & (1 << 2) ) { + // Override, force update + } + const tICMPv6_Opt_LinkAddr *la = NULL; + if( ICMPv6_ND_GetOpt(Length-sizeof(*hdr)+sizeof(*na), na+1, ICMPV6_OPTION_SRCLINK, (const void**)&la) ) + return ; + if( !la ) { + LOG("No link address on neighbor advertisement"); + return; + } + + HWCache_Set(Interface->Adapter, 6, Address, (const tMacAddr*)la->Address); + } + else { + // Return unhandled? + } + + switch(hdr->Type) + { + case ICMPV6_INFO_ROUTER_SOLICITATION: + // TODO: If routing is active, send out RA + break; + case ICMPV6_INFO_ROUTER_ADVERTISEMENT: { + // TODO: If routing INACTIVE, and interface is ::/0, set address + break; } + case ICMPV6_INFO_NEIGHBOUR_SOLICITATION: + if( Length < sizeof(tICMPv6_Header) + sizeof(tICMPv6_NS) ) + return ; + // TODO: Check that IP.HopLimt == 255 + break; + case ICMPV6_INFO_NEIGHBOUR_ADVERTISMENT: + //HWCache_Set(Interface->Adapter, + break; + } +} + +int ICMPv6_ND_GetOpt(size_t Length, const void *Buffer, Uint8 OptID, const void **Ptr) +{ + const struct { + Uint8 Type; + Uint8 Length; + Uint16 _resvd1; + Uint32 _resvd2; + } *opts = Buffer; + + while( Length >= sizeof(*opts)) + { + if( opts->Length == 0 ) + return 1; + if( opts->Length * 8 > Length ) + return 1; + + if( opts->Type == OptID ) { + *Ptr = opts; + return 0; + } + opts += opts->Length; + } + return 0; +} + +void ICMPv6_SendNS(tInterface *Interface, const void *Address) +{ + const Uint8 *addr8 = Address; + struct { + tIPv6Header ip; + tICMPv6_Header icmp; + tICMPv6_NS ns; + tICMPv6_Opt_LinkAddr linkaddr; + Uint8 our_mac[6]; + } PACKED pkt; + // Custom-crafted IPv6 header + pkt.ip.Version = 6; + pkt.ip.TrafficClass = 0; + pkt.ip.FlowLabel = 0; + pkt.ip.Head = htonl(pkt.ip.Head); + pkt.ip.PayloadLength = htons(sizeof(pkt)-sizeof(pkt.ip)); + pkt.ip.NextHeader = IPV6PROT_ICMPV6; + pkt.ip.HopLimit = 255; // Max value + pkt.ip.Source = *(tIPv6*)Interface->Address; + pkt.ip.Destination = (tIPv6){.B={0xFF,0x02, 0,0, 0,0, 0,0, 0,0, 0,1,0xFF,addr8[13], addr8[14],addr8[15]}}; + + pkt.icmp.Type = ICMPV6_INFO_NEIGHBOUR_SOLICITATION; + pkt.icmp.Code = 0; + pkt.icmp.Checksum = 0; // populated later + + pkt.ns.Reserved = 0; + pkt.ns.TargetAddress = *(const tIPv6*)Address; + + pkt.linkaddr.Type = ICMPV6_OPTION_SRCLINK; + pkt.linkaddr.Length = 1; // 1 * 8 bytes + memcpy(pkt.our_mac, Interface->Adapter->HWAddr, 6); + + pkt.icmp.Checksum = IPv4_Checksum(&pkt, sizeof(pkt)); + + + tMacAddr to = {.B={0x33, 0x33, addr8[12], addr8[13], addr8[14], addr8[15]}}; + + tIPStackBuffer *buffer = IPStack_Buffer_CreateBuffer(2); + IPStack_Buffer_AppendSubBuffer(buffer, sizeof(pkt), 0, &pkt, NULL, NULL); + Link_SendPacket(Interface->Adapter, IPV6_ETHERNET_ID, to, buffer); + IPStack_Buffer_DestroyBuffer(buffer); +} diff --git a/KernelLand/Modules/IPStack/icmpv6.h b/KernelLand/Modules/IPStack/icmpv6.h new file mode 100644 index 00000000..bda70b13 --- /dev/null +++ b/KernelLand/Modules/IPStack/icmpv6.h @@ -0,0 +1,109 @@ +/* + * Acess2 IP Stack + * - By John Hodge (thePowersGang) + * + * icmpv6.c + * - Internet Control Message Protocol v6 + */ +#ifndef _ICMPV6_H_ +#define _ICMPV6_H_ + +#define IPV6PROT_ICMPV6 58 + +typedef struct { + Uint8 Type; + Uint8 Code; + Uint16 Checksum; +} PACKED tICMPv6_Header; + +typedef struct { + Uint32 Reserved; +} PACKED tICMPv6_RS; + +typedef struct { + Uint8 CurHopLimit; + Uint8 Flags; // M:O:6 + Uint16 RouterLifetime; // Seconds, life time as a default router (wtf does that mean?) + Uint32 ReachableTime; + Uint32 RetransTimer; // Miliseconds, time between transmissions of RAs from this router + Uint8 Options[]; +} PACKED tICMPv6_RA; + +typedef struct { + Uint32 Reserved; + tIPv6 TargetAddress; + Uint8 Options[]; +} PACKED tICMPv6_NS; + +typedef struct { + Uint32 Flags; + tIPv6 TargetAddress; + Uint8 Options[]; +} PACKED tICMPv6_NA; + +typedef struct { + Uint32 Reserved; + tIPv6 TargetAddress; + tIPv6 DestinationAddress; + Uint8 Options[]; +} PACKED tICMPv6_Redirect; + +typedef struct { + Uint8 Type; // 1,2 + Uint8 Length; // Length of field in units of 8 bytes (incl header), typically 1 + Uint8 Address[]; +} PACKED tICMPv6_Opt_LinkAddr; + +typedef struct { + Uint8 Type; // 3 + Uint8 Length; // Length of field in units of 8 bytes (incl header), typically + Uint8 PrefixLength; + Uint8 Flags; // L:A:6 - + Uint32 ValidLifetime; + Uint32 PreferredLifetime; + Uint32 _reserved2; + tIPv6 Prefix[]; +} PACKED tICMPv6_Opt_Prefix; + +typedef struct { + Uint8 Type; // 4 + Uint8 Length; + Uint16 _rsvd1; + Uint32 _rsvd2; + Uint8 Data[]; // All or part of the redirected message (not exceeding MTU) +} PACKED tICMPv6_Opt_Redirect; + +typedef struct { + Uint8 Type; // 4 + Uint8 Length; + Uint16 _resvd1; + Uint32 MTU; +} PACKED tICMPv6_Opt_MTU; + +enum { + ICMPV6_ERR_UNREACHABLE, + ICMPV6_ERR_PACKET_TOO_BIG, + ICMPV6_ERR_TIME_EXCEEDED, + ICMPV6_ERR_PARAMETER_PROBLEM, +}; +enum { + ICMPV6_INFO_ECHO_REQUEST = 128, + ICMPV6_INFO_ECHO_REPLY, + + ICMPV6_INFO_ROUTER_SOLICITATION = 133, + ICMPV6_INFO_ROUTER_ADVERTISEMENT, + ICMPV6_INFO_NEIGHBOUR_SOLICITATION, + ICMPV6_INFO_NEIGHBOUR_ADVERTISMENT, + ICMPV6_INFO_REDIRECT, +}; + +enum { + ICMPV6_OPTION_SRCLINK = 1, + ICMPV6_OPTION_TGTLINK, + ICMPV6_OPTION_PREFIX, + ICMPV6_OPTION_REDIRECTED_HDR, + ICMPV6_OPTION_MTU, +}; + +#endif + diff --git a/KernelLand/Modules/IPStack/ipv6.h b/KernelLand/Modules/IPStack/ipv6.h index ee17c01a..35cd7422 100644 --- a/KernelLand/Modules/IPStack/ipv6.h +++ b/KernelLand/Modules/IPStack/ipv6.h @@ -6,6 +6,7 @@ #define _IPV6_H_ #include "ipstack.h" +#include "include/buffer.h" typedef struct sIPv6Header tIPv6Header; diff --git a/KernelLand/Modules/IPStack/tcp.c b/KernelLand/Modules/IPStack/tcp.c index 8f16da84..ac58ec37 100644 --- a/KernelLand/Modules/IPStack/tcp.c +++ b/KernelLand/Modules/IPStack/tcp.c @@ -2,7 +2,7 @@ * Acess2 IP Stack * - TCP Handling */ -#define DEBUG 0 +#define DEBUG 1 #include "ipstack.h" #include "ipv4.h" #include "ipv6.h" @@ -52,6 +52,7 @@ size_t TCP_Client_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void void TCP_Client_Close(tVFS_Node *Node); // --- Helpers int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue); +Uint32 GetRelative(Uint32 Base, Uint32 Value); // === TEMPLATES === tSocketFile gTCP_ServerFile = {NULL, "tcps", TCP_Server_Init}; @@ -119,7 +120,7 @@ void TCP_int_SendPacket(tInterface *Interface, const void *Dest, tTCPHeader *Hea LOG("Sending %i+%i to %s:%i", sizeof(*Header), Length, IPStack_PrintAddress(Interface->Type, Dest), - ntohs(Header->RemotePort) + ntohs(Header->DestPort) ); Header->Checksum = 0; @@ -289,6 +290,7 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1; conn->HighestSequenceRcvd = conn->NextSequenceRcv; conn->NextSequenceSend = rand(); + conn->LastACKSequence = ntohl( hdr->SequenceNumber ); conn->Node.ImplInt = srv->NextID ++; @@ -516,7 +518,9 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head #if 1 // - Only send an ACK if we've had a burst - if( Connection->NextSequenceRcv > (Uint32)(TCP_DACK_THRESHOLD + Connection->LastACKSequence) ) + Uint32 bytes_since_last_ack = Connection->NextSequenceRcv - Connection->LastACKSequence; + LOG("bytes_since_last_ack = 0x%x", bytes_since_last_ack); + if( bytes_since_last_ack > TCP_DACK_THRESHOLD ) { TCP_INT_SendACK(Connection, "DACK Burst"); // - Extend TCP deferred ACK timer @@ -529,14 +533,13 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head #endif } // Check if the packet is in window - else if( WrapBetween(Connection->NextSequenceRcv, sequence_num, - Connection->NextSequenceRcv+TCP_WINDOW_SIZE, 0xFFFFFFFF) ) + else if( sequence_num - Connection->NextSequenceRcv < TCP_WINDOW_SIZE ) { Uint8 *dataptr = (Uint8*)Header + (Header->DataOffset>>4)*4; - #if CACHE_FUTURE_PACKETS_IN_BYTES - Uint32 index; - - index = sequence_num % TCP_WINDOW_SIZE; + Uint32 index = sequence_num % TCP_WINDOW_SIZE; + Uint32 max = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; + if( !(Connection->FuturePacketValidBytes[index/8] & (1 << (index%8))) ) + TCP_INT_SendACK(Connection, "Lost packet"); for( int i = 0; i < dataLen; i ++ ) { Connection->FuturePacketValidBytes[index/8] |= 1 << (index%8); @@ -544,52 +547,15 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head // Do a wrap increment index ++; if(index == TCP_WINDOW_SIZE) index = 0; + if(index == max) break; } - #else - tTCPStoredPacket *pkt, *tmp, *prev = NULL; - - // Allocate and fill cached packet - pkt = malloc( sizeof(tTCPStoredPacket) + dataLen ); - pkt->Next = NULL; - pkt->Sequence = ntohl(Header->SequenceNumber); - pkt->Length = dataLen; - memcpy(pkt->Data, dataptr, dataLen); - - Log_Log("TCP", "We missed a packet, caching", - pkt->Sequence, Connection->NextSequenceRcv); - - // No? Well, let's cache it and look at it later - SHORTLOCK( &Connection->lFuturePackets ); - for(tmp = Connection->FuturePackets; - tmp; - prev = tmp, tmp = tmp->Next) - { - if(tmp->Sequence >= pkt->Sequence) break; - } - - // Add if before first, or sequences don't match - if( !tmp || tmp->Sequence != pkt->Sequence ) - { - if(prev) - prev->Next = pkt; - else - Connection->FuturePackets = pkt; - pkt->Next = tmp; - } - // Replace if larger - else if(pkt->Length > tmp->Length) - { - if(prev) - prev->Next = pkt; - pkt->Next = tmp->Next; - free(tmp); - } - else + Uint32 rel_highest = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv; + Uint32 rel_this = index - Connection->NextSequenceRcv; + LOG("Updating highest this(0x%x) > highest(%x)", rel_this, rel_highest); + if( rel_this > rel_highest ) { - free(pkt); // TODO: Find some way to remove this + Connection->HighestSequenceRcvd = index; } - SHORTREL( &Connection->lFuturePackets ); - #endif } // Badly out of sequence packet else @@ -716,17 +682,18 @@ int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t */ void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection) { - #if CACHE_FUTURE_PACKETS_IN_BYTES // Calculate length of contiguous bytes - int length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv; + const int length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv; Uint32 index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; - LOG("length=%i, index=%i", length, index); + size_t runlength = length; + LOG("length=%i, index=0x%x", length, index); for( int i = 0; i < length; i ++ ) { int bit = index % 8; Uint8 bitfield_byte = Connection->FuturePacketValidBytes[index / 8]; if( (bitfield_byte & (1 << bit)) == 0 ) { - length = i; + runlength = i; + LOG("Hit missing, break"); break; } @@ -743,90 +710,51 @@ void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection) } index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; + Connection->NextSequenceRcv += runlength; // Write data to to the ring buffer - if( TCP_WINDOW_SIZE - index > length ) + if( TCP_WINDOW_SIZE - index > runlength ) { // Simple case - RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, length ); + RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, runlength ); } else { int endLen = TCP_WINDOW_SIZE - index; // 2-part case RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, endLen ); - RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData, endLen - length ); + RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData, endLen - runlength ); } // Mark (now saved) bytes as invalid // - Align index - while(index % 8 && length > 0) + while(index % 8 && runlength > 0) { Connection->FuturePacketData[index] = 0; Connection->FuturePacketValidBytes[index/8] &= ~(1 << (index%8)); index ++; if(index > TCP_WINDOW_SIZE) index -= TCP_WINDOW_SIZE; - length --; + runlength --; } - while( length > 7 ) + while( runlength > 7 ) { Connection->FuturePacketData[index] = 0; Connection->FuturePacketValidBytes[index/8] = 0; - length -= 8; + runlength -= 8; index += 8; if(index > TCP_WINDOW_SIZE) index -= TCP_WINDOW_SIZE; } - while(length) + while( runlength > 0) { Connection->FuturePacketData[index] = 0; Connection->FuturePacketData[index/8] &= ~(1 << (index%8)); index ++; if(index > TCP_WINDOW_SIZE) index -= TCP_WINDOW_SIZE; - length --; - } - - #else - tTCPStoredPacket *pkt; - for(;;) - { - SHORTLOCK( &Connection->lFuturePackets ); - - // Clear out duplicates from cache - // - If a packet has just been recieved, and it is expected, then - // (since NextSequenceRcv = rcvd->Sequence + rcvd->Length) all - // packets in cache that are smaller than the next expected - // are now defunct. - pkt = Connection->FuturePackets; - while(pkt && pkt->Sequence < Connection->NextSequenceRcv) - { - tTCPStoredPacket *next = pkt->Next; - free(pkt); - pkt = next; - } - - // If there's no packets left in cache, stop looking - if(!pkt || pkt->Sequence > Connection->NextSequenceRcv) { - SHORTREL( &Connection->lFuturePackets ); - return; - } - - // Delete packet from future list - Connection->FuturePackets = pkt->Next; - - // Release list - SHORTREL( &Connection->lFuturePackets ); - - // Looks like we found one - TCP_INT_AppendRecieved(Connection, pkt->Data, pkt->Length); - if( Connection->HighestSequenceRcvd == Connection->NextSequenceRcv ) - Connection->HighestSequenceRcvd += pkt->Length; - Connection->NextSequenceRcv += pkt->Length; - free(pkt); + runlength --; } - #endif } void TCP_int_SendDelayedACK(void *ConnPtr) @@ -1259,6 +1187,8 @@ void TCP_INT_SendDataPacket(tTCPConnection *Connection, size_t Length, const voi // - Stop Delayed ACK timer (as this data packet ACKs) Time_RemoveTimer(Connection->DeferredACKTimer); + + // TODO: Don't exceed window size packet->SourcePort = htons(Connection->LocalPort); packet->DestPort = htons(Connection->RemotePort); @@ -1534,3 +1464,10 @@ int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue) return 0; } +Uint32 GetRelative(Uint32 Base, Uint32 Value) +{ + if( Value < Base ) + return Value - Base + 0xFFFFFFFF; + else + return Value - Base; +} diff --git a/KernelLand/Modules/IPStack/tcp.h b/KernelLand/Modules/IPStack/tcp.h index c1ceb658..43828f18 100644 --- a/KernelLand/Modules/IPStack/tcp.h +++ b/KernelLand/Modules/IPStack/tcp.h @@ -156,14 +156,9 @@ struct sTCPConnection * \todo Convert this to a ring buffer and a bitmap of valid bytes * \{ */ - #if CACHE_FUTURE_PACKETS_IN_BYTES Uint32 HighestSequenceRcvd; //!< Highest sequence number (within window) recieved Uint8 *FuturePacketData; //!< Future packet data (indexed by sequence number) Uint8 *FuturePacketValidBytes; //!< Valid byte bitmap (WINDOW_SIZE/8 bytes) - #else - tShortSpinlock lFuturePackets; //!< Future packets spinlock - tTCPStoredPacket *FuturePackets; //!< Out of sequence packets - #endif /** * \} */