Home | History | Annotate | Download | only in sppptun
      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