comparison usr/src/cmd/cmd-inet/usr.sbin/sppptun/sppptun.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
comparison
equal deleted inserted replaced
-1:000000000000 0:c9caec207d52
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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * sppptun.c - Solaris STREAMS PPP multiplexing tunnel driver
24 * installer.
25 *
26 * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
27 * All rights reserved.
28 */
29
30 #pragma ident "@(#)sppptun.c 1.2 05/06/08 SMI"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <signal.h>
39 #include <alloca.h>
40 #include <stropts.h>
41 #include <fcntl.h>
42 #include <locale.h>
43 #include <sys/dlpi.h>
44 #include <sys/fcntl.h>
45 #include <sys/stropts.h>
46 #include <sys/socket.h>
47 #include <net/if.h>
48 #include <netinet/in.h>
49 #include <netinet/if_ether.h>
50 #include <net/sppptun.h>
51
52 static char *myname; /* Copied from argv[0] */
53 static int verbose; /* -v on command line */
54
55 /* Data gathered during per-style attach routine. */
56 struct attach_data {
57 ppptun_lname appstr; /* String to append to interface name (PPA) */
58 ppptun_atype localaddr; /* Local interface address */
59 int locallen; /* Length of local address */
60 };
61
62 /* Per-protocol plumbing data */
63 struct protos {
64 const char *name;
65 const char *desc;
66 int (*attach)(struct protos *prot, char *ifname,
67 struct attach_data *adata);
68 int protval;
69 int style;
70 };
71
72 /*
73 * Print a usage string and terminate. Used for command line argument
74 * errors. Does not return.
75 */
76 static void
77 usage(void)
78 {
79 (void) fprintf(stderr, gettext(
80 "Usage:\n\t%s plumb [<protocol> <device>]\n"
81 "\t%s unplumb <interface-name>\n"
82 "\t%s query\n"), myname, myname, myname);
83 exit(1);
84 }
85
86 /*
87 * Await a DLPI response to a previous driver command. "etype" is set
88 * to the expected response primitive. "rptr" and "rlen" may point to
89 * a buffer to hold returned data, if desired. Otherwise, "rptr" is
90 * NULL. Returns -1 on error, 0 on success.
91 *
92 * If "rlen" is a positive number, then it indicates the number of
93 * bytes expected in return, and any longer response is truncated to
94 * that value, and any shorter response generates a warning message.
95 * If it's a negative number, then it indicates the maximum number of
96 * bytes expected, and no warning is printed if fewer are received.
97 */
98 static int
99 dlpi_reply(int fd, int etype, void *rptr, int rlen)
100 {
101 /* Align 'buf' on natural boundary for aggregates. */
102 uintptr_t buf[BUFSIZ/sizeof (uintptr_t)];
103 int flags;
104 union DL_primitives *dlp = (union DL_primitives *)buf;
105 struct strbuf ctl;
106
107 /* read reply */
108 ctl.buf = (caddr_t)dlp;
109 ctl.len = 0;
110 ctl.maxlen = BUFSIZ;
111 flags = 0;
112 if (getmsg(fd, &ctl, NULL, &flags) < 0) {
113 perror("getmsg");
114 return (-1);
115 }
116
117 /* Validate reply. */
118 if (ctl.len < sizeof (t_uscalar_t)) {
119 (void) fprintf(stderr, gettext("%s: request: short reply\n"),
120 myname);
121 return (-1);
122 }
123
124 if (dlp->dl_primitive == DL_ERROR_ACK) {
125 (void) fprintf(stderr,
126 gettext("%s: request: dl_errno %lu errno %lu\n"), myname,
127 dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno);
128 return (-1);
129 }
130 if (dlp->dl_primitive != etype) {
131 (void) fprintf(stderr, gettext("%s: request: unexpected "
132 "dl_primitive %lu received\n"), myname, dlp->dl_primitive);
133 return (-1);
134 }
135 if (rptr == NULL)
136 return (0);
137 if (ctl.len < rlen) {
138 (void) fprintf(stderr, gettext("%s: request: short information"
139 " received %d < %d\n"), myname, ctl.len, rlen);
140 return (-1);
141 }
142 if (rlen < 0)
143 rlen = -rlen;
144 (void) memcpy(rptr, buf, rlen);
145 return (0);
146 }
147
148 /*
149 * Send a DLPI Info-Request message and return the response in the
150 * provided buffer. Returns -1 on error, 0 on success.
151 */
152 static int
153 dlpi_info_req(int fd, dl_info_ack_t *info_ack)
154 {
155 dl_info_req_t info_req;
156 struct strbuf ctl;
157 int flags;
158
159 (void) memset(&info_req, '\0', sizeof (info_req));
160 info_req.dl_primitive = DL_INFO_REQ;
161
162 ctl.maxlen = 0;
163 ctl.len = DL_INFO_REQ_SIZE;
164 ctl.buf = (char *)&info_req;
165
166 flags = 0;
167 if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
168 perror("putmsg DL_INFO_REQ");
169 return (-1);
170 }
171 return (dlpi_reply(fd, DL_INFO_ACK, info_ack, sizeof (*info_ack)));
172 }
173
174 /*
175 * Send a DLPI Attach-Request message for the indicated PPA. Returns
176 * -1 on error, 0 for success.
177 */
178 static int
179 dlpi_attach_req(int fd, int ppa)
180 {
181 dl_attach_req_t attach_req;
182 struct strbuf ctl;
183 int flags;
184
185 (void) memset(&attach_req, '\0', sizeof (attach_req));
186 attach_req.dl_primitive = DL_ATTACH_REQ;
187 attach_req.dl_ppa = ppa;
188
189 ctl.maxlen = 0;
190 ctl.len = DL_ATTACH_REQ_SIZE;
191 ctl.buf = (char *)&attach_req;
192
193 flags = 0;
194 if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
195 perror("putmsg DL_ATTACH_REQ");
196 return (-1);
197 }
198 return (dlpi_reply(fd, DL_OK_ACK, NULL, 0));
199 }
200
201 /*
202 * Send a DLPI Bind-Request message for the requested SAP and set the
203 * local address. Returns -1 for error. Otherwise, the length of the
204 * local address is returned.
205 */
206 static int
207 dlpi_bind_req(int fd, int sap, uint8_t *localaddr, int maxaddr)
208 {
209 dl_bind_req_t bind_req;
210 dl_bind_ack_t *back;
211 struct strbuf ctl;
212 int flags, repsize, rsize;
213
214 (void) memset(&bind_req, '\0', sizeof (*&bind_req));
215 bind_req.dl_primitive = DL_BIND_REQ;
216 /* DLPI SAPs are in host byte order! */
217 bind_req.dl_sap = sap;
218 bind_req.dl_service_mode = DL_CLDLS;
219
220 ctl.maxlen = 0;
221 ctl.len = DL_BIND_REQ_SIZE;
222 ctl.buf = (char *)&bind_req;
223
224 flags = 0;
225 if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
226 perror("putmsg DL_BIND_REQ");
227 return (-1);
228 }
229
230 repsize = sizeof (*back) + maxaddr;
231 back = (dl_bind_ack_t *)alloca(repsize);
232 if (dlpi_reply(fd, DL_BIND_ACK, (void *)back, -repsize) < 0)
233 return (-1);
234 rsize = back->dl_addr_length;
235 if (rsize > maxaddr || back->dl_addr_offset+rsize > repsize) {
236 (void) fprintf(stderr, gettext("%s: Bad hardware address size "
237 "from driver; %d > %d or %lu+%d > %d\n"), myname,
238 rsize, maxaddr, back->dl_addr_offset, rsize, repsize);
239 return (-1);
240 }
241 (void) memcpy(localaddr, (char *)back + back->dl_addr_offset, rsize);
242 return (rsize);
243 }
244
245 /*
246 * Return a printable string for a DLPI style number. (Unfortunately,
247 * these style numbers aren't just simple integer values, and printing
248 * with %d gives ugly output.)
249 */
250 static const char *
251 styleof(int dlstyle)
252 {
253 static char buf[32];
254
255 switch (dlstyle) {
256 case DL_STYLE1:
257 return ("1");
258 case DL_STYLE2:
259 return ("2");
260 }
261 (void) snprintf(buf, sizeof (buf), gettext("Unknown (0x%04X)"),
262 dlstyle);
263 return ((const char *)buf);
264 }
265
266 /*
267 * General DLPI attach function. This is called indirectly through
268 * the protos structure for the selected lower stream protocol.
269 */
270 static int
271 dlpi_attach(struct protos *prot, char *ifname, struct attach_data *adata)
272 {
273 int devfd, ppa, dlstyle, retv;
274 dl_info_ack_t dl_info;
275 char tname[MAXPATHLEN], *cp;
276
277 cp = ifname + strlen(ifname) - 1;
278 while (cp > ifname && isdigit(*cp))
279 cp--;
280 cp++;
281 ppa = strtol(cp, NULL, 10);
282
283 /*
284 * Try once for the exact device name as a node. If it's
285 * there, then this should be a DLPI style 1 driver (one node
286 * per instance). If it's not, then it should be a style 2
287 * driver (attach specifies instance number).
288 */
289 dlstyle = DL_STYLE1;
290 (void) strlcpy(tname, ifname, MAXPATHLEN-1);
291 if ((devfd = open(tname, O_RDWR)) < 0) {
292 if (cp < ifname + MAXPATHLEN)
293 tname[cp - ifname] = '\0';
294 if ((devfd = open(tname, O_RDWR)) < 0) {
295 perror(ifname);
296 return (-1);
297 }
298 dlstyle = DL_STYLE2;
299 }
300
301 if (verbose)
302 (void) printf(gettext("requesting device info on %s\n"),
303 tname);
304 if (dlpi_info_req(devfd, &dl_info))
305 return (-1);
306 if (dl_info.dl_provider_style != dlstyle) {
307 (void) fprintf(stderr, gettext("%s: unexpected DLPI provider "
308 "style on %s: got %s, "), myname, tname,
309 styleof(dl_info.dl_provider_style));
310 (void) fprintf(stderr, gettext("expected %s\n"),
311 styleof(dlstyle));
312 if (ifname[0] != '\0' &&
313 !isdigit(ifname[strlen(ifname) - 1])) {
314 (void) fprintf(stderr, gettext("(did you forget an "
315 "instance number?)\n"));
316 }
317 (void) close(devfd);
318 return (-1);
319 }
320
321 if (dlstyle == DL_STYLE2) {
322 if (verbose)
323 (void) printf(gettext("attaching to ppa %d\n"), ppa);
324 if (dlpi_attach_req(devfd, ppa)) {
325 (void) close(devfd);
326 return (-1);
327 }
328 }
329
330 if (verbose)
331 (void) printf(gettext("binding to Ethertype %04X\n"),
332 prot->protval);
333 retv = dlpi_bind_req(devfd, prot->protval,
334 (uint8_t *)&adata->localaddr, sizeof (adata->localaddr));
335 if (retv < 0) {
336 (void) close(devfd);
337 return (-1);
338 }
339 adata->locallen = retv;
340
341 (void) snprintf(adata->appstr, sizeof (adata->appstr), "%d", ppa);
342 return (devfd);
343 }
344
345
346 static struct protos proto_list[] = {
347 { "pppoe", "RFC 2516 PPP over Ethernet", dlpi_attach, ETHERTYPE_PPPOES,
348 PTS_PPPOE },
349 { "pppoed", "RFC 2516 PPP over Ethernet Discovery", dlpi_attach,
350 ETHERTYPE_PPPOED, PTS_PPPOE },
351 { NULL }
352 };
353
354 /*
355 * Issue a STREAMS I_STR ioctl and fetch the result. Returns -1 on
356 * error, or length of returned data on success.
357 */
358 static int
359 strioctl(int fd, int cmd, void *ptr, int ilen, int olen, const char *iocname)
360 {
361 struct strioctl str;
362
363 str.ic_cmd = cmd;
364 str.ic_timout = 0;
365 str.ic_len = ilen;
366 str.ic_dp = ptr;
367
368 if (ioctl(fd, I_STR, &str) == -1) {
369 perror(iocname);
370 return (-1);
371 }
372
373 if (olen >= 0) {
374 if (str.ic_len > olen && verbose > 1) {
375 (void) printf(gettext("%s:%s: extra data received; "
376 "%d > %d\n"), myname, iocname, str.ic_len, olen);
377 } else if (str.ic_len < olen) {
378 (void) fprintf(stderr, gettext("%s:%s: expected %d "
379 "bytes, got %d\n"), myname, iocname, olen,
380 str.ic_len);
381 return (-1);
382 }
383 }
384
385 return (str.ic_len);
386 }
387
388 /*
389 * Handle user request to plumb a new lower stream under the sppptun
390 * driver.
391 */
392 static int
393 plumb_it(int argc, char **argv)
394 {
395 int devfd, muxfd, muxid;
396 struct ppptun_info pti;
397 char *cp, *ifname;
398 struct protos *prot;
399 char dname[MAXPATHLEN];
400 struct attach_data adata;
401
402 /* If no protocol requested, then list known protocols. */
403 if (optind == argc) {
404 (void) puts("Known tunneling protocols:");
405 for (prot = proto_list; prot->name != NULL; prot++)
406 (void) printf("\t%s\t%s\n", prot->name, prot->desc);
407 return (0);
408 }
409
410 /* If missing protocol or device, then abort. */
411 if (optind != argc-2)
412 usage();
413
414 /* Look up requested protocol. */
415 cp = argv[optind++];
416 for (prot = proto_list; prot->name != NULL; prot++)
417 if (strcasecmp(cp, prot->name) == 0)
418 break;
419 if (prot->name == NULL) {
420 (void) fprintf(stderr, gettext("%s: unknown protocol %s\n"),
421 myname, cp);
422 return (1);
423 }
424
425 /* Get interface and make relative to /dev/ if necessary. */
426 ifname = argv[optind];
427 if (ifname[0] != '.' && ifname[0] != '/') {
428 (void) snprintf(dname, sizeof (dname), "/dev/%s", ifname);
429 ifname = dname;
430 }
431
432 /* Call per-protocol attach routine to open device */
433 if (verbose)
434 (void) printf(gettext("opening %s\n"), ifname);
435 devfd = (*prot->attach)(prot, ifname, &adata);
436 if (devfd < 0)
437 return (1);
438
439 /* Open sppptun driver */
440 if (verbose)
441 (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME);
442 if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) {
443 perror("/dev/" PPP_TUN_NAME);
444 return (1);
445 }
446
447 /* Push sppptun module on top of lower driver. */
448 if (verbose)
449 (void) printf(gettext("pushing %s on %s\n"), PPP_TUN_NAME,
450 ifname);
451 if (ioctl(devfd, I_PUSH, PPP_TUN_NAME) == -1) {
452 perror("I_PUSH " PPP_TUN_NAME);
453 return (1);
454 }
455
456 /* Get the name of the newly-created lower stream. */
457 if (verbose)
458 (void) printf(gettext("getting new interface name\n"));
459 if (strioctl(devfd, PPPTUN_GNAME, pti.pti_name, 0,
460 sizeof (pti.pti_name), "PPPTUN_GNAME") < 0)
461 return (1);
462 if (verbose)
463 (void) printf(gettext("got interface %s\n"), pti.pti_name);
464
465 /* Convert stream name to protocol-specific name. */
466 if ((cp = strchr(pti.pti_name, ':')) != NULL)
467 *cp = '\0';
468 (void) snprintf(pti.pti_name+strlen(pti.pti_name),
469 sizeof (pti.pti_name)-strlen(pti.pti_name), "%s:%s", adata.appstr,
470 prot->name);
471
472 /* Change the lower stream name. */
473 if (verbose)
474 (void) printf(gettext("resetting interface name to %s\n"),
475 pti.pti_name);
476 if (strioctl(devfd, PPPTUN_SNAME, pti.pti_name,
477 sizeof (pti.pti_name), 0, "PPPTUN_SNAME") < 0) {
478 if (errno == EEXIST)
479 (void) fprintf(stderr, gettext("%s: %s already "
480 "installed\n"), myname, pti.pti_name);
481 return (1);
482 }
483
484 /*
485 * Send down the local interface address to the lower stream
486 * so that it can originate packets.
487 */
488 if (verbose)
489 (void) printf(gettext("send down local address\n"));
490 if (strioctl(devfd, PPPTUN_LCLADDR, &adata.localaddr, adata.locallen,
491 0, "PPPTUN_LCLADDR") < 0)
492 return (1);
493
494 /* Link the lower stream under the tunnel device. */
495 if (verbose)
496 (void) printf(gettext("doing I_PLINK\n"));
497 if ((muxid = ioctl(muxfd, I_PLINK, devfd)) == -1) {
498 perror("I_PLINK");
499 return (1);
500 }
501
502 /*
503 * Give the tunnel driver the multiplex ID of the new lower
504 * stream. This allows the unplumb function to find and
505 * disconnect the lower stream.
506 */
507 if (verbose)
508 (void) printf(gettext("sending muxid %d and style %d to "
509 "driver\n"), muxid, prot->style);
510 pti.pti_muxid = muxid;
511 pti.pti_style = prot->style;
512 if (strioctl(muxfd, PPPTUN_SINFO, &pti, sizeof (pti), 0,
513 "PPPTUN_SINFO") < 0)
514 return (1);
515
516 if (verbose)
517 (void) printf(gettext("done; installed %s\n"), pti.pti_name);
518 else
519 (void) puts(pti.pti_name);
520
521 return (0);
522 }
523
524 /*
525 * Handle user request to unplumb an existing lower stream from the
526 * sppptun driver.
527 */
528 static int
529 unplumb_it(int argc, char **argv)
530 {
531 char *ifname;
532 int muxfd;
533 struct ppptun_info pti;
534
535 /*
536 * Need to have the name of the lower stream on the command
537 * line.
538 */
539 if (optind != argc-1)
540 usage();
541
542 ifname = argv[optind];
543
544 /* Open the tunnel driver. */
545 if (verbose)
546 (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME);
547 if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) {
548 perror("/dev/" PPP_TUN_NAME);
549 return (1);
550 }
551
552 /* Get lower stream information; including multiplex ID. */
553 if (verbose)
554 (void) printf(gettext("getting info from driver\n"));
555 (void) strncpy(pti.pti_name, ifname, sizeof (pti.pti_name));
556 if (strioctl(muxfd, PPPTUN_GINFO, &pti, sizeof (pti),
557 sizeof (pti), "PPPTUN_GINFO") < 0)
558 return (1);
559 if (verbose)
560 (void) printf(gettext("got muxid %d from driver\n"),
561 pti.pti_muxid);
562
563 /* Unlink lower stream from driver. */
564 if (verbose)
565 (void) printf(gettext("doing I_PUNLINK\n"));
566 if (ioctl(muxfd, I_PUNLINK, pti.pti_muxid) < 0) {
567 perror("I_PUNLINK");
568 return (1);
569 }
570 if (verbose)
571 (void) printf(gettext("done!\n"));
572
573 return (0);
574 }
575
576 /*
577 * Handle user request to list lower streams plumbed under the sppptun
578 * driver.
579 */
580 /*ARGSUSED*/
581 static int
582 query_interfaces(int argc, char **argv)
583 {
584 int muxfd, i;
585 union ppptun_name ptn;
586
587 /* No other arguments permitted. */
588 if (optind != argc)
589 usage();
590
591 /* Open the tunnel driver. */
592 if (verbose)
593 (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME);
594 if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) {
595 perror("/dev/" PPP_TUN_NAME);
596 return (1);
597 }
598
599 /* Read and print names of lower streams. */
600 for (i = 0; ; i++) {
601 ptn.ptn_index = i;
602 if (strioctl(muxfd, PPPTUN_GNNAME, &ptn, sizeof (ptn),
603 sizeof (ptn), "PPPTUN_GNNAME") < 0) {
604 perror("PPPTUN_GNNAME");
605 break;
606 }
607 /* Stop when we index off the end of the list. */
608 if (ptn.ptn_name[0] == '\0')
609 break;
610 (void) puts(ptn.ptn_name);
611 }
612 return (0);
613 }
614
615 /*
616 * Invoked by SIGALRM -- timer prevents problems in driver from
617 * hanging the utility.
618 */
619 /*ARGSUSED*/
620 static void
621 toolong(int dummy)
622 {
623 (void) fprintf(stderr, gettext("%s: time-out in driver\n"), myname);
624 exit(1);
625 }
626
627 int
628 main(int argc, char **argv)
629 {
630 int opt, errflag = 0;
631 char *arg;
632
633 myname = *argv;
634
635
636 (void) setlocale(LC_ALL, "");
637
638 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
639 #define TEXT_DOMAIN "SYS_TEST"
640 #endif
641 (void) textdomain(TEXT_DOMAIN);
642
643 /* Parse command line flags */
644 while ((opt = getopt(argc, argv, "v")) != EOF)
645 switch (opt) {
646 case 'v':
647 verbose++;
648 break;
649 default:
650 errflag++;
651 break;
652 }
653 if (errflag != 0 || optind >= argc)
654 usage();
655
656 /* Set alarm to avoid stalling on any driver errors. */
657 (void) signal(SIGALRM, toolong);
658 (void) alarm(2);
659
660 /* Switch out based on user-requested function. */
661 arg = argv[optind++];
662 if (strcmp(arg, "plumb") == 0)
663 return (plumb_it(argc, argv));
664 if (strcmp(arg, "unplumb") == 0)
665 return (unplumb_it(argc, argv));
666 if (strcmp(arg, "query") == 0)
667 return (query_interfaces(argc, argv));
668
669 usage();
670 return (1);
671 }