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.c

Committer:
andrewboyson
Date:
2018-12-05
Revision:
94:e2973a2c488e
Parent:
86:55bc5ddac16c
Child:
97:d91f7db00235

File content as of revision 94:e2973a2c488e:

#include <stdint.h>
#include <stdbool.h>

#include     "log.h"
#include     "net.h"
#include  "action.h"
#include   "icmp4.h"
#include "udptcp4.h"
#include     "ar4.h"
#include     "nr4.h"
#include    "dhcp.h"
#include     "eth.h"
#include      "ip.h"
#include "ip4addr.h"
#include     "ntp.h"
#include     "mac.h"

bool Ip4Trace = true;

#define OFF_LINK_TTL 64

__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 totalLength;
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 readHeader(struct header * pHeader)
{
             version       =             pHeader->versionIhl >> 4;
    uint8_t  ihl           =             pHeader->versionIhl & 0xF;
             headerLength  =             ihl * 4;
             tos           =             pHeader->tos;
             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;
}
static 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(totalLength);
    pHeader->checksum    = 0;
    pHeader->checksum    = NetCheckSum(headerLength, pHeader);
    calcsum              = 0;
}

static void logHeader()
{
    if (NetTraceVerbose)
    {
        Log ("IP4 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("  Total length      %d\r\n", totalLength);
        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);
        LogF("  Protocol          "); IpProtocolLog(protocol); Log("\r\n");
        LogF("  Checksum (hex)    %04hX\r\n", checksum);
        LogF("  Calculated (hex)  %04hX\r\n",  calcsum);
        LogF("  Source IP         "); Ip4AddressLog(srcIp); Log("\r\n");
        LogF("  Destination IP    "); Ip4AddressLog(dstIp); Log("\r\n");
    }
    else
    {
        Log ("IP4   header ");
        IpProtocolLog(protocol);
        Log(" ");
        Ip4AddressLog(srcIp);
        Log(" >>> ");
        Ip4AddressLog(dstIp);
        Log("\r\n");
    }
}
static void (*pTraceBack)(void);
static void trace()
{
    pTraceBack();
    logHeader();
}
int Ip4HandleReceivedPacket(void (*traceback)(void), void* pPacketRx, int sizeRx, void* pPacketTx, int* pSizeTx, char* macRemote)
{
    
    pTraceBack = traceback;
    struct header * pHeaderRx = (struct header*)pPacketRx;
    struct header * pHeaderTx = (struct header*)pPacketTx;
    
    char* pDataRx = (char*)pHeaderRx + headerLength;
    char* pDataTx = (char*)pHeaderTx + headerLength;
    
    readHeader(pHeaderRx);
    if (totalLength < sizeRx) sizeRx = totalLength;
    int dataLengthRx = sizeRx - headerLength;
    int dataLengthTx = *pSizeTx - sizeof(struct header);
    
    bool isMe             = dstIp ==  DhcpLocalIp;
    bool isLocalBroadcast = dstIp == (DhcpLocalIp | 0xFF000000); // dstIp == 192.168.1.255; '|' is lower precendence than '=='
    bool isBroadcast      = dstIp ==  IP4_BROADCAST_ADDRESS;     // dstIp == 255.255.255.255
    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 (Ip4Trace);
        {
            LogTimeF("IP4 filtered out ip "); Ip4AddressLog(dstIp); 
            Log(" from ");
            Ip4AddressLog(srcIp);
            Log("\r\n");
        }
        return DO_NOTHING;
    }
    
    int remArIndex = Ar4AddIpRecord(trace, macRemote, srcIp);
    Nr4MakeRequestForNameFromIp(srcIp);
    
    int action = DO_NOTHING;
    switch (protocol)
    {
        case ICMP:   action = Icmp4HandleReceivedPacket(trace, pDataRx, dataLengthRx, pDataTx, &dataLengthTx, &srcIp, &dstIp); break;
        case IGMP:                                                                                                             break;
        case UDP:    action =  Udp4HandleReceivedPacket(trace, pDataRx, dataLengthRx, pDataTx, &dataLengthTx, &srcIp, &dstIp); break;
        case TCP:    action =  Tcp4HandleReceivedPacket(trace, pDataRx, dataLengthRx, pDataTx, &dataLengthTx, &srcIp, &dstIp, remArIndex); break;
        case IP6IN4:                                                                                                           break;
        default:
            LogTimeF("IP4 received packet unknown protocol %d\r\n");
            return DO_NOTHING;
    }
    if (!action)
    {
        return DO_NOTHING;
    }
    
    if (DhcpIpNeedsToBeRouted(dstIp))
    {
        Ar4IpToMac(DhcpRouter, macRemote);  //Send back to the router
        ttl = OFF_LINK_TTL;
    }
    else
    {
        ttl = 255;
    }

    totalLength = sizeof(struct header) + dataLengthTx;
    headerLength = sizeof(struct header);

    writeHeader(pHeaderTx);
    
    *pSizeTx = totalLength;
    
    if (ActionGetTracePart(action)) logHeader();

    return action;
}
int Ip4PollForPacketToSend(void* pPacket, int* pSize, char* pDstMac)
{    
    headerLength  = sizeof(struct header);
    char* pData   = (char*)pPacket + headerLength;
    version       = 4;
    tos           = 0;
    id            = 0;
    dontFragment  = true;
    moreFragments = false;
    offset        = 0;
    
    int dataLength = *pSize - sizeof(struct header);
    
    int action  = DO_NOTHING;
    if (!action) { action = Udp4PollForPacketToSend(pData, &dataLength, &srcIp, &dstIp); protocol = UDP; }
    if (!action) { action = Tcp4PollForPacketToSend(pData, &dataLength, &srcIp, &dstIp); protocol = TCP; }
    if (!action) return DO_NOTHING;
    int dest = ActionGetDestPart(action);
    switch (dest)
    {
        case UNICAST:
        case UNICAST_DNS:
        case UNICAST_DHCP:
        case UNICAST_NTP:
        case UNICAST_TFTP:
            if (DhcpIpNeedsToBeRouted(dstIp))
            {
                Ar4IpToMac(DhcpRouter, pDstMac); //send via router
                ttl = OFF_LINK_TTL;
            }
            else
            {
                Ar4IpToMac(dstIp,      pDstMac); //Send direct
                ttl = 255;
            }
            break;
        case MULTICAST_NODE:
        case MULTICAST_ROUTER:
        case MULTICAST_MDNS:
        case MULTICAST_LLMNR:
        case BROADCAST:
            ttl = 255;
            break;
        default:
            LogTimeF("Ip4PollForPacketToSend - undefined destination %d\r\n", dest);
            return DO_NOTHING;
    }

    struct header* pHeader = (struct header*)pPacket;

    totalLength = sizeof(struct header) + dataLength;
    headerLength = sizeof(struct header);

    writeHeader(pHeader);
    
    *pSize = totalLength;
    
    if (ActionGetTracePart(action)) logHeader();

    return action;
}