error_t ipv4FragmentDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, uint16_t id, const ChunkedBuffer *payload, size_t payloadOffset, uint8_t timeToLive) { error_t error; size_t offset; size_t length; size_t payloadLength; size_t fragmentOffset; ChunkedBuffer *fragment; //Retrieve the length of the payload payloadLength = chunkedBufferGetLength(payload) - payloadOffset; //Allocate a memory buffer to hold IP fragments fragment = ipAllocBuffer(0, &fragmentOffset); //Failed to allocate memory? if(!fragment) return ERROR_OUT_OF_MEMORY; //Split the payload into multiple IP fragments for(offset = 0; offset < payloadLength; offset += length) { //Flush the contents of the fragment error = chunkedBufferSetLength(fragment, fragmentOffset); //Sanity check if(error) break; //Process the last fragment? if((payloadLength - offset) <= IPV4_MAX_FRAG_SIZE) { //Size of the current fragment length = payloadLength - offset; //Copy fragment data chunkedBufferConcat(fragment, payload, payloadOffset + offset, length); //Do not set the MF flag for the last fragment error = ipv4SendPacket(interface, pseudoHeader, id, offset / 8, fragment, fragmentOffset, timeToLive); } else { //Size of the current fragment (must be a multiple of 8-byte blocks) length = IPV4_MAX_FRAG_SIZE; //Copy fragment data chunkedBufferConcat(fragment, payload, payloadOffset + offset, length); //Fragmented packets must have the MF flag set error = ipv4SendPacket(interface, pseudoHeader, id, IPV4_FLAG_MF | (offset / 8), fragment, fragmentOffset, timeToLive); } //Failed to send current IP packet? if(error) break; } //Free previously allocated memory chunkedBufferFree(fragment); //Return status code return error; }
void mldProcessListenerReport(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { uint_t i; size_t length; MldMessage *message; Ipv6FilterEntry *entry; //Retrieve the length of the MLD message length = chunkedBufferGetLength(buffer) - offset; //The message must be at least 24 octets long if(length < sizeof(MldMessage)) return; //Point to the beginning of the MLD message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose mldDumpMessage(message); //Make sure the source address of the message is a valid link-local address if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) return; //Check the Hop Limit field if(hopLimit != MLD_HOP_LIMIT) return; //Acquire exclusive access to the IPv6 filter table osMutexAcquire(interface->ipv6FilterMutex); //Loop through filter table entries for(i = 0; i < interface->ipv6FilterSize; i++) { //Point to the current entry entry = &interface->ipv6Filter[i]; //Report messages are ignored for multicast addresses //in the Non-Listener or Idle Listener state if(entry->state == MLD_STATE_DELAYING_LISTENER) { //The Multicast Listener Report message matches the current entry? if(ipv6CompAddr(&message->multicastAddr, &entry->addr)) { //Clear flag entry->flag = FALSE; //Switch to the Idle Listener state entry->state = MLD_STATE_IDLE_LISTENER; } } } //Release exclusive access to the IPv6 filter table osMutexRelease(interface->ipv6FilterMutex); }
error_t dm9000SendPacket(NetInterface *interface, const ChunkedBuffer *buffer, size_t offset) { size_t i; uint16_t *p; //Point to the driver context Dm9000Context *context = (Dm9000Context *) interface->nicContext; //Retrieve the length of the packet size_t length = chunkedBufferGetLength(buffer) - offset; //Check the frame length if(length > 1536) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); //Report an error return ERROR_INVALID_LENGTH; } //Copy user data chunkedBufferRead(txBuffer, buffer, offset, length); //A dummy write is required before accessing FIFO dm9000WriteReg(DM9000_REG_MWCMDX, 0); //Select MWCMD register *DM9000_INDEX_REG = DM9000_REG_MWCMD; //Point to the beginning of the buffer p = (uint16_t *) txBuffer; //Write data to the FIFO using 16-bit mode for(i = length; i > 1; i -= 2) *DM9000_DATA_REG = *(p++); //Odd number of bytes? if(i > 0) *DM9000_DATA_REG = *((uint8_t *) p); //Write the number of bytes to send dm9000WriteReg(DM9000_REG_TXPLL, LSB(length)); dm9000WriteReg(DM9000_REG_TXPLH, MSB(length)); //Clear interrupt flag dm9000WriteReg(DM9000_REG_ISR, ISR_PT); //Start data transfer dm9000WriteReg(DM9000_REG_TCR, TCR_TXREQ); //The packet was successfully written to FIFO context->queuedPackets++; //Successful processing return NO_ERROR; }
error_t sam9263EthSendPacket(NetInterface *interface, const ChunkedBuffer *buffer, size_t offset) { //Retrieve the length of the packet size_t length = chunkedBufferGetLength(buffer) - offset; //Check the frame length if(length > SAM9263_ETH_TX_BUFFER_SIZE) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); //Report an error return ERROR_INVALID_LENGTH; } //Make sure the current buffer is available for writing if(!(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED)) return ERROR_FAILURE; //Copy user data to the transmit buffer chunkedBufferRead(txBuffer[txBufferIndex], buffer, offset, length); //Set the necessary flags in the descriptor entry if(txBufferIndex < (SAM9263_ETH_TX_BUFFER_COUNT - 1)) { //Write the status word txBufferDesc[txBufferIndex].status = AT91C_EMAC_TX_LAST | (length & AT91C_EMAC_TX_LENGTH); //Point to the next buffer txBufferIndex++; } else { //Write the status word txBufferDesc[txBufferIndex].status = AT91C_EMAC_TX_WRAP | AT91C_EMAC_TX_LAST | (length & AT91C_EMAC_TX_LENGTH); //Wrap around txBufferIndex = 0; } //Set the TSTART bit to initiate transmission AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART; //Check whether the next buffer is available for writing if(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); } //Successful processing return NO_ERROR; }
void icmpProcessMessage(NetInterface *interface, Ipv4Addr srcIpAddr, const ChunkedBuffer *buffer, size_t offset) { size_t length; IcmpHeader *header; //Retrieve the length of the ICMP message length = chunkedBufferGetLength(buffer) - offset; //Ensure the message length is correct if(length < sizeof(IcmpHeader)) { //Debug message TRACE_WARNING("ICMP message length is invalid!\r\n"); //Silently discard incoming message return; } //Point to the ICMP message header header = chunkedBufferAt(buffer, offset); //Sanity check if(!header) return; //Debug message TRACE_INFO("ICMP message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpDumpMessage(header); //Verify checksum value if(ipCalcChecksumEx(buffer, offset, length) != 0x0000) { //Debug message TRACE_WARNING("Wrong ICMP header checksum!\r\n"); //Drop incoming message return; } //Check the type of ICMP message switch(header->type) { //Echo request? case ICMP_TYPE_ECHO_REQUEST: //Process Echo Request message icmpProcessEchoRequest(interface, srcIpAddr, buffer, offset); break; //Unknown type? default: //Debug message TRACE_WARNING("Unknown ICMP message type!\r\n"); //Discard incoming ICMP message break; } }
void mdnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const ChunkedBuffer *buffer, size_t offset, void *params) { size_t length; DnsHeader *message; //Retrieve the length of the mDNS message length = chunkedBufferGetLength(buffer) - offset; //Ensure the mDNS message is valid if(length < sizeof(DnsHeader)) return; if(length > DNS_MESSAGE_MAX_SIZE) return; //Point to the mDNS message header message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("mDNS message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); //mDNS messages received with an opcode other than zero must be silently ignored if(message->opcode != DNS_OPCODE_QUERY) return; //mDNS messages received with non-zero response codes must be silently ignored if(message->rcode != DNS_RCODE_NO_ERROR) return; #if (MDNS_RESPONDER_SUPPORT == ENABLED) //mDNS query received? if(!message->qr) { //Process incoming mDNS query message mdnsProcessQuery(interface, pseudoHeader, udpHeader, message, length); } #endif #if (MDNS_CLIENT_SUPPORT == ENABLED) //mDNS response received? if(message->qr) { //Process incoming mDNS response message mdnsProcessResponse(interface, pseudoHeader, udpHeader, message, length); } #endif }
error_t m2sxxxEthSendPacket(NetInterface *interface, const ChunkedBuffer *buffer, size_t offset) { //Retrieve the length of the packet size_t length = chunkedBufferGetLength(buffer) - offset; //Check the frame length if(length > M2SXXX_ETH_TX_BUFFER_SIZE) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); //Report an error return ERROR_INVALID_LENGTH; } //Make sure the current buffer is available for writing if(!(txCurDmaDesc->size & DMA_DESC_EMPTY_FLAG)) return ERROR_FAILURE; //Copy user data to the transmit buffer chunkedBufferRead((uint8_t *) txCurDmaDesc->addr, buffer, offset, length); //Set the packet length and give the ownership of the descriptor to the DMA txCurDmaDesc->size = length & DMA_DESC_SIZE_MASK; //Check whether DMA transfers are suspended if(!(MAC->DMA_TX_CTRL & DMA_TX_CTRL_TX_EN)) { //Set the start position in the ring buffer MAC->DMA_TX_DESC = (uint32_t) txCurDmaDesc; } //Instruct the DMA controller to transfer the packet MAC->DMA_TX_CTRL = DMA_TX_CTRL_TX_EN; //Point to the next descriptor in the list txCurDmaDesc = (M2sxxxTxDmaDesc *) txCurDmaDesc->next; //Check whether the next buffer is available for writing if(txCurDmaDesc->size & DMA_DESC_EMPTY_FLAG) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); } //Data successfully written return NO_ERROR; }
void ndpProcessRouterAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { size_t length; NdpRouterAdvMessage *message; //Retrieve the length of the message length = chunkedBufferGetLength(buffer) - offset; //Check the length of the Router Advertisement message if(length < sizeof(NdpRouterAdvMessage)) return; //Point to the beginning of the message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("Router Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpRouterAdvMessage(message); //Routers must use their link-local address as the source for the //Router Advertisement so that hosts can uniquely identify routers if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) return; //The IPv6 Hop Limit field must have a value of 255 to ensure //that the packet has not been forwarded by a router if(hopLimit != NDP_HOP_LIMIT) return; //ICMPv6 Code must be 0. An advertisement that passes the validity //checks is called a valid advertisement if(message->code) return; #if (SLAAC_SUPPORT == ENABLED) //Stateless Address Autoconfiguration is currently used? if(interface->slaacContext != NULL) { //Process the valid advertisement slaacProcessRouterAdv(interface->slaacContext, &pseudoHeader->srcAddr, message, length); } #endif }
error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, ChunkedBuffer *buffer, size_t offset, uint8_t timeToLive) { error_t error; size_t length; uint16_t id; //Retrieve the length of payload length = chunkedBufferGetLength(buffer) - offset; //Identification field is primarily used to identify //fragments of an original IP datagram id = osAtomicInc16(&interface->ipv4Identification); //If the payload length is smaller than the network //interface MTU then no fragmentation is needed if(length <= IPV4_MAX_PAYLOAD_SIZE) { //Send data as is error = ipv4SendPacket(interface, pseudoHeader, id, 0, buffer, offset, timeToLive); } //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, timeToLive); #else //Fragmentation is not supported error = ERROR_MESSAGE_TOO_LONG; #endif } //Return status code return error; }
void icmpProcessEchoRequest(NetInterface *interface, Ipv4Addr srcIpAddr, const ChunkedBuffer *request, size_t requestOffset) { error_t error; size_t requestLength; size_t replyOffset; size_t replyLength; ChunkedBuffer *reply; IcmpEchoMessage *requestHeader; IcmpEchoMessage *replyHeader; Ipv4PseudoHeader pseudoHeader; //Retrieve the length of the Echo Request message requestLength = chunkedBufferGetLength(request) - requestOffset; //Ensure the packet length is correct if(requestLength < sizeof(IcmpEchoMessage)) return; //Point to the Echo Request header requestHeader = chunkedBufferAt(request, requestOffset); //Sanity check if(!requestHeader) return; //Debug message TRACE_INFO("ICMP Echo Request message received (%" PRIuSIZE " bytes)...\r\n", requestLength); //Dump message contents for debugging purpose icmpDumpEchoMessage(requestHeader); //Allocate memory to hold the Echo Reply message reply = ipAllocBuffer(sizeof(IcmpEchoMessage), &replyOffset); //Failed to allocate memory? if(!reply) return; //Point to the Echo Reply header replyHeader = chunkedBufferAt(reply, replyOffset); //Format Echo Reply header replyHeader->type = ICMP_TYPE_ECHO_REPLY; replyHeader->code = 0; replyHeader->checksum = 0; replyHeader->identifier = requestHeader->identifier; replyHeader->sequenceNumber = requestHeader->sequenceNumber; //Point to the first data byte requestOffset += sizeof(IcmpEchoMessage); requestLength -= sizeof(IcmpEchoMessage); //Copy data error = chunkedBufferConcat(reply, request, requestOffset, requestLength); //Any error to report? if(error) { //Clean up side effects chunkedBufferFree(reply); //Exit immediately return; } //Get the length of the resulting message replyLength = chunkedBufferGetLength(reply) - replyOffset; //Calculate ICMP header checksum replyHeader->checksum = ipCalcChecksumEx(reply, replyOffset, replyLength); //Format IPv4 pseudo header pseudoHeader.srcAddr = interface->ipv4Config.addr; pseudoHeader.destAddr = srcIpAddr; pseudoHeader.reserved = 0; pseudoHeader.protocol = IPV4_PROTOCOL_ICMP; pseudoHeader.length = htons(replyLength); //Debug message TRACE_INFO("Sending ICMP Echo Reply message (%" PRIuSIZE " bytes)...\r\n", replyLength); //Dump message contents for debugging purpose icmpDumpEchoMessage(replyHeader); //Send Echo Reply message ipv4SendDatagram(interface, &pseudoHeader, reply, replyOffset, IPV4_DEFAULT_TTL); //Free previously allocated memory block chunkedBufferFree(reply); }
void ndpProcessNeighborAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { size_t length; NdpNeighborAdvMessage *message; NdpLinkLayerAddrOption *option; NdpCacheEntry *entry; //Retrieve the length of the message length = chunkedBufferGetLength(buffer) - offset; //Check the length of the Neighbor Advertisement message if(length < sizeof(NdpNeighborAdvMessage)) return; //Point to the beginning of the message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("Neighbor Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpNeighborAdvMessage(message); //The IPv6 Hop Limit field must have a value of 255 to ensure //that the packet has not been forwarded by a router if(hopLimit != NDP_HOP_LIMIT) return; //ICMPv6 Code must be 0 if(message->code) return; //Check whether the target address is tentative or matches //a unicast address assigned to the interface if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.linkLocalAddr) && interface->ipv6Config.linkLocalAddrState != IPV6_ADDR_STATE_INVALID) { //Debug message TRACE_WARNING("The address %s is a duplicate!\r\n", ipv6AddrToString(&interface->ipv6Config.linkLocalAddr, NULL)); //The address is a duplicate and should not be used interface->ipv6Config.linkLocalAddrDup = TRUE; //Exit immediately return; } else if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.globalAddr) && interface->ipv6Config.globalAddrState != IPV6_ADDR_STATE_INVALID) { //Debug message TRACE_WARNING("The address %s is a duplicate!\r\n", ipv6AddrToString(&interface->ipv6Config.globalAddr, NULL)); //The address is a duplicate and should not be used interface->ipv6Config.globalAddrDup = TRUE; //Exit immediately return; } //The target address must not be a multicast address if(ipv6IsMulticastAddr(&message->targetAddr)) { //Debug message TRACE_WARNING("Target address must not be a multicast address!\r\n"); //Exit immediately return; } //If the destination address is a multicast address //then the Solicited flag must be zero if(ipv6IsMulticastAddr(&pseudoHeader->destAddr) && message->s) { //Debug message TRACE_WARNING("Solicited flag must be zero!\r\n"); //Exit immediately return; } //Calculate the length of the Options field length -= sizeof(NdpNeighborSolMessage); //Search for the Target Link-Layer Address option option = ndpGetOption(message->options, length, NDP_OPT_TARGET_LINK_LAYER_ADDR); //Source Link-Layer Address option found? if(option && option->length == 1) { //Debug message TRACE_DEBUG(" Target Link-Layer Address = %s\r\n", macAddrToString(&option->linkLayerAddr, NULL)); //Acquire exclusive access to Neighbor cache osAcquireMutex(&interface->ndpCacheMutex); //Search the Neighbor cache for the specified target address entry = ndpFindEntry(interface, &message->targetAddr); //If no entry exists, the advertisement should be silently discarded if(entry) { //INCOMPLETE state? if(entry->state == NDP_STATE_INCOMPLETE) { //Record link-layer address entry->macAddr = option->linkLayerAddr; //Send all the packets that are pending for transmission ndpSendQueuedPackets(interface, entry); //Save current time entry->timestamp = osGetSystemTime(); //Solicited flag is set? if(message->s) { //Computing the random ReachableTime value entry->timeout = NDP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = NDP_STATE_REACHABLE; } //Solicited flag is cleared? else { //Enter the STALE state entry->state = NDP_STATE_STALE; } } //REACHABLE, STALE, DELAY or PROBE state? else { //Solicited flag is set and Override flag is cleared? if(message->s && !message->o) { //Same link-layer address than cached? if(macCompAddr(&entry->macAddr, &option->linkLayerAddr)) { //Save current time entry->timestamp = osGetSystemTime(); //Computing the random ReachableTime value entry->timeout = NDP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = NDP_STATE_REACHABLE; } //Different link-layer address than cached? else { //REACHABLE state? if(entry->state == NDP_STATE_REACHABLE) { //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } } } //Both Solicited and Override flags are set? else if(message->s && message->o) { //Record link-layer address (if different) entry->macAddr = option->linkLayerAddr; //Save current time entry->timestamp = osGetSystemTime(); //Computing the random ReachableTime value entry->timeout = NDP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = NDP_STATE_REACHABLE; } //Solicited flag is cleared and Override flag is set? else if(!message->s && message->o) { //Different link-layer address than cached? if(!macCompAddr(&entry->macAddr, &option->linkLayerAddr)) { //Record link-layer address entry->macAddr = option->linkLayerAddr; //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } } } } } //Source Link-Layer Address option not found? else { //Update content of IsRouter flag } //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); }
void ndpProcessNeighborSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { size_t length; NdpNeighborSolMessage *message; NdpLinkLayerAddrOption *option; NdpCacheEntry *entry; //Retrieve the length of the message length = chunkedBufferGetLength(buffer) - offset; //Check the length of the Neighbor Solicitation message if(length < sizeof(NdpNeighborSolMessage)) return; //Point to the beginning of the message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("Neighbor Solicitation message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpNeighborSolMessage(message); //The IPv6 Hop Limit field must have a value of 255 to ensure //that the packet has not been forwarded by a router if(hopLimit != NDP_HOP_LIMIT) return; //ICMPv6 Code must be 0 if(message->code) return; //The target address must a valid unicast address assigned to the interface //or a tentative address on which DAD is being performed if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.linkLocalAddr)) { //Check whether the target address is tentative if(interface->ipv6Config.linkLocalAddrState == IPV6_ADDR_STATE_TENTATIVE) { //If the source address of the Neighbor Solicitation is the unspecified //address, the solicitation is from a node performing Duplicate Address //Detection if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) { //Debug message TRACE_WARNING("The tentative address %s is a duplicate!\r\n", ipv6AddrToString(&interface->ipv6Config.linkLocalAddr, NULL)); //The tentative address is a duplicate and should not be used interface->ipv6Config.linkLocalAddrDup = TRUE; } //In all cases, a node must not respond to a Neighbor Solicitation //for a tentative address return; } } else if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.globalAddr)) { //Check whether the target address is tentative if(interface->ipv6Config.globalAddrState == IPV6_ADDR_STATE_TENTATIVE) { //If the source address of the Neighbor Solicitation is the unspecified //address, the solicitation is from a node performing Duplicate Address //Detection if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) { //Debug message TRACE_WARNING("The tentative address %s is a duplicate!\r\n", ipv6AddrToString(&interface->ipv6Config.globalAddr, NULL)); //The tentative address is a duplicate and should not be used interface->ipv6Config.globalAddrDup = TRUE; } //In all cases, a node must not respond to a Neighbor Solicitation //for a tentative address return; } } else { //Debug message TRACE_WARNING("Wrong target address!\r\n"); //Exit immediately return; } //If the IP source address is the unspecified address, the IP //destination address must be a solicited-node multicast address if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR) && !ipv6IsSolicitedNodeAddr(&pseudoHeader->destAddr)) { //Debug message TRACE_WARNING("Destination address must be a solicited-node address!\r\n"); //Exit immediately return; } //Calculate the length of the Options field length -= sizeof(NdpNeighborSolMessage); //Search for the Source Link-Layer Address option option = ndpGetOption(message->options, length, NDP_OPT_SOURCE_LINK_LAYER_ADDR); //Source Link-Layer Address option found? if(option && option->length == 1) { //Debug message TRACE_DEBUG(" Source Link-Layer Address = %s\r\n", macAddrToString(&option->linkLayerAddr, NULL)); //If the Source Address is not the unspecified address, then the Neighbor //cache should be updated for the IP source address of the solicitation if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) return; //Acquire exclusive access to Neighbor cache osAcquireMutex(&interface->ndpCacheMutex); //Search the Neighbor cache for the source address of the solicitation entry = ndpFindEntry(interface, &pseudoHeader->srcAddr); //No matching entry has been found? if(!entry) { //Create an entry entry = ndpCreateEntry(interface); //Neighbor cache entry successfully created? if(entry) { //Record the IPv6 and the corresponding MAC address entry->ipAddr = pseudoHeader->srcAddr; entry->macAddr = option->linkLayerAddr; //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } } else { //INCOMPLETE state? if(entry->state == NDP_STATE_INCOMPLETE) { //Record link-layer address entry->macAddr = option->linkLayerAddr; //Send all the packets that are pending for transmission ndpSendQueuedPackets(interface, entry); //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } //REACHABLE, STALE, DELAY or PROBE state? else { //Different link-layer address than cached? if(!macCompAddr(&entry->macAddr, &option->linkLayerAddr)) { //Update link-layer address entry->macAddr = option->linkLayerAddr; //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } } } //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); } //Source Link-Layer Address option not found? else { //This option must be included in multicast solicitations if(ipv6IsMulticastAddr(&pseudoHeader->destAddr)) { //Debug message TRACE_WARNING("The Source Link-Layer Address must be included!\r\n"); //Exit immediately return; } } //After any updates to the Neighbor cache, the node sends a Neighbor //Advertisement response as described in RFC 4861 7.2.4 ndpSendNeighborAdv(interface, &message->targetAddr, &pseudoHeader->srcAddr); }
error_t ndpEnqueuePacket(NetInterface *interface, const Ipv6Addr *ipAddr, ChunkedBuffer *buffer, size_t offset) { error_t error; uint_t i; size_t length; NdpCacheEntry *entry; //Retrieve the length of the multi-part buffer length = chunkedBufferGetLength(buffer); //Acquire exclusive access to Neighbor cache osAcquireMutex(&interface->ndpCacheMutex); //Search the Neighbor cache for the specified Ipv6 address entry = ndpFindEntry(interface, ipAddr); //No matching entry in Neighbor cache? if(!entry) { //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //Report an error to the calling function return ERROR_FAILURE; } //Check current state if(entry->state == NDP_STATE_INCOMPLETE) { //Check whether the packet queue is full if(entry->queueSize >= NDP_MAX_PENDING_PACKETS) { //When the queue overflows, the new arrival should replace the oldest entry chunkedBufferFree(entry->queue[0].buffer); //Make room for the new packet for(i = 1; i < NDP_MAX_PENDING_PACKETS; i++) entry->queue[i - 1] = entry->queue[i]; //Adjust the number of pending packets entry->queueSize--; } //Index of the entry to be filled in i = entry->queueSize; //Allocate a memory buffer to store the packet entry->queue[i].buffer = chunkedBufferAlloc(length); //Failed to allocate memory? if(!entry->queue[i].buffer) { //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //Report an error to the calling function return ERROR_OUT_OF_MEMORY; } //Copy packet contents chunkedBufferCopy(entry->queue[i].buffer, 0, buffer, 0, length); //Offset to the first byte of the IPv6 header entry->queue[i].offset = offset; //Increment the number of queued packets entry->queueSize++; //The packet was successfully enqueued error = NO_ERROR; } else { //Send immediately the packet since the address is already resolved error = ethSendFrame(interface, &entry->macAddr, buffer, offset, ETH_TYPE_IPV6); } //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //Return status code return error; }
void icmpv6ProcessMessage(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { size_t length; Icmpv6Header *header; //Retrieve the length of the ICMPv6 message length = chunkedBufferGetLength(buffer) - offset; //Ensure the message length is correct if(length < sizeof(Icmpv6Header)) { //Debug message TRACE_WARNING("ICMPv6 message length is invalid!\r\n"); //Exit immediately return; } //Point to the ICMPv6 message header header = chunkedBufferAt(buffer, offset); //Sanity check if(!header) return; //Debug message TRACE_INFO("ICMPv6 message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpv6DumpMessage(header); //Verify checksum value if(ipCalcUpperLayerChecksumEx(pseudoHeader, sizeof(Ipv6PseudoHeader), buffer, offset, length) != 0xFFFF) { //Debug message TRACE_WARNING("Wrong ICMPv6 header checksum!\r\n"); //Exit immediately return; } //Check whether the destination address is the tentative address if(ipv6IsTentativeAddr(interface, &pseudoHeader->destAddr)) { //The interface must accept Neighbor Solicitation and //Neighbor Advertisement messages if(header->type != ICMPV6_TYPE_NEIGHBOR_SOL && header->type != ICMPV6_TYPE_NEIGHBOR_ADV) { //Other packets addressed to the tentative address //should be silently discarded return; } } //Check the type of message switch(header->type) { //Echo Request message? case ICMPV6_TYPE_ECHO_REQUEST: //Process Echo Request message icmpv6ProcessEchoRequest(interface, pseudoHeader, buffer, offset); break; #if (MLD_SUPPORT == ENABLED) //Multicast Listener Query message? case ICMPV6_TYPE_MULTICAST_LISTENER_QUERY: //Process Multicast Listener Query message mldProcessListenerQuery(interface, pseudoHeader, buffer, offset, hopLimit); break; //Version 1 Multicast Listener Report message? case ICMPV6_TYPE_MULTICAST_LISTENER_REPORT_V1: //Process Version 1 Multicast Listener Report message mldProcessListenerReport(interface, pseudoHeader, buffer, offset, hopLimit); break; #endif #if (NDP_SUPPORT == ENABLED) //Router Advertisement message? case ICMPV6_TYPE_ROUTER_ADV: //Process Router Advertisement message ndpProcessRouterAdv(interface, pseudoHeader, buffer, offset, hopLimit); break; //Neighbor Solicitation message? case ICMPV6_TYPE_NEIGHBOR_SOL: //Process Neighbor Solicitation message ndpProcessNeighborSol(interface, pseudoHeader, buffer, offset, hopLimit); break; //Neighbor Advertisement message? case ICMPV6_TYPE_NEIGHBOR_ADV: //Process Neighbor Advertisement message ndpProcessNeighborAdv(interface, pseudoHeader, buffer, offset, hopLimit); break; #endif //Unknown type? default: //Debug message TRACE_WARNING("Unknown ICMPv6 message type!\r\n"); //Discard incoming ICMPv6 message break; } }
void mldProcessListenerQuery(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { uint_t i; size_t length; systime_t time; systime_t maxRespDelay; MldMessage *message; Ipv6FilterEntry *entry; //Retrieve the length of the MLD message length = chunkedBufferGetLength(buffer) - offset; //The message must be at least 24 octets long if(length < sizeof(MldMessage)) return; //Point to the beginning of the MLD message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose mldDumpMessage(message); //Make sure the source address of the message is a valid link-local address if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) return; //Check the Hop Limit field if(hopLimit != MLD_HOP_LIMIT) return; //Get current time time = osGetTickCount(); //The Max Resp Delay field specifies the maximum time allowed //before sending a responding report maxRespDelay = message->maxRespDelay * 10; //Acquire exclusive access to the IPv6 filter table osMutexAcquire(interface->ipv6FilterMutex); //Loop through filter table entries for(i = 0; i < interface->ipv6FilterSize; i++) { //Point to the current entry entry = &interface->ipv6Filter[i]; //The link-scope all-nodes address (FF02::1) is handled as a special //case. The host starts in Idle Listener state for that address on //every interface and never transitions to another state if(ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) continue; //A General Query is used to learn which multicast addresses have listeners //on an attached link. A Multicast-Address-Specific Query is used to learn //if a particular multicast address has any listeners on an attached link if(ipv6CompAddr(&message->multicastAddr, &IPV6_UNSPECIFIED_ADDR) || ipv6CompAddr(&message->multicastAddr, &entry->addr)) { //Delaying Listener state? if(entry->state == MLD_STATE_DELAYING_LISTENER) { //The timer has not yet expired? if(timeCompare(time, entry->timer) < 0) { //If a timer for the address is already running, it is reset to //the new random value only if the requested Max Response Delay //is less than the remaining value of the running timer if(maxRespDelay < (entry->timer - time)) { //Restart delay timer entry->timer = time + mldRand(maxRespDelay); } } } //Idle Listener state? else if(entry->state == MLD_STATE_IDLE_LISTENER) { //Switch to the Delaying Listener state entry->state = MLD_STATE_DELAYING_LISTENER; //Delay the response by a random amount of time entry->timer = time + mldRand(maxRespDelay); } } } //Release exclusive access to the IPv6 filter table osMutexRelease(interface->ipv6FilterMutex); }
void ipv4ProcessDatagram(NetInterface *interface, const MacAddr *srcMacAddr, const ChunkedBuffer *buffer) { error_t error; size_t offset; size_t length; Ipv4Header *header; IpPseudoHeader pseudoHeader; //Retrieve the length of the IPv4 datagram length = chunkedBufferGetLength(buffer); //Point to the IPv4 header header = chunkedBufferAt(buffer, 0); //Sanity check if(!header) return; //Debug message TRACE_INFO("IPv4 datagram received (%u 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); //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 rawSocketProcessDatagram(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 rawSocketProcessDatagram(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 = rawSocketProcessDatagram(interface, &pseudoHeader, buffer, offset); #else //Report an error error = ERROR_PROTOCOL_UNREACHABLE; #endif //Continue processing break; } //Unreachable protocol? if(error == ERROR_PROTOCOL_UNREACHABLE) { //Send a Destination Unreachable message icmpSendErrorMessage(interface, ICMP_TYPE_DEST_UNREACHABLE, ICMP_CODE_PROTOCOL_UNREACHABLE, 0, buffer); } //Unreachable port? else if(error == ERROR_PORT_UNREACHABLE) { //Send a Destination Unreachable message icmpSendErrorMessage(interface, ICMP_TYPE_DEST_UNREACHABLE, ICMP_CODE_PORT_UNREACHABLE, 0, buffer); } }
error_t ipv6FragmentDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *payload, size_t payloadOffset, uint8_t hopLimit) { error_t error; uint32_t id; size_t offset; size_t length; size_t payloadLength; size_t fragmentOffset; ChunkedBuffer *fragment; //Identification field is used to identify fragments of an original IP datagram id = osAtomicInc32(&interface->ipv6Identification); //Retrieve the length of the payload payloadLength = chunkedBufferGetLength(payload) - payloadOffset; //Allocate a memory buffer to hold IP fragments fragment = ipAllocBuffer(0, &fragmentOffset); //Failed to allocate memory? if(!fragment) return ERROR_OUT_OF_MEMORY; //Split the payload into multiple IP fragments for(offset = 0; offset < payloadLength; offset += length) { //Flush the contents of the fragment error = chunkedBufferSetLength(fragment, fragmentOffset); //Sanity check if(error) break; //Process the last fragment? if((payloadLength - offset) <= IPV6_MAX_FRAG_SIZE) { //Size of the current fragment length = payloadLength - offset; //Copy fragment data chunkedBufferConcat(fragment, payload, payloadOffset + offset, length); //Do not set the MF flag for the last fragment error = ipv6SendPacket(interface, pseudoHeader, id, offset, fragment, fragmentOffset, hopLimit); } else { //Size of the current fragment (must be a multiple of 8-byte blocks) length = IPV6_MAX_FRAG_SIZE; //Copy fragment data chunkedBufferConcat(fragment, payload, payloadOffset + offset, length); //Fragmented packets must have the M flag set error = ipv6SendPacket(interface, pseudoHeader, id, offset | IPV6_FLAG_M, fragment, fragmentOffset, hopLimit); } //Failed to send current IP fragment? if(error) break; } //Free previously allocated memory chunkedBufferFree(fragment); //Return status code return error; }
void ipv6ParseFragmentHeader(NetInterface *interface, const ChunkedBuffer *buffer, size_t fragHeaderOffset, size_t nextHeaderOffset) { error_t error; size_t length; uint16_t offset; uint16_t dataFirst; uint16_t dataLast; Ipv6FragDesc *frag; Ipv6HoleDesc *hole; Ipv6HoleDesc *prevHole; Ipv6Header *packet; Ipv6FragmentHeader *header; //Remaining bytes to process in the payload length = chunkedBufferGetLength(buffer) - fragHeaderOffset; //Ensure the fragment header is valid if(length < sizeof(Ipv6FragmentHeader)) return; //Point to the IPv6 header packet = chunkedBufferAt(buffer, 0); //Sanity check if(!packet) return; //Point to the IPv6 Fragment header header = chunkedBufferAt(buffer, fragHeaderOffset); //Sanity check if(!header) return; //Calculate the length of the fragment length -= sizeof(Ipv6FragmentHeader); //Convert the fragment offset from network byte order offset = ntohs(header->fragmentOffset); //Every fragment except the last must contain a multiple of 8 bytes of data if((offset & IPV6_FLAG_M) && (length % 8)) { //Compute the offset of the Payload Length field within the packet size_t n = (uint8_t *) &packet->payloadLength - (uint8_t *) packet; //The fragment must be discarded and an ICMP Parameter Problem //message should be sent to the source of the fragment, pointing //to the Payload Length field of the fragment packet icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM, ICMPV6_CODE_INVALID_HEADER_FIELD, n, buffer); //Exit immediately return; } //Calculate the index of the first byte dataFirst = offset & IPV6_OFFSET_MASK; //Calculate the index immediately following the last byte dataLast = dataFirst + length; //Search for a matching IP datagram being reassembled frag = ipv6SearchFragQueue(interface, packet, header); //No matching entry in the reassembly queue? if(!frag) return; //The very first fragment requires special handling if(!(offset & IPV6_OFFSET_MASK)) { uint8_t *p; //Calculate the length of the unfragmentable part frag->unfragPartLength = fragHeaderOffset; //The size of the reconstructed datagram exceeds the maximum value? if((frag->unfragPartLength + frag->fragPartLength) > IPV6_MAX_FRAG_DATAGRAM_SIZE) { //Compute the offset of the Fragment Offset field within the packet size_t n = fragHeaderOffset + (uint8_t *) &header->fragmentOffset - (uint8_t *) header; //The fragment must be discarded and an ICMP Parameter Problem //message should be sent to the source of the fragment, pointing //to the Fragment Offset field of the fragment packet icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM, ICMPV6_CODE_INVALID_HEADER_FIELD, n, buffer); //Drop any allocated resources chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return; } //Make sure the unfragmentable part entirely fits in the first chunk if(frag->unfragPartLength > frag->buffer.chunk[0].size) { //Drop the reconstructed datagram chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return; } //Fix the length of the first chunk frag->buffer.chunk[0].length = frag->unfragPartLength; //The unfragmentable part of the reassembled packet consists //of all headers up to, but not including, the Fragment header //of the first fragment packet chunkedBufferCopy((ChunkedBuffer *) &frag->buffer, 0, buffer, 0, frag->unfragPartLength); //Point to the Next Header field of the last header p = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, nextHeaderOffset); //The Next Header field of the last header of the unfragmentable //part is obtained from the Next Header field of the first //fragment's Fragment header *p = header->nextHeader; } //It may be necessary to increase the size of the buffer... if(dataLast > frag->fragPartLength) { //The size of the reconstructed datagram exceeds the maximum value? if((frag->unfragPartLength + dataLast) > IPV6_MAX_FRAG_DATAGRAM_SIZE) { //Compute the offset of the Fragment Offset field within the packet size_t n = fragHeaderOffset + (uint8_t *) &header->fragmentOffset - (uint8_t *) header; //The fragment must be discarded and an ICMP Parameter Problem //message should be sent to the source of the fragment, pointing //to the Fragment Offset field of the fragment packet icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM, ICMPV6_CODE_INVALID_HEADER_FIELD, n, buffer); //Drop any allocated resources chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return; } //Adjust the size of the reconstructed datagram error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, frag->unfragPartLength + dataLast + sizeof(Ipv6HoleDesc)); //Any error to report? if(error) { //Drop the reconstructed datagram chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return; } //Actual length of the fragmentable part frag->fragPartLength = dataLast; } //Select the first hole descriptor from the list hole = ipv6FindHole(frag, frag->firstHole); //Keep track of the previous hole in the list prevHole = NULL; //Iterate through the hole descriptors while(hole != NULL) { //Save lower and upper boundaries for later use uint16_t holeFirst = hole->first; uint16_t holeLast = hole->last; //Check whether the newly arrived fragment //interacts with this hole in some way if(dataFirst < holeLast && dataLast > holeFirst) { //The current descriptor is no longer valid. We will destroy //it, and in the next two steps, we will determine whether //or not it is necessary to create any new hole descriptors if(prevHole != NULL) prevHole->next = hole->next; else frag->firstHole = hole->next; //Is there still a hole at the beginning of the segment? if(dataFirst > holeFirst) { //Create a new entry that describes this hole hole = ipv6FindHole(frag, holeFirst); hole->first = holeFirst; hole->last = dataFirst; //Insert the newly created entry into the hole descriptor list if(prevHole != NULL) { hole->next = prevHole->next; prevHole->next = hole->first; } else { hole->next = frag->firstHole; frag->firstHole = hole->first; } //Always keep track of the previous hole prevHole = hole; } //Is there still a hole at the end of the segment? if(dataLast < holeLast && (offset & IPV6_FLAG_M)) { //Create a new entry that describes this hole hole = ipv6FindHole(frag, dataLast); hole->first = dataLast; hole->last = holeLast; //Insert the newly created entry into the hole descriptor list if(prevHole != NULL) { hole->next = prevHole->next; prevHole->next = hole->first; } else { hole->next = frag->firstHole; frag->firstHole = hole->first; } //Always keep track of the previous hole prevHole = hole; } } else { //The newly arrived fragment does not interact with the current hole prevHole = hole; } //Select the next hole descriptor from the list hole = ipv6FindHole(frag, prevHole ? prevHole->next : frag->firstHole); } //Copy data from the fragment to the reassembly buffer chunkedBufferCopy((ChunkedBuffer *) &frag->buffer, frag->unfragPartLength + dataFirst, buffer, fragHeaderOffset + sizeof(Ipv6FragmentHeader), length); //Dump hole descriptor list ipv6DumpHoleList(frag); //If the hole descriptor list is empty, the reassembly process is now complete if(!ipv6FindHole(frag, frag->firstHole)) { //Discard the extra hole descriptor that follows the reconstructed datagram error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, frag->unfragPartLength + frag->fragPartLength); //Check status code if(!error) { //Point to the IPv6 header Ipv6Header *datagram = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, 0); //Fix the Payload Length field datagram->payloadLength = htons(frag->unfragPartLength + frag->fragPartLength - sizeof(Ipv6Header)); //Pass the original IPv6 datagram to the higher protocol layer ipv6ProcessPacket(interface, (ChunkedBuffer *) &frag->buffer); } //Release previously allocated memory chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); } }
error_t udpSendDatagramEx(NetInterface *interface, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, ChunkedBuffer *buffer, size_t offset, uint8_t ttl) { error_t error; size_t length; UdpHeader *header; IpPseudoHeader pseudoHeader; //Make room for the UDP header offset -= sizeof(UdpHeader); //Retrieve the length of the datagram length = chunkedBufferGetLength(buffer) - offset; //Point to the UDP header header = chunkedBufferAt(buffer, offset); //Sanity check if(!header) return ERROR_FAILURE; //Format UDP header header->srcPort = htons(srcPort); header->destPort = htons(destPort); header->length = htons(length); header->checksum = 0; #if (IPV4_SUPPORT == ENABLED) //Destination address is an IPv4 address? if(destIpAddr->length == sizeof(Ipv4Addr)) { Ipv4Addr srcIpAddr; //Select the source IPv4 address and the relevant network interface //to use when sending data to the specified destination host error = ipv4SelectSourceAddr(&interface, destIpAddr->ipv4Addr, &srcIpAddr); //Any error to report? if(error) return error; //Format IPv4 pseudo header pseudoHeader.length = sizeof(Ipv4PseudoHeader); pseudoHeader.ipv4Data.srcAddr = srcIpAddr; pseudoHeader.ipv4Data.destAddr = destIpAddr->ipv4Addr; pseudoHeader.ipv4Data.reserved = 0; pseudoHeader.ipv4Data.protocol = IPV4_PROTOCOL_UDP; pseudoHeader.ipv4Data.length = htons(length); //Calculate UDP header checksum header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv4Data, sizeof(Ipv4PseudoHeader), buffer, offset, length); } else #endif #if (IPV6_SUPPORT == ENABLED) //Destination address is an IPv6 address? if(destIpAddr->length == sizeof(Ipv6Addr)) { //Select the source IPv6 address and the relevant network interface //to use when sending data to the specified destination host error = ipv6SelectSourceAddr(&interface, &destIpAddr->ipv6Addr, &pseudoHeader.ipv6Data.srcAddr); //Any error to report? if(error) return error; //Format IPv6 pseudo header pseudoHeader.length = sizeof(Ipv6PseudoHeader); pseudoHeader.ipv6Data.destAddr = destIpAddr->ipv6Addr; pseudoHeader.ipv6Data.length = htonl(length); pseudoHeader.ipv6Data.reserved = 0; pseudoHeader.ipv6Data.nextHeader = IPV6_UDP_HEADER; //Calculate UDP header checksum header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv6Data, sizeof(Ipv6PseudoHeader), buffer, offset, length); } else #endif //Invalid destination address? { //An internal error has occurred return ERROR_FAILURE; } //Debug message TRACE_INFO("Sending UDP datagram (%" PRIuSIZE " bytes)\r\n", length); //Dump UDP header contents for debugging purpose udpDumpHeader(header); //Send UDP datagram return ipSendDatagram(interface, &pseudoHeader, buffer, offset, ttl); }
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; }
error_t rawSocketProcessIpPacket(NetInterface *interface, IpPseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset) { uint_t i; size_t length; Socket *socket; SocketQueueItem *queueItem; ChunkedBuffer *p; //Retrieve the length of the raw IP packet length = chunkedBufferGetLength(buffer) - offset; //Enter critical section osAcquireMutex(&socketMutex); //Loop through opened sockets for(i = 0; i < SOCKET_MAX_COUNT; i++) { //Point to the current socket socket = socketTable + i; //Raw socket found? if(socket->type != SOCKET_TYPE_RAW_IP) continue; //Check whether the socket is bound to a particular interface if(socket->interface && socket->interface != interface) continue; #if (IPV4_SUPPORT == ENABLED) //An IPv4 packet was received? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Check protocol field if(socket->protocol != pseudoHeader->ipv4Data.protocol) continue; //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)) { //Check protocol field if(socket->protocol != pseudoHeader->ipv6Data.nextHeader) continue; //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; } //The current socket meets all the criteria break; } //Drop incoming packet if no matching socket was found if(i >= SOCKET_MAX_COUNT) { //Leave critical section osReleaseMutex(&socketMutex); //Unreachable protocol... return ERROR_PROTOCOL_UNREACHABLE; } //Empty receive queue? if(!socket->receiveQueue) { //Allocate a memory buffer to hold the data and the associated descriptor p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Point to the newly created item queueItem = chunkedBufferAt(p, 0); queueItem->buffer = p; //Add the newly created item to the queue socket->receiveQueue = queueItem; } else { //Memory allocation failed queueItem = NULL; } } else { //Point to the very first item queueItem = socket->receiveQueue; //Reach the last item in the receive queue for(i = 1; queueItem->next; i++) queueItem = queueItem->next; //Make sure the receive queue is not full if(i >= RAW_SOCKET_RX_QUEUE_SIZE) { //Leave critical section osReleaseMutex(&socketMutex); //Notify the calling function that the queue is full return ERROR_RECEIVE_QUEUE_FULL; } //Allocate a memory buffer to hold the data and the associated descriptor p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Add the newly created item to the queue queueItem->next = chunkedBufferAt(p, 0); //Point to the newly created item queueItem = queueItem->next; queueItem->buffer = p; } else { //Memory allocation failed queueItem = NULL; } } //Failed to allocate memory? if(!queueItem) { //Leave critical section osReleaseMutex(&socketMutex); //Return error code return ERROR_OUT_OF_MEMORY; } //Initialize next field queueItem->next = NULL; //Port number is unused queueItem->srcPort = 0; #if (IPV4_SUPPORT == ENABLED) //IPv4 remote address? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Save the source IPv4 address queueItem->srcIpAddr.length = sizeof(Ipv4Addr); queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr; //Save the destination IPv4 address queueItem->destIpAddr.length = sizeof(Ipv4Addr); queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr; } #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 remote address? if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Save the source IPv6 address queueItem->srcIpAddr.length = sizeof(Ipv6Addr); queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr; //Save the destination IPv6 address queueItem->destIpAddr.length = sizeof(Ipv6Addr); queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr; } #endif //Offset to the raw IP packet queueItem->offset = sizeof(SocketQueueItem); //Copy the raw data chunkedBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length); //Notify user that data is available rawSocketUpdateEvents(socket); //Leave critical section osReleaseMutex(&socketMutex); //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; }
error_t icmpv6SendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code, uint32_t parameter, const ChunkedBuffer *ipPacket) { error_t error; size_t offset; size_t length; Ipv6Header *ipHeader; ChunkedBuffer *icmpMessage; Icmpv6ErrorMessage *icmpHeader; Ipv6PseudoHeader pseudoHeader; //Retrieve the length of the invoking IPv6 packet length = chunkedBufferGetLength(ipPacket); //Check the length of the IPv6 packet if(length < sizeof(Ipv6Header)) 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 an IPv6 multicast address if(ipv6IsMulticastAddr(&ipHeader->destAddr)) return ERROR_INVALID_ADDRESS; //Return as much of invoking IPv6 packet as possible without //the ICMPv6 packet exceeding the minimum IPv6 MTU length = MIN(length, IPV6_DEFAULT_MTU - sizeof(Ipv6Header) - sizeof(Icmpv6ErrorMessage)); //Allocate a memory buffer to hold the ICMPv6 message icmpMessage = ipAllocBuffer(sizeof(Icmpv6ErrorMessage), &offset); //Failed to allocate memory? if(!icmpMessage) return ERROR_OUT_OF_MEMORY; //Point to the ICMPv6 header icmpHeader = chunkedBufferAt(icmpMessage, offset); //Format ICMPv6 Error message icmpHeader->type = type; icmpHeader->code = code; icmpHeader->checksum = 0; icmpHeader->parameter = htonl(parameter); //Copy incoming IPv6 packet contents 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; //Format IPv6 pseudo header pseudoHeader.srcAddr = ipHeader->destAddr; pseudoHeader.destAddr = ipHeader->srcAddr; pseudoHeader.length = htonl(length); pseudoHeader.reserved = 0; pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; //Message checksum calculation icmpHeader->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, sizeof(Ipv6PseudoHeader), icmpMessage, offset, length); //Debug message TRACE_INFO("Sending ICMPv6 Error message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpv6DumpErrorMessage(icmpHeader); //Send ICMPv6 Error message error = ipv6SendDatagram(interface, &pseudoHeader, icmpMessage, offset, IPV6_DEFAULT_HOP_LIMIT); //Free previously allocated memory chunkedBufferFree(icmpMessage); //Return status code return error; }
error_t udpProcessDatagram(NetInterface *interface, IpPseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset) { error_t error; uint_t i; size_t length; UdpHeader *header; Socket *socket; SocketQueueItem *queueItem; ChunkedBuffer *p; //Retrieve the length of the UDP datagram length = chunkedBufferGetLength(buffer) - offset; //Ensure the UDP header is valid if(length < sizeof(UdpHeader)) { //Debug message TRACE_WARNING("UDP datagram length is invalid!\r\n"); //Report an error return ERROR_INVALID_HEADER; } //Point to the UDP header header = chunkedBufferAt(buffer, offset); //Sanity check if(!header) return ERROR_FAILURE; //Debug message TRACE_INFO("UDP datagram received (%" PRIuSIZE " bytes)...\r\n", length); //Dump UDP header contents for debugging purpose udpDumpHeader(header); //When UDP runs over IPv6, the checksum is mandatory if(header->checksum || pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Verify UDP checksum if(ipCalcUpperLayerChecksumEx(pseudoHeader->data, pseudoHeader->length, buffer, offset, length) != 0xFFFF) { //Debug message TRACE_WARNING("Wrong UDP header checksum!\r\n"); //Report an error return ERROR_WRONG_CHECKSUM; } } //Enter critical section osMutexAcquire(socketMutex); //Loop through opened sockets for(i = 0; i < SOCKET_MAX_COUNT; i++) { //Point to the current socket socket = socketTable + i; //UDP socket found? if(socket->type != SOCKET_TYPE_DGRAM) 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(header->destPort)) continue; //Source port number filtering if(socket->remotePort && socket->remotePort != ntohs(header->srcPort)) 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; } //The current socket meets all the criteria break; } //Point to the payload offset += sizeof(UdpHeader); length -= sizeof(UdpHeader); //No matching socket found? if(i >= SOCKET_MAX_COUNT) { //Leave critical section osMutexRelease(socketMutex); //Invoke user callback, if any error = udpInvokeRxCallback(interface, pseudoHeader, header, buffer, offset); //Return status code return error; } //Empty receive queue? if(!socket->receiveQueue) { //Allocate a memory buffer to hold the data and the associated descriptor p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Point to the newly created item queueItem = chunkedBufferAt(p, 0); queueItem->buffer = p; //Add the newly created item to the queue socket->receiveQueue = queueItem; } else { //Memory allocation failed queueItem = NULL; } } else { //Point to the very first item queueItem = socket->receiveQueue; //Reach the last item in the receive queue for(i = 1; queueItem->next; i++) queueItem = queueItem->next; //Make sure the receive queue is not full if(i >= UDP_RX_QUEUE_SIZE) { //Leave critical section osMutexRelease(socketMutex); //Notify the calling function that the queue is full return ERROR_RECEIVE_QUEUE_FULL; } //Allocate a memory buffer to hold the data and the associated descriptor p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Add the newly created item to the queue queueItem->next = chunkedBufferAt(p, 0); //Point to the newly created item queueItem = queueItem->next; queueItem->buffer = p; } else { //Memory allocation failed queueItem = NULL; } } //Failed to allocate memory? if(!queueItem) { //Leave critical section osMutexRelease(socketMutex); //Return error code return ERROR_OUT_OF_MEMORY; } //Initialize next field queueItem->next = NULL; //Record the source port number queueItem->srcPort = ntohs(header->srcPort); #if (IPV4_SUPPORT == ENABLED) //IPv4 remote address? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Save the source IPv4 address queueItem->srcIpAddr.length = sizeof(Ipv4Addr); queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr; //Save the destination IPv4 address queueItem->destIpAddr.length = sizeof(Ipv4Addr); queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr; } #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 remote address? if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Save the source IPv6 address queueItem->srcIpAddr.length = sizeof(Ipv6Addr); queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr; //Save the destination IPv6 address queueItem->destIpAddr.length = sizeof(Ipv6Addr); queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr; } #endif //Offset to the payload queueItem->offset = sizeof(SocketQueueItem); //Copy the payload chunkedBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length); //Notify user that data is available udpUpdateEvents(socket); //Leave critical section osMutexRelease(socketMutex); //Successful processing return NO_ERROR; }
void dnsProcessResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const ChunkedBuffer *buffer, size_t offset, void *params) { uint_t i; uint_t j; size_t n; size_t pos; size_t length; DnsHeader *message; DnsQuestion *question; DnsResourceRecord *resourceRecord; DnsCacheEntry *entry; //Retrieve the length of the DNS message length = chunkedBufferGetLength(buffer) - offset; //Ensure the DNS message is valid if(length < sizeof(DnsHeader)) return; if(length > DNS_MESSAGE_MAX_SIZE) return; //Point to the DNS message header message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("DNS message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); //Acquire exclusive access to the DNS cache osMutexAcquire(dnsCacheMutex); //Loop through DNS cache entries for(i = 0; i < DNS_CACHE_SIZE; i++) { //Point to the current entry entry = &dnsCache[i]; //DNS name resolution in progress? if(entry->state == DNS_STATE_IN_PROGRESS && entry->protocol == HOST_NAME_RESOLVER_DNS) { //Check destination port number if(entry->port == ntohs(udpHeader->destPort)) { //Compare identifier against expected one if(ntohs(message->id) != entry->id) break; //Check message type if(!message->qr) break; //The DNS message shall contain one question if(ntohs(message->qdcount) != 1) break; //Point to the first question pos = sizeof(DnsHeader); //Parse domain name n = dnsParseName(message, length, pos, NULL, 0); //Invalid name? if(!n) break; //Malformed mDNS message? if((n + sizeof(DnsQuestion)) > length) break; //Compare domain name if(!dnsCompareName(message, length, pos, entry->name, 0)) break; //Point to the corresponding entry question = DNS_GET_QUESTION(message, n); //Check the class of the query if(ntohs(question->qclass) != DNS_RR_CLASS_IN) break; //Check the type of the query if(entry->type == HOST_TYPE_IPV4 && ntohs(question->qtype) != DNS_RR_TYPE_A) break; if(entry->type == HOST_TYPE_IPV6 && ntohs(question->qtype) != DNS_RR_TYPE_AAAA) break; //Make sure recursion is available if(!message->ra) { //The entry should be deleted since name resolution has failed dnsDeleteEntry(entry); //Exit immediately break; } //Check return code if(message->rcode != DNS_RCODE_NO_ERROR) { //The entry should be deleted since name resolution has failed dnsDeleteEntry(entry); //Exit immediately break; } //Point to the first answer pos = n + sizeof(DnsQuestion); //Parse answer resource records for(j = 0; j < ntohs(message->ancount); j++) { //Parse domain name pos = dnsParseName(message, length, pos, NULL, 0); //Invalid name? if(!pos) break; //Point to the associated resource record resourceRecord = DNS_GET_RESOURCE_RECORD(message, pos); //Point to the resource data pos += sizeof(DnsResourceRecord); //Make sure the resource record is valid if(pos >= length) break; if((pos + ntohs(resourceRecord->rdlength)) > length) break; #if (IPV4_SUPPORT == ENABLED) //IPv4 address expected? if(entry->type == HOST_TYPE_IPV4) { //A resource record found? if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_A) { //Verify the length of the data field if(ntohs(resourceRecord->rdlength) == sizeof(Ipv4Addr)) { //Copy the IPv4 address entry->ipAddr.length = sizeof(Ipv4Addr); ipv4CopyAddr(&entry->ipAddr.ipv4Addr, resourceRecord->rdata); //Save current time entry->timestamp = osGetTickCount(); //Save TTL value entry->timeout = ntohl(resourceRecord->ttl) * 1000; //Limit the lifetime of the DNS cache entries entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME); //Unregister UDP callback function udpDetachRxCallback(interface, entry->port); //Host name successfully resolved entry->state = DNS_STATE_RESOLVED; //Exit immediately break; } } } #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 address expected? if(entry->type == HOST_TYPE_IPV6) { //AAAA resource record found? if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_AAAA) { //Verify the length of the data field if(ntohs(resourceRecord->rdlength) == sizeof(Ipv6Addr)) { //Copy the IPv6 address entry->ipAddr.length = sizeof(Ipv6Addr); ipv6CopyAddr(&entry->ipAddr.ipv6Addr, resourceRecord->rdata); //Save current time entry->timestamp = osGetTickCount(); //Save TTL value entry->timeout = ntohl(resourceRecord->ttl) * 1000; //Limit the lifetime of the DNS cache entries entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME); //Unregister UDP callback function udpDetachRxCallback(interface, entry->port); //Host name successfully resolved entry->state = DNS_STATE_RESOLVED; //Exit immediately break; } } } #endif //Point to the next resource record pos += ntohs(resourceRecord->rdlength); } //We are done break; } } } //Release exclusive access to the DNS cache osMutexRelease(dnsCacheMutex); }