GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/npppctl/npppctl.c Lines: 0 235 0.0 %
Date: 2017-11-07 Branches: 0 165 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: npppctl.c,v 1.8 2017/08/11 16:25:59 goda Exp $	*/
2
3
/*
4
 * Copyright (c) 2012 Internet Initiative Japan Inc.
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
#include <sys/queue.h>
19
#include <sys/socket.h>
20
#include <sys/un.h>
21
#include <sys/uio.h>
22
#include <net/if.h>
23
#include <net/if_dl.h>
24
#include <netinet/in.h>
25
#include <arpa/inet.h>
26
27
#include <errno.h>
28
#include <netdb.h>
29
#include <stdbool.h>
30
#include <stddef.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <imsg.h>
35
36
#include <unistd.h>
37
#include <err.h>
38
39
#include "parser.h"
40
#include "npppd_ctl.h"
41
42
#define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
43
44
#ifndef nitems
45
#define nitems(_x)	(sizeof(_x) / sizeof(_x[0]))
46
#endif
47
48
#define	NMAX_DISCONNECT		2048
49
50
static void         usage (void);
51
static void         show_clear_session (struct parse_result *, FILE *);
52
static void         monitor_session (struct parse_result *, FILE *);
53
static void         clear_session (u_int[], int, int, FILE *);
54
static void         fprint_who_brief (int, struct npppd_who *, FILE *);
55
static void         fprint_who_packets (int, struct npppd_who *, FILE *);
56
static void         fprint_who_all (int, struct npppd_who *, FILE *);
57
static const char  *peerstr (struct sockaddr *, char *, int);
58
static const char  *humanize_duration (uint32_t, char *, int);
59
static const char  *humanize_bytes (double, char *, int);
60
static bool         filter_match(struct parse_result *, struct npppd_who *);
61
static int          imsg_wait_command_completion (void);
62
63
static int             nflag = 0;
64
static struct imsgbuf  ctl_ibuf;
65
static struct imsg     ctl_imsg;
66
67
static void
68
usage(void)
69
{
70
	extern char		*__progname;
71
72
	fprintf(stderr,
73
	    "usage: %s [-n] [-s socket] command [arg ...]\n", __progname);
74
}
75
76
int
77
main(int argc, char *argv[])
78
{
79
	int			 ch, ctlsock = -1;
80
	struct parse_result	*result;
81
	struct sockaddr_un	 sun;
82
	const char		*npppd_ctlpath = NPPPD_SOCKET;
83
	extern int		 optind;
84
	extern char		*optarg;
85
86
	while ((ch = getopt(argc, argv, "ns:")) != -1)
87
		switch (ch) {
88
		case 'n':
89
			nflag = 1;
90
			break;
91
		case 's':
92
			npppd_ctlpath = optarg;
93
			break;
94
		default:
95
			usage();
96
			exit(EXIT_FAILURE);
97
		}
98
99
	argc -= optind;
100
	argv += optind;
101
102
	if ((result = parse(argc, argv)) == NULL)
103
		exit(EXIT_FAILURE);
104
105
	if ((ctlsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
106
		err(EXIT_FAILURE, "socket");
107
	memset(&sun, 0, sizeof(sun));
108
	sun.sun_family = AF_UNIX;
109
	strlcpy(sun.sun_path, npppd_ctlpath, sizeof(sun.sun_path));
110
	if (connect(ctlsock, (struct sockaddr *)&sun, sizeof(sun)) < 0)
111
		err(EXIT_FAILURE, "connect");
112
113
	imsg_init(&ctl_ibuf, ctlsock);
114
115
	switch (result->action) {
116
	case SESSION_BRIEF:
117
	case SESSION_PKTS:
118
	case SESSION_ALL:
119
		show_clear_session(result, stdout);
120
		break;
121
	case CLEAR_SESSION:
122
		if (!result->has_ppp_id)
123
			show_clear_session(result, stdout);
124
		else {
125
			u_int ids[1];
126
			ids[0] = result->ppp_id;
127
			clear_session(ids, 1, 1, stdout);
128
		}
129
		break;
130
	case MONITOR_SESSION:
131
		monitor_session(result, stdout);
132
		break;
133
	case NONE:
134
		break;
135
	}
136
137
	exit(EXIT_SUCCESS);
138
}
139
140
static void
141
show_clear_session(struct parse_result *result, FILE *out)
142
{
143
	int                    i, n, ppp_id_idx;
144
	struct npppd_who_list *res;
145
	u_int                  ppp_id[NMAX_DISCONNECT];
146
147
	if (imsg_compose(&ctl_ibuf, IMSG_CTL_WHO, 0, 0, -1, NULL, 0) == -1)
148
		err(EXIT_FAILURE, "failed to componse a message\n");
149
	if (imsg_wait_command_completion() < 0)
150
		errx(EXIT_FAILURE, "failed to get response");
151
	if (ctl_imsg.hdr.type != IMSG_CTL_OK)
152
		errx(EXIT_FAILURE, "command was fail");
153
	n = ppp_id_idx = 0;
154
	while (imsg_wait_command_completion() == IMSG_PPP_START) {
155
		res = (struct npppd_who_list *)ctl_imsg.data;
156
		if (ctl_imsg.hdr.len - IMSG_HEADER_SIZE <
157
		    offsetof(struct npppd_who_list,
158
			    entry[res->entry_count])) {
159
			errx(1, "response size %d is too small for "
160
			    "the entry count %d",
161
			    (int)(ctl_imsg.hdr.len - IMSG_HEADER_SIZE),
162
			    res->entry_count);
163
		}
164
		for (i = 0; i < res->entry_count; i++, n++) {
165
			switch (result->action) {
166
			case SESSION_BRIEF:
167
				fprint_who_brief(n, &res->entry[i], out);
168
				break;
169
			case SESSION_PKTS:
170
				fprint_who_packets(n, &res->entry[i], out);
171
				break;
172
			case SESSION_ALL:
173
				if (filter_match(result, &res->entry[i]))
174
					fprint_who_all(n, &res->entry[i], out);
175
				break;
176
			case CLEAR_SESSION:
177
				if (filter_match(result, &res->entry[i])) {
178
					if (ppp_id_idx < nitems(ppp_id))
179
						ppp_id[ppp_id_idx] =
180
						    res->entry[i].ppp_id;
181
					ppp_id_idx++;
182
				}
183
				break;
184
			default:
185
				warnx("must not reached here");
186
				abort();
187
			}
188
		}
189
		if (!res->more_data)
190
			break;
191
	}
192
	if (result->action == CLEAR_SESSION) {
193
		if (ppp_id_idx > nitems(ppp_id))
194
			warnx(
195
			    "Disconnection for %d sessions has been requested, "
196
			    "but cannot disconnect only %d sessions because of "
197
			    "the implementation limit.",
198
			    ppp_id_idx, (int)nitems(ppp_id));
199
		clear_session(ppp_id, MINIMUM(ppp_id_idx, nitems(ppp_id)),
200
			ppp_id_idx, out);
201
	}
202
}
203
204
const char *bar =
205
"------------------------------------------------------------------------\n";
206
static void
207
monitor_session(struct parse_result *result, FILE *out)
208
{
209
	int                    i, n;
210
	struct npppd_who_list *res;
211
212
	if (imsg_compose(&ctl_ibuf, IMSG_CTL_MONITOR, 0, 0, -1, NULL, 0) == -1)
213
		err(EXIT_FAILURE, "failed to compose a message");
214
	if (imsg_wait_command_completion() < 0)
215
		errx(EXIT_FAILURE, "failed to get response");
216
	if (ctl_imsg.hdr.type != IMSG_CTL_OK)
217
		errx(EXIT_FAILURE, "command was fail");
218
	do {
219
		if (imsg_wait_command_completion() < 0)
220
			break;
221
		n = 0;
222
		if (ctl_imsg.hdr.type == IMSG_PPP_START ||
223
		    ctl_imsg.hdr.type == IMSG_PPP_STOP) {
224
			res = (struct npppd_who_list *)ctl_imsg.data;
225
			for (i = 0; i < res->entry_count; i++) {
226
				if (!filter_match(result, &res->entry[i]))
227
					continue;
228
				if (n == 0)
229
					fprintf(out, "PPP %s\n%s",
230
					    (ctl_imsg.hdr.type ==
231
						    IMSG_PPP_START)
232
						    ? "Started"
233
						    : "Stopped", bar);
234
				fprint_who_all(n++, &res->entry[i], out);
235
			}
236
			if (n > 0)
237
				fputs(bar, out);
238
		} else {
239
			warnx("received unknown message type = %d",
240
			    ctl_imsg.hdr.type);
241
			break;
242
		}
243
	} while (true);
244
245
	return;
246
}
247
248
static void
249
fprint_who_brief(int i, struct npppd_who *w, FILE *out)
250
{
251
	char buf[BUFSIZ];
252
253
	if (i == 0)
254
		fputs(
255
"Ppp Id     Assigned IPv4   Username             Proto Tunnel From\n"
256
"---------- --------------- -------------------- ----- ------------------------"
257
"-\n",
258
		    out);
259
	fprintf(out, "%10u %-15s %-20s %-5s %s\n", w->ppp_id,
260
	    inet_ntoa(w->framed_ip_address), w->username, w->tunnel_proto,
261
	    peerstr((struct sockaddr *)&w->tunnel_peer, buf, sizeof(buf)));
262
}
263
264
static void
265
fprint_who_packets(int i, struct npppd_who *w, FILE *out)
266
{
267
	if (i == 0)
268
		fputs(
269
"Ppd Id     Username             In(Kbytes/pkts/errs)    Out(Kbytes/pkts/errs)"
270
"\n"
271
"---------- -------------------- ----------------------- ----------------------"
272
"-\n",
273
		    out);
274
	fprintf(out, "%10u %-20s %9.1f %7u %5u %9.1f %7u %5u\n", w->ppp_id,
275
	    w->username,
276
	    (double)w->ibytes/1024, w->ipackets, w->ierrors,
277
	    (double)w->obytes/1024, w->opackets, w->oerrors);
278
}
279
280
static void
281
fprint_who_all(int i, struct npppd_who *w, FILE *out)
282
{
283
	struct tm  tm;
284
	char       ibytes_buf[48], obytes_buf[48], peer_buf[48], time_buf[48];
285
	char       dur_buf[48];
286
287
	localtime_r(&w->time, &tm);
288
	strftime(time_buf, sizeof(time_buf), "%Y/%m/%d %T", &tm);
289
	if (i != 0)
290
		fputs("\n", out);
291
292
	fprintf(out,
293
	    "Ppp Id = %u\n"
294
	    "          Ppp Id                  : %u\n"
295
	    "          Username                : %s\n"
296
	    "          Realm Name              : %s\n"
297
	    "          Concentrated Interface  : %s\n"
298
	    "          Assigned IPv4 Address   : %s\n"
299
	    "          MRU                     : %u\n"
300
	    "          Tunnel Protocol         : %s\n"
301
	    "          Tunnel From             : %s\n"
302
	    "          Start Time              : %s\n"
303
	    "          Elapsed Time            : %lu sec %s\n"
304
	    "          Input Bytes             : %llu%s\n"
305
	    "          Input Packets           : %lu\n"
306
	    "          Input Errors            : %lu (%.1f%%)\n"
307
	    "          Output Bytes            : %llu%s\n"
308
	    "          Output Packets          : %lu\n"
309
	    "          Output Errors           : %lu (%.1f%%)\n",
310
	    w->ppp_id, w->ppp_id, w->username, w->rlmname, w->ifname,
311
	    inet_ntoa(w->framed_ip_address), (u_int)w->mru, w->tunnel_proto,
312
	    peerstr((struct sockaddr *)&w->tunnel_peer, peer_buf,
313
		sizeof(peer_buf)), time_buf,
314
	    (unsigned long)w->duration_sec,
315
	    humanize_duration(w->duration_sec, dur_buf, sizeof(dur_buf)),
316
	    (unsigned long long)w->ibytes,
317
	    humanize_bytes((double)w->ibytes, ibytes_buf, sizeof(ibytes_buf)),
318
	    (unsigned long)w->ipackets,
319
	    (unsigned long)w->ierrors,
320
	    ((w->ipackets + w->ierrors) <= 0)
321
		? 0.0 : (100.0 * w->ierrors) / (w->ierrors + w->ipackets),
322
	    (unsigned long long)w->obytes,
323
	    humanize_bytes((double)w->obytes, obytes_buf, sizeof(obytes_buf)),
324
	    (unsigned long)w->opackets,
325
	    (unsigned long)w->oerrors,
326
	    ((w->opackets + w->oerrors) <= 0)
327
		? 0.0 : (100.0 * w->oerrors) / (w->oerrors + w->opackets));
328
}
329
330
/***********************************************************************
331
 * clear session
332
 ***********************************************************************/
