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-07-03
Revision:
22:914b970356f0
Parent:
18:accfcb80d9c3
Child:
30:e34173b7585c

File content as of revision 22:914b970356f0:

#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   "ip.h"
#include  "ip4.h"
#include  "ntp.h"

#define DEBUG false

#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 Ip4DestIpFromAction(int action, uint32_t* pDstIp)
{
    switch (action)
    {
        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("Ip4 DestIpFromAction unknown action %d\r\n", action);
            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;
uint32_t       Ip4Src;
uint32_t       Ip4Dst;
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);
             Ip4Src        =             pHeader->src;
             Ip4Dst        =             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         = Ip4Dst;
    pHeader->src         = Ip4Src;
    pHeader->length      = NetToHost16(headerLength + dataLength);
    pHeader->checksum    = 0;
    pHeader->checksum    = NetCheckSum(headerLength, pHeader);
    calcsum              = 0;
}

static 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);
    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(Ip4Src, sizeof(text), text);
    LogF("  Source IP         %s\r\n", text);
    Ip4AddressToString(Ip4Dst, sizeof(text), text);
    LogF("  Destination IP    %s\r\n", text);
}
int Ip4HandleReceivedPacket(char* pSrcMac, void* pPacket, int* pSize, char* pDstMac)
{
    struct header * pHeader = (header*)pPacket;
    readHeader(pHeader);
    
    bool isMe        = Ip4Dst == DhcpLocalIp;
    bool isLocalBroadcast = Ip4Dst == DhcpLocalIp | 0xFF000000;
    bool isBroadcast = Ip4Dst == IP4_BROADCAST_ADDRESS;
    bool isMulticast = (Ip4Dst & 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 (DEBUG)
        {
            char text[20];
            Ip4AddressToString(Ip4Dst, sizeof(text), text);
            LogTimeF("IP4 filtered out ip %s ", text);
            Ip4AddressToString(Ip4Src, sizeof(text), text);
            LogF("from %s \r\n", text);
        }
        return DO_NOTHING;
    }
    
    ArAdd4(pSrcMac, Ip4Src);
    
    if (DEBUG) logHeader("IP4 packet received");

    int action = DO_NOTHING;
    switch (protocol)
    {
        case ICMP: action = IcmpHandleReceivedPacket(&Ip4Src, &Ip4Dst, &dataLength, pData); break;
        case IGMP: return DO_NOTHING;
        case UDP:  action = Udp4HandleReceivedPacket(&Ip4Src, &Ip4Dst, &dataLength, pData); break;
        case TCP:  action = Tcp4HandleReceivedPacket(&Ip4Src, &Ip4Dst, &dataLength, pData); break;        
        default:
            logHeader("IP4 packet unhandled");
            return DO_NOTHING;
    }
    if (!action) return DO_NOTHING;
    
    memcpy(pDstMac, pSrcMac, 6);
    
    if (DEBUG) logHeader("IP4 packet replied to");
    
    writeHeader(pHeader);
    
    *pSize = headerLength + dataLength;
    
    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, &Ip4Src, &Ip4Dst);
    if (!action) return DO_NOTHING;
    switch (action)
    {
        case UNICAST:
        case UNICAST_DNS:
        case UNICAST_DHCP:
        case UNICAST_NTP:
            ArRev4(Ip4Dst, pDstMac);             //Make the remote MAC from ARP
            break;
        case BROADCAST:
            break;
        default:
            LogTimeF("Ip4PollForPacketToSend - undefined action %d\r\n", action);
            break;
    }

    
    if (DEBUG) logHeader("IP4 polled packet sent");

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