/* Copyright (C) 2011-2015 P.D. Buchan (pdbuchan@yahoo.com) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ // Simplified: initial lookups removed. See icmp4.c for the original version. // Send an IPv4 ICMP packet via raw socket. // Stack fills out layer 2 (data link) information (MAC addresses) for us. // Values set for echo request packet, includes some ICMP data. #include #include #include // close() #include // strcpy, memset(), and memcpy() #include // needed for socket(), uint8_t, uint16_t, uint32_t #include // needed for socket() #include // IPPROTO_RAW, IPPROTO_IP, IPPROTO_ICMP, INET_ADDRSTRLEN #include // struct ip and IP_MAXPACKET (which is 65535) #include // struct icmp, ICMP_ECHO #include // inet_pton() and inet_ntop() #include // errno, perror() // Define some constants. #define IP4_HDRLEN 20 // IPv4 header length #define ICMP_HDRLEN 8 // ICMP header length for echo request, excludes data #define DST_IP "129.170.213.53" #define SRC_IP "8.8.8.8" // IP checksum uint16_t checksum (uint16_t *, int); int main (int argc, char **argv) { int datalen, sd; uint8_t data[IP_MAXPACKET], packet[IP_MAXPACKET]; const int on = 1; struct ip iphdr; struct icmp icmphdr; uint32_t src_ip, dst_ip; struct sockaddr_in sin; // Submit request for a raw socket descriptor. if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror ("socket() failed"); exit (EXIT_FAILURE); } // Set flag so socket expects us to provide IPv4 header. if (setsockopt (sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on)) < 0) { perror ("setsockopt() failed to set IP_HDRINCL "); exit (EXIT_FAILURE); } if( !inet_aton( SRC_IP, (struct in_addr*) &src_ip ) ){ perror ("SRC_IP not a valid IP address"); exit(1); } if( !inet_aton( DST_IP, (struct in_addr*) &dst_ip ) ){ perror ("DST_IP not a valid IP address"); exit(1); } // ICMP data datalen = 4; data[0] = 'T'; data[1] = 'e'; data[2] = 's'; data[3] = 't'; // IPv4 header // IPv4 header length (4 bits): Number of 32-bit words in header = 5 iphdr.ip_hl = IP4_HDRLEN / sizeof (uint32_t); // Internet Protocol version (4 bits): IPv4 iphdr.ip_v = 4; // Type of service (8 bits) iphdr.ip_tos = 0; // Total length of datagram (16 bits): IP header + ICMP header + ICMP data iphdr.ip_len = htons (IP4_HDRLEN + ICMP_HDRLEN + datalen); // ID sequence number (16 bits): unused, since single datagram iphdr.ip_id = htons (0); // Flags, and Fragmentation offset (3, 13 bits): 0 since single datagram // Plain 0x0000 would work, but set the Don't Fragment flag just for kicks iphdr.ip_off = htons(0 | IP_DF ); // this fails if you forget htons! // Time-to-Live (8 bits): default to maximum value iphdr.ip_ttl = 255; // Transport layer protocol (8 bits): 1 for ICMP iphdr.ip_p = IPPROTO_ICMP; iphdr.ip_src.s_addr = (in_addr_t) src_ip; iphdr.ip_dst.s_addr = (in_addr_t) dst_ip; // IPv4 header checksum (16 bits): set to 0 when calculating checksum iphdr.ip_sum = 0; iphdr.ip_sum = checksum ((uint16_t *) &iphdr, IP4_HDRLEN); // ICMP header // Message Type (8 bits): echo request icmphdr.icmp_type = ICMP_ECHO; // Message Code (8 bits): echo request icmphdr.icmp_code = 0; // Identifier (16 bits): usually pid of sending process - pick a number icmphdr.icmp_id = htons (1000); // Sequence Number (16 bits): starts at 0 icmphdr.icmp_seq = htons (0); // ICMP header checksum (16 bits): set to 0 when calculating checksum icmphdr.icmp_cksum = 0; // Prepare packet. // First part is an IPv4 header. memcpy (packet, &iphdr, IP4_HDRLEN); // Next part of packet is upper layer protocol header. memcpy ((packet + IP4_HDRLEN), &icmphdr, ICMP_HDRLEN); // Finally, add the ICMP data. memcpy (packet + IP4_HDRLEN + ICMP_HDRLEN, data, datalen); // Calculate ICMP header checksum icmphdr.icmp_cksum = checksum ((uint16_t *) (packet + IP4_HDRLEN), ICMP_HDRLEN + datalen); memcpy ((packet + IP4_HDRLEN), &icmphdr, ICMP_HDRLEN); // The kernel is going to prepare layer 2 information (ethernet frame header) for us. // For that, we need to specify a destination for the kernel in order for it // to decide where to send the raw datagram. We fill in a struct in_addr with // the desired destination IP address, and pass this structure to the sendto() function. memset (&sin, 0, sizeof (struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = dst_ip; // Send packet. if (sendto (sd, packet, IP4_HDRLEN + ICMP_HDRLEN + datalen, 0, (struct sockaddr *) &sin, sizeof (struct sockaddr)) < 0) { perror ("sendto() failed "); exit (EXIT_FAILURE); } // Close socket descriptor. close (sd); return (EXIT_SUCCESS); } // Computing the internet checksum (RFC 1071). // Note that the internet checksum does not preclude collisions. uint16_t checksum (uint16_t *addr, int len) { int count = len; register uint32_t sum = 0; uint16_t answer = 0; // Sum up 2-byte values until none or only one byte left. while (count > 1) { sum += *(addr++); count -= 2; } // Add left-over byte, if any. if (count > 0) { sum += *(uint8_t *) addr; } // Fold 32-bit sum into 16 bits; we lose information by doing this, // increasing the chances of a collision. // sum = (lower 16 bits) + (upper 16 bits shifted right 16 bits) while (sum >> 16) { sum = (sum & 0xffff) + (sum >> 16); } // Checksum is one's compliment of sum. answer = ~sum; return (answer); }