BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
d48e86e
Branches
Tags
master
flaneth
firmware
ip.c
header fix
Stefan Schuermans
commited
d48e86e
at 2012-05-02 18:39:39
ip.c
Blame
History
Raw
/* flaneth - flash and ethernet Copyright (C) 2007-2012 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 ... }