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