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-10-04
Revision:
37:793b39683406
Parent:
36:900e24b27bfb
Child:
42:222a4f45f916

File content as of revision 37:793b39683406:

#include   "mbed.h"
#include    "log.h"
#include    "net.h"
#include "action.h"
#include   "icmp.h"
#include "udptcp4.h"
#include     "ar.h"
#include     "nr.h"
#include   "dhcp.h"
#include    "eth.h"
#include     "ip.h"
#include    "ip4.h"
#include    "ntp.h"
#include    "mac.h"

#define SHOW_FILTERED true

#define IP4_BROADCAST_ADDRESS       0xFFFFFFFF
#define IP4_MULTICAST_ALL_HOSTS     0x010000E0
#define IP4_MULTICAST_ALL_ROUTERS   0x020000E0
#define IP4_MULTICAST_DNS_ADDRESS   0xFB0000E0
#define IP4_MULTICAST_LLMNR_ADDRESS 0xFC0000E0

int Ip4AddressToString(uint32_t ip, int size, char* text)
{
    int a0 = (ip & 0xFF000000) >> 24;
    int a1 = (ip & 0x00FF0000) >> 16;
    int a2 = (ip & 0x0000FF00) >>  8;
    int a3 = (ip & 0x000000FF);
    return snprintf(text, size, "%d.%d.%d.%d", a3, a2, a1, a0); 
}

uint32_t Ip4Parse(char* text)
{
    int ints[4];
    sscanf(text, "%d.%d.%d.%d", &ints[3], &ints[2], &ints[1], &ints[0]);
    return (ints[0] << 24) + (ints[1] << 16) + (ints[2] << 8) + ints[3];
}

void Ip4DestIpFromDest(int dest, uint32_t* pDstIp)
{
    switch (dest)
    {
        case UNICAST:                                                 break;
        case UNICAST_DNS:      *pDstIp = DhcpDnsServer;               break;
        case UNICAST_DHCP:     *pDstIp = DhcpServer;                  break;
        case UNICAST_NTP:      *pDstIp = NtpServerIp4;                break;
        case MULTICAST_NODE:   *pDstIp = IP4_MULTICAST_ALL_HOSTS;     break;
        case MULTICAST_ROUTER: *pDstIp = IP4_MULTICAST_ALL_ROUTERS;   break;
        case MULTICAST_MDNS:   *pDstIp = IP4_MULTICAST_DNS_ADDRESS;   break;
        case MULTICAST_LLMNR:  *pDstIp = IP4_MULTICAST_LLMNR_ADDRESS; break;
        case BROADCAST:        *pDstIp = IP4_BROADCAST_ADDRESS;       break;
        default:
            LogTimeF("Ip4DestIpFromDest unknown destination %d\r\n", dest);
            break;
    }
}

#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;

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;
}

static void logHeader()
{
    char text[30];
    Log ("IPv4 header\r\n");
    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);
    IpProtocolToString(protocol, sizeof(text), text);
    LogF("  Protocol          %s\r\n", text);
    LogF("  Checksum (hex)    %04hX\r\n", checksum);
    LogF("  Calculated (hex)  %04hX\r\n",  calcsum);
    Ip4AddressToString(srcIp, sizeof(text), text);
    LogF("  Source IP         %s\r\n", text);
    Ip4AddressToString(dstIp, sizeof(text), text);
    LogF("  Destination IP    %s\r\n", text);
}
static void (*pTraceBack)(void);
static void trace()
{
    pTraceBack();
    logHeader();
}
int Ip4HandleReceivedPacket(void (*traceback)(void), char* pSrcMac, void* pPacket, int* pSize, char* pDstMac)
{
    pTraceBack = traceback;
    struct header * pHeader = (header*)pPacket;
    readHeader(pHeader);
    
    bool isMe             = dstIp == DhcpLocalIp;
    bool isLocalBroadcast = dstIp == DhcpLocalIp | 0xFF000000;
    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
    
    bool doIt = isMe || isLocalBroadcast || isBroadcast || isMulticast;
    if (!doIt)
    {
        if (SHOW_FILTERED)
        {
            char text[20];
            Ip4AddressToString(dstIp, sizeof(text), text);
            LogTimeF("IP4 filtered out ip %s ", text);
            Ip4AddressToString(srcIp, sizeof(text), text);
            LogF("from %s \r\n", text);
        }
        return DO_NOTHING;
    }
    
    ArAddIp4Record(pSrcMac, srcIp);
    NrMakeRequestForNameFromIp4(srcIp);

    int action = DO_NOTHING;
    switch (protocol)
    {
        case ICMP:   action = IcmpHandleReceivedPacket(trace, &srcIp, &dstIp, &dataLength, pData); break;
        case IGMP:                                                                                 break;
        case UDP:    action = Udp4HandleReceivedPacket(trace, &srcIp, &dstIp, &dataLength, pData); break;
        case TCP:    action = Tcp4HandleReceivedPacket(trace, &srcIp, &dstIp, &dataLength, pData); break;
        case IP6IN4:                                                                               break;
        default:
            LogTimeF("IP4 received packet unknown protocol %d\r\n");
            return DO_NOTHING;
    }
    if (!action) return DO_NOTHING;
    
    MacCopy(pDstMac, pSrcMac);
        
    writeHeader(pHeader);
    
    *pSize = headerLength + dataLength;
    
    if (ActionGetTracePart(action)) logHeader();

    return action;
}
int Ip4PollForPacketToSend(void* pPacket, int* pSize, char* pDstMac)
{
    headerLength  = HEADER_LENGTH;
    pData         = (char*)pPacket + headerLength;
    dataLength    = 0;
    version       = 4;
    tos           = 0;
    id            = 0;
    dontFragment  = true;
    moreFragments = false;
    offset        = 0;
    ttl           = 255;
    protocol      = UDP;
    
    int action  = DO_NOTHING;
    if (!action) action = Udp4PollForPacketToSend(pData, &dataLength, &srcIp, &dstIp);
    if (!action) return DO_NOTHING;
    switch (action)
    {
        case UNICAST:
        case UNICAST_DNS:
        case UNICAST_DHCP:
        case UNICAST_NTP:
            ArIpToMac4(dstIp, pDstMac);             //Make the remote MAC from ARP
            break;
        case BROADCAST:
            break;
        default:
            LogTimeF("Ip4PollForPacketToSend - undefined action %d\r\n", action);
            break;
    }

    writeHeader((header*)pPacket);
    
    *pSize = headerLength + dataLength;
    
    if (ActionGetTracePart(action)) logHeader();

    return action;
}