A stack which works with or without an Mbed os library. Provides IPv4 or IPv6 with a full 1500 byte buffer.

Dependents:   oldheating gps motorhome heating

ip4/ip4.cpp

Committer:
andrewboyson
Date:
2017-04-16
Revision:
10:f0854784e960
Child:
11:c051adb70c5a

File content as of revision 10:f0854784e960:

#include "mbed.h"
#include  "log.h"
#include  "net.h"
#include "icmp.h"
#include "udptcp4.h"
#include   "ar.h"
#include "dhcp.h"
#include  "eth.h"
#include  "ip4.h"

#define DEBUG false

#define HEADER_LENGTH 20

__packed struct header
{
    uint8_t  versionIhl;
    uint8_t  tos;
    uint16_t length;
    uint16_t id;
    uint16_t flagsOffset;
    uint8_t  ttl;
    uint8_t  protocol;
    uint16_t checksum;
    uint32_t src;
    uint32_t dst;
};

//Header variables
static uint8_t      version;
static int     headerLength;
static uint8_t          tos;
static uint16_t          id;
static bool    dontFragment;
static bool    moreFragments;
static uint16_t      offset;
static uint8_t          ttl;
static uint8_t     protocol;
static uint16_t    checksum;
static uint16_t     calcsum;
static uint32_t       srcIp;
static uint32_t       dstIp;
static void*          pData;
static int       dataLength;

static void protocolToString(uint16_t protocol, int size, char* text)
{
    switch (protocol)
    {
        case ICMP: strncpy (text, "ICMP", size);         break;
        case  TCP: strncpy (text, "TCP" , size);         break;
        case  UDP: strncpy (text, "UDP" , size);         break;
        default:   snprintf(text, size, "%d", protocol); break;
    }
}

