error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, NetBuffer *buffer, size_t offset, uint8_t ttl) { error_t error; size_t length; uint16_t id; //Retrieve the length of payload length = netBufferGetLength(buffer) - offset; //Check whether the TTL value is zero if(ttl == 0) { //Use default Time-To-Live value ttl = IPV4_DEFAULT_TTL; } //Identification field is primarily used to identify //fragments of an original IP datagram id = osAtomicInc16(&interface->ipv4Identification); //If the payload length is smaller than the network //interface MTU then no fragmentation is needed if((length + sizeof(Ipv4Header)) <= interface->ipv4Config.mtu) { //Send data as is error = ipv4SendPacket(interface, pseudoHeader, id, 0, buffer, offset, ttl); } //If the payload length exceeds the network interface MTU //then the device must fragment the data else { #if (IPV4_FRAG_SUPPORT == ENABLED) //Fragment IP datagram into smaller packets error = ipv4FragmentDatagram(interface, pseudoHeader, id, buffer, offset, ttl); #else //Fragmentation is not supported error = ERROR_MESSAGE_TOO_LONG; #endif } //Return status code return error; }
error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, ChunkedBuffer *buffer, size_t offset, uint8_t timeToLive) { error_t error; size_t length; uint16_t id; //Retrieve the length of payload length = chunkedBufferGetLength(buffer) - offset; //Identification field is primarily used to identify //fragments of an original IP datagram id = osAtomicInc16(&interface->ipv4Identification); //If the payload length is smaller than the network //interface MTU then no fragmentation is needed if(length <= IPV4_MAX_PAYLOAD_SIZE) { //Send data as is error = ipv4SendPacket(interface, pseudoHeader, id, 0, buffer, offset, timeToLive); } //If the payload length exceeds the network interface MTU //then the device must fragment the data else { #if (IPV4_FRAG_SUPPORT == ENABLED) //Fragment IP datagram into smaller packets error = ipv4FragmentDatagram(interface, pseudoHeader, id, buffer, offset, timeToLive); #else //Fragmentation is not supported error = ERROR_MESSAGE_TOO_LONG; #endif } //Return status code return error; }
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; }