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