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
printkstatus(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
modifyroute(unsigned int cmd,in6_addr_t * in_gw)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
usage(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
main(int argc,char ** argv)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