error_t udpInvokeRxCallback(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *header, const NetBuffer *buffer, size_t offset) { uint_t i; void *params; UdpRxCallbackDesc *entry; //This flag tells whether a matching entry has been found bool_t found = FALSE; //Acquire exclusive access to the callback table osAcquireMutex(&udpCallbackMutex); //Loop through the table for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++) { //Point to the current entry entry = &udpCallbackTable[i]; //Check whether the entry is currently in used if(entry->callback != NULL) { //Bound to a particular interface? if(entry->interface == NULL || entry->interface == interface) { //Does the specified port number match the current entry? if(entry->port == ntohs(header->destPort)) { //Retrieve callback parameters params = entry->params; //Release mutex to prevent any deadlock if(params == NULL) osReleaseMutex(&udpCallbackMutex); //Invoke user callback function entry->callback(interface, pseudoHeader, header, buffer, offset, params); //Acquire mutex if(params == NULL) osAcquireMutex(&udpCallbackMutex); //A matching entry was found found = TRUE; } } } } //Release exclusive access to the callback table osReleaseMutex(&udpCallbackMutex); //Return status code return found ? NO_ERROR : ERROR_PORT_UNREACHABLE; }
error_t ipv4LeaveMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr) { uint_t i; uint_t j; MacAddr macAddr; //Ensure the specified IPv4 address is a multicast address if(!ipv4IsMulticastAddr(groupAddr)) return ERROR_INVALID_ADDRESS; //Acquire exclusive access to the IPv4 filter table osAcquireMutex(&interface->ipv4FilterMutex); //Loop through filter table entries for(i = 0; i < interface->ipv4FilterSize; i++) { //Specified IPv4 address found? if(interface->ipv4Filter[i].addr == groupAddr) { //Decrement the reference count interface->ipv4Filter[i].refCount--; //Remove the entry if the reference count drops to zero if(interface->ipv4Filter[i].refCount < 1) { #if (IGMP_SUPPORT == ENABLED) //Report group membership termination igmpLeaveGroup(interface, &interface->ipv4Filter[i]); #endif //Map the multicast IPv4 address to a MAC-layer address ipv4MapMulticastAddrToMac(groupAddr, &macAddr); #if (ETH_SUPPORT == ENABLED) //Drop the corresponding address from the MAC filter table ethDropMulticastAddr(interface, &macAddr); #endif //Adjust the size of the IPv4 filter table interface->ipv4FilterSize--; //Remove the corresponding entry for(j = i; j < interface->ipv4FilterSize; j++) interface->ipv4Filter[j] = interface->ipv4Filter[j + 1]; } //Release exclusive access to the IPv4 filter table osReleaseMutex(&interface->ipv4FilterMutex); //No error to report return NO_ERROR; } } //Release exclusive access to the IPv4 filter table osReleaseMutex(&interface->ipv4FilterMutex); //The specified IPv4 address does not exist return ERROR_FAILURE; }
void dnsFlushCache(NetInterface *interface) { uint_t i; DnsCacheEntry *entry; //Acquire exclusive access to the DNS cache osAcquireMutex(&dnsCacheMutex); //Go through DNS cache for(i = 0; i < DNS_CACHE_SIZE; i++) { //Point to the current entry entry = &dnsCache[i]; //Check whether the entry is currently in used if(entry->state != DNS_STATE_NONE) { //Delete DNS entries only for the given network interface if(entry->interface == interface) dnsDeleteEntry(entry); } } //Release exclusive access to the DNS cache osReleaseMutex(&dnsCacheMutex); }
error_t socketListen(Socket *socket, uint_t backlog) { #if (TCP_SUPPORT == ENABLED) error_t error; //Make sure the socket handle is valid if(!socket) return ERROR_INVALID_PARAMETER; //This function shall be used with connection-oriented socket types if(socket->type != SOCKET_TYPE_STREAM) return ERROR_INVALID_SOCKET; //Enter critical section osAcquireMutex(&socketMutex); //Start listening for an incoming connection error = tcpListen(socket, backlog); //Leave critical section osReleaseMutex(&socketMutex); //Return status code return error; #else return ERROR_NOT_IMPLEMENTED; #endif }
error_t socketShutdown(Socket *socket, uint_t how) { #if (TCP_SUPPORT == ENABLED) error_t error; //Make sure the socket handle is valid if(!socket) return ERROR_INVALID_PARAMETER; //Make sure the socket type is correct if(socket->type != SOCKET_TYPE_STREAM) return ERROR_INVALID_SOCKET; //Check flags if((how != SOCKET_SD_SEND) && (how != SOCKET_SD_RECEIVE) && (how != SOCKET_SD_BOTH)) return ERROR_INVALID_PARAMETER; //Enter critical section osAcquireMutex(&socketMutex); //Graceful shutdown error = tcpShutdown(socket, how); //Leave critical section osReleaseMutex(&socketMutex); //Return status code return error; #else return ERROR_NOT_IMPLEMENTED; #endif }
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 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); //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; }
void ipv6FragTick(NetInterface *interface) { error_t error; uint_t i; systime_t time; Ipv6HoleDesc *hole; //Acquire exclusive access to the reassembly queue osAcquireMutex(&interface->ipv6FragQueueMutex); //Get current time time = osGetSystemTime(); //Loop through the reassembly queue for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue Ipv6FragDesc *frag = &interface->ipv6FragQueue[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 ICMPv6 Time Exceeded message sent to the source host if((time - frag->timestamp) >= IPV6_FRAG_TIME_TO_LIVE) { //Debug message TRACE_INFO("IPv6 fragment reassembly timeout...\r\n"); //Dump IP header contents for debugging purpose ipv6DumpHeader(frag->buffer.chunk[0].address); //Point to the first hole descriptor hole = ipv6FindHole(frag, frag->firstHole); //Make sure the fragment zero has been received //before sending an ICMPv6 message if(hole != NULL && hole->first > 0) { //Fix the size of the reconstructed datagram error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, frag->unfragPartLength + hole->first); //Check status code if(!error) { //Send an ICMPv6 Time Exceeded message icmpv6SendErrorMessage(interface, ICMPV6_TYPE_TIME_EXCEEDED, ICMPV6_CODE_REASSEMBLY_TIME_EXCEEDED, 0, (ChunkedBuffer *) &frag->buffer); } } //Drop the partially reconstructed datagram chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); } } } //Release exclusive access to the reassembly queue osReleaseMutex(&interface->ipv6FragQueueMutex); }
error_t socketSendTo(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort, const void *data, size_t length, size_t *written, uint_t flags) { error_t error; //No data has been transmitted yet if(written) *written = 0; //Make sure the socket handle is valid if(!socket) return ERROR_INVALID_PARAMETER; #if (TCP_SUPPORT == ENABLED) //Connection-oriented socket? if(socket->type == SOCKET_TYPE_STREAM) { //Enter critical section osAcquireMutex(&socketMutex); //For connection-oriented sockets, target address is ignored error = tcpSend(socket, data, length, written, flags); //Leave critical section osReleaseMutex(&socketMutex); } else #endif #if (UDP_SUPPORT == ENABLED) //Connectionless socket? if(socket->type == SOCKET_TYPE_DGRAM) { //Send UDP datagram error = udpSendDatagram(socket, destIpAddr, destPort, data, length, written); } else #endif #if (RAW_SOCKET_SUPPORT == ENABLED) //Raw socket? if(socket->type == SOCKET_TYPE_RAW_IP) { //Send a raw IP packet error = rawSocketSendIpPacket(socket, destIpAddr, data, length, written); } else if(socket->type == SOCKET_TYPE_RAW_ETH) { //Send a raw Ethernet packet error = rawSocketSendEthPacket(socket, data, length, written); } else #endif //Socket type not supported... { //Invalid socket type error = ERROR_INVALID_SOCKET; } //Return status code return error; }
void slaacLinkChangeEvent(SlaacContext *context) { //Enter critical section osAcquireMutex(&context->mutex); //Reinitialize state machine context->state = SLAAC_STATE_INIT; //Leave critical section osReleaseMutex(&context->mutex); }
error_t rawSocketReceiveEthPacket(Socket *socket, void *data, size_t size, size_t *received, uint_t flags) { SocketQueueItem *queueItem; //The SOCKET_FLAG_DONT_WAIT enables non-blocking operation if(!(flags & SOCKET_FLAG_DONT_WAIT)) { //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 = netBufferRead(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 netBufferFree(queueItem->buffer); } //Update the state of events rawSocketUpdateEvents(socket); //Successful read operation return NO_ERROR; }
error_t ipv4CheckDestAddr(NetInterface *interface, Ipv4Addr ipAddr) { uint_t i; //Filter out any invalid addresses error_t error = ERROR_INVALID_ADDRESS; //Broadcast address? if(ipv4IsBroadcastAddr(interface, ipAddr)) { //Always accept broadcast address error = NO_ERROR; } //Multicast address? else if(ipv4IsMulticastAddr(ipAddr)) { //Acquire exclusive access to the IPv4 filter table osAcquireMutex(&interface->ipv4FilterMutex); //Loop through the IPv4 filter table for(i = 0; i < interface->ipv4FilterSize; i++) { //Check whether the destination IPv4 address matches //a relevant multicast address if(interface->ipv4Filter[i].addr == ipAddr) { //The multicast address is acceptable error = NO_ERROR; //Stop immediately break; } } //Release exclusive access to the IPv4 filter table osReleaseMutex(&interface->ipv4FilterMutex); } //Unicast address? else { //The destination address matches the host address? if(interface->ipv4Config.addrState != IPV4_ADDR_STATE_INVALID && interface->ipv4Config.addr == ipAddr) { //The destination address is acceptable error = NO_ERROR; } } //Return status code return error; }
TcpState tcpGetState(Socket *socket) { TcpState state; //Enter critical section osAcquireMutex(&socketMutex); //Get TCP FSM current state state = socket->state; //Leave critical section osReleaseMutex(&socketMutex); //Return current state return state; }
error_t slaacStop(SlaacContext *context) { //Enter critical section osAcquireMutex(&context->mutex); //Suspend SLAAC operation context->running = FALSE; //Reinitialize state machine context->state = SLAAC_STATE_INIT; //Leave critical section osReleaseMutex(&context->mutex); //Successful processing return NO_ERROR; }
error_t socketUnregisterEvents(Socket *socket) { //Make sure the socket handle is valid if(!socket) return ERROR_INVALID_PARAMETER; //Enter critical section osAcquireMutex(&socketMutex); //Unsuscribe socket events socket->userEvent = NULL; //Leave critical section osReleaseMutex(&socketMutex); //Successful processing return NO_ERROR; }
error_t socketRegisterEvents(Socket *socket, OsEvent *event, uint_t eventMask) { //Make sure the socket handle is valid if(!socket) return ERROR_INVALID_PARAMETER; //Enter critical section osAcquireMutex(&socketMutex); //An user event may have been previously registered... if(socket->userEvent != NULL) socket->eventMask |= eventMask; else socket->eventMask = eventMask; //Suscribe to get notified of events socket->userEvent = event; #if (TCP_SUPPORT == ENABLED) //Handle TCP specific events if(socket->type == SOCKET_TYPE_STREAM) { tcpUpdateEvents(socket); } #endif #if (UDP_SUPPORT == ENABLED) //Handle UDP specific events if(socket->type == SOCKET_TYPE_DGRAM) { udpUpdateEvents(socket); } #endif #if (RAW_SOCKET_SUPPORT == ENABLED) //Handle events that are specific to raw sockets if(socket->type == SOCKET_TYPE_RAW_IP || socket->type == SOCKET_TYPE_RAW_ETH) { rawSocketUpdateEvents(socket); } #endif //Leave critical section osReleaseMutex(&socketMutex); //Successful processing return NO_ERROR; }
void ipv6FlushFragQueue(NetInterface *interface) { uint_t i; //Acquire exclusive access to the reassembly queue osAcquireMutex(&interface->ipv6FragQueueMutex); //Loop through the reassembly queue for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++) { //Drop any partially reconstructed datagram chunkedBufferSetLength((ChunkedBuffer *) &interface->ipv6FragQueue[i].buffer, 0); } //Release exclusive access to the reassembly queue osReleaseMutex(&interface->ipv6FragQueueMutex); }
void socketClose(Socket *socket) { //Make sure the socket handle is valid if(!socket) return; //Enter critical section osAcquireMutex(&socketMutex); #if (TCP_SUPPORT == ENABLED) //Connection-oriented socket? if(socket->type == SOCKET_TYPE_STREAM) { //Abort the current TCP connection tcpAbort(socket); } #endif #if (UDP_SUPPORT == ENABLED || RAW_SOCKET_SUPPORT == ENABLED) //Connectionless socket or raw socket? if(socket->type == SOCKET_TYPE_DGRAM || socket->type == SOCKET_TYPE_RAW_IP || socket->type == SOCKET_TYPE_RAW_ETH) { //Point to the first item in the receive queue SocketQueueItem *queueItem = socket->receiveQueue; //Purge the receive queue while(queueItem) { //Keep track of the next item in the queue SocketQueueItem *nextQueueItem = queueItem->next; //Free previously allocated memory memPoolFree(queueItem); //Point to the next item queueItem = nextQueueItem; } //Mark the socket as closed socket->type = SOCKET_TYPE_UNUSED; } #endif //Leave critical section osReleaseMutex(&socketMutex); }
error_t socketGetEvents(Socket *socket, uint_t *eventFlags) { //Make sure the socket handle is valid if(!socket) { //Always return a valid value *eventFlags = 0; //Report an error return ERROR_INVALID_PARAMETER; } //Enter critical section osAcquireMutex(&socketMutex); //Read event flags for the specified socket *eventFlags = socket->eventFlags; //Leave critical section osReleaseMutex(&socketMutex); //Successful processing return NO_ERROR; }
error_t udpAttachRxCallback(NetInterface *interface, uint16_t port, UdpRxCallback callback, void *params) { uint_t i; UdpRxCallbackDesc *entry; //Acquire exclusive access to the callback table osAcquireMutex(&udpCallbackMutex); //Loop through the table for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++) { //Point to the current entry entry = &udpCallbackTable[i]; //Check whether the entry is currently in used if(entry->callback == NULL) { //Create a new entry entry->interface = interface; entry->port = port; entry->callback = callback; entry->params = params; //We are done break; } } //Release exclusive access to the callback table osReleaseMutex(&udpCallbackMutex); //Failed to attach the specified user callback? if(i >= UDP_CALLBACK_TABLE_SIZE) return ERROR_OUT_OF_RESOURCES; //Successful processing return NO_ERROR; }
void ndpFlushCache(NetInterface *interface) { uint_t i; NdpCacheEntry *entry; //Acquire exclusive access to Neighbor cache osAcquireMutex(&interface->ndpCacheMutex); //Loop through Neighbor cache entries for(i = 0; i < NDP_CACHE_SIZE; i++) { //Point to the current entry entry = &interface->ndpCache[i]; //Drop packets that are waiting for address resolution ndpFlushQueuedPackets(interface, entry); //Release Neighbor cache entry entry->state = NDP_STATE_NONE; } //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); }
error_t udpDetachRxCallback(NetInterface *interface, uint16_t port) { uint_t i; UdpRxCallbackDesc *entry; //This flag tells whether an entry has been found bool_t found = FALSE; //Acquire exclusive access to the callback table osAcquireMutex(&udpCallbackMutex); //Loop through the table for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++) { //Point to the current entry entry = &udpCallbackTable[i]; //Check whether the entry is currently in used if(entry->callback != NULL) { //Does the specified port number match the current entry? if(entry->port == port && entry->interface == interface) { //Unregister user callback entry->callback = NULL; //A matching entry was found found = TRUE; } } } //Release exclusive access to the callback table osReleaseMutex(&udpCallbackMutex); //Check whether the specified callback has been successfully unregistered return found ? NO_ERROR : ERROR_FAILURE; }
error_t udpProcessDatagram(NetInterface *interface, IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset) { error_t error; uint_t i; size_t length; UdpHeader *header; Socket *socket; SocketQueueItem *queueItem; NetBuffer *p; //Retrieve the length of the UDP datagram length = netBufferGetLength(buffer) - offset; //Ensure the UDP header is valid if(length < sizeof(UdpHeader)) { //Debug message TRACE_WARNING("UDP datagram length is invalid!\r\n"); //Report an error return ERROR_INVALID_HEADER; } //Point to the UDP header header = netBufferAt(buffer, offset); //Sanity check if(!header) return ERROR_FAILURE; //Debug message TRACE_INFO("UDP datagram received (%" PRIuSIZE " bytes)...\r\n", length); //Dump UDP header contents for debugging purpose udpDumpHeader(header); //When UDP runs over IPv6, the checksum is mandatory if(header->checksum || pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Verify UDP checksum if(ipCalcUpperLayerChecksumEx(pseudoHeader->data, pseudoHeader->length, buffer, offset, length) != 0xFFFF) { //Debug message TRACE_WARNING("Wrong UDP header checksum!\r\n"); //Report an error return ERROR_WRONG_CHECKSUM; } } //Enter critical section osAcquireMutex(&socketMutex); //Loop through opened sockets for(i = 0; i < SOCKET_MAX_COUNT; i++) { //Point to the current socket socket = socketTable + i; //UDP socket found? if(socket->type != SOCKET_TYPE_DGRAM) continue; //Check whether the socket is bound to a particular interface if(socket->interface && socket->interface != interface) continue; //Check destination port number if(socket->localPort != ntohs(header->destPort)) continue; //Source port number filtering if(socket->remotePort && socket->remotePort != ntohs(header->srcPort)) continue; #if (IPV4_SUPPORT == ENABLED) //An IPv4 packet was received? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Destination IP address filtering if(socket->localIpAddr.length) { //An IPv4 address is expected if(socket->localIpAddr.length != sizeof(Ipv4Addr)) continue; //Filter out non-matching addresses if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr) continue; } //Source IP address filtering if(socket->remoteIpAddr.length) { //An IPv4 address is expected if(socket->remoteIpAddr.length != sizeof(Ipv4Addr)) continue; //Filter out non-matching addresses if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr) continue; } } else #endif #if (IPV6_SUPPORT == ENABLED) //An IPv6 packet was received? if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Destination IP address filtering if(socket->localIpAddr.length) { //An IPv6 address is expected if(socket->localIpAddr.length != sizeof(Ipv6Addr)) continue; //Filter out non-matching addresses if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr)) continue; } //Source IP address filtering if(socket->remoteIpAddr.length) { //An IPv6 address is expected if(socket->remoteIpAddr.length != sizeof(Ipv6Addr)) continue; //Filter out non-matching addresses if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr)) continue; } } else #endif //An invalid packet was received? { //This should never occur... continue; } //The current socket meets all the criteria break; } //Point to the payload offset += sizeof(UdpHeader); length -= sizeof(UdpHeader); //No matching socket found? if(i >= SOCKET_MAX_COUNT) { //Leave critical section osReleaseMutex(&socketMutex); //Invoke user callback, if any error = udpInvokeRxCallback(interface, pseudoHeader, header, buffer, offset); //Return status code return error; } //Empty receive queue? if(!socket->receiveQueue) { //Allocate a memory buffer to hold the data and the associated descriptor p = netBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Point to the newly created item queueItem = netBufferAt(p, 0); queueItem->buffer = p; //Add the newly created item to the queue socket->receiveQueue = queueItem; } else { //Memory allocation failed queueItem = NULL; } } else { //Point to the very first item queueItem = socket->receiveQueue; //Reach the last item in the receive queue for(i = 1; queueItem->next; i++) queueItem = queueItem->next; //Make sure the receive queue is not full if(i >= UDP_RX_QUEUE_SIZE) { //Leave critical section osReleaseMutex(&socketMutex); //Notify the calling function that the queue is full return ERROR_RECEIVE_QUEUE_FULL; } //Allocate a memory buffer to hold the data and the associated descriptor p = netBufferAlloc(sizeof(SocketQueueItem) + length); //Successful memory allocation? if(p != NULL) { //Add the newly created item to the queue queueItem->next = netBufferAt(p, 0); //Point to the newly created item queueItem = queueItem->next; queueItem->buffer = p; } else { //Memory allocation failed queueItem = NULL; } } //Failed to allocate memory? if(!queueItem) { //Leave critical section osReleaseMutex(&socketMutex); //Return error code return ERROR_OUT_OF_MEMORY; } //Initialize next field queueItem->next = NULL; //Record the source port number queueItem->srcPort = ntohs(header->srcPort); #if (IPV4_SUPPORT == ENABLED) //IPv4 remote address? if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Save the source IPv4 address queueItem->srcIpAddr.length = sizeof(Ipv4Addr); queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr; //Save the destination IPv4 address queueItem->destIpAddr.length = sizeof(Ipv4Addr); queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr; } #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 remote address? if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Save the source IPv6 address queueItem->srcIpAddr.length = sizeof(Ipv6Addr); queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr; //Save the destination IPv6 address queueItem->destIpAddr.length = sizeof(Ipv6Addr); queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr; } #endif //Offset to the payload queueItem->offset = sizeof(SocketQueueItem); //Copy the payload netBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length); //Notify user that data is available udpUpdateEvents(socket); //Leave critical section osReleaseMutex(&socketMutex); //Successful processing return NO_ERROR; }
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 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); }
void ndpTick(NetInterface *interface) { uint_t i; systime_t time; NdpCacheEntry *entry; //Get current time time = osGetSystemTime(); //Acquire exclusive access to Neighbor cache osAcquireMutex(&interface->ndpCacheMutex); //Go through Neighbor cache for(i = 0; i < NDP_CACHE_SIZE; i++) { //Point to the current entry entry = &interface->ndpCache[i]; //INCOMPLETE state? if(entry->state == NDP_STATE_INCOMPLETE) { //The Neighbor Solicitation timed out? if((time - entry->timestamp) >= entry->timeout) { //Increment retransmission counter entry->retransmitCount++; //Check whether the maximum number of retransmissions has been exceeded if(entry->retransmitCount < NDP_MAX_MULTICAST_SOLICIT) { //Retransmit a multicast Neighbor Solicitation message ndpSendNeighborSol(interface, &entry->ipAddr); //Save the time at which the message was sent entry->timestamp = time; //Set timeout value entry->timeout = NDP_RETRANS_TIMER; } else { //Drop packets that are waiting for address resolution ndpFlushQueuedPackets(interface, entry); //The entry should be deleted since address resolution has failed entry->state = NDP_STATE_NONE; } } } //REACHABLE state? else if(entry->state == NDP_STATE_REACHABLE) { //Periodically time out Neighbor cache entries if((time - entry->timestamp) >= entry->timeout) { //Save current time entry->timestamp = osGetSystemTime(); //Enter STALE state entry->state = NDP_STATE_STALE; } } //DELAY state? else if(entry->state == NDP_STATE_DELAY) { //Wait for the specified delay before sending the first probe if((time - entry->timestamp) >= entry->timeout) { //Send a unicast Neighbor Solicitation message ndpSendNeighborSol(interface, &entry->ipAddr); //Save the time at which the message was sent entry->timestamp = time; //Set timeout value entry->timeout = NDP_RETRANS_TIMER; //Switch to the PROBE state entry->state = NDP_STATE_PROBE; } } //PROBE state? else if(entry->state == NDP_STATE_PROBE) { //The request timed out? if((time - entry->timestamp) >= entry->timeout) { //Increment retransmission counter entry->retransmitCount++; //Check whether the maximum number of retransmissions has been exceeded if(entry->retransmitCount < NDP_MAX_UNICAST_SOLICIT) { //Send a unicast Neighbor Solicitation message ndpSendNeighborSol(interface, &entry->ipAddr); //Save the time at which the packet was sent entry->timestamp = time; //Set timeout value entry->timeout = NDP_RETRANS_TIMER; } else { //The entry should be deleted since the host is not reachable anymore entry->state = NDP_STATE_NONE; } } } } //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); }
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 ndpResolve(NetInterface *interface, const Ipv6Addr *ipAddr, MacAddr *macAddr) { NdpCacheEntry *entry; //Acquire exclusive access to Neighbor cache osAcquireMutex(&interface->ndpCacheMutex); //Search the ndpCacheMutex cache for the specified IPv6 address entry = ndpFindEntry(interface, ipAddr); //Check whether a matching entry has been found if(entry) { //Check the state of the Neighbor cache entry if(entry->state == NDP_STATE_INCOMPLETE) { //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //The address resolution is already in progress return ERROR_IN_PROGRESS; } else if(entry->state == NDP_STATE_STALE) { //Copy the MAC address associated with the specified IPv6 address *macAddr = entry->macAddr; //Start delay timer entry->timestamp = osGetSystemTime(); //Delay before sending the first probe entry->timeout = NDP_DELAY_FIRST_PROBE_TIME; //Switch to the DELAY state entry->state = NDP_STATE_DELAY; //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //Successful address resolution return NO_ERROR; } else { //Copy the MAC address associated with the specified IPv6 address *macAddr = entry->macAddr; //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //Successful address resolution return NO_ERROR; } } //If no entry exists, then create a new one entry = ndpCreateEntry(interface); //Any error to report? if(!entry) { //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //Report an error to the calling function return ERROR_OUT_OF_RESOURCES; } //Record the IPv6 address whose MAC address is unknown entry->ipAddr = *ipAddr; entry->macAddr = MAC_UNSPECIFIED_ADDR; //Reset retransmission counter entry->retransmitCount = 0; //No packet are pending in the transmit queue entry->queueSize = 0; //Send a multicast Neighbor Solicitation message ndpSendNeighborSol(interface, ipAddr); //Save the time at which the message was sent entry->timestamp = osGetSystemTime(); //Set timeout value entry->timeout = NDP_RETRANS_TIMER; //Enter INCOMPLETE state entry->state = NDP_STATE_INCOMPLETE; //Release exclusive access to Neighbor cache osReleaseMutex(&interface->ndpCacheMutex); //The address resolution is in progress return ERROR_IN_PROGRESS; }
error_t socketConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort) { error_t error; //Check input parameters if(!socket || !remoteIpAddr) return ERROR_INVALID_PARAMETER; #if (TCP_SUPPORT == ENABLED) //Connection-oriented socket? if(socket->type == SOCKET_TYPE_STREAM) { //Save port number and IP address of the remote host socket->remoteIpAddr = *remoteIpAddr; socket->remotePort = remotePort; //Select the source address and the relevant network interface //to use when establishing the connection error = ipSelectSourceAddr(&socket->interface, &socket->remoteIpAddr, &socket->localIpAddr); //Any error to report? if(error) return error; //Make sure the source address is valid if(ipIsUnspecifiedAddr(&socket->localIpAddr)) return ERROR_NOT_CONFIGURED; //Enter critical section osAcquireMutex(&socketMutex); //Establish TCP connection error = tcpConnect(socket); //Leave critical section osReleaseMutex(&socketMutex); } else #endif //Connectionless socket? if(socket->type == SOCKET_TYPE_DGRAM) { //Save port number and IP address of the remote host socket->remoteIpAddr = *remoteIpAddr; socket->remotePort = remotePort; //No error to report error = NO_ERROR; } //Raw socket? else if(socket->type == SOCKET_TYPE_RAW_IP) { //Save the IP address of the remote host socket->remoteIpAddr = *remoteIpAddr; //No error to report error = NO_ERROR; } //Socket type not supported... else { //Invalid socket type error = ERROR_INVALID_SOCKET; } //Return status code return error; }
void mib2Unlock(void) { //Leave critical section osReleaseMutex(&mib2Mutex); }
error_t socketReceiveEx(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort, IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags) { error_t error; //Make sure the socket handle is valid if(!socket) return ERROR_INVALID_PARAMETER; //Enter critical section osAcquireMutex(&socketMutex); #if (TCP_SUPPORT == ENABLED) //Connection-oriented socket? if(socket->type == SOCKET_TYPE_STREAM) { //Receive data error = tcpReceive(socket, data, size, received, flags); //Output parameters if(srcIpAddr) *srcIpAddr = socket->remoteIpAddr; if(srcPort) *srcPort = socket->remotePort; if(destIpAddr) *destIpAddr = socket->localIpAddr; } else #endif #if (UDP_SUPPORT == ENABLED) //Connectionless socket? if(socket->type == SOCKET_TYPE_DGRAM) { //Receive UDP datagram error = udpReceiveDatagram(socket, srcIpAddr, srcPort, destIpAddr, data, size, received, flags); } else #endif #if (RAW_SOCKET_SUPPORT == ENABLED) //Raw socket? if(socket->type == SOCKET_TYPE_RAW_IP) { //Receive a raw IP packet error = rawSocketReceiveIpPacket(socket, srcIpAddr, destIpAddr, data, size, received, flags); } else if(socket->type == SOCKET_TYPE_RAW_ETH) { //Receive a raw Ethernet packet error = rawSocketReceiveEthPacket(socket, data, size, received, flags); } else #endif //Socket type not supported... { //No data can be read *received = 0; //Invalid socket type error = ERROR_INVALID_SOCKET; } //Leave critical section osReleaseMutex(&socketMutex); //Return status code return error; }