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 (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/socket.h>
     27 #include <sys/stream.h>
     28 #include <sys/param.h>
     29 
     30 #include <net/route.h>
     31 #include <net/if.h>
     32 #include <netinet/in.h>
     33 #include <arpa/inet.h>
     34 
     35 #include <locale.h>
     36 
     37 #include <errno.h>
     38 #include <unistd.h>
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <strings.h>
     42 #include <string.h>
     43 #include <stropts.h>
     44 #include <fcntl.h>
     45 #include <libdliptun.h>
     46 
     47 static void usage(void);
     48 
     49 static dladm_handle_t	handle;
     50 /* booleans corresponding to command line flags */
     51 static boolean_t	eflag = B_FALSE;
     52 static boolean_t	dflag = B_FALSE;
     53 static boolean_t	aflag = B_FALSE;
     54 
     55 
     56 /*
     57  * printkstatus()
     58  *
     59  * Queries the kernel for the current 6to4 Relay Router value, prints
     60  * a status message based on the value and exits this command.
     61  * INADDR_ANY is used to denote that Relay Router communication support is
     62  * disabled within the kernel.
     63  */
     64 static void
     65 printkstatus(void)
     66 {
     67 	struct in_addr	rr_addr;
     68 	char		buf[INET6_ADDRSTRLEN];
     69 	char		errstr[DLADM_STRSIZE];
     70 	dladm_status_t	status;
     71 
     72 	status = dladm_iptun_get6to4relay(handle, &rr_addr);
     73 	if (status != DLADM_STATUS_OK) {
     74 		(void) fprintf(stderr, gettext("6to4relay: unable to get "
     75 		    "6to4 relay status: %s\n"),
     76 		    dladm_status2str(status, errstr));
     77 		return;
     78 	}
     79 	(void) printf("6to4relay: ");
     80 	if (rr_addr.s_addr == INADDR_ANY) {
     81 		(void) printf(gettext("6to4 Relay Router communication "
     82 		    "support is disabled.\n"));
     83 	} else {
     84 		(void) printf(gettext("6to4 Relay Router communication "
     85 		    "support is enabled.\n"));
     86 		(void) printf(gettext("IPv4 destination address of Relay "
     87 		    "Router = "));
     88 		(void) printf("%s\n",
     89 		    inet_ntop(AF_INET, &rr_addr, buf, sizeof (buf)));
     90 	}
     91 }
     92 
     93 /*
     94  * modifyroute(cmd, in_gw)
     95  *
     96  * Modifies a default IPv6 route with DST = ::, GATEWAY = in_gw, NETMASK = ::
     97  * and flags = <GATEWAY, STATIC>.
     98  * This route is to be propagated through the 6to4 site so that 6to4 hosts
     99  * can send packets to native IPv6 hosts behind a remote 6to4 Relay Router.
    100  */
    101 static void
    102 modifyroute(unsigned int cmd, in6_addr_t *in_gw)
    103 {
    104 	static int rtmseq;
    105 	int rtsock;
    106 	int rlen;
    107 
    108 	static struct {
    109 		struct rt_msghdr	rt_hdr;
    110 		struct sockaddr_in6	rt_dst;
    111 		struct sockaddr_in6	rt_gate;
    112 		struct sockaddr_in6	rt_mask;
    113 	} rt_msg;
    114 
    115 	/* Open a routing socket for passing route commands */
    116 	if ((rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
    117 		(void) fprintf(stderr, gettext("6to4relay: unable to modify "
    118 		    "default IPv6 route: socket: %s\n"), strerror(errno));
    119 		return;
    120 	}
    121 
    122 	(void) memset(&rt_msg, 0, sizeof (rt_msg));
    123 	rt_msg.rt_hdr.rtm_msglen = sizeof (rt_msg);
    124 	rt_msg.rt_hdr.rtm_version = RTM_VERSION;
    125 	rt_msg.rt_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
    126 	rt_msg.rt_hdr.rtm_pid = getpid();
    127 	rt_msg.rt_hdr.rtm_type = cmd;
    128 	rt_msg.rt_hdr.rtm_seq = ++rtmseq;
    129 	rt_msg.rt_hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY;
    130 
    131 	/* DST */
    132 	rt_msg.rt_dst.sin6_family = AF_INET6;
    133 	(void) memset(&rt_msg.rt_dst.sin6_addr.s6_addr, 0,
    134 	    sizeof (in6_addr_t));
    135 
    136 	/* GATEWAY */
    137 	rt_msg.rt_gate.sin6_family = AF_INET6;
    138 	bcopy(in_gw->s6_addr, &rt_msg.rt_gate.sin6_addr.s6_addr,
    139 	    sizeof (in6_addr_t));
    140 
    141 	/* NETMASK */
    142 	rt_msg.rt_mask.sin6_family = AF_INET6;
    143 	(void) memset(&rt_msg.rt_mask.sin6_addr.s6_addr, 0,
    144 	    sizeof (in6_addr_t));
    145 
    146 	/* Send the routing message */
    147 	rlen = write(rtsock, &rt_msg, rt_msg.rt_hdr.rtm_msglen);
    148 	if (rlen < rt_msg.rt_hdr.rtm_msglen) {
    149 		if (rlen < 0) {
    150 			(void) fprintf(stderr,
    151 			    gettext("6to4relay: write to routing socket: %s\n"),
    152 			    strerror(errno));
    153 		} else {
    154 			(void) fprintf(stderr, gettext("6to4relay: write to "
    155 			    "routing socket got only %d for rlen\n"), rlen);
    156 		}
    157 	}
    158 	(void) close(rtsock);
    159 }
    160 
    161 static void
    162 usage(void)
    163 {
    164 	(void) fprintf(stderr,
    165 	    gettext("usage:\n"
    166 	    "\t6to4relay\n"
    167 	    "\t6to4relay -e [-a <addr>]\n"
    168 	    "\t6to4relay -d\n"
    169 	    "\t6to4relay -h\n"));
    170 }
    171 
    172 int
    173 main(int argc, char **argv)
    174 {
    175 	int		ch;
    176 	char		*relay_arg = NULL;
    177 	dladm_status_t	status;
    178 	char		errstr[DLADM_STRSIZE];
    179 
    180 	(void) setlocale(LC_ALL, "");
    181 
    182 #if !defined(TEXT_DOMAIN)
    183 #define	TEXT_DOMAIN "SYS_TEST"
    184 #endif
    185 	(void) textdomain(TEXT_DOMAIN);
    186 
    187 	if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) {
    188 		(void) fprintf(stderr, gettext("6to4relay: error opening "
    189 		    "dladm handle: %s\n"), dladm_status2str(status, errstr));
    190 		return (EXIT_FAILURE);
    191 	}
    192 
    193 	/* If no args are specified, print the current status. */
    194 	if (argc < 2) {
    195 		printkstatus();
    196 		return (EXIT_SUCCESS);
    197 	}
    198 
    199 	while ((ch = getopt(argc, argv, "ea:dh")) != EOF) {
    200 		switch (ch) {
    201 		case 'e':
    202 			eflag = B_TRUE;
    203 			break;
    204 		case 'd':
    205 			dflag = B_TRUE;
    206 			break;
    207 		case 'a':
    208 			aflag = B_TRUE;
    209 			relay_arg = optarg;
    210 			break;
    211 		case 'h':
    212 			usage();
    213 			return (EXIT_SUCCESS);
    214 		default:
    215 			usage();
    216 			return (EXIT_FAILURE);
    217 		}
    218 	}
    219 	/*
    220 	 * If -a is specified, -e must also be specified.  Also, the
    221 	 * combination of -e and -d is illegal.  Fail on either case.
    222 	 */
    223 	if ((aflag && !eflag) || (eflag && dflag)) {
    224 		usage();
    225 		return (EXIT_FAILURE);
    226 	}
    227 
    228 	/*
    229 	 * Enable Relay Router communication support in the kernel.
    230 	 */
    231 	if (eflag) {
    232 		struct in_addr current_addr;
    233 		struct in_addr new_addr;
    234 		in6_addr_t v6_rt;
    235 
    236 		/*
    237 		 * if -a was not specified, the well-known anycast will
    238 		 * be used.
    239 		 */
    240 		if (!aflag) {
    241 			new_addr.s_addr = htonl(INADDR_6TO4RRANYCAST);
    242 		} else if (inet_pton(AF_INET, relay_arg, &new_addr) <= 0) {
    243 			(void) fprintf(stderr, gettext("6to4relay: input "
    244 			    "address (%s) is not a valid IPv4 dotted-decimal "
    245 			    "string.\n"), relay_arg);
    246 			return (EXIT_FAILURE);
    247 		}
    248 
    249 		status = dladm_iptun_get6to4relay(handle, &current_addr);
    250 		if (status != DLADM_STATUS_OK) {
    251 			(void) fprintf(stderr, gettext("6to4relay: "
    252 			    "unable to obtain current 6to4 relay address: %s"),
    253 			    dladm_status2str(status, errstr));
    254 			return (EXIT_FAILURE);
    255 		}
    256 
    257 		if (current_addr.s_addr == new_addr.s_addr)
    258 			return (EXIT_SUCCESS);
    259 
    260 		status = dladm_iptun_set6to4relay(handle, &new_addr);
    261 		if (status != DLADM_STATUS_OK) {
    262 			(void) fprintf(stderr, gettext("6to4relay: "
    263 			    "unable to set the 6to4 relay router address: "
    264 			    "%s\n"), dladm_status2str(status, errstr));
    265 			return (EXIT_FAILURE);
    266 		}
    267 
    268 		if (current_addr.s_addr != INADDR_ANY) {
    269 			/* remove old default IPv6 route */
    270 			IN6_V4ADDR_TO_6TO4(&current_addr, &v6_rt);
    271 			modifyroute(RTM_DELETE, &v6_rt);
    272 		}
    273 
    274 		IN6_V4ADDR_TO_6TO4(&new_addr, &v6_rt);
    275 		modifyroute(RTM_ADD, &v6_rt);
    276 	}
    277 
    278 	/*
    279 	 * Disable Relay Router communication support in kernel.
    280 	 */
    281 	if (dflag) {
    282 		struct in_addr rr_addr;
    283 		in6_addr_t v6_rt;
    284 
    285 		/*
    286 		 * get Relay Router address from the kernel and delete
    287 		 * default IPv6 route that was added for it.
    288 		 */
    289 		status = dladm_iptun_get6to4relay(handle, &rr_addr);
    290 		if (status != DLADM_STATUS_OK) {
    291 			(void) fprintf(stderr, gettext("6to4relay: "
    292 			    "unable to obtain current 6to4 relay address: %s"),
    293 			    dladm_status2str(status, errstr));
    294 			return (EXIT_FAILURE);
    295 		}
    296 		if (rr_addr.s_addr == INADDR_ANY)
    297 			return (EXIT_SUCCESS);
    298 
    299 		IN6_V4ADDR_TO_6TO4(&rr_addr, &v6_rt);
    300 		modifyroute(RTM_DELETE, &v6_rt);
    301 
    302 		/*
    303 		 * INADDR_ANY (0.0.0.0) is used by the kernel to disable Relay
    304 		 * Router communication support.
    305 		 */
    306 		rr_addr.s_addr = INADDR_ANY;
    307 		status = dladm_iptun_set6to4relay(handle, &rr_addr);
    308 		if (status != DLADM_STATUS_OK) {
    309 			(void) fprintf(stderr, gettext("6to4relay: "
    310 			    "unable to disable tunneling to 6to4 relay router: "
    311 			    "%s\n"), dladm_status2str(status, errstr));
    312 			return (EXIT_FAILURE);
    313 		}
    314 	}
    315 
    316 	return (EXIT_SUCCESS);
    317 }
    318