ChunkedBuffer *chunkedBufferAlloc(size_t length) { error_t error; ChunkedBuffer *buffer; //Allocate memory to hold the multi-part buffer buffer = memPoolAlloc(MEM_POOL_BUFFER_SIZE); //Failed to allocate memory? if(!buffer) return NULL; //The multi-part buffer consists of a single chunk buffer->chunkCount = 1; buffer->maxChunkCount = MAX_CHUNK_COUNT; buffer->chunk[0].address = (uint8_t *) buffer + MAX_CHUNK_COUNT * sizeof(ChunkDesc); buffer->chunk[0].length = MEM_POOL_BUFFER_SIZE - MAX_CHUNK_COUNT * sizeof(ChunkDesc); buffer->chunk[0].size = 0; //Adjust the length of the buffer error = chunkedBufferSetLength(buffer, length); //Any error to report? if(error) { //Clean up side effects chunkedBufferFree(buffer); //Report an failure return NULL; } //Successful memory allocation return buffer; }
error_t udpReceiveDatagram(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort, IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags) { SocketQueueItem *queueItem; //The receive queue is empty? if(!socket->receiveQueue) { //Set the events the application is interested in socket->eventMask = SOCKET_EVENT_RX_READY; //Reset the event object osEventReset(socket->event); //Leave critical section osMutexRelease(socketMutex); //Wait until an event is triggered osEventWait(socket->event, socket->timeout); //Enter critical section osMutexAcquire(socketMutex); } //Check whether the read operation timed out if(!socket->receiveQueue) { //No data can be read *received = 0; //Report a timeout error return ERROR_TIMEOUT; } //Point to the first item in the receive queue queueItem = socket->receiveQueue; //Copy data to user buffer *received = chunkedBufferRead(data, queueItem->buffer, queueItem->offset, size); //Save the source IP address if(srcIpAddr) *srcIpAddr = queueItem->srcIpAddr; //Save the source port number if(srcPort) *srcPort = queueItem->srcPort; //Save the destination IP address if(destIpAddr) *destIpAddr = queueItem->destIpAddr; //If the SOCKET_FLAG_PEEK flag is set, the data is copied //into the buffer but is not removed from the input queue if(!(flags & SOCKET_FLAG_PEEK)) { //Remove the item from the receive queue socket->receiveQueue = queueItem->next; //Deallocate memory buffer chunkedBufferFree(queueItem->buffer); } //Update the state of events udpUpdateEvents(socket); //Successful read operation return NO_ERROR; }
error_t ipv4FragmentDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, uint16_t id, const ChunkedBuffer *payload, size_t payloadOffset, uint8_t timeToLive) { error_t error; size_t offset; size_t length; size_t payloadLength; size_t fragmentOffset; ChunkedBuffer *fragment; //Retrieve the length of the payload payloadLength = chunkedBufferGetLength(payload) - payloadOffset; //Allocate a memory buffer to hold IP fragments fragment = ipAllocBuffer(0, &fragmentOffset); //Failed to allocate memory? if(!fragment) return ERROR_OUT_OF_MEMORY; //Split the payload into multiple IP fragments for(offset = 0; offset < payloadLength; offset += length) { //Flush the contents of the fragment error = chunkedBufferSetLength(fragment, fragmentOffset); //Sanity check if(error) break; //Process the last fragment? if((payloadLength - offset) <= IPV4_MAX_FRAG_SIZE) { //Size of the current fragment length = payloadLength - offset; //Copy fragment data chunkedBufferConcat(fragment, payload, payloadOffset + offset, length); //Do not set the MF flag for the last fragment error = ipv4SendPacket(interface, pseudoHeader, id, offset / 8, fragment, fragmentOffset, timeToLive); } else { //Size of the current fragment (must be a multiple of 8-byte blocks) length = IPV4_MAX_FRAG_SIZE; //Copy fragment data chunkedBufferConcat(fragment, payload, payloadOffset + offset, length); //Fragmented packets must have the MF flag set error = ipv4SendPacket(interface, pseudoHeader, id, IPV4_FLAG_MF | (offset / 8), fragment, fragmentOffset, timeToLive); } //Failed to send current IP packet? if(error) break; } //Free previously allocated memory chunkedBufferFree(fragment); //Return status code return error; }
error_t 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; }
error_t rawSocketReceiveEthPacket(Socket *socket, void *data, size_t size, size_t *received, uint_t flags) { SocketQueueItem *queueItem; //The receive queue is empty? if(!socket->receiveQueue) { //Set the events the application is interested in socket->eventMask = SOCKET_EVENT_RX_READY; //Reset the event object osResetEvent(&socket->event); //Leave critical section osReleaseMutex(&socketMutex); //Wait until an event is triggered osWaitForEvent(&socket->event, socket->timeout); //Enter critical section osAcquireMutex(&socketMutex); } //Check whether the read operation timed out if(!socket->receiveQueue) { //No data can be read *received = 0; //Report a timeout error return ERROR_TIMEOUT; } //Point to the first item in the receive queue queueItem = socket->receiveQueue; //Copy data to user buffer *received = chunkedBufferRead(data, queueItem->buffer, queueItem->offset, size); //If the SOCKET_FLAG_PEEK flag is set, the data is copied //into the buffer but is not removed from the input queue if(!(flags & SOCKET_FLAG_PEEK)) { //Remove the item from the receive queue socket->receiveQueue = queueItem->next; //Deallocate memory buffer chunkedBufferFree(queueItem->buffer); } //Update the state of events rawSocketUpdateEvents(socket); //Successful read operation return NO_ERROR; }
void ndpFlushQueuedPackets(NetInterface *interface, NdpCacheEntry *entry) { uint_t i; //Check current state if(entry->state == NDP_STATE_INCOMPLETE) { //Drop packets that are waiting for address resolution for(i = 0; i < entry->queueSize; i++) chunkedBufferFree(entry->queue[i].buffer); } //The queue is now empty entry->queueSize = 0; }
void ndpSendQueuedPackets(NetInterface *interface, NdpCacheEntry *entry) { uint_t i; //Check current state if(entry->state == NDP_STATE_INCOMPLETE) { //Loop through queued packets for(i = 0; i < entry->queueSize; i++) { //Send current packet ethSendFrame(interface, &entry->macAddr, entry->queue[i].buffer, entry->queue[i].offset, ETH_TYPE_IPV6); //Release previously allocated memory chunkedBufferFree(entry->queue[i].buffer); } } //The queue is now empty entry->queueSize = 0; }
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; }
error_t udpSendDatagram(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort, const void *data, size_t length, size_t *written) { error_t error; size_t offset; ChunkedBuffer *buffer; //Allocate a memory buffer to hold the UDP datagram buffer = udpAllocBuffer(0, &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Copy data payload error = chunkedBufferAppend(buffer, data, length); //Successful processing? if(!error) { //Send UDP datagram error = udpSendDatagramEx(socket->interface, socket->localPort, destIpAddr, destPort, buffer, offset, socket->ttl); } //Successful processing? if(!error) { //Total number of data bytes successfully transmitted if(written != NULL) *written = length; } //Free previously allocated memory chunkedBufferFree(buffer); //Return status code return error; }
error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code, uint8_t parameter, const ChunkedBuffer *ipPacket) { error_t error; size_t offset; size_t length; Ipv4Header *ipHeader; ChunkedBuffer *icmpMessage; IcmpErrorMessage *icmpHeader; Ipv4PseudoHeader pseudoHeader; //Retrieve the length of the invoking IPv4 packet length = chunkedBufferGetLength(ipPacket); //Check the length of the IPv4 packet if(length < sizeof(Ipv4Header)) return ERROR_INVALID_LENGTH; //Point to the header of the invoking packet ipHeader = chunkedBufferAt(ipPacket, 0); //Sanity check if(!ipHeader) return ERROR_FAILURE; //Never respond to a packet destined to a broadcast or a multicast address if(ipv4IsBroadcastAddr(interface, ipHeader->destAddr) || ipv4IsMulticastAddr(ipHeader->destAddr)) { //Report an error return ERROR_INVALID_ADDRESS; } //Length of the data that will be returned along with the ICMP header length = MIN(length, ipHeader->headerLength * 4 + 8); //Allocate a memory buffer to hold the ICMP message icmpMessage = ipAllocBuffer(sizeof(IcmpErrorMessage), &offset); //Failed to allocate memory? if(!icmpMessage) return ERROR_OUT_OF_MEMORY; //Point to the ICMP header icmpHeader = chunkedBufferAt(icmpMessage, offset); //Format ICMP message icmpHeader->type = type; icmpHeader->code = code; icmpHeader->checksum = 0; icmpHeader->parameter = parameter; icmpHeader->unused = 0; //Copy the IP header and the first 8 bytes of the original datagram data error = chunkedBufferConcat(icmpMessage, ipPacket, 0, length); //Any error to report? if(error) { //Clean up side effects chunkedBufferFree(icmpMessage); //Exit immediately return error; } //Get the length of the resulting message length = chunkedBufferGetLength(icmpMessage) - offset; //Message checksum calculation icmpHeader->checksum = ipCalcChecksumEx(icmpMessage, offset, length); //Format IPv4 pseudo header pseudoHeader.srcAddr = ipHeader->destAddr; pseudoHeader.destAddr = ipHeader->srcAddr; pseudoHeader.reserved = 0; pseudoHeader.protocol = IPV4_PROTOCOL_ICMP; pseudoHeader.length = htons(length); //Debug message TRACE_INFO("Sending ICMP Error message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpDumpErrorMessage(icmpHeader); //Send ICMP Error message error = ipv4SendDatagram(interface, &pseudoHeader, icmpMessage, offset, IPV4_DEFAULT_TTL); //Free previously allocated memory chunkedBufferFree(icmpMessage); //Return status code return error; }
void icmpProcessEchoRequest(NetInterface *interface, Ipv4Addr srcIpAddr, const ChunkedBuffer *request, size_t requestOffset) { error_t error; size_t requestLength; size_t replyOffset; size_t replyLength; ChunkedBuffer *reply; IcmpEchoMessage *requestHeader; IcmpEchoMessage *replyHeader; Ipv4PseudoHeader pseudoHeader; //Retrieve the length of the Echo Request message requestLength = chunkedBufferGetLength(request) - requestOffset; //Ensure the packet length is correct if(requestLength < sizeof(IcmpEchoMessage)) return; //Point to the Echo Request header requestHeader = chunkedBufferAt(request, requestOffset); //Sanity check if(!requestHeader) return; //Debug message TRACE_INFO("ICMP Echo Request message received (%" PRIuSIZE " bytes)...\r\n", requestLength); //Dump message contents for debugging purpose icmpDumpEchoMessage(requestHeader); //Allocate memory to hold the Echo Reply message reply = ipAllocBuffer(sizeof(IcmpEchoMessage), &replyOffset); //Failed to allocate memory? if(!reply) return; //Point to the Echo Reply header replyHeader = chunkedBufferAt(reply, replyOffset); //Format Echo Reply header replyHeader->type = ICMP_TYPE_ECHO_REPLY; replyHeader->code = 0; replyHeader->checksum = 0; replyHeader->identifier = requestHeader->identifier; replyHeader->sequenceNumber = requestHeader->sequenceNumber; //Point to the first data byte requestOffset += sizeof(IcmpEchoMessage); requestLength -= sizeof(IcmpEchoMessage); //Copy data error = chunkedBufferConcat(reply, request, requestOffset, requestLength); //Any error to report? if(error) { //Clean up side effects chunkedBufferFree(reply); //Exit immediately return; } //Get the length of the resulting message replyLength = chunkedBufferGetLength(reply) - replyOffset; //Calculate ICMP header checksum replyHeader->checksum = ipCalcChecksumEx(reply, replyOffset, replyLength); //Format IPv4 pseudo header pseudoHeader.srcAddr = interface->ipv4Config.addr; pseudoHeader.destAddr = srcIpAddr; pseudoHeader.reserved = 0; pseudoHeader.protocol = IPV4_PROTOCOL_ICMP; pseudoHeader.length = htons(replyLength); //Debug message TRACE_INFO("Sending ICMP Echo Reply message (%" PRIuSIZE " bytes)...\r\n", replyLength); //Dump message contents for debugging purpose icmpDumpEchoMessage(replyHeader); //Send Echo Reply message ipv4SendDatagram(interface, &pseudoHeader, reply, replyOffset, IPV4_DEFAULT_TTL); //Free previously allocated memory block chunkedBufferFree(reply); }
error_t ndpEnqueuePacket(NetInterface *interface, const Ipv6Addr *ipAddr, ChunkedBuffer *buffer, size_t offset) { error_t error; uint_t i; size_t length; NdpCacheEntry *entry; //Retrieve the length of the multi-part buffer length = chunkedBufferGetLength(buffer); //Acquire exclusive access to Neighbor cache osAcquireMutex(&interface->ndpCacheMutex); //Search the Neighbor cache for the specified Ipv6 address entry = ndpFindEntry(interface, ipAddr); //No matching entry in Neighbor cache? if(!entry) { //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //Report an error to the calling function return ERROR_FAILURE; } //Check current state if(entry->state == NDP_STATE_INCOMPLETE) { //Check whether the packet queue is full if(entry->queueSize >= NDP_MAX_PENDING_PACKETS) { //When the queue overflows, the new arrival should replace the oldest entry chunkedBufferFree(entry->queue[0].buffer); //Make room for the new packet for(i = 1; i < NDP_MAX_PENDING_PACKETS; i++) entry->queue[i - 1] = entry->queue[i]; //Adjust the number of pending packets entry->queueSize--; } //Index of the entry to be filled in i = entry->queueSize; //Allocate a memory buffer to store the packet entry->queue[i].buffer = chunkedBufferAlloc(length); //Failed to allocate memory? if(!entry->queue[i].buffer) { //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //Report an error to the calling function return ERROR_OUT_OF_MEMORY; } //Copy packet contents chunkedBufferCopy(entry->queue[i].buffer, 0, buffer, 0, length); //Offset to the first byte of the IPv6 header entry->queue[i].offset = offset; //Increment the number of queued packets entry->queueSize++; //The packet was successfully enqueued error = NO_ERROR; } else { //Send immediately the packet since the address is already resolved error = ethSendFrame(interface, &entry->macAddr, buffer, offset, ETH_TYPE_IPV6); } //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //Return status code return error; }
error_t rawSocketSendEthPacket(Socket *socket, const void *data, size_t length, size_t *written) { error_t error; uint32_t crc; ChunkedBuffer *buffer; NetInterface *interface; //Select the relevant network interface if(!socket->interface) interface = tcpIpStackGetDefaultInterface(); else interface = socket->interface; //Allocate a buffer memory to hold the raw Ethernet packet buffer = chunkedBufferAlloc(0); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Copy the raw data error = chunkedBufferAppend(buffer, data, length); //Successful processing? if(!error) { //Automatic padding not supported by hardware? if(!interface->nicDriver->autoPadding) { //The host controller should manually add padding //to the packet before transmitting it if(length < (ETH_MIN_FRAME_SIZE - ETH_CRC_SIZE)) { //Add padding as necessary size_t n = (ETH_MIN_FRAME_SIZE - ETH_CRC_SIZE) - length; //Append padding bytes error = chunkedBufferAppend(buffer, ethPadding, n); //Any error to report? if(error) return error; //Adjust frame length length += n; } } //CRC generation not supported by hardware? if(!interface->nicDriver->autoCrcGen) { //Compute CRC over the header and payload crc = ethCalcCrcEx(buffer, 0, length); //Convert from host byte order to little-endian byte order crc = htole32(crc); //Append the calculated CRC value error = chunkedBufferAppend(buffer, &crc, sizeof(crc)); //Any error to report? if(error) return error; //Adjust frame length length += sizeof(crc); } //Debug message TRACE_DEBUG("Sending raw Ethernet frame (%" PRIuSIZE " bytes)...\r\n", length); //Send the resulting packet over the specified link error = nicSendPacket(interface, buffer, 0); } //Successful processing? if(!error) { //Total number of bytes successfully transmitted if(written != NULL) *written = length; } //Free previously allocated memory block chunkedBufferFree(buffer); //Return status code return error; }
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 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; }
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 icmpv6SendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code, uint32_t parameter, const ChunkedBuffer *ipPacket) { error_t error; size_t offset; size_t length; Ipv6Header *ipHeader; ChunkedBuffer *icmpMessage; Icmpv6ErrorMessage *icmpHeader; Ipv6PseudoHeader pseudoHeader; //Retrieve the length of the invoking IPv6 packet length = chunkedBufferGetLength(ipPacket); //Check the length of the IPv6 packet if(length < sizeof(Ipv6Header)) return ERROR_INVALID_LENGTH; //Point to the header of the invoking packet ipHeader = chunkedBufferAt(ipPacket, 0); //Sanity check if(!ipHeader) return ERROR_FAILURE; //Never respond to a packet destined to an IPv6 multicast address if(ipv6IsMulticastAddr(&ipHeader->destAddr)) return ERROR_INVALID_ADDRESS; //Return as much of invoking IPv6 packet as possible without //the ICMPv6 packet exceeding the minimum IPv6 MTU length = MIN(length, IPV6_DEFAULT_MTU - sizeof(Ipv6Header) - sizeof(Icmpv6ErrorMessage)); //Allocate a memory buffer to hold the ICMPv6 message icmpMessage = ipAllocBuffer(sizeof(Icmpv6ErrorMessage), &offset); //Failed to allocate memory? if(!icmpMessage) return ERROR_OUT_OF_MEMORY; //Point to the ICMPv6 header icmpHeader = chunkedBufferAt(icmpMessage, offset); //Format ICMPv6 Error message icmpHeader->type = type; icmpHeader->code = code; icmpHeader->checksum = 0; icmpHeader->parameter = htonl(parameter); //Copy incoming IPv6 packet contents error = chunkedBufferConcat(icmpMessage, ipPacket, 0, length); //Any error to report? if(error) { //Clean up side effects chunkedBufferFree(icmpMessage); //Exit immediately return error; } //Get the length of the resulting message length = chunkedBufferGetLength(icmpMessage) - offset; //Format IPv6 pseudo header pseudoHeader.srcAddr = ipHeader->destAddr; pseudoHeader.destAddr = ipHeader->srcAddr; pseudoHeader.length = htonl(length); pseudoHeader.reserved = 0; pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; //Message checksum calculation icmpHeader->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, sizeof(Ipv6PseudoHeader), icmpMessage, offset, length); //Debug message TRACE_INFO("Sending ICMPv6 Error message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose icmpv6DumpErrorMessage(icmpHeader); //Send ICMPv6 Error message error = ipv6SendDatagram(interface, &pseudoHeader, icmpMessage, offset, IPV6_DEFAULT_HOP_LIMIT); //Free previously allocated memory chunkedBufferFree(icmpMessage); //Return status code return error; }
error_t ipv6FragmentDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *payload, size_t payloadOffset, uint8_t hopLimit) { error_t error; uint32_t id; size_t offset; size_t length; size_t payloadLength; size_t fragmentOffset; ChunkedBuffer *fragment; //Identification field is used to identify fragments of an original IP datagram id = osAtomicInc32(&interface->ipv6Identification); //Retrieve the length of the payload payloadLength = chunkedBufferGetLength(payload) - payloadOffset; //Allocate a memory buffer to hold IP fragments fragment = ipAllocBuffer(0, &fragmentOffset); //Failed to allocate memory? if(!fragment) return ERROR_OUT_OF_MEMORY; //Split the payload into multiple IP fragments for(offset = 0; offset < payloadLength; offset += length) { //Flush the contents of the fragment error = chunkedBufferSetLength(fragment, fragmentOffset); //Sanity check if(error) break; //Process the last fragment? if((payloadLength - offset) <= IPV6_MAX_FRAG_SIZE) { //Size of the current fragment length = payloadLength - offset; //Copy fragment data chunkedBufferConcat(fragment, payload, payloadOffset + offset, length); //Do not set the MF flag for the last fragment error = ipv6SendPacket(interface, pseudoHeader, id, offset, fragment, fragmentOffset, hopLimit); } else { //Size of the current fragment (must be a multiple of 8-byte blocks) length = IPV6_MAX_FRAG_SIZE; //Copy fragment data chunkedBufferConcat(fragment, payload, payloadOffset + offset, length); //Fragmented packets must have the M flag set error = ipv6SendPacket(interface, pseudoHeader, id, offset | IPV6_FLAG_M, fragment, fragmentOffset, hopLimit); } //Failed to send current IP fragment? if(error) break; } //Free previously allocated memory chunkedBufferFree(fragment); //Return status code return error; }
error_t rawSocketSendIpPacket(Socket *socket, const IpAddr *destIpAddr, const void *data, size_t length, size_t *written) { error_t error; size_t offset; uint_t timeToLive; ChunkedBuffer *buffer; NetInterface *interface; IpPseudoHeader pseudoHeader; //The socket may be bound to a particular network interface interface = socket->interface; //Allocate a buffer memory to hold the raw IP datagram buffer = ipAllocBuffer(0, &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Start of exception handling block do { //Copy the raw data error = chunkedBufferAppend(buffer, data, length); //Any error to report? if(error) break; #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) break; //Format IPv4 pseudo header pseudoHeader.length = sizeof(Ipv4PseudoHeader); pseudoHeader.ipv4Data.srcAddr = srcIpAddr; pseudoHeader.ipv4Data.destAddr = destIpAddr->ipv4Addr; pseudoHeader.ipv4Data.reserved = 0; pseudoHeader.ipv4Data.protocol = socket->protocol; pseudoHeader.ipv4Data.length = htons(length); //Set TTL value timeToLive = IPV4_DEFAULT_TTL; } 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) break; //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 = socket->protocol; //Set Hop Limit value timeToLive = IPV6_DEFAULT_HOP_LIMIT; } else #endif //Invalid destination address? { //An internal error has occurred error = ERROR_FAILURE; //Exit immediately break; } //Send raw IP datagram error = ipSendDatagram(interface, &pseudoHeader, buffer, offset, timeToLive); //Failed to send data? if(error) break; //Total number of bytes successfully transmitted if(written != NULL) *written = length; //End of exception handling block } while(0); //Free previously allocated memory block chunkedBufferFree(buffer); //Return status code return error; }
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; }