Webserver+3d print
Diff: cyclone_tcp/core/ip.c
- Revision:
- 0:8918a71cdbe9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cyclone_tcp/core/ip.c Sat Feb 04 18:15:49 2017 +0000 @@ -0,0 +1,683 @@ +/** + * @file ip.c + * @brief IPv4 and IPv6 common routines + * + * @section License + * + * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. + * + * This file is part of CycloneTCP Open. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * @author Oryx Embedded SARL (www.oryx-embedded.com) + * @version 1.7.6 + **/ + +//Switch to the appropriate trace level +#define TRACE_LEVEL IP_TRACE_LEVEL + +//Dependencies +#include "core/net.h" +#include "core/ethernet.h" +#include "core/ip.h" +#include "ipv4/ipv4.h" +#include "ipv6/ipv6.h" +#include "ipv6/ipv6_misc.h" +#include "debug.h" + +//Special IP address +const IpAddr IP_ADDR_ANY = {0}; +const IpAddr IP_ADDR_UNSPECIFIED = {0}; + + +/** + * @brief Send an IP datagram + * @param[in] interface Underlying network interface + * @param[in] pseudoHeader IP pseudo header + * @param[in] buffer Multi-part buffer containing the payload + * @param[in] offset Offset to the first payload byte + * @param[in] ttl TTL value. Default Time-To-Live is used when this parameter is zero + * @return Error code + **/ + +error_t ipSendDatagram(NetInterface *interface, IpPseudoHeader *pseudoHeader, + NetBuffer *buffer, size_t offset, uint8_t ttl) +{ + error_t error; + +#if (IPV4_SUPPORT == ENABLED) + //Destination address is an IPv4 address? + if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) + { + //Form an IPv4 packet and send it + error = ipv4SendDatagram(interface, &pseudoHeader->ipv4Data, + buffer, offset, ttl); + } + else +#endif +#if (IPV6_SUPPORT == ENABLED) + //Destination address is an IPv6 address? + if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) + { + //Form an IPv6 packet and send it + error = ipv6SendDatagram(interface, &pseudoHeader->ipv6Data, + buffer, offset, ttl); + } + else +#endif + //Destination address is invalid + { + //Report an error + error = ERROR_INVALID_ADDRESS; + } + + //Return status code + return error; +} + + +/** + * @brief IP source address selection + * + * This function selects the source address and the relevant network interface + * to be used in order to join the specified destination address + * + * @param[in,out] interface A pointer to a valid network interface may be provided as + * a hint. The function returns a pointer identifying the interface to be used + * @param[in] destAddr Destination IP address + * @param[out] srcAddr Local IP address to be used + * @return Error code + **/ + +error_t ipSelectSourceAddr(NetInterface **interface, + const IpAddr *destAddr, IpAddr *srcAddr) +{ +#if (IPV4_SUPPORT == ENABLED) + //The destination address is an IPv4 address? + if(destAddr->length == sizeof(Ipv4Addr)) + { + //An IPv4 address is expected + srcAddr->length = sizeof(Ipv4Addr); + //Get the most appropriate source address to use + return ipv4SelectSourceAddr(interface, destAddr->ipv4Addr, &srcAddr->ipv4Addr); + } + else +#endif +#if (IPV6_SUPPORT == ENABLED) + //The destination address is an IPv6 address? + if(destAddr->length == sizeof(Ipv6Addr)) + { + //An IPv6 address is expected + srcAddr->length = sizeof(Ipv6Addr); + //Get the most appropriate source address to use + return ipv6SelectSourceAddr(interface, &destAddr->ipv6Addr, &srcAddr->ipv6Addr); + } + else +#endif + //The destination address is not valid? + { + //Report an error + return ERROR_INVALID_ADDRESS; + } +} + + +/** + * @brief IP checksum calculation + * @param[in] data Pointer to the data over which to calculate the IP checksum + * @param[in] length Number of bytes to process + * @return Checksum value + **/ + +uint16_t ipCalcChecksum(const void *data, size_t length) +{ + //Checksum preset value + uint32_t checksum = 0x0000; + + //Process all the data + while(length > 1) + { + //Update checksum value + checksum += *((uint16_t *) data); + //Point to the next 16-bit word + data = (uint16_t *) data + 1; + //Adjust the number of remaining words to process + length -= 2; + } + + //Add left-over byte, if any + if(length > 0) + checksum += *((uint8_t *) data); + + //Fold 32-bit sum to 16 bits + while(checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + + //Return 1's complement value + return checksum ^ 0xFFFF; +} + + +/** + * @brief Calculate IP checksum over a multi-part buffer + * @param[in] buffer Pointer to the multi-part buffer + * @param[in] offset Offset from the beginning of the buffer + * @param[in] length Number of bytes to process + * @return Checksum value + **/ + +uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length) +{ + uint_t i; + uint_t m; + uint_t n; + bool_t odd; + uint8_t *data; + uint32_t checksum; + + //Checksum preset value + checksum = 0x0000; + //Total number of bytes processed + n = 0; + + //Loop through data chunks + for(i = 0; i < buffer->chunkCount && n < length; i++) + { + //Is there any data to process in the current chunk? + if(offset < buffer->chunk[i].length) + { + //Check whether the total number of bytes already processed is odd + odd = (n & 1) ? TRUE : FALSE; + + //Point to the first data byte + data = (uint8_t *) buffer->chunk[i].address + offset; + + //Number of bytes available in the current chunk + m = buffer->chunk[i].length - offset; + //Limit the number of byte to process + m = MIN(m, length - n); + + //Now adjust the total length + n += m; + + //Data buffer is not aligned on 16-bit boundaries? + if((uint_t) data & 1) + { + //The total number of bytes is even? + if(!odd) + { + //Fold 32-bit sum to 16 bits + while(checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + //Swap checksum value + checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF; + } + + //Restore the alignment on 16-bit boundaries + if(m > 0) + { +#ifdef _CPU_BIG_ENDIAN + //Update checksum value + checksum += *data; +#else + //Update checksum value + checksum += *data << 8; +#endif + //Point to the next byte + data += 1; + m -= 1; + } + + //Process the data 2 bytes at a time + while(m > 1) + { + //Update checksum value + checksum += *((uint16_t *) data); + //Point to the next 16-bit word + data += 2; + m -= 2; + } + + //Add left-over byte, if any + if(m > 0) + { +#ifdef _CPU_BIG_ENDIAN + //Update checksum value + checksum += *data << 8; +#else + //Update checksum value + checksum += *data; +#endif + } + + //Restore checksum endianness + if(!odd) + { + //Fold 32-bit sum to 16 bits + while(checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + //Swap checksum value + checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF; + } + } + //Data buffer is aligned on 16-bit boundaries? + else + { + //The total number of bytes is odd? + if(odd) + { + //Fold 32-bit sum to 16 bits + while(checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + //Swap checksum value + checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF; + } + + //Process the data 2 bytes at a time + while(m > 1) + { + //Update checksum value + checksum += *((uint16_t *) data); + //Point to the next 16-bit word + data += 2; + m -= 2; + } + + //Add left-over byte, if any + if(m > 0) + { +#ifdef _CPU_BIG_ENDIAN + //Update checksum value + checksum += *data << 8; +#else + //Update checksum value + checksum += *data; +#endif + } + + //Restore checksum endianness + if(odd) + { + //Fold 32-bit sum to 16 bits + while(checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + //Swap checksum value + checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF; + } + } + + //Process the next block from the start + offset = 0; + } + else + { + //Skip the current chunk + offset -= buffer->chunk[i].length; + } + } + + //Fold 32-bit sum to 16 bits + while(checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + + //Return 1's complement value + return checksum ^ 0xFFFF; +} + + +/** + * @brief Calculate IP upper-layer checksum + * @param[in] pseudoHeader Pointer to the pseudo header + * @param[in] pseudoHeaderLength Pseudo header length + * @param[in] data Pointer to the upper-layer data + * @param[in] dataLength Upper-layer data length + * @return Checksum value + **/ + +uint16_t ipCalcUpperLayerChecksum(const void *pseudoHeader, + size_t pseudoHeaderLength, const void *data, size_t dataLength) +{ + //Checksum preset value + uint32_t checksum = 0x0000; + + //Process pseudo header + while(pseudoHeaderLength > 1) + { + //Update checksum value + checksum += *((uint16_t *) pseudoHeader); + //Point to the next 16-bit word + pseudoHeader = (uint16_t *) pseudoHeader + 1; + //Adjust the number of remaining words to process + pseudoHeaderLength -= 2; + } + + //Process upper-layer data + while(dataLength > 1) + { + //Update checksum value + checksum += *((uint16_t *) data); + //Point to the next 16-bit word + data = (uint16_t *) data + 1; + //Adjust the number of remaining words to process + dataLength -= 2; + } + + //Add left-over byte, if any + if(dataLength > 0) + { +#ifdef _CPU_BIG_ENDIAN + //Update checksum value + checksum += *((uint8_t *) data) << 8; +#else + //Update checksum value + checksum += *((uint8_t *) data); +#endif + } + + //Fold 32-bit sum to 16 bits + while(checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + + //Return 1's complement value + return checksum ^ 0xFFFF; +} + + +/** + * @brief Calculate IP upper-layer checksum over a multi-part buffer + * @param[in] pseudoHeader Pointer to the pseudo header + * @param[in] pseudoHeaderLength Pseudo header length + * @param[in] buffer Multi-part buffer containing the upper-layer data + * @param[in] offset Offset from the first data byte to process + * @param[in] length Number of data bytes to process + * @return Checksum value + **/ + +uint16_t ipCalcUpperLayerChecksumEx(const void *pseudoHeader, + size_t pseudoHeaderLength, const NetBuffer *buffer, size_t offset, size_t length) +{ + uint32_t checksum; + + //Process upper-layer data + checksum = ipCalcChecksumEx(buffer, offset, length); + //Calculate 1's complement value + checksum = checksum ^ 0xFFFF; + + //Process pseudo header + while(pseudoHeaderLength > 1) + { + //Update checksum value + checksum += *((uint16_t *) pseudoHeader); + //Point to the next 16-bit word + pseudoHeader = (uint16_t *) pseudoHeader + 1; + //Adjust the number of remaining words to process + pseudoHeaderLength -= 2; + } + + //Fold 32-bit sum to 16 bits + while(checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + + //Return 1's complement value + return checksum ^ 0xFFFF; +} + + +/** + * @brief Allocate a buffer to hold an IP packet + * @param[in] length Desired payload length + * @param[out] offset Offset to the first byte of the payload + * @return The function returns a pointer to the newly allocated + * buffer. If the system is out of resources, NULL is returned + **/ + +NetBuffer *ipAllocBuffer(size_t length, size_t *offset) +{ + size_t headerLength; + NetBuffer *buffer; + +#if (IPV6_SUPPORT == ENABLED) + //Maximum overhead when using IPv6 + headerLength = sizeof(Ipv6Header) + sizeof(Ipv6FragmentHeader); +#else + //Maximum overhead when using IPv4 + headerLength = sizeof(Ipv4Header); +#endif + +#if (ETH_SUPPORT == ENABLED) + //Allocate a buffer to hold the Ethernet header and the IP packet + buffer = ethAllocBuffer(length + headerLength, offset); +#elif (PPP_SUPPORT == ENABLED) + //Allocate a buffer to hold the PPP header and the IP packet + buffer = pppAllocBuffer(length + headerLength, offset); +#else + //Allocate a buffer to hold the IP packet + buffer = netBufferAlloc(length + headerLength); + //Clear offset value + *offset = 0; +#endif + + //Successful memory allocation? + if(buffer != NULL) + { + //Offset to the first byte of the payload + *offset += headerLength; + } + + //Return a pointer to the freshly allocated buffer + return buffer; +} + + +/** + * @brief Join the specified host group + * @param[in] interface Underlying network interface (optional parameter) + * @param[in] groupAddr IP address identifying the host group to join + * @return Error code + **/ + +error_t ipJoinMulticastGroup(NetInterface *interface, const IpAddr *groupAddr) +{ + error_t error; + + //Use default network interface? + if(interface == NULL) + interface = netGetDefaultInterface(); + + //Get exclusive access + osAcquireMutex(&netMutex); + +#if (IPV4_SUPPORT == ENABLED) + //IPv4 multicast address? + if(groupAddr->length == sizeof(Ipv4Addr)) + { + //Join the specified host group + error = ipv4JoinMulticastGroup(interface, groupAddr->ipv4Addr); + } + else +#endif +#if (IPV6_SUPPORT == ENABLED) + //IPv6 multicast address? + if(groupAddr->length == sizeof(Ipv6Addr)) + { + //Join the specified host group + error = ipv6JoinMulticastGroup(interface, &groupAddr->ipv6Addr); + } + else +#endif + //Invalid IP address? + { + //Report an error + error = ERROR_INVALID_ADDRESS; + } + + //Release exclusive access + osReleaseMutex(&netMutex); + + //Return status code + return error; +} + + +/** + * @brief Leave the specified host group + * @param[in] interface Underlying network interface (optional parameter) + * @param[in] groupAddr IP address identifying the host group to leave + * @return Error code + **/ + +error_t ipLeaveMulticastGroup(NetInterface *interface, const IpAddr *groupAddr) +{ + //Use default network interface? + if(interface == NULL) + interface = netGetDefaultInterface(); + +#if (IPV4_SUPPORT == ENABLED) + //IPv4 multicast address? + if(groupAddr->length == sizeof(Ipv4Addr)) + { + //Drop membership + return ipv4LeaveMulticastGroup(interface, groupAddr->ipv4Addr); + } + else +#endif +#if (IPV6_SUPPORT == ENABLED) + //IPv6 multicast address? + if(groupAddr->length == sizeof(Ipv6Addr)) + { + //Drop membership + return ipv6LeaveMulticastGroup(interface, &groupAddr->ipv6Addr); + } + else +#endif + //Invalid IP address? + { + return ERROR_INVALID_ADDRESS; + } +} + + +/** + * @brief Compare an IP address against the unspecified address + * @param[in] ipAddr IP address + * @return TRUE if the IP address is unspecified, else FALSE + **/ + +bool_t ipIsUnspecifiedAddr(const IpAddr *ipAddr) +{ +#if (IPV4_SUPPORT == ENABLED) + //IPv4 address? + if(ipAddr->length == sizeof(Ipv4Addr)) + { + //Compare IPv4 address + return (ipAddr->ipv4Addr == IPV4_UNSPECIFIED_ADDR) ? TRUE : FALSE; + } + else +#endif +#if (IPV6_SUPPORT == ENABLED) + //IPv6 address? + if(ipAddr->length == sizeof(Ipv6Addr)) + { + //Compare IPv6 address + return ipv6CompAddr(&ipAddr->ipv6Addr, &IPV6_UNSPECIFIED_ADDR); + } + else +#endif + //Invalid IP address? + { + return FALSE; + } +} + + +/** + * @brief Convert a string representation of an IP address to a binary IP address + * @param[in] str NULL-terminated string representing the IP address + * @param[out] ipAddr Binary representation of the IP address + * @return Error code + **/ + +error_t ipStringToAddr(const char_t *str, IpAddr *ipAddr) +{ +#if (IPV6_SUPPORT == ENABLED) + //IPv6 address? + if(strchr(str, ':')) + { + //IPv6 addresses are 16-byte long + ipAddr->length = sizeof(Ipv6Addr); + //Convert the string to IPv6 address + return ipv6StringToAddr(str, &ipAddr->ipv6Addr); + } + else +#endif +#if (IPV4_SUPPORT == ENABLED) + //IPv4 address? + if(strchr(str, '.')) + { + //IPv4 addresses are 4-byte long + ipAddr->length = sizeof(Ipv4Addr); + //Convert the string to IPv4 address + return ipv4StringToAddr(str, &ipAddr->ipv4Addr); + } + else +#endif + //Invalid IP address? + { + //Report an error + return ERROR_FAILURE; + } +} + + +/** + * @brief Convert a binary IP address to a string representation + * @param[in] ipAddr Binary representation of the IP address + * @param[out] str NULL-terminated string representing the IP address + * @return Pointer to the formatted string + **/ + +char_t *ipAddrToString(const IpAddr *ipAddr, char_t *str) +{ +#if (IPV4_SUPPORT == ENABLED) + //IPv4 address? + if(ipAddr->length == sizeof(Ipv4Addr)) + { + //Convert IPv4 address to string representation + return ipv4AddrToString(ipAddr->ipv4Addr, str); + } + else +#endif +#if (IPV6_SUPPORT == ENABLED) + //IPv6 address? + if(ipAddr->length == sizeof(Ipv6Addr)) + { + //Convert IPv6 address to string representation + return ipv6AddrToString(&ipAddr->ipv6Addr, str); + } + else +#endif + //Invalid IP address? + { + static char_t c; + //The str parameter is optional + if(!str) str = &c; + //Properly terminate the string + str[0] = '\0'; + //Return an empty string + return str; + } +} +