error_t httpServerCgiCallback(HttpConnection *connection, const char_t *param) { static uint_t pageCounter = 0; uint_t length; MacAddr macAddr; #if (IPV4_SUPPORT == ENABLED) Ipv4Addr ipv4Addr; #endif #if (IPV6_SUPPORT == ENABLED) uint_t n; Ipv6Addr ipv6Addr; #endif //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, "SAM7SE-EK"); } else if(!strcasecmp(param, "SYSTEM_TIME")) { systime_t time = osGetSystemTime(); formatSystemTime(time, connection->buffer); } else if(!strcasecmp(param, "MAC_ADDR")) { netGetMacAddr(interface, &macAddr); macAddrToString(&macAddr, connection->buffer); } else if(!strcasecmp(param, "IPV4_ADDR")) { ipv4GetHostAddr(interface, &ipv4Addr); ipv4AddrToString(ipv4Addr, connection->buffer); } else if(!strcasecmp(param, "SUBNET_MASK")) { ipv4GetSubnetMask(interface, &ipv4Addr); ipv4AddrToString(ipv4Addr, connection->buffer); } else if(!strcasecmp(param, "DEFAULT_GATEWAY")) { ipv4GetDefaultGateway(interface, &ipv4Addr); ipv4AddrToString(ipv4Addr, connection->buffer); } else if(!strcasecmp(param, "IPV4_PRIMARY_DNS")) { ipv4GetDnsServer(interface, 0, &ipv4Addr); ipv4AddrToString(ipv4Addr, connection->buffer); } else if(!strcasecmp(param, "IPV4_SECONDARY_DNS")) { ipv4GetDnsServer(interface, 1, &ipv4Addr); ipv4AddrToString(ipv4Addr, connection->buffer); } #if (IPV6_SUPPORT == ENABLED) else if(!strcasecmp(param, "LINK_LOCAL_ADDR")) { ipv6GetLinkLocalAddr(interface, &ipv6Addr); ipv6AddrToString(&ipv6Addr, connection->buffer); } else if(!strcasecmp(param, "GLOBAL_ADDR")) { ipv6GetGlobalAddr(interface, 0, &ipv6Addr); ipv6AddrToString(&ipv6Addr, connection->buffer); } else if(!strcasecmp(param, "IPV6_PREFIX")) { ipv6GetPrefix(interface, 0, &ipv6Addr, &n); ipv6AddrToString(&ipv6Addr, connection->buffer); length = strlen(connection->buffer); sprintf(connection->buffer + length, "/%u", n); } else if(!strcasecmp(param, "ROUTER")) { ipv6GetDefaultRouter(interface, 0, &ipv6Addr); ipv6AddrToString(&ipv6Addr, connection->buffer); } else if(!strcasecmp(param, "IPV6_PRIMARY_DNS")) { ipv6GetDnsServer(interface, 0, &ipv6Addr); ipv6AddrToString(&ipv6Addr, connection->buffer); } else if(!strcasecmp(param, "IPV6_SECONDARY_DNS")) { ipv6GetDnsServer(interface, 1, &ipv6Addr); ipv6AddrToString(&ipv6Addr, 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); }
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, "STM32F4-Discovery"); } else if(!strcasecmp(param, "SYSTEM_TIME")) { systime_t time = osGetSystemTime(); formatSystemTime(time, connection->buffer); } 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); }
void tcpProcessSegment(NetInterface *interface, IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset) { uint_t i; size_t length; Socket *socket; Socket *passiveSocket; TcpHeader *segment; //Total number of segments received, including those received in error MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInSegs, 1); MIB2_INC_COUNTER64(mib2Base.tcpGroup.tcpHCInSegs, 1); //A TCP implementation must silently discard an incoming //segment that is addressed to a broadcast or multicast //address (see RFC 1122 4.2.3.10) #if (IPV4_SUPPORT == ENABLED) if(pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //Ensure the destination address is not a broadcast address if(ipv4IsBroadcastAddr(interface, pseudoHeader->ipv4Data.destAddr)) return; //Ensure the destination address is not a multicast address if(ipv4IsMulticastAddr(pseudoHeader->ipv4Data.destAddr)) return; } else #endif #if (IPV6_SUPPORT == ENABLED) if(pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //Ensure the destination address is not a multicast address if(ipv6IsMulticastAddr(&pseudoHeader->ipv6Data.destAddr)) return; } else #endif { //This should never occur... return; } //Retrieve the length of the TCP segment length = netBufferGetLength(buffer) - offset; //Point to the TCP header segment = netBufferAt(buffer, offset); //Sanity check if(segment == NULL) return; //Ensure the TCP header is valid if(length < sizeof(TcpHeader)) { //Debug message TRACE_WARNING("TCP segment length is invalid!\r\n"); //Total number of segments received in error MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1); //Exit immediately return; } //Check header length if(segment->dataOffset < 5 || (segment->dataOffset * 4) > length) { //Debug message TRACE_WARNING("TCP header length is invalid!\r\n"); //Total number of segments received in error MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1); //Exit immediately return; } //Verify TCP checksum if(ipCalcUpperLayerChecksumEx(pseudoHeader->data, pseudoHeader->length, buffer, offset, length) != 0x0000) { //Debug message TRACE_WARNING("Wrong TCP header checksum!\r\n"); //Total number of segments received in error MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1); //Exit immediately return; } //No matching socket in the LISTEN state for the moment passiveSocket = NULL; //Look through opened sockets for(i = 0; i < SOCKET_MAX_COUNT; i++) { //Point to the current socket socket = socketTable + i; //TCP socket found? if(socket->type != SOCKET_TYPE_STREAM) 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(segment->destPort)) 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; } //Keep track of the first matching socket in the LISTEN state if(socket->state == TCP_STATE_LISTEN && !passiveSocket) passiveSocket = socket; //Source port filtering if(socket->remotePort != ntohs(segment->srcPort)) continue; //A matching socket has been found break; } //If no matching socket has been found then try to //use the first matching socket in the LISTEN state if(i >= SOCKET_MAX_COUNT) socket = passiveSocket; //Offset to the first data byte offset += segment->dataOffset * 4; //Calculate the length of the data length -= segment->dataOffset * 4; //Debug message TRACE_DEBUG("%s: TCP segment received (%" PRIuSIZE " data bytes)...\r\n", formatSystemTime(osGetSystemTime(), NULL), length); //Dump TCP header contents for debugging purpose if(!socket) tcpDumpHeader(segment, length, 0, 0); else tcpDumpHeader(segment, length, socket->irs, socket->iss); //Convert from network byte order to host byte order segment->srcPort = ntohs(segment->srcPort); segment->destPort = ntohs(segment->destPort); segment->seqNum = ntohl(segment->seqNum); segment->ackNum = ntohl(segment->ackNum); segment->window = ntohs(segment->window); segment->urgentPointer = ntohs(segment->urgentPointer); //Specified port is unreachable? if(!socket) { //An incoming segment not containing a RST causes //a reset to be sent in response if(!(segment->flags & TCP_FLAG_RST)) tcpSendResetSegment(interface, pseudoHeader, segment, length); //Return immediately return; } //Check current state switch(socket->state) { //Process CLOSED state case TCP_STATE_CLOSED: //This is the default state that each connection starts in before //the process of establishing it begins tcpStateClosed(interface, pseudoHeader, segment, length); break; //Process LISTEN state case TCP_STATE_LISTEN: //A device (normally a server) is waiting to receive a synchronize (SYN) //message from a client. It has not yet sent its own SYN message tcpStateListen(socket, interface, pseudoHeader, segment, length); break; //Process SYN_SENT state case TCP_STATE_SYN_SENT: //The device (normally a client) has sent a synchronize (SYN) message and //is waiting for a matching SYN from the other device (usually a server) tcpStateSynSent(socket, segment, length); break; //Process SYN_RECEIVED state case TCP_STATE_SYN_RECEIVED: //The device has both received a SYN from its partner and sent its own SYN. //It is now waiting for an ACK to its SYN to finish connection setup tcpStateSynReceived(socket, segment, buffer, offset, length); break; //Process ESTABLISHED state case TCP_STATE_ESTABLISHED: //Data can be exchanged freely once both devices in the connection enter //this state. This will continue until the connection is closed tcpStateEstablished(socket, segment, buffer, offset, length); break; //Process CLOSE_WAIT state case TCP_STATE_CLOSE_WAIT: //The device has received a close request (FIN) from the other device. It //must now wait for the application to acknowledge this request and //generate a matching request tcpStateCloseWait(socket, segment, length); break; //Process LAST_ACK state case TCP_STATE_LAST_ACK: //A device that has already received a close request and acknowledged it, //has sent its own FIN and is waiting for an ACK to this request tcpStateLastAck(socket, segment, length); break; //Process FIN_WAIT_1 state case TCP_STATE_FIN_WAIT_1: //A device in this state is waiting for an ACK for a FIN it has sent, or //is waiting for a connection termination request from the other device tcpStateFinWait1(socket, segment, buffer, offset, length); break; //Process FIN_WAIT_2 state case TCP_STATE_FIN_WAIT_2: //A device in this state has received an ACK for its request to terminate the //connection and is now waiting for a matching FIN from the other device tcpStateFinWait2(socket, segment, buffer, offset, length); break; //Process CLOSING state case TCP_STATE_CLOSING: //The device has received a FIN from the other device and sent an ACK for //it, but not yet received an ACK for its own FIN message tcpStateClosing(socket, segment, length); break; //Process TIME_WAIT state case TCP_STATE_TIME_WAIT: //The device has now received a FIN from the other device and acknowledged //it, and sent its own FIN and received an ACK for it. We are done, except //for waiting to ensure the ACK is received and prevent potential overlap //with new connections tcpStateTimeWait(socket, segment, length); break; //Invalid state... default: //Back to the CLOSED state tcpChangeState(socket, TCP_STATE_CLOSED); //Silently discard incoming packet break; } }
void tcpTick(void) { error_t error; uint_t i; uint_t n; uint_t u; //Enter critical section osAcquireMutex(&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(tcpTimerElapsed(&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", formatSystemTime(osGetSystemTime(), NULL), 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 tcpTimerStart(&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 tcpTimerStop(&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(tcpTimerElapsed(&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", formatSystemTime(osGetSystemTime(), NULL), 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 tcpTimerStart(&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 && tcpTimerElapsed(&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) tcpTimerStart(&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(tcpTimerElapsed(&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(tcpTimerElapsed(&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 osReleaseMutex(&socketMutex); }