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; }
error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code, uint8_t parameter, const ChunkedBuffer *ipPacket) { error_t error; size_t offset; size_t length; Ipv4Header *ipHeader; ChunkedBuffer *icmpMessage; IcmpErrorMessage *icmpHeader; Ipv4PseudoHeader pseudoHeader; //Retrieve the length of the invoking IPv4 packet length = chunkedBufferGetLength(ipPacket); //Check the length of the IPv4 packet if(length < sizeof(Ipv4Header)) return ERROR_INVALID_LENGTH; //Point to the header of the invoking packet ipHeader = chunkedBufferAt(ipPacket, 0); //Sanity check if(!ipHeader) return ERROR_FAILURE; //Never respond to a packet destined to a broadcast or a multicast address if(ipv4IsBroadcastAddr(interface, ipHeader->destAddr) || ipv4IsMulticastAddr(ipHeader->destAddr)) { //Report an error return ERROR_INVALID_ADDRESS; } //Length of the data that will be returned along with the ICMP header length = MIN(length, ipHeader->headerLength * 4 + 8); //Allocate a memory buffer to hold the ICMP message icmpMessage = ipAllocBuffer(sizeof(IcmpErrorMessage), &offset); //Failed to allocate memory? if(!icmpMessage) return ERROR_OUT_OF_MEMORY; //Point to the ICMP header icmpHeader = chunkedBufferAt(icmpMessage, offset); //Format ICMP message icmpHeader->type = type; icmpHeader->code = code; icmpHeader->checksum = 0; icmpHeader->parameter = parameter; icmpHeader->unused = 0; //Copy the IP header and the first 8 bytes of the original datagram data error = chunkedBufferConcat(icmpMessage, ipPacket, 0, length); //Any error to report? if(error) { //Clean up side effects chunkedBufferFree(icmpMessage); //Exit immediately return error; } //Get the length of the resulting message length = chunkedBufferGetLength(icmpMessage) - offset; //Message checksum calculation icmpHeader->checksum = ipCalcChecksumEx(icmpMessage, offset, length); //Format IPv4 pseudo header pseudoHeader.srcAddr = ipHeader->destAddr; pseudoHeader.destAddr = ipHeader->srcAddr; pseudoHeader.reserved = 0; pseudoHeader.protocol = IPV4_PROTOCOL_ICMP; pseudoHeader.length = htons(length); //Debug message TRACE_INFO("Sending ICMP Error message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpDumpErrorMessage(icmpHeader); //Send ICMP Error message error = ipv4SendDatagram(interface, &pseudoHeader, icmpMessage, offset, IPV4_DEFAULT_TTL); //Free previously allocated memory chunkedBufferFree(icmpMessage); //Return status code return error; }
void 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); }
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 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; }