blob: f62d39a4b13b698af08230b6541a5f37d6af7a53 [file] [log] [blame]
/******************************************************************************/
/* */
/* Copyright (c) International Business Machines Corp., 2006 */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
/* the GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* */
/******************************************************************************/
/*
* File:
* ns-icmp_redirector.c
*
* Description:
* This is ICMPv4/ICMPv6 redirect message sender.
* The host under test assume the host where this utility run is a
* gateway. When the utility receives the packet from the host
* under test. This utility reply ICMP redirect message.
*
* Author:
* Mitsuru Chinen <mitch@jp.ibm.com>
*
* History:
* Mar 31 2006 - Created (Mitsuru Chinen)
*---------------------------------------------------------------------------*/
/*
* Header Files
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <netinet/if_ether.h>
#include "ns-traffic.h"
/*
* Structure definition
*/
struct redirector_info {
int sd;
char *ifname;
double timeout;
};
struct ip4_gateway_info {
unsigned char hd_addr[ETH_ALEN];
unsigned char ip_addr[4];
unsigned char nexthop[4];
};
struct ip6_gateway_info {
unsigned char hd_addr[ETH_ALEN];
struct in6_addr ip_addr;
struct in6_addr nexthop;
};
/*
* Gloval variables
*/
char *program_name; /* program name */
struct sigaction handler; /* Behavior for a signal */
int catch_sighup; /* When catch the SIGHUP, set to non-zero */
/*
* Function: usage()
*
* Descripton:
* Print the usage of this program. Then, terminate this program with
* the specified exit value.
*
* Argument:
* exit_value: exit value
*
* Return value:
* This function does not return.
*/
void
usage (char *program_name, int exit_value)
{
FILE *stream = stdout; /* stream where the usage is output */
if (exit_value == EXIT_FAILURE)
stream = stderr;
fprintf (stream, "%s [OPTION]\n"
"\t-I if_name\tInterface where input/output packets\n"
"\t-t value\ttimeout [sec]\n"
"\t-b\t\ttimeout [sec]\n"
"\t-d\t\tdisplay debug informations\n"
"\t-h\t\tdisplay this usage\n"
, program_name);
exit (exit_value);
}
/*
* Function: set_signal_flag()
*
* Description:
* This function sets global variables according to a signal
*
* Argument:
* type: type of signal
*
* Return value:
* None
*/
void
set_signal_flag(int type)
{
if (debug)
fprintf(stderr, "Catch signal. type is %d\n", type);
switch (type) {
case SIGHUP:
catch_sighup = 1;
handler.sa_handler = SIG_IGN;
if (sigaction(type, &handler, NULL) < 0)
fatal_error("sigaction()");
break;
default:
fprintf(stderr, "Unexpected signal (%d) is caught\n", type);
exit(EXIT_FAILURE);
}
}
/*
* Function: parse_options()
*
* Description:
* This function parse the options
*
* Argument:
* argc: number of argument
* argv: arguments
* redirector_p: pointer to data for the redirector information
* bg_p: pointer to the flag of working in backgrond
*
* Return value:
* None
*/
void
parse_options(int argc, char *argv[], struct redirector_info *redirector_p, int *bg_p)
{
int optc; /* option */
double opt_d; /* option value in double */
while ((optc = getopt(argc, argv, "I:N:t:bdh")) != EOF) {
switch (optc) {
case 'I':
redirector_p->ifname = strdup(optarg);
if (redirector_p->ifname == NULL)
fatal_error("strdup() failed.");
break;
case 't':
opt_d = strtod(optarg, NULL);
if (opt_d < 0.0) {
fprintf(stderr, "Timeout should be positive value\n");
usage(program_name, EXIT_FAILURE);
}
redirector_p->timeout = opt_d;
break;
case 'b':
*bg_p = 1;
break;
case 'd':
debug = 1;
break;
case 'h':
usage(program_name, EXIT_SUCCESS);
break;
default:
usage(program_name, EXIT_FAILURE);
}
}
if (redirector_p->ifname == NULL) {
fprintf(stderr, "Interface name is not specified\n");
usage(program_name, EXIT_FAILURE);
}
}
/*
* Function: open_socket()
*
* Description:
* This function opens a socket for capture/sending
*
* Argument:
* ifname: interface name
*
* Return value:
* socket file descriptor for receiving packets
*/
int
open_socket(const char *ifname)
{
int sd; /* Socket to packets */
struct ifreq ifinfo; /* Interface information */
struct sockaddr_ll lla; /* Link-local address info for receiving */
/* Create a socket for capture */
if ((sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)
fatal_error("socket()");
/* Make a socket into non-blocking mode */
if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0)
fatal_error("fcntl()");
/* Get the logical interface number */
get_ifinfo(&ifinfo, sd, ifname, SIOCGIFINDEX);
/* Bind to the interface */
memset(&lla, '\0', sizeof(struct sockaddr_ll));
lla.sll_family = PF_PACKET;
lla.sll_protocol = htons(ETH_P_ALL);
lla.sll_ifindex = ifinfo.ifr_ifindex;
if (bind(sd, (struct sockaddr *)&lla, sizeof(struct sockaddr_ll)) < 0)
fatal_error("bind()");
/* Change into the promiscuous mode */
get_ifinfo(&ifinfo, sd, ifname, SIOCGIFFLAGS);
ifinfo.ifr_flags = ifinfo.ifr_flags | IFF_PROMISC;
if (ioctl(sd, SIOCSIFFLAGS, &ifinfo) < 0)
fatal_error("ioctl()");
if (debug)
fprintf(stderr, "%s is changed into promiscuous mode\n", ifname);
if (debug)
fprintf(stderr, "Packet receiving socket is %d\n", sd);
return sd;
}
/*
* Function: return_arp_reply()
*
* Description:
* This function returns arp reply message to arp request message.
* And it updates the IPv4 gateway information.
*
* Argument:
* sd : socket to send arp reply message
* rcveth_p : pointer to ether frame data
* gateway_p : pointer to IPv4 gateway information
*
* Return value:
* None
*/
void
return_arp_reply(int sd, struct eth_frame *rcveth_p, struct ip4_gateway_info *gateway_p)
{
int retval;
struct arp_datagram *rcvarp_p; /* ARP part of receiving frame */
unsigned char new_hd_addr[ETH_ALEN]; /* New MAC address */
unsigned char new_nexthop[4]; /* New next hop */
size_t sndeth_size; /* Size of sending frame */
struct eth_frame sndeth; /* sending frame */
struct arp_datagram *sndarp_p; /* ARP part of sending frame */
rcvarp_p = (struct arp_datagram *)&(rcveth_p->data);
/* If arp message is not arp request, do nothing */
if (debug)
fprintf(stderr, "ARP OP code is %02x\n", ntohs(rcvarp_p->hdr.ar_op));
if (rcvarp_p->hdr.ar_op != htons(ARPOP_REQUEST))
return;
/* Update the gateway information */
memset(new_hd_addr, '\0', ETH_ALEN); /* MAC address */
for (;;) {
new_hd_addr[3] = rand_within(0, 254);
new_hd_addr[4] = rand_within(0, 254);
new_hd_addr[5] = rand_within(1, 254);
if (memcmp(gateway_p->hd_addr, new_hd_addr, ETH_ALEN)) {
memcpy(gateway_p->hd_addr, new_hd_addr, ETH_ALEN);
break;
}
}
memcpy(gateway_p->ip_addr, rcvarp_p->ar_tip, 4); /* IP address */
for (;;) { /* next hop */
memcpy(new_nexthop, gateway_p->ip_addr, 4);
new_nexthop[3] = rand_within(1, 254);
if (memcmp(gateway_p->nexthop, new_nexthop, 4)) {
memcpy(gateway_p->nexthop, new_nexthop, 4);
break;
}
}
/* Build a frame to send */
memset(&sndeth, '\0', sizeof(struct eth_frame));
sndarp_p = (struct arp_datagram *)&(sndeth.data);
sndeth_size = sizeof(struct ethhdr) + sizeof(struct arp_datagram);
/* Ether */
memcpy(sndeth.hdr.h_dest, rcveth_p->hdr.h_source, ETH_ALEN);
memcpy(sndeth.hdr.h_source, gateway_p->hd_addr, ETH_ALEN);
sndeth.hdr.h_proto = htons(ETH_P_ARP);
/* Arp */
sndarp_p->hdr.ar_hrd = htons(ARPHRD_ETHER);
sndarp_p->hdr.ar_pro = htons(ETH_P_IP);
sndarp_p->hdr.ar_hln = ETH_ALEN;
sndarp_p->hdr.ar_pln = 4;
sndarp_p->hdr.ar_op = htons(ARPOP_REPLY);
memcpy(sndarp_p->ar_sha, gateway_p->hd_addr, ETH_ALEN);
memcpy(sndarp_p->ar_sip, gateway_p->ip_addr, 4);
memcpy(sndarp_p->ar_tha, rcvarp_p->ar_sha, ETH_ALEN);
memcpy(sndarp_p->ar_tip, rcvarp_p->ar_sip, 4);
/* Send ARP reply */
retval = write(sd, &sndeth, sndeth_size);
if (retval != sndeth_size)
fatal_error("write()");
}
/*
* Function: return_icmp4_redirect()
*
* Description:
* This function returns icmp redirect message
*
* Argument:
* sd : socket to send arp reply message
* rcveth_p : pointer to ether frame data
* rcveth_size: size of received ehter frame
* new_gw_p : pointer to new IPv4 gateway information
*
* Return value:
* None
*/
void
return_icmp4_redirect(int sd, struct eth_frame *rcveth_p, size_t rcveth_size, struct ip4_gateway_info *new_gw_p)
{
static struct ip4_gateway_info *gw_p; /* pointor to gateway */
int retval;
struct ip4_datagram *rcvip_p; /* IPv4 part of receiving frame */
size_t sndeth_size; /* Size of sending frame */
struct eth_frame sndeth; /* sending frame */
struct ip4_datagram *sndip_p; /* IPv4 part of sending frame */
struct icmp4_segment *sndicmp_p; /* ICMPv4 part of sending frame */
size_t icmp4_datasize; /* Size of sending ICMPv4 */
/* If MAC address in received frame is changed, update the gateway info */
if (memcmp(rcveth_p->hdr.h_dest, new_gw_p->hd_addr, ETH_ALEN) == 0) {
if (gw_p == NULL)
if ((gw_p = malloc(sizeof(struct ip4_gateway_info))) == NULL)
fatal_error("malloc()");
*gw_p = *new_gw_p;
} else if (gw_p == NULL
|| memcmp(rcveth_p->hdr.h_dest, gw_p->hd_addr, ETH_ALEN))
return;
rcvip_p = (struct ip4_datagram *)&(rcveth_p->data);
/* Build a frame to send */
sndeth_size = sizeof(struct ethhdr) /* Ether header */
+ sizeof(struct iphdr) /* IPv4 header */
+ sizeof(struct icmphdr) /* ICMPv4 header */
+ rcveth_size - sizeof(struct ethhdr); /* ICMPv4 payload */
sndeth_size = (sndeth_size < ETH_DATA_MAXSIZE)
? sndeth_size : ETH_DATA_MAXSIZE;
memset(&sndeth, '\0', sizeof(struct eth_frame));
sndip_p = (struct ip4_datagram *)&(sndeth.data);
sndicmp_p = (struct icmp4_segment *)&(sndip_p->payload);
/* Ether */
memcpy(sndeth.hdr.h_dest, rcveth_p->hdr.h_source, ETH_ALEN);
memcpy(sndeth.hdr.h_source, gw_p->hd_addr, ETH_ALEN);
sndeth.hdr.h_proto = htons(ETH_P_IP);
/* IP */
sndip_p->hdr.version = 4;
sndip_p->hdr.ihl = sizeof(struct iphdr) / 4;
sndip_p->hdr.tos = 0;
sndip_p->hdr.tot_len = htons(sndeth_size - sizeof(struct ethhdr));
sndip_p->hdr.id = htons(IPV4_PACKET_ID);
sndip_p->hdr.frag_off = htons(IPV4_DEFAULT_FLAG);
sndip_p->hdr.ttl = IPV4_DEFAULT_TTL;
sndip_p->hdr.protocol = IPPROTO_ICMP;
sndip_p->hdr.check = 0; /* Calculate later */
memcpy((unsigned char *)&sndip_p->hdr.saddr, gw_p->ip_addr, 4);
sndip_p->hdr.daddr = rcvip_p->hdr.saddr;
sndip_p->hdr.check = calc_checksum((u_int16_t *)&(sndip_p->hdr),
sizeof(struct iphdr));
/* ICMP */
sndicmp_p->hdr.type = ICMP_REDIRECT;
sndicmp_p->hdr.code = ICMP_REDIR_HOST;
sndicmp_p->hdr.checksum = 0; /* Calculate later */
memcpy((unsigned char *)&(sndicmp_p->hdr.un.gateway), gw_p->nexthop, 4);
/* ICMP payload */
icmp4_datasize = rcveth_size - sizeof(struct ethhdr);
icmp4_datasize = (icmp4_datasize < ICMPV4_DATA_MAXSIZE) ? icmp4_datasize : ICMPV4_DATA_MAXSIZE;
memcpy(sndicmp_p->data, rcvip_p, icmp4_datasize);
/* Calculate ICMP checksum */
sndicmp_p->hdr.checksum = calc_checksum((u_int16_t *)sndicmp_p,
sizeof(struct icmphdr) + icmp4_datasize);
/* Send ICMP redirect */
retval = write(sd, &sndeth, sndeth_size);
if (retval != sndeth_size)
fatal_error("write()");
}
/*
* Function: return_neigh_adv()
*
* Description:
* This function returns neighbor advertisement message
* And this updates the gateway information.
*
* Argument:
* sd : socket to send arp reply message
* rcveth_p : pointer to ether frame data
* current_eth: current MAC address
* gateway_p : pointer to IPv6 gateway information
*
* Return value:
* None
*/
void
return_neigh_adv(int sd, struct eth_frame *rcveth_p, struct ip6_gateway_info *gateway_p)
{
int retval;
struct ip6_datagram *rcvip6_p; /* IPv6 part of receiving frame */
struct neighbor_sol *rcvns_p; /* NS part of receiving frame */
unsigned char new_hd_addr[ETH_ALEN]; /* new MAC address */
struct in6_addr new_nexthop; /* new next hop */
size_t sndeth_size; /* size of sending frame */
struct eth_frame sndeth; /* sending frame */
struct ip6_datagram *sndip6_p; /* IPv6 part of sending frame */
struct pseudo_ip6_datagram p_ip6; /* pseudo IP header */
struct neighbor_adv *sndna_p; /* NA part of sending frame */
rcvip6_p = (struct ip6_datagram *)&(rcveth_p->data);
rcvns_p = (struct neighbor_sol *)&(rcvip6_p->payload);
/* If NS is DAD NS, do nothing */
if (memcmp(&(rcvip6_p->hdr.ip6_src), &in6addr_any, sizeof(struct in6_addr)) == 0) {
if (debug) {
fprintf(stderr, "Received NS is a DAD NS\n");
return;
}
}
/* Update the gateway information */
memset(new_hd_addr, '\0', ETH_ALEN); /* MAC address */
for (;;) {
new_hd_addr[3] = rand_within(0, 254);
new_hd_addr[4] = rand_within(0, 254);
new_hd_addr[5] = rand_within(1, 254);
if (memcmp(gateway_p->hd_addr, new_hd_addr, ETH_ALEN)) {
memcpy(gateway_p->hd_addr, new_hd_addr, ETH_ALEN);
break;
}
}
gateway_p->ip_addr = rcvns_p->defs.nd_ns_target; /* IP address */
for (;;) { /* next hop */
memset(&new_nexthop, '\0', sizeof(struct in6_addr));
new_nexthop.s6_addr[0] = 0xfe;
new_nexthop.s6_addr[1] = 0x80;
new_nexthop.s6_addr[15] = rand_within(1, 254);
if (memcmp(&(gateway_p->nexthop), &new_nexthop, sizeof(struct in6_addr))) {
gateway_p->nexthop = new_nexthop;
break;
}
}
/* Build a frame to send */
sndeth_size = sizeof(struct ethhdr) + sizeof(struct ip6_hdr)
+ sizeof(struct neighbor_adv);
memset(&sndeth, '\0', sizeof(struct eth_frame));
sndip6_p = (struct ip6_datagram *)&(sndeth.data);
sndna_p = (struct neighbor_adv *)&(sndip6_p->payload);
/* Ether */
memcpy(sndeth.hdr.h_dest, rcvns_p->src_laddr, ETH_ALEN);
memcpy(sndeth.hdr.h_source, gateway_p->hd_addr, ETH_ALEN);
sndeth.hdr.h_proto = htons(ETH_P_IPV6);
/* IPv6 */
sndip6_p->hdr.ip6_vfc = 6 << 4;
sndip6_p->hdr.ip6_flow |= 0;
sndip6_p->hdr.ip6_plen = htons(sizeof(struct neighbor_adv));
sndip6_p->hdr.ip6_nxt = IPPROTO_ICMPV6;
sndip6_p->hdr.ip6_hlim = 255;
sndip6_p->hdr.ip6_src = gateway_p->ip_addr;
sndip6_p->hdr.ip6_dst = rcvip6_p->hdr.ip6_src;
/* Neighbor Advertisement */
sndna_p->defs.nd_na_type = ND_NEIGHBOR_ADVERT;
sndna_p->defs.nd_na_code = 0;
sndna_p->defs.nd_na_cksum = 0; /* Calculate later */
sndna_p->defs.nd_na_target = gateway_p->ip_addr;
sndna_p->defs.nd_na_flags_reserved
= ND_NA_FLAG_ROUTER | ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE;
sndna_p->tla_opt.nd_opt_type = ND_OPT_TARGET_LINKADDR;
sndna_p->tla_opt.nd_opt_len = 1;
memcpy(sndna_p->tgt_laddr, &(gateway_p->hd_addr), ETH_ALEN);
/* Pseudo IPv6 datagram for checksum calculation */
memset(&p_ip6, '\0', sizeof(struct pseudo_ip6_datagram));
p_ip6.hdr.p_ip6_src = sndip6_p->hdr.ip6_src;
p_ip6.hdr.p_ip6_dst = sndip6_p->hdr.ip6_dst;
p_ip6.hdr.p_ip6_plen = sndip6_p->hdr.ip6_plen;
p_ip6.hdr.p_ip6_zero1 = 0;
p_ip6.hdr.p_ip6_zero2 = 0;
p_ip6.hdr.p_ip6_nxt = sndip6_p->hdr.ip6_nxt;
memcpy(p_ip6.payload, sndna_p, sizeof(struct neighbor_adv));
/* Calculate checksum */
sndna_p->defs.nd_na_cksum = calc_checksum((u_int16_t *)(&p_ip6),
sizeof(struct pseudo_ip6_hdr) + sizeof(struct neighbor_adv));
/* Send Neighbor Advertisement reply */
retval = write(sd, &sndeth, sndeth_size);
if (retval != sndeth_size)
fatal_error("write()");
}
/*
* Function: return_icmp6_redirect()
*
* Description:
* This function returns an ICMPv6 redirect message
*
* Argument:
* sd : socket to send arp reply message
* rcveth_p : pointer to ether frame data
* rcveth_size: size of received ehter frame
* new_gw_p : pointer to new IPv4 gateway information
*
* Return value:
* None
*/
void
return_icmp6_redirect(int sd, struct eth_frame *rcveth_p, size_t rcveth_size, struct ip6_gateway_info *new_gw_p)
{
static struct ip6_gateway_info *gw_p = NULL; /* pointor to gateway */
int retval;
struct ip6_datagram *rcvip6_p; /* IPv6 part of receiving frame */
struct eth_frame sndeth; /* sending frame */
size_t sndeth_size; /* size of sending frame */
struct ip6_datagram *sndip6_p; /* IPv6 part of sending frame */
size_t ip6_payload_size; /* payload size of IPv6 part */
struct pseudo_ip6_datagram p_ip6; /* pseudo header for checksum */
struct neighbor_redirect *sndrd_p; /* ICMPv6 part of sending frame */
size_t redirect_optsize; /* Option size of ICMPv6 */
rcvip6_p = (struct ip6_datagram *)&(rcveth_p->data);
/* If MAC address in received frame is changed, update the gateway info */
if (memcmp(rcveth_p->hdr.h_dest, new_gw_p->hd_addr, ETH_ALEN) == 0) {
if (gw_p == NULL)
if ((gw_p = malloc(sizeof(struct in6_addr))) == NULL)
fatal_error("malloc()");
*gw_p = *new_gw_p;
} else if (gw_p == NULL
|| memcmp(rcveth_p->hdr.h_dest, gw_p->hd_addr, ETH_ALEN))
return;
/* Build a frame to send */
memset(&sndeth, '\0', sizeof(struct eth_frame));
sndip6_p = (struct ip6_datagram *)&(sndeth.data);
sndrd_p = (struct neighbor_redirect *)&(sndip6_p->payload);
redirect_optsize = sizeof(struct nd_opt_rd_hdr)
+ rcveth_size - sizeof(struct ethhdr);
redirect_optsize = (redirect_optsize < RDOPT_MAXSIZE)
? redirect_optsize : RDOPT_MAXSIZE;
ip6_payload_size = sizeof(struct nd_redirect) + redirect_optsize;
sndeth_size = sizeof(struct ethhdr) + sizeof(struct ip6_hdr)
+ ip6_payload_size;
/* Ether */
memcpy(sndeth.hdr.h_dest, rcveth_p->hdr.h_source, ETH_ALEN);
memcpy(sndeth.hdr.h_source, gw_p->hd_addr, ETH_ALEN);
sndeth.hdr.h_proto = htons(ETH_P_IPV6);
/* IPv6 */
sndip6_p->hdr.ip6_vfc = 6 << 4;
sndip6_p->hdr.ip6_flow |= 0;
sndip6_p->hdr.ip6_plen = htons(ip6_payload_size);
sndip6_p->hdr.ip6_nxt = IPPROTO_ICMPV6;
sndip6_p->hdr.ip6_hlim = 255;
sndip6_p->hdr.ip6_src = gw_p->ip_addr;
sndip6_p->hdr.ip6_dst = rcvip6_p->hdr.ip6_src;
/* Rediret Message */
sndrd_p->defs.nd_rd_type = ND_REDIRECT;
sndrd_p->defs.nd_rd_code = 0;
sndrd_p->defs.nd_rd_cksum = 0; /* Calculate later */
sndrd_p->defs.nd_rd_reserved = 0;
sndrd_p->defs.nd_rd_target = gw_p->nexthop;
sndrd_p->defs.nd_rd_dst = rcvip6_p->hdr.ip6_dst;;
sndrd_p->rdopt_hdr.nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
sndrd_p->rdopt_hdr.nd_opt_rh_len = redirect_optsize / 8;
memcpy(sndrd_p->rdopt_data, rcvip6_p, redirect_optsize);
/* Pseudo IPv6 datagram for checksum calculation */
memset(&p_ip6, '\0', sizeof(struct pseudo_ip6_datagram));
p_ip6.hdr.p_ip6_src = sndip6_p->hdr.ip6_src;
p_ip6.hdr.p_ip6_dst = sndip6_p->hdr.ip6_dst;
p_ip6.hdr.p_ip6_plen = sndip6_p->hdr.ip6_plen;
p_ip6.hdr.p_ip6_zero1 = 0;
p_ip6.hdr.p_ip6_zero2 = 0;
p_ip6.hdr.p_ip6_nxt = sndip6_p->hdr.ip6_nxt;
memcpy(p_ip6.payload, sndrd_p, ip6_payload_size);
/* Calculate checksum */
sndrd_p->defs.nd_rd_cksum = calc_checksum((u_int16_t *)(&p_ip6),
sizeof(struct pseudo_ip6_hdr) + ip6_payload_size);
/* Send ICMPv6 redirct message */
retval = write(sd, &sndeth, sndeth_size);
if (retval != sndeth_size)
fatal_error("write()");
}
/*
* Function: analyze_ip6_datagram()
*
* Description:
* This function analyze captured IPv6 datagram
*
* Argument:
* sd : socket to send arp reply message
* rcveth_p : pointer to ether frame data
* rcveth_size : size of received ehter frame
* gateway_p : pointer to IPv6 gateway information
*
* Return value:
* None
*/
void
analyze_ip6_datagram(int sd, struct eth_frame *rcveth_p, size_t rcveth_size, struct ip6_gateway_info *gateway_p)
{
struct ip6_datagram *rcvip6_p; /* IPv6 Part of receiving frame */
struct icmp6_segment *rcvicmp6_p; /* ICMPv6 Part of receiving frame */
uint8_t nxt_hdr; /* Next header of IPv6 */
uint8_t icmp6_type; /* Type of ICMPv6 */
rcvip6_p = (struct ip6_datagram *)&(rcveth_p->data);
rcvicmp6_p = (struct icmp6_segment *)&(rcvip6_p->payload);
nxt_hdr = rcvip6_p->hdr.ip6_nxt;
switch (nxt_hdr) {
case IPPROTO_ICMPV6:
icmp6_type = rcvicmp6_p->hdr.icmp6_type;
switch (icmp6_type) {
case ND_NEIGHBOR_SOLICIT:
if (debug)
fprintf(stderr, "Received ICMP NS\n");
return_neigh_adv(sd, rcveth_p, gateway_p);
break;
case ICMP6_ECHO_REQUEST:
if (debug)
fprintf(stderr, "Received ICMP Echo Request\n");
return_icmp6_redirect(sd, rcveth_p, rcveth_size, gateway_p);
break;
}
break;
case IPPROTO_UDP:
if (debug)
fprintf(stderr, "Received UDP message\n");
return_icmp6_redirect(sd, rcveth_p, rcveth_size, gateway_p);
break;
}
}
/*
* Function: capture_frames()
*
* Description:
* This function captures frames
*
* Argument:
* redirector_p: pointer to data for the redirector information
*
* Return value:
* socket file descriptor for receiving packets
*/
void
capture_frames(struct redirector_info *redirector_p)
{
struct ip4_gateway_info ip4_gateway; /* IPv4 gateway information */
struct ip6_gateway_info ip6_gateway; /* IPv6 gateway information */
struct eth_frame frame; /* captured frame data */
ssize_t frame_size; /* captured frame size */
double start_time; /* capture starting time */
int sd = redirector_p->sd; /* socket fd for capture */
/* Initialize gateway information */
memset(&ip4_gateway, '\0', sizeof(struct ip4_gateway_info));
memset(&ip6_gateway, '\0', sizeof(struct ip6_gateway_info));
/* Set singal hander for SIGHUP */
handler.sa_handler = set_signal_flag;
handler.sa_flags = 0;
if (sigfillset(&handler.sa_mask) < 0)
fatal_error("sigfillset()");
if (sigaction(SIGHUP, &handler, NULL) < 0)
fatal_error("sigfillset()");
/*
* loop for capture
*/
start_time = time(NULL);
for (;;) {
frame_size = read(sd, (void *)(&frame), sizeof (frame));
if (frame_size < 0) {
if (errno != EAGAIN)
fatal_error("read()");
} else {
switch (ntohs(frame.hdr.h_proto)) {
case ETH_P_ARP:
if (debug)
fprintf(stderr, "Get ARP packet\n");
return_arp_reply(sd, &frame, &ip4_gateway);
break;
case ETH_P_IP:
if (debug)
fprintf(stderr, "Get IPv4 packet\n");
return_icmp4_redirect(sd, &frame, frame_size, &ip4_gateway);
break;
case ETH_P_IPV6:
if (debug)
fprintf(stderr, "Get IPv6 packet\n");
analyze_ip6_datagram(sd, &frame, frame_size, &ip6_gateway);
break;
}
}
if (redirector_p->timeout)
if (redirector_p->timeout < difftime(time(NULL), start_time))
break;
if (catch_sighup) /* catch SIGHUP */
break;
}
}
/*
*
* Function: main()
*
*/
int
main(int argc, char *argv[])
{
struct redirector_info redirector;
int background = 0;
debug = 0;
program_name = strdup(argv[0]);
srand(getpid());
memset(&redirector, '\0', sizeof(struct redirector_info));
parse_options(argc, argv, &redirector, &background);
redirector.sd = open_socket(redirector.ifname);
if (background) /* Work in the background */
if (daemon(0, 0) < 0)
fatal_error("daemon()");
capture_frames(&redirector);
close(redirector.sd);
exit(EXIT_SUCCESS);
}