void logHeader(char* title)
{
    char text[30];
    LogTimeF("%s\r\n", title);
    LogF("  Version           %d\r\n", version);
    LogF("  Header length     %d\r\n", headerLength);
    LogF("  Type of service   %d\r\n", tos);
    LogF("  Data length       %d\r\n", dataLength);
    LogF("  Identification    %d\r\n", id);
    if (dontFragment)  LogF("  Don't fragment\r\n");
    else               LogF("  Do fragment\r\n");
    if (moreFragments) LogF("  More fragments\r\n");
    else               LogF("  No more fragments\r\n");
    LogF("  Offset            %d\r\n", offset);
    LogF("  Time to live      %d\r\n", ttl);
    protocolToString(protocol, sizeof(text), text);
    LogF("  Protocol          %s\r\n", text);
    LogF("  Checksum (hex)    %04hX\r\n", checksum);
    LogF("  Calculated (hex)  %04hX\r\n",  calcsum);
    NetIp4AddressToString(srcIp, sizeof(text), text);
    LogF("  Source IP         %s\r\n", text);
    NetIp4AddressToString(dstIp, sizeof(text), text);
    LogF("  Destination IP    %s\r\n", text);
}
void readHeader(struct header * pHeader)
{
             version       =             pHeader->versionIhl >> 4;
    uint8_t  ihl           =             pHeader->versionIhl & 0xF;
             headerLength  =             ihl * 4;
             tos           =             pHeader->tos;
    uint16_t totalLength   = NetToHost16(pHeader->length);
             id            = NetToHost16(pHeader->id);
    uint16_t flagsOffset   = NetToHost16(pHeader->flagsOffset);
             dontFragment  =             flagsOffset & 0x4000;
             moreFragments =             flagsOffset & 0x8000;
             offset        =             flagsOffset & 0x1FFF;
             ttl           =             pHeader->ttl;
             protocol      =             pHeader->protocol;
             checksum      = NetToHost16(pHeader->checksum);
             calcsum       = NetCheckSum(headerLength, pHeader);
             srcIp         =             pHeader->src;
             dstIp         =             pHeader->dst;
             pData         =      (char*)pHeader + headerLength;
             dataLength    =         totalLength - headerLength;
}
void writeHeader(struct header * pHeader)
{
    uint16_t flagsOffset = offset;
    if (dontFragment)  flagsOffset |= 0x4000;
    if (moreFragments) flagsOffset |= 0x8000;

    uint8_t ihl          = headerLength >> 2;
    pHeader->versionIhl  = (version << 4) + ihl;
    pHeader->tos         = tos;
    pHeader->id          = NetToHost16(id);
    pHeader->flagsOffset = NetToHost16(flagsOffset);
    pHeader->ttl         = ttl;
    pHeader->protocol    = protocol;
    
    pHeader->dst         = dstIp;
    pHeader->src         = srcIp;
    pHeader->length      = NetToHost16(headerLength + dataLength);
    pHeader->checksum    = 0;
    pHeader->checksum    = NetCheckSum(headerLength, pHeader);
    calcsum              = 0;
}
int Ip4HandleReceivedPacket(char* pSrcMac, void* pPacket, int* pSize, char* pDstMac)
{
    struct header * pHeader = (header*)pPacket;
    readHeader(pHeader);
    
    bool isMe        = dstIp == DhcpLocalIp;
    bool isBroadcast = dstIp == IP4_BROADCAST_ADDRESS;
    bool isMulticast = (dstIp & 0xE0) == 0xE0; //224.x.x.x == 1110 0000 == E0.xx.xx.xx == xx.xx.xx.E0 in little endian
    
    if (!isMe && !isBroadcast && !isMulticast) return DO_NOTHING;
    
    ArAdd4(pSrcMac, srcIp);
    
    if (DEBUG) logHeader("IP4 packet received");

    int action = DO_NOTHING;
    switch (protocol)
    {
        case ICMP: action = IcmpHandleReceivedPacket(&srcIp, &dstIp, &dataLength, pData); break;
        case UDP:  action = Udp4HandleReceivedPacket(&srcIp, &dstIp, &dataLength, pData); break;
        case TCP:  action = Tcp4HandleReceivedPacket(&srcIp, &dstIp, &dataLength, pData); break;        
        default:
            logHeader("IP4 packet unhandled");
            return DO_NOTHING;
    }
    
    switch (action)
    {
        case DO_NOTHING:
            return DO_NOTHING;
        case UNICAST:
        case UNICAST_DNS:
        case UNICAST_DHCP:
            memcpy(pDstMac, pSrcMac, 6);
            break;
        case MULTICAST_NODE:
        case MULTICAST_ROUTER:
        case MULTICAST_MDNS:
        case MULTICAST_LLMNR:
        case BROADCAST:
            break;
        default:
            LogTimeF("Ip4HandleReceivedPacket unknown action %d\r\n", action);
            return DO_NOTHING;
    }

    if (DEBUG) logHeader("IP4 packet replied to");
    
    writeHeader(pHeader);
    
    *pSize = headerLength + dataLength;
    
    return action;
}
int Ip4PollForPacketToSend(void* pPacket, int* pSize, char* pDstMac)
{
    struct header * pHeader = (header*)pPacket;
    
    headerLength = HEADER_LENGTH;
    pData = (char*)pPacket + headerLength;
    
    srcIp = 0;
    dstIp = 0;
    
    dataLength = 0;
    int action  = DO_NOTHING;
    if (!action) action = Udp4PollForPacketToSend(pData, &dataLength, &srcIp, &dstIp);
    
    switch (action)
    {
        case DO_NOTHING:
            return DO_NOTHING;
        case UNICAST:
        case UNICAST_DNS:
        case UNICAST_DHCP:
            ArRev4(dstIp, pDstMac); //Make the remote MAC from ARP
            break;
        case MULTICAST_MDNS:
        case MULTICAST_LLMNR:
        case BROADCAST:
            break;
        default:
            LogTimeF("Ip4PollForPacketToSend unknown action %d\r\n", action);
            return DO_NOTHING;
    }

    version       = 4;
    tos           = 0;
    id            = 0;
    dontFragment  = true;
    moreFragments = false;
    offset        = 0;
    ttl           = 255;
    protocol      = UDP;
    
    if (DEBUG) logHeader("IP4 packet replied to");

    writeHeader(pHeader);
    
    *pSize = headerLength + dataLength;
    
    return action;
}