#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stropts.h>
#include <fcntl.h>
#include <locale.h>
#include <sys/fcntl.h>
#include <sys/stropts.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/sppptun.h>
#include <libdlpi.h>
static char *myname;
static int verbose;
struct attach_data {
ppptun_lname appstr;
ppptun_atype localaddr;
uint_t locallen;
uint_t sap;
};
struct protos {
const char *name;
const char *desc;
int (*attach)(struct protos *prot, char *linkname,
struct attach_data *adata);
uint_t protval;
int style;
};
static void
usage(void)
{
(void) fprintf(stderr, gettext(
"Usage:\n\t%s plumb [-s <sap>] [<protocol> <device>]\n"
"\t%s unplumb <interface-name>\n"
"\t%s query\n"), myname, myname, myname);
exit(1);
}
static int
sppp_dlpi(struct protos *prot, char *linkname, struct attach_data *adata)
{
int retv;
dlpi_handle_t dh;
if (verbose)
(void) printf(gettext("opening DLPI link %s\n"), linkname);
if ((retv = dlpi_open(linkname, &dh, 0)) != DLPI_SUCCESS) {
(void) fprintf(stderr, gettext("%s: failed opening %s: %s\n"),
myname, linkname, dlpi_strerror(retv));
return (-1);
}
if (verbose) {
(void) printf(gettext("binding to Ethertype %04X\n"),
adata->sap);
}
if ((retv = dlpi_bind(dh, adata->sap, NULL)) != DLPI_SUCCESS) {
(void) fprintf(stderr,
gettext("%s: failed binding on %s: %s\n"),
myname, linkname, dlpi_strerror(retv));
dlpi_close(dh);
return (-1);
}
adata->locallen = DLPI_PHYSADDR_MAX;
if ((retv = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, &adata->localaddr,
&adata->locallen)) != DLPI_SUCCESS) {
(void) fprintf(stderr, gettext("%s: failed getting physical"
" address on %s: %s\n"), myname, linkname,
dlpi_strerror(retv));
dlpi_close(dh);
return (-1);
}
if (strlcpy(adata->appstr, linkname, sizeof (adata->appstr)) >=
sizeof (adata->appstr)) {
(void) fprintf(stderr,
gettext("%s: interface name too long: %s\n"),
myname, linkname);
dlpi_close(dh);
return (-1);
}
return (dlpi_fd(dh));
}
static struct protos proto_list[] = {
{ "pppoe", "RFC 2516 PPP over Ethernet", sppp_dlpi, ETHERTYPE_PPPOES,
PTS_PPPOE },
{ "pppoed", "RFC 2516 PPP over Ethernet Discovery", sppp_dlpi,
ETHERTYPE_PPPOED, PTS_PPPOE },
{ NULL }
};
static int
strioctl(int fd, int cmd, void *ptr, int ilen, int olen, const char *iocname)
{
struct strioctl str;
str.ic_cmd = cmd;
str.ic_timout = 0;
str.ic_len = ilen;
str.ic_dp = ptr;
if (ioctl(fd, I_STR, &str) == -1) {
perror(iocname);
return (-1);
}
if (olen >= 0) {
if (str.ic_len > olen && verbose > 1) {
(void) printf(gettext("%s:%s: extra data received; "
"%d > %d\n"), myname, iocname, str.ic_len, olen);
} else if (str.ic_len < olen) {
(void) fprintf(stderr, gettext("%s:%s: expected %d "
"bytes, got %d\n"), myname, iocname, olen,
str.ic_len);
return (-1);
}
}
return (str.ic_len);
}
static int
plumb_it(int argc, char **argv)
{
int opt, devfd, muxfd, muxid;
struct ppptun_info pti;
char *cp, *linkname;
struct protos *prot;
struct attach_data adata;
uint_t sap = 0;
if (optind == argc) {
(void) puts("Known tunneling protocols:");
for (prot = proto_list; prot->name != NULL; prot++)
(void) printf("\t%s\t%s\n", prot->name, prot->desc);
return (0);
}
while ((opt = getopt(argc, argv, "s:")) != EOF) {
switch (opt) {
case 's':
sap = strtoul(optarg, NULL, 16);
break;
default:
usage();
}
}
if (optind != argc-2)
usage();
cp = argv[optind++];
for (prot = proto_list; prot->name != NULL; prot++)
if (strcasecmp(cp, prot->name) == 0)
break;
if (prot->name == NULL) {
(void) fprintf(stderr, gettext("%s: unknown protocol %s\n"),
myname, cp);
return (1);
}
adata.sap = sap == 0 ? prot->protval : sap;
linkname = argv[optind];
if (verbose)
(void) printf(gettext("opening %s\n"), linkname);
if ((devfd = (*prot->attach)(prot, linkname, &adata)) < 0)
return (1);
if (verbose)
(void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME);
if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) {
perror("/dev/" PPP_TUN_NAME);
return (1);
}
if (verbose)
(void) printf(gettext("pushing %s on %s\n"), PPP_TUN_NAME,
linkname);
if (ioctl(devfd, I_PUSH, PPP_TUN_NAME) == -1) {
perror("I_PUSH " PPP_TUN_NAME);
return (1);
}
if (snprintf(pti.pti_name, sizeof (pti.pti_name), "%s:%s",
adata.appstr, prot->name) >= sizeof (pti.pti_name)) {
(void) fprintf(stderr,
gettext("%s: stream name too long: %s:%s\n"),
myname, adata.appstr, prot->name);
return (1);
}
if (verbose)
(void) printf(gettext("resetting interface name to %s\n"),
pti.pti_name);
if (strioctl(devfd, PPPTUN_SNAME, pti.pti_name,
sizeof (pti.pti_name), 0, "PPPTUN_SNAME") < 0) {
if (errno == EEXIST)
(void) fprintf(stderr, gettext("%s: %s already "
"installed\n"), myname, pti.pti_name);
return (1);
}
if (verbose)
(void) printf(gettext("send down local address\n"));
if (strioctl(devfd, PPPTUN_LCLADDR, &adata.localaddr, adata.locallen,
0, "PPPTUN_LCLADDR") < 0)
return (1);
if (verbose)
(void) printf(gettext("send down SAP %x\n"), adata.sap);
if (strioctl(devfd, PPPTUN_SSAP, &adata.sap, sizeof (adata.sap), 0,
"PPPTUN_SSAP") < 0)
return (1);
if (verbose)
(void) printf(gettext("doing I_PLINK\n"));
if ((muxid = ioctl(muxfd, I_PLINK, devfd)) == -1) {
perror("I_PLINK");
return (1);
}
if (verbose)
(void) printf(gettext("sending muxid %d and style %d to "
"driver\n"), muxid, prot->style);
pti.pti_muxid = muxid;
pti.pti_style = prot->style;
if (strioctl(muxfd, PPPTUN_SINFO, &pti, sizeof (pti), 0,
"PPPTUN_SINFO") < 0)
return (1);
if (verbose)
(void) printf(gettext("done; installed %s\n"), pti.pti_name);
else
(void) puts(pti.pti_name);
return (0);
}
static int
unplumb_it(int argc, char **argv)
{
char *ifname;
int muxfd;
struct ppptun_info pti;
if (optind != argc-1)
usage();
ifname = argv[optind];
if (verbose)
(void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME);
if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) {
perror("/dev/" PPP_TUN_NAME);
return (1);
}
if (verbose)
(void) printf(gettext("getting info from driver\n"));
(void) strncpy(pti.pti_name, ifname, sizeof (pti.pti_name));
if (strioctl(muxfd, PPPTUN_GINFO, &pti, sizeof (pti),
sizeof (pti), "PPPTUN_GINFO") < 0)
return (1);
if (verbose)
(void) printf(gettext("got muxid %d from driver\n"),
pti.pti_muxid);
if (verbose)
(void) printf(gettext("doing I_PUNLINK\n"));
if (ioctl(muxfd, I_PUNLINK, pti.pti_muxid) < 0) {
perror("I_PUNLINK");
return (1);
}
if (verbose)
(void) printf(gettext("done!\n"));
return (0);
}
static int
query_interfaces(int argc, char **argv)
{
int muxfd, i;
union ppptun_name ptn;
if (optind != argc)
usage();
if (verbose)
(void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME);
if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) {
perror("/dev/" PPP_TUN_NAME);
return (1);
}
for (i = 0; ; i++) {
ptn.ptn_index = i;
if (strioctl(muxfd, PPPTUN_GNNAME, &ptn, sizeof (ptn),
sizeof (ptn), "PPPTUN_GNNAME") < 0) {
perror("PPPTUN_GNNAME");
break;
}
if (ptn.ptn_name[0] == '\0')
break;
(void) puts(ptn.ptn_name);
}
return (0);
}
static void
toolong(int dummy)
{
(void) fprintf(stderr, gettext("%s: time-out in driver\n"), myname);
exit(1);
}
int
main(int argc, char **argv)
{
int opt, errflag = 0;
char *arg;
myname = *argv;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
while ((opt = getopt(argc, argv, "v")) != EOF)
switch (opt) {
case 'v':
verbose++;
break;
default:
errflag++;
break;
}
if (errflag != 0 || optind >= argc)
usage();
(void) signal(SIGALRM, toolong);
(void) alarm(2);
arg = argv[optind++];
if (strcmp(arg, "plumb") == 0)
return (plumb_it(argc, argv));
if (strcmp(arg, "unplumb") == 0)
return (unplumb_it(argc, argv));
if (strcmp(arg, "query") == 0)
return (query_interfaces(argc, argv));
usage();
return (1);
}