333
static void
334
clear_session(u_int ppp_id[], int ppp_id_count, int total, FILE *out)
335
{
336
	int                               succ, fail, i, n, nmax;
337
	struct iovec                      iov[2];
338
	struct npppd_disconnect_request   req;
339
	struct npppd_disconnect_response *res;
340
341
	succ = fail = 0;
342
	if (ppp_id_count > 0) {
343
		nmax = (MAX_IMSGSIZE - IMSG_HEADER_SIZE -
344
		    offsetof(struct npppd_disconnect_request, ppp_id[0])) /
345
		    sizeof(u_int);
346
		for (i = 0; i < ppp_id_count; i += n) {
347
			n = MINIMUM(nmax, ppp_id_count - i);
348
			req.count = n;
349
			iov[0].iov_base = &req;
350
			iov[0].iov_len = offsetof(
351
			    struct npppd_disconnect_request, ppp_id[0]);
352
			iov[1].iov_base = &ppp_id[i];
353
			iov[1].iov_len = sizeof(u_int) * n;
354
355
			if (imsg_composev(&ctl_ibuf, IMSG_CTL_DISCONNECT, 0, 0,
356
			    -1, iov, 2) == -1)
357
				err(EXIT_FAILURE,
358
				    "Failed to compose a message");
359
			if (imsg_wait_command_completion() < 0)
360
				errx(EXIT_FAILURE, "failed to get response");
361
			if (ctl_imsg.hdr.type != IMSG_CTL_OK)
362
				errx(EXIT_FAILURE,
363
				    "Command was fail: msg type = %d",
364
				    ctl_imsg.hdr.type);
365
			if (ctl_imsg.hdr.len - IMSG_HEADER_SIZE <
366
			    sizeof(struct npppd_disconnect_response))
367
				err(EXIT_FAILURE, "response is corrupted");
368
			res = (struct npppd_disconnect_response *)ctl_imsg.data;
369
			succ += res->count;
370
		}
371
		fail = total - succ;
372
	}
373
	if (succ > 0)
374
		fprintf(out, "Successfully disconnected %d session%s.\n",
375
		    succ, (succ > 1)? "s" : "");
376
	if (fail > 0)
377
		fprintf(out, "Failed to disconnect %d session%s.\n",
378
		    fail, (fail > 1)? "s" : "");
379
	if (succ == 0 && fail == 0)
380
		fprintf(out, "No session to disconnect.\n");
381
}
382
383
/***********************************************************************
384
 * common functions
385
 ***********************************************************************/
