void ipv6FragTick(NetInterface *interface) { error_t error; uint_t i; time_t time; Ipv6HoleDesc *hole; //Acquire exclusive access to the reassembly queue osMutexAcquire(interface->ipv6FragQueueMutex); //Get current time time = osGetTickCount(); //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 osMutexRelease(interface->ipv6FragQueueMutex); }
error_t tlsSaveSession(const TlsContext *context, TlsSession *session) { //Check parameters if(context == NULL || session == NULL) return ERROR_INVALID_PARAMETER; //Invalid session parameters? if(!context->sessionIdLength || !context->cipherSuite) return ERROR_FAILURE; //Save session identifier memcpy(session->id, context->sessionId, context->sessionIdLength); session->idLength = context->sessionIdLength; //Get current time session->timestamp = osGetTickCount(); //Negotiated cipher suite and compression method session->cipherSuite = context->cipherSuite; session->compressionMethod = context->compressionMethod; //Save master secret memcpy(session->masterSecret, context->masterSecret, 48); //Successful processing return NO_ERROR; }
void mldLinkChangeEvent(NetInterface *interface) { uint_t i; systime_t time; Ipv6FilterEntry *entry; //Get current time time = osGetTickCount(); //Acquire exclusive access to the IPv6 filter table osMutexAcquire(interface->ipv6FilterMutex); //Link up event? if(interface->linkState) { //Loop through filter table entries for(i = 0; i < interface->ipv6FilterSize; i++) { //Point to the current entry entry = &interface->ipv6Filter[i]; //The link-scope all-nodes address (FF02::1) is handled as a special //case. The host starts in Idle Listener state for that address on //every interface and never transitions to another state if(ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) continue; //Send an unsolicited Multicast Listener Report message for that group mldSendListenerReport(interface, &entry->addr); //Set flag entry->flag = TRUE; //Start timer entry->timer = time + MLD_UNSOLICITED_REPORT_INTERVAL; //Enter the Delaying Listener state entry->state = MLD_STATE_DELAYING_LISTENER; } } //Link down event? else { //Loop through filter table entries for(i = 0; i < interface->ipv6FilterSize; i++) { //Point to the current entry entry = &interface->ipv6Filter[i]; //Clear flag entry->flag = FALSE; //Enter the Idle Listener state entry->state = MLD_STATE_IDLE_LISTENER; } } //Release exclusive access to the IPv6 filter table osMutexRelease(interface->ipv6FilterMutex); }
bool_t osTimerElapsed(OsTimer *timer) { if(!timer->running) return FALSE; if(timeCompare(osGetTickCount(), timer->startTime + timer->interval) >= 0) return TRUE; else return FALSE; }
error_t mldStartListening(NetInterface *interface, Ipv6FilterEntry *entry) { //The link-scope all-nodes address (FF02::1) is handled as a special //case. The host starts in Idle Listener state for that address on //every interface and never transitions to another state if(ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) { //Clear flag entry->flag = FALSE; //Enter the Idle Listener state entry->state = MLD_STATE_IDLE_LISTENER; } else { //Link is up? if(interface->linkState) { //Send a Multicast Listener Report message for the group on the interface mldSendListenerReport(interface, &entry->addr); //Set flag entry->flag = TRUE; //Start timer entry->timer = osGetTickCount() + MLD_UNSOLICITED_REPORT_INTERVAL; //Enter the Delaying Listener state entry->state = MLD_STATE_DELAYING_LISTENER; } //Link is down? else { //Clear flag entry->flag = FALSE; //Enter the Idle Listener state entry->state = MLD_STATE_IDLE_LISTENER; } } //Successful processing return NO_ERROR; }
void mldTick(NetInterface *interface) { uint_t i; systime_t time; Ipv6FilterEntry *entry; //Get current time time = osGetTickCount(); //Acquire exclusive access to the IPv6 filter table osMutexAcquire(interface->ipv6FilterMutex); //Loop through filter table entries for(i = 0; i < interface->ipv6FilterSize; i++) { //Point to the current entry entry = &interface->ipv6Filter[i]; //Delaying Listener state? if(entry->state == MLD_STATE_DELAYING_LISTENER) { //Timer expired? if(timeCompare(time, entry->timer) >= 0) { //Send a Multicast Listener Report message mldSendListenerReport(interface, &entry->addr); //Set flag entry->flag = TRUE; //Switch to the Idle Listener state entry->state = MLD_STATE_IDLE_LISTENER; } } } //Release exclusive access to the IPv6 filter table osMutexRelease(interface->ipv6FilterMutex); }
void mldProcessListenerQuery(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit) { uint_t i; size_t length; systime_t time; systime_t maxRespDelay; MldMessage *message; Ipv6FilterEntry *entry; //Retrieve the length of the MLD message length = chunkedBufferGetLength(buffer) - offset; //The message must be at least 24 octets long if(length < sizeof(MldMessage)) return; //Point to the beginning of the MLD message message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose mldDumpMessage(message); //Make sure the source address of the message is a valid link-local address if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) return; //Check the Hop Limit field if(hopLimit != MLD_HOP_LIMIT) return; //Get current time time = osGetTickCount(); //The Max Resp Delay field specifies the maximum time allowed //before sending a responding report maxRespDelay = message->maxRespDelay * 10; //Acquire exclusive access to the IPv6 filter table osMutexAcquire(interface->ipv6FilterMutex); //Loop through filter table entries for(i = 0; i < interface->ipv6FilterSize; i++) { //Point to the current entry entry = &interface->ipv6Filter[i]; //The link-scope all-nodes address (FF02::1) is handled as a special //case. The host starts in Idle Listener state for that address on //every interface and never transitions to another state if(ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) continue; //A General Query is used to learn which multicast addresses have listeners //on an attached link. A Multicast-Address-Specific Query is used to learn //if a particular multicast address has any listeners on an attached link if(ipv6CompAddr(&message->multicastAddr, &IPV6_UNSPECIFIED_ADDR) || ipv6CompAddr(&message->multicastAddr, &entry->addr)) { //Delaying Listener state? if(entry->state == MLD_STATE_DELAYING_LISTENER) { //The timer has not yet expired? if(timeCompare(time, entry->timer) < 0) { //If a timer for the address is already running, it is reset to //the new random value only if the requested Max Response Delay //is less than the remaining value of the running timer if(maxRespDelay < (entry->timer - time)) { //Restart delay timer entry->timer = time + mldRand(maxRespDelay); } } } //Idle Listener state? else if(entry->state == MLD_STATE_IDLE_LISTENER) { //Switch to the Delaying Listener state entry->state = MLD_STATE_DELAYING_LISTENER; //Delay the response by a random amount of time entry->timer = time + mldRand(maxRespDelay); } } } //Release exclusive access to the IPv6 filter table osMutexRelease(interface->ipv6FilterMutex); }
error_t ndpResolve(NetInterface *interface, const Ipv6Addr *ipAddr, MacAddr *macAddr) { NdpCacheEntry *entry; //Acquire exclusive access to Neighbor cache osMutexAcquire(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 osMutexRelease(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 = osGetTickCount(); //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 osMutexRelease(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 osMutexRelease(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 osMutexRelease(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 = osGetTickCount(); //Set timeout value entry->timeout = NDP_RETRANS_TIMER; //Enter INCOMPLETE state entry->state = NDP_STATE_INCOMPLETE; //Release exclusive access to Neighbor cache osMutexRelease(interface->ndpCacheMutex); //The address resolution is in progress return ERROR_IN_PROGRESS; }
void arpTick(NetInterface *interface) { uint_t i; time_t time; ArpCacheEntry *entry; //Get current time time = osGetTickCount(); //Acquire exclusive access to ARP cache osMutexAcquire(interface->arpCacheMutex); //Go through ARP cache for(i = 0; i < ARP_CACHE_SIZE; i++) { //Point to the current entry entry = &interface->arpCache[i]; //INCOMPLETE state? if(entry->state == ARP_STATE_INCOMPLETE) { //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 < ARP_MAX_REQUESTS) { //Retransmit ARP request arpSendRequest(interface, interface->ipv4Config.addr, entry->ipAddr, &MAC_BROADCAST_ADDR); //Save the time at which the packet was sent entry->timestamp = time; //Set timeout value entry->timeout = ARP_REQUEST_TIMEOUT; } else { //Drop packets that are waiting for address resolution arpFlushQueuedPackets(interface, entry); //The entry should be deleted since address resolution has failed entry->state = ARP_STATE_NONE; } } } //REACHABLE state? else if(entry->state == ARP_STATE_REACHABLE) { //Periodically time out ARP cache entries if((time - entry->timestamp) >= entry->timeout) { //Save current time entry->timestamp = osGetTickCount(); //Enter STALE state entry->state = ARP_STATE_STALE; } } //DELAY state? else if(entry->state == ARP_STATE_DELAY) { //Wait for the specified delay before sending the first probe if((time - entry->timestamp) >= entry->timeout) { //Send a point-to-point ARP request to the host arpSendRequest(interface, interface->ipv4Config.addr, entry->ipAddr, &entry->macAddr); //Save the time at which the packet was sent entry->timestamp = time; //Set timeout value entry->timeout = ARP_PROBE_TIMEOUT; //Switch to the PROBE state entry->state = ARP_STATE_PROBE; } } //PROBE state? else if(entry->state == ARP_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 < ARP_MAX_PROBES) { //Send a point-to-point ARP request to the host arpSendRequest(interface, interface->ipv4Config.addr, entry->ipAddr, &entry->macAddr); //Save the time at which the packet was sent entry->timestamp = time; //Set timeout value entry->timeout = ARP_PROBE_TIMEOUT; } else { //The entry should be deleted since the host is not reachable anymore entry->state = ARP_STATE_NONE; } } } } //Release exclusive access to ARP cache osMutexRelease(interface->arpCacheMutex); }
void tcpChargenConnectionTask(void *param) { error_t error; //size_t i; size_t n; //size_t offset; size_t byteCount; systime_t startTime; systime_t duration; ChargenServiceContext *context; //Get a pointer to the context context = (ChargenServiceContext *) param; //Get current time startTime = osGetTickCount(); //Initialize counters byteCount = 0; //offset = 0; //Once a connection is established a stream of data is sent out //the connection (and any data received is thrown away). This //continues until the calling user terminates the connection while(1) { //Format output data /*for(i = 0; i < CHARGEN_BUFFER_SIZE; i += 95) { //Calculate the length of the current line n = min(CHARGEN_BUFFER_SIZE - i, 95); //Copy character pattern memcpy(context->buffer + i, pattern + offset, n); } //Update offset offset += CHARGEN_BUFFER_SIZE + 95 - i; //Wrap around if necessary if(offset >= 95) offset = 0;*/ //Send data error = socketSend(context->socket, context->buffer, CHARGEN_BUFFER_SIZE, &n, 0); //Any error to report? if(error) break; //Total number of bytes sent byteCount += n; } //Graceful shutdown socketShutdown(context->socket, SOCKET_SD_BOTH); //Compute total duration duration = osGetTickCount() - startTime; //Avoid division by zero... if(!duration) duration = 1; //Debug message TRACE_INFO("Chargen service: %" PRIuSIZE " bytes " "sent in %" PRIu32 " ms (%" PRIu32 " kBps, %" PRIu32 " kbps)\r\n", byteCount, duration, byteCount / duration, (byteCount * 8) / duration); //Close socket socketClose(context->socket); //Release previously allocated memory osMemFree(context); //Kill ourselves osTaskDelete(NULL); }
//The following functions are deprecated void osTimerStart(OsTimer *timer, systime_t delay) { timer->startTime = osGetTickCount(); timer->interval = delay; timer->running = TRUE; }
Ipv6FragDesc *ipv6SearchFragQueue(NetInterface *interface, Ipv6Header *packet, Ipv6FragmentHeader *header) { error_t error; uint_t i; Ipv6Header *datagram; Ipv6FragDesc *frag; Ipv6HoleDesc *hole; //Search for a matching IP datagram being reassembled for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue frag = &interface->ipv6FragQueue[i]; //Check whether the current entry is used? if(frag->buffer.chunkCount > 0) { //Point to the corresponding datagram datagram = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, 0); //Check source and destination addresses if(!ipv6CompAddr(&datagram->srcAddr, &packet->srcAddr)) continue; if(!ipv6CompAddr(&datagram->destAddr, &packet->destAddr)) continue; //Compare fragment identification fields if(frag->identification != header->identification) continue; //A matching entry has been found in the reassembly queue return frag; } } //If the current packet does not match an existing entry //in the reassembly queue, then create a new entry for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++) { //Point to the current entry in the reassembly queue frag = &interface->ipv6FragQueue[i]; //The current entry is free? if(!frag->buffer.chunkCount) { //Number of chunks that comprise the reassembly buffer frag->buffer.maxChunkCount = arraysize(frag->buffer.chunk); //Allocate sufficient memory to hold the IPv6 header and //the first hole descriptor error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, MEM_POOL_BUFFER_SIZE + sizeof(Ipv6HoleDesc)); //Failed to allocate memory? if(error) { //Clean up side effects chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0); //Exit immediately return NULL; } //Initial length of the reconstructed datagram frag->unfragPartLength = sizeof(Ipv6Header); frag->fragPartLength = 0; //Fix the length of the first chunk frag->buffer.chunk[0].length = frag->unfragPartLength; //Copy IPv6 header from the incoming fragment chunkedBufferWrite((ChunkedBuffer *) &frag->buffer, 0, packet, frag->unfragPartLength); //Save current time frag->timestamp = osGetTickCount(); //Record fragment identification field frag->identification = header->identification; //Create a new entry in the hole descriptor list frag->firstHole = 0; //Point to first hole descriptor hole = ipv6FindHole(frag, frag->firstHole); //The entry describes the datagram as being completely missing hole->first = 0; hole->last = IPV6_INFINITY; hole->next = IPV6_INFINITY; //Dump hole descriptor list ipv6DumpHoleList(frag); //Return the matching fragment descriptor return frag; } } //The reassembly queue is full return NULL; }
error_t ping(NetInterface *interface, const IpAddr *ipAddr, time_t timeout, time_t *rtt) { error_t error; uint_t i; size_t length; uint16_t identifier; uint16_t sequenceNumber; time_t startTime; time_t roundTripTime; Socket *socket; IcmpEchoMessage *message; //Debug message TRACE_INFO("Pinging %s with 64 bytes of data...\r\n", ipAddrToString(ipAddr, NULL)); //Length of the complete ICMP message including header and data length = sizeof(IcmpEchoMessage) + PING_DATA_SIZE; //Allocate memory buffer to hold an ICMP message message = osMemAlloc(length); //Failed to allocate memory? if(!message) return ERROR_OUT_OF_MEMORY; //Identifier field is used to help matching requests and replies identifier = rand(); //Sequence Number field is increment each time an Echo Request is sent sequenceNumber = osAtomicInc16(&pingSequenceNumber); //Format ICMP Echo Request message message->type = ICMP_TYPE_ECHO_REQUEST; message->code = 0; message->checksum = 0; message->identifier = identifier; message->sequenceNumber = sequenceNumber; //Copy data for(i = 0; i < PING_DATA_SIZE; i++) message->data[i] = i; #if (IPV4_SUPPORT == ENABLED) //Target address is an IPv4 address? if(ipAddr->length == sizeof(Ipv4Addr)) { Ipv4Addr srcIpAddr; //Select the source IPv4 address and the relevant network //interface to use when pinging the specified host error = ipv4SelectSourceAddr(&interface, ipAddr->ipv4Addr, &srcIpAddr); //Any error to report? if(error) { //Free previously allocated memory osMemFree(message); //Return the corresponding error code return error; } //ICMP Echo Request message message->type = ICMP_TYPE_ECHO_REQUEST; //Message checksum calculation message->checksum = ipCalcChecksum(message, length); //Open a raw socket socket = socketOpen(SOCKET_TYPE_RAW, SOCKET_PROTOCOL_ICMP); } else #endif #if (IPV6_SUPPORT == ENABLED) //Target address is an IPv6 address? if(ipAddr->length == sizeof(Ipv6Addr)) { Ipv6PseudoHeader pseudoHeader; //Select the source IPv6 address and the relevant network //interface to use when pinging the specified host error = ipv6SelectSourceAddr(&interface, &ipAddr->ipv6Addr, &pseudoHeader.srcAddr); //Any error to report? if(error) { //Free previously allocated memory osMemFree(message); //Return the corresponding error code return error; } //ICMPv6 Echo Request message message->type = ICMPV6_TYPE_ECHO_REQUEST; //Format IPv6 pseudo header pseudoHeader.destAddr = ipAddr->ipv6Addr; pseudoHeader.length = htonl(length); pseudoHeader.reserved = 0; pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; //Message checksum calculation message->checksum = ipCalcUpperLayerChecksum( &pseudoHeader, sizeof(Ipv6PseudoHeader), message, length); //Open a raw socket socket = socketOpen(SOCKET_TYPE_RAW, SOCKET_PROTOCOL_ICMPV6); } else #endif //Target address is not valid? { //Free previously allocated memory osMemFree(message); //Report an error return ERROR_INVALID_ADDRESS; } //Failed to open socket? if(!socket) { //Free previously allocated memory osMemFree(message); //Report an error return ERROR_OPEN_FAILED; } //Associate the newly created socket with the relevant interface error = socketBindToInterface(socket, interface); //Unable to bind the socket to the desired interface? if(error) { //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //Return status code return error; } //Connect the socket to the target host error = socketConnect(socket, ipAddr, 0); //Any error to report? if(error) { //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //Return status code return error; } //Send Echo Request message error = socketSend(socket, message, length, NULL, 0); //Failed to send message ? if(error) { //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //Return status code return error; } //Save the time at which the request was sent startTime = osGetTickCount(); //Timeout value exceeded? while((osGetTickCount() - startTime) < timeout) { //Adjust receive timeout error = socketSetTimeout(socket, timeout); //Any error to report? if(error) break; //Wait for an incoming ICMP message error = socketReceive(socket, message, sizeof(IcmpEchoMessage) + PING_DATA_SIZE, &length, 0); //Any error to report? if(error) break; //Check message length if(length != (sizeof(IcmpEchoMessage) + PING_DATA_SIZE)) continue; //Verify message type if(ipAddr->length == sizeof(Ipv4Addr) && message->type != ICMP_TYPE_ECHO_REPLY) continue; if(ipAddr->length == sizeof(Ipv6Addr) && message->type != ICMPV6_TYPE_ECHO_REPLY) continue; //Response identifier matches request identifier? if(message->identifier != identifier) continue; //Make sure the sequence number is correct if(message->sequenceNumber != sequenceNumber) continue; //Loop through data field for(i = 0; i < PING_DATA_SIZE; i++) { //Compare received data against expected data if(message->data[i] != i) break; } //Valid Echo Reply message received? if(i == PING_DATA_SIZE) { //Calculate round-trip time roundTripTime = osGetTickCount() - startTime; //Debug message TRACE_INFO("Echo received (round-trip time = %ums)...\r\n", roundTripTime); //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //Return round-trip time if(rtt) *rtt = roundTripTime; //No error to report return NO_ERROR; } } //Debug message TRACE_INFO("No echo received!\r\n"); //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //No Echo Reply received from host... return ERROR_NO_RESPONSE; }
void tcpTick(void) { error_t error; uint_t i; uint_t n; uint_t u; //Enter critical section osMutexAcquire(socketMutex); //Loop through opened sockets for(i = 0; i < SOCKET_MAX_COUNT; i++) { //Shortcut to the current socket Socket *socket = socketTable + i; //Check socket type if(socket->type != SOCKET_TYPE_STREAM) continue; //Check the current state of the TCP state machine if(socket->state == TCP_STATE_CLOSED) continue; //Is there any packet in the retransmission queue? if(socket->retransmitQueue != NULL) { //Retransmission timeout? if(osTimerElapsed(&socket->retransmitTimer)) { //When a TCP sender detects segment loss using the retransmission //timer and the given segment has not yet been resent by way of //the retransmission timer, the value of ssthresh must be updated if(!socket->retransmitCount) { //Amount of data that has been sent but not yet acknowledged uint_t flightSize = socket->sndNxt - socket->sndUna; //Adjust ssthresh value socket->ssthresh = max(flightSize / 2, 2 * socket->mss); } //Furthermore, upon a timeout cwnd must be set to no more than //the loss window, LW, which equals 1 full-sized segment socket->cwnd = min(TCP_LOSS_WINDOW * socket->mss, socket->txBufferSize); //Make sure the maximum number of retransmissions has not been reached if(socket->retransmitCount < TCP_MAX_RETRIES) { //Debug message TRACE_INFO("%s: TCP segment retransmission #%u (%u data bytes)...\r\n", timeFormat(osGetTickCount()), socket->retransmitCount + 1, socket->retransmitQueue->length); //Retransmit the earliest segment that has not been //acknowledged by the TCP receiver tcpRetransmitSegment(socket); //Use exponential back-off algorithm to calculate the new RTO socket->rto = min(socket->rto * 2, TCP_MAX_RTO); //Restart retransmission timer osTimerStart(&socket->retransmitTimer, socket->rto); //Increment retransmission counter socket->retransmitCount++; } else { //The maximum number of retransmissions has been exceeded tcpChangeState(socket, TCP_STATE_CLOSED); //Turn off the retransmission timer osTimerStop(&socket->retransmitTimer); } //TCP must use Karn's algorithm for taking RTT samples. That is, RTT //samples must not be made using segments that were retransmitted socket->rttBusy = FALSE; } } //Check the current state of the TCP state machine if(socket->state == TCP_STATE_CLOSED) continue; //The persist timer is used when the remote host advertises //a window size of zero if(!socket->sndWnd && socket->wndProbeInterval) { //Time to send a new probe? if(osTimerElapsed(&socket->persistTimer)) { //Make sure the maximum number of retransmissions has not been reached if(socket->wndProbeCount < TCP_MAX_RETRIES) { //Debug message TRACE_INFO("%s: TCP zero window probe #%u...\r\n", timeFormat(osGetTickCount()), socket->wndProbeCount + 1); //Zero window probes usually have the sequence number one less than expected tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt - 1, socket->rcvNxt, 0, FALSE); //The interval between successive probes should be increased exponentially socket->wndProbeInterval = min(socket->wndProbeInterval * 2, TCP_MAX_PROBE_INTERVAL); //Restart the persist timer osTimerStart(&socket->persistTimer, socket->wndProbeInterval); //Increment window probe counter socket->wndProbeCount++; } else { //Enter CLOSED state tcpChangeState(socket, TCP_STATE_CLOSED); } } } //To avoid a deadlock, it is necessary to have a timeout to force //transmission of data, overriding the SWS avoidance algorithm. In //practice, this timeout should seldom occur (see RFC 1122 4.2.3.4) if(socket->state == TCP_STATE_ESTABLISHED || socket->state == TCP_STATE_CLOSE_WAIT) { //The override timeout occurred? if(socket->sndUser && osTimerElapsed(&socket->overrideTimer)) { //The amount of data that can be sent at any given time is //limited by the receiver window and the congestion window n = min(socket->sndWnd, socket->cwnd); n = min(n, socket->txBufferSize); //Retrieve the size of the usable window u = n - (socket->sndNxt - socket->sndUna); //Send as much data as possible while(socket->sndUser > 0) { //The usable window size may become zero or negative, //preventing packet transmission if((int_t) u <= 0) break; //Calculate the number of bytes to send at a time n = min(u, socket->sndUser); n = min(n, socket->mss); //Send TCP segment error = tcpSendSegment(socket, TCP_FLAG_PSH | TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, n, TRUE); //Failed to send TCP segment? if(error) break; //Advance SND.NXT pointer socket->sndNxt += n; //Adjust the number of bytes buffered but not yet sent socket->sndUser -= n; } //Check whether the transmitter can accept more data tcpUpdateEvents(socket); //Restart override timer if necessary if(socket->sndUser > 0) osTimerStart(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT); } } //The FIN-WAIT-2 timer prevents the connection //from staying in the FIN-WAIT-2 state forever if(socket->state == TCP_STATE_FIN_WAIT_2) { //Maximum FIN-WAIT-2 time has elapsed? if(osTimerElapsed(&socket->finWait2Timer)) { //Debug message TRACE_WARNING("TCP FIN-WAIT-2 timer elapsed...\r\n"); //Enter CLOSED state tcpChangeState(socket, TCP_STATE_CLOSED); } } //TIME-WAIT timer if(socket->state == TCP_STATE_TIME_WAIT) { //2MSL time has elapsed? if(osTimerElapsed(&socket->timeWaitTimer)) { //Debug message TRACE_WARNING("TCP 2MSL timer elapsed (socket %u)...\r\n", i); //Enter CLOSED state tcpChangeState(socket, TCP_STATE_CLOSED); //Dispose the socket if the user does not have the ownership anymore if(!socket->ownedFlag) { //Delete the TCB tcpDeleteControlBlock(socket); //Mark the socket as closed socket->type = SOCKET_TYPE_UNUSED; } } } } //Leave critical section osMutexRelease(socketMutex); }
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; //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 osMutexAcquire(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 = osGetTickCount(); //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 = osGetTickCount(); //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 = osGetTickCount(); //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 = osGetTickCount(); //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 = osGetTickCount(); //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 osMutexRelease(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) && !ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.globalAddr)) { //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 osMutexAcquire(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 = osGetTickCount(); //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 = osGetTickCount(); //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 = osGetTickCount(); //Enter the STALE state entry->state = NDP_STATE_STALE; } } } //Release exclusive access to Neighbor cache osMutexRelease(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 = osGetTickCount(); //Acquire exclusive access to Neighbor cache osMutexAcquire(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 = osGetTickCount(); //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 osMutexRelease(interface->ndpCacheMutex); }
error_t arpResolve(NetInterface *interface, Ipv4Addr ipAddr, MacAddr *macAddr) { ArpCacheEntry *entry; //Acquire exclusive access to ARP cache osMutexAcquire(interface->arpCacheMutex); //Search the ARP cache for the specified IPv4 address entry = arpFindEntry(interface, ipAddr); //Check whether a matching entry has been found if(entry) { //Check the state of the ARP entry if(entry->state == ARP_STATE_INCOMPLETE) { //Release exclusive access to ARP cache osMutexRelease(interface->arpCacheMutex); //The address resolution is already in progress return ERROR_IN_PROGRESS; } else if(entry->state == ARP_STATE_STALE) { //Copy the MAC address associated with the specified IPv4 address *macAddr = entry->macAddr; //Start delay timer entry->timestamp = osGetTickCount(); //Delay before sending the first probe entry->timeout = ARP_DELAY_FIRST_PROBE_TIME; //Switch to the DELAY state entry->state = ARP_STATE_DELAY; //Release exclusive access to ARP cache osMutexRelease(interface->arpCacheMutex); //Successful address resolution return NO_ERROR; } else { //Copy the MAC address associated with the specified IPv4 address *macAddr = entry->macAddr; //Release exclusive access to ARP cache osMutexRelease(interface->arpCacheMutex); //Successful address resolution return NO_ERROR; } } //If no entry exists, then create a new one entry = arpCreateEntry(interface); //Any error to report? if(!entry) { //Release exclusive access to ARP cache osMutexRelease(interface->arpCacheMutex); //Report an error to the calling function return ERROR_OUT_OF_RESOURCES; } //Record the IPv4 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 an ARP request arpSendRequest(interface, interface->ipv4Config.addr, entry->ipAddr, &MAC_BROADCAST_ADDR); //Save the time at which the packet was sent entry->timestamp = osGetTickCount(); //Set timeout value entry->timeout = ARP_REQUEST_TIMEOUT; //Enter INCOMPLETE state entry->state = ARP_STATE_INCOMPLETE; //Release exclusive access to ARP cache osMutexRelease(interface->arpCacheMutex); //The address resolution is in progress return ERROR_IN_PROGRESS; }
void dnsProcessResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const ChunkedBuffer *buffer, size_t offset, void *params) { uint_t i; uint_t j; size_t n; size_t pos; size_t length; DnsHeader *message; DnsQuestion *question; DnsResourceRecord *resourceRecord; DnsCacheEntry *entry; //Retrieve the length of the DNS message length = chunkedBufferGetLength(buffer) - offset; //Ensure the DNS message is valid if(length < sizeof(DnsHeader)) return; if(length > DNS_MESSAGE_MAX_SIZE) return; //Point to the DNS message header message = chunkedBufferAt(buffer, offset); //Sanity check if(!message) return; //Debug message TRACE_INFO("DNS message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message dnsDumpMessage(message, length); //Acquire exclusive access to the DNS cache osMutexAcquire(dnsCacheMutex); //Loop through DNS cache entries for(i = 0; i < DNS_CACHE_SIZE; i++) { //Point to the current entry entry = &dnsCache[i]; //DNS name resolution in progress? if(entry->state == DNS_STATE_IN_PROGRESS && entry->protocol == HOST_NAME_RESOLVER_DNS) { //Check destination port number if(entry->port == ntohs(udpHeader->destPort)) { //Compare identifier against expected one if(ntohs(message->id) != entry->id) break; //Check message type if(!message->qr) break; //The DNS message shall contain one question if(ntohs(message->qdcount) != 1) break; //Point to the first question pos = sizeof(DnsHeader); //Parse domain name n = dnsParseName(message, length, pos, NULL, 0); //Invalid name? if(!n) break; //Malformed mDNS message? if((n + sizeof(DnsQuestion)) > length) break; //Compare domain name if(!dnsCompareName(message, length, pos, entry->name, 0)) break; //Point to the corresponding entry question = DNS_GET_QUESTION(message, n); //Check the class of the query if(ntohs(question->qclass) != DNS_RR_CLASS_IN) break; //Check the type of the query if(entry->type == HOST_TYPE_IPV4 && ntohs(question->qtype) != DNS_RR_TYPE_A) break; if(entry->type == HOST_TYPE_IPV6 && ntohs(question->qtype) != DNS_RR_TYPE_AAAA) break; //Make sure recursion is available if(!message->ra) { //The entry should be deleted since name resolution has failed dnsDeleteEntry(entry); //Exit immediately break; } //Check return code if(message->rcode != DNS_RCODE_NO_ERROR) { //The entry should be deleted since name resolution has failed dnsDeleteEntry(entry); //Exit immediately break; } //Point to the first answer pos = n + sizeof(DnsQuestion); //Parse answer resource records for(j = 0; j < ntohs(message->ancount); j++) { //Parse domain name pos = dnsParseName(message, length, pos, NULL, 0); //Invalid name? if(!pos) break; //Point to the associated resource record resourceRecord = DNS_GET_RESOURCE_RECORD(message, pos); //Point to the resource data pos += sizeof(DnsResourceRecord); //Make sure the resource record is valid if(pos >= length) break; if((pos + ntohs(resourceRecord->rdlength)) > length) break; #if (IPV4_SUPPORT == ENABLED) //IPv4 address expected? if(entry->type == HOST_TYPE_IPV4) { //A resource record found? if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_A) { //Verify the length of the data field if(ntohs(resourceRecord->rdlength) == sizeof(Ipv4Addr)) { //Copy the IPv4 address entry->ipAddr.length = sizeof(Ipv4Addr); ipv4CopyAddr(&entry->ipAddr.ipv4Addr, resourceRecord->rdata); //Save current time entry->timestamp = osGetTickCount(); //Save TTL value entry->timeout = ntohl(resourceRecord->ttl) * 1000; //Limit the lifetime of the DNS cache entries entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME); //Unregister UDP callback function udpDetachRxCallback(interface, entry->port); //Host name successfully resolved entry->state = DNS_STATE_RESOLVED; //Exit immediately break; } } } #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 address expected? if(entry->type == HOST_TYPE_IPV6) { //AAAA resource record found? if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_AAAA) { //Verify the length of the data field if(ntohs(resourceRecord->rdlength) == sizeof(Ipv6Addr)) { //Copy the IPv6 address entry->ipAddr.length = sizeof(Ipv6Addr); ipv6CopyAddr(&entry->ipAddr.ipv6Addr, resourceRecord->rdata); //Save current time entry->timestamp = osGetTickCount(); //Save TTL value entry->timeout = ntohl(resourceRecord->ttl) * 1000; //Limit the lifetime of the DNS cache entries entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME); //Unregister UDP callback function udpDetachRxCallback(interface, entry->port); //Host name successfully resolved entry->state = DNS_STATE_RESOLVED; //Exit immediately break; } } } #endif //Point to the next resource record pos += ntohs(resourceRecord->rdlength); } //We are done break; } } } //Release exclusive access to the DNS cache osMutexRelease(dnsCacheMutex); }
void arpProcessReply(NetInterface *interface, ArpPacket *arpReply) { ArpCacheEntry *entry; //Debug message TRACE_INFO("ARP Reply received...\r\n"); //Check sender protocol address if(arpReply->spa == IPV4_UNSPECIFIED_ADDR) return; if(ipv4IsMulticastAddr(arpReply->spa)) return; if(ipv4IsBroadcastAddr(interface, arpReply->spa)) return; //Check sender hardware address if(macCompAddr(&arpReply->sha, &MAC_UNSPECIFIED_ADDR)) return; if(macCompAddr(&arpReply->sha, &MAC_BROADCAST_ADDR)) return; //Acquire exclusive access to ARP cache osMutexAcquire(interface->arpCacheMutex); //Search the ARP cache for the specified IPv4 address entry = arpFindEntry(interface, arpReply->spa); //A matching ARP entry has been found if(entry) { //Check current state if(entry->state == ARP_STATE_INCOMPLETE) { //Record the corresponding MAC address entry->macAddr = arpReply->sha; //Send all the packets that are pending for transmission arpSendQueuedPackets(interface, entry); //Save current time entry->timestamp = osGetTickCount(); //The validity of the ARP entry is limited in time entry->timeout = ARP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = ARP_STATE_REACHABLE; } else if(entry->state == ARP_STATE_REACHABLE) { //Different link-layer address than cached? if(!macCompAddr(&arpReply->sha, &entry->macAddr)) { //Enter STALE state entry->state = ARP_STATE_STALE; } } else if(entry->state == ARP_STATE_PROBE) { //Record IPv4/MAC address pair entry->ipAddr = arpReply->spa; entry->macAddr = arpReply->sha; //Save current time entry->timestamp = osGetTickCount(); //The validity of the ARP entry is limited in time entry->timeout = ARP_REACHABLE_TIME; //Switch to the REACHABLE state entry->state = ARP_STATE_REACHABLE; } } //Release exclusive access to ARP cache osMutexRelease(interface->arpCacheMutex); }
error_t dnsResolve(NetInterface *interface, const char_t *name, HostType type, IpAddr *ipAddr) { error_t error; systime_t delay; DnsCacheEntry *entry; //Debug message TRACE_INFO("Resolving host name %s (DNS resolver)...\r\n", name); //Acquire exclusive access to the DNS cache osMutexAcquire(dnsCacheMutex); //Search the DNS cache for the specified host name entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_DNS); //Check whether a matching entry has been found if(entry) { //Host name already resolved? if(entry->state == DNS_STATE_RESOLVED || entry->state == DNS_STATE_PERMANENT) { //Return the corresponding IP address *ipAddr = entry->ipAddr; //Successful host name resolution error = NO_ERROR; } else { //Host name resolution is in progress... error = ERROR_IN_PROGRESS; } } else { //If no entry exists, then create a new one entry = dnsCreateEntry(); //Record the host name whose IP address is unknown strcpy(entry->name, name); //Initialize DNS cache entry entry->type = type; entry->protocol = HOST_NAME_RESOLVER_DNS; entry->interface = interface; //Select primary DNS server entry->dnsServerNum = 0; //Get an ephemeral port number entry->port = socketGetEphemeralPort(); //An identifier is used by the DNS client to match replies //with corresponding requests entry->id = tcpIpStackGetRand(); //Callback function to be called when a DNS response is received error = udpAttachRxCallback(interface, entry->port, dnsProcessResponse, NULL); //Check status code if(!error) { //Initialize retransmission counter entry->retransmitCount = DNS_CLIENT_MAX_RETRIES; //Send DNS query error = dnsSendQuery(entry); //DNS message successfully sent? if(!error) { //Save the time at which the query message was sent entry->timestamp = osGetTickCount(); //Set timeout value entry->timeout = DNS_CLIENT_INIT_TIMEOUT; entry->maxTimeout = DNS_CLIENT_MAX_TIMEOUT; //Decrement retransmission counter entry->retransmitCount--; //Switch state entry->state = DNS_STATE_IN_PROGRESS; //Host name resolution is in progress error = ERROR_IN_PROGRESS; } else { //Unregister callback function udpDetachRxCallback(interface, entry->port); } } } //Release exclusive access to the DNS cache osMutexRelease(dnsCacheMutex); //Set default polling interval delay = DNS_CACHE_INIT_POLLING_INTERVAL; //Wait the host name resolution to complete while(error == ERROR_IN_PROGRESS) { //Wait until the next polling period osDelay(delay); //Acquire exclusive access to the DNS cache osMutexAcquire(dnsCacheMutex); //Search the DNS cache for the specified host name entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_DNS); //Check whether a matching entry has been found if(entry) { //Host name successfully resolved? if(entry->state == DNS_STATE_RESOLVED) { //Return the corresponding IP address *ipAddr = entry->ipAddr; //Successful host name resolution error = NO_ERROR; } } else { //Host name resolution failed error = ERROR_FAILURE; } //Release exclusive access to the DNS cache osMutexRelease(dnsCacheMutex); //Backoff support for less aggressive polling delay = min(delay * 2, DNS_CACHE_MAX_POLLING_INTERVAL); } //Check status code if(error) { //Failed to resolve host name TRACE_INFO("Host name resolution failed!\r\n"); } else { //Successful host name resolution TRACE_INFO("Host name resolved to %s...\r\n", ipAddrToString(ipAddr, NULL)); } //Return status code return error; }
void tcpEchoConnectionTask(void *param) { error_t error; uint_t n; uint_t writeIndex; uint_t readIndex; uint_t bufferLength; uint_t rxByteCount; uint_t txByteCount; time_t startTime; time_t duration; SocketEventDesc eventDesc; EchoServiceContext *context; //Get a pointer to the context context = (EchoServiceContext *) param; //Get current time startTime = osGetTickCount(); //Initialize variables writeIndex = 0; readIndex = 0; bufferLength = 0; rxByteCount = 0; txByteCount = 0; //Main loop while(1) { //Buffer is empty? if(!bufferLength) { //Get notified when the socket is readable eventDesc.socket = context->socket; eventDesc.eventMask = SOCKET_EVENT_RX_READY; } //Buffer is not empty of full? else if(bufferLength < ECHO_BUFFER_SIZE) { //Get notified when the socket is readable or writable eventDesc.socket = context->socket; eventDesc.eventMask = SOCKET_EVENT_RX_READY | SOCKET_EVENT_TX_READY; } //Buffer is full? else { //Get notified when the socket is writable eventDesc.socket = context->socket; eventDesc.eventMask = SOCKET_EVENT_TX_READY; } //Wait for an event to be fired error = socketPoll(&eventDesc, 1, NULL, ECHO_TIMEOUT); //Timeout error or any other exception to report? if(error) break; //The socket is available for reading if(eventDesc.eventFlags & SOCKET_EVENT_RX_READY) { //Read as much data as possible n = min(ECHO_BUFFER_SIZE - writeIndex, ECHO_BUFFER_SIZE - bufferLength); //Read incoming data error = socketReceive(context->socket, context->buffer + writeIndex, n, &n, 0); //Any error to report? if(error) break; //Increment write index writeIndex += n; //Wrap around if necessary if(writeIndex >= ECHO_BUFFER_SIZE) writeIndex = 0; //Increment buffer length bufferLength += n; //Total number of bytes received rxByteCount += n; } //The socket is available for writing? if(eventDesc.eventFlags & SOCKET_EVENT_TX_READY) { //Write as much data as possible n = min(ECHO_BUFFER_SIZE - readIndex, bufferLength); //Send data back to the client error = socketSend(context->socket, context->buffer + readIndex, n, &n, 0); //Any error to report? if(error && error != ERROR_TIMEOUT) break; //Increment read index readIndex += n; //Wrap around if necessary if(readIndex >= ECHO_BUFFER_SIZE) readIndex = 0; //Update buffer length bufferLength -= n; //Total number of bytes sent txByteCount += n; } } //Adjust timeout value socketSetTimeout(context->socket, ECHO_TIMEOUT); //Graceful shutdown socketShutdown(context->socket, SOCKET_SD_BOTH); //Compute total duration duration = osGetTickCount() - startTime; //Debug message TRACE_INFO("Echo service: %u bytes received, %u bytes sent in %lu ms\r\n", rxByteCount, txByteCount, duration); //Close socket socketClose(context->socket); //Release previously allocated memory osMemFree(context); //Kill ourselves osTaskDelete(NULL); }
error_t httpServerCgiCallback(HttpConnection *connection, const char_t *param) { static uint_t pageCounter = 0; uint_t length; //Underlying network interface NetInterface *interface = connection->socket->interface; //Check parameter name if(!strcasecmp(param, "PAGE_COUNTER")) { pageCounter++; sprintf(connection->buffer, "%u time%s", pageCounter, (pageCounter >= 2) ? "s" : ""); } else if(!strcasecmp(param, "BOARD_NAME")) { strcpy(connection->buffer, "STM3240G-EVAL"); } else if(!strcasecmp(param, "SYSTEM_TIME")) { time_t time = osGetTickCount(); sprintf(connection->buffer, "%lus %03lums", time / 1000, time % 1000); } else if(!strcasecmp(param, "MAC_ADDR")) { macAddrToString(&interface->macAddr, connection->buffer); } else if(!strcasecmp(param, "IPV4_ADDR")) { ipv4AddrToString(interface->ipv4Config.addr, connection->buffer); } else if(!strcasecmp(param, "SUBNET_MASK")) { ipv4AddrToString(interface->ipv4Config.subnetMask, connection->buffer); } else if(!strcasecmp(param, "DEFAULT_GATEWAY")) { ipv4AddrToString(interface->ipv4Config.defaultGateway, connection->buffer); } else if(!strcasecmp(param, "IPV4_PRIMARY_DNS")) { ipv4AddrToString(interface->ipv4Config.dnsServer[0], connection->buffer); } else if(!strcasecmp(param, "IPV4_SECONDARY_DNS")) { ipv4AddrToString(interface->ipv4Config.dnsServer[1], connection->buffer); } #if (IPV6_SUPPORT == ENABLED) else if(!strcasecmp(param, "LINK_LOCAL_ADDR")) { ipv6AddrToString(&interface->ipv6Config.linkLocalAddr, connection->buffer); } else if(!strcasecmp(param, "GLOBAL_ADDR")) { ipv6AddrToString(&interface->ipv6Config.globalAddr, connection->buffer); } else if(!strcasecmp(param, "IPV6_PREFIX")) { ipv6AddrToString(&interface->ipv6Config.prefix, connection->buffer); length = strlen(connection->buffer); sprintf(connection->buffer + length, "/%u", interface->ipv6Config.prefixLength); } else if(!strcasecmp(param, "ROUTER")) { ipv6AddrToString(&interface->ipv6Config.router, connection->buffer); } else if(!strcasecmp(param, "IPV6_PRIMARY_DNS")) { ipv6AddrToString(&interface->ipv6Config.dnsServer[0], connection->buffer); } else if(!strcasecmp(param, "IPV6_SECONDARY_DNS")) { ipv6AddrToString(&interface->ipv6Config.dnsServer[1], connection->buffer); } #endif else { return ERROR_INVALID_TAG; } //Get the length of the resulting string length = strlen(connection->buffer); //Send the contents of the specified environment variable return httpWriteStream(connection, connection->buffer, length); }