error_t samv71EthSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset) { size_t length; //Retrieve the length of the packet length = netBufferGetLength(buffer) - offset; //Check the frame length if(length > SAMV71_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 & GMAC_TX_USED)) return ERROR_FAILURE; //Copy user data to the transmit buffer netBufferRead(txBuffer[txBufferIndex], buffer, offset, length); //Set the necessary flags in the descriptor entry if(txBufferIndex < (SAMV71_ETH_TX_BUFFER_COUNT - 1)) { //Write the status word txBufferDesc[txBufferIndex].status = GMAC_TX_LAST | (length & GMAC_TX_LENGTH); //Point to the next buffer txBufferIndex++; } else { //Write the status word txBufferDesc[txBufferIndex].status = GMAC_TX_WRAP | GMAC_TX_LAST | (length & GMAC_TX_LENGTH); //Wrap around txBufferIndex = 0; } //Data synchronization barrier __DSB(); //Set the TSTART bit to initiate transmission GMAC->GMAC_NCR |= GMAC_NCR_TSTART; //Check whether the next buffer is available for writing if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); } //Successful processing return NO_ERROR; }
error_t rx63nEthSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset) { //Retrieve the length of the packet size_t length = netBufferGetLength(buffer) - offset; //Check the frame length if(length > RX63N_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(txDmaDesc[txIndex].td0 & EDMAC_TD0_TACT) return ERROR_FAILURE; //Copy user data to the transmit buffer netBufferRead(txBuffer[txIndex], buffer, offset, length); //Write the number of bytes to send txDmaDesc[txIndex].td1 = (length << 16) & EDMAC_TD1_TBL; //Check current index if(txIndex < (RX63N_ETH_TX_BUFFER_COUNT - 1)) { //Give the ownership of the descriptor to the DMA engine txDmaDesc[txIndex].td0 = EDMAC_TD0_TACT | EDMAC_TD0_TFP_SOF | EDMAC_TD0_TFP_EOF | EDMAC_TD0_TWBI; //Point to the next descriptor txIndex++; } else { //Give the ownership of the descriptor to the DMA engine txDmaDesc[txIndex].td0 = EDMAC_TD0_TACT | EDMAC_TD0_TDLE | EDMAC_TD0_TFP_SOF | EDMAC_TD0_TFP_EOF | EDMAC_TD0_TWBI; //Wrap around txIndex = 0; } //Instruct the DMA to poll the transmit descriptor list EDMAC.EDTRR.BIT.TR = 1; //Check whether the next buffer is available for writing if(!(txDmaDesc[txIndex].td0 & EDMAC_TD0_TACT)) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); } //Successful write operation return NO_ERROR; }
void nbnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params) { size_t length; NbnsHeader *message; //Make sure the NBNS message was received from an IPv4 peer if(pseudoHeader->length != sizeof(Ipv4PseudoHeader)) return; //Retrieve the length of the NBNS message length = netBufferGetLength(buffer) - offset; //Ensure the NBNS message is valid if(length < sizeof(NbnsHeader)) return; if(length > DNS_MESSAGE_MAX_SIZE) return; //Point to the NBNS message header message = netBufferAt(buffer, offset); //Sanity check if(message == NULL) return; //Debug message TRACE_INFO("NBNS message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage((DnsHeader *) message, length); //NBNS messages received with an opcode other than zero must be silently ignored if(message->opcode != DNS_OPCODE_QUERY) return; //NBNS messages received with non-zero response codes must be silently ignored if(message->rcode != DNS_RCODE_NO_ERROR) return; //NBNS query received? if(!message->qr) { #if (NBNS_RESPONDER_SUPPORT == ENABLED) //Process incoming NBNS query message nbnsProcessQuery(interface, &pseudoHeader->ipv4Data, udpHeader, message, length); #endif } //NBNS response received? else { #if (NBNS_CLIENT_SUPPORT == ENABLED) //Process incoming NBNS response message nbnsProcessResponse(interface, &pseudoHeader->ipv4Data, udpHeader, message, length); #endif } }
error_t dm9000SendPacket(NetInterface *interface, const NetBuffer *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 = netBufferGetLength(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 netBufferRead(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 m2sxxxEthSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset) { //Retrieve the length of the packet size_t length = netBufferGetLength(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 netBufferRead((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; }
error_t stm32f4x7EthSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset) { //Retrieve the length of the packet size_t length = netBufferGetLength(buffer) - offset; //Check the frame length if(length > STM32F4X7_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->tdes0 & ETH_TDES0_OWN) return ERROR_FAILURE; //Copy user data to the transmit buffer netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length); //Write the number of bytes to send txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1; //Set LS and FS flags as the data fits in a single buffer txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS; //Give the ownership of the descriptor to the DMA txCurDmaDesc->tdes0 |= ETH_TDES0_OWN; //Clear TBUS flag to resume processing ETH->DMASR = ETH_DMASR_TBUS; //Instruct the DMA to poll the transmit descriptor list ETH->DMATPDR = 0; //Point to the next descriptor in the list txCurDmaDesc = (Stm32f4x7TxDmaDesc *) txCurDmaDesc->tdes3; //Check whether the next buffer is available for writing if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); } //Data successfully written return NO_ERROR; }
error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, NetBuffer *buffer, size_t offset, uint8_t ttl) { error_t error; size_t length; uint16_t id; //Retrieve the length of payload length = netBufferGetLength(buffer) - offset; //Check whether the TTL value is zero if(ttl == 0) { //Use default Time-To-Live value ttl = IPV4_DEFAULT_TTL; } //Identification field is primarily used to identify //fragments of an original IP datagram id = osAtomicInc16(&interface->ipv4Identification); //If the payload length is smaller than the network //interface MTU then no fragmentation is needed if((length + sizeof(Ipv4Header)) <= interface->ipv4Config.mtu) { //Send data as is error = ipv4SendPacket(interface, pseudoHeader, id, 0, buffer, offset, ttl); } //If the payload length exceeds the network interface MTU //then the device must fragment the data else { #if (IPV4_FRAG_SUPPORT == ENABLED) //Fragment IP datagram into smaller packets error = ipv4FragmentDatagram(interface, pseudoHeader, id, buffer, offset, ttl); #else //Fragmentation is not supported error = ERROR_MESSAGE_TOO_LONG; #endif } //Return status code return error; }
error_t a2fxxxm3EthSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset) { //Retrieve the length of the packet size_t length = netBufferGetLength(buffer) - offset; //Check the frame length if(length > A2FXXXM3_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->tdes0 & TDES0_OWN) return ERROR_FAILURE; //Copy user data to the transmit buffer netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length); //Write the number of bytes to send txCurDmaDesc->tdes1 = length & TDES1_TBS1_MASK; //Set LS and FS flags as the data fits in a single buffer txCurDmaDesc->tdes1 |= TDES1_IC | TDES1_LS | TDES1_FS | TDES1_TCH; //Give the ownership of the descriptor to the DMA txCurDmaDesc->tdes0 |= TDES0_OWN; //Instruct the DMA to poll the transmit descriptor list MAC->CSR1 = 1; //Point to the next descriptor in the list txCurDmaDesc = (A2fxxxm3TxDmaDesc *) txCurDmaDesc->tdes3; //Check whether the next buffer is available for writing if(!(txCurDmaDesc->tdes0 & TDES0_OWN)) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); } //Data successfully written return NO_ERROR; }
error_t wilc1000SendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset) { int8_t status; size_t length; //Retrieve the length of the packet length = netBufferGetLength(buffer) - offset; //Check the frame length if(length > WILC1000_TX_BUFFER_SIZE) { //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); //Report an error return ERROR_INVALID_LENGTH; } //Copy user data to the transmit buffer netBufferRead(txBuffer, buffer, offset, length); //STA or AP mode? if(interface == wilc1000StaInterface) { //Send packet status = m2m_wifi_send_ethernet_pkt(txBuffer, length); } else { status = m2m_wifi_send_ethernet_pkt_ifc1(txBuffer, length); } //The transmitter can accept another packet osSetEvent(&interface->nicTxEvent); //Return status code if(status == M2M_SUCCESS) return NO_ERROR; else return ERROR_FAILURE; }
error_t ipv4FragmentDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, uint16_t id, const NetBuffer *payload, size_t payloadOffset, uint8_t timeToLive) { error_t error; size_t offset; size_t length; size_t payloadLength; size_t fragmentOffset; size_t maxFragmentSize; NetBuffer *fragment; //Retrieve the length of the payload payloadLength = netBufferGetLength(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; //Maximum payload size for fragmented packets maxFragmentSize = interface->ipv4Config.mtu - sizeof(Ipv4Header); //The size shall be a multiple of 8-byte blocks maxFragmentSize -= (maxFragmentSize % 8); //Split the payload into multiple IP fragments for(offset = 0; offset < payloadLength; offset += length) { //Flush the contents of the fragment error = netBufferSetLength(fragment, fragmentOffset); //Sanity check if(error) break; //Process the last fragment? if((payloadLength - offset) <= maxFragmentSize) { //Size of the current fragment length = payloadLength - offset; //Copy fragment data netBufferConcat(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 = maxFragmentSize; //Copy fragment data netBufferConcat(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 netBufferFree(fragment); //Return status code return error; }
error_t udpSendDatagramEx(NetInterface *interface, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, NetBuffer *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 = netBufferGetLength(buffer) - offset; //Point to the UDP header header = netBufferAt(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 udpProcessDatagram(NetInterface *interface, IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset) { error_t error; uint_t i; size_t length; UdpHeader *header; Socket *socket; SocketQueueItem *queueItem; NetBuffer *p; //Retrieve the length of the UDP datagram length = netBufferGetLength(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 = netBufferAt(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 osAcquireMutex(&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 osReleaseMutex(&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 = netBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Point to the newly created item queueItem = netBufferAt(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 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 = netBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Add the newly created item to the queue queueItem->next = netBufferAt(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; //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 netBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length); //Notify user that data is available udpUpdateEvents(socket); //Leave critical section osReleaseMutex(&socketMutex); //Successful processing return NO_ERROR; }
error_t rawSocketProcessIpPacket(NetInterface *interface, IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset) { uint_t i; size_t length; Socket *socket; SocketQueueItem *queueItem; NetBuffer *p; //Retrieve the length of the raw IP packet length = netBufferGetLength(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 = netBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Point to the newly created item queueItem = netBufferAt(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 = netBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Add the newly created item to the queue queueItem->next = netBufferAt(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 netBufferCopy(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; }
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; } }
void ipv4ProcessDatagram(NetInterface *interface, const NetBuffer *buffer) { error_t error; size_t offset; size_t length; Ipv4Header *header; IpPseudoHeader pseudoHeader; //Retrieve the length of the IPv4 datagram length = netBufferGetLength(buffer); //Point to the IPv4 header header = netBufferAt(buffer, 0); //Sanity check if(!header) return; //Debug message TRACE_INFO("IPv4 datagram received (%" PRIuSIZE " bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(header); //Get the offset to the payload offset = header->headerLength * 4; //Compute the length of the payload length -= header->headerLength * 4; //Form the IPv4 pseudo header pseudoHeader.length = sizeof(Ipv4PseudoHeader); pseudoHeader.ipv4Data.srcAddr = header->srcAddr; pseudoHeader.ipv4Data.destAddr = header->destAddr; pseudoHeader.ipv4Data.reserved = 0; pseudoHeader.ipv4Data.protocol = header->protocol; pseudoHeader.ipv4Data.length = htons(length); #if defined(IPV4_DATAGRAM_FORWARD_HOOK) IPV4_DATAGRAM_FORWARD_HOOK(interface, &pseudoHeader, buffer, offset); #endif //Check the protocol field switch(header->protocol) { //ICMP protocol? case IPV4_PROTOCOL_ICMP: //Process incoming ICMP message icmpProcessMessage(interface, header->srcAddr, buffer, offset); #if (RAW_SOCKET_SUPPORT == ENABLED) //Allow raw sockets to process ICMP messages rawSocketProcessIpPacket(interface, &pseudoHeader, buffer, offset); #endif //No error to report error = NO_ERROR; //Continue processing break; #if (IGMP_SUPPORT == ENABLED) //IGMP protocol? case IPV4_PROTOCOL_IGMP: //Process incoming IGMP message igmpProcessMessage(interface, buffer, offset); #if (RAW_SOCKET_SUPPORT == ENABLED) //Allow raw sockets to process IGMP messages rawSocketProcessIpPacket(interface, &pseudoHeader, buffer, offset); #endif //No error to report error = NO_ERROR; //Continue processing break; #endif #if (TCP_SUPPORT == ENABLED) //TCP protocol? case IPV4_PROTOCOL_TCP: //Process incoming TCP segment tcpProcessSegment(interface, &pseudoHeader, buffer, offset); //No error to report error = NO_ERROR; //Continue processing break; #endif #if (UDP_SUPPORT == ENABLED) //UDP protocol? case IPV4_PROTOCOL_UDP: //Process incoming UDP datagram error = udpProcessDatagram(interface, &pseudoHeader, buffer, offset); //Continue processing break; #endif //Unknown protocol? default: #if (RAW_SOCKET_SUPPORT == ENABLED) //Allow raw sockets to process IPv4 packets error = rawSocketProcessIpPacket(interface, &pseudoHeader, buffer, offset); #else //Report an error error = ERROR_PROTOCOL_UNREACHABLE; #endif //Continue processing break; } //Unreachable protocol? if(error == ERROR_PROTOCOL_UNREACHABLE) { //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); } }
void dnsProcessResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params) { uint_t i; uint_t j; size_t pos; size_t length; DnsHeader *message; DnsQuestion *question; DnsResourceRecord *resourceRecord; DnsCacheEntry *entry; //Retrieve the length of the DNS message length = netBufferGetLength(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 = netBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("DNS message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); //Check message type if(!message->qr) return; //The DNS message shall contain one question if(ntohs(message->qdcount) != 1) return; //Acquire exclusive access to the DNS cache osAcquireMutex(&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 the expected one if(ntohs(message->id) != entry->id) break; //Point to the first question pos = sizeof(DnsHeader); //Parse domain name pos = dnsParseName(message, length, pos, NULL, 0); //Invalid name? if(!pos) break; //Malformed mDNS message? if((pos + sizeof(DnsQuestion)) > length) break; //Compare domain name if(!dnsCompareName(message, length, sizeof(DnsHeader), entry->name, 0)) break; //Point to the corresponding entry question = DNS_GET_QUESTION(message, pos); //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 += 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 && 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 = osGetSystemTime(); //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 && 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 = osGetSystemTime(); //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 osReleaseMutex(&dnsCacheMutex); }
error_t ipv4SendPacket(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, uint16_t fragOffset, NetBuffer *buffer, size_t offset, uint8_t ttl) { error_t error; size_t length; Ipv4Header *packet; //Is there enough space for the IPv4 header? if(offset < sizeof(Ipv4Header)) return ERROR_INVALID_PARAMETER; //Make room for the header offset -= sizeof(Ipv4Header); //Calculate the size of the entire packet, including header and data length = netBufferGetLength(buffer) - offset; //Point to the IPv4 header packet = netBufferAt(buffer, offset); //Format IPv4 header packet->version = IPV4_VERSION; packet->headerLength = 5; packet->typeOfService = 0; packet->totalLength = htons(length); packet->identification = htons(fragId); packet->fragmentOffset = htons(fragOffset); packet->timeToLive = ttl; packet->protocol = pseudoHeader->protocol; packet->headerChecksum = 0; packet->srcAddr = pseudoHeader->srcAddr; packet->destAddr = pseudoHeader->destAddr; //Calculate IP header checksum packet->headerChecksum = ipCalcChecksumEx(buffer, offset, packet->headerLength * 4); //Ensure the source address is valid error = ipv4CheckSourceAddr(interface, pseudoHeader->srcAddr); //Invalid source address? if(error) return error; //Destination address is the unspecified address? if(pseudoHeader->destAddr == IPV4_UNSPECIFIED_ADDR) { //Destination address is not acceptable return ERROR_INVALID_ADDRESS; } //Destination address is the loopback address? else if(pseudoHeader->destAddr == IPV4_LOOPBACK_ADDR) { //Not yet implemented... return ERROR_NOT_IMPLEMENTED; } #if (ETH_SUPPORT == ENABLED) //Ethernet interface? if(interface->nicDriver->type == NIC_TYPE_ETHERNET) { Ipv4Addr destIpAddr; MacAddr destMacAddr; //Destination address is a broadcast address? 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 (%" PRIuSIZE " bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(packet); //Send Ethernet frame error = ethSendFrame(interface, &destMacAddr, buffer, offset, ETH_TYPE_IPV4); } //Address resolution is in progress? else if(error == ERROR_IN_PROGRESS) { //Debug message TRACE_INFO("Enqueuing IPv4 packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(packet); //Enqueue packets waiting for address resolution error = arpEnqueuePacket(interface, destIpAddr, buffer, offset); } //Address resolution failed? else { //Debug message TRACE_WARNING("Cannot map IPv4 address to Ethernet address!\r\n"); } } else #endif #if (PPP_SUPPORT == ENABLED) //PPP interface? if(interface->nicDriver->type == NIC_TYPE_PPP) { //Debug message TRACE_INFO("Sending IPv4 packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(packet); //Send PPP frame error = pppSendFrame(interface, buffer, offset, PPP_PROTOCOL_IP); } else #endif //Unknown interface type? { //Report an error error = ERROR_INVALID_INTERFACE; } //Return status code return error; }