void ipv4FragTick(NetInterface *interface) { error_t error; uint_t i; systime_t time; Ipv4HoleDesc *hole; //Acquire exclusive access to the reassembly queue osAcquireMutex(&interface->ipv4FragQueueMutex); //Get current time time = osGetSystemTime(); //Loop through the reassembly queue for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue Ipv4FragDesc *frag = &interface->ipv4FragQueue[i]; //Make sure the entry is currently in use if(frag->buffer.chunkCount > 0) { //If the timer runs out, the partially-reassembled datagram must be //discarded and ICMP Time Exceeded message sent to the source host if((time - frag->timestamp) >= IPV4_FRAG_TIME_TO_LIVE) { //Debug message TRACE_INFO("IPv4 fragment reassembly timeout...\r\n"); //Dump IP header contents for debugging purpose ipv4DumpHeader(frag->buffer.chunk[0].address); //Point to the first hole descriptor hole = ipv4FindHole(frag, frag->firstHole); //Make sure the fragment zero has been received //before sending an ICMP message if(hole != NULL && hole->first > 0) { //Fix the size of the reconstructed datagram error = netBufferSetLength((NetBuffer *) &frag->buffer, frag->headerLength + hole->first); //Check status code if(!error) { //Send an ICMP Time Exceeded message icmpSendErrorMessage(interface, ICMP_TYPE_TIME_EXCEEDED, ICMP_CODE_REASSEMBLY_TIME_EXCEEDED, 0, (NetBuffer *) &frag->buffer); } } //Drop the partially reconstructed datagram netBufferSetLength((NetBuffer *) &frag->buffer, 0); } } } //Release exclusive access to the reassembly queue osReleaseMutex(&interface->ipv4FragQueueMutex); }
void ipv4FlushFragQueue(NetInterface *interface) { uint_t i; //Acquire exclusive access to the reassembly queue osAcquireMutex(&interface->ipv4FragQueueMutex); //Loop through the reassembly queue for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++) { //Drop any partially reconstructed datagram netBufferSetLength((NetBuffer *) &interface->ipv4FragQueue[i].buffer, 0); } //Release exclusive access to the reassembly queue osReleaseMutex(&interface->ipv4FragQueueMutex); }
error_t nbnsSendQuery(DnsCacheEntry *entry) { error_t error; size_t length; size_t offset; NetBuffer *buffer; NbnsHeader *message; DnsQuestion *dnsQuestion; IpAddr destIpAddr; //Allocate a memory buffer to hold the NBNS query message buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the NBNS header message = netBufferAt(buffer, offset); //Format NBNS query message message->id = htons(entry->id); message->qr = 0; message->opcode = DNS_OPCODE_QUERY; message->aa = 0; message->tc = 0; message->rd = 0; message->ra = 0; message->z = 0; message->b = 1; message->rcode = DNS_RCODE_NO_ERROR; //The NBNS query contains one question message->qdcount = HTONS(1); message->ancount = 0; message->nscount = 0; message->arcount = 0; //Length of the NBNS query message length = sizeof(DnsHeader); //Encode the NetBIOS name length += nbnsEncodeName(entry->name, message->questions); //Point to the corresponding question structure dnsQuestion = DNS_GET_QUESTION(message, length); //Fill in question structure dnsQuestion->qtype = HTONS(DNS_RR_TYPE_NB); dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN); //Update the length of the NBNS query message length += sizeof(DnsQuestion); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending NBNS message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage((DnsHeader *) message, length); //The destination address is the broadcast address destIpAddr.length = sizeof(Ipv4Addr); ipv4GetBroadcastAddr(entry->interface, &destIpAddr.ipv4Addr); //A request packet is always sent to the well known port 137 error = udpSendDatagramEx(entry->interface, NBNS_PORT, &destIpAddr, NBNS_PORT, buffer, offset, IPV4_DEFAULT_TTL); //Free previously allocated memory netBufferFree(buffer); //Return status code return error; }
Socket *tcpAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort) { error_t error; Socket *newSocket; TcpSynQueueItem *queueItem; //Ensure the socket was previously placed in the listening state if(tcpGetState(socket) != TCP_STATE_LISTEN) return NULL; //Enter critical section osAcquireMutex(&socketMutex); //Wait for an connection attempt while(1) { //The SYN queue is empty? if(!socket->synQueue) { //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 a SYN message is received from a client osWaitForEvent(&socket->event, socket->timeout); //Enter critical section osAcquireMutex(&socketMutex); } //Check whether the queue is still empty if(!socket->synQueue) { //Timeout error newSocket = NULL; //Exit immediately break; } //Point to the first item in the receive queue queueItem = socket->synQueue; //Return the client IP address and port number if(clientIpAddr) *clientIpAddr = queueItem->srcAddr; if(clientPort) *clientPort = queueItem->srcPort; //Leave critical section osReleaseMutex(&socketMutex); //Create a new socket to handle the incoming connection request newSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); //Enter critical section osAcquireMutex(&socketMutex); //Socket successfully created? if(newSocket != NULL) { //The user owns the socket newSocket->ownedFlag = TRUE; //Inherit settings from the listening socket newSocket->txBufferSize = socket->txBufferSize; newSocket->rxBufferSize = socket->rxBufferSize; //Number of chunks that comprise the TX and the RX buffers newSocket->txBuffer.maxChunkCount = arraysize(newSocket->txBuffer.chunk); newSocket->rxBuffer.maxChunkCount = arraysize(newSocket->rxBuffer.chunk); //Allocate transmit buffer error = netBufferSetLength((NetBuffer *) &newSocket->txBuffer, newSocket->txBufferSize); //Check status code if(!error) { //Allocate receive buffer error = netBufferSetLength((NetBuffer *) &newSocket->rxBuffer, newSocket->rxBufferSize); } //Transmit and receive buffers successfully allocated? if(!error) { //Bind the newly created socket to the appropriate interface newSocket->interface = queueItem->interface; //Bind the socket to the specified address newSocket->localIpAddr = queueItem->destAddr; newSocket->localPort = socket->localPort; //Save the port number and the IP address of the remote host newSocket->remoteIpAddr = queueItem->srcAddr; newSocket->remotePort = queueItem->srcPort; //Save the maximum segment size newSocket->mss = queueItem->mss; //Initialize TCP control block newSocket->iss = netGetRand(); newSocket->irs = queueItem->isn; newSocket->sndUna = newSocket->iss; newSocket->sndNxt = newSocket->iss + 1; newSocket->rcvNxt = newSocket->irs + 1; newSocket->rcvUser = 0; newSocket->rcvWnd = newSocket->rxBufferSize; //Default retransmission timeout newSocket->rto = TCP_INITIAL_RTO; #if (TCP_CONGESTION_CONTROL_SUPPORT == ENABLED) //Initial congestion window newSocket->cwnd = MIN(TCP_INITIAL_WINDOW * newSocket->mss, newSocket->txBufferSize); //Slow start threshold should be set arbitrarily high newSocket->ssthresh = UINT16_MAX; #endif //Send a SYN ACK control segment error = tcpSendSegment(newSocket, TCP_FLAG_SYN | TCP_FLAG_ACK, newSocket->iss, newSocket->rcvNxt, 0, TRUE); //TCP segment successfully sent? if(!error) { //Remove the item from the SYN queue socket->synQueue = queueItem->next; //Deallocate memory buffer memPoolFree(queueItem); //Update the state of events tcpUpdateEvents(socket); //The connection state should be changed to SYN-RECEIVED tcpChangeState(newSocket, TCP_STATE_SYN_RECEIVED); //We are done... break; } } //Dispose the socket tcpAbort(newSocket); } //Debug message TRACE_WARNING("Cannot accept TCP connection!\r\n"); //Remove the item from the SYN queue socket->synQueue = queueItem->next; //Deallocate memory buffer memPoolFree(queueItem); //Wait for the next connection attempt } //Leave critical section osReleaseMutex(&socketMutex); //Return a handle to the newly created socket return newSocket; }
error_t tcpConnect(Socket *socket) { error_t error; uint_t event; //Socket already connected? if(socket->state != TCP_STATE_CLOSED) return ERROR_ALREADY_CONNECTED; //The user owns the socket socket->ownedFlag = TRUE; //Number of chunks that comprise the TX and the RX buffers socket->txBuffer.maxChunkCount = arraysize(socket->txBuffer.chunk); socket->rxBuffer.maxChunkCount = arraysize(socket->rxBuffer.chunk); //Allocate transmit buffer error = netBufferSetLength((NetBuffer *) &socket->txBuffer, socket->txBufferSize); //Allocate receive buffer if(!error) error = netBufferSetLength((NetBuffer *) &socket->rxBuffer, socket->rxBufferSize); //Failed to allocate memory? if(error) { //Free any previously allocated memory tcpDeleteControlBlock(socket); //Report an error to the caller return error; } //Default MSS value socket->mss = MIN(TCP_DEFAULT_MSS, TCP_MAX_MSS); //An initial send sequence number is selected socket->iss = netGetRand(); //Initialize TCP control block socket->sndUna = socket->iss; socket->sndNxt = socket->iss + 1; socket->rcvUser = 0; socket->rcvWnd = socket->rxBufferSize; //Default retransmission timeout socket->rto = TCP_INITIAL_RTO; //Send a SYN segment error = tcpSendSegment(socket, TCP_FLAG_SYN, socket->iss, 0, 0, TRUE); //Failed to send TCP segment? if(error) return error; //Switch to the SYN-SENT state tcpChangeState(socket, TCP_STATE_SYN_SENT); //Wait for the connection to be established event = tcpWaitForEvents(socket, SOCKET_EVENT_CONNECTED | SOCKET_EVENT_CLOSED, socket->timeout); //Connection successfully established? if(event == SOCKET_EVENT_CONNECTED) return NO_ERROR; //Failed to establish connection? else if(event == SOCKET_EVENT_CLOSED) return ERROR_CONNECTION_FAILED; //Timeout exception? else return ERROR_TIMEOUT; }
error_t mdnsSendResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, NetBuffer *buffer, size_t offset, size_t length) { uint16_t destPort; IpAddr destIpAddr; DnsHeader *response; #if (IPV4_SUPPORT == ENABLED) //Check whether the mDNS query was received from an IPv4 peer if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //If the source UDP port in a received Multicast DNS query is not port 5353, //this indicates that the querier originating the query is a simple resolver if(udpHeader->srcPort != HTONS(MDNS_PORT)) { //the mDNS responder must send a UDP response directly back to the querier, //via unicast, to the query packet's source IP address and port destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr; } else { //Use mDNS IPv4 multicast address destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR; } } #endif #if (IPV6_SUPPORT == ENABLED) //Check whether the mDNS query was received from an IPv6 peer if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //If the source UDP port in a received Multicast DNS query is not port 5353, //this indicates that the querier originating the query is a simple resolver if(udpHeader->srcPort != HTONS(MDNS_PORT)) { //the mDNS responder must send a UDP response directly back to the querier, //via unicast, to the query packet's source IP address and port destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr; } else { //Use mDNS IPv6 multicast address destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR; } } #endif //Destination port destPort = ntohs(udpHeader->srcPort); //Point to the mDNS response header response = netBufferAt(buffer, offset); //Convert 16-bit value to network byte order response->ancount = htons(response->ancount); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending mDNS message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(response, length); //All multicast DNS responses should be sent with an IP TTL set to 255 return udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, destPort, buffer, offset, MDNS_DEFAULT_IP_TTL); }
error_t mdnsSendAnnouncement(NetInterface *interface) { error_t error; size_t length; size_t offset; NetBuffer *buffer; DnsHeader *message; IpAddr destIpAddr; //Initialize error code error = NO_ERROR; //Allocate a memory buffer to hold the mDNS query message buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the mDNS header message = netBufferAt(buffer, offset); //Format mDNS query message message->id = 0; message->qr = 1; message->opcode = DNS_OPCODE_QUERY; message->aa = 1; message->tc = 0; message->rd = 0; message->ra = 0; message->z = 0; message->rcode = DNS_RCODE_NO_ERROR; //Multicast DNS responses must not contain any questions message->qdcount = 0; message->ancount = 0; message->nscount = 0; message->arcount = 0; //Length of the mDNS query message length = sizeof(DnsHeader); //Start of exception handling block do { //Format A resource record error = mdnsAddIpv4ResourceRecord(interface, message, &length, TRUE); //Any error to report? if(error) break; //Format AAAA resource record error = mdnsAddIpv6ResourceRecord(interface, message, &length, TRUE); //Any error to report? if(error) break; //Convert 16-bit value to network byte order message->ancount = htons(message->ancount); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending mDNS announcement (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); #if (IPV4_SUPPORT == ENABLED) //Select the relevant multicast address (224.0.0.251) destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR; //All multicast DNS queries should be sent with an IP TTL set to 255 error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL); //Any error to report? if(error)break; #endif #if (IPV6_SUPPORT == ENABLED) //Select the relevant multicast address (ff02::fb) destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR; //All multicast DNS queries should be sent with an IP TTL set to 255 error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL); //Any error to report? if(error)break; #endif //End of exception handling block } while(0); //Free previously allocated memory netBufferFree(buffer); //Return status code return error; }
error_t mdnsSendProbe(NetInterface *interface) { error_t error; size_t length; size_t offset; NetBuffer *buffer; DnsHeader *message; DnsQuestion *dnsQuestion; IpAddr destIpAddr; //Initialize error code error = NO_ERROR; //Allocate a memory buffer to hold the mDNS query message buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the mDNS header message = netBufferAt(buffer, offset); //Format mDNS query message message->id = 0; message->qr = 0; message->opcode = DNS_OPCODE_QUERY; message->aa = 0; message->tc = 0; message->rd = 0; message->ra = 0; message->z = 0; message->rcode = DNS_RCODE_NO_ERROR; //The mDNS query contains one question message->qdcount = HTONS(1); message->ancount = 0; message->nscount = 0; message->arcount = 0; //Length of the mDNS query message length = sizeof(DnsHeader); //Encode the host name using the DNS name notation length += mdnsEncodeName(interface->hostname, "", ".local", (uint8_t *) message + length); //Point to the corresponding question structure dnsQuestion = DNS_GET_QUESTION(message, length); //Fill in question structure dnsQuestion->qtype = HTONS(DNS_RR_TYPE_ANY); dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN); //Update the length of the mDNS query message length += sizeof(DnsQuestion); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending mDNS probe (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); #if (IPV4_SUPPORT == ENABLED) //Check status code if(!error) { //Select the relevant multicast address (224.0.0.251) destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR; //All multicast DNS queries should be sent with an IP TTL set to 255 error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL); } #endif #if (IPV6_SUPPORT == ENABLED) //Check status code if(!error) { //Select the relevant multicast address (ff02::fb) destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR; //All multicast DNS queries should be sent with an IP TTL set to 255 error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr, MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL); } #endif //Free previously allocated memory netBufferFree(buffer); //Return status code return error; }
error_t lcpSendConfigureReq(PppContext *context) { error_t error; size_t length; size_t offset; NetBuffer *buffer; PppConfigurePacket *configureReqPacket; //Debug message TRACE_INFO("LCP Send-Configure-Request callback\r\n"); //Calculate the maximum size of the Configure-Request packet length = sizeof(PppConfigurePacket) + sizeof(LcpMruOption) + sizeof(LcpAccmOption); //Allocate a buffer memory to hold the packet buffer = pppAllocBuffer(length, &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the Configure-Request packet configureReqPacket = netBufferAt(buffer, offset); //Format packet header configureReqPacket->code = PPP_CODE_CONFIGURE_REQ; configureReqPacket->identifier = ++context->lcpFsm.identifier; configureReqPacket->length = sizeof(PppConfigurePacket); //Make sure the Maximum-Receive-Unit option has not been previously rejected if(!context->localConfig.mruRejected) { //Convert MRU to network byte order uint16_t value = htons(context->localConfig.mru); //Add option pppAddOption(configureReqPacket, LCP_OPTION_MRU, &value, sizeof(uint16_t)); } //Make sure the Async-Control-Character-Map option has not been previously rejected if(!context->localConfig.accmRejected) { //Convert ACCM to network byte order uint32_t value = htonl(context->localConfig.accm); //Add option pppAddOption(configureReqPacket, LCP_OPTION_ACCM, &value, sizeof(uint32_t)); } //Save packet length length = configureReqPacket->length; //Convert length field to network byte order configureReqPacket->length = htons(length); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending Configure-Request packet (%" PRIuSIZE " bytes)...\r\n", length); //Dump packet contents for debugging purpose pppDumpPacket((PppPacket *) configureReqPacket, length, PPP_PROTOCOL_LCP); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_LCP); //The restart counter is decremented each time a Configure-Request is sent if(context->lcpFsm.restartCounter > 0) context->lcpFsm.restartCounter--; //Save the time at which the packet was sent context->lcpFsm.timestamp = osGetSystemTime(); //Free previously allocated memory block netBufferFree(buffer); //Return status code return error; }
error_t dnsSendQuery(DnsCacheEntry *entry) { error_t error; size_t length; size_t offset; NetBuffer *buffer; DnsHeader *message; DnsQuestion *dnsQuestion; IpAddr destIpAddr; #if (IPV4_SUPPORT == ENABLED) //An IPv4 address is expected? if(entry->type == HOST_TYPE_IPV4) { //Select the relevant DNS server destIpAddr.length = sizeof(Ipv4Addr); ipv4GetDnsServer(entry->interface, entry->dnsServerNum, &destIpAddr.ipv4Addr); //Make sure the IP address is valid if(destIpAddr.ipv4Addr == IPV4_UNSPECIFIED_ADDR) return ERROR_NO_DNS_SERVER; } else #endif #if (IPV6_SUPPORT == ENABLED) //An IPv6 address is expected? if(entry->type == HOST_TYPE_IPV6) { //Select the relevant DNS server destIpAddr.length = sizeof(Ipv6Addr); ipv6GetDnsServer(entry->interface, entry->dnsServerNum, &destIpAddr.ipv6Addr); //Make sure the IP address is valid if(ipv6CompAddr(&destIpAddr.ipv6Addr, &IPV6_UNSPECIFIED_ADDR)) return ERROR_NO_DNS_SERVER; } else #endif //Invalid host type? { //Report an error return ERROR_INVALID_PARAMETER; } //Allocate a memory buffer to hold the DNS query message buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset); //Failed to allocate buffer? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the DNS header message = netBufferAt(buffer, offset); //Format DNS query message message->id = htons(entry->id); message->qr = 0; message->opcode = DNS_OPCODE_QUERY; message->aa = 0; message->tc = 0; message->rd = 1; message->ra = 0; message->z = 0; message->rcode = DNS_RCODE_NO_ERROR; //The DNS query contains one question message->qdcount = HTONS(1); message->ancount = 0; message->nscount = 0; message->arcount = 0; //Length of the DNS query message length = sizeof(DnsHeader); //Encode the host name using the DNS name notation length += dnsEncodeName(entry->name, message->questions); //Point to the corresponding question structure dnsQuestion = DNS_GET_QUESTION(message, length); #if (IPV4_SUPPORT == ENABLED) //An IPv4 address is expected? if(entry->type == HOST_TYPE_IPV4) { //Fill in question structure dnsQuestion->qtype = HTONS(DNS_RR_TYPE_A); dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN); } #endif #if (IPV6_SUPPORT == ENABLED) //An IPv6 address is expected? if(entry->type == HOST_TYPE_IPV6) { //Fill in question structure dnsQuestion->qtype = HTONS(DNS_RR_TYPE_AAAA); dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN); } #endif //Update the length of the DNS query message length += sizeof(DnsQuestion); //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + length); //Debug message TRACE_INFO("Sending DNS message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); //Send DNS query message error = udpSendDatagramEx(entry->interface, entry->port, &destIpAddr, DNS_PORT, buffer, offset, 0); //Free previously allocated memory netBufferFree(buffer); //Return status code return error; }
error_t pppSendConfigureAckNak(PppContext *context, const PppConfigurePacket *configureReqPacket, PppProtocol protocol, PppCode code) { error_t error; size_t length; size_t offset; NetBuffer *buffer; PppConfigurePacket *configureAckNakPacket; PppOption *option; //Initialize status code error = NO_ERROR; //Retrieve the length of the Configure-Request packet length = ntohs(configureReqPacket->length); //Allocate a buffer memory to hold the Configure-Ack, Nak or Reject packet buffer = pppAllocBuffer(length, &offset); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Point to the beginning of the packet configureAckNakPacket = netBufferAt(buffer, offset); //Format packet header configureAckNakPacket->code = code; configureAckNakPacket->identifier = configureReqPacket->identifier; configureAckNakPacket->length = sizeof(PppConfigurePacket); //Retrieve the length of the option list length -= sizeof(PppConfigurePacket); //Point to the first option option = (PppOption *) configureReqPacket->options; //Parse configuration options while(length > 0) { //LCP protocol? if(protocol == PPP_PROTOCOL_LCP) { //Parse LCP option lcpParseOption(context, option, length, configureAckNakPacket); } #if (IPV4_SUPPORT) //IPCP protocol? else if(protocol == PPP_PROTOCOL_IPCP) { //Parse IPCP option ipcpParseOption(context, option, length, configureAckNakPacket); } #endif //Remaining bytes to process length -= option->length; //Jump to the next option option = (PppOption *) ((uint8_t *) option + option->length); } //Adjust the length of the multi-part buffer netBufferSetLength(buffer, offset + configureAckNakPacket->length); //Convert length field to network byte order configureAckNakPacket->length = htons(configureAckNakPacket->length); //Debug message if(code == PPP_CODE_CONFIGURE_ACK) { TRACE_INFO("Sending Configure-Ack packet (%" PRIuSIZE " bytes)...\r\n", ntohs(configureAckNakPacket->length)); } else if(code == PPP_CODE_CONFIGURE_NAK) { TRACE_INFO("Sending Configure-Nak packet (%" PRIuSIZE " bytes)...\r\n", ntohs(configureAckNakPacket->length)); } else if(code == PPP_CODE_CONFIGURE_REJ) { TRACE_INFO("Sending Configure-Reject packet (%" PRIuSIZE " bytes)...\r\n", ntohs(configureAckNakPacket->length)); } //Dump packet contents for debugging purpose pppDumpPacket((PppPacket *) configureAckNakPacket, ntohs(configureAckNakPacket->length), protocol); //Send PPP frame error = pppSendFrame(context->interface, buffer, offset, protocol); //Free previously allocated memory block netBufferFree(buffer); //Return status code return error; }
error_t ipv4FragmentDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, uint16_t id, const NetBuffer *payload, size_t payloadOffset, uint8_t timeToLive) { error_t error; size_t offset; size_t length; size_t payloadLength; size_t fragmentOffset; size_t maxFragmentSize; NetBuffer *fragment; //Retrieve the length of the payload payloadLength = netBufferGetLength(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; //Maximum payload size for fragmented packets maxFragmentSize = interface->ipv4Config.mtu - sizeof(Ipv4Header); //The size shall be a multiple of 8-byte blocks maxFragmentSize -= (maxFragmentSize % 8); //Split the payload into multiple IP fragments for(offset = 0; offset < payloadLength; offset += length) { //Flush the contents of the fragment error = netBufferSetLength(fragment, fragmentOffset); //Sanity check if(error) break; //Process the last fragment? if((payloadLength - offset) <= maxFragmentSize) { //Size of the current fragment length = payloadLength - offset; //Copy fragment data netBufferConcat(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 = maxFragmentSize; //Copy fragment data netBufferConcat(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 netBufferFree(fragment); //Return status code return error; }
Ipv4FragDesc *ipv4SearchFragQueue(NetInterface *interface, const Ipv4Header *packet) { error_t error; uint_t i; Ipv4Header *datagram; Ipv4FragDesc *frag; Ipv4HoleDesc *hole; //Search for a matching IP datagram being reassembled for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue frag = &interface->ipv4FragQueue[i]; //Check whether the current entry is used? if(frag->buffer.chunkCount > 0) { //Point to the corresponding datagram datagram = netBufferAt((NetBuffer *) &frag->buffer, 0); //Check source and destination addresses if(datagram->srcAddr != packet->srcAddr) continue; if(datagram->destAddr != packet->destAddr) continue; //Compare identification and protocol fields if(datagram->identification != packet->identification) continue; if(datagram->protocol != packet->protocol) continue; //A matching entry has been found in the reassembly queue return frag; } } //If the current packet does not match an existing entry //in the reassembly queue, then create a new entry for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue frag = &interface->ipv4FragQueue[i]; //The current entry is free? if(!frag->buffer.chunkCount) { //Number of chunks that comprise the reassembly buffer frag->buffer.maxChunkCount = arraysize(frag->buffer.chunk); //Allocate sufficient memory to hold the IPv4 header and //the first hole descriptor error = netBufferSetLength((NetBuffer *) &frag->buffer, NET_MEM_POOL_BUFFER_SIZE + sizeof(Ipv4HoleDesc)); //Failed to allocate memory? if(error) { //Clean up side effects netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return NULL; } //Initial length of the reconstructed datagram frag->headerLength = packet->headerLength * 4; frag->dataLength = 0; //Fix the length of the first chunk frag->buffer.chunk[0].length = frag->headerLength; //Copy IPv4 header from the incoming fragment netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->headerLength); //Save current time frag->timestamp = osGetSystemTime(); //Create a new entry in the hole descriptor list frag->firstHole = 0; //Point to first hole descriptor hole = ipv4FindHole(frag, frag->firstHole); //The entry describes the datagram as being completely missing hole->first = 0; hole->last = IPV4_INFINITY; hole->next = IPV4_INFINITY; //Dump hole descriptor list ipv4DumpHoleList(frag); //Return the matching fragment descriptor return frag; } } //The reassembly queue is full return NULL; }
void ipv4ReassembleDatagram(NetInterface *interface, const Ipv4Header *packet, size_t length) { error_t error; uint16_t offset; uint16_t dataFirst; uint16_t dataLast; Ipv4FragDesc *frag; Ipv4HoleDesc *hole; Ipv4HoleDesc *prevHole; //Get the length of the payload length -= packet->headerLength * 4; //Convert the fragment offset from network byte order offset = ntohs(packet->fragmentOffset); //Every fragment except the last must contain a multiple of 8 bytes of data if((offset & IPV4_FLAG_MF) && (length % 8)) { //Drop incoming packet return; } //Calculate the index of the first byte dataFirst = (offset & IPV4_OFFSET_MASK) * 8; //Calculate the index immediately following the last byte dataLast = dataFirst + length; //Search for a matching IP datagram being reassembled frag = ipv4SearchFragQueue(interface, packet); //No matching entry in the reassembly queue? if(!frag) return; //The very first fragment requires special handling if(!(offset & IPV4_OFFSET_MASK)) { //Calculate the length of the IP header including options frag->headerLength = packet->headerLength * 4; //Enforce the size of the reconstructed datagram if((frag->headerLength + frag->dataLength) > IPV4_MAX_FRAG_DATAGRAM_SIZE) { //Drop any allocated resources netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return; } //Make sure the IP header entirely fits in the first chunk if(frag->headerLength > frag->buffer.chunk[0].size) { //Drop the reconstructed datagram netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return; } //Fix the length of the first chunk frag->buffer.chunk[0].length = frag->headerLength; //Always take the IP header from the first fragment netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->headerLength); } //It may be necessary to increase the size of the buffer... if(dataLast > frag->dataLength) { //Enforce the size of the reconstructed datagram if((frag->headerLength + dataLast) > IPV4_MAX_FRAG_DATAGRAM_SIZE) { //Drop any allocated resources netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return; } //Adjust the size of the reconstructed datagram error = netBufferSetLength((NetBuffer *) &frag->buffer, frag->headerLength + dataLast + sizeof(Ipv4HoleDesc)); //Any error to report? if(error) { //Drop the reconstructed datagram netBufferSetLength((NetBuffer *) &frag->buffer, 0); //Exit immediately return; } //Actual length of the payload frag->dataLength = dataLast; } //Select the first hole descriptor from the list hole = ipv4FindHole(frag, frag->firstHole); //Keep track of the previous hole in the list prevHole = NULL; //Iterate through the hole descriptors while(hole != NULL) { //Save lower and upper boundaries for later use uint16_t holeFirst = hole->first; uint16_t holeLast = hole->last; //Check whether the newly arrived fragment //interacts with this hole in some way if(dataFirst < holeLast && dataLast > holeFirst) { //The current descriptor is no longer valid. We will destroy //it, and in the next two steps, we will determine whether //or not it is necessary to create any new hole descriptors if(prevHole != NULL) prevHole->next = hole->next; else frag->firstHole = hole->next; //Is there still a hole at the beginning of the segment? if(dataFirst > holeFirst) { //Create a new entry that describes this hole hole = ipv4FindHole(frag, holeFirst); hole->first = holeFirst; hole->last = dataFirst; //Insert the newly created entry into the hole descriptor list if(prevHole != NULL) { hole->next = prevHole->next; prevHole->next = hole->first; } else { hole->next = frag->firstHole; frag->firstHole = hole->first; } //Always keep track of the previous hole prevHole = hole; } //Is there still a hole at the end of the segment? if(dataLast < holeLast && (offset & IPV4_FLAG_MF)) { //Create a new entry that describes this hole hole = ipv4FindHole(frag, dataLast); hole->first = dataLast; hole->last = holeLast; //Insert the newly created entry into the hole descriptor list if(prevHole != NULL) { hole->next = prevHole->next; prevHole->next = hole->first; } else { hole->next = frag->firstHole; frag->firstHole = hole->first; } //Always keep track of the previous hole prevHole = hole; } } else { //The newly arrived fragment does not interact with the current hole prevHole = hole; } //Select the next hole descriptor from the list hole = ipv4FindHole(frag, prevHole ? prevHole->next : frag->firstHole); } //Copy data from the fragment to the reassembly buffer netBufferWrite((NetBuffer *) &frag->buffer, frag->headerLength + dataFirst, IPV4_DATA(packet), length); //Dump hole descriptor list ipv4DumpHoleList(frag); //If the hole descriptor list is empty, the reassembly process is now complete if(!ipv4FindHole(frag, frag->firstHole)) { //Discard the extra hole descriptor that follows the reconstructed datagram error = netBufferSetLength((NetBuffer *) &frag->buffer, frag->headerLength + frag->dataLength); //Check status code if(!error) { //Point to the IP header Ipv4Header *datagram = netBufferAt((NetBuffer *) &frag->buffer, 0); //Fix IP header datagram->totalLength = htons(frag->headerLength + frag->dataLength); datagram->fragmentOffset = 0; datagram->headerChecksum = 0; //Recalculate IP header checksum datagram->headerChecksum = ipCalcChecksum(datagram, frag->buffer.chunk[0].length); //Pass the original IPv4 datagram to the higher protocol layer ipv4ProcessDatagram(interface, (NetBuffer *) &frag->buffer); } //Release previously allocated memory netBufferSetLength((NetBuffer *) &frag->buffer, 0); } }