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