diff options
author | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2012-01-18 03:13:05 +0000 |
---|---|---|
committer | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2012-01-18 03:13:05 +0000 |
commit | bd8d8220ca6e68ae13d0f436232bc575048ffbe5 (patch) | |
tree | 7d9bb500dfea03ae958ea1214ff70176f4525041 /usr.sbin/npppctl | |
parent | 9d0f7ef1e2ec7c4f36cabc65160cf8f93e503bbf (diff) |
Replace npppdctl(8) by new npppctl(8). npppctl was written from
scratch, it uses parser.c derived from ikectl(8) to have OpenBSD's
fashion. This includes related changes listed below:
- changed npppd control IPC heavyly.
- support IPv6 as tunnel source address.
- deleted support changing the configuration of npppd_ctl on running.
Because it is not so needed but it requires privilege operations.
- refactors.
man page helps from jmc. tested by sebastia.
ok deraadt sebastia sthen
Diffstat (limited to 'usr.sbin/npppctl')
-rw-r--r-- | usr.sbin/npppctl/Makefile | 8 | ||||
-rw-r--r-- | usr.sbin/npppctl/npppctl.8 | 138 | ||||
-rw-r--r-- | usr.sbin/npppctl/npppctl.c | 494 | ||||
-rw-r--r-- | usr.sbin/npppctl/parser.c | 319 | ||||
-rw-r--r-- | usr.sbin/npppctl/parser.h | 53 |
5 files changed, 1012 insertions, 0 deletions
diff --git a/usr.sbin/npppctl/Makefile b/usr.sbin/npppctl/Makefile new file mode 100644 index 00000000000..f2045c6139d --- /dev/null +++ b/usr.sbin/npppctl/Makefile @@ -0,0 +1,8 @@ +# $Id: Makefile,v 1.1 2012/01/18 03:13:04 yasuoka Exp $ +WARNS= 2 +PROG= npppctl +SRCS= npppctl.c parser.c +MAN= npppctl.8 +CPPFLAGS+= -I${.CURDIR}/../npppd/npppd -I${.CURDIR}/../npppd/common + +.include <bsd.prog.mk> diff --git a/usr.sbin/npppctl/npppctl.8 b/usr.sbin/npppctl/npppctl.8 new file mode 100644 index 00000000000..dca858fb406 --- /dev/null +++ b/usr.sbin/npppctl/npppctl.8 @@ -0,0 +1,138 @@ +.\" $OpenBSD: npppctl.8,v 1.1 2012/01/18 03:13:04 yasuoka Exp $ +.\" +.\" Copyright (c) 2012 Internet Initiative Japan Inc. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: January 18 2012 $ +.Dt NPPPCTL 8 +.Os +.Sh NAME +.Nm npppctl +.Nd control the npppd daemon +.\" XXX .Xr npppd 8 +.Sh SYNOPSIS +.Nm +.Op Fl n +.Op Fl r Ar rcvbuf_size +.Op Fl s Ar socket +.Op Fl t Ar timeout_sec +.Ar command +.Op Ar argument ... +.Sh DESCRIPTION +The +.Nm +utility controls +the +.\" XXX .Xr npppd 8 +npppd +daemon. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl n +Show IP addresses instead of their hostnames. +.It Fl r Ar rcvbuf_size +.Nm +may fail if the receive buffer is not large enough for +the requested information. +Use +.Ar rcvbuf_size +to extend the receive buffer size instead of the default value of 131072. +This must be greater than or equal to 2048. +.It Fl s Ar socket +Use +.Ar socket +instead of the default +.Pa /var/run/npppd_ctl +to communicate with +.\" XXX .Xr npppd 8 . +npppd. +.It Fl t Ar timeout_sec +Use +.Ar timeout_sec +as the I/O timeout timer value in seconds instead of the default value of 2. +.El +.Sh NPPPD CONTROL COMMANDS +The following commands are available: +.Bl -tag -width Ds +.It Cm clear Ar all | filter ... +Disconnect PPP sessions. +If +.Ar filter +is specified, only matching PPP sessions are disconnected. +If +.Ar all +is specified, all PPP sessions are disconnected. +See +.Cm session all +for the types of +.Ar filter . +.It Cm session all Op Ar filter ... +Show detailed information for PPP sessions. +If +.Ar filter +is specified, only matching PPP sessions are shown; +otherwise all PPP sessions are shown. +The following filters are available: +.Bl -tag -width Ds +.It Cm address Ar ip_address +Show or clear PPP sessions whose IP address match +.Ar ip_address . +.It Cm interface Ar interface_name +Show or clear PPP sessions that use the interface specified by +.Ar interface_name . +.It Cm ppp-id Ar id +Show or clear PPP sessions whose Ppp-Id match +.Ar id . +.It Cm protocol Ar protocol +Show or clear PPP sessions that use the tunneling protocol specified by +.Ar protocol . +.It Cm realm Ar realm_name +Show or clear PPP sessions whose realm match the specified +.Ar realm_name . +.It Cm username Ar username +Show or clear PPP sessions whose username match +.Ar username . +.El +.It Cm session brief +Show brief information for all PPP sessions. +.It Cm session packets +Show I/O statistics for all PPP sessions. +.El +.\" The following requests should be uncommented and used where appropriate. +.\" This next request is for sections 2, 3, and 9 function return values only. +.\" .Sh RETURN VALUES +.\" This next request is for sections 1, 6, 7 & 8 only. +.\" .Sh ENVIRONMENT +.\" .Sh FILES +.\" .Sh EXAMPLES +.\" This next request is for sections 1, 4, 6, and 8 only. +.\" .Sh DIAGNOSTICS +.\" The next request is for sections 2, 3, and 9 error and signal handling only. +.\" .Sh ERRORS +.\" .Sh SEE ALSO +.\" .Xr npppd 8 +.\" .Sh STANDARDS +.Sh HISTORY +The +.Nm +program first appeared in +.Ox +5.1. +.Sh AUTHORS +The +.Nm +program was written by Internet Initiative Japan Inc. +.\" .Sh CAVEATS +.\" .Sh BUGS diff --git a/usr.sbin/npppctl/npppctl.c b/usr.sbin/npppctl/npppctl.c new file mode 100644 index 00000000000..c81a95c0256 --- /dev/null +++ b/usr.sbin/npppctl/npppctl.c @@ -0,0 +1,494 @@ +/* $OpenBSD: npppctl.c,v 1.1 2012/01/18 03:13:04 yasuoka Exp $ */ + +/* + * Copyright (c) 2012 Internet Initiative Japan Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include <sys/uio.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <netdb.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <err.h> + +#include "parser.h" +#include "npppd_ctl.h" + +#ifndef nitems +#define nitems(_x) (sizeof(_x) / sizeof(_x[0])) +#endif + +#define NMAX_DISCONNECT 2048 + +static void usage (void); +static void on_exit (void); +static void show_clear_session (struct parse_result *, FILE *); +static void clear_session (u_int[], int, int, FILE *); +static void fprint_who_brief (int, struct npppd_who *, FILE *); +static void fprint_who_packets (int, struct npppd_who *, FILE *); +static void fprint_who_all (int, struct npppd_who *, FILE *); +static const char *peerstr (struct sockaddr *, char *, int); +static const char *humanize_duration (uint32_t, char *, int); +static const char *humanize_bytes (double, char *, int); +static bool filter_match(struct parse_result *, struct npppd_who *); + +static int ctlsock = -1; +static char ctlsockpath[] = "/tmp/npppctl.XXXXXX"; +static int nflag = 0; + +static void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, + "usage: %s [-n] [-r rcvbuf_size] [-s socket] [-t timeout_sec] " + "command [arg ...]\n", __progname); +} + +int +main(int argc, char *argv[]) +{ + int ch, timeout_sec, rcvbuf; + struct parse_result *result; + struct sockaddr_un sun; + const char *npppd_ctlpath = NPPPD_CTL_SOCK_PATH; + struct timeval tv; + extern int optind; + extern char *optarg; + + rcvbuf = NPPPD_CTL_MSGSZ * 64; + timeout_sec = 2; + while ((ch = getopt(argc, argv, "nr:s:t:")) != -1) + switch (ch) { + case 'n': + nflag = 1; + break; + case 'r': + if (sscanf(optarg, "%d", &rcvbuf) != 1 || rcvbuf <= 0) + errx(EXIT_FAILURE, "invalid rcvbuf_size value"); + if (rcvbuf < NPPPD_CTL_MSGSZ) + errx(EXIT_FAILURE, + "rcvbuf_size can not be less than %d", + NPPPD_CTL_MSGSZ); + break; + case 's': + npppd_ctlpath = optarg; + break; + case 't': + if (sscanf(optarg, "%d", &timeout_sec) != 1 || + timeout_sec <= 0) + errx(EXIT_FAILURE, "invalid timeout value"); + break; + default: + usage(); + exit(EXIT_FAILURE); + } + + argc -= optind; + argv += optind; + + if ((result = parse(argc, argv)) == NULL) + exit(EXIT_FAILURE); + + if ((ctlsock = mkstemp(ctlsockpath)) < 0) + err(1, "mkstemp"); + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, ctlsockpath, sizeof(sun.sun_path)); + + close(ctlsock); + unlink(ctlsockpath); + + if ((ctlsock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) + err(EXIT_FAILURE, "socket"); + if (bind(ctlsock, (struct sockaddr *)&sun, sizeof(sun)) != 0) + err(EXIT_FAILURE, "bind"); + + if (setsockopt(ctlsock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) + != 0) + err(EXIT_FAILURE, "setsockopt(,,SO_RCVBUF)"); + strlcpy(sun.sun_path, npppd_ctlpath, sizeof(sun.sun_path)); + if (connect(ctlsock, (struct sockaddr *)&sun, sizeof(sun)) != 0) + err(EXIT_FAILURE, "connect"); + /* setup cleaner */ + atexit(on_exit); + + tv.tv_sec = timeout_sec; + tv.tv_usec = 0L; + if (setsockopt(ctlsock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))) + warn("setsockopt(,,SO_SNDTIMEO)"); + tv.tv_sec = timeout_sec; + tv.tv_usec = 0L; + if (setsockopt(ctlsock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) + warn("setsockopt(,,SO_RCVTIMEO)"); + + switch (result->action) { + case SESSION_BRIEF: + case SESSION_PKTS: + case SESSION_ALL: + show_clear_session(result, stdout); + break; + case CLEAR_SESSION: + if (!result->has_ppp_id) + show_clear_session(result, stdout); + else { + u_int ids[1]; + ids[0] = result->ppp_id; + clear_session(ids, 1, 1, stdout); + } + break; + case NONE: + break; + } + + exit(EXIT_SUCCESS); +} + +static void +on_exit(void) +{ + if (ctlsock >= 0) + close(ctlsock); + unlink(ctlsockpath); +} + +static void +show_clear_session(struct parse_result *result, FILE *out) +{ + int i, n, sz, ppp_id_idx; + struct npppd_ctl_who_request req = { .cmd = NPPPD_CTL_CMD_WHO }; + struct npppd_ctl_who_response *res; + u_char buf[NPPPD_CTL_MSGSZ + BUFSIZ]; + u_int ppp_id[NMAX_DISCONNECT]; + + res = (struct npppd_ctl_who_response *)buf; + if (send(ctlsock, &req, sizeof(req), 0) < 0) + err(1, "send"); + res->count = 0; + n = 0; + ppp_id_idx = 0; + do { + if ((sz = recv(ctlsock, &buf, sizeof(buf), MSG_WAITALL)) < 0) { + if (errno == EAGAIN) { + warn("recv"); + break; + } + err(EXIT_FAILURE, "recv"); + } + for (i = 0; n < res->count && + offsetof(struct npppd_ctl_who_response, entry[i + 1]) <= sz; + i++, n++) + switch (result->action) { + case SESSION_BRIEF: + fprint_who_brief(n, &res->entry[i], out); + break; + case SESSION_PKTS: + fprint_who_packets(n, &res->entry[i], out); + break; + case SESSION_ALL: + if (filter_match(result, &res->entry[i])) + fprint_who_all(n, &res->entry[i], out); + break; + case CLEAR_SESSION: + if (filter_match(result, &res->entry[i])) { + if (ppp_id_idx < nitems(ppp_id)) + ppp_id[ppp_id_idx] = + res->entry[i].ppp_id; + ppp_id_idx++; + } + break; + default: + warnx("must not reached here"); + abort(); + } + } while (n < res->count); + if (n > 0 && n < res->count) + warnx("There are %d sessions in total, but we received only %d " + "sessions. Receive buffer size may not be enough, use -r " + "option to increase the size.", res->count, n); + if (result->action == CLEAR_SESSION) + clear_session(ppp_id, MIN(ppp_id_idx, nitems(ppp_id)), + ppp_id_idx, out); +} + +static void +fprint_who_brief(int i, struct npppd_who *w, FILE *out) +{ + char buf[BUFSIZ]; + + if (i == 0) + fputs( +"Ppp Id Assigned IPv4 Username Proto Tunnel From\n" +"---------- --------------- -------------------- ----- ------------------------" +"-\n", + out); + fprintf(out, "%10u %-15s %-20s %-5s %s\n", w->ppp_id, + inet_ntoa(w->framed_ip_address), w->username, w->tunnel_proto, + peerstr((struct sockaddr *)&w->tunnel_peer, buf, sizeof(buf))); +} + +static void +fprint_who_packets(int i, struct npppd_who *w, FILE *out) +{ + if (i == 0) + fputs( +"Ppd Id Username In(Kbytes/pkts/errs) Out(Kbytes/pkts/errs)" +"\n" +"---------- -------------------- ----------------------- ----------------------" +"-\n", + out); + fprintf(out, "%10u %-20s %9.1f %7u %5u %9.1f %7u %5u\n", w->ppp_id, + w->username, + (double)w->ibytes/1024, w->ipackets, w->ierrors, + (double)w->obytes/1024, w->opackets, w->oerrors); +} + +static void +fprint_who_all(int i, struct npppd_who *w, FILE *out) +{ + struct tm tm; + char ibytes_buf[48], obytes_buf[48], peer_buf[48], + time_buf[48], dur_buf[48]; + + localtime_r(&w->time, &tm); + strftime(time_buf, sizeof(time_buf), "%Y/%m/%d %T", &tm); + if (i != 0) + fputs("\n", out); + + fprintf(out, + "Ppp Id = %u\n" + " Ppp Id : %u\n" + " Username : %s\n" + " Realm Name : %s\n" + " Concentrated Interface : %s\n" + " Assigned IPv4 Address : %s\n" + " Tunnel Protocol : %s\n" + " Tunnel From : %s\n" + " Start Time : %s\n" + " Elapsed Time : %lu sec %s\n" + " Input Bytes : %llu%s\n" + " Input Packets : %lu\n" + " Input Errors : %lu (%.1f%%)\n" + " Output Bytes : %llu%s\n" + " Output Packets : %lu\n" + " Output Errors : %lu (%.1f%%)\n", + w->ppp_id, w->ppp_id, w->username, w->rlmname, w->ifname, + inet_ntoa(w->framed_ip_address), w->tunnel_proto, + peerstr((struct sockaddr *)&w->tunnel_peer, peer_buf, + sizeof(peer_buf)), time_buf, + (unsigned long)w->duration_sec, + humanize_duration(w->duration_sec, dur_buf, sizeof(dur_buf)), + (unsigned long long)w->ibytes, + humanize_bytes((double)w->ibytes, ibytes_buf, sizeof(ibytes_buf)), + (unsigned long)w->ipackets, + (unsigned long)w->ierrors, + ((w->ipackets + w->ierrors) <= 0) + ? 0.0 : (100.0 * w->ierrors) / (w->ierrors + w->ipackets), + (unsigned long long)w->obytes, + humanize_bytes((double)w->obytes, obytes_buf, sizeof(obytes_buf)), + (unsigned long)w->opackets, + (unsigned long)w->oerrors, + ((w->opackets + w->oerrors) <= 0) + ? 0.0 : (100.0 * w->oerrors) / (w->oerrors + w->opackets)); +} + +/*********************************************************************** + * clear session + ***********************************************************************/ +static void +clear_session(u_int ppp_id[], int ppp_id_count, int total, FILE *out) +{ + int succ, fail, i, n, nmax; + struct npppd_ctl_disconnect_request req; + struct npppd_ctl_disconnect_response res; + struct iovec iov[2]; + struct msghdr msg; + + succ = fail = 0; + if (ppp_id_count > 0) { + nmax = (NPPPD_CTL_MSGSZ - + offsetof(struct npppd_ctl_disconnect_request, ppp_id[0])) / + sizeof(u_int); + for (i = 0; i < ppp_id_count; i += n) { + n = MIN(nmax, ppp_id_count - i); + req.cmd = NPPPD_CTL_CMD_DISCONNECT; + req.count = n; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = nitems(iov); + iov[0].iov_base = &req; + iov[0].iov_len = offsetof( + struct npppd_ctl_disconnect_request, ppp_id[0]); + iov[1].iov_base = &ppp_id[i]; + iov[1].iov_len = sizeof(u_int) * n; + if (sendmsg(ctlsock, &msg, 0) < 0) + err(EXIT_FAILURE, "sendmsg"); + if (recv(ctlsock, &res, sizeof(res), 0) < 0) + err(EXIT_FAILURE, "recv"); + succ += res.count; + } + fail = total - succ; + } + if (succ > 0) + fprintf(out, "Successfully disconnected %d session%s.\n", + succ, (succ > 1)? "s" : ""); + if (fail > 0) + fprintf(out, "Failed to disconnect %d session%s.\n", + fail, (fail > 1)? "s" : ""); + if (succ == 0 && fail == 0) + fprintf(out, "No session to disconnect.\n"); +} + +/*********************************************************************** + * common functions + ***********************************************************************/ +static bool +filter_match(struct parse_result *result, struct npppd_who *who) +{ + if (result->has_ppp_id && result->ppp_id != who->ppp_id) + return false; + + switch (result->address.ss_family) { + case AF_INET: + if (((struct sockaddr_in *)&result->address)->sin_addr. + s_addr != who->framed_ip_address.s_addr) + return false; + break; + case AF_INET6: + /* npppd doesn't support IPv6 yet */ + return false; + } + + if (result->interface != NULL && + strcmp(result->interface, who->ifname) != 0) + return false; + + if (result->protocol != PROTO_UNSPEC && + result->protocol != parse_protocol(who->tunnel_proto) ) + return false; + + if (result->realm != NULL && strcmp(result->realm, who->rlmname) != 0) + return false; + + if (result->username != NULL && + strcmp(result->username, who->username) != 0) + return false; + + return true; +} + +static const char * +peerstr(struct sockaddr *sa, char *buf, int lbuf) +{ + int niflags, hasserv; + char hoststr[NI_MAXHOST], servstr[NI_MAXSERV]; + + niflags = hasserv = 0; + if (nflag) + niflags |= NI_NUMERICHOST; + if (sa->sa_family == AF_INET || sa->sa_family ==AF_INET6) { + hasserv = 1; + niflags |= NI_NUMERICSERV; + } + + if (sa->sa_family == AF_LINK) + snprintf(hoststr, sizeof(hoststr), + "%02x:%02x:%02x:%02x:%02x:%02x", + LLADDR((struct sockaddr_dl *)sa)[0] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[1] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[2] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[3] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[4] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[5] & 0xff); + else + getnameinfo(sa, sa->sa_len, hoststr, sizeof(hoststr), servstr, + sizeof(servstr), niflags); + + strlcpy(buf, hoststr, lbuf); + if (hasserv) { + strlcat(buf, ":", lbuf); + strlcat(buf, servstr, lbuf); + } + + return buf; +} + +static const char * +humanize_duration(uint32_t sec, char *buf, int lbuf) +{ + char fbuf[128]; + int hour, min; + + hour = sec / (60 * 60); + min = sec / 60; + min %= 60; + + if (lbuf <= 0) + return buf; + buf[0] = '\0'; + if (hour || min) { + strlcat(buf, "(", lbuf); + if (hour) { + snprintf(fbuf, sizeof(fbuf), + "%d hour%s", hour, (hour > 1)? "s" : ""); + strlcat(buf, fbuf, lbuf); + } + if (hour && min) + strlcat(buf, " and ", lbuf); + if (min) { + snprintf(fbuf, sizeof(fbuf), + "%d minute%s", min, (min > 1)? "s" : ""); + strlcat(buf, fbuf, lbuf); + } + strlcat(buf, ")", lbuf); + } + + return buf; +} + +static const char * +humanize_bytes(double val, char *buf, int lbuf) +{ + if (lbuf <= 0) + return buf; + + if (val >= 1000 * 1024 * 1024) + snprintf(buf, lbuf, " (%.1f GB)", + (double)val / (1024 * 1024 * 1024)); + else if (val >= 1000 * 1024) + snprintf(buf, lbuf, " (%.1f MB)", (double)val / (1024 * 1024)); + else if (val >= 1000) + snprintf(buf, lbuf, " (%.1f KB)", (double)val / 1024); + else + buf[0] = '\0'; + + return buf; +} diff --git a/usr.sbin/npppctl/parser.c b/usr.sbin/npppctl/parser.c new file mode 100644 index 00000000000..263872d2e8a --- /dev/null +++ b/usr.sbin/npppctl/parser.c @@ -0,0 +1,319 @@ +/* $OpenBSD: parser.c,v 1.1 2012/01/18 03:13:04 yasuoka Exp $ */ + +/* This file is derived from OpenBSD:src/usr.sbin/ikectl/parser.c 1.9 */ +/* + * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "parser.h" + +enum token_type { + NOTOKEN, + ENDTOKEN, + KEYWORD, + PPP_ID, + ADDRESS, + INTERFACE, + PROTOCOL, + REALM, + USERNAME +}; + +struct token { + enum token_type type; + const char *keyword; + int value; + const struct token *next; +}; + +static struct parse_result res; + +static const struct token t_main[]; +static const struct token t_session[]; +static const struct token t_clear[]; +static const struct token t_filter[]; +static const struct token t_ppp_id[]; +static const struct token t_address[]; +static const struct token t_interface[]; +static const struct token t_protocol[]; +static const struct token t_realm[]; +static const struct token t_username[]; + +static const struct token t_main[] = { + { KEYWORD, "session", NONE, t_session }, + { KEYWORD, "clear", NONE, t_clear }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_session[] = { + { KEYWORD, "brief", SESSION_BRIEF, NULL }, + { KEYWORD, "packets", SESSION_PKTS, NULL }, + { KEYWORD, "all", SESSION_ALL, t_filter }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_clear[] = { + { KEYWORD, "all", CLEAR_SESSION, NULL }, + { KEYWORD, "ppp-id", CLEAR_SESSION, t_ppp_id }, + { KEYWORD, "address", CLEAR_SESSION, t_address }, + { KEYWORD, "interface", CLEAR_SESSION, t_interface }, + { KEYWORD, "protocol", CLEAR_SESSION, t_protocol }, + { KEYWORD, "realm", CLEAR_SESSION, t_realm }, + { KEYWORD, "username", CLEAR_SESSION, t_username }, + { ENDTOKEN, "", CLEAR_SESSION, NULL } +}; + +static const struct token t_filter[] = { + { NOTOKEN, "", NONE, NULL }, + { KEYWORD, "ppp-id", NONE, t_ppp_id }, + { KEYWORD, "address", NONE, t_address }, + { KEYWORD, "interface", NONE, t_interface }, + { KEYWORD, "protocol", NONE, t_protocol }, + { KEYWORD, "realm", NONE, t_realm }, + { KEYWORD, "username", NONE, t_username }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_ppp_id[] = { + { PPP_ID, "", NONE, t_filter }, + { ENDTOKEN, "", NONE, NULL } +}; +static const struct token t_address[] = { + { ADDRESS, "", NONE, t_filter }, + { ENDTOKEN, "", NONE, NULL } +}; +static const struct token t_interface[] = { + { INTERFACE, "", NONE, t_filter }, + { ENDTOKEN, "", NONE, NULL } +}; +static const struct token t_protocol[] = { + { PROTOCOL, "", NONE, t_filter }, + { ENDTOKEN, "", NONE, NULL } +}; +static const struct token t_realm[] = { + { REALM, "", NONE, t_filter }, + { ENDTOKEN, "", NONE, NULL } +}; +static const struct token t_username[] = { + { USERNAME, "", NONE, t_filter }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token *match_token(char *, const struct token []); +static void show_valid_args(const struct token []); + +struct parse_result * +parse(int argc, char *argv[]) +{ + const struct token *table = t_main; + const struct token *match; + + bzero(&res, sizeof(res)); + + while (argc >= 0) { + if ((match = match_token(argv[0], table)) == NULL) { + fprintf(stderr, "valid commands/args:\n"); + show_valid_args(table); + return (NULL); + } + + argc--; + argv++; + + if (match->type == NOTOKEN || match->next == NULL) + break; + + table = match->next; + } + + if (argc > 0) { + fprintf(stderr, "superfluous argument: %s\n", argv[0]); + return (NULL); + } + + return (&res); +} + +static const struct token * +match_token(char *word, const struct token table[]) +{ + u_int i, match = 0; + unsigned long int ulval; + const struct token *t = NULL; + char *ep; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + if (word == NULL || strlen(word) == 0) { + match++; + t = &table[i]; + } + break; + case KEYWORD: + + if (word != NULL && strncmp(word, table[i].keyword, + strlen(word)) == 0) { + match++; + t = &table[i]; + if (t->value) + res.action = t->value; + } + break; + case PPP_ID: + if (word == NULL) + break; + errno = 0; + ulval = strtoul(word, &ep, 10); + if (isdigit((unsigned char)*word) && *ep == '\0' && + !(errno == ERANGE && ulval == ULONG_MAX)) { + res.ppp_id = ulval; + res.has_ppp_id = 1; + match++; + t = &table[i]; + } + break; + case ADDRESS: + { + struct sockaddr_in sin4 = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in) + }; + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6) + }; + if (word == NULL) + break; + if (inet_pton(AF_INET, word, &sin4.sin_addr) == 1) + memcpy(&res.address, &sin4, sin4.sin_len); + else + if (inet_pton(AF_INET6, word, &sin6.sin6_addr) == 1) + memcpy(&res.address, &sin6, sin6.sin6_len); + else + break; + match++; + t = &table[i]; + } + break; + case INTERFACE: + if (word == NULL) + break; + res.interface = word; + match++; + t = &table[i]; + break; + case PROTOCOL: + if (word == NULL) + break; + if ((res.protocol = parse_protocol(word)) == + PROTO_UNSPEC) + break; + match++; + t = &table[i]; + break; + case REALM: + if (word == NULL) + break; + res.realm = word; + match++; + t = &table[i]; + break; + case USERNAME: + if (word == NULL) + break; + res.username = word; + match++; + t = &table[i]; + break; + case ENDTOKEN: + break; + } + } + + if (match != 1) { + if (word == NULL) + fprintf(stderr, "missing argument:\n"); + else if (match > 1) + fprintf(stderr, "ambiguous argument: %s\n", word); + else if (match < 1) + fprintf(stderr, "unknown argument: %s\n", word); + return (NULL); + } + + return (t); +} + +static void +show_valid_args(const struct token table[]) +{ + int i; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + fprintf(stderr, " <cr>\n"); + break; + case KEYWORD: + fprintf(stderr, " %s\n", table[i].keyword); + break; + case PPP_ID: + fprintf(stderr, " <ppp-id>\n"); + break; + case ADDRESS: + fprintf(stderr, " <address>\n"); + break; + case INTERFACE: + fprintf(stderr, " <interface>\n"); + break; + case PROTOCOL: + fprintf(stderr, " [ pppoe | l2tp | pptp | sstp ]\n"); + break; + case REALM: + fprintf(stderr, " <realm>\n"); + break; + case USERNAME: + fprintf(stderr, " <username>\n"); + break; + case ENDTOKEN: + break; + } + } +} + +enum protocol +parse_protocol(const char *str) +{ + return + (strcasecmp(str, "PPTP" ) == 0)? PPTP : + (strcasecmp(str, "L2TP" ) == 0)? L2TP : + (strcasecmp(str, "PPPoE") == 0)? PPPOE : + (strcasecmp(str, "SSTP" ) == 0)? SSTP : PROTO_UNSPEC; +} + diff --git a/usr.sbin/npppctl/parser.h b/usr.sbin/npppctl/parser.h new file mode 100644 index 00000000000..c3a1a7638ca --- /dev/null +++ b/usr.sbin/npppctl/parser.h @@ -0,0 +1,53 @@ +/* $OpenBSD: parser.h,v 1.1 2012/01/18 03:13:04 yasuoka Exp $ */ + +/* This file is derived from OpenBSD:src/usr.sbin/ikectl/parser.h 1.9 */ +/* + * Copyright (c) 2007, 2008 Reyk Floeter <reyk@vantronix.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _NPPPCTL_PARSER_H +#define _NPPPCTL_PARSER_H + +enum actions { + NONE, + SESSION_BRIEF, + SESSION_PKTS, + SESSION_ALL, + CLEAR_SESSION +}; + +enum protocol { + PROTO_UNSPEC = 0, + PPTP, + L2TP, + PPPOE, + SSTP +}; + +struct parse_result { + enum actions action; + u_int ppp_id; + int has_ppp_id; + struct sockaddr_storage address; + const char *interface; + enum protocol protocol; + const char *realm; + const char *username; +}; + +struct parse_result *parse(int, char *[]); +enum protocol parse_protocol(const char *); + +#endif /* _PPPCTL_PARSER_H */ |