xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/6to4relay.c (revision 657a8c206b913d1ee578fd725f0b25eca5b77253)
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