BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
e8658d5
Branches
Tags
master
flaneth
firmware.dartboard
ip.c
initial commit after making CF identify work
Stefan Schuermans
commited
e8658d5
at 2012-04-15 19:57:57
ip.c
Blame
History
Raw
/* flaneth - flash and ethernet - dartboard mod * version 0.1 date 2008-11-09 * Copyright (C) 2007-2008 Stefan Schuermans <stefan@schuermans.info> * Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html * a BlinkenArea project - http://www.blinkenarea.org/ */ #include <string.h> #include "arp.h" #include "checksum.h" #include "config.h" #include "debug.h" #include "ethernet.h" #include "icmp.h" #include "ip.h" #include "macros.h" #include "nethelp.h" #include "tcp.h" #include "udp.h" // timing parameters #define IpBufferTicksMax 50 // maximum age of buffered IP packet (in 200ms steps) // buffers for IP packets to transmit // - used if MAC is unknown when packet shall be transmitted // - packet is sent when MAC becomes known unsigned char IpBuffer0[80]; // some buffers with different length (IP packets have different length) unsigned char IpBuffer1[80]; unsigned char IpBuffer2[160]; unsigned char IpBuffer3[320]; struct IpBufferTable // table with buffers { unsigned char * pBuffer; // pointer to buffer for packet unsigned short BufferLength; // length of buffer unsigned short PacketLength; // length of packet in buffer, 0 if no packet in this buffer unsigned char Ticks; // age of entry in 200ms steps } IpBufferTab[] = { // put smaller buffers in front of larger buffers // - then short packets will use smaller buffers more often { IpBuffer0, sizeof( IpBuffer0 ), 0, 0 }, { IpBuffer1, sizeof( IpBuffer1 ), 0, 0 }, { IpBuffer2, sizeof( IpBuffer2 ), 0, 0 }, { IpBuffer3, sizeof( IpBuffer3 ), 0, 0 }, }; // tick procedure - call every 200ms void IpTick200( void ) // (extern) { unsigned char i; // increase age of buffered IP packets and remove timed out ones for( i = 0; i < count( IpBufferTab ); i++ ) { if( IpBufferTab[i].PacketLength > 0 ) // buffer in use { IpBufferTab[i].Ticks++; // increase age if( IpBufferTab[i].Ticks > IpBufferTicksMax ) // too old IpBufferTab[i].PacketLength = 0; // discard packet } } } // process a received IP packet void IpRecv( unsigned char * pData, unsigned short Length ) // (extern) { struct IpPacket * pIpPack; unsigned int len; // packet too short if( Length < sizeof( struct IpPacket ) ) return; // convert pointer to IP packet // (this saves us from always casting pData) pIpPack = (struct IpPacket *)pData; // not IPv4 if( pIpPack->IpHdr.Ver_HdrLen != 0x45 ) // IPv4 with no options present return; // check destination address do { if( ip_eq( pIpPack->IpHdr.Dest, ConfigIp ) ) // own IP break; if( ip_eq( pIpPack->IpHdr.Dest, "\xFF\xFF\xFF\xFF" ) ) // broadcast break; if( pIpPack->IpHdr.Dest[0] == (ConfigIp[0] & ConfigMask[0]) && // local broadcast pIpPack->IpHdr.Dest[1] == (ConfigIp[1] & ConfigMask[1]) && pIpPack->IpHdr.Dest[2] == (ConfigIp[2] & ConfigMask[2]) && pIpPack->IpHdr.Dest[3] == (ConfigIp[3] & ConfigMask[3]) ) break; return; // packet not to this node } while( 0 ); // ignore packets sent from invalid source adresses // - this might be some attack or some router fault if( pIpPack->IpHdr.Src[0] >= 0xE0 || // broadcast, reserved or multicast addresses pIpPack->IpHdr.Src[0] == 0x7F || // loopback network ip_eq( pIpPack->IpHdr.Src, "\x00\x00\x00\x00" ) ) // IP 0.0.0.0 return; // ignore packets sent from local network address or broadcast address if( (pIpPack->IpHdr.Src[0] & ConfigMask[0]) == (ConfigIp[0] & ConfigMask[0]) && // source IP is in own subnet (pIpPack->IpHdr.Src[1] & ConfigMask[1]) == (ConfigIp[1] & ConfigMask[1]) && (pIpPack->IpHdr.Src[2] & ConfigMask[2]) == (ConfigIp[2] & ConfigMask[2]) && (pIpPack->IpHdr.Src[3] & ConfigMask[3]) == (ConfigIp[3] & ConfigMask[3]) ) { if( (pIpPack->IpHdr.Src[0] & ~ConfigMask[0]) == 0x00 && // local network address (pIpPack->IpHdr.Src[1] & ~ConfigMask[1]) == 0x00 && (pIpPack->IpHdr.Src[2] & ~ConfigMask[2]) == 0x00 && (pIpPack->IpHdr.Src[3] & ~ConfigMask[3]) == 0x00 ) return; if( (pIpPack->IpHdr.Src[0] & ~ConfigMask[0]) == 0xFF && // local broadcast address (pIpPack->IpHdr.Src[1] & ~ConfigMask[1]) == 0xFF && (pIpPack->IpHdr.Src[2] & ~ConfigMask[2]) == 0xFF && (pIpPack->IpHdr.Src[3] & ~ConfigMask[3]) == 0xFF ) return; } // ignore packets sent from own IP address if( ip_eq( pIpPack->IpHdr.Src, ConfigIp ) ) return; // ignore fragmented packets // BUG: fragmentation must be supported according to RFC781 // but there is no way of assembling packets with up to 64kB on a processor with 4kB of RAM if( (ntohs( pIpPack->IpHdr.FragOfs ) & 0xBFFF) != 0x0000 ) // fragment offset 0, MoreFrags=0, DontFrag=x, reservedFlag=0 return; // check total length len = sizeof( struct EthernetHeader ) + ntohs( pIpPack->IpHdr.TotalLen ); // length according to IP header if( Length < len ) // packet is truncated return; Length = len; // remove ethernet padding from packet (maybe Length > len) // test header checksum if( Checksum( (unsigned char*)&pIpPack->IpHdr, sizeof( struct IpHeader ), 0x0000, 0x0000 ) != 0 ) return; debug_ip_printf( "recv src=%u.%u.%u.%u protocol=%u len=%u", pIpPack->IpHdr.Src[0], pIpPack->IpHdr.Src[1], pIpPack->IpHdr.Src[2], pIpPack->IpHdr.Src[3], pIpPack->IpHdr.Proto, Length ); // branch according to protocol switch( pIpPack->IpHdr.Proto ) { // ICMP case 0x01: IcmpRecv( pData, Length ); break; // TCP case 0x06: TcpRecv( pData, Length ); break; // UDP case 0x11: UdpRecv( pData, Length ); break; } } // send an IP packet // pData must point to a struct IpPacket with IpHdr.Proto and IpHdr.Dest already initialized void IpSend( unsigned char * pData, unsigned short Length ) // (extern) { struct IpPacket * pIpPack; unsigned int chk; unsigned char i; // packet too short if( Length < sizeof( struct IpPacket ) ) return; // convert pointer to IP packet // (this saves us from always casting pData) pIpPack = (struct IpPacket *)pData; debug_ip_printf( "send dest=%u.%u.%u.%u protocol=%u len=%u", pIpPack->IpHdr.Dest[0], pIpPack->IpHdr.Dest[1], pIpPack->IpHdr.Dest[2], pIpPack->IpHdr.Dest[3], pIpPack->IpHdr.Proto, Length ); // fill in header values pIpPack->IpHdr.Ver_HdrLen = 0x45; pIpPack->IpHdr.Tos = 0x00; pIpPack->IpHdr.TotalLen = htons( Length - sizeof( struct EthernetHeader ) ); pIpPack->IpHdr.Id = 0x0000; pIpPack->IpHdr.FragOfs = 0x0000; pIpPack->IpHdr.Ttl = 0x40; pIpPack->IpHdr.HdrChk = 0x0000; ip_cpy( pIpPack->IpHdr.Src, ConfigIp ); // generate header checksum chk = Checksum( (unsigned char *)&pIpPack->IpHdr, sizeof( struct IpHeader ), 0x0000, 0x0000 ); pIpPack->IpHdr.HdrChk = htons( chk ); // destination is in own subnet if( (pIpPack->IpHdr.Dest[0] & ConfigMask[0]) == (ConfigIp[0] & ConfigMask[0]) && (pIpPack->IpHdr.Dest[1] & ConfigMask[1]) == (ConfigIp[1] & ConfigMask[1]) && (pIpPack->IpHdr.Dest[2] & ConfigMask[2]) == (ConfigIp[2] & ConfigMask[2]) && (pIpPack->IpHdr.Dest[3] & ConfigMask[3]) == (ConfigIp[3] & ConfigMask[3]) ) { // broadcast if( (pIpPack->IpHdr.Dest[0] | ConfigMask[0]) == 255 && (pIpPack->IpHdr.Dest[1] | ConfigMask[1]) == 255 && (pIpPack->IpHdr.Dest[2] | ConfigMask[2]) == 255 && (pIpPack->IpHdr.Dest[3] | ConfigMask[3]) == 255 ) { mac_cpy( pIpPack->EthHdr.Dest, "\xFF\xFF\xFF\xFF\xFF\xFF" ); i = 0x00; } // unicast else { // lookup MAC address of destination i = ArpLookup( pIpPack->IpHdr.Dest, pIpPack->EthHdr.Dest ); } } // destination is not in own subnet else { // broadcast if( pIpPack->IpHdr.Dest[0] == 255 && pIpPack->IpHdr.Dest[1] == 255 && pIpPack->IpHdr.Dest[2] == 255 && pIpPack->IpHdr.Dest[3] == 255 ) { mac_cpy( pIpPack->EthHdr.Dest, "\xFF\xFF\xFF\xFF\xFF\xFF" ); i = 0x00; } // unicast else { // lookup MAC address of default gateway i = ArpLookup( ConfigGw, pIpPack->EthHdr.Dest ); } } // MAC available if( i == 0x00 ) { // sent IP packet pIpPack->EthHdr.Type = htons( 0x0800 ); // ethernet packet type: IP EthernetSend( pData, Length ); return; } // find a buffer to store the packet in for( i = 0; i < count( IpBufferTab ); i++ ) { if( IpBufferTab[i].PacketLength == 0 && // buffer not in use Length < IpBufferTab[i].BufferLength ) // buffer long enough { // put packet into buffer memcpy( IpBufferTab[i].pBuffer, pData, Length ); IpBufferTab[i].PacketLength = Length; IpBufferTab[i].Ticks = 0; debug_ip_printf( "queued" ); break; } } // if no buffer was found, we cannnot do anything about it and must discard the packet (i.e. do nothing here) } // a MAC address was discovered // called by ARP to notify IP void IpGotMac( unsigned char Ip[4], unsigned char Mac[6] ) // (extern) { unsigned char i; struct IpPacket * pIpPack; // search for buffered packets that can be sent now for( i = 0; i < count( IpBufferTab ); i++ ) { if( IpBufferTab[i].PacketLength > 0 ) // buffer in use { // convert pointer to IP packet pIpPack = (struct IpPacket *)IpBufferTab[i].pBuffer; debug_ip_printf( "send queued dest=%u.%u.%u.%u protocol=%u len=%u", pIpPack->IpHdr.Dest[0], pIpPack->IpHdr.Dest[1], pIpPack->IpHdr.Dest[2], pIpPack->IpHdr.Dest[3], pIpPack->IpHdr.Proto, IpBufferTab[i].PacketLength ); // destination is in own subnet if( (pIpPack->IpHdr.Dest[0] & ConfigMask[0]) == (ConfigIp[0] & ConfigMask[0]) && (pIpPack->IpHdr.Dest[1] & ConfigMask[1]) == (ConfigIp[1] & ConfigMask[1]) && (pIpPack->IpHdr.Dest[2] & ConfigMask[2]) == (ConfigIp[2] & ConfigMask[2]) && (pIpPack->IpHdr.Dest[3] & ConfigMask[3]) == (ConfigIp[3] & ConfigMask[3]) ) { // packet can be sent to destination if( ip_eq( pIpPack->IpHdr.Dest, Ip ) ) { // send IP packet pIpPack->EthHdr.Type = htons( 0x0800 ); // ethernet packet type: IP mac_cpy( pIpPack->EthHdr.Dest, Mac ); EthernetSend( IpBufferTab[i].pBuffer, IpBufferTab[i].PacketLength ); // buffer is now free IpBufferTab[i].PacketLength = 0; } } // destination is not in own subnet else { // packet can be sent to gateway if( ip_eq( ConfigGw, Ip ) ) { // send IP packet pIpPack->EthHdr.Type = htons( 0x0800 ); // ethernet packet type: IP mac_cpy( pIpPack->EthHdr.Dest, Mac ); EthernetSend( IpBufferTab[i].pBuffer, IpBufferTab[i].PacketLength ); // buffer is now free IpBufferTab[i].PacketLength = 0; } } } } // for( i ... }