#include #include #include #include #include #include #include #include #include struct icmp_base { uint8_t type ; uint8_t code ; uint16_t checksum ; } __attribute__ ((packed)); struct icmp_echo_request { struct icmp_base base; uint16_t identifier; uint16_t sequence; struct timeval timestamp __attribute__ ((packed)); } __attribute__ ((packed)); struct icmp_echo_response { struct icmp_base base; uint16_t identifier; uint16_t sequence; struct timeval timestamp __attribute__ ((packed)); } __attribute__ ((packed)); /** * Generate the 16 bit checksum for the packet. This checksum * is the 16 bit one's complement of the one's complement sum of * the ICMP packet for the data from offset 0 thru offset 0 + len - 1 * with the checksum field set to 0. */ uint16_t ICMP_generate_checksum ( register const uint16_t * packet, register size_t len ) { register uint32_t sum = ( uint32_t ) * packet; packet += 2; len -= 4; while ( 1 < len ) { sum += * packet; packet ++; len -= 2; } if ( 1 == len ) { sum += ( ( uint32_t ) ( * (uint8_t *) packet ) ) << 8; } sum = ( sum >> 16 ) + ( sum & 0xffff ); sum += ( sum >> 16 ); return ( uint16_t ) ( ~sum ); } int ICMP_send_echo ( const char * presentation_addr, int socket, struct sockaddr_in * remote, uint16_t identifier, uint16_t sequence ) { struct icmp_echo_request request; int sendto_result; /** * Type of 8 and code of 0 means the ICMP packet * is an echo request */ request.base.type = 8; request.base.code = 0; /** * The next line is actually redundant since the checksum * func included here skips over the checksum field during * the calc operation. */ request.base.checksum = 0; /** * The identifier and sequence creates a unique ID so we * can tell if the packet is the expected one. */ request.identifier = htons (identifier ); request.sequence = htons ( sequence ); /** * Just grabbing current system time to used as a timestamp * and do round trip speed calcs. */ (void) gettimeofday ( & request.timestamp, NULL ); /** * Since this is an echo back to the originating * system, we don't need to worry about endianism * for the data. */ request.timestamp.tv_sec = request.timestamp.tv_sec; request.timestamp.tv_usec = request.timestamp.tv_usec; /** * All fields and data are now set, so we generate our * checksum field and assign it. */ request.base.checksum = ( ICMP_generate_checksum ( ( uint16_t * ) & request, sizeof ( struct icmp_echo_request ) ) ); printf ( "Pinging addr: %s\n", presentation_addr ); /** * Loop until we have a successful uninterrupted * send operation. All errors other than interrupt * are considered fatal for this demo. * * Note that since we did not use setsockopt to turn * IP_HDRINCL on, the system will create and tag on * the ip header for us. If we had turned on that option * we would have to create and write the ip header * ourself. */ do { sendto_result = sendto ( socket, ( const void * ) & request, ( size_t ) sizeof ( struct icmp_echo_request ), 0, ( const struct sockaddr * ) remote, ( socklen_t ) sizeof ( struct sockaddr_in ) ); } while ( ( -1 == sendto_result ) & ( EINTR == errno ) ); return sendto_result; } int ICMP_receive_echo ( const char * presentation_addr, int socket, uint16_t identifier, uint16_t sequence ) { struct icmp_echo_response response; struct sockaddr_in remote; char in_buff[ 512 ]; socklen_t addr_len = sizeof ( struct sockaddr_in ); int recvfrom_result; struct timeval current_time; int32_t time_difference; for ( ; ; ) { /** * Loop until we recv an incoming packet. * * Note that the recvfrom will always return the packet * with the ip header intact no matter what the IP_HDRINCL * sock option status. After receiving we must look at * the header and skip over it to get to our ICMP packet. */ do { printf ("Waiting to receive response...\n"); recvfrom_result = recvfrom ( socket, ( void * ) in_buff, ( size_t ) 512, 0, ( struct sockaddr * ) & remote, ( socklen_t *) & addr_len ); } while ( ( -1 == recvfrom_result ) & ( EINTR == errno ) ); if ( -1 == recvfrom_result ) { /** * Consider anything other than in interrupt as * a fatal error. */ return -1; } if ( sizeof ( struct icmp_echo_response ) != recvfrom_result - ( 4 * ( ( uint8_t) in_buff[0] & 0x0f ) ) ) { printf ( "Received ICMP packet with an unexpected length:\n\tExpecting: %i\n\tReceived: %i\n\n", sizeof ( struct icmp_echo_response ), recvfrom_result - ( 4 * ( ( uint8_t) in_buff[0] & 0x0f ) ) ); continue; } /** * Calc so we skip over the ip header segment and * do a copy to avoid any potential alignment problems. */ (void) memcpy ( ( void * ) & response, in_buff + ( 4 * ( ( uint8_t) in_buff[0] & 0x0f ) ), sizeof ( struct icmp_echo_response ) ); /** * Should validate the checksum here. Since many systems * don't calc correctly, skipping for now. */ if ( 0 != response.base.type ) { /* Not an echo response */ printf ( "Received ICMP packet with wrong type value:\n\tExpecting: %i\n\tReceived: %i\n\n", 0, response.base.type ); continue; } if ( 0 != response.base.code ) { /* Not an echo response */ printf ( "Received ICMP packet with wrong code value:\n\tExpecting: %i\n\tReceived: %i\n\n", 0, response.base.code ); continue; } if ( identifier != ntohs ( response.identifier ) ) { printf ( "Received ICMP echo reply with wrong identifier:\n\tExpecting: %i\n\tReceived: %i\n\n", identifier, ntohs ( response.identifier ) ); continue; } if ( sequence != ntohs ( response.sequence ) ) { printf ( "Received ICMP echo reply with wrong sequence:\n\tExpecting: %i\n\tReceived: %i\n\n", sequence, htons ( response.sequence ) ); continue; } break; } /* End for ( ; ; ) */ /** * Get the current system time and subtract the time * we previously embedded in our echo packet. This will * give the round trip time for the packet. */ (void) gettimeofday ( & current_time, NULL ); time_difference = 1000 * ( current_time.tv_sec - response.timestamp.tv_sec ); time_difference += ( current_time.tv_usec - response.timestamp.tv_usec ) / 1000; printf ( "Received echo reply from: %s with %i ms delay.\n", presentation_addr, time_difference ); return 0; } int main ( int argc, char ** argv ) { struct hostent * host_entry; struct sockaddr_in remote_addr; int socket_fd; if ( 2 != argc ) { printf ( "Useage is: program \n" ); return -1; } /** * Old way of grabbing the ip address for a host * name. Non-reentrant and so forth but works * for our purposes. */ host_entry = gethostbyname ( ( const char * ) argv[1] ); if ( NULL == host_entry ) { printf ( "Could not resolve host name: %s\n.", (const char *) argv[1] ); return -1; } /** * Always zero out our entire address structure just * for good luck. */ memset ( ( void * ) & remote_addr, 0, sizeof ( struct sockaddr_in ) ); /** * We must set the address family and the target system * address. We leave our port set to 0 (from the previous * operstion. */ remote_addr.sin_family = AF_INET; remote_addr.sin_addr.s_addr = * ( in_addr_t * ) host_entry->h_addr_list[0]; /** * Create a socket... based on ip.... holding icmp... etc. */ socket_fd = socket ( AF_INET, SOCK_RAW, IPPROTO_ICMP ); if ( -1 == socket_fd ) { printf ( "Could not create socket.\n" ); return -1; } /* Send that packet */ if ( -1 == ICMP_send_echo ( argv[1], socket_fd, & remote_addr, getpid( ), 0 ) ) { perror ( "During ICMP send"); return -1; } /* Wait for it to come echoing back! */ if ( -1 == ICMP_receive_echo ( argv[1], socket_fd, getpid( ), 0 ) ) { perror ( "During ICMP receive"); return -1; } return 0; }