dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.sbin / sppptun / sppptun.c
blob70292b1cbc434e0f78fb1c929a5774d9698c2fcf
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * sppptun.c - Solaris STREAMS PPP multiplexing tunnel driver
23 * installer.
25 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
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>
48 static char *myname; /* Copied from argv[0] */
49 static int verbose; /* -v on command line */
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 */
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;
70 * Print a usage string and terminate. Used for command line argument
71 * errors. Does not return.
73 static void
74 usage(void)
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);
84 * General DLPI function. This is called indirectly through
85 * the protos structure for the selected lower stream protocol.
87 /* ARGSUSED */
88 static int
89 sppp_dlpi(struct protos *prot, char *linkname, struct attach_data *adata)
91 int retv;
92 dlpi_handle_t dh;
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);
102 if (verbose) {
103 (void) printf(gettext("binding to Ethertype %04X\n"),
104 adata->sap);
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);
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);
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);
133 return (dlpi_fd(dh));
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 }
146 * Issue a STREAMS I_STR ioctl and fetch the result. Returns -1 on
147 * error, or length of returned data on success.
149 static int
150 strioctl(int fd, int cmd, void *ptr, int ilen, int olen, const char *iocname)
152 struct strioctl str;
154 str.ic_cmd = cmd;
155 str.ic_timout = 0;
156 str.ic_len = ilen;
157 str.ic_dp = ptr;
159 if (ioctl(fd, I_STR, &str) == -1) {
160 perror(iocname);
161 return (-1);
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);
176 return (str.ic_len);
180 * Handle user request to plumb a new lower stream under the sppptun
181 * driver.
183 static int
184 plumb_it(int argc, char **argv)
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;
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);
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();
212 /* If missing protocol or device, then abort. */
213 if (optind != argc-2)
214 usage();
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);
227 adata.sap = sap == 0 ? prot->protval : sap;
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);
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);
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);
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);
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);
276 * Send down the local interface address to the lower stream
277 * so that it can originate packets.
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);
286 * And set the SAP value.
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);
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);
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.
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);
316 if (verbose)
317 (void) printf(gettext("done; installed %s\n"), pti.pti_name);
318 else
319 (void) puts(pti.pti_name);
321 return (0);
325 * Handle user request to unplumb an existing lower stream from the
326 * sppptun driver.
328 static int
329 unplumb_it(int argc, char **argv)
331 char *ifname;
332 int muxfd;
333 struct ppptun_info pti;
336 * Need to have the name of the lower stream on the command
337 * line.
339 if (optind != argc-1)
340 usage();
342 ifname = argv[optind];
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);
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);
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);
370 if (verbose)
371 (void) printf(gettext("done!\n"));
373 return (0);
377 * Handle user request to list lower streams plumbed under the sppptun
378 * driver.
380 /*ARGSUSED*/
381 static int
382 query_interfaces(int argc, char **argv)
384 int muxfd, i;
385 union ppptun_name ptn;
387 /* No other arguments permitted. */
388 if (optind != argc)
389 usage();
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);
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;
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);
412 return (0);
416 * Invoked by SIGALRM -- timer prevents problems in driver from
417 * hanging the utility.
419 /*ARGSUSED*/
420 static void
421 toolong(int dummy)
423 (void) fprintf(stderr, gettext("%s: time-out in driver\n"), myname);
424 exit(1);
428 main(int argc, char **argv)
430 int opt, errflag = 0;
431 char *arg;
433 myname = *argv;
436 (void) setlocale(LC_ALL, "");
438 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
439 #define TEXT_DOMAIN "SYS_TEST"
440 #endif
441 (void) textdomain(TEXT_DOMAIN);
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;
453 if (errflag != 0 || optind >= argc)
454 usage();
456 /* Set alarm to avoid stalling on any driver errors. */
457 (void) signal(SIGALRM, toolong);
458 (void) alarm(2);
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));
469 usage();
470 return (1);