/* 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);
}