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(¤t_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(¤t_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(¤t_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(¤t_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