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 pppSendEchoRep(PppContext *context, const PppEchoPacket *echoReqPacket, PppProtocol protocol) { error_t error; size_t length; size_t offset; NetBuffer *buffer; PppEchoPacket *echoRepPacket; //Retrieve the length of the Echo-Request packet length = ntohs(echoReqPacket->length); //Make sure the length is valid if(length < sizeof(PppEchoPacket)) return ERROR_INVALID_LENGTH; if(length > context->peerConfig.mru) return ERROR_INVALID_LENGTH; //Allocate a buffer memory to hold the Echo-Reply packet buffer = pppAllocBuffer(sizeof(PppEchoPacket), &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the Echo-Reply packet echoRepPacket = netBufferAt(buffer, offset); //Format packet header echoRepPacket->code = PPP_CODE_CODE_REJ; echoRepPacket->identifier = echoReqPacket->identifier; echoRepPacket->length = htons(length); echoRepPacket->magicNumber = context->localConfig.magicNumber; //The data field of the Echo-Request packet is copied into the data //field of the Echo-Reply packet error = netBufferAppend(buffer, echoReqPacket->data, length - sizeof(PppEchoPacket)); //Check status code if(!error) { //Debug message TRACE_INFO("Sending Echo-Reply packet (%" PRIuSIZE " bytes)...\r\n", length); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, protocol); } //Free previously allocated memory block netBufferFree(buffer); //Return status code return error; }
error_t pppSendProtocolRej(PppContext *context, uint8_t identifier, uint16_t protocol, const uint8_t *information, size_t length) { error_t error; size_t offset; NetBuffer *buffer; PppProtocolRejPacket *protocolRejPacket; //Calculate the length of the Protocol-Reject packet length += sizeof(PppProtocolRejPacket); //The Rejected-Information must be truncated to comply with //the peer's established MRU length = MIN(length, context->peerConfig.mru); //Allocate a buffer memory to hold the Protocol-Reject packet buffer = pppAllocBuffer(sizeof(PppProtocolRejPacket), &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the Protocol-Reject packet protocolRejPacket = netBufferAt(buffer, offset); //Format packet header protocolRejPacket->code = PPP_CODE_PROTOCOL_REJ; protocolRejPacket->identifier = identifier; protocolRejPacket->length = htons(length); protocolRejPacket->rejectedProtocol = htons(protocol); //The Rejected-Information field contains a copy of the //packet which is being rejected error = netBufferAppend(buffer, information, length - sizeof(PppProtocolRejPacket)); //Check status code if(!error) { //Debug message TRACE_INFO("Sending Protocol-Reject packet (%" PRIuSIZE " bytes)...\r\n", length); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_LCP); } //Free previously allocated memory block netBufferFree(buffer); //Return status code return error; }
error_t chapSendResponse(PppContext *context, const uint8_t *value) { error_t error; size_t n; size_t length; size_t offset; NetBuffer *buffer; ChapResponsePacket *responsePacket; //Retrieve the length of the username n = strlen(context->username); //Calculate the length of the Response packet length = sizeof(ChapResponsePacket) + MD5_DIGEST_SIZE + n; //Allocate a buffer memory to hold the Response packet buffer = pppAllocBuffer(length, &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the Response packet responsePacket = netBufferAt(buffer, offset); //Format packet header responsePacket->code = CHAP_CODE_RESPONSE; responsePacket->identifier = context->chapFsm.peerIdentifier; responsePacket->length = htons(length); responsePacket->valueSize = MD5_DIGEST_SIZE; //Copy the Response value memcpy(responsePacket->value, value, MD5_DIGEST_SIZE); //The Name field is one or more octets representing the //identification of the system transmitting the packet memcpy(responsePacket->value + MD5_DIGEST_SIZE, context->username, n); //Debug message TRACE_INFO("Sending CHAP Response packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump packet contents for debugging purpose pppDumpPacket((PppPacket *) responsePacket, length, PPP_PROTOCOL_CHAP); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_CHAP); //Free previously allocated memory block netBufferFree(buffer); //Return status code return error; }
NetBuffer *mdnsCreateResponse(uint16_t id, size_t *offset, DnsHeader **response, size_t *responseLen) { NetBuffer *buffer; DnsHeader *header; //Allocate a memory buffer to hold the mDNS message buffer = udpAllocBuffer(MDNS_MESSAGE_MAX_SIZE, offset); //Successful memory allocation? if(buffer != NULL) { //Point to the mDNS response header header = netBufferAt(buffer, *offset); //Take the identifier from the query message header->id = id; //Format mDNS response header header->qr = 1; header->opcode = DNS_OPCODE_QUERY; header->aa = 1; header->tc = 0; header->rd = 0; header->ra = 0; header->z = 0; header->rcode = DNS_RCODE_NO_ERROR; //Multicast DNS responses must not contain any questions header->qdcount = 0; header->ancount = 0; header->nscount = 0; header->arcount = 0; //Pointer to the mDNS response header *response = header; //Actual length of the mDNS response message *responseLen = sizeof(DnsHeader); } //Return a pointer to the buffer that holds the mDNS message return buffer; }
error_t pppSendTerminateAck(PppContext *context, uint8_t identifier, PppProtocol protocol) { error_t error; size_t length; size_t offset; NetBuffer *buffer; PppTerminatePacket *terminateAckPacket; //Length of the Terminate-Ack packet length = sizeof(PppTerminatePacket); //Allocate a buffer memory to hold the Terminate-Ack packet buffer = pppAllocBuffer(length, &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the Terminate-Ack packet terminateAckPacket = netBufferAt(buffer, offset); //Format packet header terminateAckPacket->code = PPP_CODE_TERMINATE_ACK; terminateAckPacket->identifier = identifier; terminateAckPacket->length = htons(length); //Debug message TRACE_INFO("Sending Terminate-Ack packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump packet contents for debugging purpose pppDumpPacket((PppPacket *) terminateAckPacket, length, protocol); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, protocol); //Free previously allocated memory block netBufferFree(buffer); //Return status code return error; }
error_t chapSendFailure(PppContext *context) { error_t error; size_t length; size_t offset; NetBuffer *buffer; PppPacket *failurePacket; //Retrieve the length of the Failure packet length = sizeof(PppPacket); //Allocate a buffer memory to hold the Failure packet buffer = pppAllocBuffer(length, &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the Failure packet failurePacket = netBufferAt(buffer, offset); //Format packet header failurePacket->code = CHAP_CODE_FAILURE; failurePacket->identifier = context->chapFsm.localIdentifier; failurePacket->length = htons(length); //Debug message TRACE_INFO("Sending CHAP Failure packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump packet contents for debugging purpose pppDumpPacket((PppPacket *) failurePacket, length, PPP_PROTOCOL_CHAP); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_CHAP); //Free previously allocated memory block netBufferFree(buffer); //Return status code return error; }
error_t mdnsSendAnnouncement(NetInterface *interface) { error_t error; size_t length; size_t offset; NetBuffer *buffer; DnsHeader *message; IpAddr destIpAddr; //Initialize error code error = NO_ERROR; //Allocate a memory buffer to hold the mDNS query message buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the mDNS header message = netBufferAt(buffer, offset); //Format mDNS query message message->id = 0; message->qr = 1; message->opcode = DNS_OPCODE_QUERY; message->aa = 1; message->tc = 0; message->rd = 0; message->ra = 0; message->z = 0; message->rcode = DNS_RCODE_NO_ERROR; //Multicast DNS responses must not contain any questions message->qdcount = 0; message->ancount = 0; message->nscount = 0; message->arcount = 0; //Length of the mDNS query message length = sizeof(DnsHeader); //Start of exception handling block do { //Format A resource record error = mdnsAddIpv4ResourceRecord(interface, message, &length, TRUE); //Any error to report? if(error) break; //Format AAAA resource record error = mdnsAddIpv6ResourceRecord(interface, message, &length, TRUE); //Any error to report? if(error) break; //Convert 16-bit value to network byte order message->ancount = htons(message->ancount); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending mDNS announcement (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); #if (IPV4_SUPPORT == ENABLED) //Select the relevant multicast address (224.0.0.251) destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR; //All multicast DNS queries should be sent with an IP TTL set to 255 error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL); //Any error to report? if(error)break; #endif #if (IPV6_SUPPORT == ENABLED) //Select the relevant multicast address (ff02::fb) destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR; //All multicast DNS queries should be sent with an IP TTL set to 255 error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL); //Any error to report? if(error)break; #endif //End of exception handling block } while(0); //Free previously allocated memory netBufferFree(buffer); //Return status code return error; }
error_t mdnsSendProbe(NetInterface *interface) { error_t error; size_t length; size_t offset; NetBuffer *buffer; DnsHeader *message; DnsQuestion *dnsQuestion; IpAddr destIpAddr; //Initialize error code error = NO_ERROR; //Allocate a memory buffer to hold the mDNS query message buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the mDNS header message = netBufferAt(buffer, offset); //Format mDNS query message message->id = 0; message->qr = 0; message->opcode = DNS_OPCODE_QUERY; message->aa = 0; message->tc = 0; message->rd = 0; message->ra = 0; message->z = 0; message->rcode = DNS_RCODE_NO_ERROR; //The mDNS query contains one question message->qdcount = HTONS(1); message->ancount = 0; message->nscount = 0; message->arcount = 0; //Length of the mDNS query message length = sizeof(DnsHeader); //Encode the host name using the DNS name notation length += mdnsEncodeName(interface->hostname, "", ".local", (uint8_t *) message + length); //Point to the corresponding question structure dnsQuestion = DNS_GET_QUESTION(message, length); //Fill in question structure dnsQuestion->qtype = HTONS(DNS_RR_TYPE_ANY); dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN); //Update the length of the mDNS query message length += sizeof(DnsQuestion); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending mDNS probe (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); #if (IPV4_SUPPORT == ENABLED) //Check status code if(!error) { //Select the relevant multicast address (224.0.0.251) destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR; //All multicast DNS queries should be sent with an IP TTL set to 255 error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL); } #endif #if (IPV6_SUPPORT == ENABLED) //Check status code if(!error) { //Select the relevant multicast address (ff02::fb) destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR; //All multicast DNS queries should be sent with an IP TTL set to 255 error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL); } #endif //Free previously allocated memory netBufferFree(buffer); //Return status code return error; }
error_t lcpSendConfigureReq(PppContext *context) { error_t error; size_t length; size_t offset; NetBuffer *buffer; PppConfigurePacket *configureReqPacket; //Debug message TRACE_INFO("LCP Send-Configure-Request callback\r\n"); //Calculate the maximum size of the Configure-Request packet length = sizeof(PppConfigurePacket) + sizeof(LcpMruOption) + sizeof(LcpAccmOption); //Allocate a buffer memory to hold the packet buffer = pppAllocBuffer(length, &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the Configure-Request packet configureReqPacket = netBufferAt(buffer, offset); //Format packet header configureReqPacket->code = PPP_CODE_CONFIGURE_REQ; configureReqPacket->identifier = ++context->lcpFsm.identifier; configureReqPacket->length = sizeof(PppConfigurePacket); //Make sure the Maximum-Receive-Unit option has not been previously rejected if(!context->localConfig.mruRejected) { //Convert MRU to network byte order uint16_t value = htons(context->localConfig.mru); //Add option pppAddOption(configureReqPacket, LCP_OPTION_MRU, &value, sizeof(uint16_t)); } //Make sure the Async-Control-Character-Map option has not been previously rejected if(!context->localConfig.accmRejected) { //Convert ACCM to network byte order uint32_t value = htonl(context->localConfig.accm); //Add option pppAddOption(configureReqPacket, LCP_OPTION_ACCM, &value, sizeof(uint32_t)); } //Save packet length length = configureReqPacket->length; //Convert length field to network byte order configureReqPacket->length = htons(length); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending Configure-Request packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump packet contents for debugging purpose pppDumpPacket((PppPacket *) configureReqPacket, length, PPP_PROTOCOL_LCP); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_LCP); //The restart counter is decremented each time a Configure-Request is sent if(context->lcpFsm.restartCounter > 0) context->lcpFsm.restartCounter--; //Save the time at which the packet was sent context->lcpFsm.timestamp = osGetSystemTime(); //Free previously allocated memory block netBufferFree(buffer); //Return status code return error; }
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); }
Ipv4FragDesc *ipv4SearchFragQueue(NetInterface *interface, const Ipv4Header *packet) { error_t error; uint_t i; Ipv4Header *datagram; Ipv4FragDesc *frag; Ipv4HoleDesc *hole; //Search for a matching IP datagram being reassembled for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue frag = &interface->ipv4FragQueue[i]; //Check whether the current entry is used? if(frag->buffer.chunkCount > 0) { //Point to the corresponding datagram datagram = netBufferAt((NetBuffer *) &frag->buffer, 0); //Check source and destination addresses if(datagram->srcAddr != packet->srcAddr) continue; if(datagram->destAddr != packet->destAddr) continue; //Compare identification and protocol fields if(datagram->identification != packet->identification) continue; if(datagram->protocol != packet->protocol) continue; //A matching entry has been found in the reassembly queue return frag; } } //If the current packet does not match an existing entry //in the reassembly queue, then create a new entry for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue frag = &interface->ipv4FragQueue[i]; //The current entry is free? if(!frag->buffer.chunkCount) { //Number of chunks that comprise the reassembly buffer frag->buffer.maxChunkCount = arraysize(frag->buffer.chunk); //Allocate sufficient memory to hold the IPv4 header and //the first hole descriptor error = netBufferSetLength((NetBuffer *) &frag->buffer, NET_MEM_POOL_BUFFER_SIZE + sizeof(Ipv4HoleDesc)); //Failed to allocate memory? if(error) { //Clean up side effects netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return NULL; } //Initial length of the reconstructed datagram frag->headerLength = packet->headerLength * 4; frag->dataLength = 0; //Fix the length of the first chunk frag->buffer.chunk[0].length = frag->headerLength; //Copy IPv4 header from the incoming fragment netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->headerLength); //Save current time frag->timestamp = osGetSystemTime(); //Create a new entry in the hole descriptor list frag->firstHole = 0; //Point to first hole descriptor hole = ipv4FindHole(frag, frag->firstHole); //The entry describes the datagram as being completely missing hole->first = 0; hole->last = IPV4_INFINITY; hole->next = IPV4_INFINITY; //Dump hole descriptor list ipv4DumpHoleList(frag); //Return the matching fragment descriptor return frag; } } //The reassembly queue is full return NULL; }
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 chapSendChallenge(PppContext *context) { error_t error; size_t n; size_t length; size_t offset; NetBuffer *buffer; ChapChallengePacket *challengePacket; //Retrieve the length of the username n = strlen(context->username); //Calculate the length of the Challenge packet length = sizeof(ChapChallengePacket) + MD5_DIGEST_SIZE + n; //Allocate a buffer memory to hold the Challenge packet buffer = pppAllocBuffer(length, &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the Challenge packet challengePacket = netBufferAt(buffer, offset); //Format packet header challengePacket->code = CHAP_CODE_CHALLENGE; challengePacket->identifier = ++context->chapFsm.localIdentifier; challengePacket->length = htons(length); challengePacket->valueSize = MD5_DIGEST_SIZE; //Make sure that the callback function has been registered if(context->settings.randCallback != NULL) { //Generate a random challenge value error = context->settings.randCallback( context->chapFsm.challenge, MD5_DIGEST_SIZE); } else { //Report an error error = ERROR_FAILURE; } //Check status code if(!error) { //Copy the challenge value memcpy(challengePacket->value, context->chapFsm.challenge, MD5_DIGEST_SIZE); //The Name field is one or more octets representing the //identification of the system transmitting the packet memcpy(challengePacket->value + MD5_DIGEST_SIZE, context->username, n); //Debug message TRACE_INFO("Sending CHAP Challenge packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump packet contents for debugging purpose pppDumpPacket((PppPacket *) challengePacket, length, PPP_PROTOCOL_CHAP); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_CHAP); //The restart counter is decremented each time a Challenge packet is sent if(context->chapFsm.restartCounter > 0) context->chapFsm.restartCounter--; //Save the time at which the packet was sent context->chapFsm.timestamp = osGetSystemTime(); } //Free previously allocated memory block netBufferFree(buffer); //Return status code return 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; } }
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; }
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); } }
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; }
error_t mdnsSendResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, NetBuffer *buffer, size_t offset, size_t length) { uint16_t destPort; IpAddr destIpAddr; DnsHeader *response; #if (IPV4_SUPPORT == ENABLED) //Check whether the mDNS query was received from an IPv4 peer if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //If the source UDP port in a received Multicast DNS query is not port 5353, //this indicates that the querier originating the query is a simple resolver if(udpHeader->srcPort != HTONS(MDNS_PORT)) { //the mDNS responder must send a UDP response directly back to the querier, //via unicast, to the query packet's source IP address and port destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr; } else { //Use mDNS IPv4 multicast address destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR; } } #endif #if (IPV6_SUPPORT == ENABLED) //Check whether the mDNS query was received from an IPv6 peer if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //If the source UDP port in a received Multicast DNS query is not port 5353, //this indicates that the querier originating the query is a simple resolver if(udpHeader->srcPort != HTONS(MDNS_PORT)) { //the mDNS responder must send a UDP response directly back to the querier, //via unicast, to the query packet's source IP address and port destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr; } else { //Use mDNS IPv6 multicast address destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR; } } #endif //Destination port destPort = ntohs(udpHeader->srcPort); //Point to the mDNS response header response = netBufferAt(buffer, offset); //Convert 16-bit value to network byte order response->ancount = htons(response->ancount); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending mDNS message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(response, length); //All multicast DNS responses should be sent with an IP TTL set to 255 return udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, destPort, buffer, offset, MDNS_DEFAULT_IP_TTL); }
Ipv4HoleDesc *ipv4FindHole(Ipv4FragDesc *frag, uint16_t offset) { //Return a pointer to the hole descriptor return netBufferAt((NetBuffer *) &frag->buffer, frag->headerLength + offset); }
void rawSocketProcessEthPacket(NetInterface *interface, EthHeader *ethFrame, size_t length) { uint_t i; Socket *socket; SocketQueueItem *queueItem; NetBuffer *p; //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_ETH) continue; //Check whether the socket is bound to a particular interface if(socket->interface && socket->interface != interface) continue; //Check protocol field if(socket->protocol != SOCKET_ETH_PROTO_ALL && socket->protocol != ntohs(ethFrame->type)) 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); //Return immediately return; } //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); //Return immediately return; } //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 immediately return; } //Initialize next field queueItem->next = NULL; //Other fields are meaningless queueItem->srcPort = 0; queueItem->srcIpAddr = IP_ADDR_ANY; queueItem->destIpAddr = IP_ADDR_ANY; //Offset to the raw datagram queueItem->offset = sizeof(SocketQueueItem); //Copy the raw data netBufferWrite(queueItem->buffer, queueItem->offset, ethFrame, length); //Notify user that data is available rawSocketUpdateEvents(socket); //Leave critical section osReleaseMutex(&socketMutex); }
error_t pppSendConfigureAckNak(PppContext *context, const PppConfigurePacket *configureReqPacket, PppProtocol protocol, PppCode code) { error_t error; size_t length; size_t offset; NetBuffer *buffer; PppConfigurePacket *configureAckNakPacket; PppOption *option; //Initialize status code error = NO_ERROR; //Retrieve the length of the Configure-Request packet length = ntohs(configureReqPacket->length); //Allocate a buffer memory to hold the Configure-Ack, Nak or Reject packet buffer = pppAllocBuffer(length, &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the beginning of the packet configureAckNakPacket = netBufferAt(buffer, offset); //Format packet header configureAckNakPacket->code = code; configureAckNakPacket->identifier = configureReqPacket->identifier; configureAckNakPacket->length = sizeof(PppConfigurePacket); //Retrieve the length of the option list length -= sizeof(PppConfigurePacket); //Point to the first option option = (PppOption *) configureReqPacket->options; //Parse configuration options while(length > 0) { //LCP protocol? if(protocol == PPP_PROTOCOL_LCP) { //Parse LCP option lcpParseOption(context, option, length, configureAckNakPacket); } #if (IPV4_SUPPORT) //IPCP protocol? else if(protocol == PPP_PROTOCOL_IPCP) { //Parse IPCP option ipcpParseOption(context, option, length, configureAckNakPacket); } #endif //Remaining bytes to process length -= option->length; //Jump to the next option option = (PppOption *) ((uint8_t *) option + option->length); } //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + configureAckNakPacket->length); //Convert length field to network byte order configureAckNakPacket->length = htons(configureAckNakPacket->length); //Debug message if(code == PPP_CODE_CONFIGURE_ACK) { TRACE_INFO("Sending Configure-Ack packet (%" PRIuSIZE " bytes)...\r\n", ntohs(configureAckNakPacket->length)); } else if(code == PPP_CODE_CONFIGURE_NAK) { TRACE_INFO("Sending Configure-Nak packet (%" PRIuSIZE " bytes)...\r\n", ntohs(configureAckNakPacket->length)); } else if(code == PPP_CODE_CONFIGURE_REJ) { TRACE_INFO("Sending Configure-Reject packet (%" PRIuSIZE " bytes)...\r\n", ntohs(configureAckNakPacket->length)); } //Dump packet contents for debugging purpose pppDumpPacket((PppPacket *) configureAckNakPacket, ntohs(configureAckNakPacket->length), protocol); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, protocol); //Free previously allocated memory block netBufferFree(buffer); //Return status code return error; }
error_t nbnsSendQuery(DnsCacheEntry *entry) { error_t error; size_t length; size_t offset; NetBuffer *buffer; NbnsHeader *message; DnsQuestion *dnsQuestion; IpAddr destIpAddr; //Allocate a memory buffer to hold the NBNS query message buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the NBNS header message = netBufferAt(buffer, offset); //Format NBNS query message message->id = htons(entry->id); message->qr = 0; message->opcode = DNS_OPCODE_QUERY; message->aa = 0; message->tc = 0; message->rd = 0; message->ra = 0; message->z = 0; message->b = 1; message->rcode = DNS_RCODE_NO_ERROR; //The NBNS query contains one question message->qdcount = HTONS(1); message->ancount = 0; message->nscount = 0; message->arcount = 0; //Length of the NBNS query message length = sizeof(DnsHeader); //Encode the NetBIOS name length += nbnsEncodeName(entry->name, message->questions); //Point to the corresponding question structure dnsQuestion = DNS_GET_QUESTION(message, length); //Fill in question structure dnsQuestion->qtype = HTONS(DNS_RR_TYPE_NB); dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN); //Update the length of the NBNS query message length += sizeof(DnsQuestion); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending NBNS message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage((DnsHeader *) message, length); //The destination address is the broadcast address destIpAddr.length = sizeof(Ipv4Addr); ipv4GetBroadcastAddr(entry->interface, &destIpAddr.ipv4Addr); //A request packet is always sent to the well known port 137 error = udpSendDatagramEx(entry->interface, NBNS_PORT, &destIpAddr, NBNS_PORT, buffer, offset, IPV4_DEFAULT_TTL); //Free previously allocated memory netBufferFree(buffer); //Return status code return error; }
error_t dnsSendQuery(DnsCacheEntry *entry) { error_t error; size_t length; size_t offset; NetBuffer *buffer; DnsHeader *message; DnsQuestion *dnsQuestion; IpAddr destIpAddr; #if (IPV4_SUPPORT == ENABLED) //An IPv4 address is expected? if(entry->type == HOST_TYPE_IPV4) { //Select the relevant DNS server destIpAddr.length = sizeof(Ipv4Addr); ipv4GetDnsServer(entry->interface, entry->dnsServerNum, &destIpAddr.ipv4Addr); //Make sure the IP address is valid if(destIpAddr.ipv4Addr == IPV4_UNSPECIFIED_ADDR) return ERROR_NO_DNS_SERVER; } else #endif #if (IPV6_SUPPORT == ENABLED) //An IPv6 address is expected? if(entry->type == HOST_TYPE_IPV6) { //Select the relevant DNS server destIpAddr.length = sizeof(Ipv6Addr); ipv6GetDnsServer(entry->interface, entry->dnsServerNum, &destIpAddr.ipv6Addr); //Make sure the IP address is valid if(ipv6CompAddr(&destIpAddr.ipv6Addr, &IPV6_UNSPECIFIED_ADDR)) return ERROR_NO_DNS_SERVER; } else #endif //Invalid host type? { //Report an error return ERROR_INVALID_PARAMETER; } //Allocate a memory buffer to hold the DNS query message buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the DNS header message = netBufferAt(buffer, offset); //Format DNS query message message->id = htons(entry->id); message->qr = 0; message->opcode = DNS_OPCODE_QUERY; message->aa = 0; message->tc = 0; message->rd = 1; message->ra = 0; message->z = 0; message->rcode = DNS_RCODE_NO_ERROR; //The DNS query contains one question message->qdcount = HTONS(1); message->ancount = 0; message->nscount = 0; message->arcount = 0; //Length of the DNS query message length = sizeof(DnsHeader); //Encode the host name using the DNS name notation length += dnsEncodeName(entry->name, message->questions); //Point to the corresponding question structure dnsQuestion = DNS_GET_QUESTION(message, length); #if (IPV4_SUPPORT == ENABLED) //An IPv4 address is expected? if(entry->type == HOST_TYPE_IPV4) { //Fill in question structure dnsQuestion->qtype = HTONS(DNS_RR_TYPE_A); dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN); } #endif #if (IPV6_SUPPORT == ENABLED) //An IPv6 address is expected? if(entry->type == HOST_TYPE_IPV6) { //Fill in question structure dnsQuestion->qtype = HTONS(DNS_RR_TYPE_AAAA); dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN); } #endif //Update the length of the DNS query message length += sizeof(DnsQuestion); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending DNS message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); //Send DNS query message error = udpSendDatagramEx(entry->interface, entry->port, &destIpAddr, DNS_PORT, buffer, offset, 0); //Free previously allocated memory netBufferFree(buffer); //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); }
void ipv4ReassembleDatagram(NetInterface *interface, const Ipv4Header *packet, size_t length) { error_t error; uint16_t offset; uint16_t dataFirst; uint16_t dataLast; Ipv4FragDesc *frag; Ipv4HoleDesc *hole; Ipv4HoleDesc *prevHole; //Get the length of the payload length -= packet->headerLength * 4; //Convert the fragment offset from network byte order offset = ntohs(packet->fragmentOffset); //Every fragment except the last must contain a multiple of 8 bytes of data if((offset & IPV4_FLAG_MF) && (length % 8)) { //Drop incoming packet return; } //Calculate the index of the first byte dataFirst = (offset & IPV4_OFFSET_MASK) * 8; //Calculate the index immediately following the last byte dataLast = dataFirst + length; //Search for a matching IP datagram being reassembled frag = ipv4SearchFragQueue(interface, packet); //No matching entry in the reassembly queue? if(!frag) return; //The very first fragment requires special handling if(!(offset & IPV4_OFFSET_MASK)) { //Calculate the length of the IP header including options frag->headerLength = packet->headerLength * 4; //Enforce the size of the reconstructed datagram if((frag->headerLength + frag->dataLength) > IPV4_MAX_FRAG_DATAGRAM_SIZE) { //Drop any allocated resources netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return; } //Make sure the IP header entirely fits in the first chunk if(frag->headerLength > frag->buffer.chunk[0].size) { //Drop the reconstructed datagram netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return; } //Fix the length of the first chunk frag->buffer.chunk[0].length = frag->headerLength; //Always take the IP header from the first fragment netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->headerLength); } //It may be necessary to increase the size of the buffer... if(dataLast > frag->dataLength) { //Enforce the size of the reconstructed datagram if((frag->headerLength + dataLast) > IPV4_MAX_FRAG_DATAGRAM_SIZE) { //Drop any allocated resources netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return; } //Adjust the size of the reconstructed datagram error = netBufferSetLength((NetBuffer *) &frag->buffer, frag->headerLength + dataLast + sizeof(Ipv4HoleDesc)); //Any error to report? if(error) { //Drop the reconstructed datagram netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return; } //Actual length of the payload frag->dataLength = dataLast; } //Select the first hole descriptor from the list hole = ipv4FindHole(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 = ipv4FindHole(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 & IPV4_FLAG_MF)) { //Create a new entry that describes this hole hole = ipv4FindHole(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 = ipv4FindHole(frag, prevHole ? prevHole->next : frag->firstHole); } //Copy data from the fragment to the reassembly buffer netBufferWrite((NetBuffer *) &frag->buffer, frag->headerLength + dataFirst, IPV4_DATA(packet), length); //Dump hole descriptor list ipv4DumpHoleList(frag); //If the hole descriptor list is empty, the reassembly process is now complete if(!ipv4FindHole(frag, frag->firstHole)) { //Discard the extra hole descriptor that follows the reconstructed datagram error = netBufferSetLength((NetBuffer *) &frag->buffer, frag->headerLength + frag->dataLength); //Check status code if(!error) { //Point to the IP header Ipv4Header *datagram = netBufferAt((NetBuffer *) &frag->buffer, 0); //Fix IP header datagram->totalLength = htons(frag->headerLength + frag->dataLength); datagram->fragmentOffset = 0; datagram->headerChecksum = 0; //Recalculate IP header checksum datagram->headerChecksum = ipCalcChecksum(datagram, frag->buffer.chunk[0].length); //Pass the original IPv4 datagram to the higher protocol layer ipv4ProcessDatagram(interface, (NetBuffer *) &frag->buffer); } //Release previously allocated memory netBufferSetLength((NetBuffer *) &frag->buffer, 0); } }