• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-icmp_redirector.c
24  *
25  * Description:
26  *	This is ICMPv4/ICMPv6 redirect message sender.
27  *	The host under test assume the host where this utility run is a
28  *	gateway. When the utility receives the packet from the host
29  *	under test. This utility reply ICMP redirect message.
30  *
31  * Author:
32  *	Mitsuru Chinen <mitch@jp.ibm.com>
33  *
34  * History:
35  *	Mar 31 2006 - Created (Mitsuru Chinen)
36  *---------------------------------------------------------------------------*/
37 
38 /*
39  * Header Files
40  */
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <netdb.h>
47 #include <signal.h>
48 #include <time.h>
49 #include <unistd.h>
50 #include <sys/ioctl.h>
51 #include <sys/socket.h>
52 #include <arpa/inet.h>
53 #include <net/ethernet.h>
54 #include <net/if_arp.h>
55 #include <netinet/if_ether.h>
56 
57 #include "ns-traffic.h"
58 
59 /*
60  * Structure definition
61  */
62 struct redirector_info {
63 	int sd;
64 	char *ifname;
65 	double timeout;
66 };
67 
68 struct ip4_gateway_info {
69 	unsigned char hd_addr[ETH_ALEN];
70 	unsigned char ip_addr[4];
71 	unsigned char nexthop[4];
72 };
73 
74 struct ip6_gateway_info {
75 	unsigned char hd_addr[ETH_ALEN];
76 	struct in6_addr ip_addr;
77 	struct in6_addr nexthop;
78 };
79 
80 /*
81  * Gloval variables
82  */
83 char *program_name;		/* program name */
84 struct sigaction handler;	/* Behavior for a signal */
85 int catch_sighup;		/* When catch the SIGHUP, set to non-zero */
86 
87 /*
88  * Function: usage()
89  *
90  * Descripton:
91  *  Print the usage of this program. Then, terminate this program with
92  *  the specified exit value.
93  *
94  * Argument:
95  *  exit_value:	exit value
96  *
97  * Return value:
98  *  This function does not return.
99  */
usage(char * program_name,int exit_value)100 void usage(char *program_name, int exit_value)
101 {
102 	FILE *stream = stdout;	/* stream where the usage is output */
103 
104 	if (exit_value == EXIT_FAILURE)
105 		stream = stderr;
106 
107 	fprintf(stream, "%s [OPTION]\n"
108 		"\t-I if_name\tInterface where input/output packets\n"
109 		"\t-t value\ttimeout [sec]\n"
110 		"\t-b\t\ttimeout [sec]\n"
111 		"\t-d\t\tdisplay debug informations\n"
112 		"\t-h\t\tdisplay this usage\n", program_name);
113 	exit(exit_value);
114 }
115 
116 /*
117  * Function: set_signal_flag()
118  *
119  * Description:
120  *  This function sets global variables according to a signal
121  *
122  * Argument:
123  *  type: type of signal
124  *
125  * Return value:
126  *  None
127  */
set_signal_flag(int type)128 void set_signal_flag(int type)
129 {
130 	if (debug)
131 		fprintf(stderr, "Catch signal. type is %d\n", type);
132 
133 	switch (type) {
134 	case SIGHUP:
135 		catch_sighup = 1;
136 		handler.sa_handler = SIG_IGN;
137 		if (sigaction(type, &handler, NULL) < 0)
138 			fatal_error("sigaction()");
139 		break;
140 
141 	default:
142 		fprintf(stderr, "Unexpected signal (%d) is caught\n", type);
143 		exit(EXIT_FAILURE);
144 	}
145 }
146 
147 /*
148  * Function: parse_options()
149  *
150  * Description:
151  *  This function parse the options
152  *
153  * Argument:
154  *     argc:	    number of argument
155  *     argv:	    arguments
156  *  redirector_p:   pointer to data for the redirector information
157  *     bg_p:	    pointer to the flag of working in backgrond
158  *
159  * Return value:
160  *  None
161  */
162 void
parse_options(int argc,char * argv[],struct redirector_info * redirector_p,int * bg_p)163 parse_options(int argc, char *argv[], struct redirector_info *redirector_p,
164 	      int *bg_p)
165 {
166 	int optc;		/* option */
167 	double opt_d;		/* option value in double */
168 
169 	while ((optc = getopt(argc, argv, "I:N:t:bdh")) != EOF) {
170 		switch (optc) {
171 		case 'I':
172 			redirector_p->ifname = strdup(optarg);
173 			if (redirector_p->ifname == NULL)
174 				fatal_error("strdup() failed.");
175 			break;
176 
177 		case 't':
178 			opt_d = strtod(optarg, NULL);
179 			if (opt_d < 0.0) {
180 				fprintf(stderr,
181 					"Timeout should be positive value\n");
182 				usage(program_name, EXIT_FAILURE);
183 			}
184 			redirector_p->timeout = opt_d;
185 			break;
186 
187 		case 'b':
188 			*bg_p = 1;
189 			break;
190 
191 		case 'd':
192 			debug = 1;
193 			break;
194 
195 		case 'h':
196 			usage(program_name, EXIT_SUCCESS);
197 			break;
198 
199 		default:
200 			usage(program_name, EXIT_FAILURE);
201 		}
202 	}
203 
204 	if (redirector_p->ifname == NULL) {
205 		fprintf(stderr, "Interface name is not specified\n");
206 		usage(program_name, EXIT_FAILURE);
207 	}
208 }
209 
210 /*
211  * Function: open_socket()
212  *
213  * Description:
214  *  This function opens a socket for capture/sending
215  *
216  * Argument:
217  *  ifname: interface name
218  *
219  * Return value:
220  *  socket file descriptor for receiving packets
221  */
open_socket(const char * ifname)222 int open_socket(const char *ifname)
223 {
224 	int sd;			/* Socket to packets */
225 	struct ifreq ifinfo;	/* Interface information */
226 	struct sockaddr_ll lla;	/* Link-local address info for receiving */
227 
228 	/* Create a socket for capture */
229 	if ((sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)
230 		fatal_error("socket()");
231 
232 	/* Make a socket into non-blocking mode */
233 	if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0)
234 		fatal_error("fcntl()");
235 
236 	/* Get the logical interface number */
237 	get_ifinfo(&ifinfo, sd, ifname, SIOCGIFINDEX);
238 
239 	/* Bind to the interface */
240 	memset(&lla, '\0', sizeof(struct sockaddr_ll));
241 	lla.sll_family = PF_PACKET;
242 	lla.sll_protocol = htons(ETH_P_ALL);
243 	lla.sll_ifindex = ifinfo.ifr_ifindex;
244 	if (bind(sd, (struct sockaddr *)&lla, sizeof(struct sockaddr_ll)) < 0)
245 		fatal_error("bind()");
246 
247 	/* Change into the promiscuous mode */
248 	get_ifinfo(&ifinfo, sd, ifname, SIOCGIFFLAGS);
249 	ifinfo.ifr_flags = ifinfo.ifr_flags | IFF_PROMISC;
250 	if (ioctl(sd, SIOCSIFFLAGS, &ifinfo) < 0)
251 		fatal_error("ioctl()");
252 	if (debug)
253 		fprintf(stderr, "%s is changed into promiscuous mode\n",
254 			ifname);
255 
256 	if (debug)
257 		fprintf(stderr, "Packet receiving socket is %d\n", sd);
258 	return sd;
259 }
260 
261 /*
262  * Function: return_arp_reply()
263  *
264  * Description:
265  *  This function returns arp reply message to arp request message.
266  *  And it updates the IPv4 gateway information.
267  *
268  * Argument:
269  *      sd     : socket to send arp reply message
270  *   rcveth_p  : pointer to ether frame data
271  *   gateway_p : pointer to IPv4 gateway information
272  *
273  * Return value:
274  *  None
275  */
276 void
return_arp_reply(int sd,struct eth_frame * rcveth_p,struct ip4_gateway_info * gateway_p)277 return_arp_reply(int sd, struct eth_frame *rcveth_p,
278 		 struct ip4_gateway_info *gateway_p)
279 {
280 	int retval;
281 	struct arp_datagram *rcvarp_p;	/* ARP part of receiving frame */
282 	unsigned char new_hd_addr[ETH_ALEN];	/* New MAC address */
283 	unsigned char new_nexthop[4];	/* New next hop */
284 	size_t sndeth_size;	/* Size of sending frame */
285 	struct eth_frame sndeth;	/* sending frame */
286 	struct arp_datagram *sndarp_p;	/* ARP part of sending frame */
287 
288 	rcvarp_p = (struct arp_datagram *)&(rcveth_p->data);
289 
290 	/* If arp message is not arp request, do nothing */
291 	if (debug)
292 		fprintf(stderr, "ARP OP code is %02x\n",
293 			ntohs(rcvarp_p->hdr.ar_op));
294 	if (rcvarp_p->hdr.ar_op != htons(ARPOP_REQUEST))
295 		return;
296 
297 	/* Update the gateway information */
298 	memset(new_hd_addr, '\0', ETH_ALEN);	/* MAC address */
299 	for (;;) {
300 		new_hd_addr[3] = rand_within(0, 254);
301 		new_hd_addr[4] = rand_within(0, 254);
302 		new_hd_addr[5] = rand_within(1, 254);
303 		if (memcmp(gateway_p->hd_addr, new_hd_addr, ETH_ALEN)) {
304 			memcpy(gateway_p->hd_addr, new_hd_addr, ETH_ALEN);
305 			break;
306 		}
307 	}
308 
309 	memcpy(gateway_p->ip_addr, rcvarp_p->ar_tip, 4);	/* IP address */
310 
311 	for (;;) {		/* next hop */
312 		memcpy(new_nexthop, gateway_p->ip_addr, 4);
313 		new_nexthop[3] = rand_within(1, 254);
314 		if (memcmp(gateway_p->nexthop, new_nexthop, 4)) {
315 			memcpy(gateway_p->nexthop, new_nexthop, 4);
316 			break;
317 		}
318 	}
319 
320 	/* Build a frame to send */
321 	memset(&sndeth, '\0', sizeof(struct eth_frame));
322 	sndarp_p = (struct arp_datagram *)&(sndeth.data);
323 	sndeth_size = sizeof(struct ethhdr) + sizeof(struct arp_datagram);
324 
325 	/* Ether */
326 	memcpy(sndeth.hdr.h_dest, rcveth_p->hdr.h_source, ETH_ALEN);
327 	memcpy(sndeth.hdr.h_source, gateway_p->hd_addr, ETH_ALEN);
328 	sndeth.hdr.h_proto = htons(ETH_P_ARP);
329 
330 	/* Arp */
331 	sndarp_p->hdr.ar_hrd = htons(ARPHRD_ETHER);
332 	sndarp_p->hdr.ar_pro = htons(ETH_P_IP);
333 	sndarp_p->hdr.ar_hln = ETH_ALEN;
334 	sndarp_p->hdr.ar_pln = 4;
335 	sndarp_p->hdr.ar_op = htons(ARPOP_REPLY);
336 	memcpy(sndarp_p->ar_sha, gateway_p->hd_addr, ETH_ALEN);
337 	memcpy(sndarp_p->ar_sip, gateway_p->ip_addr, 4);
338 	memcpy(sndarp_p->ar_tha, rcvarp_p->ar_sha, ETH_ALEN);
339 	memcpy(sndarp_p->ar_tip, rcvarp_p->ar_sip, 4);
340 
341 	/* Send ARP reply */
342 	retval = write(sd, &sndeth, sndeth_size);
343 	if (retval != sndeth_size)
344 		fatal_error("write()");
345 }
346 
347 /*
348  * Function: return_icmp4_redirect()
349  *
350  * Description:
351  *  This function returns icmp redirect message
352  *
353  * Argument:
354  *      sd     : socket to send arp reply message
355  *   rcveth_p  : pointer to ether frame data
356  *  rcveth_size: size of received ehter frame
357  *    new_gw_p : pointer to new IPv4 gateway information
358  *
359  * Return value:
360  *  None
361  */
362 void
return_icmp4_redirect(int sd,struct eth_frame * rcveth_p,size_t rcveth_size,struct ip4_gateway_info * new_gw_p)363 return_icmp4_redirect(int sd, struct eth_frame *rcveth_p, size_t rcveth_size,
364 		      struct ip4_gateway_info *new_gw_p)
365 {
366 	static struct ip4_gateway_info *gw_p;	/* pointor to gateway */
367 
368 	int retval;
369 	struct ip4_datagram *rcvip_p;	/* IPv4 part of receiving frame */
370 	size_t sndeth_size;	/* Size of sending frame */
371 	struct eth_frame sndeth;	/* sending frame */
372 	struct ip4_datagram *sndip_p;	/* IPv4 part of sending frame */
373 	struct icmp4_segment *sndicmp_p;	/* ICMPv4 part of sending frame */
374 	size_t icmp4_datasize;	/* Size of sending ICMPv4 */
375 
376 	/* If MAC address in received frame is changed, update the gateway info */
377 	if (memcmp(rcveth_p->hdr.h_dest, new_gw_p->hd_addr, ETH_ALEN) == 0) {
378 		if (gw_p == NULL)
379 			if ((gw_p = malloc(sizeof(struct ip4_gateway_info))) == NULL)
380 				fatal_error("malloc()");
381 		*gw_p = *new_gw_p;
382 	} else if (gw_p == NULL
383 		   || memcmp(rcveth_p->hdr.h_dest, gw_p->hd_addr, ETH_ALEN))
384 		return;
385 
386 	rcvip_p = (struct ip4_datagram *)&(rcveth_p->data);
387 
388 	/* Build a frame to send */
389 	sndeth_size = sizeof(struct ethhdr)	/* Ether header */
390 	    +sizeof(struct iphdr)	/* IPv4 header */
391 	    +sizeof(struct icmphdr)	/* ICMPv4 header */
392 	    +rcveth_size - sizeof(struct ethhdr);	/* ICMPv4 payload */
393 	sndeth_size = (sndeth_size < ETH_DATA_MAXSIZE)
394 	    ? sndeth_size : ETH_DATA_MAXSIZE;
395 	memset(&sndeth, '\0', sizeof(struct eth_frame));
396 	sndip_p = (struct ip4_datagram *)&(sndeth.data);
397 	sndicmp_p = (struct icmp4_segment *)&(sndip_p->payload);
398 
399 	/* Ether */
400 	memcpy(sndeth.hdr.h_dest, rcveth_p->hdr.h_source, ETH_ALEN);
401 	memcpy(sndeth.hdr.h_source, gw_p->hd_addr, ETH_ALEN);
402 	sndeth.hdr.h_proto = htons(ETH_P_IP);
403 
404 	/* IP */
405 	sndip_p->hdr.version = 4;
406 	sndip_p->hdr.ihl = sizeof(struct iphdr) / 4;
407 	sndip_p->hdr.tos = 0;
408 	sndip_p->hdr.tot_len = htons(sndeth_size - sizeof(struct ethhdr));
409 	sndip_p->hdr.id = htons(IPV4_PACKET_ID);
410 	sndip_p->hdr.frag_off = htons(IPV4_DEFAULT_FLAG);
411 	sndip_p->hdr.ttl = IPV4_DEFAULT_TTL;
412 	sndip_p->hdr.protocol = IPPROTO_ICMP;
413 	sndip_p->hdr.check = 0;	/* Calculate later */
414 	memcpy((unsigned char *)&sndip_p->hdr.saddr, gw_p->ip_addr, 4);
415 	sndip_p->hdr.daddr = rcvip_p->hdr.saddr;
416 	sndip_p->hdr.check = calc_checksum((u_int16_t *) & (sndip_p->hdr),
417 					   sizeof(struct iphdr));
418 
419 	/* ICMP */
420 	sndicmp_p->hdr.type = ICMP_REDIRECT;
421 	sndicmp_p->hdr.code = ICMP_REDIR_HOST;
422 	sndicmp_p->hdr.checksum = 0;	/* Calculate later */
423 	memcpy((unsigned char *)&(sndicmp_p->hdr.un.gateway), gw_p->nexthop, 4);
424 
425 	/* ICMP payload */
426 	icmp4_datasize = rcveth_size - sizeof(struct ethhdr);
427 	icmp4_datasize =
428 	    (icmp4_datasize <
429 	     ICMPV4_DATA_MAXSIZE) ? icmp4_datasize : ICMPV4_DATA_MAXSIZE;
430 	memcpy(sndicmp_p->data, rcvip_p, icmp4_datasize);
431 
432 	/* Calculate ICMP checksum */
433 	sndicmp_p->hdr.checksum = calc_checksum((u_int16_t *) sndicmp_p,
434 						sizeof(struct icmphdr) +
435 						icmp4_datasize);
436 
437 	/* Send ICMP redirect */
438 	retval = write(sd, &sndeth, sndeth_size);
439 	if (retval != sndeth_size)
440 		fatal_error("write()");
441 }
442 
443 /*
444  * Function: return_neigh_adv()
445  *
446  * Description:
447  *  This function returns neighbor advertisement message
448  *  And this updates the gateway information.
449  *
450  * Argument:
451  *      sd     : socket to send arp reply message
452  *   rcveth_p  : pointer to ether frame data
453  *  current_eth: current MAC address
454  *   gateway_p : pointer to IPv6 gateway information
455  *
456  * Return value:
457  *  None
458  */
459 void
return_neigh_adv(int sd,struct eth_frame * rcveth_p,struct ip6_gateway_info * gateway_p)460 return_neigh_adv(int sd, struct eth_frame *rcveth_p,
461 		 struct ip6_gateway_info *gateway_p)
462 {
463 	int retval;
464 	struct ip6_datagram *rcvip6_p;	/* IPv6 part of receiving frame */
465 	struct neighbor_sol *rcvns_p;	/* NS part of receiving frame */
466 	unsigned char new_hd_addr[ETH_ALEN];	/* new MAC address */
467 	struct in6_addr new_nexthop;	/* new next hop */
468 	size_t sndeth_size;	/* size of sending frame */
469 	struct eth_frame sndeth;	/* sending frame */
470 	struct ip6_datagram *sndip6_p;	/* IPv6 part of sending frame */
471 	struct pseudo_ip6_datagram p_ip6;	/* pseudo IP header */
472 	struct neighbor_adv *sndna_p;	/* NA part of sending frame */
473 
474 	rcvip6_p = (struct ip6_datagram *)&(rcveth_p->data);
475 	rcvns_p = (struct neighbor_sol *)&(rcvip6_p->payload);
476 
477 	/* If NS is DAD NS, do nothing */
478 	if (memcmp
479 	    (&(rcvip6_p->hdr.ip6_src), &in6addr_any,
480 	     sizeof(struct in6_addr)) == 0) {
481 		if (debug) {
482 			fprintf(stderr, "Received NS is a DAD NS\n");
483 			return;
484 		}
485 	}
486 
487 	/* Update the gateway information */
488 	memset(new_hd_addr, '\0', ETH_ALEN);	/* MAC address */
489 	for (;;) {
490 		new_hd_addr[3] = rand_within(0, 254);
491 		new_hd_addr[4] = rand_within(0, 254);
492 		new_hd_addr[5] = rand_within(1, 254);
493 		if (memcmp(gateway_p->hd_addr, new_hd_addr, ETH_ALEN)) {
494 			memcpy(gateway_p->hd_addr, new_hd_addr, ETH_ALEN);
495 			break;
496 		}
497 	}
498 
499 	gateway_p->ip_addr = rcvns_p->defs.nd_ns_target;	/* IP address */
500 
501 	for (;;) {		/* next hop */
502 		memset(&new_nexthop, '\0', sizeof(struct in6_addr));
503 		new_nexthop.s6_addr[0] = 0xfe;
504 		new_nexthop.s6_addr[1] = 0x80;
505 		new_nexthop.s6_addr[15] = rand_within(1, 254);
506 		if (memcmp
507 		    (&(gateway_p->nexthop), &new_nexthop,
508 		     sizeof(struct in6_addr))) {
509 			gateway_p->nexthop = new_nexthop;
510 			break;
511 		}
512 	}
513 
514 	/* Build a frame to send */
515 	sndeth_size = sizeof(struct ethhdr) + sizeof(struct ip6_hdr)
516 	    + sizeof(struct neighbor_adv);
517 	memset(&sndeth, '\0', sizeof(struct eth_frame));
518 	sndip6_p = (struct ip6_datagram *)&(sndeth.data);
519 	sndna_p = (struct neighbor_adv *)&(sndip6_p->payload);
520 
521 	/* Ether */
522 	memcpy(sndeth.hdr.h_dest, rcvns_p->src_laddr, ETH_ALEN);
523 	memcpy(sndeth.hdr.h_source, gateway_p->hd_addr, ETH_ALEN);
524 	sndeth.hdr.h_proto = htons(ETH_P_IPV6);
525 
526 	/* IPv6 */
527 	sndip6_p->hdr.ip6_vfc = 6 << 4;
528 	sndip6_p->hdr.ip6_flow |= 0;
529 	sndip6_p->hdr.ip6_plen = htons(sizeof(struct neighbor_adv));
530 	sndip6_p->hdr.ip6_nxt = IPPROTO_ICMPV6;
531 	sndip6_p->hdr.ip6_hlim = 255;
532 	sndip6_p->hdr.ip6_src = gateway_p->ip_addr;
533 	sndip6_p->hdr.ip6_dst = rcvip6_p->hdr.ip6_src;
534 
535 	/* Neighbor Advertisement */
536 	sndna_p->defs.nd_na_type = ND_NEIGHBOR_ADVERT;
537 	sndna_p->defs.nd_na_code = 0;
538 	sndna_p->defs.nd_na_cksum = 0;	/* Calculate later */
539 	sndna_p->defs.nd_na_target = gateway_p->ip_addr;
540 	sndna_p->defs.nd_na_flags_reserved
541 	    = ND_NA_FLAG_ROUTER | ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE;
542 	sndna_p->tla_opt.nd_opt_type = ND_OPT_TARGET_LINKADDR;
543 	sndna_p->tla_opt.nd_opt_len = 1;
544 	memcpy(sndna_p->tgt_laddr, &(gateway_p->hd_addr), ETH_ALEN);
545 
546 	/* Pseudo IPv6 datagram for checksum calculation */
547 	memset(&p_ip6, '\0', sizeof(struct pseudo_ip6_datagram));
548 	p_ip6.hdr.p_ip6_src = sndip6_p->hdr.ip6_src;
549 	p_ip6.hdr.p_ip6_dst = sndip6_p->hdr.ip6_dst;
550 	p_ip6.hdr.p_ip6_plen = sndip6_p->hdr.ip6_plen;
551 	p_ip6.hdr.p_ip6_zero1 = 0;
552 	p_ip6.hdr.p_ip6_zero2 = 0;
553 	p_ip6.hdr.p_ip6_nxt = sndip6_p->hdr.ip6_nxt;
554 	memcpy(p_ip6.payload, sndna_p, sizeof(struct neighbor_adv));
555 
556 	/* Calculate checksum */
557 	sndna_p->defs.nd_na_cksum = calc_checksum((u_int16_t *) (&p_ip6),
558 						  sizeof(struct pseudo_ip6_hdr)
559 						  +
560 						  sizeof(struct neighbor_adv));
561 
562 	/* Send Neighbor Advertisement reply */
563 	retval = write(sd, &sndeth, sndeth_size);
564 	if (retval != sndeth_size)
565 		fatal_error("write()");
566 }
567 
568 /*
569  * Function: return_icmp6_redirect()
570  *
571  * Description:
572  *  This function returns an ICMPv6 redirect message
573  *
574  * Argument:
575  *      sd     : socket to send arp reply message
576  *   rcveth_p  : pointer to ether frame data
577  *  rcveth_size: size of received ehter frame
578  *   new_gw_p  : pointer to new IPv4 gateway information
579  *
580  * Return value:
581  *  None
582  */
583 void
return_icmp6_redirect(int sd,struct eth_frame * rcveth_p,size_t rcveth_size,struct ip6_gateway_info * new_gw_p)584 return_icmp6_redirect(int sd, struct eth_frame *rcveth_p, size_t rcveth_size,
585 		      struct ip6_gateway_info *new_gw_p)
586 {
587 	static struct ip6_gateway_info *gw_p = NULL;	/* pointor to gateway */
588 
589 	int retval;
590 	struct ip6_datagram *rcvip6_p;	/* IPv6 part of receiving frame */
591 	struct eth_frame sndeth;	/* sending frame */
592 	size_t sndeth_size;	/* size of sending frame */
593 	struct ip6_datagram *sndip6_p;	/* IPv6 part of sending frame */
594 	size_t ip6_payload_size;	/* payload size of IPv6 part */
595 	struct pseudo_ip6_datagram p_ip6;	/* pseudo header for checksum */
596 	struct neighbor_redirect *sndrd_p;	/* ICMPv6 part of sending frame */
597 	size_t redirect_optsize;	/* Option size of ICMPv6 */
598 
599 	rcvip6_p = (struct ip6_datagram *)&(rcveth_p->data);
600 
601 	/* If MAC address in received frame is changed, update the gateway info */
602 	if (memcmp(rcveth_p->hdr.h_dest, new_gw_p->hd_addr, ETH_ALEN) == 0) {
603 		if (gw_p == NULL)
604 			if ((gw_p = malloc(sizeof(struct in6_addr))) == NULL)
605 				fatal_error("malloc()");
606 		*gw_p = *new_gw_p;
607 	} else if (gw_p == NULL
608 		   || memcmp(rcveth_p->hdr.h_dest, gw_p->hd_addr, ETH_ALEN))
609 		return;
610 
611 	/* Build a frame to send */
612 	memset(&sndeth, '\0', sizeof(struct eth_frame));
613 	sndip6_p = (struct ip6_datagram *)&(sndeth.data);
614 	sndrd_p = (struct neighbor_redirect *)&(sndip6_p->payload);
615 	redirect_optsize = sizeof(struct nd_opt_rd_hdr)
616 	    + rcveth_size - sizeof(struct ethhdr);
617 	redirect_optsize = (redirect_optsize < RDOPT_MAXSIZE)
618 	    ? redirect_optsize : RDOPT_MAXSIZE;
619 	ip6_payload_size = sizeof(struct nd_redirect) + redirect_optsize;
620 	sndeth_size = sizeof(struct ethhdr) + sizeof(struct ip6_hdr)
621 	    + ip6_payload_size;
622 
623 	/* Ether */
624 	memcpy(sndeth.hdr.h_dest, rcveth_p->hdr.h_source, ETH_ALEN);
625 	memcpy(sndeth.hdr.h_source, gw_p->hd_addr, ETH_ALEN);
626 	sndeth.hdr.h_proto = htons(ETH_P_IPV6);
627 
628 	/* IPv6 */
629 	sndip6_p->hdr.ip6_vfc = 6 << 4;
630 	sndip6_p->hdr.ip6_flow |= 0;
631 	sndip6_p->hdr.ip6_plen = htons(ip6_payload_size);
632 	sndip6_p->hdr.ip6_nxt = IPPROTO_ICMPV6;
633 	sndip6_p->hdr.ip6_hlim = 255;
634 	sndip6_p->hdr.ip6_src = gw_p->ip_addr;
635 	sndip6_p->hdr.ip6_dst = rcvip6_p->hdr.ip6_src;
636 
637 	/* Rediret Message */
638 	sndrd_p->defs.nd_rd_type = ND_REDIRECT;
639 	sndrd_p->defs.nd_rd_code = 0;
640 	sndrd_p->defs.nd_rd_cksum = 0;	/* Calculate later */
641 	sndrd_p->defs.nd_rd_reserved = 0;
642 	sndrd_p->defs.nd_rd_target = gw_p->nexthop;
643 	sndrd_p->defs.nd_rd_dst = rcvip6_p->hdr.ip6_dst;;
644 	sndrd_p->rdopt_hdr.nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
645 	sndrd_p->rdopt_hdr.nd_opt_rh_len = redirect_optsize / 8;
646 	memcpy(sndrd_p->rdopt_data, rcvip6_p, redirect_optsize);
647 
648 	/* Pseudo IPv6 datagram for checksum calculation */
649 	memset(&p_ip6, '\0', sizeof(struct pseudo_ip6_datagram));
650 	p_ip6.hdr.p_ip6_src = sndip6_p->hdr.ip6_src;
651 	p_ip6.hdr.p_ip6_dst = sndip6_p->hdr.ip6_dst;
652 	p_ip6.hdr.p_ip6_plen = sndip6_p->hdr.ip6_plen;
653 	p_ip6.hdr.p_ip6_zero1 = 0;
654 	p_ip6.hdr.p_ip6_zero2 = 0;
655 	p_ip6.hdr.p_ip6_nxt = sndip6_p->hdr.ip6_nxt;
656 	memcpy(p_ip6.payload, sndrd_p, ip6_payload_size);
657 
658 	/* Calculate checksum */
659 	sndrd_p->defs.nd_rd_cksum = calc_checksum((u_int16_t *) (&p_ip6),
660 						  sizeof(struct pseudo_ip6_hdr)
661 						  + ip6_payload_size);
662 
663 	/* Send ICMPv6 redirct message */
664 	retval = write(sd, &sndeth, sndeth_size);
665 	if (retval != sndeth_size)
666 		fatal_error("write()");
667 }
668 
669 /*
670  * Function: analyze_ip6_datagram()
671  *
672  * Description:
673  *  This function analyze captured IPv6 datagram
674  *
675  * Argument:
676  *       sd      : socket to send arp reply message
677  *    rcveth_p   : pointer to ether frame data
678  *   rcveth_size : size of received ehter frame
679  *    gateway_p  : pointer to IPv6 gateway information
680  *
681  * Return value:
682  *  None
683  */
684 void
analyze_ip6_datagram(int sd,struct eth_frame * rcveth_p,size_t rcveth_size,struct ip6_gateway_info * gateway_p)685 analyze_ip6_datagram(int sd, struct eth_frame *rcveth_p, size_t rcveth_size,
686 		     struct ip6_gateway_info *gateway_p)
687 {
688 	struct ip6_datagram *rcvip6_p;	/* IPv6 Part of receiving frame */
689 	struct icmp6_segment *rcvicmp6_p;	/* ICMPv6 Part of receiving frame */
690 	uint8_t nxt_hdr;	/* Next header of IPv6 */
691 	uint8_t icmp6_type;	/* Type of ICMPv6 */
692 
693 	rcvip6_p = (struct ip6_datagram *)&(rcveth_p->data);
694 	rcvicmp6_p = (struct icmp6_segment *)&(rcvip6_p->payload);
695 
696 	nxt_hdr = rcvip6_p->hdr.ip6_nxt;
697 	switch (nxt_hdr) {
698 	case IPPROTO_ICMPV6:
699 		icmp6_type = rcvicmp6_p->hdr.icmp6_type;
700 		switch (icmp6_type) {
701 		case ND_NEIGHBOR_SOLICIT:
702 			if (debug)
703 				fprintf(stderr, "Received ICMP NS\n");
704 			return_neigh_adv(sd, rcveth_p, gateway_p);
705 			break;
706 
707 		case ICMP6_ECHO_REQUEST:
708 			if (debug)
709 				fprintf(stderr, "Received ICMP Echo Request\n");
710 			return_icmp6_redirect(sd, rcveth_p, rcveth_size,
711 					      gateway_p);
712 			break;
713 		}
714 		break;
715 
716 	case IPPROTO_UDP:
717 		if (debug)
718 			fprintf(stderr, "Received UDP message\n");
719 		return_icmp6_redirect(sd, rcveth_p, rcveth_size, gateway_p);
720 		break;
721 	}
722 }
723 
724 /*
725  * Function: capture_frames()
726  *
727  * Description:
728  *  This function captures frames
729  *
730  * Argument:
731  *  redirector_p: pointer to data for the redirector information
732  *
733  * Return value:
734  *  socket file descriptor for receiving packets
735  */
capture_frames(struct redirector_info * redirector_p)736 void capture_frames(struct redirector_info *redirector_p)
737 {
738 	struct ip4_gateway_info ip4_gateway;	/* IPv4 gateway information */
739 	struct ip6_gateway_info ip6_gateway;	/* IPv6 gateway information */
740 	struct eth_frame frame;	/* captured frame data */
741 	ssize_t frame_size;	/* captured frame size */
742 	double start_time;	/* capture starting time */
743 	int sd = redirector_p->sd;	/* socket fd for capture */
744 
745 	/* Initialize gateway information */
746 	memset(&ip4_gateway, '\0', sizeof(struct ip4_gateway_info));
747 	memset(&ip6_gateway, '\0', sizeof(struct ip6_gateway_info));
748 
749 	/* Set singal hander for SIGHUP */
750 	handler.sa_handler = set_signal_flag;
751 	handler.sa_flags = 0;
752 	if (sigfillset(&handler.sa_mask) < 0)
753 		fatal_error("sigfillset()");
754 	if (sigaction(SIGHUP, &handler, NULL) < 0)
755 		fatal_error("sigfillset()");
756 
757 	/*
758 	 * loop for capture
759 	 */
760 	start_time = time(NULL);
761 
762 	for (;;) {
763 		frame_size = read(sd, (void *)(&frame), sizeof(frame));
764 		if (frame_size < 0) {
765 			if (errno != EAGAIN)
766 				fatal_error("read()");
767 		} else {
768 			switch (ntohs(frame.hdr.h_proto)) {
769 			case ETH_P_ARP:
770 				if (debug)
771 					fprintf(stderr, "Get ARP packet\n");
772 				return_arp_reply(sd, &frame, &ip4_gateway);
773 				break;
774 
775 			case ETH_P_IP:
776 				if (debug)
777 					fprintf(stderr, "Get IPv4 packet\n");
778 				return_icmp4_redirect(sd, &frame, frame_size,
779 						      &ip4_gateway);
780 				break;
781 
782 			case ETH_P_IPV6:
783 				if (debug)
784 					fprintf(stderr, "Get IPv6 packet\n");
785 				analyze_ip6_datagram(sd, &frame, frame_size,
786 						     &ip6_gateway);
787 				break;
788 			}
789 		}
790 
791 		if (redirector_p->timeout)
792 			if (redirector_p->timeout <
793 			    difftime(time(NULL), start_time))
794 				break;
795 
796 		if (catch_sighup)	/* catch SIGHUP */
797 			break;
798 	}
799 }
800 
801 /*
802  *
803  *  Function: main()
804  *
805  */
main(int argc,char * argv[])806 int main(int argc, char *argv[])
807 {
808 	struct redirector_info redirector;
809 	int background = 0;
810 
811 	debug = 0;
812 	program_name = strdup(argv[0]);
813 	srand(getpid());
814 
815 	memset(&redirector, '\0', sizeof(struct redirector_info));
816 	parse_options(argc, argv, &redirector, &background);
817 
818 	redirector.sd = open_socket(redirector.ifname);
819 
820 	if (background)		/* Work in the background */
821 		if (daemon(0, 0) < 0)
822 			fatal_error("daemon()");
823 
824 	capture_frames(&redirector);
825 
826 	close(redirector.sd);
827 	exit(EXIT_SUCCESS);
828 }
829