void mldProcessListenerReport(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { uint_t i; size_t length; MldMessage *message; Ipv6FilterEntry *entry; //Retrieve the length of the MLD message length = chunkedBufferGetLength(buffer) - offset; //The message must be at least 24 octets long if(length < sizeof(MldMessage)) return; //Point to the beginning of the MLD message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose mldDumpMessage(message); //Make sure the source address of the message is a valid link-local address if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) return; //Check the Hop Limit field if(hopLimit != MLD_HOP_LIMIT) return; //Acquire exclusive access to the IPv6 filter table osMutexAcquire(interface->ipv6FilterMutex); //Loop through filter table entries for(i = 0; i < interface->ipv6FilterSize; i++) { //Point to the current entry entry = &interface->ipv6Filter[i]; //Report messages are ignored for multicast addresses //in the Non-Listener or Idle Listener state if(entry->state == MLD_STATE_DELAYING_LISTENER) { //The Multicast Listener Report message matches the current entry? if(ipv6CompAddr(&message->multicastAddr, &entry->addr)) { //Clear flag entry->flag = FALSE; //Switch to the Idle Listener state entry->state = MLD_STATE_IDLE_LISTENER; } } } //Release exclusive access to the IPv6 filter table osMutexRelease(interface->ipv6FilterMutex); }
error_t mldSendListenerDone(NetInterface *interface, Ipv6Addr *ipAddr) { error_t error; size_t offset; MldMessage *message; ChunkedBuffer *buffer; Ipv6PseudoHeader pseudoHeader; //Make sure the specified address is a valid multicast address if(!ipv6IsMulticastAddr(ipAddr)) return ERROR_INVALID_ADDRESS; //The link-scope all-nodes address (FF02::1) is handled as a special //case. The host never sends a report for that address if(ipv6CompAddr(ipAddr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) return ERROR_INVALID_ADDRESS; //Allocate a memory buffer to hold a MLD message buffer = ipAllocBuffer(sizeof(MldMessage), &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the beginning of the MLD message message = chunkedBufferAt(buffer, offset); //Format the Multicast Listener Done message message->type = ICMPV6_TYPE_MULTICAST_LISTENER_DONE_V1; message->code = 0; message->checksum = 0; message->maxRespDelay = 0; message->reserved = 0; message->multicastAddr = *ipAddr; //Format IPv6 pseudo header pseudoHeader.srcAddr = interface->ipv6Config.linkLocalAddr; pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR; pseudoHeader.length = HTONS(sizeof(MldMessage)); pseudoHeader.reserved = 0; pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; //Message checksum calculation message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(MldMessage)); //Debug message TRACE_INFO("Sending MLD message (%" PRIuSIZE " bytes)...\r\n", sizeof(MldMessage)); //Dump message contents for debugging purpose mldDumpMessage(message); //The Multicast Listener Done message is sent to the all-routers multicast address error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, MLD_HOP_LIMIT); //Free previously allocated memory chunkedBufferFree(buffer); //Return status code return error; }
void icmpProcessMessage(NetInterface *interface, Ipv4Addr srcIpAddr, const ChunkedBuffer *buffer, size_t offset) { size_t length; IcmpHeader *header; //Retrieve the length of the ICMP message length = chunkedBufferGetLength(buffer) - offset; //Ensure the message length is correct if(length < sizeof(IcmpHeader)) { //Debug message TRACE_WARNING("ICMP message length is invalid!\r\n"); //Silently discard incoming message return; } //Point to the ICMP message header header = chunkedBufferAt(buffer, offset); //Sanity check if(!header) return; //Debug message TRACE_INFO("ICMP message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpDumpMessage(header); //Verify checksum value if(ipCalcChecksumEx(buffer, offset, length) != 0x0000) { //Debug message TRACE_WARNING("Wrong ICMP header checksum!\r\n"); //Drop incoming message return; } //Check the type of ICMP message switch(header->type) { //Echo request? case ICMP_TYPE_ECHO_REQUEST: //Process Echo Request message icmpProcessEchoRequest(interface, srcIpAddr, buffer, offset); break; //Unknown type? default: //Debug message TRACE_WARNING("Unknown ICMP message type!\r\n"); //Discard incoming ICMP message break; } }
void mdnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const ChunkedBuffer *buffer, size_t offset, void *params) { size_t length; DnsHeader *message; //Retrieve the length of the mDNS message length = chunkedBufferGetLength(buffer) - offset; //Ensure the mDNS message is valid if(length < sizeof(DnsHeader)) return; if(length > DNS_MESSAGE_MAX_SIZE) return; //Point to the mDNS message header message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("mDNS message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); //mDNS messages received with an opcode other than zero must be silently ignored if(message->opcode != DNS_OPCODE_QUERY) return; //mDNS messages received with non-zero response codes must be silently ignored if(message->rcode != DNS_RCODE_NO_ERROR) return; #if (MDNS_RESPONDER_SUPPORT == ENABLED) //mDNS query received? if(!message->qr) { //Process incoming mDNS query message mdnsProcessQuery(interface, pseudoHeader, udpHeader, message, length); } #endif #if (MDNS_CLIENT_SUPPORT == ENABLED) //mDNS response received? if(message->qr) { //Process incoming mDNS response message mdnsProcessResponse(interface, pseudoHeader, udpHeader, message, length); } #endif }
void ndpProcessRouterAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { size_t length; NdpRouterAdvMessage *message; //Retrieve the length of the message length = chunkedBufferGetLength(buffer) - offset; //Check the length of the Router Advertisement message if(length < sizeof(NdpRouterAdvMessage)) return; //Point to the beginning of the message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("Router Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpRouterAdvMessage(message); //Routers must use their link-local address as the source for the //Router Advertisement so that hosts can uniquely identify routers if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) return; //The IPv6 Hop Limit field must have a value of 255 to ensure //that the packet has not been forwarded by a router if(hopLimit != NDP_HOP_LIMIT) return; //ICMPv6 Code must be 0. An advertisement that passes the validity //checks is called a valid advertisement if(message->code) return; #if (SLAAC_SUPPORT == ENABLED) //Stateless Address Autoconfiguration is currently used? if(interface->slaacContext != NULL) { //Process the valid advertisement slaacProcessRouterAdv(interface->slaacContext, &pseudoHeader->srcAddr, message, length); } #endif }
error_t arpSendReply(NetInterface *interface, Ipv4Addr targetIpAddr, const MacAddr *targetMacAddr, const MacAddr *destMacAddr) { error_t error; size_t offset; ChunkedBuffer *buffer; ArpPacket *arpReply; //Allocate a memory buffer to hold an ARP packet buffer = ethAllocBuffer(sizeof(ArpPacket), &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the beginning of the ARP packet arpReply = chunkedBufferAt(buffer, offset); //Format ARP reply arpReply->hrd = htons(ARP_HARDWARE_TYPE_ETH); arpReply->pro = htons(ETH_TYPE_IPV4); arpReply->hln = sizeof(MacAddr); arpReply->pln = sizeof(Ipv4Addr); arpReply->op = htons(ARP_OPCODE_ARP_REPLY); arpReply->sha = interface->macAddr; arpReply->spa = interface->ipv4Config.addr; arpReply->tha = *targetMacAddr; arpReply->tpa = targetIpAddr; //Debug message TRACE_INFO("Sending ARP Reply (%u bytes)...\r\n", sizeof(ArpPacket)); //Dump ARP packet contents for debugging purpose arpDumpPacket(arpReply); //Send ARP reply error = ethSendFrame(interface, destMacAddr, buffer, offset, ETH_TYPE_ARP); //Free previously allocated memory chunkedBufferFree(buffer); //Return status code return error; }
Ipv6HoleDesc *ipv6FindHole(Ipv6FragDesc *frag, uint16_t offset) { //Return a pointer to the hole descriptor return chunkedBufferAt((ChunkedBuffer *) &frag->buffer, frag->unfragPartLength + offset); }
Ipv6FragDesc *ipv6SearchFragQueue(NetInterface *interface, Ipv6Header *packet, Ipv6FragmentHeader *header) { error_t error; uint_t i; Ipv6Header *datagram; Ipv6FragDesc *frag; Ipv6HoleDesc *hole; //Search for a matching IP datagram being reassembled for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue frag = &interface->ipv6FragQueue[i]; //Check whether the current entry is used? if(frag->buffer.chunkCount > 0) { //Point to the corresponding datagram datagram = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, 0); //Check source and destination addresses if(!ipv6CompAddr(&datagram->srcAddr, &packet->srcAddr)) continue; if(!ipv6CompAddr(&datagram->destAddr, &packet->destAddr)) continue; //Compare fragment identification fields if(frag->identification != header->identification) 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 < IPV6_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue frag = &interface->ipv6FragQueue[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 IPv6 header and //the first hole descriptor error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, MEM_POOL_BUFFER_SIZE + sizeof(Ipv6HoleDesc)); //Failed to allocate memory? if(error) { //Clean up side effects chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return NULL; } //Initial length of the reconstructed datagram frag->unfragPartLength = sizeof(Ipv6Header); frag->fragPartLength = 0; //Fix the length of the first chunk frag->buffer.chunk[0].length = frag->unfragPartLength; //Copy IPv6 header from the incoming fragment chunkedBufferWrite((ChunkedBuffer *) &frag->buffer, 0, packet, frag->unfragPartLength); //Save current time frag->timestamp = osGetSystemTime(); //Record fragment identification field frag->identification = header->identification; //Create a new entry in the hole descriptor list frag->firstHole = 0; //Point to first hole descriptor hole = ipv6FindHole(frag, frag->firstHole); //The entry describes the datagram as being completely missing hole->first = 0; hole->last = IPV6_INFINITY; hole->next = IPV6_INFINITY; //Dump hole descriptor list ipv6DumpHoleList(frag); //Return the matching fragment descriptor return frag; } } //The reassembly queue is full return NULL; }
void ipv6ParseFragmentHeader(NetInterface *interface, const ChunkedBuffer *buffer, size_t fragHeaderOffset, size_t nextHeaderOffset) { error_t error; size_t length; uint16_t offset; uint16_t dataFirst; uint16_t dataLast; Ipv6FragDesc *frag; Ipv6HoleDesc *hole; Ipv6HoleDesc *prevHole; Ipv6Header *packet; Ipv6FragmentHeader *header; //Remaining bytes to process in the payload length = chunkedBufferGetLength(buffer) - fragHeaderOffset; //Ensure the fragment header is valid if(length < sizeof(Ipv6FragmentHeader)) return; //Point to the IPv6 header packet = chunkedBufferAt(buffer, 0); //Sanity check if(!packet) return; //Point to the IPv6 Fragment header header = chunkedBufferAt(buffer, fragHeaderOffset); //Sanity check if(!header) return; //Calculate the length of the fragment length -= sizeof(Ipv6FragmentHeader); //Convert the fragment offset from network byte order offset = ntohs(header->fragmentOffset); //Every fragment except the last must contain a multiple of 8 bytes of data if((offset & IPV6_FLAG_M) && (length % 8)) { //Compute the offset of the Payload Length field within the packet size_t n = (uint8_t *) &packet->payloadLength - (uint8_t *) packet; //The fragment must be discarded and an ICMP Parameter Problem //message should be sent to the source of the fragment, pointing //to the Payload Length field of the fragment packet icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM, ICMPV6_CODE_INVALID_HEADER_FIELD, n, buffer); //Exit immediately return; } //Calculate the index of the first byte dataFirst = offset & IPV6_OFFSET_MASK; //Calculate the index immediately following the last byte dataLast = dataFirst + length; //Search for a matching IP datagram being reassembled frag = ipv6SearchFragQueue(interface, packet, header); //No matching entry in the reassembly queue? if(!frag) return; //The very first fragment requires special handling if(!(offset & IPV6_OFFSET_MASK)) { uint8_t *p; //Calculate the length of the unfragmentable part frag->unfragPartLength = fragHeaderOffset; //The size of the reconstructed datagram exceeds the maximum value? if((frag->unfragPartLength + frag->fragPartLength) > IPV6_MAX_FRAG_DATAGRAM_SIZE) { //Compute the offset of the Fragment Offset field within the packet size_t n = fragHeaderOffset + (uint8_t *) &header->fragmentOffset - (uint8_t *) header; //The fragment must be discarded and an ICMP Parameter Problem //message should be sent to the source of the fragment, pointing //to the Fragment Offset field of the fragment packet icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM, ICMPV6_CODE_INVALID_HEADER_FIELD, n, buffer); //Drop any allocated resources chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return; } //Make sure the unfragmentable part entirely fits in the first chunk if(frag->unfragPartLength > frag->buffer.chunk[0].size) { //Drop the reconstructed datagram chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return; } //Fix the length of the first chunk frag->buffer.chunk[0].length = frag->unfragPartLength; //The unfragmentable part of the reassembled packet consists //of all headers up to, but not including, the Fragment header //of the first fragment packet chunkedBufferCopy((ChunkedBuffer *) &frag->buffer, 0, buffer, 0, frag->unfragPartLength); //Point to the Next Header field of the last header p = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, nextHeaderOffset); //The Next Header field of the last header of the unfragmentable //part is obtained from the Next Header field of the first //fragment's Fragment header *p = header->nextHeader; } //It may be necessary to increase the size of the buffer... if(dataLast > frag->fragPartLength) { //The size of the reconstructed datagram exceeds the maximum value? if((frag->unfragPartLength + dataLast) > IPV6_MAX_FRAG_DATAGRAM_SIZE) { //Compute the offset of the Fragment Offset field within the packet size_t n = fragHeaderOffset + (uint8_t *) &header->fragmentOffset - (uint8_t *) header; //The fragment must be discarded and an ICMP Parameter Problem //message should be sent to the source of the fragment, pointing //to the Fragment Offset field of the fragment packet icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM, ICMPV6_CODE_INVALID_HEADER_FIELD, n, buffer); //Drop any allocated resources chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return; } //Adjust the size of the reconstructed datagram error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, frag->unfragPartLength + dataLast + sizeof(Ipv6HoleDesc)); //Any error to report? if(error) { //Drop the reconstructed datagram chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return; } //Actual length of the fragmentable part frag->fragPartLength = dataLast; } //Select the first hole descriptor from the list hole = ipv6FindHole(frag, frag->firstHole); //Keep track of the previous hole in the list prevHole = NULL; //Iterate through the hole descriptors while(hole != NULL) { //Save lower and upper boundaries for later use uint16_t holeFirst = hole->first; uint16_t holeLast = hole->last; //Check whether the newly arrived fragment //interacts with this hole in some way if(dataFirst < holeLast && dataLast > holeFirst) { //The current descriptor is no longer valid. We will destroy //it, and in the next two steps, we will determine whether //or not it is necessary to create any new hole descriptors if(prevHole != NULL) prevHole->next = hole->next; else frag->firstHole = hole->next; //Is there still a hole at the beginning of the segment? if(dataFirst > holeFirst) { //Create a new entry that describes this hole hole = ipv6FindHole(frag, holeFirst); hole->first = holeFirst; hole->last = dataFirst; //Insert the newly created entry into the hole descriptor list if(prevHole != NULL) { hole->next = prevHole->next; prevHole->next = hole->first; } else { hole->next = frag->firstHole; frag->firstHole = hole->first; } //Always keep track of the previous hole prevHole = hole; } //Is there still a hole at the end of the segment? if(dataLast < holeLast && (offset & IPV6_FLAG_M)) { //Create a new entry that describes this hole hole = ipv6FindHole(frag, dataLast); hole->first = dataLast; hole->last = holeLast; //Insert the newly created entry into the hole descriptor list if(prevHole != NULL) { hole->next = prevHole->next; prevHole->next = hole->first; } else { hole->next = frag->firstHole; frag->firstHole = hole->first; } //Always keep track of the previous hole prevHole = hole; } } else { //The newly arrived fragment does not interact with the current hole prevHole = hole; } //Select the next hole descriptor from the list hole = ipv6FindHole(frag, prevHole ? prevHole->next : frag->firstHole); } //Copy data from the fragment to the reassembly buffer chunkedBufferCopy((ChunkedBuffer *) &frag->buffer, frag->unfragPartLength + dataFirst, buffer, fragHeaderOffset + sizeof(Ipv6FragmentHeader), length); //Dump hole descriptor list ipv6DumpHoleList(frag); //If the hole descriptor list is empty, the reassembly process is now complete if(!ipv6FindHole(frag, frag->firstHole)) { //Discard the extra hole descriptor that follows the reconstructed datagram error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, frag->unfragPartLength + frag->fragPartLength); //Check status code if(!error) { //Point to the IPv6 header Ipv6Header *datagram = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, 0); //Fix the Payload Length field datagram->payloadLength = htons(frag->unfragPartLength + frag->fragPartLength - sizeof(Ipv6Header)); //Pass the original IPv6 datagram to the higher protocol layer ipv6ProcessPacket(interface, (ChunkedBuffer *) &frag->buffer); } //Release previously allocated memory chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); } }
Ipv4HoleDesc *ipv4FindHole(Ipv4FragDesc *frag, uint16_t offset) { //Return a pointer to the hole descriptor return chunkedBufferAt((ChunkedBuffer *) &frag->buffer, frag->headerLength + offset); }
error_t rawSocketProcessIpPacket(NetInterface *interface, IpPseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset) { uint_t i; size_t length; Socket *socket; SocketQueueItem *queueItem; ChunkedBuffer *p; //Retrieve the length of the raw IP packet length = chunkedBufferGetLength(buffer) - offset; //Enter critical section osAcquireMutex(&socketMutex); //Loop through opened sockets for(i = 0; i < SOCKET_MAX_COUNT; i++) { //Point to the current socket socket = socketTable + i; //Raw socket found? if(socket->type != SOCKET_TYPE_RAW_IP) continue; //Check whether the socket is bound to a particular interface if(socket->interface && socket->interface != interface) continue; #if (IPV4_SUPPORT == ENABLED) //An IPv4 packet was received? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Check protocol field if(socket->protocol != pseudoHeader->ipv4Data.protocol) continue; //Destination IP address filtering if(socket->localIpAddr.length) { //An IPv4 address is expected if(socket->localIpAddr.length != sizeof(Ipv4Addr)) continue; //Filter out non-matching addresses if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr) continue; } //Source IP address filtering if(socket->remoteIpAddr.length) { //An IPv4 address is expected if(socket->remoteIpAddr.length != sizeof(Ipv4Addr)) continue; //Filter out non-matching addresses if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr) continue; } } else #endif #if (IPV6_SUPPORT == ENABLED) //An IPv6 packet was received? if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Check protocol field if(socket->protocol != pseudoHeader->ipv6Data.nextHeader) continue; //Destination IP address filtering if(socket->localIpAddr.length) { //An IPv6 address is expected if(socket->localIpAddr.length != sizeof(Ipv6Addr)) continue; //Filter out non-matching addresses if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr)) continue; } //Source IP address filtering if(socket->remoteIpAddr.length) { //An IPv6 address is expected if(socket->remoteIpAddr.length != sizeof(Ipv6Addr)) continue; //Filter out non-matching addresses if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr)) continue; } } else #endif //An invalid packet was received? { //This should never occur... continue; } //The current socket meets all the criteria break; } //Drop incoming packet if no matching socket was found if(i >= SOCKET_MAX_COUNT) { //Leave critical section osReleaseMutex(&socketMutex); //Unreachable protocol... return ERROR_PROTOCOL_UNREACHABLE; } //Empty receive queue? if(!socket->receiveQueue) { //Allocate a memory buffer to hold the data and the associated descriptor p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Point to the newly created item queueItem = chunkedBufferAt(p, 0); queueItem->buffer = p; //Add the newly created item to the queue socket->receiveQueue = queueItem; } else { //Memory allocation failed queueItem = NULL; } } else { //Point to the very first item queueItem = socket->receiveQueue; //Reach the last item in the receive queue for(i = 1; queueItem->next; i++) queueItem = queueItem->next; //Make sure the receive queue is not full if(i >= RAW_SOCKET_RX_QUEUE_SIZE) { //Leave critical section osReleaseMutex(&socketMutex); //Notify the calling function that the queue is full return ERROR_RECEIVE_QUEUE_FULL; } //Allocate a memory buffer to hold the data and the associated descriptor p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Add the newly created item to the queue queueItem->next = chunkedBufferAt(p, 0); //Point to the newly created item queueItem = queueItem->next; queueItem->buffer = p; } else { //Memory allocation failed queueItem = NULL; } } //Failed to allocate memory? if(!queueItem) { //Leave critical section osReleaseMutex(&socketMutex); //Return error code return ERROR_OUT_OF_MEMORY; } //Initialize next field queueItem->next = NULL; //Port number is unused queueItem->srcPort = 0; #if (IPV4_SUPPORT == ENABLED) //IPv4 remote address? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Save the source IPv4 address queueItem->srcIpAddr.length = sizeof(Ipv4Addr); queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr; //Save the destination IPv4 address queueItem->destIpAddr.length = sizeof(Ipv4Addr); queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr; } #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 remote address? if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Save the source IPv6 address queueItem->srcIpAddr.length = sizeof(Ipv6Addr); queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr; //Save the destination IPv6 address queueItem->destIpAddr.length = sizeof(Ipv6Addr); queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr; } #endif //Offset to the raw IP packet queueItem->offset = sizeof(SocketQueueItem); //Copy the raw data chunkedBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length); //Notify user that data is available rawSocketUpdateEvents(socket); //Leave critical section osReleaseMutex(&socketMutex); //Successful processing return NO_ERROR; }
error_t ndpSendNeighborSol(NetInterface *interface, const Ipv6Addr *targetIpAddr) { error_t error; size_t offset; size_t length; ChunkedBuffer *buffer; NdpNeighborSolMessage *message; Ipv6PseudoHeader pseudoHeader; //Allocate a memory buffer to hold the Neighbor Solicitation //message and the Source Link-Layer Address option buffer = ipAllocBuffer(sizeof(NdpNeighborSolMessage) + sizeof(NdpLinkLayerAddrOption), &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the beginning of the message message = chunkedBufferAt(buffer, offset); //Format Neighbor Solicitation message message->type = ICMPV6_TYPE_NEIGHBOR_SOL; message->code = 0; message->checksum = 0; message->reserved = 0; message->targetAddr = *targetIpAddr; //Length of the message, excluding any option length = sizeof(NdpNeighborSolMessage); //Check whether the target address is a tentative address if(ipv6IsTentativeAddr(interface, targetIpAddr)) { //The IPv6 source is set to the unspecified address pseudoHeader.srcAddr = IPV6_UNSPECIFIED_ADDR; } else { //The Source Link-Layer Address option must not be included //when the host IPv6 address is unspecified if(!ipv6CompAddr(&interface->ipv6Config.linkLocalAddr, &IPV6_UNSPECIFIED_ADDR)) { //Add Source Link-Layer Address option ndpAddOption(message, &length, NDP_OPT_SOURCE_LINK_LAYER_ADDR, &interface->macAddr, sizeof(MacAddr)); } //Set the IPv6 source address pseudoHeader.srcAddr = interface->ipv6Config.linkLocalAddr; } //Adjust the length of the multi-part buffer chunkedBufferSetLength(buffer, offset + length); //Compute the solicited-node multicast address that //corresponds to the target IPv6 address ipv6ComputeSolicitedNodeAddr(targetIpAddr, &pseudoHeader.destAddr); //Format IPv6 pseudo header pseudoHeader.length = htonl(length); pseudoHeader.reserved = 0; pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; //Calculate ICMPv6 header checksum message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, sizeof(Ipv6PseudoHeader), buffer, offset, length); //Debug message TRACE_INFO("Sending Neighbor Solicitation message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpNeighborSolMessage(message); //Send Neighbor Solicitation message error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); //Free previously allocated memory chunkedBufferFree(buffer); //Return status code return error; }
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 mldProcessListenerQuery(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { uint_t i; size_t length; systime_t time; systime_t maxRespDelay; MldMessage *message; Ipv6FilterEntry *entry; //Retrieve the length of the MLD message length = chunkedBufferGetLength(buffer) - offset; //The message must be at least 24 octets long if(length < sizeof(MldMessage)) return; //Point to the beginning of the MLD message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose mldDumpMessage(message); //Make sure the source address of the message is a valid link-local address if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) return; //Check the Hop Limit field if(hopLimit != MLD_HOP_LIMIT) return; //Get current time time = osGetTickCount(); //The Max Resp Delay field specifies the maximum time allowed //before sending a responding report maxRespDelay = message->maxRespDelay * 10; //Acquire exclusive access to the IPv6 filter table osMutexAcquire(interface->ipv6FilterMutex); //Loop through filter table entries for(i = 0; i < interface->ipv6FilterSize; i++) { //Point to the current entry entry = &interface->ipv6Filter[i]; //The link-scope all-nodes address (FF02::1) is handled as a special //case. The host starts in Idle Listener state for that address on //every interface and never transitions to another state if(ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) continue; //A General Query is used to learn which multicast addresses have listeners //on an attached link. A Multicast-Address-Specific Query is used to learn //if a particular multicast address has any listeners on an attached link if(ipv6CompAddr(&message->multicastAddr, &IPV6_UNSPECIFIED_ADDR) || ipv6CompAddr(&message->multicastAddr, &entry->addr)) { //Delaying Listener state? if(entry->state == MLD_STATE_DELAYING_LISTENER) { //The timer has not yet expired? if(timeCompare(time, entry->timer) < 0) { //If a timer for the address is already running, it is reset to //the new random value only if the requested Max Response Delay //is less than the remaining value of the running timer if(maxRespDelay < (entry->timer - time)) { //Restart delay timer entry->timer = time + mldRand(maxRespDelay); } } } //Idle Listener state? else if(entry->state == MLD_STATE_IDLE_LISTENER) { //Switch to the Delaying Listener state entry->state = MLD_STATE_DELAYING_LISTENER; //Delay the response by a random amount of time entry->timer = time + mldRand(maxRespDelay); } } } //Release exclusive access to the IPv6 filter table osMutexRelease(interface->ipv6FilterMutex); }
void dnsProcessResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const ChunkedBuffer *buffer, size_t offset, void *params) { uint_t i; uint_t j; size_t n; size_t pos; size_t length; DnsHeader *message; DnsQuestion *question; DnsResourceRecord *resourceRecord; DnsCacheEntry *entry; //Retrieve the length of the DNS message length = chunkedBufferGetLength(buffer) - offset; //Ensure the DNS message is valid if(length < sizeof(DnsHeader)) return; if(length > DNS_MESSAGE_MAX_SIZE) return; //Point to the DNS message header message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("DNS message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); //Acquire exclusive access to the DNS cache osMutexAcquire(dnsCacheMutex); //Loop through DNS cache entries for(i = 0; i < DNS_CACHE_SIZE; i++) { //Point to the current entry entry = &dnsCache[i]; //DNS name resolution in progress? if(entry->state == DNS_STATE_IN_PROGRESS && entry->protocol == HOST_NAME_RESOLVER_DNS) { //Check destination port number if(entry->port == ntohs(udpHeader->destPort)) { //Compare identifier against expected one if(ntohs(message->id) != entry->id) break; //Check message type if(!message->qr) break; //The DNS message shall contain one question if(ntohs(message->qdcount) != 1) break; //Point to the first question pos = sizeof(DnsHeader); //Parse domain name n = dnsParseName(message, length, pos, NULL, 0); //Invalid name? if(!n) break; //Malformed mDNS message? if((n + sizeof(DnsQuestion)) > length) break; //Compare domain name if(!dnsCompareName(message, length, pos, entry->name, 0)) break; //Point to the corresponding entry question = DNS_GET_QUESTION(message, n); //Check the class of the query if(ntohs(question->qclass) != DNS_RR_CLASS_IN) break; //Check the type of the query if(entry->type == HOST_TYPE_IPV4 && ntohs(question->qtype) != DNS_RR_TYPE_A) break; if(entry->type == HOST_TYPE_IPV6 && ntohs(question->qtype) != DNS_RR_TYPE_AAAA) break; //Make sure recursion is available if(!message->ra) { //The entry should be deleted since name resolution has failed dnsDeleteEntry(entry); //Exit immediately break; } //Check return code if(message->rcode != DNS_RCODE_NO_ERROR) { //The entry should be deleted since name resolution has failed dnsDeleteEntry(entry); //Exit immediately break; } //Point to the first answer pos = n + sizeof(DnsQuestion); //Parse answer resource records for(j = 0; j < ntohs(message->ancount); j++) { //Parse domain name pos = dnsParseName(message, length, pos, NULL, 0); //Invalid name? if(!pos) break; //Point to the associated resource record resourceRecord = DNS_GET_RESOURCE_RECORD(message, pos); //Point to the resource data pos += sizeof(DnsResourceRecord); //Make sure the resource record is valid if(pos >= length) break; if((pos + ntohs(resourceRecord->rdlength)) > length) break; #if (IPV4_SUPPORT == ENABLED) //IPv4 address expected? if(entry->type == HOST_TYPE_IPV4) { //A resource record found? if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_A) { //Verify the length of the data field if(ntohs(resourceRecord->rdlength) == sizeof(Ipv4Addr)) { //Copy the IPv4 address entry->ipAddr.length = sizeof(Ipv4Addr); ipv4CopyAddr(&entry->ipAddr.ipv4Addr, resourceRecord->rdata); //Save current time entry->timestamp = osGetTickCount(); //Save TTL value entry->timeout = ntohl(resourceRecord->ttl) * 1000; //Limit the lifetime of the DNS cache entries entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME); //Unregister UDP callback function udpDetachRxCallback(interface, entry->port); //Host name successfully resolved entry->state = DNS_STATE_RESOLVED; //Exit immediately break; } } } #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 address expected? if(entry->type == HOST_TYPE_IPV6) { //AAAA resource record found? if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_AAAA) { //Verify the length of the data field if(ntohs(resourceRecord->rdlength) == sizeof(Ipv6Addr)) { //Copy the IPv6 address entry->ipAddr.length = sizeof(Ipv6Addr); ipv6CopyAddr(&entry->ipAddr.ipv6Addr, resourceRecord->rdata); //Save current time entry->timestamp = osGetTickCount(); //Save TTL value entry->timeout = ntohl(resourceRecord->ttl) * 1000; //Limit the lifetime of the DNS cache entries entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME); //Unregister UDP callback function udpDetachRxCallback(interface, entry->port); //Host name successfully resolved entry->state = DNS_STATE_RESOLVED; //Exit immediately break; } } } #endif //Point to the next resource record pos += ntohs(resourceRecord->rdlength); } //We are done break; } } } //Release exclusive access to the DNS cache osMutexRelease(dnsCacheMutex); }
error_t dnsSendQuery(DnsCacheEntry *entry) { error_t error; size_t length; size_t offset; ChunkedBuffer *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 = chunkedBufferAt(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 chunkedBufferSetLength(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 chunkedBufferFree(buffer); //Return status code return error; }
error_t icmpv6SendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code, uint32_t parameter, const ChunkedBuffer *ipPacket) { error_t error; size_t offset; size_t length; Ipv6Header *ipHeader; ChunkedBuffer *icmpMessage; Icmpv6ErrorMessage *icmpHeader; Ipv6PseudoHeader pseudoHeader; //Retrieve the length of the invoking IPv6 packet length = chunkedBufferGetLength(ipPacket); //Check the length of the IPv6 packet if(length < sizeof(Ipv6Header)) return ERROR_INVALID_LENGTH; //Point to the header of the invoking packet ipHeader = chunkedBufferAt(ipPacket, 0); //Sanity check if(!ipHeader) return ERROR_FAILURE; //Never respond to a packet destined to an IPv6 multicast address if(ipv6IsMulticastAddr(&ipHeader->destAddr)) return ERROR_INVALID_ADDRESS; //Return as much of invoking IPv6 packet as possible without //the ICMPv6 packet exceeding the minimum IPv6 MTU length = MIN(length, IPV6_DEFAULT_MTU - sizeof(Ipv6Header) - sizeof(Icmpv6ErrorMessage)); //Allocate a memory buffer to hold the ICMPv6 message icmpMessage = ipAllocBuffer(sizeof(Icmpv6ErrorMessage), &offset); //Failed to allocate memory? if(!icmpMessage) return ERROR_OUT_OF_MEMORY; //Point to the ICMPv6 header icmpHeader = chunkedBufferAt(icmpMessage, offset); //Format ICMPv6 Error message icmpHeader->type = type; icmpHeader->code = code; icmpHeader->checksum = 0; icmpHeader->parameter = htonl(parameter); //Copy incoming IPv6 packet contents error = chunkedBufferConcat(icmpMessage, ipPacket, 0, length); //Any error to report? if(error) { //Clean up side effects chunkedBufferFree(icmpMessage); //Exit immediately return error; } //Get the length of the resulting message length = chunkedBufferGetLength(icmpMessage) - offset; //Format IPv6 pseudo header pseudoHeader.srcAddr = ipHeader->destAddr; pseudoHeader.destAddr = ipHeader->srcAddr; pseudoHeader.length = htonl(length); pseudoHeader.reserved = 0; pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; //Message checksum calculation icmpHeader->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, sizeof(Ipv6PseudoHeader), icmpMessage, offset, length); //Debug message TRACE_INFO("Sending ICMPv6 Error message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpv6DumpErrorMessage(icmpHeader); //Send ICMPv6 Error message error = ipv6SendDatagram(interface, &pseudoHeader, icmpMessage, offset, IPV6_DEFAULT_HOP_LIMIT); //Free previously allocated memory chunkedBufferFree(icmpMessage); //Return status code return error; }
void ndpProcessNeighborAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { size_t length; NdpNeighborAdvMessage *message; NdpLinkLayerAddrOption *option; NdpCacheEntry *entry; //Retrieve the length of the message length = chunkedBufferGetLength(buffer) - offset; //Check the length of the Neighbor Advertisement message if(length < sizeof(NdpNeighborAdvMessage)) return; //Point to the beginning of the message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("Neighbor Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpNeighborAdvMessage(message); //The IPv6 Hop Limit field must have a value of 255 to ensure //that the packet has not been forwarded by a router if(hopLimit != NDP_HOP_LIMIT) return; //ICMPv6 Code must be 0 if(message->code) return; //Check whether the target address is tentative or matches //a unicast address assigned to the interface if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.linkLocalAddr) && interface->ipv6Config.linkLocalAddrState != IPV6_ADDR_STATE_INVALID) { //Debug message TRACE_WARNING("The address %s is a duplicate!\r\n", ipv6AddrToString(&interface->ipv6Config.linkLocalAddr, NULL)); //The address is a duplicate and should not be used interface->ipv6Config.linkLocalAddrDup = TRUE; //Exit immediately return; } else if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.globalAddr) && interface->ipv6Config.globalAddrState != IPV6_ADDR_STATE_INVALID) { //Debug message TRACE_WARNING("The address %s is a duplicate!\r\n", ipv6AddrToString(&interface->ipv6Config.globalAddr, NULL)); //The address is a duplicate and should not be used interface->ipv6Config.globalAddrDup = TRUE; //Exit immediately return; } //The target address must not be a multicast address if(ipv6IsMulticastAddr(&message->targetAddr)) { //Debug message TRACE_WARNING("Target address must not be a multicast address!\r\n"); //Exit immediately return; } //If the destination address is a multicast address //then the Solicited flag must be zero if(ipv6IsMulticastAddr(&pseudoHeader->destAddr) && message->s) { //Debug message TRACE_WARNING("Solicited flag must be zero!\r\n"); //Exit immediately return; } //Calculate the length of the Options field length -= sizeof(NdpNeighborSolMessage); //Search for the Target Link-Layer Address option option = ndpGetOption(message->options, length, NDP_OPT_TARGET_LINK_LAYER_ADDR); //Source Link-Layer Address option found? if(option && option->length == 1) { //Debug message TRACE_DEBUG(" Target Link-Layer Address = %s\r\n", macAddrToString(&option->linkLayerAddr, NULL)); //Acquire exclusive access to Neighbor cache osAcquireMutex(&interface->ndpCacheMutex); //Search the Neighbor cache for the specified target address entry = ndpFindEntry(interface, &message->targetAddr); //If no entry exists, the advertisement should be silently discarded if(entry) { //INCOMPLETE state? if(entry->state == NDP_STATE_INCOMPLETE) { //Record link-layer address entry->macAddr = option->linkLayerAddr; //Send all the packets that are pending for transmission ndpSendQueuedPackets(interface, entry); //Save current time entry->timestamp = osGetSystemTime(); //Solicited flag is set? if(message->s) { //Computing the random ReachableTime value entry->timeout = NDP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = NDP_STATE_REACHABLE; } //Solicited flag is cleared? else { //Enter the STALE state entry->state = NDP_STATE_STALE; } } //REACHABLE, STALE, DELAY or PROBE state? else { //Solicited flag is set and Override flag is cleared? if(message->s && !message->o) { //Same link-layer address than cached? if(macCompAddr(&entry->macAddr, &option->linkLayerAddr)) { //Save current time entry->timestamp = osGetSystemTime(); //Computing the random ReachableTime value entry->timeout = NDP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = NDP_STATE_REACHABLE; } //Different link-layer address than cached? else { //REACHABLE state? if(entry->state == NDP_STATE_REACHABLE) { //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } } } //Both Solicited and Override flags are set? else if(message->s && message->o) { //Record link-layer address (if different) entry->macAddr = option->linkLayerAddr; //Save current time entry->timestamp = osGetSystemTime(); //Computing the random ReachableTime value entry->timeout = NDP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = NDP_STATE_REACHABLE; } //Solicited flag is cleared and Override flag is set? else if(!message->s && message->o) { //Different link-layer address than cached? if(!macCompAddr(&entry->macAddr, &option->linkLayerAddr)) { //Record link-layer address entry->macAddr = option->linkLayerAddr; //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } } } } } //Source Link-Layer Address option not found? else { //Update content of IsRouter flag } //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); }
void icmpv6ProcessMessage(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { size_t length; Icmpv6Header *header; //Retrieve the length of the ICMPv6 message length = chunkedBufferGetLength(buffer) - offset; //Ensure the message length is correct if(length < sizeof(Icmpv6Header)) { //Debug message TRACE_WARNING("ICMPv6 message length is invalid!\r\n"); //Exit immediately return; } //Point to the ICMPv6 message header header = chunkedBufferAt(buffer, offset); //Sanity check if(!header) return; //Debug message TRACE_INFO("ICMPv6 message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpv6DumpMessage(header); //Verify checksum value if(ipCalcUpperLayerChecksumEx(pseudoHeader, sizeof(Ipv6PseudoHeader), buffer, offset, length) != 0xFFFF) { //Debug message TRACE_WARNING("Wrong ICMPv6 header checksum!\r\n"); //Exit immediately return; } //Check whether the destination address is the tentative address if(ipv6IsTentativeAddr(interface, &pseudoHeader->destAddr)) { //The interface must accept Neighbor Solicitation and //Neighbor Advertisement messages if(header->type != ICMPV6_TYPE_NEIGHBOR_SOL && header->type != ICMPV6_TYPE_NEIGHBOR_ADV) { //Other packets addressed to the tentative address //should be silently discarded return; } } //Check the type of message switch(header->type) { //Echo Request message? case ICMPV6_TYPE_ECHO_REQUEST: //Process Echo Request message icmpv6ProcessEchoRequest(interface, pseudoHeader, buffer, offset); break; #if (MLD_SUPPORT == ENABLED) //Multicast Listener Query message? case ICMPV6_TYPE_MULTICAST_LISTENER_QUERY: //Process Multicast Listener Query message mldProcessListenerQuery(interface, pseudoHeader, buffer, offset, hopLimit); break; //Version 1 Multicast Listener Report message? case ICMPV6_TYPE_MULTICAST_LISTENER_REPORT_V1: //Process Version 1 Multicast Listener Report message mldProcessListenerReport(interface, pseudoHeader, buffer, offset, hopLimit); break; #endif #if (NDP_SUPPORT == ENABLED) //Router Advertisement message? case ICMPV6_TYPE_ROUTER_ADV: //Process Router Advertisement message ndpProcessRouterAdv(interface, pseudoHeader, buffer, offset, hopLimit); break; //Neighbor Solicitation message? case ICMPV6_TYPE_NEIGHBOR_SOL: //Process Neighbor Solicitation message ndpProcessNeighborSol(interface, pseudoHeader, buffer, offset, hopLimit); break; //Neighbor Advertisement message? case ICMPV6_TYPE_NEIGHBOR_ADV: //Process Neighbor Advertisement message ndpProcessNeighborAdv(interface, pseudoHeader, buffer, offset, hopLimit); break; #endif //Unknown type? default: //Debug message TRACE_WARNING("Unknown ICMPv6 message type!\r\n"); //Discard incoming ICMPv6 message break; } }
error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code, uint8_t parameter, const ChunkedBuffer *ipPacket) { error_t error; size_t offset; size_t length; Ipv4Header *ipHeader; ChunkedBuffer *icmpMessage; IcmpErrorMessage *icmpHeader; Ipv4PseudoHeader pseudoHeader; //Retrieve the length of the invoking IPv4 packet length = chunkedBufferGetLength(ipPacket); //Check the length of the IPv4 packet if(length < sizeof(Ipv4Header)) return ERROR_INVALID_LENGTH; //Point to the header of the invoking packet ipHeader = chunkedBufferAt(ipPacket, 0); //Sanity check if(!ipHeader) return ERROR_FAILURE; //Never respond to a packet destined to a broadcast or a multicast address if(ipv4IsBroadcastAddr(interface, ipHeader->destAddr) || ipv4IsMulticastAddr(ipHeader->destAddr)) { //Report an error return ERROR_INVALID_ADDRESS; } //Length of the data that will be returned along with the ICMP header length = MIN(length, ipHeader->headerLength * 4 + 8); //Allocate a memory buffer to hold the ICMP message icmpMessage = ipAllocBuffer(sizeof(IcmpErrorMessage), &offset); //Failed to allocate memory? if(!icmpMessage) return ERROR_OUT_OF_MEMORY; //Point to the ICMP header icmpHeader = chunkedBufferAt(icmpMessage, offset); //Format ICMP message icmpHeader->type = type; icmpHeader->code = code; icmpHeader->checksum = 0; icmpHeader->parameter = parameter; icmpHeader->unused = 0; //Copy the IP header and the first 8 bytes of the original datagram data error = chunkedBufferConcat(icmpMessage, ipPacket, 0, length); //Any error to report? if(error) { //Clean up side effects chunkedBufferFree(icmpMessage); //Exit immediately return error; } //Get the length of the resulting message length = chunkedBufferGetLength(icmpMessage) - offset; //Message checksum calculation icmpHeader->checksum = ipCalcChecksumEx(icmpMessage, offset, length); //Format IPv4 pseudo header pseudoHeader.srcAddr = ipHeader->destAddr; pseudoHeader.destAddr = ipHeader->srcAddr; pseudoHeader.reserved = 0; pseudoHeader.protocol = IPV4_PROTOCOL_ICMP; pseudoHeader.length = htons(length); //Debug message TRACE_INFO("Sending ICMP Error message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpDumpErrorMessage(icmpHeader); //Send ICMP Error message error = ipv4SendDatagram(interface, &pseudoHeader, icmpMessage, offset, IPV4_DEFAULT_TTL); //Free previously allocated memory chunkedBufferFree(icmpMessage); //Return status code return error; }
error_t ndpSendNeighborAdv(NetInterface *interface, const Ipv6Addr *targetIpAddr, const Ipv6Addr *destIpAddr) { error_t error; size_t offset; size_t length; ChunkedBuffer *buffer; NdpNeighborAdvMessage *message; Ipv6PseudoHeader pseudoHeader; //Allocate a memory buffer to hold the Neighbor Advertisement //message and the Target Link-Layer Address option buffer = ipAllocBuffer(sizeof(NdpNeighborAdvMessage) + sizeof(NdpLinkLayerAddrOption), &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the beginning of the message message = chunkedBufferAt(buffer, offset); //Format Neighbor Advertisement message message->type = ICMPV6_TYPE_NEIGHBOR_ADV; message->code = 0; message->checksum = 0; message->reserved1 = 0; message->o = TRUE; message->s = FALSE; message->r = FALSE; message->reserved2 = 0; message->targetAddr = *targetIpAddr; //Length of the message, excluding any option length = sizeof(NdpNeighborAdvMessage); //Add Target Link-Layer Address option ndpAddOption(message, &length, NDP_OPT_TARGET_LINK_LAYER_ADDR, &interface->macAddr, sizeof(MacAddr)); //Adjust the length of the multi-part buffer chunkedBufferSetLength(buffer, offset + length); //Format IPv6 pseudo header pseudoHeader.srcAddr = *targetIpAddr; pseudoHeader.destAddr = *destIpAddr; pseudoHeader.length = htonl(length); pseudoHeader.reserved = 0; pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; //Destination IP address is the unspecified address? if(ipv6CompAddr(destIpAddr, &IPV6_UNSPECIFIED_ADDR)) { //If the destination is the unspecified address, the node //must set the Solicited flag to zero and multicast the //advertisement to the all-nodes address pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_NODES_ADDR; } else { //Otherwise, the node must set the Solicited flag to one and //unicast the advertisement to the destination IP address message->s = TRUE; } //Calculate ICMPv6 header checksum message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, sizeof(Ipv6PseudoHeader), buffer, offset, length); //Debug message TRACE_INFO("Sending Neighbor Advertisement message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpNeighborAdvMessage(message); //Send Neighbor Advertisement message error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); //Free previously allocated memory chunkedBufferFree(buffer); //Return status code return error; }
void rawSocketProcessEthPacket(NetInterface *interface, EthHeader *ethFrame, size_t length) { uint_t i; Socket *socket; SocketQueueItem *queueItem; ChunkedBuffer *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 = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Point to the newly created item queueItem = chunkedBufferAt(p, 0); queueItem->buffer = p; //Add the newly created item to the queue socket->receiveQueue = queueItem; } else { //Memory allocation failed queueItem = NULL; } } else { //Point to the very first item queueItem = socket->receiveQueue; //Reach the last item in the receive queue for(i = 1; queueItem->next; i++) queueItem = queueItem->next; //Make sure the receive queue is not full if(i >= RAW_SOCKET_RX_QUEUE_SIZE) { //Leave critical section osReleaseMutex(&socketMutex); //Return immediately return; } //Allocate a memory buffer to hold the data and the associated descriptor p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Add the newly created item to the queue queueItem->next = chunkedBufferAt(p, 0); //Point to the newly created item queueItem = queueItem->next; queueItem->buffer = p; } else { //Memory allocation failed queueItem = NULL; } } //Failed to allocate memory? if(!queueItem) { //Leave critical section osReleaseMutex(&socketMutex); //Return 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 chunkedBufferWrite(queueItem->buffer, queueItem->offset, ethFrame, length); //Notify user that data is available rawSocketUpdateEvents(socket); //Leave critical section osReleaseMutex(&socketMutex); }
void ndpProcessNeighborSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { size_t length; NdpNeighborSolMessage *message; NdpLinkLayerAddrOption *option; NdpCacheEntry *entry; //Retrieve the length of the message length = chunkedBufferGetLength(buffer) - offset; //Check the length of the Neighbor Solicitation message if(length < sizeof(NdpNeighborSolMessage)) return; //Point to the beginning of the message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("Neighbor Solicitation message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpNeighborSolMessage(message); //The IPv6 Hop Limit field must have a value of 255 to ensure //that the packet has not been forwarded by a router if(hopLimit != NDP_HOP_LIMIT) return; //ICMPv6 Code must be 0 if(message->code) return; //The target address must a valid unicast address assigned to the interface //or a tentative address on which DAD is being performed if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.linkLocalAddr)) { //Check whether the target address is tentative if(interface->ipv6Config.linkLocalAddrState == IPV6_ADDR_STATE_TENTATIVE) { //If the source address of the Neighbor Solicitation is the unspecified //address, the solicitation is from a node performing Duplicate Address //Detection if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) { //Debug message TRACE_WARNING("The tentative address %s is a duplicate!\r\n", ipv6AddrToString(&interface->ipv6Config.linkLocalAddr, NULL)); //The tentative address is a duplicate and should not be used interface->ipv6Config.linkLocalAddrDup = TRUE; } //In all cases, a node must not respond to a Neighbor Solicitation //for a tentative address return; } } else if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.globalAddr)) { //Check whether the target address is tentative if(interface->ipv6Config.globalAddrState == IPV6_ADDR_STATE_TENTATIVE) { //If the source address of the Neighbor Solicitation is the unspecified //address, the solicitation is from a node performing Duplicate Address //Detection if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) { //Debug message TRACE_WARNING("The tentative address %s is a duplicate!\r\n", ipv6AddrToString(&interface->ipv6Config.globalAddr, NULL)); //The tentative address is a duplicate and should not be used interface->ipv6Config.globalAddrDup = TRUE; } //In all cases, a node must not respond to a Neighbor Solicitation //for a tentative address return; } } else { //Debug message TRACE_WARNING("Wrong target address!\r\n"); //Exit immediately return; } //If the IP source address is the unspecified address, the IP //destination address must be a solicited-node multicast address if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR) && !ipv6IsSolicitedNodeAddr(&pseudoHeader->destAddr)) { //Debug message TRACE_WARNING("Destination address must be a solicited-node address!\r\n"); //Exit immediately return; } //Calculate the length of the Options field length -= sizeof(NdpNeighborSolMessage); //Search for the Source Link-Layer Address option option = ndpGetOption(message->options, length, NDP_OPT_SOURCE_LINK_LAYER_ADDR); //Source Link-Layer Address option found? if(option && option->length == 1) { //Debug message TRACE_DEBUG(" Source Link-Layer Address = %s\r\n", macAddrToString(&option->linkLayerAddr, NULL)); //If the Source Address is not the unspecified address, then the Neighbor //cache should be updated for the IP source address of the solicitation if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) return; //Acquire exclusive access to Neighbor cache osAcquireMutex(&interface->ndpCacheMutex); //Search the Neighbor cache for the source address of the solicitation entry = ndpFindEntry(interface, &pseudoHeader->srcAddr); //No matching entry has been found? if(!entry) { //Create an entry entry = ndpCreateEntry(interface); //Neighbor cache entry successfully created? if(entry) { //Record the IPv6 and the corresponding MAC address entry->ipAddr = pseudoHeader->srcAddr; entry->macAddr = option->linkLayerAddr; //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } } else { //INCOMPLETE state? if(entry->state == NDP_STATE_INCOMPLETE) { //Record link-layer address entry->macAddr = option->linkLayerAddr; //Send all the packets that are pending for transmission ndpSendQueuedPackets(interface, entry); //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } //REACHABLE, STALE, DELAY or PROBE state? else { //Different link-layer address than cached? if(!macCompAddr(&entry->macAddr, &option->linkLayerAddr)) { //Update link-layer address entry->macAddr = option->linkLayerAddr; //Save current time entry->timestamp = osGetSystemTime(); //Enter the STALE state entry->state = NDP_STATE_STALE; } } } //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); } //Source Link-Layer Address option not found? else { //This option must be included in multicast solicitations if(ipv6IsMulticastAddr(&pseudoHeader->destAddr)) { //Debug message TRACE_WARNING("The Source Link-Layer Address must be included!\r\n"); //Exit immediately return; } } //After any updates to the Neighbor cache, the node sends a Neighbor //Advertisement response as described in RFC 4861 7.2.4 ndpSendNeighborAdv(interface, &message->targetAddr, &pseudoHeader->srcAddr); }
error_t nbnsSendResponse(NetInterface *interface, const IpAddr *destIpAddr, uint16_t destPort, uint16_t id) { error_t error; size_t length; size_t offset; ChunkedBuffer *buffer; NbnsHeader *message; NbnsAddrEntry *addrEntry; DnsResourceRecord *resourceRecord; //Allocate a memory buffer to hold the NBNS response 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 = chunkedBufferAt(buffer, offset); //Take the identifier from the query message message->id = id; //Format NBNS response header message->qr = 1; message->opcode = DNS_OPCODE_QUERY; message->aa = 1; message->tc = 0; message->rd = 1; message->ra = 1; message->z = 0; message->b = 0; message->rcode = DNS_RCODE_NO_ERROR; //The NBNS response contains 1 answer resource record message->qdcount = 0; message->ancount = HTONS(1); message->nscount = 0; message->arcount = 0; //NBNS response message length length = sizeof(DnsHeader); //Encode the host name using the NBNS name notation length += nbnsEncodeName(interface->hostname, (uint8_t *) message + length); //Point to the corresponding resource record resourceRecord = DNS_GET_RESOURCE_RECORD(message, length); //Fill in resource record resourceRecord->rtype = HTONS(DNS_RR_TYPE_NB); resourceRecord->rclass = HTONS(DNS_RR_CLASS_IN); resourceRecord->ttl = HTONL(NBNS_DEFAULT_RESOURCE_RECORD_TTL); resourceRecord->rdlength = HTONS(sizeof(NbnsAddrEntry)); //Point to the address entry array addrEntry = (NbnsAddrEntry *) resourceRecord->rdata; //Fill in address entry addrEntry->flags = HTONS(NBNS_G_UNIQUE | NBNS_ONT_BNODE); addrEntry->addr = interface->ipv4Config.addr; //Update the length of the NBNS response message length += sizeof(DnsResourceRecord) + sizeof(NbnsAddrEntry); //Adjust the length of the multi-part buffer chunkedBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending NBNS message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage((DnsHeader *) message, length); //A response packet is always sent to the source UDP port and //source IP address of the request packet error = udpSendDatagramEx(interface, NBNS_PORT, destIpAddr, destPort, buffer, offset, IPV4_DEFAULT_TTL); //Free previously allocated memory chunkedBufferFree(buffer); //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 udpSendDatagramEx(NetInterface *interface, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, ChunkedBuffer *buffer, size_t offset, uint8_t ttl) { error_t error; size_t length; UdpHeader *header; IpPseudoHeader pseudoHeader; //Make room for the UDP header offset -= sizeof(UdpHeader); //Retrieve the length of the datagram length = chunkedBufferGetLength(buffer) - offset; //Point to the UDP header header = chunkedBufferAt(buffer, offset); //Sanity check if(!header) return ERROR_FAILURE; //Format UDP header header->srcPort = htons(srcPort); header->destPort = htons(destPort); header->length = htons(length); header->checksum = 0; #if (IPV4_SUPPORT == ENABLED) //Destination address is an IPv4 address? if(destIpAddr->length == sizeof(Ipv4Addr)) { Ipv4Addr srcIpAddr; //Select the source IPv4 address and the relevant network interface //to use when sending data to the specified destination host error = ipv4SelectSourceAddr(&interface, destIpAddr->ipv4Addr, &srcIpAddr); //Any error to report? if(error) return error; //Format IPv4 pseudo header pseudoHeader.length = sizeof(Ipv4PseudoHeader); pseudoHeader.ipv4Data.srcAddr = srcIpAddr; pseudoHeader.ipv4Data.destAddr = destIpAddr->ipv4Addr; pseudoHeader.ipv4Data.reserved = 0; pseudoHeader.ipv4Data.protocol = IPV4_PROTOCOL_UDP; pseudoHeader.ipv4Data.length = htons(length); //Calculate UDP header checksum header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv4Data, sizeof(Ipv4PseudoHeader), buffer, offset, length); } else #endif #if (IPV6_SUPPORT == ENABLED) //Destination address is an IPv6 address? if(destIpAddr->length == sizeof(Ipv6Addr)) { //Select the source IPv6 address and the relevant network interface //to use when sending data to the specified destination host error = ipv6SelectSourceAddr(&interface, &destIpAddr->ipv6Addr, &pseudoHeader.ipv6Data.srcAddr); //Any error to report? if(error) return error; //Format IPv6 pseudo header pseudoHeader.length = sizeof(Ipv6PseudoHeader); pseudoHeader.ipv6Data.destAddr = destIpAddr->ipv6Addr; pseudoHeader.ipv6Data.length = htonl(length); pseudoHeader.ipv6Data.reserved = 0; pseudoHeader.ipv6Data.nextHeader = IPV6_UDP_HEADER; //Calculate UDP header checksum header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv6Data, sizeof(Ipv6PseudoHeader), buffer, offset, length); } else #endif //Invalid destination address? { //An internal error has occurred return ERROR_FAILURE; } //Debug message TRACE_INFO("Sending UDP datagram (%" PRIuSIZE " bytes)\r\n", length); //Dump UDP header contents for debugging purpose udpDumpHeader(header); //Send UDP datagram return ipSendDatagram(interface, &pseudoHeader, buffer, offset, ttl); }
error_t udpProcessDatagram(NetInterface *interface, IpPseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset) { error_t error; uint_t i; size_t length; UdpHeader *header; Socket *socket; SocketQueueItem *queueItem; ChunkedBuffer *p; //Retrieve the length of the UDP datagram length = chunkedBufferGetLength(buffer) - offset; //Ensure the UDP header is valid if(length < sizeof(UdpHeader)) { //Debug message TRACE_WARNING("UDP datagram length is invalid!\r\n"); //Report an error return ERROR_INVALID_HEADER; } //Point to the UDP header header = chunkedBufferAt(buffer, offset); //Sanity check if(!header) return ERROR_FAILURE; //Debug message TRACE_INFO("UDP datagram received (%" PRIuSIZE " bytes)...\r\n", length); //Dump UDP header contents for debugging purpose udpDumpHeader(header); //When UDP runs over IPv6, the checksum is mandatory if(header->checksum || pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Verify UDP checksum if(ipCalcUpperLayerChecksumEx(pseudoHeader->data, pseudoHeader->length, buffer, offset, length) != 0xFFFF) { //Debug message TRACE_WARNING("Wrong UDP header checksum!\r\n"); //Report an error return ERROR_WRONG_CHECKSUM; } } //Enter critical section osMutexAcquire(socketMutex); //Loop through opened sockets for(i = 0; i < SOCKET_MAX_COUNT; i++) { //Point to the current socket socket = socketTable + i; //UDP socket found? if(socket->type != SOCKET_TYPE_DGRAM) continue; //Check whether the socket is bound to a particular interface if(socket->interface && socket->interface != interface) continue; //Check destination port number if(socket->localPort != ntohs(header->destPort)) continue; //Source port number filtering if(socket->remotePort && socket->remotePort != ntohs(header->srcPort)) continue; #if (IPV4_SUPPORT == ENABLED) //An IPv4 packet was received? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Destination IP address filtering if(socket->localIpAddr.length) { //An IPv4 address is expected if(socket->localIpAddr.length != sizeof(Ipv4Addr)) continue; //Filter out non-matching addresses if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr) continue; } //Source IP address filtering if(socket->remoteIpAddr.length) { //An IPv4 address is expected if(socket->remoteIpAddr.length != sizeof(Ipv4Addr)) continue; //Filter out non-matching addresses if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr) continue; } } else #endif #if (IPV6_SUPPORT == ENABLED) //An IPv6 packet was received? if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Destination IP address filtering if(socket->localIpAddr.length) { //An IPv6 address is expected if(socket->localIpAddr.length != sizeof(Ipv6Addr)) continue; //Filter out non-matching addresses if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr)) continue; } //Source IP address filtering if(socket->remoteIpAddr.length) { //An IPv6 address is expected if(socket->remoteIpAddr.length != sizeof(Ipv6Addr)) continue; //Filter out non-matching addresses if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr)) continue; } } else #endif //An invalid packet was received? { //This should never occur... continue; } //The current socket meets all the criteria break; } //Point to the payload offset += sizeof(UdpHeader); length -= sizeof(UdpHeader); //No matching socket found? if(i >= SOCKET_MAX_COUNT) { //Leave critical section osMutexRelease(socketMutex); //Invoke user callback, if any error = udpInvokeRxCallback(interface, pseudoHeader, header, buffer, offset); //Return status code return error; } //Empty receive queue? if(!socket->receiveQueue) { //Allocate a memory buffer to hold the data and the associated descriptor p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Point to the newly created item queueItem = chunkedBufferAt(p, 0); queueItem->buffer = p; //Add the newly created item to the queue socket->receiveQueue = queueItem; } else { //Memory allocation failed queueItem = NULL; } } else { //Point to the very first item queueItem = socket->receiveQueue; //Reach the last item in the receive queue for(i = 1; queueItem->next; i++) queueItem = queueItem->next; //Make sure the receive queue is not full if(i >= UDP_RX_QUEUE_SIZE) { //Leave critical section osMutexRelease(socketMutex); //Notify the calling function that the queue is full return ERROR_RECEIVE_QUEUE_FULL; } //Allocate a memory buffer to hold the data and the associated descriptor p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Add the newly created item to the queue queueItem->next = chunkedBufferAt(p, 0); //Point to the newly created item queueItem = queueItem->next; queueItem->buffer = p; } else { //Memory allocation failed queueItem = NULL; } } //Failed to allocate memory? if(!queueItem) { //Leave critical section osMutexRelease(socketMutex); //Return error code return ERROR_OUT_OF_MEMORY; } //Initialize next field queueItem->next = NULL; //Record the source port number queueItem->srcPort = ntohs(header->srcPort); #if (IPV4_SUPPORT == ENABLED) //IPv4 remote address? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Save the source IPv4 address queueItem->srcIpAddr.length = sizeof(Ipv4Addr); queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr; //Save the destination IPv4 address queueItem->destIpAddr.length = sizeof(Ipv4Addr); queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr; } #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 remote address? if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Save the source IPv6 address queueItem->srcIpAddr.length = sizeof(Ipv6Addr); queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr; //Save the destination IPv6 address queueItem->destIpAddr.length = sizeof(Ipv6Addr); queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr; } #endif //Offset to the payload queueItem->offset = sizeof(SocketQueueItem); //Copy the payload chunkedBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length); //Notify user that data is available udpUpdateEvents(socket); //Leave critical section osMutexRelease(socketMutex); //Successful processing return NO_ERROR; }
void ipv4ReassembleDatagram(NetInterface *interface, const MacAddr *srcMacAddr, 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 chunkedBufferSetLength((ChunkedBuffer *) &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 chunkedBufferSetLength((ChunkedBuffer *) &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 chunkedBufferWrite((ChunkedBuffer *) &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 chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return; } //Adjust the size of the reconstructed datagram error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, frag->headerLength + dataLast + sizeof(Ipv4HoleDesc)); //Any error to report? if(error) { //Drop the reconstructed datagram chunkedBufferSetLength((ChunkedBuffer *) &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 chunkedBufferWrite((ChunkedBuffer *) &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 = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, frag->headerLength + frag->dataLength); //Check status code if(!error) { //Point to the IP header Ipv4Header *datagram = chunkedBufferAt((ChunkedBuffer *) &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, srcMacAddr, (ChunkedBuffer *) &frag->buffer); } //Release previously allocated memory chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); } }