Webserver+3d print
cyclone_tcp/ipv4/ipv4.c
- Committer:
- Sergunb
- Date:
- 2017-02-04
- Revision:
- 0:8918a71cdbe9
File content as of revision 0:8918a71cdbe9:
/** * @file ipv4.c * @brief IPv4 (Internet Protocol Version 4) * * @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. * * @section Description * * The Internet Protocol (IP) provides the functions necessary to deliver a * datagram from a source to a destination over an interconnected system of * networks. Refer to RFC 791 for complete details * * @author Oryx Embedded SARL (www.oryx-embedded.com) * @version 1.7.6 **/ //Switch to the appropriate trace level #define TRACE_LEVEL IPV4_TRACE_LEVEL //Dependencies #include <string.h> #include <ctype.h> #include "core/net.h" #include "core/ethernet.h" #include "core/ip.h" #include "core/udp.h" #include "core/tcp_fsm.h" #include "core/raw_socket.h" #include "ipv4/arp.h" #include "ipv4/ipv4.h" #include "ipv4/ipv4_routing.h" #include "ipv4/icmp.h" #include "ipv4/igmp.h" #include "ipv4/auto_ip.h" #include "dhcp/dhcp_client.h" #include "mdns/mdns_responder.h" #include "mibs/mib2_module.h" #include "debug.h" //Check TCP/IP stack configuration #if (IPV4_SUPPORT == ENABLED) /** * @brief IPv4 related initialization * @param[in] interface Underlying network interface * @return Error code **/ error_t ipv4Init(NetInterface *interface) { Ipv4Context *context; //Point to the IPv4 context context = &interface->ipv4Context; //Clear the IPv4 context memset(context, 0, sizeof(Ipv4Context)); //Initialize interface specific variables context->linkMtu = interface->nicDriver->mtu; context->isRouter = FALSE; //Identification field is primarily used to identify //fragments of an original IP datagram context->identification = 0; //Initialize the list of DNS servers memset(context->dnsServerList, 0, sizeof(context->dnsServerList)); //Initialize the multicast filter table memset(context->multicastFilter, 0, sizeof(context->multicastFilter)); #if (IPV4_FRAG_SUPPORT == ENABLED) //Initialize the reassembly queue memset(context->fragQueue, 0, sizeof(context->fragQueue)); #endif //Successful initialization return NO_ERROR; } /** * @brief Assign host address * @param[in] interface Pointer to the desired network interface * @param[in] addr IPv4 host address * @return Error code **/ error_t ipv4SetHostAddr(NetInterface *interface, Ipv4Addr addr) { //Check parameters if(interface == NULL) return ERROR_INVALID_PARAMETER; //The IPv4 address must be a valid unicast address if(ipv4IsMulticastAddr(addr)) return ERROR_INVALID_ADDRESS; //Get exclusive access osAcquireMutex(&netMutex); //Set up host address interface->ipv4Context.addr = addr; //Clear conflict flag interface->ipv4Context.addrConflict = FALSE; //Check whether the new host address is valid if(addr != IPV4_UNSPECIFIED_ADDR) { //The use of the IPv4 address is now unrestricted interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID; } else { //The IPv4 address is no longer valid interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID; } #if (MDNS_RESPONDER_SUPPORT == ENABLED) //Restart mDNS probing process mdnsResponderStartProbing(interface->mdnsResponderContext); #endif //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Retrieve host address * @param[in] interface Pointer to the desired network interface * @param[out] addr IPv4 host address * @return Error code **/ error_t ipv4GetHostAddr(NetInterface *interface, Ipv4Addr *addr) { //Check parameters if(interface == NULL || addr == NULL) return ERROR_INVALID_PARAMETER; //Get exclusive access osAcquireMutex(&netMutex); //Check whether the host address is valid if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID) { //Get IPv4 address *addr = interface->ipv4Context.addr; } else { //Return the unspecified address when no address has been assigned *addr = IPV4_UNSPECIFIED_ADDR; } //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Configure subnet mask * @param[in] interface Pointer to the desired network interface * @param[in] mask Subnet mask * @return Error code **/ error_t ipv4SetSubnetMask(NetInterface *interface, Ipv4Addr mask) { //Check parameters if(interface == NULL) return ERROR_INVALID_PARAMETER; //Get exclusive access osAcquireMutex(&netMutex); //Set up subnet mask interface->ipv4Context.subnetMask = mask; //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Retrieve subnet mask * @param[in] interface Pointer to the desired network interface * @param[out] mask Subnet mask * @return Error code **/ error_t ipv4GetSubnetMask(NetInterface *interface, Ipv4Addr *mask) { //Check parameters if(interface == NULL || mask == NULL) return ERROR_INVALID_PARAMETER; //Get exclusive access osAcquireMutex(&netMutex); //Get subnet mask *mask = interface->ipv4Context.subnetMask; //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Configure default gateway * @param[in] interface Pointer to the desired network interface * @param[in] addr Default gateway address * @return Error code **/ error_t ipv4SetDefaultGateway(NetInterface *interface, Ipv4Addr addr) { //Check parameters if(interface == NULL) return ERROR_INVALID_PARAMETER; //The IPv4 address must be a valid unicast address if(ipv4IsMulticastAddr(addr)) return ERROR_INVALID_ADDRESS; //Get exclusive access osAcquireMutex(&netMutex); //Set up default gateway address interface->ipv4Context.defaultGateway = addr; //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Retrieve default gateway * @param[in] interface Pointer to the desired network interface * @param[out] addr Default gateway address * @return Error code **/ error_t ipv4GetDefaultGateway(NetInterface *interface, Ipv4Addr *addr) { //Check parameters if(interface == NULL || addr == NULL) return ERROR_INVALID_PARAMETER; //Get exclusive access osAcquireMutex(&netMutex); //Get default gateway address *addr = interface->ipv4Context.defaultGateway; //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Configure DNS server * @param[in] interface Pointer to the desired network interface * @param[in] index This parameter selects between the primary and secondary DNS server * @param[in] addr DNS server address * @return Error code **/ error_t ipv4SetDnsServer(NetInterface *interface, uint_t index, Ipv4Addr addr) { //Check parameters if(interface == NULL) return ERROR_INVALID_PARAMETER; //Make sure that the index is valid if(index >= IPV4_DNS_SERVER_LIST_SIZE) return ERROR_OUT_OF_RANGE; //The IPv4 address must be a valid unicast address if(ipv4IsMulticastAddr(addr)) return ERROR_INVALID_ADDRESS; //Get exclusive access osAcquireMutex(&netMutex); //Set up DNS server address interface->ipv4Context.dnsServerList[index] = addr; //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Retrieve DNS server * @param[in] interface Pointer to the desired network interface * @param[in] index This parameter selects between the primary and secondary DNS server * @param[out] addr DNS server address * @return Error code **/ error_t ipv4GetDnsServer(NetInterface *interface, uint_t index, Ipv4Addr *addr) { //Check parameters if(interface == NULL || addr == NULL) return ERROR_INVALID_PARAMETER; //Make sure that the index is valid if(index >= IPV4_DNS_SERVER_LIST_SIZE) { //Return the unspecified address when the index is out of range *addr = IPV4_UNSPECIFIED_ADDR; //Report an error return ERROR_OUT_OF_RANGE; } //Get exclusive access osAcquireMutex(&netMutex); //Get DNS server address *addr = interface->ipv4Context.dnsServerList[index]; //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Get IPv4 broadcast address * @param[in] interface Pointer to the desired network interface * @param[out] addr IPv4 broadcast address **/ error_t ipv4GetBroadcastAddr(NetInterface *interface, Ipv4Addr *addr) { //Check parameters if(interface == NULL || addr == NULL) return ERROR_INVALID_PARAMETER; //The broadcast address is obtained by performing a bitwise OR operation //between the bit complement of the subnet mask and the host IP address *addr = interface->ipv4Context.addr; *addr |= ~interface->ipv4Context.subnetMask; //Successful processing return NO_ERROR; } /** * @brief Callback function for link change event * @param[in] interface Underlying network interface **/ void ipv4LinkChangeEvent(NetInterface *interface) { Ipv4Context *context; //Point to the IPv4 context context = &interface->ipv4Context; //Restore default MTU context->linkMtu = interface->nicDriver->mtu; #if (ETH_SUPPORT == ENABLED) //Flush ARP cache contents arpFlushCache(interface); #endif #if (IPV4_FRAG_SUPPORT == ENABLED) //Flush the reassembly queue ipv4FlushFragQueue(interface); #endif #if (IGMP_SUPPORT == ENABLED) //Notify IGMP of link state changes igmpLinkChangeEvent(interface); #endif #if (AUTO_IP_SUPPORT == ENABLED) //Notify Auto-IP of link state changes autoIpLinkChangeEvent(interface->autoIpContext); #endif #if (DHCP_CLIENT_SUPPORT == ENABLED) //Notify the DHCP client of link state changes dhcpClientLinkChangeEvent(interface->dhcpClientContext); #endif } /** * @brief Incoming IPv4 packet processing * @param[in] interface Underlying network interface * @param[in] packet Incoming IPv4 packet * @param[in] length Packet length including header and payload **/ void ipv4ProcessPacket(NetInterface *interface, Ipv4Header *packet, size_t length) { //Total number of input datagrams received from interfaces, //including those received in error MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInReceives, 1); //Ensure the packet length is greater than 20 bytes if(length < sizeof(Ipv4Header)) { //Number of input datagrams discarded due to errors in their IP headers MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1); //Discard the received packet return; } //Debug message TRACE_INFO("IPv4 packet received (%" PRIuSIZE " bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(packet); //A packet whose version number is not 4 must be silently discarded if(packet->version != IPV4_VERSION) { //Number of input datagrams discarded due to errors in their IP headers MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1); //Discard the received packet return; } //Valid IPv4 header shall contains more than five 32-bit words if(packet->headerLength < 5) { //Number of input datagrams discarded due to errors in their IP headers MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1); //Discard the received packet return; } //Ensure the total length is correct before processing the packet if(ntohs(packet->totalLength) < (packet->headerLength * 4)) { //Number of input datagrams discarded due to errors in their IP headers MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1); //Discard the received packet return; } if(ntohs(packet->totalLength) > length) { //Number of input datagrams discarded due to errors in their IP headers MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1); //Discard the received packet return; } //Source address filtering if(ipv4CheckSourceAddr(interface, packet->srcAddr)) { //Number of input datagrams discarded due to errors in their IP headers MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1); //Discard the received packet return; } #if defined(IPV4_PACKET_FORWARD_HOOK) IPV4_PACKET_FORWARD_HOOK(interface, packet, length); #else //Destination address filtering if(ipv4CheckDestAddr(interface, packet->destAddr)) { #if(IPV4_ROUTING_SUPPORT == ENABLED) NetBuffer1 buffer; //Unfragmented datagrams fit in a single chunk buffer.chunkCount = 1; buffer.maxChunkCount = 1; buffer.chunk[0].address = packet; buffer.chunk[0].length = length; //Forward the packet according to the routing table ipv4ForwardPacket(interface, (NetBuffer *) &buffer, 0); #else //Number of input datagrams discarded because the destination IP //address was not a valid address MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInAddrErrors, 1); #endif //We are done return; } #endif //Packets addressed to a tentative address should be silently discarded if(ipv4IsTentativeAddr(interface, packet->destAddr)) { //Number of input datagrams discarded because the destination IP //address was not a valid address MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInAddrErrors, 1); //Discard the received packet return; } //The host must verify the IP header checksum on every received //datagram and silently discard every datagram that has a bad //checksum (see RFC 1122 3.2.1.2) if(ipCalcChecksum(packet, packet->headerLength * 4) != 0x0000) { //Debug message TRACE_WARNING("Wrong IP header checksum!\r\n"); //Number of input datagrams discarded due to errors in their IP headers MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1); //Discard incoming packet return; } //Convert the total length from network byte order length = ntohs(packet->totalLength); //A fragmented packet was received? if(ntohs(packet->fragmentOffset) & (IPV4_FLAG_MF | IPV4_OFFSET_MASK)) { #if (IPV4_FRAG_SUPPORT == ENABLED) //Reassemble the original datagram ipv4ReassembleDatagram(interface, packet, length); #endif } else { NetBuffer1 buffer; //Unfragmented datagrams fit in a single chunk buffer.chunkCount = 1; buffer.maxChunkCount = 1; buffer.chunk[0].address = packet; buffer.chunk[0].length = length; //Pass the IPv4 datagram to the higher protocol layer ipv4ProcessDatagram(interface, (NetBuffer *) &buffer); } } /** * @brief Incoming IPv4 datagram processing * @param[in] interface Underlying network interface * @param[in] buffer Multi-part buffer that holds the incoming IPv4 datagram **/ void ipv4ProcessDatagram(NetInterface *interface, const NetBuffer *buffer) { error_t error; size_t offset; size_t length; Ipv4Header *header; IpPseudoHeader pseudoHeader; //Retrieve the length of the IPv4 datagram length = netBufferGetLength(buffer); //Point to the IPv4 header header = netBufferAt(buffer, 0); //Sanity check if(header == NULL) return; //Debug message TRACE_INFO("IPv4 datagram received (%" PRIuSIZE " bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(header); //Get the offset to the payload offset = header->headerLength * 4; //Compute the length of the payload length -= header->headerLength * 4; //Form the IPv4 pseudo header pseudoHeader.length = sizeof(Ipv4PseudoHeader); pseudoHeader.ipv4Data.srcAddr = header->srcAddr; pseudoHeader.ipv4Data.destAddr = header->destAddr; pseudoHeader.ipv4Data.reserved = 0; pseudoHeader.ipv4Data.protocol = header->protocol; pseudoHeader.ipv4Data.length = htons(length); #if defined(IPV4_DATAGRAM_FORWARD_HOOK) IPV4_DATAGRAM_FORWARD_HOOK(interface, &pseudoHeader, buffer, offset); #endif //Check the protocol field switch(header->protocol) { //ICMP protocol? case IPV4_PROTOCOL_ICMP: //Process incoming ICMP message icmpProcessMessage(interface, header->srcAddr, buffer, offset); #if (RAW_SOCKET_SUPPORT == ENABLED) //Allow raw sockets to process ICMP messages rawSocketProcessIpPacket(interface, &pseudoHeader, buffer, offset); #endif //No error to report error = NO_ERROR; //Continue processing break; #if (IGMP_SUPPORT == ENABLED) //IGMP protocol? case IPV4_PROTOCOL_IGMP: //Process incoming IGMP message igmpProcessMessage(interface, buffer, offset); #if (RAW_SOCKET_SUPPORT == ENABLED) //Allow raw sockets to process IGMP messages rawSocketProcessIpPacket(interface, &pseudoHeader, buffer, offset); #endif //No error to report error = NO_ERROR; //Continue processing break; #endif #if (TCP_SUPPORT == ENABLED) //TCP protocol? case IPV4_PROTOCOL_TCP: //Process incoming TCP segment tcpProcessSegment(interface, &pseudoHeader, buffer, offset); //No error to report error = NO_ERROR; //Continue processing break; #endif #if (UDP_SUPPORT == ENABLED) //UDP protocol? case IPV4_PROTOCOL_UDP: //Process incoming UDP datagram error = udpProcessDatagram(interface, &pseudoHeader, buffer, offset); //Continue processing break; #endif //Unknown protocol? default: #if (RAW_SOCKET_SUPPORT == ENABLED) //Allow raw sockets to process IPv4 packets error = rawSocketProcessIpPacket(interface, &pseudoHeader, buffer, offset); #else //Report an error error = ERROR_PROTOCOL_UNREACHABLE; #endif //Continue processing break; } //Unreachable protocol? if(error == ERROR_PROTOCOL_UNREACHABLE) { //Number of locally-addressed datagrams received successfully but //discarded because of an unknown or unsupported protocol MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInUnknownProtos, 1); //Send a Destination Unreachable message icmpSendErrorMessage(interface, ICMP_TYPE_DEST_UNREACHABLE, ICMP_CODE_PROTOCOL_UNREACHABLE, 0, buffer, 0); } else { //Total number of input datagrams successfully delivered to IP //user-protocols MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInDelivers, 1); } //Unreachable port? if(error == ERROR_PORT_UNREACHABLE) { //Send a Destination Unreachable message icmpSendErrorMessage(interface, ICMP_TYPE_DEST_UNREACHABLE, ICMP_CODE_PORT_UNREACHABLE, 0, buffer, 0); } } /** * @brief Send an IPv4 datagram * @param[in] interface Underlying network interface * @param[in] pseudoHeader IPv4 pseudo header * @param[in] buffer Multi-part buffer containing the payload * @param[in] offset Offset to the first byte of the payload * @param[in] ttl TTL value. Default Time-To-Live is used when this parameter is zero * @return Error code **/ error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, NetBuffer *buffer, size_t offset, uint8_t ttl) { error_t error; size_t length; uint16_t id; //Total number of IP datagrams which local IP user-protocols supplied //to IP in requests for transmission MIB2_INC_COUNTER32(mib2Base.ipGroup.ipOutRequests, 1); //Retrieve the length of payload length = netBufferGetLength(buffer) - offset; //Check whether the TTL value is zero if(ttl == 0) { //Use default Time-To-Live value ttl = IPV4_DEFAULT_TTL; } //Identification field is primarily used to identify //fragments of an original IP datagram id = interface->ipv4Context.identification++; //If the payload length is smaller than the network //interface MTU then no fragmentation is needed if((length + sizeof(Ipv4Header)) <= interface->ipv4Context.linkMtu) { //Send data as is error = ipv4SendPacket(interface, pseudoHeader, id, 0, buffer, offset, ttl); } //If the payload length exceeds the network interface MTU //then the device must fragment the data else { #if (IPV4_FRAG_SUPPORT == ENABLED) //Fragment IP datagram into smaller packets error = ipv4FragmentDatagram(interface, pseudoHeader, id, buffer, offset, ttl); #else //Fragmentation is not supported error = ERROR_MESSAGE_TOO_LONG; #endif } //Return status code return error; } /** * @brief Send an IPv4 packet * @param[in] interface Underlying network interface * @param[in] pseudoHeader IPv4 pseudo header * @param[in] fragId Fragment identification field * @param[in] fragOffset Fragment offset field * @param[in] buffer Multi-part buffer containing the payload * @param[in] offset Offset to the first byte of the payload * @param[in] ttl Time-To-Live value * @return Error code **/ error_t ipv4SendPacket(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, size_t fragOffset, NetBuffer *buffer, size_t offset, uint8_t ttl) { error_t error; size_t length; Ipv4Header *packet; //Is there enough space for the IPv4 header? if(offset < sizeof(Ipv4Header)) return ERROR_INVALID_PARAMETER; //Make room for the header offset -= sizeof(Ipv4Header); //Calculate the size of the entire packet, including header and data length = netBufferGetLength(buffer) - offset; //Point to the IPv4 header packet = netBufferAt(buffer, offset); //Format IPv4 header packet->version = IPV4_VERSION; packet->headerLength = 5; packet->typeOfService = 0; packet->totalLength = htons(length); packet->identification = htons(fragId); packet->fragmentOffset = htons(fragOffset); packet->timeToLive = ttl; packet->protocol = pseudoHeader->protocol; packet->headerChecksum = 0; packet->srcAddr = pseudoHeader->srcAddr; packet->destAddr = pseudoHeader->destAddr; //Calculate IP header checksum packet->headerChecksum = ipCalcChecksumEx(buffer, offset, packet->headerLength * 4); //Ensure the source address is valid error = ipv4CheckSourceAddr(interface, pseudoHeader->srcAddr); //Invalid source address? if(error) return error; //Destination address is the unspecified address? if(pseudoHeader->destAddr == IPV4_UNSPECIFIED_ADDR) { //Destination address is not acceptable return ERROR_INVALID_ADDRESS; } //Destination address is the loopback address? else if(pseudoHeader->destAddr == IPV4_LOOPBACK_ADDR) { //Not yet implemented... return ERROR_NOT_IMPLEMENTED; } #if (ETH_SUPPORT == ENABLED) //Ethernet interface? if(interface->nicDriver->type == NIC_TYPE_ETHERNET) { Ipv4Addr destIpAddr; MacAddr destMacAddr; //Get the destination IPv4 address destIpAddr = pseudoHeader->destAddr; //Destination address is a broadcast address? if(ipv4IsBroadcastAddr(interface, destIpAddr)) { //Use of the broadcast MAC address to send the packet destMacAddr = MAC_BROADCAST_ADDR; //No error to report error = NO_ERROR; } //Destination address is a multicast address? else if(ipv4IsMulticastAddr(destIpAddr)) { //Map IPv4 multicast address to MAC-layer multicast address error = ipv4MapMulticastAddrToMac(destIpAddr, &destMacAddr); } //Source or destination address is a link-local address? else if(ipv4IsLinkLocalAddr(pseudoHeader->srcAddr) || ipv4IsLinkLocalAddr(destIpAddr)) { //Packets with a link-local source or destination address are not //routable off the link error = arpResolve(interface, destIpAddr, &destMacAddr); } //Destination host is on the local subnet? else if(ipv4IsOnLocalSubnet(interface, destIpAddr)) { //Resolve destination address before sending the packet error = arpResolve(interface, destIpAddr, &destMacAddr); } //Destination host is outside the local subnet? else { //Make sure the default gateway is properly set if(interface->ipv4Context.defaultGateway != IPV4_UNSPECIFIED_ADDR) { //Use the default gateway to forward the packet destIpAddr = interface->ipv4Context.defaultGateway; //Perform address resolution error = arpResolve(interface, destIpAddr, &destMacAddr); } else { //Number of IP datagrams discarded because no route could be found //to transmit them to their destination MIB2_INC_COUNTER32(mib2Base.ipGroup.ipOutNoRoutes, 1); //Report an error error = ERROR_NO_ROUTE; } } //Successful address resolution? if(!error) { //Debug message TRACE_INFO("Sending IPv4 packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(packet); //Send Ethernet frame error = ethSendFrame(interface, &destMacAddr, buffer, offset, ETH_TYPE_IPV4); } //Address resolution is in progress? else if(error == ERROR_IN_PROGRESS) { //Debug message TRACE_INFO("Enqueuing IPv4 packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(packet); //Enqueue packets waiting for address resolution error = arpEnqueuePacket(interface, destIpAddr, buffer, offset); } //Address resolution failed? else { //Debug message TRACE_WARNING("Cannot map IPv4 address to Ethernet address!\r\n"); } } else #endif #if (PPP_SUPPORT == ENABLED) //PPP interface? if(interface->nicDriver->type == NIC_TYPE_PPP) { //Debug message TRACE_INFO("Sending IPv4 packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(packet); //Send PPP frame error = pppSendFrame(interface, buffer, offset, PPP_PROTOCOL_IP); } else #endif //Unknown interface type? { //Report an error error = ERROR_INVALID_INTERFACE; } //Return status code return error; } /** * @brief Source IPv4 address filtering * @param[in] interface Underlying network interface * @param[in] ipAddr Source IPv4 address to be checked * @return Error code **/ error_t ipv4CheckSourceAddr(NetInterface *interface, Ipv4Addr ipAddr) { //Broadcast and multicast addresses must not be used as source //address (see RFC 1122 3.2.1.3) if(ipv4IsBroadcastAddr(interface, ipAddr) || ipv4IsMulticastAddr(ipAddr)) { //Debug message TRACE_WARNING("Wrong source IPv4 address!\r\n"); //The source address not is acceptable return ERROR_INVALID_ADDRESS; } //The source address is acceptable return NO_ERROR; } /** * @brief Destination IPv4 address filtering * @param[in] interface Underlying network interface * @param[in] ipAddr Destination IPv4 address to be checked * @return Error code **/ error_t ipv4CheckDestAddr(NetInterface *interface, Ipv4Addr ipAddr) { error_t error; uint_t i; Ipv4FilterEntry *entry; //Filter out any invalid addresses error = ERROR_INVALID_ADDRESS; //Broadcast address? if(ipv4IsBroadcastAddr(interface, ipAddr)) { //Always accept broadcast address error = NO_ERROR; } //Multicast address? else if(ipv4IsMulticastAddr(ipAddr)) { //Go through the multicast filter table for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++) { //Point to the current entry entry = &interface->ipv4Context.multicastFilter[i]; //Valid entry? if(entry->refCount > 0) { //Check whether the destination IPv4 address matches //a relevant multicast address if(entry->addr == ipAddr) { //The multicast address is acceptable error = NO_ERROR; //Stop immediately break; } } } } //Unicast address? else { //Valid host address? if(interface->ipv4Context.addrState != IPV4_ADDR_STATE_INVALID) { //Check whether the destination address matches the host address if(interface->ipv4Context.addr == ipAddr) { //The destination address is acceptable error = NO_ERROR; } } } //Return status code return error; } /** * @brief IPv4 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 IPv4 address * @param[out] srcAddr Local IPv4 address to be used * @return Error code **/ error_t ipv4SelectSourceAddr(NetInterface **interface, Ipv4Addr destAddr, Ipv4Addr *srcAddr) { uint_t i; NetInterface *currentInterface; NetInterface *bestInterface; //Initialize variables bestInterface = NULL; //Loop through network interfaces for(i = 0; i < NET_INTERFACE_COUNT; i++) { //Point to the current interface currentInterface = &netInterface[i]; //A network interface may be provided as a hint... if(*interface != currentInterface && *interface != NULL) { //Select the next interface in the list continue; } //Check the state of the address if(currentInterface->ipv4Context.addrState != IPV4_ADDR_STATE_VALID) { //Select the next interface in the list continue; } //Select the first interface as default if(bestInterface == NULL) { //Give the current interface the higher precedence bestInterface = currentInterface; //Select the next interface in the list continue; } //Prefer same address if(bestInterface->ipv4Context.addr == destAddr) { //Select the next interface in the list continue; } else if(currentInterface->ipv4Context.addr == destAddr) { //Give the current interface the higher precedence bestInterface = currentInterface; //Select the next interface in the list continue; } //Prefer appropriate scope if(ipv4GetAddrScope(currentInterface->ipv4Context.addr) < ipv4GetAddrScope(bestInterface->ipv4Context.addr)) { if(ipv4GetAddrScope(currentInterface->ipv4Context.addr) >= ipv4GetAddrScope(destAddr)) { //Give the current interface the higher precedence bestInterface = currentInterface; } //Select the next interface in the list continue; } else if(ipv4GetAddrScope(bestInterface->ipv4Context.addr) < ipv4GetAddrScope(currentInterface->ipv4Context.addr)) { if(ipv4GetAddrScope(bestInterface->ipv4Context.addr) < ipv4GetAddrScope(destAddr)) { //Give the current interface the higher precedence bestInterface = currentInterface; } //Select the next interface in the list continue; } //Prefer appropriate subnet mask if(ipv4IsOnLocalSubnet(bestInterface, destAddr)) { //Select the next interface in the list continue; } else if(ipv4IsOnLocalSubnet(currentInterface, destAddr)) { //Give the current interface the higher precedence bestInterface = currentInterface; //Select the next interface in the list continue; } } //Source address selection failed? if(bestInterface == NULL) { //Report an error return ERROR_NO_ADDRESS; } //Return the out-going interface and the source address to be used *interface = bestInterface; *srcAddr = bestInterface->ipv4Context.addr; //Successful source address selection return NO_ERROR; } /** * @brief Check whether an IPv4 address is a broadcast address * @param[in] interface Underlying network interface * @param[in] ipAddr IPv4 address to be checked * @return TRUE if the IPv4 address is a broadcast address, else FALSE **/ bool_t ipv4IsBroadcastAddr(NetInterface *interface, Ipv4Addr ipAddr) { //Check whether the specified IPv4 address is the broadcast address if(ipAddr == IPV4_BROADCAST_ADDR) return TRUE; //Check whether the specified IPv4 address belongs to the local network if(ipv4IsOnLocalSubnet(interface, ipAddr)) { //Make sure the subnet mask is not 255.255.255.255 if(interface->ipv4Context.subnetMask != IPV4_BROADCAST_ADDR) { //Directed broadcast address? if((ipAddr | interface->ipv4Context.subnetMask) == IPV4_BROADCAST_ADDR) return TRUE; } } //The specified IPv4 address is not a broadcast address return FALSE; } /** * @brief Retrieve the scope of an IPv4 address * @param[in] ipAddr IPv4 address * @return IPv4 address scope **/ uint_t ipv4GetAddrScope(Ipv4Addr ipAddr) { uint_t scope; //Broadcast address? if(ipAddr == IPV4_BROADCAST_ADDR) { //The broadcast address is never forwarded by the routers connecting //the local network to other networks scope = IPV4_ADDR_SCOPE_LINK_LOCAL; } //Multicast address? else if(ipv4IsMulticastAddr(ipAddr)) { //Local Network Control Block? if((ipAddr & IPV4_MULTICAST_LNCB_MASK) == IPV4_MULTICAST_LNCB_PREFIX) { //Addresses in the Local Network Control Block are used for protocol //control traffic that is not forwarded off link scope = IPV4_ADDR_SCOPE_LINK_LOCAL; } //Any other multicast address? else { //Other addresses are assigned global scope scope = IPV4_ADDR_SCOPE_GLOBAL; } } //Unicast address? else { //Loopback address? if((ipAddr & IPV4_LOOPBACK_ADDR_MASK) == IPV4_LOOPBACK_ADDR_PREFIX) { //IPv4 loopback addresses, which have the prefix 127.0.0.0/8, //are assigned interface-local scope scope = IPV4_ADDR_SCOPE_INTERFACE_LOCAL; } //Link-local address? else if((ipAddr & IPV4_LINK_LOCAL_MASK) == IPV4_LINK_LOCAL_PREFIX) { //IPv4 auto-configuration addresses, which have the prefix //169.254.0.0/16, are assigned link-local scope scope = IPV4_ADDR_SCOPE_LINK_LOCAL; } //Any other unicast address? else { //Other addresses are assigned global scope scope = IPV4_ADDR_SCOPE_GLOBAL; } } //Return the scope of the specified IPv4 address return scope; } /** * @brief Join the specified host group * @param[in] interface Underlying network interface * @param[in] groupAddr IPv4 address identifying the host group to join * @return Error code **/ error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr) { error_t error; uint_t i; Ipv4FilterEntry *entry; Ipv4FilterEntry *firstFreeEntry; #if (ETH_SUPPORT == ENABLED) MacAddr macAddr; #endif //The IPv4 address must be a valid multicast address if(!ipv4IsMulticastAddr(groupAddr)) return ERROR_INVALID_ADDRESS; //Initialize error code error = NO_ERROR; //Keep track of the first free entry firstFreeEntry = NULL; //Go through the multicast filter table for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++) { //Point to the current entry entry = &interface->ipv4Context.multicastFilter[i]; //Valid entry? if(entry->refCount > 0) { //Check whether the table already contains the specified IPv4 address if(entry->addr == groupAddr) { //Increment the reference count entry->refCount++; //Successful processing return NO_ERROR; } } else { //Keep track of the first free entry if(firstFreeEntry == NULL) firstFreeEntry = entry; } } //Check whether the multicast filter table is full if(firstFreeEntry == NULL) { //A new entry cannot be added return ERROR_FAILURE; } #if (ETH_SUPPORT == ENABLED) //Map the IPv4 multicast address to a MAC-layer address ipv4MapMulticastAddrToMac(groupAddr, &macAddr); //Add the corresponding address to the MAC filter table error = ethAcceptMulticastAddr(interface, &macAddr); #endif //MAC filter table successfully updated? if(!error) { //Now we can safely add a new entry to the table firstFreeEntry->addr = groupAddr; //Initialize the reference count firstFreeEntry->refCount = 1; #if (IGMP_SUPPORT == ENABLED) //Report multicast group membership to the router igmpJoinGroup(interface, firstFreeEntry); #endif } //Return status code return error; } /** * @brief Leave the specified host group * @param[in] interface Underlying network interface * @param[in] groupAddr IPv4 address identifying the host group to leave * @return Error code **/ error_t ipv4LeaveMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr) { uint_t i; Ipv4FilterEntry *entry; #if (ETH_SUPPORT == ENABLED) MacAddr macAddr; #endif //The IPv4 address must be a valid multicast address if(!ipv4IsMulticastAddr(groupAddr)) return ERROR_INVALID_ADDRESS; //Go through the multicast filter table for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++) { //Point to the current entry entry = &interface->ipv4Context.multicastFilter[i]; //Valid entry? if(entry->refCount > 0) { //Specified IPv4 address found? if(entry->addr == groupAddr) { //Decrement the reference count entry->refCount--; //Remove the entry if the reference count drops to zero if(entry->refCount == 0) { #if (IGMP_SUPPORT == ENABLED) //Report group membership termination igmpLeaveGroup(interface, entry); #endif #if (ETH_SUPPORT == ENABLED) //Map the IPv4 multicast address to a MAC-layer address ipv4MapMulticastAddrToMac(groupAddr, &macAddr); //Drop the corresponding address from the MAC filter table ethDropMulticastAddr(interface, &macAddr); #endif //Remove the multicast address from the list entry->addr = IPV4_UNSPECIFIED_ADDR; } //Successful processing return NO_ERROR; } } } //The specified IPv4 address does not exist return ERROR_ADDRESS_NOT_FOUND; } /** * @brief Map an host group address to a MAC-layer multicast address * @param[in] ipAddr IPv4 host group address * @param[out] macAddr Corresponding MAC-layer multicast address * @return Error code **/ error_t ipv4MapMulticastAddrToMac(Ipv4Addr ipAddr, MacAddr *macAddr) { uint8_t *p; //Ensure the specified IPv4 address is a valid host group address if(!ipv4IsMulticastAddr(ipAddr)) return ERROR_INVALID_ADDRESS; //Cast the address to byte array p = (uint8_t *) &ipAddr; //An IP host group address is mapped to an Ethernet multicast address //by placing the low-order 23-bits of the IP address into the low-order //23 bits of the Ethernet multicast address 01-00-5E-00-00-00 macAddr->b[0] = 0x01; macAddr->b[1] = 0x00; macAddr->b[2] = 0x5E; macAddr->b[3] = p[1] & 0x7F; macAddr->b[4] = p[2]; macAddr->b[5] = p[3]; //The specified host group address was successfully //mapped to a MAC-layer address return NO_ERROR; } /** * @brief Convert a dot-decimal string to a binary IPv4 address * @param[in] str NULL-terminated string representing the IPv4 address * @param[out] ipAddr Binary representation of the IPv4 address * @return Error code **/ error_t ipv4StringToAddr(const char_t *str, Ipv4Addr *ipAddr) { error_t error; int_t i = 0; int_t value = -1; //Parse input string while(1) { //Decimal digit found? if(isdigit((uint8_t) *str)) { //First digit to be decoded? if(value < 0) value = 0; //Update the value of the current byte value = (value * 10) + (*str - '0'); //The resulting value shall be in range 0 to 255 if(value > 255) { //The conversion failed error = ERROR_INVALID_SYNTAX; break; } } //Dot separator found? else if(*str == '.' && i < 4) { //Each dot must be preceded by a valid number if(value < 0) { //The conversion failed error = ERROR_INVALID_SYNTAX; break; } //Save the current byte ((uint8_t *) ipAddr)[i++] = value; //Prepare to decode the next byte value = -1; } //End of string detected? else if(*str == '\0' && i == 3) { //The NULL character must be preceded by a valid number if(value < 0) { //The conversion failed error = ERROR_INVALID_SYNTAX; } else { //Save the last byte of the IPv4 address ((uint8_t *) ipAddr)[i] = value; //The conversion succeeded error = NO_ERROR; } //We are done break; } //Invalid character... else { //The conversion failed error = ERROR_INVALID_SYNTAX; break; } //Point to the next character str++; } //Return status code return error; } /** * @brief Convert a binary IPv4 address to dot-decimal notation * @param[in] ipAddr Binary representation of the IPv4 address * @param[out] str NULL-terminated string representing the IPv4 address * @return Pointer to the formatted string **/ char_t *ipv4AddrToString(Ipv4Addr ipAddr, char_t *str) { uint8_t *p; static char_t buffer[16]; //If the NULL pointer is given as parameter, then the internal buffer is used if(str == NULL) str = buffer; //Cast the address to byte array p = (uint8_t *) &ipAddr; //Format IPv4 address sprintf(str, "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 "", p[0], p[1], p[2], p[3]); //Return a pointer to the formatted string return str; } /** * @brief Dump IPv4 header for debugging purpose * @param[in] ipHeader Pointer to the IPv4 header **/ void ipv4DumpHeader(const Ipv4Header *ipHeader) { //Dump IP header contents TRACE_DEBUG(" Version = %" PRIu8 "\r\n", ipHeader->version); TRACE_DEBUG(" Header Length = %" PRIu8 "\r\n", ipHeader->headerLength); TRACE_DEBUG(" Type Of Service = %" PRIu8 "\r\n", ipHeader->typeOfService); TRACE_DEBUG(" Total Length = %" PRIu16 "\r\n", ntohs(ipHeader->totalLength)); TRACE_DEBUG(" Identification = %" PRIu16 "\r\n", ntohs(ipHeader->identification)); TRACE_DEBUG(" Flags = 0x%01X\r\n", ntohs(ipHeader->fragmentOffset) >> 13); TRACE_DEBUG(" Fragment Offset = %" PRIu16 "\r\n", ntohs(ipHeader->fragmentOffset) & 0x1FFF); TRACE_DEBUG(" Time To Live = %" PRIu8 "\r\n", ipHeader->timeToLive); TRACE_DEBUG(" Protocol = %" PRIu8 "\r\n", ipHeader->protocol); TRACE_DEBUG(" Header Checksum = 0x%04" PRIX16 "\r\n", ntohs(ipHeader->headerChecksum)); TRACE_DEBUG(" Src Addr = %s\r\n", ipv4AddrToString(ipHeader->srcAddr, NULL)); TRACE_DEBUG(" Dest Addr = %s\r\n", ipv4AddrToString(ipHeader->destAddr, NULL)); } #endif