/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * sppptun.c - Solaris STREAMS PPP multiplexing tunnel driver * installer. * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *myname; /* Copied from argv[0] */ static int verbose; /* -v on command line */ /* Data gathered during per-style attach routine. */ struct attach_data { ppptun_lname appstr; /* String to append to interface name (PPA) */ ppptun_atype localaddr; /* Local interface address */ uint_t locallen; /* Length of local address */ uint_t sap; /* SAP for PPPoE */ }; /* Per-protocol plumbing data */ struct protos { const char *name; const char *desc; int (*attach)(struct protos *prot, char *linkname, struct attach_data *adata); uint_t protval; int style; }; /* * Print a usage string and terminate. Used for command line argument * errors. Does not return. */ static void usage(void) { (void) fprintf(stderr, gettext( "Usage:\n\t%s plumb [-s ] [ ]\n" "\t%s unplumb \n" "\t%s query\n"), myname, myname, myname); exit(1); } /* * General DLPI function. This is called indirectly through * the protos structure for the selected lower stream protocol. */ /* ARGSUSED */ static int sppp_dlpi(struct protos *prot, char *linkname, struct attach_data *adata) { int retv; dlpi_handle_t dh; if (verbose) (void) printf(gettext("opening DLPI link %s\n"), linkname); if ((retv = dlpi_open(linkname, &dh, 0)) != DLPI_SUCCESS) { (void) fprintf(stderr, gettext("%s: failed opening %s: %s\n"), myname, linkname, dlpi_strerror(retv)); return (-1); } if (verbose) { (void) printf(gettext("binding to Ethertype %04X\n"), adata->sap); } if ((retv = dlpi_bind(dh, adata->sap, NULL)) != DLPI_SUCCESS) { (void) fprintf(stderr, gettext("%s: failed binding on %s: %s\n"), myname, linkname, dlpi_strerror(retv)); dlpi_close(dh); return (-1); } adata->locallen = DLPI_PHYSADDR_MAX; if ((retv = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, &adata->localaddr, &adata->locallen)) != DLPI_SUCCESS) { (void) fprintf(stderr, gettext("%s: failed getting physical" " address on %s: %s\n"), myname, linkname, dlpi_strerror(retv)); dlpi_close(dh); return (-1); } if (strlcpy(adata->appstr, linkname, sizeof (adata->appstr)) >= sizeof (adata->appstr)) { (void) fprintf(stderr, gettext("%s: interface name too long: %s\n"), myname, linkname); dlpi_close(dh); return (-1); } return (dlpi_fd(dh)); } static struct protos proto_list[] = { { "pppoe", "RFC 2516 PPP over Ethernet", sppp_dlpi, ETHERTYPE_PPPOES, PTS_PPPOE }, { "pppoed", "RFC 2516 PPP over Ethernet Discovery", sppp_dlpi, ETHERTYPE_PPPOED, PTS_PPPOE }, { NULL } }; /* * Issue a STREAMS I_STR ioctl and fetch the result. Returns -1 on * error, or length of returned data on success. */ static int strioctl(int fd, int cmd, void *ptr, int ilen, int olen, const char *iocname) { struct strioctl str; str.ic_cmd = cmd; str.ic_timout = 0; str.ic_len = ilen; str.ic_dp = ptr; if (ioctl(fd, I_STR, &str) == -1) { perror(iocname); return (-1); } if (olen >= 0) { if (str.ic_len > olen && verbose > 1) { (void) printf(gettext("%s:%s: extra data received; " "%d > %d\n"), myname, iocname, str.ic_len, olen); } else if (str.ic_len < olen) { (void) fprintf(stderr, gettext("%s:%s: expected %d " "bytes, got %d\n"), myname, iocname, olen, str.ic_len); return (-1); } } return (str.ic_len); } /* * Handle user request to plumb a new lower stream under the sppptun * driver. */ static int plumb_it(int argc, char **argv) { int opt, devfd, muxfd, muxid; struct ppptun_info pti; char *cp, *linkname; struct protos *prot; struct attach_data adata; uint_t sap = 0; /* If no protocol requested, then list known protocols. */ if (optind == argc) { (void) puts("Known tunneling protocols:"); for (prot = proto_list; prot->name != NULL; prot++) (void) printf("\t%s\t%s\n", prot->name, prot->desc); return (0); } /* Parse plumbing flags */ while ((opt = getopt(argc, argv, "s:")) != EOF) { switch (opt) { case 's': sap = strtoul(optarg, NULL, 16); break; default: usage(); } } /* If missing protocol or device, then abort. */ if (optind != argc-2) usage(); /* Look up requested protocol. */ cp = argv[optind++]; for (prot = proto_list; prot->name != NULL; prot++) if (strcasecmp(cp, prot->name) == 0) break; if (prot->name == NULL) { (void) fprintf(stderr, gettext("%s: unknown protocol %s\n"), myname, cp); return (1); } adata.sap = sap == 0 ? prot->protval : sap; /* Get interface. */ linkname = argv[optind]; /* Call per-protocol attach routine to open device */ if (verbose) (void) printf(gettext("opening %s\n"), linkname); if ((devfd = (*prot->attach)(prot, linkname, &adata)) < 0) return (1); /* Open sppptun driver */ if (verbose) (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME); if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) { perror("/dev/" PPP_TUN_NAME); return (1); } /* Push sppptun module on top of lower driver. */ if (verbose) (void) printf(gettext("pushing %s on %s\n"), PPP_TUN_NAME, linkname); if (ioctl(devfd, I_PUSH, PPP_TUN_NAME) == -1) { perror("I_PUSH " PPP_TUN_NAME); return (1); } /* Convert stream name to protocol-specific name. */ if (snprintf(pti.pti_name, sizeof (pti.pti_name), "%s:%s", adata.appstr, prot->name) >= sizeof (pti.pti_name)) { (void) fprintf(stderr, gettext("%s: stream name too long: %s:%s\n"), myname, adata.appstr, prot->name); return (1); } /* Change the lower stream name. */ if (verbose) (void) printf(gettext("resetting interface name to %s\n"), pti.pti_name); if (strioctl(devfd, PPPTUN_SNAME, pti.pti_name, sizeof (pti.pti_name), 0, "PPPTUN_SNAME") < 0) { if (errno == EEXIST) (void) fprintf(stderr, gettext("%s: %s already " "installed\n"), myname, pti.pti_name); return (1); } /* * Send down the local interface address to the lower stream * so that it can originate packets. */ if (verbose) (void) printf(gettext("send down local address\n")); if (strioctl(devfd, PPPTUN_LCLADDR, &adata.localaddr, adata.locallen, 0, "PPPTUN_LCLADDR") < 0) return (1); /* * And set the SAP value. */ if (verbose) (void) printf(gettext("send down SAP %x\n"), adata.sap); if (strioctl(devfd, PPPTUN_SSAP, &adata.sap, sizeof (adata.sap), 0, "PPPTUN_SSAP") < 0) return (1); /* Link the lower stream under the tunnel device. */ if (verbose) (void) printf(gettext("doing I_PLINK\n")); if ((muxid = ioctl(muxfd, I_PLINK, devfd)) == -1) { perror("I_PLINK"); return (1); } /* * Give the tunnel driver the multiplex ID of the new lower * stream. This allows the unplumb function to find and * disconnect the lower stream. */ if (verbose) (void) printf(gettext("sending muxid %d and style %d to " "driver\n"), muxid, prot->style); pti.pti_muxid = muxid; pti.pti_style = prot->style; if (strioctl(muxfd, PPPTUN_SINFO, &pti, sizeof (pti), 0, "PPPTUN_SINFO") < 0) return (1); if (verbose) (void) printf(gettext("done; installed %s\n"), pti.pti_name); else (void) puts(pti.pti_name); return (0); } /* * Handle user request to unplumb an existing lower stream from the * sppptun driver. */ static int unplumb_it(int argc, char **argv) { char *ifname; int muxfd; struct ppptun_info pti; /* * Need to have the name of the lower stream on the command * line. */ if (optind != argc-1) usage(); ifname = argv[optind]; /* Open the tunnel driver. */ if (verbose) (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME); if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) { perror("/dev/" PPP_TUN_NAME); return (1); } /* Get lower stream information; including multiplex ID. */ if (verbose) (void) printf(gettext("getting info from driver\n")); (void) strncpy(pti.pti_name, ifname, sizeof (pti.pti_name)); if (strioctl(muxfd, PPPTUN_GINFO, &pti, sizeof (pti), sizeof (pti), "PPPTUN_GINFO") < 0) return (1); if (verbose) (void) printf(gettext("got muxid %d from driver\n"), pti.pti_muxid); /* Unlink lower stream from driver. */ if (verbose) (void) printf(gettext("doing I_PUNLINK\n")); if (ioctl(muxfd, I_PUNLINK, pti.pti_muxid) < 0) { perror("I_PUNLINK"); return (1); } if (verbose) (void) printf(gettext("done!\n")); return (0); } /* * Handle user request to list lower streams plumbed under the sppptun * driver. */ /*ARGSUSED*/ static int query_interfaces(int argc, char **argv) { int muxfd, i; union ppptun_name ptn; /* No other arguments permitted. */ if (optind != argc) usage(); /* Open the tunnel driver. */ if (verbose) (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME); if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) { perror("/dev/" PPP_TUN_NAME); return (1); } /* Read and print names of lower streams. */ for (i = 0; ; i++) { ptn.ptn_index = i; if (strioctl(muxfd, PPPTUN_GNNAME, &ptn, sizeof (ptn), sizeof (ptn), "PPPTUN_GNNAME") < 0) { perror("PPPTUN_GNNAME"); break; } /* Stop when we index off the end of the list. */ if (ptn.ptn_name[0] == '\0') break; (void) puts(ptn.ptn_name); } return (0); } /* * Invoked by SIGALRM -- timer prevents problems in driver from * hanging the utility. */ /*ARGSUSED*/ static void toolong(int dummy) { (void) fprintf(stderr, gettext("%s: time-out in driver\n"), myname); exit(1); } int main(int argc, char **argv) { int opt, errflag = 0; char *arg; myname = *argv; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); /* Parse command line flags */ while ((opt = getopt(argc, argv, "v")) != EOF) switch (opt) { case 'v': verbose++; break; default: errflag++; break; } if (errflag != 0 || optind >= argc) usage(); /* Set alarm to avoid stalling on any driver errors. */ (void) signal(SIGALRM, toolong); (void) alarm(2); /* Switch out based on user-requested function. */ arg = argv[optind++]; if (strcmp(arg, "plumb") == 0) return (plumb_it(argc, argv)); if (strcmp(arg, "unplumb") == 0) return (unplumb_it(argc, argv)); if (strcmp(arg, "query") == 0) return (query_interfaces(argc, argv)); usage(); return (1); }