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