/*- * Copyright (c) 2009 Internet Initiative Japan Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 2005 * Internet Initiative Japan Inc. All rights reserved. */ /**@file * vdipwho との互換性を保ちつつ併せて次の機能を提供します。 * */ /* $Id: npppdctl.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debugutil.h" #include "npppd_local.h" #include "npppd_ctl.h" #ifndef MIN #define MIN(m,n) ((m) < (n))? (m) : (n) #endif #define DEFAULT_TIMEOUT 5 /** UNIX ドメインデータグラムの待ち受け用ファイル。*/ char dgramsock[] = "/tmp/npppdctl.XXXXXX"; /** npppd 側のソケットアドレス */ struct sockaddr_un peersock; /** ソケット */ int sock = -1; /** UNIXタイム表示 (vdipwho由来) */ int uflag = 0; /** 名前を引かない (vdipwho由来) */ int nflag = 0; /** 受信バッファサイズ */ int rcvbuf_sz = DEFAULT_NPPPD_CTL_MAX_MSGSZ; /** 長い行。80桁抑制しない */ int lflag = 0; static void usage (void); static void on_exit (void); static void npppd_who (int); static void npppd_disconnect (const char *); static const char *eat_null (const char *); static void npppd_ctl_termid_authen(const char *, const char *); static void npppd_ctl_common(int); static void print_who(struct npppd_who *); static void print_stat(struct npppd_who *); static const char *progname = NULL; /** 使い方を表示します。 */ static void usage(void) { fprintf(stderr, "usage: %s [-slnuh] [-d ppp_user] [-r rcvbuf_sz] [-p npppd_ctl_path]\n" " %s -c [-r rcvbuf_sz] {ppp_id | ip} auth_id\n" "usage: %s [-r]\n" "\t-R: Reset the routing table.\n" "\t-c: Set the client auth's auth-id.\n" "\t-d: Disconnect specified user.\n" "\t-h: Show this usage.\n" "\t-l: Use long line to display information.\n" "\t-n: Don't convert addresses/ports to names.\n" "\t-p: Specify the path to the npppd's control socket.\n" "\t-r: Receive buffer size (default %d).\n" "\t-s: Show statistics informations instead of who.\n" "\t-u: Show 'since' field as unix time.\n", progname, progname, progname, rcvbuf_sz); } static void on_signal(int notused) { exit(1); } /** nppdctl エントリポイント */ int main(int argc, char *argv[]) { int ch, sflag, fdgramsock, cflag, rtflag; const char *path = DEFAULT_NPPPD_CTL_SOCK_PATH; const char *disconn; struct sockaddr_un sun; struct timeval tv; extern int optind; progname = basename(argv[0]); disconn = NULL; sflag = cflag = rtflag = 0; while ((ch = getopt(argc, argv, "lcd:sunhp:r:R")) != -1) { switch (ch) { case 'n': nflag = 1; break; case 'd': disconn = optarg; break; case 's': sflag = 1; break; case 'u': uflag = 1; break; case 'c': cflag = 1; break; case 'l': lflag = 1; break; case 'r': if (sscanf(optarg, "%d", &rcvbuf_sz) != 1) { fprintf(stderr, "Invalid argument: %s\n", optarg); exit(1); } break; case 'p': path = optarg; break; case 'R': rtflag = 1; break; case 'h': case '?': default: usage(); exit(1); } } argc -= optind; argv += optind; // UNIX ドメインデータグラムの待ち受け用ファイル。 if ((fdgramsock = mkstemp(dgramsock)) < 0) err(1, "mkstemp"); // 終了時に削除するフック if (atexit(on_exit) != 0) err(1, "atexit"); signal(SIGINT, on_signal); signal(SIGHUP, on_signal); signal(SIGTERM, on_signal); if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) err(1, "socket"); tv.tv_sec = DEFAULT_TIMEOUT; tv.tv_usec = 0; if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf_sz, sizeof(rcvbuf_sz)) < 0) err(1, "setsockopt(SO_RCVBUF)"); if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) err(1, "setsockopt(SO_RCVTIMEO)"); // 待ち受けソケット準備 memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; sun.sun_len = sizeof(sun); strlcpy(sun.sun_path, dgramsock, sizeof(sun.sun_path)); // mkstemp でファイルを作ってしまっているので、削除 close(fdgramsock); unlink(dgramsock); if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) != 0) err(1, "bind()"); if (chmod(dgramsock, 0600) != 0) err(1, "chmod(%s,%d)", dgramsock, 0600); // npppd に接続するソケット memset(&peersock, 0, sizeof(peersock)); peersock.sun_family = AF_UNIX; peersock.sun_len = sizeof(peersock); strlcpy(peersock.sun_path, path, sizeof(peersock.sun_path)); if (disconn != NULL) npppd_disconnect(disconn); else if (sflag > 0) npppd_who(1); else if (rtflag) npppd_ctl_common(NPPPD_CTL_CMD_RESET_ROUTING_TABLE); else if (cflag > 0) { if (argc < 2) { usage(); exit(1); } npppd_ctl_termid_authen(argv[0], argv[1]); } else npppd_who(0); close(sock); sock = -1; return 0; } /** 終了処理。待ち受けソケットを消します。*/ static void on_exit(void) { if (sock >= 0) close(sock); unlink(dgramsock); } /** * vdipwho と同じ出力をします。 *

* 出力例

name             assigned         since         proto  from
yasuoka3         10.0.0.116       Aug 02 21:10  L2TP   192.168.159.103:1701

*/ static void npppd_who(int show_stat) { int i, n, sz, command; struct npppd_who_list *l; if ((l = malloc(rcvbuf_sz)) == NULL) err(1, "malloc(%d)", rcvbuf_sz); command = NPPPD_CTL_CMD_WHO; if (sendto(sock, &command, sizeof(command), 0, (struct sockaddr *)&peersock, sizeof(peersock)) < 0) err(1 ,"sendto() failed"); if (!show_stat) printf("name assigned since proto " "from\n"); else printf("id name in(Kbytes/pkts/errs)" " out(Kbytes/pkts/errs)\n"); n = 0; l->count = -1; do { if ((sz = recv(sock, l, rcvbuf_sz, 0)) <= 0) break; for (i = 0; n < l->count && offsetof(struct npppd_who_list, entry[i + 1]) <= sz; i++, n++) { if (show_stat) print_stat(&l->entry[i]); else print_who(&l->entry[i]); } } while (i < l->count); if (l->count >= 0) { if (n < l->count) warn("Warning: received message is truncated. " "Received %d user informations, but %d users are " "active.", n, l->count); } else { warn("recv"); } free(l); } static void print_who(struct npppd_who *w) { int niflags, hasserv; char assign_ip4buf[16]; char timestr[64], hoststr[NI_MAXHOST], servstr[NI_MAXSERV]; struct sockaddr *sa; if(nflag) niflags = NI_NUMERICHOST; else niflags = 0; strlcpy(assign_ip4buf, inet_ntoa(w->assign_ip4), sizeof(assign_ip4buf)); if(!uflag) strftime(timestr, sizeof(timestr), "%b %d %H:%M", localtime(&w->time)); else snprintf(timestr, sizeof(timestr), "%ld", (long)w->time); sa = (struct sockaddr *)&w->phy_info; hasserv = (sa->sa_family == AF_INET || sa->sa_family ==AF_INET6)? 1 : 0; 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); } if (sa->sa_family < AF_MAX) { getnameinfo((const struct sockaddr *)&w->phy_info, sa->sa_len, hoststr, sizeof(hoststr), servstr, sizeof(servstr), niflags | ((hasserv)? NI_NUMERICSERV :0)); } else if (sa->sa_family == NPPPD_AF_PHONE_NUMBER) { strlcpy(hoststr, ((npppd_phone_number *)sa)->pn_number, sizeof(hoststr)); } else { strlcpy(hoststr, "error", sizeof(hoststr)); } if (hasserv) printf((lflag) ? "%-15s %-15s %-12s %-6s %s:%s\n" : "%-15.15s %-15s %-12s %-6s %s:%s\n", eat_null(w->name), assign_ip4buf, timestr, w->phy_label, hoststr, servstr); else printf((lflag) ? "%-15s %-15s %-12s %-6s %s\n" : "%-15.15s %-15s %-12s %-6s %s\n", eat_null(w->name), assign_ip4buf, timestr, w->phy_label, hoststr); } static void print_stat(struct npppd_who *w) { printf((lflag) ? "%7d %-20s %9.1f %7u %5u %9.1f %7u %5u\n" : "%7d %-20.20s %9.1f %7u %5u %9.1f %7u %5u\n", w->id, eat_null(w->name), (double)w->ibytes/1024, w->ipackets, w->ierrors, (double)w->obytes/1024, w->opackets, w->oerrors); } /** ユーザ名で切断 */ static void npppd_disconnect(const char *username) { int sz, len; u_char buf[BUFSIZ]; struct npppd_disconnect_user_req *req; req = (struct npppd_disconnect_user_req *)buf; req->command = NPPPD_CTL_CMD_DISCONNECT_USER; len = sizeof(struct npppd_disconnect_user_req); len += strlcpy(req->username, username, sizeof(req->username)); if (sendto(sock, buf, sizeof(struct npppd_disconnect_user_req), 0, (struct sockaddr *)&peersock, sizeof(peersock)) < 0) err(1, "sendto"); if ((sz = recv(sock, buf, sizeof(buf), 0)) <= 0) err(1, "recv"); buf[sz] = '\0'; printf("%s\n", buf); } /** * str に指定した文字列が、NULLポインタまたは空文字列の場合 "" に変更し * て返します。それ以外の場合は、指定した文字列をそのまま返します。 */ static const char * eat_null(const char *str) { if (str == NULL || *str == '\0') return ""; return str; } static void npppd_ctl_termid_authen(const char *ppp_key, const char *authid) { int sz; char *ep; long lval; struct npppd_ctl_termid_set_auth_request req = { .command = NPPPD_CTL_CMD_TERMID_SET_AUTH, .reserved = 0, }; u_char buf[BUFSIZ]; struct in_addr ip4; if (inet_pton(AF_INET, ppp_key, &ip4) == 1) { req.ppp_key_type = NPPPD_CTL_PPP_FRAMED_IP_ADDRESS; req.ppp_key.framed_ip_address.s_addr = ip4.s_addr; } else { errno = 0; lval = strtol(ppp_key, &ep, 10); if (ppp_key[0] == '\0' || *ep != '\0') { fprintf(stderr, "not a number: %s\n", ppp_key); exit(1); } if ((errno == ERANGE && (lval == LONG_MAX|| lval == LONG_MIN))|| lval > UINT_MAX) { fprintf(stderr, "out of range: %s\n", ppp_key); exit(1); } req.ppp_key_type = NPPPD_CTL_PPP_ID; req.ppp_key.id = lval; } strlcpy(req.authid, authid, sizeof(req.authid)); if (sendto(sock, &req, sizeof(req), 0, (struct sockaddr *)&peersock, sizeof(peersock)) < 0) { err(1 ,"sendto() failed"); } if ((sz = recv(sock, buf, sizeof(buf), 0)) <= 0) err(1, "recv"); buf[sz] = '\0'; printf("%s\n", buf); } static void npppd_ctl_common(int command) { int sz; u_char buf[BUFSIZ]; if (sendto(sock, &command, sizeof(command), 0, (struct sockaddr *)&peersock, sizeof(peersock)) < 0) { err(1 ,"sendto() failed"); } if ((sz = recv(sock, buf, sizeof(buf), 0)) <= 0) err(1, "recv"); buf[sz] = '\0'; printf("%s\n", buf); }