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; }
error_t ipv4LeaveMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr) { uint_t i; uint_t j; MacAddr macAddr; //Ensure the specified IPv4 address is a multicast address if(!ipv4IsMulticastAddr(groupAddr)) return ERROR_INVALID_ADDRESS; //Acquire exclusive access to the IPv4 filter table osAcquireMutex(&interface->ipv4FilterMutex); //Loop through filter table entries for(i = 0; i < interface->ipv4FilterSize; i++) { //Specified IPv4 address found? if(interface->ipv4Filter[i].addr == groupAddr) { //Decrement the reference count interface->ipv4Filter[i].refCount--; //Remove the entry if the reference count drops to zero if(interface->ipv4Filter[i].refCount < 1) { #if (IGMP_SUPPORT == ENABLED) //Report group membership termination igmpLeaveGroup(interface, &interface->ipv4Filter[i]); #endif //Map the multicast IPv4 address to a MAC-layer address ipv4MapMulticastAddrToMac(groupAddr, &macAddr); #if (ETH_SUPPORT == ENABLED) //Drop the corresponding address from the MAC filter table ethDropMulticastAddr(interface, &macAddr); #endif //Adjust the size of the IPv4 filter table interface->ipv4FilterSize--; //Remove the corresponding entry for(j = i; j < interface->ipv4FilterSize; j++) interface->ipv4Filter[j] = interface->ipv4Filter[j + 1]; } //Release exclusive access to the IPv4 filter table osReleaseMutex(&interface->ipv4FilterMutex); //No error to report return NO_ERROR; } } //Release exclusive access to the IPv4 filter table osReleaseMutex(&interface->ipv4FilterMutex); //The specified IPv4 address does not exist return ERROR_FAILURE; }
error_t ipv4CheckDestAddr(NetInterface *interface, Ipv4Addr ipAddr) { uint_t i; //Filter out any invalid addresses error_t error = ERROR_INVALID_ADDRESS; //Broadcast address? if(ipv4IsBroadcastAddr(interface, ipAddr)) { //Always accept broadcast address error = NO_ERROR; } //Multicast address? else if(ipv4IsMulticastAddr(ipAddr)) { //Acquire exclusive access to the IPv4 filter table osAcquireMutex(&interface->ipv4FilterMutex); //Loop through the IPv4 filter table for(i = 0; i < interface->ipv4FilterSize; i++) { //Check whether the destination IPv4 address matches //a relevant multicast address if(interface->ipv4Filter[i].addr == ipAddr) { //The multicast address is acceptable error = NO_ERROR; //Stop immediately break; } } //Release exclusive access to the IPv4 filter table osReleaseMutex(&interface->ipv4FilterMutex); } //Unicast address? else { //The destination address matches the host address? if(interface->ipv4Config.addrState != IPV4_ADDR_STATE_INVALID && interface->ipv4Config.addr == ipAddr) { //The destination address is acceptable error = NO_ERROR; } } //Return status code return error; }
error_t ipv4SetDefaultGateway(NetInterface *interface, Ipv4Addr addr) { //Check parameters if(interface == NULL) return ERROR_INVALID_PARAMETER; if(ipv4IsMulticastAddr(addr)) return ERROR_INVALID_ADDRESS; //Set up default gateway address interface->ipv4Config.defaultGateway = addr; //Successful processing return NO_ERROR; }
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; }
error_t ipv4SetDnsServer(NetInterface *interface, uint_t num, Ipv4Addr addr) { //Check parameters if(interface == NULL) return ERROR_INVALID_PARAMETER; if(ipv4IsMulticastAddr(addr)) return ERROR_INVALID_ADDRESS; //Set up DNS server address if(num < IPV4_MAX_DNS_SERVERS) interface->ipv4Config.dnsServer[num] = addr; //Successful processing return NO_ERROR; }
error_t ipv4SetHostAddrEx(NetInterface *interface, Ipv4Addr addr, Ipv4AddrState state) { //Check parameters if(interface == NULL) return ERROR_INVALID_PARAMETER; if(ipv4IsMulticastAddr(addr)) return ERROR_INVALID_ADDRESS; //The address is no more valid interface->ipv4Config.addrState = IPV4_ADDR_STATE_INVALID; //Clear conflict flag interface->ipv4Config.addrConflict = FALSE; //Set up host address interface->ipv4Config.addr = addr; //Set the desired state interface->ipv4Config.addrState = state; //Successful processing return NO_ERROR; }
error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code, uint8_t parameter, const ChunkedBuffer *ipPacket) { error_t error; size_t offset; size_t length; Ipv4Header *ipHeader; ChunkedBuffer *icmpMessage; IcmpErrorMessage *icmpHeader; Ipv4PseudoHeader pseudoHeader; //Retrieve the length of the invoking IPv4 packet length = chunkedBufferGetLength(ipPacket); //Check the length of the IPv4 packet if(length < sizeof(Ipv4Header)) return ERROR_INVALID_LENGTH; //Point to the header of the invoking packet ipHeader = chunkedBufferAt(ipPacket, 0); //Sanity check if(!ipHeader) return ERROR_FAILURE; //Never respond to a packet destined to a broadcast or a multicast address if(ipv4IsBroadcastAddr(interface, ipHeader->destAddr) || ipv4IsMulticastAddr(ipHeader->destAddr)) { //Report an error return ERROR_INVALID_ADDRESS; } //Length of the data that will be returned along with the ICMP header length = MIN(length, ipHeader->headerLength * 4 + 8); //Allocate a memory buffer to hold the ICMP message icmpMessage = ipAllocBuffer(sizeof(IcmpErrorMessage), &offset); //Failed to allocate memory? if(!icmpMessage) return ERROR_OUT_OF_MEMORY; //Point to the ICMP header icmpHeader = chunkedBufferAt(icmpMessage, offset); //Format ICMP message icmpHeader->type = type; icmpHeader->code = code; icmpHeader->checksum = 0; icmpHeader->parameter = parameter; icmpHeader->unused = 0; //Copy the IP header and the first 8 bytes of the original datagram data error = chunkedBufferConcat(icmpMessage, ipPacket, 0, length); //Any error to report? if(error) { //Clean up side effects chunkedBufferFree(icmpMessage); //Exit immediately return error; } //Get the length of the resulting message length = chunkedBufferGetLength(icmpMessage) - offset; //Message checksum calculation icmpHeader->checksum = ipCalcChecksumEx(icmpMessage, offset, length); //Format IPv4 pseudo header pseudoHeader.srcAddr = ipHeader->destAddr; pseudoHeader.destAddr = ipHeader->srcAddr; pseudoHeader.reserved = 0; pseudoHeader.protocol = IPV4_PROTOCOL_ICMP; pseudoHeader.length = htons(length); //Debug message TRACE_INFO("Sending ICMP Error message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpDumpErrorMessage(icmpHeader); //Send ICMP Error message error = ipv4SendDatagram(interface, &pseudoHeader, icmpMessage, offset, IPV4_DEFAULT_TTL); //Free previously allocated memory chunkedBufferFree(icmpMessage); //Return status code return error; }
void arpProcessReply(NetInterface *interface, ArpPacket *arpReply) { ArpCacheEntry *entry; //Debug message TRACE_INFO("ARP Reply received...\r\n"); //Check sender protocol address if(arpReply->spa == IPV4_UNSPECIFIED_ADDR) return; if(ipv4IsMulticastAddr(arpReply->spa)) return; if(ipv4IsBroadcastAddr(interface, arpReply->spa)) return; //Check sender hardware address if(macCompAddr(&arpReply->sha, &MAC_UNSPECIFIED_ADDR)) return; if(macCompAddr(&arpReply->sha, &MAC_BROADCAST_ADDR)) return; //Acquire exclusive access to ARP cache osMutexAcquire(interface->arpCacheMutex); //Search the ARP cache for the specified IPv4 address entry = arpFindEntry(interface, arpReply->spa); //A matching ARP entry has been found if(entry) { //Check current state if(entry->state == ARP_STATE_INCOMPLETE) { //Record the corresponding MAC address entry->macAddr = arpReply->sha; //Send all the packets that are pending for transmission arpSendQueuedPackets(interface, entry); //Save current time entry->timestamp = osGetTickCount(); //The validity of the ARP entry is limited in time entry->timeout = ARP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = ARP_STATE_REACHABLE; } else if(entry->state == ARP_STATE_REACHABLE) { //Different link-layer address than cached? if(!macCompAddr(&arpReply->sha, &entry->macAddr)) { //Enter STALE state entry->state = ARP_STATE_STALE; } } else if(entry->state == ARP_STATE_PROBE) { //Record IPv4/MAC address pair entry->ipAddr = arpReply->spa; entry->macAddr = arpReply->sha; //Save current time entry->timestamp = osGetTickCount(); //The validity of the ARP entry is limited in time entry->timeout = ARP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = ARP_STATE_REACHABLE; } } //Release exclusive access to ARP cache osMutexRelease(interface->arpCacheMutex); }
void tcpProcessSegment(NetInterface *interface, IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset) { uint_t i; size_t length; Socket *socket; Socket *passiveSocket; TcpHeader *segment; //Total number of segments received, including those received in error MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInSegs, 1); MIB2_INC_COUNTER64(mib2Base.tcpGroup.tcpHCInSegs, 1); //A TCP implementation must silently discard an incoming //segment that is addressed to a broadcast or multicast //address (see RFC 1122 4.2.3.10) #if (IPV4_SUPPORT == ENABLED) if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Ensure the destination address is not a broadcast address if(ipv4IsBroadcastAddr(interface, pseudoHeader->ipv4Data.destAddr)) return; //Ensure the destination address is not a multicast address if(ipv4IsMulticastAddr(pseudoHeader->ipv4Data.destAddr)) return; } else #endif #if (IPV6_SUPPORT == ENABLED) if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Ensure the destination address is not a multicast address if(ipv6IsMulticastAddr(&pseudoHeader->ipv6Data.destAddr)) return; } else #endif { //This should never occur... return; } //Retrieve the length of the TCP segment length = netBufferGetLength(buffer) - offset; //Point to the TCP header segment = netBufferAt(buffer, offset); //Sanity check if(segment == NULL) return; //Ensure the TCP header is valid if(length < sizeof(TcpHeader)) { //Debug message TRACE_WARNING("TCP segment length is invalid!\r\n"); //Total number of segments received in error MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1); //Exit immediately return; } //Check header length if(segment->dataOffset < 5 || (segment->dataOffset * 4) > length) { //Debug message TRACE_WARNING("TCP header length is invalid!\r\n"); //Total number of segments received in error MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1); //Exit immediately return; } //Verify TCP checksum if(ipCalcUpperLayerChecksumEx(pseudoHeader->data, pseudoHeader->length, buffer, offset, length) != 0x0000) { //Debug message TRACE_WARNING("Wrong TCP header checksum!\r\n"); //Total number of segments received in error MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1); //Exit immediately return; } //No matching socket in the LISTEN state for the moment passiveSocket = NULL; //Look through opened sockets for(i = 0; i < SOCKET_MAX_COUNT; i++) { //Point to the current socket socket = socketTable + i; //TCP socket found? if(socket->type != SOCKET_TYPE_STREAM) continue; //Check whether the socket is bound to a particular interface if(socket->interface && socket->interface != interface) continue; //Check destination port number if(socket->localPort != ntohs(segment->destPort)) continue; #if (IPV4_SUPPORT == ENABLED) //An IPv4 packet was received? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Destination IP address filtering if(socket->localIpAddr.length) { //An IPv4 address is expected if(socket->localIpAddr.length != sizeof(Ipv4Addr)) continue; //Filter out non-matching addresses if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr) continue; } //Source IP address filtering if(socket->remoteIpAddr.length) { //An IPv4 address is expected if(socket->remoteIpAddr.length != sizeof(Ipv4Addr)) continue; //Filter out non-matching addresses if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr) continue; } } else #endif #if (IPV6_SUPPORT == ENABLED) //An IPv6 packet was received? if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Destination IP address filtering if(socket->localIpAddr.length) { //An IPv6 address is expected if(socket->localIpAddr.length != sizeof(Ipv6Addr)) continue; //Filter out non-matching addresses if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr)) continue; } //Source IP address filtering if(socket->remoteIpAddr.length) { //An IPv6 address is expected if(socket->remoteIpAddr.length != sizeof(Ipv6Addr)) continue; //Filter out non-matching addresses if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr)) continue; } } else #endif //An invalid packet was received? { //This should never occur... continue; } //Keep track of the first matching socket in the LISTEN state if(socket->state == TCP_STATE_LISTEN && !passiveSocket) passiveSocket = socket; //Source port filtering if(socket->remotePort != ntohs(segment->srcPort)) continue; //A matching socket has been found break; } //If no matching socket has been found then try to //use the first matching socket in the LISTEN state if(i >= SOCKET_MAX_COUNT) socket = passiveSocket; //Offset to the first data byte offset += segment->dataOffset * 4; //Calculate the length of the data length -= segment->dataOffset * 4; //Debug message TRACE_DEBUG("%s: TCP segment received (%" PRIuSIZE " data bytes)...\r\n", formatSystemTime(osGetSystemTime(), NULL), length); //Dump TCP header contents for debugging purpose if(!socket) tcpDumpHeader(segment, length, 0, 0); else tcpDumpHeader(segment, length, socket->irs, socket->iss); //Convert from network byte order to host byte order segment->srcPort = ntohs(segment->srcPort); segment->destPort = ntohs(segment->destPort); segment->seqNum = ntohl(segment->seqNum); segment->ackNum = ntohl(segment->ackNum); segment->window = ntohs(segment->window); segment->urgentPointer = ntohs(segment->urgentPointer); //Specified port is unreachable? if(!socket) { //An incoming segment not containing a RST causes //a reset to be sent in response if(!(segment->flags & TCP_FLAG_RST)) tcpSendResetSegment(interface, pseudoHeader, segment, length); //Return immediately return; } //Check current state switch(socket->state) { //Process CLOSED state case TCP_STATE_CLOSED: //This is the default state that each connection starts in before //the process of establishing it begins tcpStateClosed(interface, pseudoHeader, segment, length); break; //Process LISTEN state case TCP_STATE_LISTEN: //A device (normally a server) is waiting to receive a synchronize (SYN) //message from a client. It has not yet sent its own SYN message tcpStateListen(socket, interface, pseudoHeader, segment, length); break; //Process SYN_SENT state case TCP_STATE_SYN_SENT: //The device (normally a client) has sent a synchronize (SYN) message and //is waiting for a matching SYN from the other device (usually a server) tcpStateSynSent(socket, segment, length); break; //Process SYN_RECEIVED state case TCP_STATE_SYN_RECEIVED: //The device has both received a SYN from its partner and sent its own SYN. //It is now waiting for an ACK to its SYN to finish connection setup tcpStateSynReceived(socket, segment, buffer, offset, length); break; //Process ESTABLISHED state case TCP_STATE_ESTABLISHED: //Data can be exchanged freely once both devices in the connection enter //this state. This will continue until the connection is closed tcpStateEstablished(socket, segment, buffer, offset, length); break; //Process CLOSE_WAIT state case TCP_STATE_CLOSE_WAIT: //The device has received a close request (FIN) from the other device. It //must now wait for the application to acknowledge this request and //generate a matching request tcpStateCloseWait(socket, segment, length); break; //Process LAST_ACK state case TCP_STATE_LAST_ACK: //A device that has already received a close request and acknowledged it, //has sent its own FIN and is waiting for an ACK to this request tcpStateLastAck(socket, segment, length); break; //Process FIN_WAIT_1 state case TCP_STATE_FIN_WAIT_1: //A device in this state is waiting for an ACK for a FIN it has sent, or //is waiting for a connection termination request from the other device tcpStateFinWait1(socket, segment, buffer, offset, length); break; //Process FIN_WAIT_2 state case TCP_STATE_FIN_WAIT_2: //A device in this state has received an ACK for its request to terminate the //connection and is now waiting for a matching FIN from the other device tcpStateFinWait2(socket, segment, buffer, offset, length); break; //Process CLOSING state case TCP_STATE_CLOSING: //The device has received a FIN from the other device and sent an ACK for //it, but not yet received an ACK for its own FIN message tcpStateClosing(socket, segment, length); break; //Process TIME_WAIT state case TCP_STATE_TIME_WAIT: //The device has now received a FIN from the other device and acknowledged //it, and sent its own FIN and received an ACK for it. We are done, except //for waiting to ensure the ACK is received and prevent potential overlap //with new connections tcpStateTimeWait(socket, segment, length); break; //Invalid state... default: //Back to the CLOSED state tcpChangeState(socket, TCP_STATE_CLOSED); //Silently discard incoming packet break; } }
error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr) { error_t error; uint_t i; MacAddr macAddr; //Ensure the specified IPv4 address is a multicast address if(!ipv4IsMulticastAddr(groupAddr)) return ERROR_INVALID_ADDRESS; //Acquire exclusive access to the IPv4 filter table osMutexAcquire(interface->ipv4FilterMutex); //Loop through filter table entries for(i = 0; i < interface->ipv4FilterSize; i++) { //Check whether the table already contains the specified IPv4 address if(interface->ipv4Filter[i].addr == groupAddr) { //Increment the reference count interface->ipv4Filter[i].refCount++; //Release exclusive access to the IPv4 filter table osMutexRelease(interface->ipv4FilterMutex); //No error to report return NO_ERROR; } } //The IPv4 filter table is full ? if(i >= IPV4_FILTER_MAX_SIZE) { //Release exclusive access to the IPv4 filter table osMutexRelease(interface->ipv4FilterMutex); //A new entry cannot be added return ERROR_FAILURE; } //Map the multicast IPv4 address to a MAC-layer address ipv4MapMulticastAddrToMac(groupAddr, &macAddr); //Add the corresponding address to the MAC filter table error = ethAcceptMulticastAddr(interface, &macAddr); //Ensure the MAC filter table was successfully updated if(!error) { //Now we can safely add a new entry to the table interface->ipv4Filter[i].addr = groupAddr; //Initialize the reference count interface->ipv4Filter[i].refCount = 1; //Adjust the size of the IPv4 filter table interface->ipv4FilterSize++; #if (IGMP_SUPPORT == ENABLED) //Report multicast group membership to the router igmpJoinGroup(interface, &interface->ipv4Filter[i]); #endif } //Release exclusive access to the IPv4 filter table osMutexRelease(interface->ipv4FilterMutex); //Return status code return error; }
error_t ipv4SendPacket(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, uint16_t fragOffset, ChunkedBuffer *buffer, size_t offset, uint8_t timeToLive) { error_t error; size_t length; Ipv4Addr destIpAddr = 0; MacAddr destMacAddr; 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 = chunkedBufferGetLength(buffer) - offset; //Point to the IPv4 header packet = chunkedBufferAt(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 = timeToLive; 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 error = ERROR_INVALID_ADDRESS; } //Destination address is the loopback address? else if(pseudoHeader->destAddr == IPV4_LOOPBACK_ADDR) { //Not yet implemented... error = ERROR_NOT_IMPLEMENTED; } //Destination address is a broadcast address? else if(ipv4IsBroadcastAddr(interface, pseudoHeader->destAddr)) { //Destination IPv4 address destIpAddr = pseudoHeader->destAddr; //Make use of the broadcast MAC address destMacAddr = MAC_BROADCAST_ADDR; //No error to report error = NO_ERROR; } //Destination address is a multicast address? else if(ipv4IsMulticastAddr(pseudoHeader->destAddr)) { //Destination IPv4 address destIpAddr = pseudoHeader->destAddr; //Map IPv4 multicast address to MAC-layer multicast address error = ipv4MapMulticastAddrToMac(pseudoHeader->destAddr, &destMacAddr); } //Destination host is in the local subnet? else if(ipv4IsInLocalSubnet(interface, pseudoHeader->destAddr)) { //Destination IPv4 address destIpAddr = pseudoHeader->destAddr; //Resolve host address before sending the packet error = arpResolve(interface, pseudoHeader->destAddr, &destMacAddr); } //Destination host is outside the local subnet? else { //Make sure the default gateway is properly set if(interface->ipv4Config.defaultGateway != IPV4_UNSPECIFIED_ADDR) { //Use the default gateway to forward the packet destIpAddr = interface->ipv4Config.defaultGateway; //Perform address resolution error = arpResolve(interface, interface->ipv4Config.defaultGateway, &destMacAddr); } else { //There is no route to the outside world... error = ERROR_NO_ROUTE; } } //Successful address resolution? if(!error) { //Debug message TRACE_INFO("Sending IPv4 packet (%u 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 (%u 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"); } //Return status code return error; }