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 usage(char *program_name, int exit_value)
93{
94	FILE *stream = stdout;	/* stream where the usage is output */
95
96	if (exit_value == EXIT_FAILURE)
97		stream = stderr;
98
99	fprintf(stream, "%s [OPTION]\n"
100		"\t-I if_name\tInterface name of the source host\n"
101		"\t-S ip_addr\tIPv4 address of the source host\n"
102		"\t-M mac_addr\tMAC address of the destination host\n"
103		"\t-D ip_addr\tIPv4 address of the destination host\n"
104		"\t-s packetsize\tnumber of data bytes (exclude header)\n"
105		"\t-t value\ttimeout [sec]\n"
106		"\t-d\t\tdisplay debug informations\n"
107		"\t-h\t\tdisplay this usage\n"
108		"\n"
109		"\t[options for fake]\n"
110		"\t  -c\tbreak checksum\n"
111		"\t  -f\tbreak fragment information\n"
112		"\t  -i\tbreak IPv4 destination address\n"
113		"\t  -l\tbreak header length\n"
114		"\t  -L\tbreak total length\n"
115		"\t  -p\tbreak protocol number\n"
116		"\t  -v\tbreak IP version\n", program_name);
117	exit(exit_value);
118}
119
120/*
121 * Function: set_signal_flag()
122 *
123 * Description:
124 *  This function sets global variables accordig to signal
125 *
126 * Argument:
127 *  type: type of signal
128 *
129 * Return value:
130 *  None
131 */
132void set_signal_flag(int type)
133{
134	if (debug)
135		fprintf(stderr, "Catch signal. type is %d\n", type);
136
137	switch (type) {
138	case SIGHUP:
139		catch_sighup = 1;
140		handler.sa_handler = SIG_IGN;
141		if (sigaction(type, &handler, NULL) < 0)
142			fatal_error("sigaction()");
143		break;
144
145	default:
146		fprintf(stderr, "Unexpected signal (%d) is caught\n", type);
147		exit(EXIT_FAILURE);
148	}
149}
150
151/*
152 * Function: parse_options()
153 *
154 * Description:
155 *  This function parse the options, then modify the fake icmp data
156 *
157 * Argument:
158 *   argc:  the number of argument
159 *   argv:  arguments
160 *  fake_p: pointer to data of fake icmp data to modify
161 *
162 * Return value:
163 *  None
164 */
165void parse_options(int argc, char *argv[], struct icmpv4_fake *fake_p)
166{
167	int optc;		/* option */
168	unsigned long opt_ul;	/* option value in unsigned long */
169	double opt_d;		/* option value in double */
170	struct in_addr opt_addr;	/* option value in struct in_addr */
171	struct sockaddr_ll opt_addr_ll;	/* option value in struct sockaddr_ll */
172	int is_specified_src_ifname = 0;
173	int is_specified_saddr = 0;
174	int is_specified_daddr_ll = 0;
175	int is_specified_daddr = 0;
176
177	while ((optc = getopt(argc, argv, "I:S:M:D:s:t:dhcfilLpv")) != EOF) {
178		switch (optc) {
179		case 'I':
180			fake_p->src_ifname = strdup(optarg);
181			if (fake_p->src_ifname == NULL)
182				fatal_error("strdup() failed.");
183			is_specified_src_ifname = 1;
184			break;
185
186		case 'S':
187			if (inet_pton(AF_INET, optarg, &opt_addr) <= 0) {
188				fprintf(stderr, "Source address is wrong\n");
189				usage(program_name, EXIT_FAILURE);
190			}
191			fake_p->saddr = opt_addr;
192			is_specified_saddr = 1;
193			break;
194
195		case 'M':
196			if (eth_pton(AF_INET, optarg, &opt_addr_ll)) {
197				fprintf(stderr,
198					"Destination MAC address is wrong\n");
199				usage(program_name, EXIT_FAILURE);
200			}
201			fake_p->daddr_ll = opt_addr_ll;
202			is_specified_daddr_ll = 1;
203			break;
204
205		case 'D':
206			if (inet_pton(AF_INET, optarg, &opt_addr) <= 0) {
207				fprintf(stderr,
208					"Destination address is wrong\n");
209				usage(program_name, EXIT_FAILURE);
210			}
211			fake_p->daddr = opt_addr;
212			is_specified_daddr = 1;
213			break;
214
215		case 's':
216			opt_ul = strtoul(optarg, NULL, 0);
217			if (opt_ul > ICMPV4_DATA_MAXSIZE) {
218				fprintf(stderr,
219					"Data size sholud be less than %d\n",
220					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,
230					"Timeout should be positive value\n");
231				usage(program_name, EXIT_FAILURE);
232			}
233			fake_p->timeout = opt_d;
234			break;
235
236		case 'd':
237			debug = 1;
238			break;
239
240		case 'h':
241			usage(program_name, EXIT_SUCCESS);
242			break;
243
244			/* Options for fake */
245		case 'c':
246			fake_p->fake_flag |= FAKE_CHECK;
247			break;
248
249		case 'f':
250			fake_p->fake_flag |= FAKE_FRAGMENT;
251			break;
252
253		case 'i':
254			fake_p->fake_flag |= FAKE_DADDR;
255			break;
256
257		case 'l':
258			fake_p->fake_flag |= FAKE_IHL;
259			break;
260
261		case 'L':
262			fake_p->fake_flag |= FAKE_TOT_LEN;
263			break;
264
265		case 'p':
266			fake_p->fake_flag |= FAKE_PROTOCOL;
267			break;
268
269		case 'v':
270			fake_p->fake_flag |= FAKE_VERSION;
271			break;
272
273		default:
274			usage(program_name, EXIT_FAILURE);
275		}
276	}
277
278	if (!is_specified_src_ifname) {
279		fprintf(stderr,
280			"Interface name of the source host is not specified\n");
281		usage(program_name, EXIT_FAILURE);
282	}
283
284	if (!is_specified_saddr) {
285		fprintf(stderr, "Source IP address is not specified\n");
286		usage(program_name, EXIT_FAILURE);
287	}
288
289	if (!is_specified_daddr_ll) {
290		fprintf(stderr, "Destination MAC address is not specified\n");
291		usage(program_name, EXIT_FAILURE);
292	}
293
294	if (!is_specified_daddr) {
295		fprintf(stderr, "Destination IP address is not specified\n");
296		usage(program_name, EXIT_FAILURE);
297	}
298}
299
300/*
301 * Function: complete_eth_addrs()
302 *
303 * Description:
304 *  This function sets the source and destination ethernet address completely
305 *
306 * Argument:
307 *  fake_p: pointer to data of fake icmp structure
308 *
309 * Return value:
310 *  None
311 *
312 */
313void complete_eth_addrs(struct icmpv4_fake *fake_p)
314{
315	int sock_fd;		/* Socket for ioctl() */
316	struct ifreq ifinfo;	/* Interface information */
317
318	if ((sock_fd = socket(AF_PACKET, SOCK_DGRAM, 0)) < 0)
319		fatal_error("socket()");
320
321	/* Source */
322	fake_p->saddr_ll.sll_family = AF_PACKET;	/* Always AF_PACKET */
323	fake_p->saddr_ll.sll_protocol = htons(ETH_P_IP);	/* IPv4 */
324	fake_p->saddr_ll.sll_hatype = ARPHRD_ETHER;	/* Header type */
325	fake_p->saddr_ll.sll_pkttype = PACKET_HOST;	/* Packet type */
326	fake_p->saddr_ll.sll_halen = ETH_ALEN;	/* Length of address */
327
328	/* Get the MAC address of the interface at source host */
329	get_ifinfo(&ifinfo, sock_fd, fake_p->src_ifname, SIOCGIFHWADDR);
330	memcpy(fake_p->saddr_ll.sll_addr, ifinfo.ifr_hwaddr.sa_data, ETH_ALEN);
331
332	/* Get the interface index */
333	get_ifinfo(&ifinfo, sock_fd, fake_p->src_ifname, SIOCGIFINDEX);
334	fake_p->saddr_ll.sll_ifindex = ifinfo.ifr_ifindex;
335	fake_p->daddr_ll.sll_ifindex = ifinfo.ifr_ifindex;
336
337	close(sock_fd);
338}
339
340/*
341 * Function: create_clean_packet()
342 *
343 * Description:
344 *  This function creates icmpv4 packet without any fakes
345 *
346 * Argument:
347 *  fake_p: pointer to data of fake icmp structure
348 *
349 * Return value:
350 *  None
351 */
352void create_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) +
393					     fake_p->data_size);
394
395	/* Store the clean packet data */
396	fake_p->pkt = pkt;
397	fake_p->pkt_size = pkt_size;
398}
399
400/*
401 * Function: thrust_fakes()
402 *
403 * Description:
404 *  This function thrust fake information to the icmp packet
405 *
406 * Argument:
407 *     pkt   : Payload of the Ethernet frame (Namely, IPv6 packet
408 *  fake_flag: Flag which represents what information would be faked
409 *
410 * Return value:
411 *  None
412 */
413void thrust_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",
472					seed);
473			pkt->hdr.frag_off |= htons(seed);
474		}
475	}
476
477	if (fake_flag & FAKE_PROTOCOL) {	/* protocol */
478		rand_val = rand() / ((RAND_MAX + 1U) / 5);
479		switch (rand_val) {
480		case 1:
481		case 2:
482			if (debug)
483				fprintf(stderr, "Bit reverse\n");
484			bitsize = sizeof(pkt->hdr.protocol) * 8;
485			seed = bit_change_seed(bitsize, 0);
486			pkt->hdr.protocol ^= seed;
487			break;
488
489		case 3:
490		case 4:
491			if (debug)
492				fprintf(stderr, "Unknown Protocol\n");
493			if (rand_val) {
494				int number;
495				int counter;
496				for (counter = 0; counter <= 0xff; counter++) {
497					number =
498					    rand() / ((RAND_MAX + 1U) / 0x100);
499					if (getprotobynumber(number) == NULL) {
500						pkt->hdr.protocol = number;
501						break;
502					}
503				}
504			}
505			break;
506
507		default:
508			if (debug)
509				fprintf(stderr, "Do nothing\n");
510			break;
511		}
512	}
513
514	if (fake_flag & FAKE_DADDR) {	/* destination address */
515		bitsize = sizeof(pkt->hdr.daddr) * 8;
516		seed = bit_change_seed(bitsize, bitsize / 8);
517		pkt->hdr.daddr ^= seed;
518	}
519
520	/* Recalculate checksum once */
521	pkt->hdr.check = 0;
522	pkt->hdr.check =
523	    calc_checksum((u_int16_t *) & (pkt->hdr), sizeof(struct iphdr));
524
525	if (fake_flag & FAKE_CHECK) {	/* checksum */
526		bitsize = sizeof(pkt->hdr.check) * 8;
527		seed = bit_change_seed(bitsize, bitsize / 8);
528		pkt->hdr.check ^= seed;
529	}
530}
531
532/*
533 * Function: send_packet()
534 *
535 * Description:
536 *  This function sends icmpv4 packet
537 *
538 * Argument:
539 *  fake_p: pointer to data of fake icmp structure
540 *
541 * Return value:
542 *  None
543 */
544void send_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 main(int argc, char *argv[])
606{
607	struct icmpv4_fake fake_data;
608
609	debug = 0;
610	program_name = strdup(argv[0]);
611	srand(getpid());
612
613	memset(&fake_data, '\0', sizeof(struct icmpv4_fake));
614	parse_options(argc, argv, &fake_data);
615
616	complete_eth_addrs(&fake_data);
617	create_clean_packet(&fake_data);
618
619	send_packets(&fake_data);
620
621	exit(EXIT_SUCCESS);
622}
623