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 * sppptun.c - Solaris STREAMS PPP multiplexing tunnel driver 23 * installer. 24 * 25 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <string.h> 35 #include <ctype.h> 36 #include <errno.h> 37 #include <signal.h> 38 #include <stropts.h> 39 #include <fcntl.h> 40 #include <locale.h> 41 #include <sys/fcntl.h> 42 #include <sys/stropts.h> 43 #include <sys/socket.h> 44 #include <net/if.h> 45 #include <netinet/in.h> 46 #include <netinet/if_ether.h> 47 #include <net/sppptun.h> 48 #include <libdlpi.h> 49 50 static char *myname; /* Copied from argv[0] */ 51 static int verbose; /* -v on command line */ 52 53 /* Data gathered during per-style attach routine. */ 54 struct attach_data { 55 ppptun_lname appstr; /* String to append to interface name (PPA) */ 56 ppptun_atype localaddr; /* Local interface address */ 57 uint_t locallen; /* Length of local address */ 58 }; 59 60 /* Per-protocol plumbing data */ 61 struct protos { 62 const char *name; 63 const char *desc; 64 int (*attach)(struct protos *prot, char *linkname, 65 struct attach_data *adata); 66 uint_t protval; 67 int style; 68 }; 69 70 /* 71 * Print a usage string and terminate. Used for command line argument 72 * errors. Does not return. 73 */ 74 static void 75 usage(void) 76 { 77 (void) fprintf(stderr, gettext( 78 "Usage:\n\t%s plumb [<protocol> <device>]\n" 79 "\t%s unplumb <interface-name>\n" 80 "\t%s query\n"), myname, myname, myname); 81 exit(1); 82 } 83 84 /* 85 * General DLPI function. This is called indirectly through 86 * the protos structure for the selected lower stream protocol. 87 */ 88 static int 89 sppp_dlpi(struct protos *prot, char *linkname, struct attach_data *adata) 90 { 91 int retv; 92 uint_t ppa; 93 dlpi_handle_t dh; 94 95 if (verbose) 96 (void) printf(gettext("opening DLPI link %s\n"), linkname); 97 if ((retv = dlpi_open(linkname, &dh, 0)) != DLPI_SUCCESS) { 98 (void) fprintf(stderr, gettext("%s: failed opening %s: %s\n"), 99 myname, linkname, dlpi_strerror(retv)); 100 return (-1); 101 } 102 103 if (verbose) { 104 (void) printf(gettext("binding to Ethertype %04X\n"), 105 prot->protval); 106 } 107 if ((retv = dlpi_bind(dh, prot->protval, NULL)) != DLPI_SUCCESS) { 108 (void) fprintf(stderr, gettext("%s: failed binding on %s: %s"), 109 myname, linkname, dlpi_strerror(retv)); 110 dlpi_close(dh); 111 return (-1); 112 } 113 114 adata->locallen = DLPI_PHYSADDR_MAX; 115 if ((retv = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, &adata->localaddr, 116 &adata->locallen)) != DLPI_SUCCESS) { 117 (void) fprintf(stderr, gettext("%s: failed getting physical" 118 " address on %s: %s"), myname, linkname, 119 dlpi_strerror(retv)); 120 dlpi_close(dh); 121 return (-1); 122 } 123 124 /* Store ppa to append to interface name. */ 125 if ((retv = dlpi_parselink(linkname, NULL, &ppa)) != DLPI_SUCCESS) { 126 (void) fprintf(stderr, gettext("%s: failed parsing linkname on" 127 " %s: %s"), myname, linkname, dlpi_strerror(retv)); 128 dlpi_close(dh); 129 return (-1); 130 } 131 132 (void) snprintf(adata->appstr, sizeof (adata->appstr), "%d", ppa); 133 134 return (dlpi_fd(dh)); 135 } 136 137 138 static struct protos proto_list[] = { 139 { "pppoe", "RFC 2516 PPP over Ethernet", sppp_dlpi, ETHERTYPE_PPPOES, 140 PTS_PPPOE }, 141 { "pppoed", "RFC 2516 PPP over Ethernet Discovery", sppp_dlpi, 142 ETHERTYPE_PPPOED, PTS_PPPOE }, 143 { NULL } 144 }; 145 146 /* 147 * Issue a STREAMS I_STR ioctl and fetch the result. Returns -1 on 148 * error, or length of returned data on success. 149 */ 150 static int 151 strioctl(int fd, int cmd, void *ptr, int ilen, int olen, const char *iocname) 152 { 153 struct strioctl str; 154 155 str.ic_cmd = cmd; 156 str.ic_timout = 0; 157 str.ic_len = ilen; 158 str.ic_dp = ptr; 159 160 if (ioctl(fd, I_STR, &str) == -1) { 161 perror(iocname); 162 return (-1); 163 } 164 165 if (olen >= 0) { 166 if (str.ic_len > olen && verbose > 1) { 167 (void) printf(gettext("%s:%s: extra data received; " 168 "%d > %d\n"), myname, iocname, str.ic_len, olen); 169 } else if (str.ic_len < olen) { 170 (void) fprintf(stderr, gettext("%s:%s: expected %d " 171 "bytes, got %d\n"), myname, iocname, olen, 172 str.ic_len); 173 return (-1); 174 } 175 } 176 177 return (str.ic_len); 178 } 179 180 /* 181 * Handle user request to plumb a new lower stream under the sppptun 182 * driver. 183 */ 184 static int 185 plumb_it(int argc, char **argv) 186 { 187 int devfd, muxfd, muxid; 188 struct ppptun_info pti; 189 char *cp, *linkname; 190 struct protos *prot; 191 struct attach_data adata; 192 193 /* If no protocol requested, then list known protocols. */ 194 if (optind == argc) { 195 (void) puts("Known tunneling protocols:"); 196 for (prot = proto_list; prot->name != NULL; prot++) 197 (void) printf("\t%s\t%s\n", prot->name, prot->desc); 198 return (0); 199 } 200 201 /* If missing protocol or device, then abort. */ 202 if (optind != argc-2) 203 usage(); 204 205 /* Look up requested protocol. */ 206 cp = argv[optind++]; 207 for (prot = proto_list; prot->name != NULL; prot++) 208 if (strcasecmp(cp, prot->name) == 0) 209 break; 210 if (prot->name == NULL) { 211 (void) fprintf(stderr, gettext("%s: unknown protocol %s\n"), 212 myname, cp); 213 return (1); 214 } 215 216 /* Get interface. */ 217 linkname = argv[optind]; 218 /* Call per-protocol attach routine to open device */ 219 if (verbose) 220 (void) printf(gettext("opening %s\n"), linkname); 221 if ((devfd = (*prot->attach)(prot, linkname, &adata)) < 0) 222 return (1); 223 224 /* Open sppptun driver */ 225 if (verbose) 226 (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME); 227 if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) { 228 perror("/dev/" PPP_TUN_NAME); 229 return (1); 230 } 231 232 /* Push sppptun module on top of lower driver. */ 233 if (verbose) 234 (void) printf(gettext("pushing %s on %s\n"), PPP_TUN_NAME, 235 linkname); 236 if (ioctl(devfd, I_PUSH, PPP_TUN_NAME) == -1) { 237 perror("I_PUSH " PPP_TUN_NAME); 238 return (1); 239 } 240 241 /* Get the name of the newly-created lower stream. */ 242 if (verbose) 243 (void) printf(gettext("getting new interface name\n")); 244 if (strioctl(devfd, PPPTUN_GNAME, pti.pti_name, 0, 245 sizeof (pti.pti_name), "PPPTUN_GNAME") < 0) 246 return (1); 247 if (verbose) 248 (void) printf(gettext("got interface %s\n"), pti.pti_name); 249 250 /* Convert stream name to protocol-specific name. */ 251 if ((cp = strchr(pti.pti_name, ':')) != NULL) 252 *cp = '\0'; 253 (void) snprintf(pti.pti_name+strlen(pti.pti_name), 254 sizeof (pti.pti_name)-strlen(pti.pti_name), "%s:%s", adata.appstr, 255 prot->name); 256 257 /* Change the lower stream name. */ 258 if (verbose) 259 (void) printf(gettext("resetting interface name to %s\n"), 260 pti.pti_name); 261 if (strioctl(devfd, PPPTUN_SNAME, pti.pti_name, 262 sizeof (pti.pti_name), 0, "PPPTUN_SNAME") < 0) { 263 if (errno == EEXIST) 264 (void) fprintf(stderr, gettext("%s: %s already " 265 "installed\n"), myname, pti.pti_name); 266 return (1); 267 } 268 269 /* 270 * Send down the local interface address to the lower stream 271 * so that it can originate packets. 272 */ 273 if (verbose) 274 (void) printf(gettext("send down local address\n")); 275 if (strioctl(devfd, PPPTUN_LCLADDR, &adata.localaddr, adata.locallen, 276 0, "PPPTUN_LCLADDR") < 0) 277 return (1); 278 279 /* Link the lower stream under the tunnel device. */ 280 if (verbose) 281 (void) printf(gettext("doing I_PLINK\n")); 282 if ((muxid = ioctl(muxfd, I_PLINK, devfd)) == -1) { 283 perror("I_PLINK"); 284 return (1); 285 } 286 287 /* 288 * Give the tunnel driver the multiplex ID of the new lower 289 * stream. This allows the unplumb function to find and 290 * disconnect the lower stream. 291 */ 292 if (verbose) 293 (void) printf(gettext("sending muxid %d and style %d to " 294 "driver\n"), muxid, prot->style); 295 pti.pti_muxid = muxid; 296 pti.pti_style = prot->style; 297 if (strioctl(muxfd, PPPTUN_SINFO, &pti, sizeof (pti), 0, 298 "PPPTUN_SINFO") < 0) 299 return (1); 300 301 if (verbose) 302 (void) printf(gettext("done; installed %s\n"), pti.pti_name); 303 else 304 (void) puts(pti.pti_name); 305 306 return (0); 307 } 308 309 /* 310 * Handle user request to unplumb an existing lower stream from the 311 * sppptun driver. 312 */ 313 static int 314 unplumb_it(int argc, char **argv) 315 { 316 char *ifname; 317 int muxfd; 318 struct ppptun_info pti; 319 320 /* 321 * Need to have the name of the lower stream on the command 322 * line. 323 */ 324 if (optind != argc-1) 325 usage(); 326 327 ifname = argv[optind]; 328 329 /* Open the tunnel driver. */ 330 if (verbose) 331 (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME); 332 if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) { 333 perror("/dev/" PPP_TUN_NAME); 334 return (1); 335 } 336 337 /* Get lower stream information; including multiplex ID. */ 338 if (verbose) 339 (void) printf(gettext("getting info from driver\n")); 340 (void) strncpy(pti.pti_name, ifname, sizeof (pti.pti_name)); 341 if (strioctl(muxfd, PPPTUN_GINFO, &pti, sizeof (pti), 342 sizeof (pti), "PPPTUN_GINFO") < 0) 343 return (1); 344 if (verbose) 345 (void) printf(gettext("got muxid %d from driver\n"), 346 pti.pti_muxid); 347 348 /* Unlink lower stream from driver. */ 349 if (verbose) 350 (void) printf(gettext("doing I_PUNLINK\n")); 351 if (ioctl(muxfd, I_PUNLINK, pti.pti_muxid) < 0) { 352 perror("I_PUNLINK"); 353 return (1); 354 } 355 if (verbose) 356 (void) printf(gettext("done!\n")); 357 358 return (0); 359 } 360 361 /* 362 * Handle user request to list lower streams plumbed under the sppptun 363 * driver. 364 */ 365 /*ARGSUSED*/ 366 static int 367 query_interfaces(int argc, char **argv) 368 { 369 int muxfd, i; 370 union ppptun_name ptn; 371 372 /* No other arguments permitted. */ 373 if (optind != argc) 374 usage(); 375 376 /* Open the tunnel driver. */ 377 if (verbose) 378 (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME); 379 if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) { 380 perror("/dev/" PPP_TUN_NAME); 381 return (1); 382 } 383 384 /* Read and print names of lower streams. */ 385 for (i = 0; ; i++) { 386 ptn.ptn_index = i; 387 if (strioctl(muxfd, PPPTUN_GNNAME, &ptn, sizeof (ptn), 388 sizeof (ptn), "PPPTUN_GNNAME") < 0) { 389 perror("PPPTUN_GNNAME"); 390 break; 391 } 392 /* Stop when we index off the end of the list. */ 393 if (ptn.ptn_name[0] == '\0') 394 break; 395 (void) puts(ptn.ptn_name); 396 } 397 return (0); 398 } 399 400 /* 401 * Invoked by SIGALRM -- timer prevents problems in driver from 402 * hanging the utility. 403 */ 404 /*ARGSUSED*/ 405 static void 406 toolong(int dummy) 407 { 408 (void) fprintf(stderr, gettext("%s: time-out in driver\n"), myname); 409 exit(1); 410 } 411 412 int 413 main(int argc, char **argv) 414 { 415 int opt, errflag = 0; 416 char *arg; 417 418 myname = *argv; 419 420 421 (void) setlocale(LC_ALL, ""); 422 423 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 424 #define TEXT_DOMAIN "SYS_TEST" 425 #endif 426 (void) textdomain(TEXT_DOMAIN); 427 428 /* Parse command line flags */ 429 while ((opt = getopt(argc, argv, "v")) != EOF) 430 switch (opt) { 431 case 'v': 432 verbose++; 433 break; 434 default: 435 errflag++; 436 break; 437 } 438 if (errflag != 0 || optind >= argc) 439 usage(); 440 441 /* Set alarm to avoid stalling on any driver errors. */ 442 (void) signal(SIGALRM, toolong); 443 (void) alarm(2); 444 445 /* Switch out based on user-requested function. */ 446 arg = argv[optind++]; 447 if (strcmp(arg, "plumb") == 0) 448 return (plumb_it(argc, argv)); 449 if (strcmp(arg, "unplumb") == 0) 450 return (unplumb_it(argc, argv)); 451 if (strcmp(arg, "query") == 0) 452 return (query_interfaces(argc, argv)); 453 454 usage(); 455 return (1); 456 } 457