Home | History | Annotate | Download | only in usr.sbin
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <sys/socket.h>
     30 #include <sys/stream.h>
     31 #include <sys/param.h>
     32 
     33 #include <net/route.h>
     34 #include <net/if.h>
     35 #include <netinet/in.h>
     36 #include <arpa/inet.h>
     37 #include <inet/tun.h>
     38 
     39 #include <locale.h>
     40 
     41 #include <errno.h>
     42 #include <unistd.h>
     43 #include <stdio.h>
     44 #include <stdlib.h>
     45 #include <strings.h>
     46 #include <string.h>
     47 #include <stropts.h>
     48 #include <fcntl.h>
     49 
     50 /*
     51  * Converts an IPv4 address to a 6to4 /64 route.  Address is of the form
     52  * 2002:<V4ADDR>:<SUBNETID>::/64 where SUBNETID will always be 0 and V4ADDR
     53  * equals the input IPv4 address.  IN6_V4ADDR_TO_6TO4(v4, v6) creates an
     54  * address of form 2002:<V4ADDR>:<SUBNETID>::<HOSTID>, where SUBNETID equals 0
     55  * and HOSTID equals 1.  For this route, we are not concerned about the
     56  * HOSTID portion of the address, thus it can be set to 0.
     57  *
     58  *  void V4ADDR_TO_6TO4_RT(const struct in_addr *v4, in6_addr_t *v6)
     59  */
     60 #define	V4ADDR_TO_6TO4_RT(v4, v6) \
     61 	(IN6_V4ADDR_TO_6TO4(v4, v6), (v6)->_S6_un._S6_u32[3] = 0)
     62 
     63 static void strioctl(int, void *, size_t);
     64 static void getkstatus(ipaddr_t *);
     65 static void printkstatus(void);
     66 static void modifyroute(unsigned int, in6_addr_t *);
     67 static void setkrraddr(ipaddr_t);
     68 static void printerror(char *);
     69 static void usage(void);
     70 
     71 /* booleans corresponding to command line flags */
     72 static boolean_t eflag = B_FALSE;
     73 static boolean_t dflag = B_FALSE;
     74 static boolean_t aflag = B_FALSE;
     75 
     76 static int fd = -1;
     77 
     78 /*
     79  * srtioctl(cmd, buf, size)
     80  *
     81  * Passes the contents of 'buf' using the ioctl specified by 'cmd', by way of
     82  * the I_STR ioctl mechanism.  The response of the ioctl will be stored in buf
     83  * when this function returns.  The input 'size' specifies the size of the
     84  * buffer to be passed.
     85  */
     86 static void
     87 strioctl(int cmd, void *buf, size_t size)
     88 {
     89 	struct strioctl ioc;
     90 
     91 	(void) memset(&ioc, 0, sizeof (ioc));
     92 
     93 	ioc.ic_cmd = cmd;
     94 	ioc.ic_timout = 0;
     95 	ioc.ic_len = size;
     96 	ioc.ic_dp = (char *)buf;
     97 
     98 	if (ioctl(fd, I_STR, &ioc) < 0) {
     99 		printerror("ioctl (I_STR)");
    100 		(void) close(fd);
    101 		exit(EXIT_FAILURE);
    102 		/* NOTREACHED */
    103 	}
    104 }
    105 
    106 
    107 /*
    108  * getkstatus(out_addr)
    109  *
    110  * Queries the kernel for the 6to4 Relay Router destination address by sending
    111  * the SIOCG6TO4TUNRRADDR ioctl to the tunnel module using the I_STR ioctl
    112  * mechanism.  The value returned, through the ioctl, will be an ipaddr_t
    113  * embedded in a strioctl.  Output parameter is set with result.
    114  */
    115 static void
    116 getkstatus(ipaddr_t *out_addr)
    117 {
    118 	ipaddr_t an_addr;
    119 
    120 	/* Get the Relay Router address from the kernel */
    121 	strioctl(SIOCG6TO4TUNRRADDR, &an_addr, sizeof (an_addr));
    122 
    123 	*out_addr = an_addr;	/* set output parameter */
    124 }
    125 
    126 
    127 /*
    128  * printkstatus()
    129  *
    130  * Queries the kernel for the current 6to4 Relay Router value, prints
    131  * a status message based on the value and exits this command.
    132  * INADDR_ANY is used to denote that Relay Router communication support is
    133  * disabled within the kernel.
    134  */
    135 static void
    136 printkstatus(void)
    137 {
    138 	ipaddr_t rr_addr;
    139 	char buf[INET6_ADDRSTRLEN];
    140 
    141 	getkstatus(&rr_addr);	/* get value from kernel */
    142 	(void) printf("6to4relay: ");
    143 	if (rr_addr == INADDR_ANY) {
    144 		(void) printf(gettext("6to4 Relay Router communication "
    145 		    "support is disabled.\n"));
    146 	} else {
    147 		(void) printf(gettext("6to4 Relay Router communication "
    148 		    "support is enabled.\n"));
    149 		(void) printf(gettext("IPv4 destination address of Relay "
    150 		    "Router = "));
    151 		(void) printf("%s\n",
    152 		    inet_ntop(AF_INET, &rr_addr, buf, sizeof (buf)));
    153 	}
    154 }
    155 
    156 /*
    157  * modifyroute(cmd, in_gw)
    158  *
    159  * Modifies a default IPv6 route with DST = ::, GATEWAY = in_gw, NETMASK = ::
    160  * and flags = <GATEWAY, STATIC>.
    161  * This route is to be propagated through the 6to4 site so that 6to4 hosts
    162  * can send packets to native IPv6 hosts behind a remote 6to4 Relay Router.
    163  */
    164 static void
    165 modifyroute(unsigned int cmd, in6_addr_t *in_gw)
    166 {
    167 	static int rtmseq;
    168 	int rtsock;
    169 	int rlen;
    170 
    171 	static struct {
    172 		struct rt_msghdr	rt_hdr;
    173 		struct sockaddr_in6	rt_dst;
    174 		struct sockaddr_in6	rt_gate;
    175 		struct sockaddr_in6	rt_mask;
    176 	} rt_msg;
    177 
    178 	/* Open a routing socket for passing route commands */
    179 	if ((rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
    180 		printerror("socket");
    181 		(void) close(fd);
    182 		exit(EXIT_FAILURE);
    183 		/* NOTREACHED */
    184 	}
    185 
    186 	(void) memset(&rt_msg, 0, sizeof (rt_msg));
    187 	rt_msg.rt_hdr.rtm_msglen = sizeof (rt_msg);
    188 	rt_msg.rt_hdr.rtm_version = RTM_VERSION;
    189 	rt_msg.rt_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
    190 	rt_msg.rt_hdr.rtm_pid = getpid();
    191 	rt_msg.rt_hdr.rtm_type = cmd;
    192 	rt_msg.rt_hdr.rtm_seq = ++rtmseq;
    193 	rt_msg.rt_hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY;
    194 
    195 	/* DST */
    196 	rt_msg.rt_dst.sin6_family = AF_INET6;
    197 	(void) memset(&rt_msg.rt_dst.sin6_addr.s6_addr, 0,
    198 	    sizeof (in6_addr_t));
    199 
    200 	/* GATEWAY */
    201 	rt_msg.rt_gate.sin6_family = AF_INET6;
    202 	bcopy(in_gw->s6_addr, &rt_msg.rt_gate.sin6_addr.s6_addr,
    203 	    sizeof (in6_addr_t));
    204 
    205 	/* NETMASK */
    206 	rt_msg.rt_mask.sin6_family = AF_INET6;
    207 	(void) memset(&rt_msg.rt_mask.sin6_addr.s6_addr, 0,
    208 	    sizeof (in6_addr_t));
    209 
    210 	/* Send the routing message */
    211 	rlen = write(rtsock, &rt_msg, rt_msg.rt_hdr.rtm_msglen);
    212 	if (rlen < rt_msg.rt_hdr.rtm_msglen) {
    213 		if (rlen < 0) {
    214 			(void) fprintf(stderr,
    215 			    gettext("6to4relay: write to routing socket: %s\n"),
    216 			    strerror(errno));
    217 		} else {
    218 			(void) fprintf(stderr, gettext("6to4relay: write to "
    219 			    "routing socket got only %d for rlen\n"), rlen);
    220 		}
    221 	}
    222 	(void) close(rtsock);
    223 }
    224 
    225 /*
    226  * setkrraddr(in_addr)
    227  *
    228  * Sets the 6to4 Relay Router destination address value in the kernel using
    229  * the SIOCS6TO4TUNRRADDR ioctl using the I_STR ioctl mechanism.
    230  * The address is sent to the kernel, as an ipaddr_t, embedded in an strioctl.
    231  */
    232 static void
    233 setkrraddr(ipaddr_t in_addr)
    234 {
    235 	/* set Relay Router address */
    236 	strioctl(SIOCS6TO4TUNRRADDR, &in_addr, sizeof (in_addr));
    237 }
    238 
    239 static void
    240 printerror(char *s)
    241 {
    242 	int sverrno = errno;
    243 
    244 	(void) fprintf(stderr, "6to4relay: ");
    245 	if (s != NULL)
    246 		(void) fprintf(stderr, "%s: ", s);
    247 	(void) fprintf(stderr, "%s\n", strerror(sverrno));
    248 }
    249 
    250 static void
    251 usage(void)
    252 {
    253 	(void) fprintf(stderr,
    254 	    gettext("usage:\n"
    255 		"\t6to4relay\n"
    256 		"\t6to4relay -e [-a <addr>]\n"
    257 		"\t6to4relay -d\n"
    258 		"\t6to4relay -h\n"));
    259 }
    260 
    261 int
    262 main(int argc, char **argv)
    263 {
    264 	int ch;
    265 	char *in_addr = NULL;
    266 	int ret = EXIT_SUCCESS;
    267 
    268 	(void) setlocale(LC_ALL, "");
    269 
    270 #if !defined(TEXT_DOMAIN)
    271 #define	TEXT_DOMAIN "SYS_TEST"
    272 #endif
    273 	(void) textdomain(TEXT_DOMAIN);
    274 
    275 	/* open /dev/ip for use */
    276 	if ((fd = open("/dev/ip", O_RDWR)) == -1) {
    277 		printerror(gettext("can't open /dev/ip"));
    278 		exit(EXIT_FAILURE);
    279 	}
    280 
    281 	if (ioctl(fd, I_PUSH, TUN_NAME) < 0) {
    282 		printerror("ioctl (I_PUSH)");
    283 		ret = EXIT_FAILURE;
    284 		goto done;
    285 	}
    286 
    287 	/* If no args are specified, print status as queried from kernel */
    288 	if (argc < 2) {
    289 		printkstatus();
    290 		goto done;
    291 	}
    292 	while ((ch = getopt(argc, argv, "ea:dh")) != EOF) {
    293 		switch (ch) {
    294 		case 'e':
    295 			eflag = B_TRUE;
    296 			break;
    297 		case 'd':
    298 			dflag = B_TRUE;
    299 			break;
    300 		case 'a':
    301 			aflag = B_TRUE;
    302 			in_addr = optarg;
    303 			break;
    304 		case 'h':
    305 			usage();
    306 			goto done;
    307 		default:
    308 			usage();
    309 			ret = EXIT_FAILURE;
    310 			goto done;
    311 		}
    312 	}
    313 	/*
    314 	 * If -a is specified, -e must also be specified.  Also, the
    315 	 * combination of -e and -d is illegal.  Fail on either case.
    316 	 */
    317 	if ((aflag && !eflag) || (eflag && dflag)) {
    318 		usage();
    319 		ret = EXIT_FAILURE;
    320 		goto done;
    321 	}
    322 
    323 	/*
    324 	 * Enable Relay Router communication support in the kernel.
    325 	 */
    326 	if (eflag) {
    327 		struct in_addr current_addr; /* addr currently set in kernel */
    328 		struct in_addr new_addr; /* new addr we plan to set */
    329 		in6_addr_t v6_rt;
    330 
    331 		/*
    332 		 * if -a was not specified, the well-known anycast will
    333 		 * be used.
    334 		 */
    335 		if (!aflag) {
    336 			new_addr.s_addr = htonl(INADDR_6TO4RRANYCAST);
    337 
    338 		} else if (inet_pton(AF_INET, in_addr, &new_addr) <= 0) {
    339 			(void) fprintf(stderr, gettext("6to4relay: input "
    340 			    "address (%s) is not a valid IPv4 dotted-decimal "
    341 			    "string.\n"), in_addr);
    342 			ret = EXIT_FAILURE;
    343 			goto done;
    344 		}
    345 
    346 		/*
    347 		 * INADDR_ANY has special meaning in the kernel, reject this
    348 		 * input and exit.
    349 		 */
    350 		if (new_addr.s_addr == INADDR_ANY) {
    351 			(void) fprintf(stderr, gettext("6to4relay: input "
    352 			    "(0.0.0.0) is not a valid IPv4 unicast "
    353 			    "address.\n"));
    354 			ret = EXIT_FAILURE;
    355 			goto done;
    356 		}
    357 
    358 		/*
    359 		 * get the current Relay Router address from the kernel.
    360 		 *
    361 		 * 1. If the current address is INADDR_ANY, set the new
    362 		 *    address in the kernel and add a default IPv6 route using
    363 		 *    the new address.
    364 		 *
    365 		 * 2. If the current address is different than the new address,
    366 		 *    set the new address in the kernel, delete the
    367 		 *    old default IPv6 route and add a new default IPv6 route
    368 		 *    (using the new address).
    369 		 *
    370 		 * 3. If the kernel address is the same as the one we are
    371 		 *    adding, no additional processing is needed.
    372 		 */
    373 		getkstatus(&current_addr.s_addr);
    374 
    375 		if (current_addr.s_addr == INADDR_ANY) {
    376 			setkrraddr(new_addr.s_addr);
    377 			V4ADDR_TO_6TO4_RT(&new_addr, &v6_rt);
    378 			modifyroute(RTM_ADD, &v6_rt);
    379 		} else if (new_addr.s_addr != current_addr.s_addr) {
    380 			setkrraddr(new_addr.s_addr);
    381 			/* remove old default IPv6 route */
    382 			V4ADDR_TO_6TO4_RT(&current_addr, &v6_rt);
    383 			modifyroute(RTM_DELETE, &v6_rt);
    384 			/*
    385 			 * Add new default IPv6 route using a 6to4 address
    386 			 * created from the address we just set in the kernel.
    387 			 */
    388 			V4ADDR_TO_6TO4_RT(&new_addr, &v6_rt);
    389 			modifyroute(RTM_ADD, &v6_rt);
    390 		}
    391 	}
    392 
    393 	/*
    394 	 * Disable Relay Router communication support in kernel.
    395 	 */
    396 	if (dflag) {
    397 		struct in_addr current_addr; /* addr currently set in kernel */
    398 		in6_addr_t v6_rt;
    399 
    400 		/*
    401 		 * get Relay Router address from the kernel and delete
    402 		 * default IPv6 route that was added for it.
    403 		 */
    404 		getkstatus(&current_addr.s_addr);
    405 		if (current_addr.s_addr == INADDR_ANY) {
    406 			/*
    407 			 * Feature is already disabled in kernel, no
    408 			 * additional processing is needed.
    409 			 */
    410 			goto done;
    411 		}
    412 
    413 		V4ADDR_TO_6TO4_RT(&current_addr, &v6_rt);
    414 		modifyroute(RTM_DELETE, &v6_rt);
    415 
    416 		/*
    417 		 * INADDR_ANY (0.0.0.0) is used by the kernel to disable Relay
    418 		 * Router communication support.
    419 		 */
    420 		setkrraddr(INADDR_ANY);
    421 	}
    422 done:
    423 	(void) close(fd);
    424 	return (ret);
    425 }
    426