Created
October 11, 2015 15:22
-
-
Save benjojo/fec4ce4470bd68f1b714 to your computer and use it in GitHub Desktop.
How to trigger SNMP counter bug
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright (C) 2011-2015 P.D. Buchan ([email protected]) | |
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 <http://www.gnu.org/licenses/>. | |
*/ | |
// 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 <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> // close() | |
#include <string.h> // strcpy, memset(), and memcpy() | |
#include <netdb.h> // struct addrinfo | |
#include <sys/types.h> // needed for socket(), uint8_t, uint16_t, uint32_t | |
#include <sys/socket.h> // needed for socket() | |
#include <netinet/in.h> // IPPROTO_RAW, IPPROTO_IP, IPPROTO_ICMP, INET_ADDRSTRLEN | |
#include <netinet/ip.h> // struct ip and IP_MAXPACKET (which is 65535) | |
#include <netinet/ip_icmp.h> // struct icmp, ICMP_ECHO | |
#include <arpa/inet.h> // inet_pton() and inet_ntop() | |
#include <sys/ioctl.h> // macro ioctl is defined | |
#include <bits/ioctls.h> // defines values for argument "request" of ioctl. | |
#include <net/if.h> // struct ifreq | |
#include <errno.h> // errno, perror() | |
// Define some constants. | |
#define IP4_HDRLEN 21 // IPv4 header length | |
#define ICMP_HDRLEN 8 // ICMP header length for echo request, excludes data | |
// Function prototypes | |
uint16_t checksum (uint16_t *, int); | |
char *allocate_strmem (int); | |
uint8_t *allocate_ustrmem (int); | |
int *allocate_intmem (int); | |
int | |
main (int argc, char **argv) | |
{ | |
int status, datalen, sd, *ip_flags; | |
const int on = 1; | |
char *interface, *target, *src_ip, *dst_ip; | |
struct ip iphdr; | |
struct icmp icmphdr; | |
uint8_t *data, *packet; | |
struct addrinfo hints, *res; | |
struct sockaddr_in *ipv4, sin; | |
struct ifreq ifr; | |
void *tmp; | |
// Allocate memory for various arrays. | |
data = allocate_ustrmem (IP_MAXPACKET); | |
packet = allocate_ustrmem (IP_MAXPACKET); | |
interface = allocate_strmem (40); | |
target = allocate_strmem (40); | |
src_ip = allocate_strmem (INET_ADDRSTRLEN); | |
dst_ip = allocate_strmem (INET_ADDRSTRLEN); | |
ip_flags = allocate_intmem (4); | |
// Interface to send packet through. | |
strcpy (interface, "eth0"); | |
// Submit request for a socket descriptor to look up interface. | |
if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { | |
perror ("socket() failed to get socket descriptor for using ioctl() "); | |
exit (EXIT_FAILURE); | |
} | |
// Use ioctl() to look up interface index which we will use to | |
// bind socket descriptor sd to specified interface with setsockopt() since | |
// none of the other arguments of sendto() specify which interface to use. | |
memset (&ifr, 0, sizeof (ifr)); | |
snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface); | |
if (ioctl (sd, SIOCGIFINDEX, &ifr) < 0) { | |
perror ("ioctl() failed to find interface "); | |
return (EXIT_FAILURE); | |
} | |
close (sd); | |
printf ("Index for interface %s is %i\n", interface, ifr.ifr_ifindex); | |
// Source IPv4 address: you need to fill this out | |
strcpy (src_ip, "192.168.1.132"); | |
// Destination URL or IPv4 address: you need to fill this out | |
strcpy (target, "www.google.com"); | |
// Fill out hints for getaddrinfo(). | |
memset (&hints, 0, sizeof (struct addrinfo)); | |
hints.ai_family = AF_INET; | |
hints.ai_socktype = SOCK_STREAM; | |
hints.ai_flags = hints.ai_flags | AI_CANONNAME; | |
// Resolve target using getaddrinfo(). | |
if ((status = getaddrinfo (target, NULL, &hints, &res)) != 0) { | |
fprintf (stderr, "getaddrinfo() failed: %s\n", gai_strerror (status)); | |
exit (EXIT_FAILURE); | |
} | |
ipv4 = (struct sockaddr_in *) res->ai_addr; | |
tmp = &(ipv4->sin_addr); | |
if (inet_ntop (AF_INET, tmp, dst_ip, INET_ADDRSTRLEN) == NULL) { | |
status = errno; | |
fprintf (stderr, "inet_ntop() failed.\nError message: %s", strerror (status)); | |
exit (EXIT_FAILURE); | |
} | |
freeaddrinfo (res); | |
// 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); */ 0x46; | |
// 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 | |
// Zero (1 bit) | |
ip_flags[0] = 0; | |
// Do not fragment flag (1 bit) | |
ip_flags[1] = 0; | |
// More fragments following flag (1 bit) | |
ip_flags[2] = 0; | |
// Fragmentation offset (13 bits) | |
ip_flags[3] = 0; | |
iphdr.ip_off = htons ((ip_flags[0] << 15) | |
+ (ip_flags[1] << 14) | |
+ (ip_flags[2] << 13) | |
+ ip_flags[3]); | |
// 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; | |
// Source IPv4 address (32 bits) | |
if ((status = inet_pton (AF_INET, src_ip, &(iphdr.ip_src))) != 1) { | |
fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status)); | |
exit (EXIT_FAILURE); | |
} | |
// Destination IPv4 address (32 bits) | |
if ((status = inet_pton (AF_INET, dst_ip, &(iphdr.ip_dst))) != 1) { | |
fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status)); | |
exit (EXIT_FAILURE); | |
} | |
// 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 = iphdr.ip_dst.s_addr; | |
// 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); | |
} | |
// Bind socket to interface index. | |
if (setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr)) < 0) { | |
perror ("setsockopt() failed to bind to interface "); | |
exit (EXIT_FAILURE); | |
} | |
// 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); | |
// Free allocated memory. | |
free (data); | |
free (packet); | |
free (interface); | |
free (target); | |
free (src_ip); | |
free (dst_ip); | |
free (ip_flags); | |
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); | |
} | |
// Allocate memory for an array of chars. | |
char * | |
allocate_strmem (int len) | |
{ | |
void *tmp; | |
if (len <= 0) { | |
fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_strmem().\n", len); | |
exit (EXIT_FAILURE); | |
} | |
tmp = (char *) malloc (len * sizeof (char)); | |
if (tmp != NULL) { | |
memset (tmp, 0, len * sizeof (char)); | |
return (tmp); | |
} else { | |
fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_strmem().\n"); | |
exit (EXIT_FAILURE); | |
} | |
} | |
// Allocate memory for an array of unsigned chars. | |
uint8_t * | |
allocate_ustrmem (int len) | |
{ | |
void *tmp; | |
if (len <= 0) { | |
fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_ustrmem().\n", len); | |
exit (EXIT_FAILURE); | |
} | |
tmp = (uint8_t *) malloc (len * sizeof (uint8_t)); | |
if (tmp != NULL) { | |
memset (tmp, 0, len * sizeof (uint8_t)); | |
return (tmp); | |
} else { | |
fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_ustrmem().\n"); | |
exit (EXIT_FAILURE); | |
} | |
} | |
// Allocate memory for an array of ints. | |
int * | |
allocate_intmem (int len) | |
{ | |
void *tmp; | |
if (len <= 0) { | |
fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_intmem().\n", len); | |
exit (EXIT_FAILURE); | |
} | |
tmp = (int *) malloc (len * sizeof (int)); | |
if (tmp != NULL) { | |
memset (tmp, 0, len * sizeof (int)); | |
return (tmp); | |
} else { | |
fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_intmem().\n"); | |
exit (EXIT_FAILURE); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment