Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ip.c Source File

ip.c

Go to the documentation of this file.
00001 /**
00002  * @file ip.c
00003  * @brief IPv4 and IPv6 common routines
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00026  * @version 1.7.6
00027  **/
00028 
00029 //Switch to the appropriate trace level
00030 #define TRACE_LEVEL IP_TRACE_LEVEL
00031 
00032 //Dependencies
00033 #include "core/net.h"
00034 #include "core/ethernet.h"
00035 #include "core/ip.h"
00036 #include "ipv4/ipv4.h"
00037 #include "ipv6/ipv6.h"
00038 #include "ipv6/ipv6_misc.h"
00039 #include "debug.h"
00040 
00041 //Special IP address
00042 const IpAddr IP_ADDR_ANY = {0};
00043 const IpAddr IP_ADDR_UNSPECIFIED = {0};
00044 
00045 
00046 /**
00047  * @brief Send an IP datagram
00048  * @param[in] interface Underlying network interface
00049  * @param[in] pseudoHeader IP pseudo header
00050  * @param[in] buffer Multi-part buffer containing the payload
00051  * @param[in] offset Offset to the first payload byte
00052  * @param[in] ttl TTL value. Default Time-To-Live is used when this parameter is zero
00053  * @return Error code
00054  **/
00055 
00056 error_t ipSendDatagram(NetInterface *interface, IpPseudoHeader *pseudoHeader,
00057    NetBuffer *buffer, size_t offset, uint8_t ttl)
00058 {
00059    error_t error;
00060 
00061 #if (IPV4_SUPPORT == ENABLED)
00062    //Destination address is an IPv4 address?
00063    if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
00064    {
00065       //Form an IPv4 packet and send it
00066       error = ipv4SendDatagram(interface, &pseudoHeader->ipv4Data,
00067          buffer, offset, ttl);
00068    }
00069    else
00070 #endif
00071 #if (IPV6_SUPPORT == ENABLED)
00072    //Destination address is an IPv6 address?
00073    if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
00074    {
00075       //Form an IPv6 packet and send it
00076       error = ipv6SendDatagram(interface, &pseudoHeader->ipv6Data,
00077          buffer, offset, ttl);
00078    }
00079    else
00080 #endif
00081    //Destination address is invalid
00082    {
00083       //Report an error
00084       error = ERROR_INVALID_ADDRESS;
00085    }
00086 
00087    //Return status code
00088    return error;
00089 }
00090 
00091 
00092 /**
00093  * @brief IP source address selection
00094  *
00095  * This function selects the source address and the relevant network interface
00096  * to be used in order to join the specified destination address
00097  *
00098  * @param[in,out] interface A pointer to a valid network interface may be provided as
00099  *   a hint. The function returns a pointer identifying the interface to be used
00100  * @param[in] destAddr Destination IP address
00101  * @param[out] srcAddr Local IP address to be used
00102  * @return Error code
00103  **/
00104 
00105 error_t ipSelectSourceAddr(NetInterface **interface,
00106    const IpAddr *destAddr, IpAddr *srcAddr)
00107 {
00108 #if (IPV4_SUPPORT == ENABLED)
00109    //The destination address is an IPv4 address?
00110    if(destAddr->length == sizeof(Ipv4Addr))
00111    {
00112       //An IPv4 address is expected
00113       srcAddr->length = sizeof(Ipv4Addr);
00114       //Get the most appropriate source address to use
00115       return ipv4SelectSourceAddr(interface, destAddr->ipv4Addr, &srcAddr->ipv4Addr);
00116    }
00117    else
00118 #endif
00119 #if (IPV6_SUPPORT == ENABLED)
00120    //The destination address is an IPv6 address?
00121    if(destAddr->length == sizeof(Ipv6Addr))
00122    {
00123       //An IPv6 address is expected
00124       srcAddr->length = sizeof(Ipv6Addr);
00125       //Get the most appropriate source address to use
00126       return ipv6SelectSourceAddr(interface, &destAddr->ipv6Addr, &srcAddr->ipv6Addr);
00127    }
00128    else
00129 #endif
00130    //The destination address is not valid?
00131    {
00132       //Report an error
00133       return ERROR_INVALID_ADDRESS;
00134    }
00135 }
00136 
00137 
00138 /**
00139  * @brief IP checksum calculation
00140  * @param[in] data Pointer to the data over which to calculate the IP checksum
00141  * @param[in] length Number of bytes to process
00142  * @return Checksum value
00143  **/
00144 
00145 uint16_t ipCalcChecksum(const void *data, size_t length)
00146 {
00147    //Checksum preset value
00148    uint32_t checksum = 0x0000;
00149 
00150    //Process all the data
00151    while(length > 1)
00152    {
00153       //Update checksum value
00154       checksum += *((uint16_t *) data);
00155       //Point to the next 16-bit word
00156       data = (uint16_t *) data + 1;
00157       //Adjust the number of remaining words to process
00158       length -= 2;
00159    }
00160 
00161    //Add left-over byte, if any
00162    if(length > 0)
00163       checksum += *((uint8_t *) data);
00164 
00165    //Fold 32-bit sum to 16 bits
00166    while(checksum >> 16)
00167       checksum = (checksum & 0xFFFF) + (checksum >> 16);
00168 
00169    //Return 1's complement value
00170    return checksum ^ 0xFFFF;
00171 }
00172 
00173 
00174 /**
00175  * @brief Calculate IP checksum over a multi-part buffer
00176  * @param[in] buffer Pointer to the multi-part buffer
00177  * @param[in] offset Offset from the beginning of the buffer
00178  * @param[in] length Number of bytes to process
00179  * @return Checksum value
00180  **/
00181 
00182 uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length)
00183 {
00184    uint_t i;
00185    uint_t m;
00186    uint_t n;
00187    bool_t odd;
00188    uint8_t *data;
00189    uint32_t checksum;
00190 
00191    //Checksum preset value
00192    checksum = 0x0000;
00193    //Total number of bytes processed
00194    n = 0;
00195 
00196    //Loop through data chunks
00197    for(i = 0; i < buffer->chunkCount && n < length; i++)
00198    {
00199       //Is there any data to process in the current chunk?
00200       if(offset < buffer->chunk[i].length)
00201       {
00202          //Check whether the total number of bytes already processed is odd
00203          odd = (n & 1) ? TRUE : FALSE;
00204 
00205          //Point to the first data byte
00206          data = (uint8_t *) buffer->chunk[i].address + offset;
00207 
00208          //Number of bytes available in the current chunk
00209          m = buffer->chunk[i].length - offset;
00210          //Limit the number of byte to process
00211          m = MIN(m, length - n);
00212 
00213          //Now adjust the total length
00214          n += m;
00215 
00216          //Data buffer is not aligned on 16-bit boundaries?
00217          if((uint_t) data & 1)
00218          {
00219             //The total number of bytes is even?
00220             if(!odd)
00221             {
00222                //Fold 32-bit sum to 16 bits
00223                while(checksum >> 16)
00224                   checksum = (checksum & 0xFFFF) + (checksum >> 16);
00225                //Swap checksum value
00226                checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF;
00227             }
00228 
00229             //Restore the alignment on 16-bit boundaries
00230             if(m > 0)
00231             {
00232 #ifdef _CPU_BIG_ENDIAN
00233                //Update checksum value
00234                checksum += *data;
00235 #else
00236                //Update checksum value
00237                checksum += *data << 8;
00238 #endif
00239                //Point to the next byte
00240                data += 1;
00241                m -= 1;
00242             }
00243 
00244             //Process the data 2 bytes at a time
00245             while(m > 1)
00246             {
00247                //Update checksum value
00248                checksum += *((uint16_t *) data);
00249                //Point to the next 16-bit word
00250                data += 2;
00251                m -= 2;
00252             }
00253 
00254             //Add left-over byte, if any
00255             if(m > 0)
00256             {
00257 #ifdef _CPU_BIG_ENDIAN
00258                //Update checksum value
00259                checksum += *data << 8;
00260 #else
00261                //Update checksum value
00262                checksum += *data;
00263 #endif
00264             }
00265 
00266             //Restore checksum endianness
00267             if(!odd)
00268             {
00269                //Fold 32-bit sum to 16 bits
00270                while(checksum >> 16)
00271                   checksum = (checksum & 0xFFFF) + (checksum >> 16);
00272                //Swap checksum value
00273                checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF;
00274             }
00275          }
00276          //Data buffer is aligned on 16-bit boundaries?
00277          else
00278          {
00279             //The total number of bytes is odd?
00280             if(odd)
00281             {
00282                //Fold 32-bit sum to 16 bits
00283                while(checksum >> 16)
00284                   checksum = (checksum & 0xFFFF) + (checksum >> 16);
00285                //Swap checksum value
00286                checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF;
00287             }
00288 
00289             //Process the data 2 bytes at a time
00290             while(m > 1)
00291             {
00292                //Update checksum value
00293                checksum += *((uint16_t *) data);
00294                //Point to the next 16-bit word
00295                data += 2;
00296                m -= 2;
00297             }
00298 
00299             //Add left-over byte, if any
00300             if(m > 0)
00301             {
00302 #ifdef _CPU_BIG_ENDIAN
00303                //Update checksum value
00304                checksum += *data << 8;
00305 #else
00306                //Update checksum value
00307                checksum += *data;
00308 #endif
00309             }
00310 
00311             //Restore checksum endianness
00312             if(odd)
00313             {
00314                //Fold 32-bit sum to 16 bits
00315                while(checksum >> 16)
00316                   checksum = (checksum & 0xFFFF) + (checksum >> 16);
00317                //Swap checksum value
00318                checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF;
00319             }
00320          }
00321 
00322          //Process the next block from the start
00323          offset = 0;
00324       }
00325       else
00326       {
00327          //Skip the current chunk
00328          offset -= buffer->chunk[i].length;
00329       }
00330    }
00331 
00332    //Fold 32-bit sum to 16 bits
00333    while(checksum >> 16)
00334       checksum = (checksum & 0xFFFF) + (checksum >> 16);
00335 
00336    //Return 1's complement value
00337    return checksum ^ 0xFFFF;
00338 }
00339 
00340 
00341 /**
00342  * @brief Calculate IP upper-layer checksum
00343  * @param[in] pseudoHeader Pointer to the pseudo header
00344  * @param[in] pseudoHeaderLength Pseudo header length
00345  * @param[in] data Pointer to the upper-layer data
00346  * @param[in] dataLength Upper-layer data length
00347  * @return Checksum value
00348  **/
00349 
00350 uint16_t ipCalcUpperLayerChecksum(const void *pseudoHeader,
00351    size_t pseudoHeaderLength, const void *data, size_t dataLength)
00352 {
00353    //Checksum preset value
00354    uint32_t checksum = 0x0000;
00355 
00356    //Process pseudo header
00357    while(pseudoHeaderLength > 1)
00358    {
00359       //Update checksum value
00360       checksum += *((uint16_t *) pseudoHeader);
00361       //Point to the next 16-bit word
00362       pseudoHeader = (uint16_t *) pseudoHeader + 1;
00363       //Adjust the number of remaining words to process
00364       pseudoHeaderLength -= 2;
00365    }
00366 
00367    //Process upper-layer data
00368    while(dataLength > 1)
00369    {
00370       //Update checksum value
00371       checksum += *((uint16_t *) data);
00372       //Point to the next 16-bit word
00373       data = (uint16_t *) data + 1;
00374       //Adjust the number of remaining words to process
00375       dataLength -= 2;
00376    }
00377 
00378    //Add left-over byte, if any
00379    if(dataLength > 0)
00380    {
00381 #ifdef _CPU_BIG_ENDIAN
00382       //Update checksum value
00383       checksum += *((uint8_t *) data) << 8;
00384 #else
00385       //Update checksum value
00386       checksum += *((uint8_t *) data);
00387 #endif
00388    }
00389 
00390    //Fold 32-bit sum to 16 bits
00391    while(checksum >> 16)
00392       checksum = (checksum & 0xFFFF) + (checksum >> 16);
00393 
00394    //Return 1's complement value
00395    return checksum ^ 0xFFFF;
00396 }
00397 
00398 
00399 /**
00400  * @brief Calculate IP upper-layer checksum over a multi-part buffer
00401  * @param[in] pseudoHeader Pointer to the pseudo header
00402  * @param[in] pseudoHeaderLength Pseudo header length
00403  * @param[in] buffer Multi-part buffer containing the upper-layer data
00404  * @param[in] offset Offset from the first data byte to process
00405  * @param[in] length Number of data bytes to process
00406  * @return Checksum value
00407  **/
00408 
00409 uint16_t ipCalcUpperLayerChecksumEx(const void *pseudoHeader,
00410    size_t pseudoHeaderLength, const NetBuffer *buffer, size_t offset, size_t length)
00411 {
00412    uint32_t checksum;
00413 
00414    //Process upper-layer data
00415    checksum = ipCalcChecksumEx(buffer, offset, length);
00416    //Calculate 1's complement value
00417    checksum = checksum ^ 0xFFFF;
00418 
00419    //Process pseudo header
00420    while(pseudoHeaderLength > 1)
00421    {
00422       //Update checksum value
00423       checksum += *((uint16_t *) pseudoHeader);
00424       //Point to the next 16-bit word
00425       pseudoHeader = (uint16_t *) pseudoHeader + 1;
00426       //Adjust the number of remaining words to process
00427       pseudoHeaderLength -= 2;
00428    }
00429 
00430    //Fold 32-bit sum to 16 bits
00431    while(checksum >> 16)
00432       checksum = (checksum & 0xFFFF) + (checksum >> 16);
00433 
00434    //Return 1's complement value
00435    return checksum ^ 0xFFFF;
00436 }
00437 
00438 
00439 /**
00440  * @brief Allocate a buffer to hold an IP packet
00441  * @param[in] length Desired payload length
00442  * @param[out] offset Offset to the first byte of the payload
00443  * @return The function returns a pointer to the newly allocated
00444  *   buffer. If the system is out of resources, NULL is returned
00445  **/
00446 
00447 NetBuffer *ipAllocBuffer(size_t length, size_t *offset)
00448 {
00449    size_t headerLength;
00450    NetBuffer *buffer;
00451 
00452 #if (IPV6_SUPPORT == ENABLED)
00453    //Maximum overhead when using IPv6
00454    headerLength = sizeof(Ipv6Header) + sizeof(Ipv6FragmentHeader);
00455 #else
00456    //Maximum overhead when using IPv4
00457    headerLength = sizeof(Ipv4Header);
00458 #endif
00459 
00460 #if (ETH_SUPPORT == ENABLED)
00461    //Allocate a buffer to hold the Ethernet header and the IP packet
00462    buffer = ethAllocBuffer(length + headerLength, offset);
00463 #elif (PPP_SUPPORT == ENABLED)
00464    //Allocate a buffer to hold the PPP header and the IP packet
00465    buffer = pppAllocBuffer(length + headerLength, offset);
00466 #else
00467    //Allocate a buffer to hold the IP packet
00468    buffer = netBufferAlloc(length + headerLength);
00469    //Clear offset value
00470    *offset = 0;
00471 #endif
00472 
00473    //Successful memory allocation?
00474    if(buffer != NULL)
00475    {
00476       //Offset to the first byte of the payload
00477       *offset += headerLength;
00478    }
00479 
00480    //Return a pointer to the freshly allocated buffer
00481    return buffer;
00482 }
00483 
00484 
00485 /**
00486  * @brief Join the specified host group
00487  * @param[in] interface Underlying network interface (optional parameter)
00488  * @param[in] groupAddr IP address identifying the host group to join
00489  * @return Error code
00490  **/
00491 
00492 error_t ipJoinMulticastGroup(NetInterface *interface, const IpAddr *groupAddr)
00493 {
00494    error_t error;
00495 
00496    //Use default network interface?
00497    if(interface == NULL)
00498       interface = netGetDefaultInterface();
00499 
00500    //Get exclusive access
00501    osAcquireMutex(&netMutex);
00502 
00503 #if (IPV4_SUPPORT == ENABLED)
00504    //IPv4 multicast address?
00505    if(groupAddr->length == sizeof(Ipv4Addr))
00506    {
00507       //Join the specified host group
00508       error = ipv4JoinMulticastGroup(interface, groupAddr->ipv4Addr);
00509    }
00510    else
00511 #endif
00512 #if (IPV6_SUPPORT == ENABLED)
00513    //IPv6 multicast address?
00514    if(groupAddr->length == sizeof(Ipv6Addr))
00515    {
00516       //Join the specified host group
00517       error = ipv6JoinMulticastGroup(interface, &groupAddr->ipv6Addr);
00518    }
00519    else
00520 #endif
00521    //Invalid IP address?
00522    {
00523       //Report an error
00524       error = ERROR_INVALID_ADDRESS;
00525    }
00526 
00527    //Release exclusive access
00528    osReleaseMutex(&netMutex);
00529 
00530    //Return status code
00531    return error;
00532 }
00533 
00534 
00535 /**
00536  * @brief Leave the specified host group
00537  * @param[in] interface Underlying network interface (optional parameter)
00538  * @param[in] groupAddr IP address identifying the host group to leave
00539  * @return Error code
00540  **/
00541 
00542 error_t ipLeaveMulticastGroup(NetInterface *interface, const IpAddr *groupAddr)
00543 {
00544    //Use default network interface?
00545    if(interface == NULL)
00546       interface = netGetDefaultInterface();
00547 
00548 #if (IPV4_SUPPORT == ENABLED)
00549    //IPv4 multicast address?
00550    if(groupAddr->length == sizeof(Ipv4Addr))
00551    {
00552       //Drop membership
00553       return ipv4LeaveMulticastGroup(interface, groupAddr->ipv4Addr);
00554    }
00555    else
00556 #endif
00557 #if (IPV6_SUPPORT == ENABLED)
00558    //IPv6 multicast address?
00559    if(groupAddr->length == sizeof(Ipv6Addr))
00560    {
00561       //Drop membership
00562       return ipv6LeaveMulticastGroup(interface, &groupAddr->ipv6Addr);
00563    }
00564    else
00565 #endif
00566    //Invalid IP address?
00567    {
00568       return ERROR_INVALID_ADDRESS;
00569    }
00570 }
00571 
00572 
00573 /**
00574  * @brief Compare an IP address against the unspecified address
00575  * @param[in] ipAddr IP address
00576  * @return TRUE if the IP address is unspecified, else FALSE
00577  **/
00578 
00579 bool_t ipIsUnspecifiedAddr(const IpAddr *ipAddr)
00580 {
00581 #if (IPV4_SUPPORT == ENABLED)
00582    //IPv4 address?
00583    if(ipAddr->length == sizeof(Ipv4Addr))
00584    {
00585       //Compare IPv4 address
00586       return (ipAddr->ipv4Addr == IPV4_UNSPECIFIED_ADDR) ? TRUE : FALSE;
00587    }
00588    else
00589 #endif
00590 #if (IPV6_SUPPORT == ENABLED)
00591    //IPv6 address?
00592    if(ipAddr->length == sizeof(Ipv6Addr))
00593    {
00594       //Compare IPv6 address
00595       return ipv6CompAddr(&ipAddr->ipv6Addr, &IPV6_UNSPECIFIED_ADDR);
00596    }
00597    else
00598 #endif
00599    //Invalid IP address?
00600    {
00601       return FALSE;
00602    }
00603 }
00604 
00605 
00606 /**
00607  * @brief Convert a string representation of an IP address to a binary IP address
00608  * @param[in] str NULL-terminated string representing the IP address
00609  * @param[out] ipAddr Binary representation of the IP address
00610  * @return Error code
00611  **/
00612 
00613 error_t ipStringToAddr(const char_t *str, IpAddr *ipAddr)
00614 {
00615 #if (IPV6_SUPPORT == ENABLED)
00616    //IPv6 address?
00617    if(strchr(str, ':'))
00618    {
00619       //IPv6 addresses are 16-byte long
00620       ipAddr->length = sizeof(Ipv6Addr);
00621       //Convert the string to IPv6 address
00622       return ipv6StringToAddr(str, &ipAddr->ipv6Addr);
00623    }
00624    else
00625 #endif
00626 #if (IPV4_SUPPORT == ENABLED)
00627    //IPv4 address?
00628    if(strchr(str, '.'))
00629    {
00630       //IPv4 addresses are 4-byte long
00631       ipAddr->length = sizeof(Ipv4Addr);
00632       //Convert the string to IPv4 address
00633       return ipv4StringToAddr(str, &ipAddr->ipv4Addr);
00634    }
00635    else
00636 #endif
00637    //Invalid IP address?
00638    {
00639       //Report an error
00640       return ERROR_FAILURE;
00641    }
00642 }
00643 
00644 
00645 /**
00646  * @brief Convert a binary IP address to a string representation
00647  * @param[in] ipAddr Binary representation of the IP address
00648  * @param[out] str NULL-terminated string representing the IP address
00649  * @return Pointer to the formatted string
00650  **/
00651 
00652 char_t *ipAddrToString(const IpAddr *ipAddr, char_t *str)
00653 {
00654 #if (IPV4_SUPPORT == ENABLED)
00655    //IPv4 address?
00656    if(ipAddr->length == sizeof(Ipv4Addr))
00657    {
00658       //Convert IPv4 address to string representation
00659       return ipv4AddrToString(ipAddr->ipv4Addr, str);
00660    }
00661    else
00662 #endif
00663 #if (IPV6_SUPPORT == ENABLED)
00664    //IPv6 address?
00665    if(ipAddr->length == sizeof(Ipv6Addr))
00666    {
00667       //Convert IPv6 address to string representation
00668       return ipv6AddrToString(&ipAddr->ipv6Addr, str);
00669    }
00670    else
00671 #endif
00672    //Invalid IP address?
00673    {
00674       static char_t c;
00675       //The str parameter is optional
00676       if(!str) str = &c;
00677       //Properly terminate the string
00678       str[0] = '\0';
00679       //Return an empty string
00680       return str;
00681    }
00682 }
00683