ns-icmpv4_sender.c revision 4548c6cf9bcdd96d8303caa4130ab638b61f8a30
1/******************************************************************************/
2/*                                                                            */
3/*   Copyright (c) International Business Machines  Corp., 2006               */
4/*                                                                            */
5/*   This program is free software;  you can redistribute it and/or modify    */
6/*   it under the terms of the GNU General Public License as published by     */
7/*   the Free Software Foundation; either version 2 of the License, or        */
8/*   (at your option) any later version.                                      */
9/*                                                                            */
10/*   This program is distributed in the hope that it will be useful,          */
11/*   but WITHOUT ANY WARRANTY;  without even the implied warranty of          */
12/*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See                */
13/*   the GNU General Public License for more details.                         */
14/*                                                                            */
15/*   You should have received a copy of the GNU General Public License        */
16/*   along with this program;  if not, write to the Free Software             */
17/*   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA  */
18/*                                                                            */
19/******************************************************************************/
20
21/*
22 * File:
23 *	ns-icmpv4_sender.c
24 *
25 * Description:
26 *	This is ICMPv4 echo request sender.
27 *	This utility is also able to set illegal information in the IP header
28 *
29 * Author:
30 *	Mitsuru Chinen <mitch@jp.ibm.com>
31 *
32 * History:
33 *	Mar 5 2006 - Created (Mitsuru Chinen)
34 *---------------------------------------------------------------------------*/
35
36/*
37 * Header Files
38 */
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <errno.h>
43#include <netdb.h>
44#include <signal.h>
45#include <time.h>
46#include <unistd.h>
47#include <sys/ioctl.h>
48#include <sys/socket.h>
49#include <arpa/inet.h>
50#include <net/ethernet.h>
51#include <net/if_arp.h>
52
53#include "ns-traffic.h"
54
55/*
56 * Structure Definitions
57 */
58struct icmpv4_fake {
59    struct ip4_datagram pkt;
60    char *src_ifname;
61    struct sockaddr_ll saddr_ll;
62    struct sockaddr_ll daddr_ll;
63    struct in_addr saddr;
64    struct in_addr daddr;
65    unsigned short int pkt_size;
66    unsigned short int data_size;
67    double timeout;
68
69    u_int16_t fake_flag;
70};
71
72/*
73 * Gloval variables
74 */
75char *program_name;		/* program name */
76struct sigaction handler;	/* Behavior for a signal */
77int catch_sighup;		/* When catch the SIGHUP, set to non-zero */
78
79/*
80 * Function: usage()
81 *
82 * Descripton:
83 *  Print the usage of this program. Then, terminate this program with
84 *  the specified exit value.
85 *
86 * Argument:
87 *  exit_value:	exit value
88 *
89 * Return value:
90 *  This function does not return.
91 */
92void
93usage (char *program_name, int exit_value)
94{
95    FILE *stream = stdout;	/* stream where the usage is output */
96
97    if (exit_value == EXIT_FAILURE)
98	stream = stderr;
99
100    fprintf (stream, "%s [OPTION]\n"
101		     "\t-I if_name\tInterface name of the source host\n"
102		     "\t-S ip_addr\tIPv4 address of the source host\n"
103		     "\t-M mac_addr\tMAC address of the destination host\n"
104		     "\t-D ip_addr\tIPv4 address of the destination host\n"
105		     "\t-s packetsize\tnumber of data bytes (exclude header)\n"
106		     "\t-t value\ttimeout [sec]\n"
107		     "\t-d\t\tdisplay debug informations\n"
108		     "\t-h\t\tdisplay this usage\n"
109		     "\n"
110		     "\t[options for fake]\n"
111		     "\t  -c\tbreak checksum\n"
112		     "\t  -f\tbreak fragment information\n"
113		     "\t  -i\tbreak IPv4 destination address\n"
114		     "\t  -l\tbreak header length\n"
115		     "\t  -L\tbreak total length\n"
116		     "\t  -p\tbreak protocol number\n"
117		     "\t  -v\tbreak IP version\n"
118			    , program_name);
119    exit (exit_value);
120}
121
122/*
123 * Function: set_signal_flag()
124 *
125 * Description:
126 *  This function sets global variables accordig to signal
127 *
128 * Argument:
129 *  type: type of signal
130 *
131 * Return value:
132 *  None
133 */
134void
135set_signal_flag(int type)
136{
137    if (debug)
138	fprintf(stderr, "Catch signal. type is %d\n", type);
139
140    switch (type) {
141	case SIGHUP:
142	    catch_sighup = 1;
143	    handler.sa_handler = SIG_IGN;
144	    if (sigaction(type, &handler, NULL) < 0)
145		fatal_error("sigaction()");
146	    break;
147
148	default:
149	    fprintf(stderr, "Unexpected signal (%d) is caught\n", type);
150	    exit(EXIT_FAILURE);
151    }
152}
153
154/*
155 * Function: parse_options()
156 *
157 * Description:
158 *  This function parse the options, then modify the fake icmp data
159 *
160 * Argument:
161 *   argc:  the number of argument
162 *   argv:  arguments
163 *  fake_p: pointer to data of fake icmp data to modify
164 *
165 * Return value:
166 *  None
167 */
168void
169parse_options(int argc, char *argv[], struct icmpv4_fake *fake_p)
170{
171    int optc;			/* option */
172    unsigned long opt_ul;	/* option value in unsigned long */
173    double opt_d;		/* option value in double */
174    struct in_addr opt_addr;	/* option value in struct in_addr */
175    struct sockaddr_ll opt_addr_ll;	/* option value in struct sockaddr_ll */
176    int is_specified_src_ifname = 0;
177    int is_specified_saddr = 0;
178    int is_specified_daddr_ll = 0;
179    int is_specified_daddr = 0;
180
181    while ((optc = getopt(argc, argv, "I:S:M:D:s:t:dhcfilLpv")) != EOF) {
182	switch (optc) {
183	    case 'I':
184		fake_p->src_ifname = strdup(optarg);
185		if (fake_p->src_ifname == NULL)
186		    fatal_error("strdup() failed.");
187		is_specified_src_ifname = 1;
188		break;
189
190	    case 'S':
191		if (inet_pton(AF_INET, optarg, &opt_addr) <= 0) {
192		    fprintf(stderr, "Source address is wrong\n");
193		    usage(program_name, EXIT_FAILURE);
194		}
195		fake_p->saddr = opt_addr;
196		is_specified_saddr = 1;
197		break;
198
199	    case 'M':
200		if (eth_pton(AF_INET, optarg, &opt_addr_ll)) {
201		    fprintf(stderr, "Destination MAC address is wrong\n");
202		    usage(program_name, EXIT_FAILURE);
203		}
204		fake_p->daddr_ll = opt_addr_ll;
205		is_specified_daddr_ll = 1;
206		break;
207
208	    case 'D':
209		if (inet_pton(AF_INET, optarg, &opt_addr) <= 0) {
210		    fprintf(stderr, "Destination address is wrong\n");
211		    usage(program_name, EXIT_FAILURE);
212		}
213		fake_p->daddr = opt_addr;
214		is_specified_daddr = 1;
215		break;
216
217	    case 's':
218		opt_ul = strtoul(optarg, NULL, 0);
219		if (opt_ul > ICMPV4_DATA_MAXSIZE) {
220		    fprintf(stderr, "Data size sholud be less than %d\n", ICMPV4_DATA_MAXSIZE + 1);
221		    usage(program_name, EXIT_FAILURE);
222		}
223		fake_p->data_size = opt_ul;
224		break;
225
226	    case 't':
227		opt_d = strtod(optarg, NULL);
228		if (opt_d < 0.0) {
229		    fprintf(stderr, "Timeout should be positive value\n");
230		    usage(program_name, EXIT_FAILURE);
231		}
232		fake_p->timeout = opt_d;
233		break;
234
235	    case 'd':
236		debug = 1;
237		break;
238
239	    case 'h':
240		usage(program_name, EXIT_SUCCESS);
241		break;
242
243	    /* Options for fake */
244	    case 'c':
245		fake_p->fake_flag |= FAKE_CHECK;
246		break;
247
248	    case 'f':
249		fake_p->fake_flag |= FAKE_FRAGMENT;
250		break;
251
252	    case 'i':
253		fake_p->fake_flag |= FAKE_DADDR;
254		break;
255
256	    case 'l':
257		fake_p->fake_flag |= FAKE_IHL;
258		break;
259
260	    case 'L':
261		fake_p->fake_flag |= FAKE_TOT_LEN;
262		break;
263
264	    case 'p':
265		fake_p->fake_flag |= FAKE_PROTOCOL;
266		break;
267
268	    case 'v':
269		fake_p->fake_flag |= FAKE_VERSION;
270		break;
271
272	    default:
273		usage(program_name, EXIT_FAILURE);
274	}
275    }
276
277    if (! is_specified_src_ifname) {
278	fprintf(stderr, "Interface name of the source host is not specified\n");
279	usage(program_name, EXIT_FAILURE);
280    }
281
282    if (! is_specified_saddr) {
283	fprintf(stderr, "Source IP address is not specified\n");
284	usage(program_name, EXIT_FAILURE);
285    }
286
287    if (! is_specified_daddr_ll) {
288	fprintf(stderr, "Destination MAC address is not specified\n");
289	usage(program_name, EXIT_FAILURE);
290    }
291
292    if (! is_specified_daddr) {
293	fprintf(stderr, "Destination IP address is not specified\n");
294	usage(program_name, EXIT_FAILURE);
295    }
296}
297
298/*
299 * Function: complete_eth_addrs()
300 *
301 * Description:
302 *  This function sets the source and destination ethernet address completely
303 *
304 * Argument:
305 *  fake_p: pointer to data of fake icmp structure
306 *
307 * Return value:
308 *  None
309 *
310 */
311void
312complete_eth_addrs(struct icmpv4_fake *fake_p)
313{
314    int sock_fd;		/* Socket for ioctl() */
315    struct ifreq ifinfo;	/* Interface information */
316
317    if ((sock_fd = socket(AF_PACKET, SOCK_DGRAM, 0)) < 0)
318	fatal_error("socket()");
319
320    /* Source */
321    fake_p->saddr_ll.sll_family	    = AF_PACKET;	/* Always AF_PACKET */
322    fake_p->saddr_ll.sll_protocol   = htons(ETH_P_IP);	/* IPv4 */
323    fake_p->saddr_ll.sll_hatype	    = ARPHRD_ETHER;	/* Header type */
324    fake_p->saddr_ll.sll_pkttype    = PACKET_HOST;	/* Packet type */
325    fake_p->saddr_ll.sll_halen	    = ETH_ALEN;		/* Length of address */
326
327    /* Get the MAC address of the interface at source host */
328    get_ifinfo(&ifinfo, sock_fd, fake_p->src_ifname, SIOCGIFHWADDR);
329    memcpy(fake_p->saddr_ll.sll_addr, ifinfo.ifr_hwaddr.sa_data, ETH_ALEN);
330
331    /* Get the interface index */
332    get_ifinfo(&ifinfo, sock_fd, fake_p->src_ifname, SIOCGIFINDEX);
333    fake_p->saddr_ll.sll_ifindex = ifinfo.ifr_ifindex;
334    fake_p->daddr_ll.sll_ifindex = ifinfo.ifr_ifindex;
335
336    close(sock_fd);
337}
338
339/*
340 * Function: create_clean_packet()
341 *
342 * Description:
343 *  This function creates icmpv4 packet without any fakes
344 *
345 * Argument:
346 *  fake_p: pointer to data of fake icmp structure
347 *
348 * Return value:
349 *  None
350 */
351void
352create_clean_packet(struct icmpv4_fake *fake_p)
353{
354    struct ip4_datagram pkt;		/* sending IPv4 packet */
355    struct icmp4_segment *icmp_p;	/* ICMPv4 part of sending packet */
356    unsigned short int pkt_size;
357
358    memset(&pkt, '\0', sizeof(struct ip4_datagram));
359    pkt_size = sizeof(struct iphdr)		/* IP header */
360		    + sizeof(struct icmphdr)	/* ICMP header */
361		    + fake_p->data_size;	/* ICMP payload */
362
363    icmp_p = (struct icmp4_segment *)&(pkt.payload);
364
365    /* IPv4 Header */
366    pkt.hdr.version	= 4;
367    pkt.hdr.ihl		= sizeof(struct iphdr) / 4;
368    pkt.hdr.tos		= 0;
369    pkt.hdr.tot_len	= htons(pkt_size);
370    pkt.hdr.id		= htons(IPV4_PACKET_ID);
371    pkt.hdr.frag_off	= htons(IPV4_DEFAULT_FLAG);
372    pkt.hdr.ttl		= IPV4_DEFAULT_TTL;
373    pkt.hdr.protocol	= IPPROTO_ICMP;
374    pkt.hdr.check	= 0;	/* Calculate later */
375    pkt.hdr.saddr	= fake_p->saddr.s_addr;
376    pkt.hdr.daddr	= fake_p->daddr.s_addr;
377
378    /* ICMPv4 Header */
379    icmp_p->hdr.type			= ICMP_ECHO;
380    icmp_p->hdr.code			= 0;
381    icmp_p->hdr.checksum		= 0;	/* Calculate later */
382    icmp_p->hdr.un.echo.id		= htons(ICMP_ECHO_ID);
383    icmp_p->hdr.un.echo.sequence	= htons(1);
384
385    /* ICMPv4 Payload */
386    fill_payload(icmp_p->data, fake_p->data_size);
387
388    /* Calcualte checksums */
389    pkt.hdr.check = calc_checksum((u_int16_t *)(&pkt.hdr),
390				sizeof(struct iphdr));
391    icmp_p->hdr.checksum = calc_checksum((u_int16_t *)icmp_p,
392				sizeof(struct icmphdr) + fake_p->data_size);
393
394    /* Store the clean packet data */
395    fake_p->pkt = pkt;
396    fake_p->pkt_size = pkt_size;
397}
398
399/*
400 * Function: thrust_fakes()
401 *
402 * Description:
403 *  This function thrust fake information to the icmp packet
404 *
405 * Argument:
406 *     pkt   : Payload of the Ethernet frame (Namely, IPv6 packet
407 *  fake_flag: Flag which represents what information would be faked
408 *
409 * Return value:
410 *  None
411 */
412void
413thrust_fakes(struct ip4_datagram *pkt, u_int16_t fake_flag)
414{
415    int rand_val;
416    size_t bitsize;
417    u_int32_t seed;
418
419    if (debug)
420	fprintf(stderr, "fake_flag = %2x\n", fake_flag);
421
422    if (fake_flag & FAKE_VERSION) {	/* version */
423	bitsize = 4;
424	seed = bit_change_seed(bitsize, 1);
425	pkt->hdr.version ^= seed;
426    }
427
428    if (fake_flag & FAKE_IHL) {		/* header length */
429	bitsize = 4;
430	seed = bit_change_seed(bitsize, 1);
431	pkt->hdr.ihl ^= seed;
432    }
433
434    if (fake_flag & FAKE_TOT_LEN) {	/* total length */
435	bitsize = sizeof(pkt->hdr.tot_len) * 8;
436	seed = bit_change_seed(bitsize, bitsize / 8);
437	pkt->hdr.tot_len ^= seed;
438    }
439
440    if (fake_flag & FAKE_FRAGMENT) {	/* fragment information */
441	/* Set reserved flag */
442	rand_val = rand() / ((RAND_MAX + 1U) / 16);
443	if (! rand_val) {
444	    if (debug)
445		fprintf (stderr, "Up reserved bit\n");
446	    pkt->hdr.frag_off |= htonl(0x80000000);
447	}
448
449	/* Set more fragments flag */
450	rand_val = rand() / ((RAND_MAX + 1U) / 3);
451	if (! rand_val) {
452	    if (debug)
453		fprintf (stderr, "Set more fragments flag\n");
454	    pkt->hdr.frag_off |= htons(0x2000);
455	}
456
457	/* Unset unfragmented flag */
458	rand_val = rand() / ((RAND_MAX + 1U) / 3);
459	if (! rand_val) {
460	    if (debug)
461		fprintf (stderr, "Unset unfragmented flag\n");
462	    pkt->hdr.frag_off &= htons(0xbfff);
463	}
464
465	/* Set fragment offset */
466	rand_val = rand() / ((RAND_MAX + 1U) / 3);
467	if (! rand_val) {
468	    bitsize = 13;
469	    seed = bit_change_seed(bitsize, 0);
470	    if (debug)
471		fprintf (stderr, "Set fragment offset %02x\n", seed);
472	    pkt->hdr.frag_off |= htons(seed);
473	}
474    }
475
476    if (fake_flag & FAKE_PROTOCOL) {	/* protocol */
477	rand_val = rand() / ((RAND_MAX + 1U) / 5);
478	switch (rand_val) {
479	    case 1:
480	    case 2:
481		if (debug)
482		    fprintf(stderr, "Bit reverse\n");
483		bitsize = sizeof(pkt->hdr.protocol) * 8;
484		seed = bit_change_seed(bitsize, 0);
485		pkt->hdr.protocol ^= seed;
486		break;
487
488	    case 3:
489	    case 4:
490		if (debug)
491		    fprintf(stderr, "Unknown Protocol\n");
492		if (rand_val) {
493		    int number;
494		    int counter;
495		    for (counter=0; counter <= 0xff; counter++) {
496			number = rand() / ((RAND_MAX + 1U) / 0x100);
497			if (getprotobynumber(number) == NULL) {
498			    pkt->hdr.protocol = number;
499			    break;
500			}
501		    }
502		}
503		break;
504
505	    default:
506		if (debug)
507		    fprintf(stderr, "Do nothing\n");
508		break;
509	}
510    }
511
512    if (fake_flag & FAKE_DADDR) {	/* destination address */
513	bitsize = sizeof(pkt->hdr.daddr) * 8;
514	seed = bit_change_seed(bitsize, bitsize / 8);
515	pkt->hdr.daddr ^= seed;
516    }
517
518    /* Recalculate checksum once */
519    pkt->hdr.check = 0;
520    pkt->hdr.check =
521	calc_checksum((u_int16_t *)&(pkt->hdr),
522		sizeof(struct iphdr));
523
524    if (fake_flag & FAKE_CHECK) {	/* checksum */
525	bitsize = sizeof(pkt->hdr.check) * 8;
526	seed = bit_change_seed(bitsize, bitsize / 8);
527	pkt->hdr.check ^= seed;
528    }
529}
530
531/*
532 * Function: send_packet()
533 *
534 * Description:
535 *  This function sends icmpv4 packet
536 *
537 * Argument:
538 *  fake_p: pointer to data of fake icmp structure
539 *
540 * Return value:
541 *  None
542 */
543void
544send_packets(struct icmpv4_fake *fake_p)
545{
546    int sock_fd;
547    int retval;
548    struct ip4_datagram pkt;
549    double start_time;
550
551    /* Open a socket */
552    sock_fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
553    if (sock_fd < 0)
554	fatal_error("socket()");
555
556    /* Bind the socket to the physical address */
557    retval = bind(sock_fd, (struct sockaddr *)&(fake_p->saddr_ll),
558			sizeof(struct sockaddr_ll));
559    if (retval < 0)
560	fatal_error("bind()");
561
562    /* Set singal hander for SIGHUP */
563    handler.sa_handler = set_signal_flag;
564    handler.sa_flags = 0;
565    if (sigfillset(&handler.sa_mask) < 0)
566	fatal_error("sigfillset()");
567    if (sigaction(SIGHUP, &handler, NULL) < 0)
568	fatal_error("sigfillset()");
569
570    /*
571     * loop for sending packets
572     */
573    pkt	= fake_p->pkt;
574    start_time = time(NULL);
575
576    for (;;) {
577	if (fake_p->fake_flag) {
578	    pkt	= fake_p->pkt;
579	    thrust_fakes(&pkt, fake_p->fake_flag);
580	}
581
582	retval = sendto(sock_fd, &pkt, fake_p->pkt_size, 0,
583		(struct sockaddr *)&(fake_p->daddr_ll),
584		sizeof(struct sockaddr_ll));
585	if (retval < 0)
586	    fatal_error("sendto()");
587
588	if (fake_p->timeout)		/* timeout */
589	    if (fake_p->timeout < difftime(time(NULL), start_time))
590		break;
591
592	if (catch_sighup) /* catch SIGHUP */
593	    break;
594    }
595
596    /* Close the socket */
597    close(sock_fd);
598}
599
600/*
601 *
602 *  Function: main()
603 *
604 */
605int
606main(int argc, char *argv[])
607{
608    struct icmpv4_fake fake_data;
609
610    debug = 0;
611    program_name = strdup(argv[0]);
612    srand(getpid());
613
614    memset(&fake_data, '\0', sizeof(struct icmpv4_fake));
615    parse_options(argc, argv, &fake_data);
616
617    complete_eth_addrs(&fake_data);
618    create_clean_packet(&fake_data);
619
620    send_packets(&fake_data);
621
622    exit(EXIT_SUCCESS);
623}
624