Home | History | Annotate | Download | only in ns-tools
      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 (at) 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  */
    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  */
    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
    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  */
    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
    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
    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
    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
    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
    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  */
    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  */
    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