xref: /netbsd-src/sbin/pppoectl/pppoectl.c (revision 9fb66d812c00ebfb445c0b47dea128f32aa6fe96)
1 /*	$NetBSD: pppoectl.c,v 1.26 2020/11/25 10:32:54 yamaguchi Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Joerg Wunsch
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * From: spppcontrol.c,v 1.3 1998/01/07 07:55:26 charnier Exp
29  * From: ispppcontrol
30  */
31 #include <sys/cdefs.h>
32 
33 #ifndef lint
34 __RCSID("$NetBSD: pppoectl.c,v 1.26 2020/11/25 10:32:54 yamaguchi Exp $");
35 #endif
36 
37 
38 #include <sys/param.h>
39 #include <sys/callout.h>
40 #include <sys/ioctl.h>
41 #include <sys/socket.h>
42 #include <sys/time.h>
43 #include <sys/sysctl.h>
44 #include <net/if.h>
45 #include <net/if_sppp.h>
46 #include <net/if_pppoe.h>
47 #include <arpa/inet.h>
48 #include <err.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <sysexits.h>
53 #include <unistd.h>
54 
55 __dead static void usage(void);
56 __dead static void print_error(const char *ifname, int error, const char * str);
57 static void print_vals(const char *ifname, int phase, struct spppauthcfg *sp,
58 	int lcp_timeout, time_t idle_timeout, int authfailures,
59 	int max_auth_failures, u_int maxalive, time_t max_noreceive);
60 static void print_dns(const char *ifname, int dns1, int dns2, int s, int tabs);
61 static void print_stats(const char *ifname, int s, int dump);
62 static const char *phase_name(int phase);
63 static const char *proto_name(int proto);
64 static const char *authflags(int flags);
65 static const char *pppoe_state_name(int state);
66 static const char *ppp_state_name(int state);
67 static void pppoectl_argument(char *arg);
68 
69 #define	ISSET(x, a)	((x) & (a))
70 #define PPPOECTL_IOCTL(_ifname, _s, _cmd, _st)	do {	\
71 	int __e;					\
72 	memset((_st), 0, sizeof(*(_st)));		\
73 	strncpy((_st)->ifname, (_ifname),		\
74 	    sizeof((_st)->ifname));			\
75 	__e = ioctl((_s), (_cmd), (_st));		\
76 	if (__e != 0)					\
77 		print_error((_ifname), __e, #_cmd);	\
78 } while (0)
79 
80 static int hz = 0;
81 
82 static int set_auth, set_lcp, set_idle_to, set_auth_failure, set_dns,
83     clear_auth_failure_count, set_keepalive;
84 static int maxalive = -1;
85 static int max_noreceive = -1;
86 static struct spppauthcfg spr;
87 static struct sppplcpcfg lcp;
88 static struct spppstatus status;
89 static struct spppidletimeout timeout;
90 static struct spppauthfailurestats authfailstats;
91 static struct spppauthfailuresettings authfailset;
92 static struct spppdnssettings dnssettings;
93 static struct spppkeepalivesettings keepalivesettings;
94 
95 int
96 main(int argc, char **argv)
97 {
98 	FILE *fp;
99 	int s, c;
100 	int errs = 0, verbose = 0, dump = 0, dns1 = 0, dns2 = 0;
101 	size_t len;
102 	const char *eth_if_name, *access_concentrator, *service;
103 	const char *ifname, *configname;
104 	char *line;
105 	int mib[2];
106 	struct clockinfo clockinfo;
107 	setprogname(argv[0]);
108 
109 	eth_if_name = NULL;
110 	access_concentrator = NULL;
111 	service = NULL;
112 	configname = NULL;
113 	while ((c = getopt(argc, argv, "vde:f:s:a:n:")) != -1)
114 		switch (c) {
115 		case 'v':
116 			verbose++;
117 			break;
118 
119 		case 'd':
120 			dump++;
121 			break;
122 
123 		case 'e':
124 			eth_if_name = optarg;
125 			break;
126 
127 		case 'f':
128 			configname = optarg;
129 			break;
130 
131 		case 's':
132 			service = optarg;
133 			break;
134 
135 		case 'a':
136 			access_concentrator = optarg;
137 			break;
138 
139 		case 'n':
140 			if (strcmp(optarg, "1") == 0)
141 				dns1 = 1;
142 			else if (strcmp(optarg, "2") == 0)
143 				dns2 = 1;
144 			else {
145 				fprintf(stderr, "bad argument \"%s\" to -n (only 1 or two allowed)\n",
146 					optarg);
147 				errs++;
148 			}
149 			break;
150 
151 		default:
152 			errs++;
153 			break;
154 		}
155 	argv += optind;
156 	argc -= optind;
157 
158 	if (errs || argc < 1)
159 		usage();
160 
161 	ifname = argv[0];
162 
163 	/* use a random AF to create the socket */
164 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
165 		err(EX_UNAVAILABLE, "ifconfig: socket");
166 
167 	argc--;
168 	argv++;
169 
170 	if (eth_if_name) {
171 		struct pppoediscparms parms;
172 		int e;
173 
174 		memset(&parms, 0, sizeof parms);
175 		strncpy(parms.ifname, ifname, sizeof(parms.ifname));
176 		strncpy(parms.eth_ifname, eth_if_name, sizeof(parms.eth_ifname));
177 		if (access_concentrator) {
178 			parms.ac_name = access_concentrator;
179 			parms.ac_name_len = strlen(access_concentrator);
180 		}
181 		if (service) {
182 			parms.service_name = service;
183 			parms.service_name_len = strlen(service);
184 		}
185 
186 		e = ioctl(s, PPPOESETPARMS, &parms);
187 		if (e)
188 			print_error(ifname, e, "PPPOESETPARMS");
189 		return 0;
190 	}
191 
192 	if (dns1 || dns2) {
193 		print_dns(ifname, dns1, dns2, s, 0);
194 	}
195 
196 	if (dump) {
197 		print_stats(ifname, s, dump);
198 		return 0;
199 	}
200 
201 	memset(&spr, 0, sizeof spr);
202 	strncpy(spr.ifname, ifname, sizeof spr.ifname);
203 	memset(&lcp, 0, sizeof lcp);
204 	strncpy(lcp.ifname, ifname, sizeof lcp.ifname);
205 	memset(&status, 0, sizeof status);
206 	strncpy(status.ifname, ifname, sizeof status.ifname);
207 	memset(&timeout, 0, sizeof timeout);
208 	strncpy(timeout.ifname, ifname, sizeof timeout.ifname);
209 	memset(&authfailstats, 0, sizeof authfailstats);
210 	strncpy(authfailstats.ifname, ifname, sizeof authfailstats.ifname);
211 	memset(&authfailset, 0, sizeof authfailset);
212 	strncpy(authfailset.ifname, ifname, sizeof authfailset.ifname);
213 	memset(&dnssettings, 0, sizeof dnssettings);
214 	strncpy(dnssettings.ifname, ifname, sizeof dnssettings.ifname);
215 	memset(&keepalivesettings, 0, sizeof keepalivesettings);
216 	strncpy(keepalivesettings.ifname, ifname, sizeof keepalivesettings.ifname);
217 
218 	mib[0] = CTL_KERN;
219 	mib[1] = KERN_CLOCKRATE;
220 	len = sizeof(clockinfo);
221 	if(sysctl(mib, 2, &clockinfo, &len, NULL, 0) == -1)
222 	{
223 		fprintf(stderr, "error, cannot sysctl kern.clockrate!\n");
224 		exit(1);
225 	}
226 
227 	hz = clockinfo.hz;
228 
229 	if (argc == 0 && !(dns1||dns2) && !configname) {
230 		/* list only mode */
231 
232 		/* first pass, get name lengths */
233 		if (ioctl(s, SPPPGETAUTHCFG, &spr) == -1)
234 			err(EX_OSERR, "SPPPGETAUTHCFG");
235 		/* now allocate buffers for strings */
236 		if (spr.myname_length)
237 			if ((spr.myname = malloc(spr.myname_length)) == NULL)
238 				err(1, NULL);
239 		if (spr.hisname_length)
240 			if ((spr.hisname = malloc(spr.hisname_length)) == NULL)
241 				err(1, NULL);
242 		/* second pass: get names too */
243 		if (ioctl(s, SPPPGETAUTHCFG, &spr) == -1)
244 			err(EX_OSERR, "SPPPGETAUTHCFG");
245 
246 		if (ioctl(s, SPPPGETLCPCFG, &lcp) == -1)
247 			err(EX_OSERR, "SPPPGETLCPCFG");
248 		if (ioctl(s, SPPPGETSTATUS, &status) == -1)
249 			err(EX_OSERR, "SPPPGETSTATUS");
250 		if (ioctl(s, SPPPGETIDLETO, &timeout) == -1)
251 			err(EX_OSERR, "SPPPGETIDLETO");
252 		if (ioctl(s, SPPPGETAUTHFAILURES, &authfailstats) == -1)
253 			err(EX_OSERR, "SPPPGETAUTHFAILURES");
254 		if (ioctl(s, SPPPGETKEEPALIVE, &keepalivesettings) == -1)
255 			err(EX_OSERR, "SPPPGETKEEPALIVE");
256 
257 		print_vals(ifname, status.phase, &spr, lcp.lcp_timeout,
258 		    timeout.idle_seconds, authfailstats.auth_failures,
259 		    authfailstats.max_failures,
260 		    keepalivesettings.maxalive,
261 		    keepalivesettings.max_noreceive);
262 
263 		if (spr.hisname) free(spr.hisname);
264 		if (spr.myname) free(spr.myname);
265 		return 0;
266 	}
267 
268 	/* first load the config file, then parse command line args */
269 	if (configname && (fp = fopen(configname, "r")))
270 		while ((line = fparseln(fp, NULL, NULL, NULL,
271 		    FPARSELN_UNESCALL)) != NULL) {
272 			if (line[0] != '\0')
273 				pppoectl_argument(line);
274 			/*
275 			 * We do not free(line) here, because we
276 			 * still have references to parts of the
277 			 * string collected in the various ioctl
278 			 * argument structures (and need those).
279 			 * Yes, this is a memory leak.
280 			 * We could copy the partial strings instead,
281 			 * and free those later - but this is a one-shot
282 			 * program and memory will be freed at process
283 			 * exit time anyway.
284 			 */
285 		}
286 
287 
288 	while (argc > 0) {
289 		pppoectl_argument(argv[0]);
290 
291 		argv++;
292 		argc--;
293 	}
294 
295 	if (set_auth) {
296 		if (ioctl(s, SPPPSETAUTHCFG, &spr) == -1)
297 			err(EX_OSERR, "SPPPSETAUTHCFG");
298 	}
299 	if (set_lcp) {
300 		if (ioctl(s, SPPPSETLCPCFG, &lcp) == -1)
301 			err(EX_OSERR, "SPPPSETLCPCFG");
302 	}
303 	if (set_idle_to) {
304 		if (ioctl(s, SPPPSETIDLETO, &timeout) == -1)
305 			err(EX_OSERR, "SPPPSETIDLETO");
306 	}
307 	if (set_auth_failure) {
308 		if (ioctl(s, SPPPSETAUTHFAILURE, &authfailset) == -1)
309 			err(EX_OSERR, "SPPPSETAUTHFAILURE");
310 	}
311 	if (clear_auth_failure_count && !(set_auth || set_auth_failure)) {
312 		/*
313 		 * We want to clear the auth failure count, but did not
314 		 * do that implicitly by setting authentication - so
315 		 * do a zero-effect auth setting change
316 		 */
317 		if (ioctl(s, SPPPGETAUTHFAILURES, &authfailstats) == -1)
318 			err(EX_OSERR, "SPPPGETAUTHFAILURES");
319 		authfailset.max_failures = authfailstats.max_failures;
320 		if (ioctl(s, SPPPSETAUTHFAILURE, &authfailset) == -1)
321 			err(EX_OSERR, "SPPPSETAUTHFAILURE");
322 	}
323 	if (set_dns) {
324 		if (ioctl(s, SPPPSETDNSOPTS, &dnssettings) == -1)
325 			err(EX_OSERR, "SPPPSETDNSOPTS");
326 	}
327 	if (set_keepalive) {
328 		if (ioctl(s, SPPPGETKEEPALIVE, &keepalivesettings) == -1)
329 			err(EX_OSERR, "SPPPGETKEEPALIVE");
330 		if (max_noreceive >= 0)
331 			keepalivesettings.max_noreceive = max_noreceive;
332 		if (maxalive >= 0)
333 			keepalivesettings.maxalive = maxalive;
334 		if (ioctl(s, SPPPSETKEEPALIVE, &keepalivesettings) == -1)
335 			err(EX_OSERR, "SPPPSETKEEPALIVE");
336 	}
337 
338 	if (verbose) {
339 		if (ioctl(s, SPPPGETAUTHFAILURES, &authfailstats) == -1)
340 			err(EX_OSERR, "SPPPGETAUTHFAILURES");
341 		if (ioctl(s, SPPPGETKEEPALIVE, &keepalivesettings) == -1)
342 			err(EX_OSERR, "SPPPGETKEEPALIVE");
343 		print_vals(ifname, status.phase, &spr, lcp.lcp_timeout,
344 		    timeout.idle_seconds, authfailstats.auth_failures,
345 		    authfailstats.max_failures,
346 		    keepalivesettings.maxalive,
347 		    keepalivesettings.max_noreceive);
348 	}
349 
350 	return 0;
351 }
352 
353 static void
354 pppoectl_argument(char *arg)
355 {
356 	size_t off;
357 	const char *cp;
358 
359 #define startswith(a,s) strncmp(a, s, (off = strlen(s))) == 0
360 	if (startswith(arg, "authproto=")) {
361 		cp = arg + off;
362 		if (strcmp(cp, "pap") == 0)
363 			spr.myauth =
364 				spr.hisauth = SPPP_AUTHPROTO_PAP;
365 		else if (strcmp(cp, "chap") == 0)
366 			spr.myauth = spr.hisauth = SPPP_AUTHPROTO_CHAP;
367 		else if (strcmp(cp, "none") == 0)
368 			spr.myauth = spr.hisauth = SPPP_AUTHPROTO_NONE;
369 		else
370 			errx(EX_DATAERR, "bad auth proto: %s", cp);
371 		set_auth = 1;
372 	} else if (startswith(arg, "myauthproto=")) {
373 		cp = arg + off;
374 		if (strcmp(cp, "pap") == 0)
375 			spr.myauth = SPPP_AUTHPROTO_PAP;
376 		else if (strcmp(cp, "chap") == 0)
377 			spr.myauth = SPPP_AUTHPROTO_CHAP;
378 		else if (strcmp(cp, "none") == 0)
379 			spr.myauth = SPPP_AUTHPROTO_NONE;
380 		else
381 			errx(EX_DATAERR, "bad auth proto: %s", cp);
382 		set_auth = 1;
383 	} else if (startswith(arg, "myauthname=")) {
384 		spr.myname = arg + off;
385 		spr.myname_length = strlen(spr.myname)+1;
386 		set_auth = 1;
387 	} else if (startswith(arg, "myauthsecret=") || startswith(arg, "myauthkey=")) {
388 		spr.mysecret = arg + off;
389 		spr.mysecret_length = strlen(spr.mysecret)+1;
390 		set_auth = 1;
391 	} else if (startswith(arg, "hisauthproto=")) {
392 		cp = arg + off;
393 		if (strcmp(cp, "pap") == 0)
394 			spr.hisauth = SPPP_AUTHPROTO_PAP;
395 		else if (strcmp(cp, "chap") == 0)
396 			spr.hisauth = SPPP_AUTHPROTO_CHAP;
397 		else if (strcmp(cp, "none") == 0)
398 			spr.hisauth = SPPP_AUTHPROTO_NONE;
399 		else
400 			errx(EX_DATAERR, "bad auth proto: %s", cp);
401 		set_auth = 1;
402 	} else if (startswith(arg, "hisauthname=")) {
403 		spr.hisname = arg + off;
404 		spr.hisname_length = strlen(spr.hisname)+1;
405 		set_auth = 1;
406 	} else if (startswith(arg, "hisauthsecret=") || startswith(arg, "hisauthkey=")) {
407 		spr.hissecret = arg + off;
408 		spr.hissecret_length = strlen(spr.hissecret)+1;
409 		set_auth = 1;
410 	} else if (startswith(arg, "max-noreceive=")) {
411 		max_noreceive = atoi(arg+off);
412 		if (max_noreceive < 0) {
413 			fprintf(stderr,
414 			    "max-noreceive value must be at least 0\n");
415 			max_noreceive = -1;
416 		} else {
417 			set_keepalive = 1;
418 		}
419 	} else if (startswith(arg, "max-alive-missed=")) {
420 		maxalive = atoi(arg+off);
421 		if (maxalive < 0) {
422 			fprintf(stderr,
423 			    "max-alive-missed value must be at least 0\n");
424 			maxalive = -1;
425 		} else {
426 			set_keepalive = 1;
427 		}
428 	} else if (strcmp(arg, "callin") == 0)
429 		spr.hisauthflags |= SPPP_AUTHFLAG_NOCALLOUT;
430 	else if (strcmp(arg, "always") == 0)
431 		spr.hisauthflags &= ~SPPP_AUTHFLAG_NOCALLOUT;
432 	else if (strcmp(arg, "norechallenge") == 0)
433 		spr.hisauthflags |= SPPP_AUTHFLAG_NORECHALLENGE;
434 	else if (strcmp(arg, "rechallenge") == 0)
435 		spr.hisauthflags &= ~SPPP_AUTHFLAG_NORECHALLENGE;
436 #ifndef __NetBSD__
437 	else if (strcmp(arg, "enable-vj") == 0)
438 		spr.defs.enable_vj = 1;
439 	else if (strcmp(arg, "disable-vj") == 0)
440 		spr.defs.enable_vj = 0;
441 #endif
442 	else if (startswith(arg, "lcp-timeout=")) {
443 		int timeout_arg = atoi(arg+off);
444 		if ((timeout_arg > 20000) || (timeout_arg <= 0))
445 			errx(EX_DATAERR, "bad lcp timeout value: %s",
446 			     arg+off);
447 		lcp.lcp_timeout = timeout_arg * hz / 1000;
448 		set_lcp = 1;
449 	} else if (startswith(arg, "idle-timeout=")) {
450 		timeout.idle_seconds = (time_t)atol(arg+off);
451 		set_idle_to = 1;
452 	} else if (startswith(arg, "max-auth-failure=")) {
453 		authfailset.max_failures = atoi(arg+off);
454 		set_auth_failure = 1;
455 	} else if (strcmp(arg, "clear-auth-failure") == 0) {
456 		clear_auth_failure_count = 1;
457 	} else if (startswith(arg, "query-dns=")) {
458 		dnssettings.query_dns = atoi(arg+off);
459 		set_dns = 1;
460 	} else
461 		errx(EX_DATAERR, "bad parameter: \"%s\"", arg);
462 }
463 
464 static void
465 usage(void)
466 {
467 	const char * prog = getprogname();
468 	fprintf(stderr,
469 	    "usage:\n"
470 	    "       %s [-f config] ifname [...]\n"
471 	    "       %s [-v] ifname [{my|his}auth{proto|name|secret}=...] \\\n"
472             "                      [callin] [always] [{no}rechallenge]\n"
473             "                      [query-dns=3]\n"
474 	    "           to set authentication names, passwords\n"
475 	    "           and (optional) paramaters\n"
476 	    "       %s [-v] ifname lcp-timeout=ms|idle-timeout=s|\n"
477 	    "                      max-noreceive=s|max-alive-missed=cnt|\n"
478 	    "                      max-auth-failure=count|clear-auth-failure\n"
479 	    "           to set general parameters\n"
480 	    "   or\n"
481 	    "       %s -e ethernet-ifname ifname\n"
482 	    "           to connect an ethernet interface for PPPoE\n"
483 	    "       %s [-a access-concentrator-name] [-s service-name] ifname\n"
484 	    "           to specify (optional) data for PPPoE sessions\n"
485 	    "       %s -d ifname\n"
486 	    "           to dump the current PPPoE session state\n"
487 	    "       %s -n (1|2) ifname\n"
488 	    "           to print DNS addresses retrieved via query-dns\n"
489 	    , prog, prog, prog, prog, prog, prog, prog);
490 	exit(EX_USAGE);
491 }
492 
493 static void
494 print_vals(const char *ifname, int phase, struct spppauthcfg *sp, int lcp_timeout,
495 	time_t idle_timeout, int authfailures, int max_auth_failures,
496 	u_int maxalive_cnt, time_t max_noreceive_time)
497 {
498 #ifndef __NetBSD__
499 	time_t send, recv;
500 #endif
501 
502 	printf("%s:\tphase=%s\n", ifname, phase_name(phase));
503 	if (sp->myauth) {
504 		printf("\tmyauthproto=%s myauthname=\"%s\"\n",
505 		       proto_name(sp->myauth),
506 		       sp->myname);
507 	}
508 	if (sp->hisauth) {
509 		printf("\thisauthproto=%s hisauthname=\"%s\"%s\n",
510 		       proto_name(sp->hisauth),
511 		       sp->hisname,
512 		       authflags(sp->hisauthflags));
513 	}
514 #ifndef __NetBSD__
515 	if (sp->defs.pp_phase > PHASE_DEAD) {
516 		send = time(NULL) - sp->defs.pp_last_sent;
517 		recv = time(NULL) - sp->defs.pp_last_recv;
518 		printf("\tidle_time=%ld\n", (send<recv)? send : recv);
519 	}
520 #endif
521 
522 	printf("\tlcp timeout: %.3f s\n",
523 	       (double)lcp_timeout / hz);
524 
525 	if (idle_timeout != 0)
526 		printf("\tidle timeout = %lu s\n", (unsigned long)idle_timeout);
527 	else
528 		printf("\tidle timeout = disabled\n");
529 
530 	if (authfailures != 0)
531 		printf("\tauthentication failures = %d\n", authfailures);
532 	printf("\tmax-auth-failure = %d\n", max_auth_failures);
533 
534 	printf("\tmax-noreceive = %ld seconds\n", (long)max_noreceive_time);
535 	printf("\tmax-alive-missed = %u unanswered echo requests\n", maxalive_cnt);
536 
537 #ifndef __NetBSD__
538 	printf("\tenable_vj: %s\n",
539 	       sp->defs.enable_vj ? "on" : "off");
540 #endif
541 }
542 
543 static void
544 print_dns(const char *ifname, int dns1, int dns2, int s, int tabs)
545 {
546 	int i;
547 	struct spppdnsaddrs addrs;
548 
549 	if (!dns1 && !dns2)
550 		return;
551 
552 	PPPOECTL_IOCTL(ifname, s, SPPPGETDNSADDRS, &addrs);
553 	if (dns1) {
554 		for (i = 0; i < tabs; i++)
555 			printf("\t");
556 		if (tabs > 0)
557 			printf("primary dns address ");
558 		printf("%d.%d.%d.%d\n",
559 		       (addrs.dns[0] >> 24) & 0xff,
560 		       (addrs.dns[0] >> 16) & 0xff,
561 		       (addrs.dns[0] >> 8) & 0xff,
562 		       addrs.dns[0] & 0xff);
563 	}
564 	if (dns2) {
565 		for (i = 0; i < tabs; i++)
566 			printf("\t");
567 		if (tabs > 0)
568 			printf("secondary dns address ");
569 		printf("%d.%d.%d.%d\n",
570 		       (addrs.dns[1] >> 24) & 0xff,
571 		       (addrs.dns[1] >> 16) & 0xff,
572 		       (addrs.dns[1] >> 8) & 0xff,
573 		       addrs.dns[1] & 0xff);
574 	}
575 }
576 
577 static void
578 print_stats(const char *ifname, int s, int dump)
579 {
580 	struct pppoeconnectionstate state;
581 	struct sppplcpstatus lcpst;
582 	struct spppipcpstatus ipcpst;
583 	struct spppipv6cpstatus ipv6cpst;
584 	struct in_addr addr;
585 
586 	PPPOECTL_IOCTL(ifname, s, PPPOEGETSESSION, &state);
587 
588 	/* dump PPPoE session state */
589 	printf("%s:\t%s %s\n", ifname,
590 	    dump > 1 ? "PPPoE state:" : "state =",
591 	    pppoe_state_name(state.state));
592 	printf("\tSession ID: 0x%x\n", state.session_id);
593 	printf("\tPADI retries: %d\n", state.padi_retry_no);
594 	printf("\tPADR retries: %d\n", state.padr_retry_no);
595 
596 	if (dump > 1) {
597 		PPPOECTL_IOCTL(ifname, s, SPPPGETLCPSTATUS, &lcpst);
598 		PPPOECTL_IOCTL(ifname, s, SPPPGETIPCPSTATUS, &ipcpst);
599 		PPPOECTL_IOCTL(ifname, s, SPPPGETIPV6CPSTATUS, &ipv6cpst);
600 
601 		printf("\tLCP state: %s\n",
602 		    ppp_state_name(lcpst.state));
603 		printf("\tIPCP state: %s\n",
604 		    ppp_state_name(ipcpst.state));
605 		printf("\tIPv6CP state: %s\n",
606 		    ppp_state_name(ipv6cpst.state));
607 
608 		if (lcpst.state == SPPP_STATE_OPENED) {
609 			printf("\tLCP negotiated options:\n");
610 			printf("\t\tmru %lu\n", lcpst.mru);
611 			printf("\t\tmagic number 0x%lx\n",
612 			    lcpst.magic);
613 		}
614 
615 		if (ipcpst.state == SPPP_STATE_OPENED) {
616 			addr.s_addr = ipcpst.myaddr;
617 
618 			printf("\tIPCP negotiated options:\n");
619 			printf("\t\taddress %s\n", inet_ntoa(addr));
620 			print_dns(ifname,
621 			    ISSET(ipcpst.opts, SPPP_IPCP_OPT_PRIMDNS),
622 			    ISSET(ipcpst.opts, SPPP_IPCP_OPT_SECDNS),
623 			    s, 2);
624 		}
625 
626 		if (ipv6cpst.state == SPPP_STATE_OPENED) {
627 			printf("\tIPv6CP negotiated options:\n");
628 			if (ISSET(ipv6cpst.opts, SPPP_IPV6CP_OPT_COMPRESSION))
629 				printf("\t\tcompression\n");
630 			if (ISSET(ipv6cpst.opts, SPPP_IPV6CP_OPT_IFID)) {
631 				printf("\t\tifid: "
632 				    "my_ifid=0x%02x%02x%02x%02x%02x%02x%02x%02x, "
633 				    "his_ifid=0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
634 				ipv6cpst.my_ifid[0], ipv6cpst.my_ifid[1],
635 				ipv6cpst.my_ifid[2], ipv6cpst.my_ifid[3],
636 				ipv6cpst.my_ifid[4], ipv6cpst.my_ifid[5],
637 				ipv6cpst.my_ifid[6], ipv6cpst.my_ifid[7],
638 				ipv6cpst.his_ifid[0], ipv6cpst.his_ifid[1],
639 				ipv6cpst.his_ifid[2], ipv6cpst.his_ifid[3],
640 				ipv6cpst.his_ifid[4], ipv6cpst.his_ifid[5],
641 				ipv6cpst.his_ifid[6], ipv6cpst.his_ifid[7]);
642 			}
643 		}
644 	}
645 }
646 
647 static const char *
648 phase_name(int phase)
649 {
650 	switch (phase) {
651 	case SPPP_PHASE_DEAD:		return "dead";
652 	case SPPP_PHASE_ESTABLISH:	return "establish";
653 	case SPPP_PHASE_TERMINATE:	return "terminate";
654 	case SPPP_PHASE_AUTHENTICATE:	return "authenticate";
655 	case SPPP_PHASE_NETWORK:	return "network";
656 	}
657 	return "illegal";
658 }
659 
660 static const char *
661 proto_name(int proto)
662 {
663 	static char buf[12];
664 	switch (proto) {
665 	case SPPP_AUTHPROTO_PAP:	return "pap";
666 	case SPPP_AUTHPROTO_CHAP:	return "chap";
667 	case SPPP_AUTHPROTO_NONE:	return "none";
668 	}
669 	snprintf(buf, sizeof(buf), "0x%x", (unsigned)proto);
670 	return buf;
671 }
672 
673 static const char *
674 authflags(int flags)
675 {
676 	static char buf[32];
677 	buf[0] = '\0';
678 	if (flags & SPPP_AUTHFLAG_NOCALLOUT)
679 		strlcat(buf, " callin", sizeof(buf));
680 	if (flags & SPPP_AUTHFLAG_NORECHALLENGE)
681 		strlcat(buf, " norechallenge", sizeof(buf));
682 	return buf;
683 }
684 
685 static const char *
686 pppoe_state_name(int state)
687 {
688 
689 	switch(state) {
690 	case PPPOE_STATE_INITIAL:
691 		return "initial";
692 	case PPPOE_STATE_PADI_SENT:
693 		return "PADI sent";
694 	case PPPOE_STATE_PADR_SENT:
695 		return "PADR sent";
696 	case PPPOE_STATE_SESSION:
697 		return "session";
698 	case PPPOE_STATE_CLOSING:
699 		return "closing";
700 	}
701 
702 	return "unknown";
703 }
704 static const char *
705 ppp_state_name(int state)
706 {
707 
708 	switch (state) {
709 	case SPPP_STATE_INITIAL:	return "initial";
710 	case SPPP_STATE_STARTING:	return "starting";
711 	case SPPP_STATE_CLOSED:		return "closed";
712 	case SPPP_STATE_STOPPED:	return "stopped";
713 	case SPPP_STATE_CLOSING:	return "closing";
714 	case SPPP_STATE_STOPPING:	return "stopping";
715 	case SPPP_STATE_REQ_SENT:	return "req-sent";
716 	case SPPP_STATE_ACK_RCVD:	return "ack-rcvd";
717 	case SPPP_STATE_ACK_SENT:	return "ack-sent";
718 	case SPPP_STATE_OPENED:		return "opened";
719 	}
720 
721 	return "unknown";
722 }
723 
724 static void
725 print_error(const char *ifname, int error, const char * str)
726 {
727 	if (error == -1)
728 		fprintf(stderr, "%s: interface not found\n", ifname);
729 	else
730 		fprintf(stderr, "%s: %s: %s\n", ifname, str, strerror(error));
731 	exit(EX_DATAERR);
732 }
733