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