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