/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2000-2019 Ericsson Telecom AB // // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v2.0 // which accompanies this distribution, and is available at // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html // // /////////////////////////////////////////////////////////////////////////////// // // File: IPL4asp_protocolL234.hh // Reference: RFC 791, RFC 792, RFC 768 /////////////////////////////////////////////// /* This file contains structures for protocol layer 2, 3 and 4 messages: Ethernet, ARP, IP, ICMP, UDP; basic filling, checking and related functions and methods as inlines. */ #include // memcpy, memcmp #include // ntohs, htons namespace IPL4asp__PortType { /* #if __BYTE_ORDER == __LITTLE_ENDIAN inline unsigned short ntoh2 ( unsigned short a) { return ( ( a << 8 ) & 0xFF00 ) | ( ( a >> 8 ) & 0x00FF ); } inline unsigned int ntoh4 ( unsigned int a) { return ( ( a << 24 ) & 0xFF000000 ) | ( ( a << 8 ) & 0x00FF0000 ) | ( ( a >> 8 ) & 0x0000FF00 ) | ( ( a >> 24 ) & 0x000000FF ); } #else inline unsigned short ntoh2 ( unsigned short a) { return a; } inline unsigned int ntoh4 ( unsigned int a) { return a; } #endif inline unsigned short hton2 ( unsigned short a) { return ntoh2 ( a ); } inline unsigned int hton4 ( unsigned int a) { return ntoh4 ( a ); } */ // Ethernet address structure struct EtherAddr { unsigned char bytes[6]; inline unsigned int copyTo ( unsigned char * buf ) const { * (unsigned int*) buf = * (unsigned int*) bytes; * (unsigned short*) ( buf + 4 ) = * (unsigned short*) ( bytes + 4 ); return 6; } inline EtherAddr ( unsigned int b0, unsigned int b1, unsigned int b2, unsigned int b3, unsigned int b4, unsigned int b5 ) { bytes[0] = b0; bytes[1] = b1; bytes[2] = b2; bytes[3] = b3; bytes[4] = b4; bytes[5] = b5; } inline EtherAddr ( unsigned char * buf, unsigned int len ) { (void) len; * (unsigned int*) bytes = * (unsigned int*) buf; * (unsigned short*) ( bytes + 4 ) = * (unsigned short*) ( buf + 4 ); } inline EtherAddr () {} const char * asStr() const; } __attribute__ ((__packed__)); inline bool operator == ( const EtherAddr & aL, const EtherAddr & aR ) { return * (unsigned int*) aL.bytes == * (unsigned int*) aR.bytes && * (unsigned short*) ( aL.bytes + 4 ) == * (unsigned short*) ( aR.bytes + 4 ); } static const EtherAddr ETHER_BC_ADDR ( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ); static const EtherAddr ETHER_ADDR_ZERO ( 0, 0, 0, 0, 0, 0 ); // Ethernet header structure struct EtherHdr { EtherAddr dstAddr; EtherAddr srcAddr; unsigned char type[2]; enum { IP = 0x0800, ARP = 0x0806 }; enum { LENGTH = 14 }; inline void setIP ( const EtherAddr & srcA, const EtherAddr & dstA = ETHER_BC_ADDR ) { dstAddr = dstA; srcAddr = srcA; type[0] = IP >> 8; type[1] = IP & 0xFF; } inline void setARP ( const EtherAddr & srcA, const EtherAddr & dstA = ETHER_BC_ADDR ) { dstAddr = dstA; srcAddr = srcA; type[0] = ARP >> 8; type[1] = ARP & 0xFF; } inline unsigned short getType () const { unsigned short t = ( type[0] << 8 ) + type[1]; return t; } static inline const EtherHdr * check ( const unsigned char * packet, unsigned int len ) { return ( len >= LENGTH ) ? (const EtherHdr*) packet : 0; } } __attribute__ ((__packed__)); // IP address structures struct IPv6Addr { unsigned char bytes[16]; }; struct IPAddr { union { unsigned int v4; IPv6Addr v6; unsigned char bytes[16]; }; unsigned int len; inline unsigned int copyTo ( unsigned char * buf ) const { memcpy ( buf, this, len ); return len; } inline IPAddr () : v4 ( 0 ), len ( 0 ) {} inline IPAddr ( unsigned char * buf, unsigned int length ) : len ( length ) { memcpy ( bytes, buf, length ); } inline IPAddr ( const char * str ) : len ( 0 ) { if ( str != 0 ) len = inet_aton ( str, (struct in_addr*) this ) ? 4 : 0; // TODO: IPv6 } inline void set ( unsigned int ipv4Addr ) { v4 = ipv4Addr; len = 4; } inline void set ( const IPv6Addr & ipv6Addr ) { v6 = ipv6Addr; len = 16; } inline void clear () { v4 = 0; len = 0; } const char * asStr() const; }; inline bool operator == ( const IPAddr & aL, const IPAddr & aR ) { if ( aL.len != aR.len ) return false; if ( aL.len == 4 ) return aL.v4 == aR.v4; if ( aL.len == 16 ) return memcmp ( aL.v6.bytes, aR.v6.bytes, 16 ) == 0; return true; } inline bool operator != ( const IPAddr & aL, const IPAddr & aR ) { return ! ( aL == aR ); } inline bool operator == ( const IPAddr & aL, unsigned int aR ) { return ( aL.len == 4 && aL.v4 == aR ); } inline bool operator != ( const IPAddr & aL, unsigned int aR ) { return ! ( aL == aR ); } inline bool operator == ( unsigned int aL, const IPAddr & aR ) { return ( aR.len == 4 && aL == aR.v4 ); } inline bool operator != ( unsigned int aL, const IPAddr & aR ) { return ! ( aL == aR ); } const char * ipv4ToStr ( unsigned int ipv4Addr ); inline IPAddr ipv4ToIPAddr ( unsigned int ipv4Addr ) { IPAddr ipAddr; ipAddr.set ( ipv4Addr ); return ipAddr; } // Default contents of the ARP header (IPv4 and Request) struct Raw4Bytes { unsigned char bytes[4]; }; static const Raw4Bytes ARPV4_HDR_CONST = { { 0, 1, 8, 0 } }; // ARP header structure struct ARPHdr { unsigned short hType; unsigned short pType; unsigned char hLen; unsigned char pLen; unsigned char operation[2]; enum { REQUEST = 1, REPLY = 2 }; enum { LENGTH = 8 }; inline void set ( unsigned char op = REQUEST, unsigned int ipLen = 4 ) { * (Raw4Bytes*) this = ARPV4_HDR_CONST; hLen = 6; pLen = ipLen; operation[0] = 0; operation[1] = op; } static inline const ARPHdr * check ( const unsigned char * arpMsg, unsigned int len ) { if ( len < LENGTH ) return 0; const ARPHdr * hdr = (const ARPHdr*) arpMsg; return ( len >= LENGTH && * (unsigned int*) arpMsg == * (unsigned int*) &ARPV4_HDR_CONST && hdr->hLen == 6 && hdr->operation[0] == 0 && ( hdr->pLen == 4 || hdr->pLen == 16 ) && ( hdr->operation[1] == REQUEST || hdr->operation[1] == REPLY ) ) ? hdr : 0; } } __attribute__ ((__packed__)); struct ARPv4Payload { EtherAddr sha; unsigned int spa; EtherAddr tha; unsigned int tpa; enum { LENGTH = 20 }; static inline const ARPv4Payload * check ( const unsigned char * msg, unsigned int len ) { return ( len >= LENGTH ) ? (const ARPv4Payload*) msg : 0; } } __attribute__ ((__packed__)); struct ARPv6Payload { EtherAddr sha; IPv6Addr spa; EtherAddr tha; IPv6Addr tpa; enum { LENGTH = 44 }; static inline const ARPv6Payload * check ( const unsigned char * msg, unsigned int len ) { return ( len >= LENGTH ) ? (const ARPv6Payload*) msg : 0; } } __attribute__ ((__packed__)); class ARPMsgOut { public: enum { BUFFER_SIZE = EtherHdr::LENGTH + ARPHdr::LENGTH + ARPv6Payload::LENGTH }; /* BUFFER_SIZE = 66 */ static inline unsigned int create ( unsigned char * msgOut, const EtherAddr & srcEtherAddr, const IPAddr & srcIPAddr, const EtherAddr & dstEtherAddr, const IPAddr & dstIPAddr, unsigned char op ) { unsigned int n; ( (EtherHdr*) msgOut )->setARP ( srcEtherAddr ); n = EtherHdr::LENGTH; ( (ARPHdr*) ( msgOut + n ) )->set ( op, dstIPAddr.len ); n += ARPHdr::LENGTH; n += srcEtherAddr.copyTo ( msgOut + n ); n += srcIPAddr.copyTo ( msgOut + n ); n += dstEtherAddr.copyTo ( msgOut + n ); n += dstIPAddr.copyTo ( msgOut + n ); return n; } }; // 16 bit one's complement checksum calculation // It is used in the IP header, in ICMP meessages and in UDP messages inline unsigned short onesChkSum ( const unsigned short * data, unsigned int len, unsigned int chkSum = 0 ) { for ( ; len >= 2; len -= 2, ++data ) chkSum += ntohs ( *data ); if ( len != 0 ) chkSum += ( * (unsigned char*) data ) << 8; while ( ( chkSum & 0xFFFF0000 ) != 0 ) chkSum = ( chkSum & 0xFFFF ) + ( chkSum >> 16 ); return (unsigned short) chkSum; } // IPv4 header structure (fixed part) struct IPv4Hdr { unsigned char verAndHdrLen; // bits 0..3: version, 4..7: IHL unsigned char tos; unsigned short totalLength; unsigned short id; unsigned short flagsAndOffset; // bit 1: DF, 2: MF, 3..15: fragment offset unsigned char ttl; unsigned char protocol; unsigned short headerCheckSum; unsigned int srcAddr; unsigned int dstAddr; enum { ICMP = 1, TCP = 6, UDP = 17, SCTP = 132 }; enum { LENGTH = 20 }; inline void setDef ( unsigned char proto, unsigned int srcA, unsigned int dstA ) { verAndHdrLen = 0x45; tos = 0; totalLength = 0 /* set later */; id = 0; flagsAndOffset = 0; ttl = 64; protocol = proto; headerCheckSum = 0 /* set later */; srcAddr = srcA; dstAddr = dstA; } inline void setUDP ( unsigned int srcA, unsigned int dstA ) { setDef ( UDP, srcA, dstA ); } inline void setTCP ( unsigned int srcA, unsigned int dstA ) { setDef ( TCP, srcA, dstA ); } inline void setLength ( unsigned int len ) { totalLength = htons ( len ); } inline void setCheckSum ( unsigned short chkSum ) { headerCheckSum = htons ( ~chkSum ); } inline void setDF () { *(unsigned char*) &flagsAndOffset |= 0x40; } inline void setMF () { *(unsigned char*) &flagsAndOffset |= 0x20; } inline unsigned int hdrLen () const { return ( verAndHdrLen & 0x0F ) * 4; } inline unsigned int payloadLen () const { return ntohs ( totalLength ) - hdrLen (); } inline bool isDFSet () const { return ( *(unsigned char*) &flagsAndOffset & 0x40 ) != 0; } inline bool isMFSet () const { return ( *(unsigned char*) &flagsAndOffset & 0x20 ) != 0; } inline unsigned int getOffset () const { return ntohs ( flagsAndOffset ) & 0x1FFFF; } inline const unsigned char * raw () const { return (const unsigned char*) this; } static inline unsigned short calcCheckSum ( const unsigned char * ipMsg ) { const IPv4Hdr * hdr = (const IPv4Hdr *) ipMsg; return onesChkSum ( (const unsigned short*) ipMsg, hdr->hdrLen() ); } static inline const IPv4Hdr * check ( const unsigned char * ipMsg, unsigned int len ) { if ( len < LENGTH ) return 0; const IPv4Hdr * hdr = (const IPv4Hdr *) ipMsg; if ( ( hdr->verAndHdrLen >> 4 ) != 4 ) return 0; unsigned int ihl = hdr->verAndHdrLen & 0x0F; if ( ihl < 5 || len < ihl * 4 ) return 0; if ( hdr->protocol != ICMP && hdr->protocol != UDP && hdr->protocol != TCP ) return 0; if ( ntohs ( hdr->totalLength ) > len ) return 0; unsigned short chkSum = calcCheckSum ( ipMsg ); if ( chkSum != 0 && chkSum != 0xFFFF ) return 0; return hdr; } } __attribute__ ((__packed__)); // ICMPv4 header structure (together with used ICMP messages) struct ICMPv4Hdr { unsigned char type; unsigned char code; unsigned short checkSum; enum { ECHO_REPLY = 0, ECHO_REQUEST = 8, ROUTER_ADVERTISEMENT = 9, ROUTER_SOLICITATION = 10, ADDRESS_MASK_REQUEST = 17, ADDRESS_MASK_REPLY = 18 }; enum { LENGTH = 4 }; inline void setCheckSum ( unsigned short chkSum ) { checkSum = htons ( ~chkSum ); } static inline unsigned short calcCheckSum ( const unsigned char * icmpMsg, const unsigned int len ) { return onesChkSum ( (const unsigned short*) icmpMsg, len ); } static inline const ICMPv4Hdr * check ( const unsigned char * icmpMsg, unsigned int len ) { if ( len < LENGTH ) return 0; unsigned short chkSum = calcCheckSum ( icmpMsg, len ); if ( chkSum != 0 && chkSum != 0xFFFF ) return 0; return (const ICMPv4Hdr*) icmpMsg; } } __attribute__ ((__packed__)); struct ICMPv4Echo { unsigned short id; unsigned short seqNum; enum { LENGTH = 4 }; static inline const ICMPv4Echo * check ( const unsigned char * msg, unsigned int len ) { return ( len >= LENGTH ) ? (const ICMPv4Echo*) msg : 0; } } __attribute__ ((__packed__)); struct ICMPv4RouterAdvEntry { unsigned int ipAddr; unsigned int prefLvl; } __attribute__ ((__packed__)); struct ICMPv4RouterAdv { unsigned char nAddrs; unsigned char entrySize; unsigned short lifetime; ICMPv4RouterAdvEntry entry[1]; static inline const ICMPv4RouterAdv * check ( const unsigned char * msg, unsigned int len ) { if ( len < 4 ) return 0; const ICMPv4RouterAdv * adv = (const ICMPv4RouterAdv*) msg; return ( adv->entrySize == 2U && len >= 4U + adv->nAddrs * 8U ) ? adv : 0; } } __attribute__ ((__packed__)); // UDP header structure struct UDPHdr { unsigned short srcPort; unsigned short dstPort; unsigned short length; unsigned short checkSum; enum { LENGTH = 8 }; inline void set ( unsigned int srcP, unsigned int dstP ) { srcPort = htons ( srcP ); dstPort = htons ( dstP ); length = 0; checkSum = 0; } inline void setLength ( unsigned int len ) { length = htons ( len ); } inline void setCheckSum ( unsigned short chkSum ) { if ( chkSum != 0xFFFF ) chkSum = ~chkSum; checkSum = htons ( chkSum ); } inline const unsigned char * raw () const { return (const unsigned char*) this; } static inline unsigned short calcCheckSum ( const unsigned char * udpMsg, const IPv4Hdr & ipv4Hdr ) { unsigned char * sA = (unsigned char*) &ipv4Hdr.srcAddr; unsigned char * dA = (unsigned char*) &ipv4Hdr.dstAddr; unsigned int udpLength = ( udpMsg[4] << 8 ) + udpMsg[5]; unsigned int chkSum = ( sA[0] << 8 ) + sA[1] + ( sA[2] << 8 ) + sA[3] + ( dA[0] << 8 ) + dA[1] + ( dA[2] << 8 ) + dA[3] + IPv4Hdr::UDP + udpLength; return onesChkSum ( (const unsigned short*) udpMsg, udpLength, chkSum ); } static inline const UDPHdr * check ( const unsigned char * udpMsg, const IPv4Hdr & ipv4Hdr ) { unsigned int len = ipv4Hdr.payloadLen (); if ( len < LENGTH ) return 0; const UDPHdr * hdr = (const UDPHdr*) udpMsg; if ( ntohs ( hdr->length ) != len ) return 0; if ( hdr->checkSum != 0 ) { unsigned short checkSum = calcCheckSum ( udpMsg, ipv4Hdr ); if ( checkSum != 0 && checkSum != 0xFFFF ) { return 0; } } return hdr; } } __attribute__ ((__packed__)); } /* namespace IPL4asp__PortType */