void ipv4FragTick(NetInterface *interface) { error_t error; uint_t i; time_t time; Ipv4HoleDesc *hole; //Acquire exclusive access to the reassembly queue osMutexAcquire(interface->ipv4FragQueueMutex); //Get current time time = osGetTickCount(); //Loop through the reassembly queue for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue Ipv4FragDesc *frag = &interface->ipv4FragQueue[i]; //Make sure the entry is currently in use if(frag->buffer.chunkCount > 0) { //If the timer runs out, the partially-reassembled datagram must be //discarded and ICMP Time Exceeded message sent to the source host if((time - frag->timestamp) >= IPV4_FRAG_TIME_TO_LIVE) { //Debug message TRACE_INFO("IPv4 fragment reassembly timeout...\r\n"); //Dump IP header contents for debugging purpose ipv4DumpHeader(frag->buffer.chunk[0].address); //Point to the first hole descriptor hole = ipv4FindHole(frag, frag->firstHole); //Make sure the fragment zero has been received //before sending an ICMP message if(hole != NULL && hole->first > 0) { //Fix the size of the reconstructed datagram error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, frag->headerLength + hole->first); //Check status code if(!error) { //Send an ICMP Time Exceeded message icmpSendErrorMessage(interface, ICMP_TYPE_TIME_EXCEEDED, ICMP_CODE_REASSEMBLY_TIME_EXCEEDED, 0, (ChunkedBuffer *) &frag->buffer); } } //Drop the partially reconstructed datagram chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); } } } //Release exclusive access to the reassembly queue osMutexRelease(interface->ipv4FragQueueMutex); }
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; }
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); } }
void ipv4ProcessPacket(NetInterface *interface, const MacAddr *srcMacAddr, Ipv4Header *packet, size_t length) { //Ensure the packet length is greater than 20 bytes if(length < sizeof(Ipv4Header)) return; //Debug message TRACE_INFO("IPv4 packet received (%u bytes)...\r\n", length); //Dump IP header contents for debugging purpose ipv4DumpHeader(packet); //A packet whose version number is not 4 must be silently discarded if(packet->version != IPV4_VERSION) return; //Valid IPv4 header shall contains more than five 32-bit words if(packet->headerLength < 5) return; //Ensure the total length is correct before processing the packet if(ntohs(packet->totalLength) < (packet->headerLength * 4)) return; if(ntohs(packet->totalLength) > length) return; //Destination address filtering if(ipv4CheckDestAddr(interface, packet->destAddr)) return; //Source address filtering if(ipv4CheckSourceAddr(interface, packet->srcAddr)) return; //The host must verify the IP header checksum on every received //datagram and silently discard every datagram that has a bad //checksum (see RFC 1122 3.2.1.2) if(ipCalcChecksum(packet, packet->headerLength * 4) != 0x0000) { //Debug message TRACE_WARNING("Wrong IP header checksum!\r\n"); //Discard incoming packet return; } //Convert the total length from network byte order length = ntohs(packet->totalLength); //A fragmented packet was received? if(ntohs(packet->fragmentOffset) & (IPV4_FLAG_MF | IPV4_OFFSET_MASK)) { #if (IPV4_FRAG_SUPPORT == ENABLED) //Acquire exclusive access to the reassembly queue osMutexAcquire(interface->ipv4FragQueueMutex); //Reassemble the original datagram ipv4ReassembleDatagram(interface, srcMacAddr, packet, length); //Release exclusive access to the reassembly queue osMutexRelease(interface->ipv4FragQueueMutex); #endif } else { ChunkedBuffer1 buffer; //Unfragmented datagrams fit in a single chunk buffer.chunkCount = 1; buffer.maxChunkCount = 1; buffer.chunk[0].address = packet; buffer.chunk[0].length = length; //Pass the IPv4 datagram to the higher protocol layer ipv4ProcessDatagram(interface, srcMacAddr, (ChunkedBuffer *) &buffer); } }