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]
22 * sppptun.c - Solaris STREAMS PPP multiplexing tunnel driver
25 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
39 #include <sys/fcntl.h>
40 #include <sys/stropts.h>
41 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <netinet/if_ether.h>
45 #include <net/sppptun.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. */
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 */
63 int (*attach
)(struct protos
*prot
, char *linkname
,
64 struct attach_data
*adata
);
70 * Print a usage string and terminate. Used for command line argument
71 * errors. Does not return.
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
);
84 * General DLPI function. This is called indirectly through
85 * the protos structure for the selected lower stream protocol.
89 sppp_dlpi(struct protos
*prot
, char *linkname
, struct attach_data
*adata
)
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
));
103 (void) printf(gettext("binding to Ethertype %04X\n"),
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
));
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
));
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"),
133 return (dlpi_fd(dh
));
137 static struct protos proto_list
[] = {
138 { "pppoe", "RFC 2516 PPP over Ethernet", sppp_dlpi
, ETHERTYPE_PPPOES
,
140 { "pppoed", "RFC 2516 PPP over Ethernet Discovery", sppp_dlpi
,
141 ETHERTYPE_PPPOED
, PTS_PPPOE
},
146 * Issue a STREAMS I_STR ioctl and fetch the result. Returns -1 on
147 * error, or length of returned data on success.
150 strioctl(int fd
, int cmd
, void *ptr
, int ilen
, int olen
, const char *iocname
)
159 if (ioctl(fd
, I_STR
, &str
) == -1) {
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
,
180 * Handle user request to plumb a new lower stream under the sppptun
184 plumb_it(int argc
, char **argv
)
186 int opt
, devfd
, muxfd
, muxid
;
187 struct ppptun_info pti
;
190 struct attach_data adata
;
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
);
201 /* Parse plumbing flags */
202 while ((opt
= getopt(argc
, argv
, "s:")) != EOF
) {
205 sap
= strtoul(optarg
, NULL
, 16);
212 /* If missing protocol or device, then abort. */
213 if (optind
!= argc
-2)
216 /* Look up requested protocol. */
218 for (prot
= proto_list
; prot
->name
!= NULL
; prot
++)
219 if (strcasecmp(cp
, prot
->name
) == 0)
221 if (prot
->name
== NULL
) {
222 (void) fprintf(stderr
, gettext("%s: unknown protocol %s\n"),
227 adata
.sap
= sap
== 0 ? prot
->protval
: sap
;
230 linkname
= argv
[optind
];
231 /* Call per-protocol attach routine to open device */
233 (void) printf(gettext("opening %s\n"), linkname
);
234 if ((devfd
= (*prot
->attach
)(prot
, linkname
, &adata
)) < 0)
237 /* Open sppptun driver */
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
);
245 /* Push sppptun module on top of lower driver. */
247 (void) printf(gettext("pushing %s on %s\n"), PPP_TUN_NAME
,
249 if (ioctl(devfd
, I_PUSH
, PPP_TUN_NAME
) == -1) {
250 perror("I_PUSH " PPP_TUN_NAME
);
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
);
263 /* Change the lower stream name. */
265 (void) printf(gettext("resetting interface name to %s\n"),
267 if (strioctl(devfd
, PPPTUN_SNAME
, pti
.pti_name
,
268 sizeof (pti
.pti_name
), 0, "PPPTUN_SNAME") < 0) {
270 (void) fprintf(stderr
, gettext("%s: %s already "
271 "installed\n"), myname
, pti
.pti_name
);
276 * Send down the local interface address to the lower stream
277 * so that it can originate packets.
280 (void) printf(gettext("send down local address\n"));
281 if (strioctl(devfd
, PPPTUN_LCLADDR
, &adata
.localaddr
, adata
.locallen
,
282 0, "PPPTUN_LCLADDR") < 0)
286 * And set the SAP value.
289 (void) printf(gettext("send down SAP %x\n"), adata
.sap
);
290 if (strioctl(devfd
, PPPTUN_SSAP
, &adata
.sap
, sizeof (adata
.sap
), 0,
294 /* Link the lower stream under the tunnel device. */
296 (void) printf(gettext("doing I_PLINK\n"));
297 if ((muxid
= ioctl(muxfd
, I_PLINK
, devfd
)) == -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.
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,
317 (void) printf(gettext("done; installed %s\n"), pti
.pti_name
);
319 (void) puts(pti
.pti_name
);
325 * Handle user request to unplumb an existing lower stream from the
329 unplumb_it(int argc
, char **argv
)
333 struct ppptun_info pti
;
336 * Need to have the name of the lower stream on the command
339 if (optind
!= argc
-1)
342 ifname
= argv
[optind
];
344 /* Open the tunnel driver. */
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
);
352 /* Get lower stream information; including multiplex ID. */
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)
360 (void) printf(gettext("got muxid %d from driver\n"),
363 /* Unlink lower stream from driver. */
365 (void) printf(gettext("doing I_PUNLINK\n"));
366 if (ioctl(muxfd
, I_PUNLINK
, pti
.pti_muxid
) < 0) {
371 (void) printf(gettext("done!\n"));
377 * Handle user request to list lower streams plumbed under the sppptun
382 query_interfaces(int argc
, char **argv
)
385 union ppptun_name ptn
;
387 /* No other arguments permitted. */
391 /* Open the tunnel driver. */
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
);
399 /* Read and print names of lower streams. */
402 if (strioctl(muxfd
, PPPTUN_GNNAME
, &ptn
, sizeof (ptn
),
403 sizeof (ptn
), "PPPTUN_GNNAME") < 0) {
404 perror("PPPTUN_GNNAME");
407 /* Stop when we index off the end of the list. */
408 if (ptn
.ptn_name
[0] == '\0')
410 (void) puts(ptn
.ptn_name
);
416 * Invoked by SIGALRM -- timer prevents problems in driver from
417 * hanging the utility.
423 (void) fprintf(stderr
, gettext("%s: time-out in driver\n"), myname
);
428 main(int argc
, char **argv
)
430 int opt
, errflag
= 0;
436 (void) setlocale(LC_ALL
, "");
438 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
439 #define TEXT_DOMAIN "SYS_TEST"
441 (void) textdomain(TEXT_DOMAIN
);
443 /* Parse command line flags */
444 while ((opt
= getopt(argc
, argv
, "v")) != EOF
)
453 if (errflag
!= 0 || optind
>= argc
)
456 /* Set alarm to avoid stalling on any driver errors. */
457 (void) signal(SIGALRM
, toolong
);
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
));