Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
ip.c
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
Generated on Tue Jul 12 2022 17:10:13 by