386
static bool
387
filter_match(struct parse_result *result, struct npppd_who *who)
388
{
389
	if (result->has_ppp_id && result->ppp_id != who->ppp_id)
390
		return (false);
391
392
	switch (result->address.ss_family) {
393
	case AF_INET:
394
		if (((struct sockaddr_in *)&result->address)->sin_addr.
395
		    s_addr != who->framed_ip_address.s_addr)
396
			return (false);
397
		break;
398
	case AF_INET6:
399
		/* npppd doesn't support IPv6 yet */
400
		return (false);
401
	}
402
403
	if (result->interface != NULL &&
404
	    strcmp(result->interface, who->ifname) != 0)
405
		return (false);
406
407
	if (result->protocol != PROTO_UNSPEC &&
408
	    result->protocol != parse_protocol(who->tunnel_proto) )
409
		return (false);
410
411
	if (result->realm != NULL && strcmp(result->realm, who->rlmname) != 0)
412
		return (false);
413
414
	if (result->username != NULL &&
415
	    strcmp(result->username, who->username) != 0)
416
		return (false);
417
418
	return (true);
419
}
420
421
static const char *
422
peerstr(struct sockaddr *sa, char *buf, int lbuf)
423
{
424
	int   niflags, hasserv;
425
	char  hoststr[NI_MAXHOST], servstr[NI_MAXSERV];
426
427
	niflags = hasserv = 0;
428
	if (nflag)
429
		niflags |= NI_NUMERICHOST;
430
	if (sa->sa_family == AF_INET || sa->sa_family ==AF_INET6) {
431
		hasserv = 1;
432
		niflags |= NI_NUMERICSERV;
433
	}
434
435
	if (sa->sa_family == AF_LINK)
436
		snprintf(hoststr, sizeof(hoststr),
437
		    "%02x:%02x:%02x:%02x:%02x:%02x",
438
		    LLADDR((struct sockaddr_dl *)sa)[0] & 0xff,
439
		    LLADDR((struct sockaddr_dl *)sa)[1] & 0xff,
440
		    LLADDR((struct sockaddr_dl *)sa)[2] & 0xff,
441
		    LLADDR((struct sockaddr_dl *)sa)[3] & 0xff,
442
		    LLADDR((struct sockaddr_dl *)sa)[4] & 0xff,
443
		    LLADDR((struct sockaddr_dl *)sa)[5] & 0xff);
444
	else
445
		getnameinfo(sa, sa->sa_len, hoststr, sizeof(hoststr), servstr,
446
		    sizeof(servstr), niflags);
447
448
	strlcpy(buf, hoststr, lbuf);
449
	if (hasserv) {
450
		strlcat(buf, ":", lbuf);
451
		strlcat(buf, servstr, lbuf);
452
	}
453
454
	return (buf);
455
}
456
457
static const char *
458
humanize_duration(uint32_t sec, char *buf, int lbuf)
459
{
460
	char  fbuf[128];
461
	int   hour, min;
462
463
	hour = sec / (60 * 60);
464
	min = sec / 60;
465
	min %= 60;
466
467
	if (lbuf <= 0)
468
		return (buf);
469
	buf[0] = '\0';
470
	if (hour || min) {
471
		strlcat(buf, "(", lbuf);
472
		if (hour) {
473
			snprintf(fbuf, sizeof(fbuf),
474
			    "%d hour%s", hour, (hour > 1)? "s" : "");
475
			strlcat(buf, fbuf, lbuf);
476
		}
477
		if (hour && min)
478
			strlcat(buf, " and ", lbuf);
479
		if (min) {
480
			snprintf(fbuf, sizeof(fbuf),
481
			    "%d minute%s", min, (min > 1)? "s" : "");
482
			strlcat(buf, fbuf, lbuf);
483
		}
484
		strlcat(buf, ")", lbuf);
485
	}
486
487
	return (buf);
488
}
489
490
static const char *
491
humanize_bytes(double val, char *buf, int lbuf)
492
{
493
	if (lbuf <= 0)
494
		return (buf);
495
496
	if (val >= 1000 * 1024 * 1024)
497
		snprintf(buf, lbuf, " (%.1f GB)",
498
		    (double)val / (1024 * 1024 * 1024));
499
	else if (val >= 1000 * 1024)
500
		snprintf(buf, lbuf, " (%.1f MB)", (double)val / (1024 * 1024));
501
	else if (val >= 1000)
502
		snprintf(buf, lbuf, " (%.1f KB)", (double)val / 1024);
503
	else
504
		buf[0] = '\0';
505
506
	return (buf);
507
}
508
509
static int
510
imsg_wait_command_completion(void)
511
{
512
	int  n;
513
514
	while (ctl_ibuf.w.queued)
515
		if (msgbuf_write(&ctl_ibuf.w) <= 0 && errno != EAGAIN)
516
			return (-1);
517
	do {
518
		if ((n = imsg_get(&ctl_ibuf, &ctl_imsg)) == -1)
519
			return (-1);
520
		if (n != 0)
521
			break;
522
		if (((n = imsg_read(&ctl_ibuf)) == -1 && errno != EAGAIN) ||
523
		    n == 0)
524
			return (-1);
525
	} while (1);
526
527
	return (ctl_imsg.hdr.type);
528
}