xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoec.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * PPPoE Client-mode "chat" utility for use with Solaris PPP 4.0.
24  *
25  * Copyright 2000-2002 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <ctype.h>
35 #include <strings.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <signal.h>
39 #include <stropts.h>
40 #include <netdb.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <net/if.h>
44 #include <netinet/in.h>
45 #include <netinet/if_ether.h>
46 
47 #include <net/sppptun.h>
48 #include <net/pppoe.h>
49 
50 #include "common.h"
51 #include "logging.h"
52 
53 /*
54  * This value, currently set to the characters "POE1," is used to
55  * distinguish among control messages from multiple lower streams
56  * under /dev/sppp.  This feature is needed to support PPP translation
57  * (LAC-like behavior), but isn't currently used.
58  */
59 #define	PPPOE_DISCRIM	0x504F4531
60 
61 /* milliseconds between retries */
62 #define	PADI_RESTART_TIME	500
63 #define	PADR_RESTART_TIME	2000
64 
65 /* default inquiry mode timer in milliseconds. */
66 #define	PADI_INQUIRY_DWELL	3000
67 
68 /* maximum timer value in milliseconds */
69 #define	RESTART_LIMIT	5000
70 
71 char *myname;		/* copy of argv[0] for error messages */
72 static int verbose;	/* -v flag given */
73 static int onlyflag;	/* keyword "only" at end of command line */
74 static char *service = "";	/* saved service name from command line */
75 
76 static int pado_wait_time = 0;	/* see main() */
77 static int pads_wait_time = PADR_RESTART_TIME;
78 
79 static int tunfd;	/* open connection to sppptun driver */
80 
81 static struct timeval tvstart;	/* time of last PADI/PADR transmission */
82 
83 struct server_filter {
84 	struct server_filter *sf_next;	/* Next filter in list */
85 	struct ether_addr sf_mac;	/* Ethernet address */
86 	struct ether_addr sf_mask;	/* Mask (0 or 0xFF in each byte) */
87 	const char *sf_name;		/* String for AC-Name compare */
88 	boolean_t sf_hasmac;		/* Set if string could be MAC */
89 	boolean_t sf_isexcept;		/* Ignore server if matching */
90 };
91 
92 /* List of filters defined on command line. */
93 static struct server_filter *sfhead, *sftail;
94 
95 /*
96  * PPPoE Client State Machine
97  */
98 
99 /* Client events */
100 #define	PCSME_CLOSE	0	/* User close */
101 #define	PCSME_OPEN	1	/* User open */
102 #define	PCSME_TOP	2	/* Timeout+ (counter non-zero) */
103 #define	PCSME_TOM	3	/* Timeout- (counter zero) */
104 #define	PCSME_RPADT	4	/* Receive PADT (unexpected here) */
105 #define	PCSME_RPADOP	5	/* Receive desired PADO */
106 #define	PCSME_RPADO	6	/* Receive ordinary PADO */
107 #define	PCSME_RPADS	7	/* Receive PADS */
108 #define	PCSME_RPADSN	8	/* Receive bad (errored) PADS */
109 #define	PCSME__MAX	9
110 
111 /* Client states */
112 #define	PCSMS_DEAD	0	/* Initial state */
113 #define	PCSMS_INITSENT	1	/* PADI sent */
114 #define	PCSMS_OFFRRCVD	2	/* PADO received */
115 #define	PCSMS_REQSENT	3	/* PADR sent */
116 #define	PCSMS_CONVERS	4	/* Conversational */
117 #define	PCSMS__MAX	5
118 
119 /* Client actions */
120 #define	PCSMA_NONE	0	/* Do nothing */
121 #define	PCSMA_FAIL	1	/* Unrecoverable error */
122 #define	PCSMA_SPADI	2	/* Send PADI */
123 #define	PCSMA_ADD	3	/* Add ordinary server to list */
124 #define	PCSMA_SPADR	4	/* Send PADR to top server */
125 #define	PCSMA_SPADRP	5	/* Send PADR to this server (make top) */
126 #define	PCSMA_SPADRN	6	/* Send PADR to next (or terminate) */
127 #define	PCSMA_OPEN	7	/* Start PPP */
128 #define	PCSMA__MAX	8
129 
130 static uint8_t client_next_state[PCSMS__MAX][PCSME__MAX] = {
131 /* 0 PCSMS_DEAD Initial state */
132 	{
133 		PCSMS_DEAD,	/* PCSME_CLOSE  User close */
134 		PCSMS_INITSENT,	/* PCSME_OPEN   User open */
135 		PCSMS_DEAD,	/* PCSME_TOP    Timeout+ */
136 		PCSMS_DEAD,	/* PCSME_TOM    Timeout- */
137 		PCSMS_DEAD,	/* PCSME_RPADT  Receive PADT */
138 		PCSMS_DEAD,	/* PCSME_RPADOP Receive desired PADO */
139 		PCSMS_DEAD,	/* PCSME_RPADO  Receive ordinary PADO */
140 		PCSMS_DEAD,	/* PCSME_RPADS  Receive PADS */
141 		PCSMS_DEAD,	/* PCSME_RPADSN Receive bad PADS */
142 	},
143 /* 1 PCSMS_INITSENT PADI sent */
144 	{
145 		PCSMS_DEAD,	/* PCSME_CLOSE  User close */
146 		PCSMS_INITSENT,	/* PCSME_OPEN   User open */
147 		PCSMS_INITSENT,	/* PCSME_TOP    Timeout+ */
148 		PCSMS_DEAD,	/* PCSME_TOM    Timeout- */
149 		PCSMS_DEAD,	/* PCSME_RPADT  Receive PADT */
150 		PCSMS_REQSENT,	/* PCSME_RPADOP Receive desired PADO */
151 		PCSMS_OFFRRCVD,	/* PCSME_RPADO  Receive ordinary PADO */
152 		PCSMS_INITSENT,	/* PCSME_RPADS  Receive PADS */
153 		PCSMS_INITSENT,	/* PCSME_RPADSN Receive bad PADS */
154 	},
155 /* 2 PCSMS_OFFRRCVD PADO received */
156 	{
157 		PCSMS_DEAD,	/* PCSME_CLOSE  User close */
158 		PCSMS_INITSENT,	/* PCSME_OPEN   User open */
159 		PCSMS_REQSENT,	/* PCSME_TOP    Timeout+ */
160 		PCSMS_REQSENT,	/* PCSME_TOM    Timeout- */
161 		PCSMS_DEAD,	/* PCSME_RPADT  Receive PADT */
162 		PCSMS_REQSENT,	/* PCSME_RPADOP Receive desired PADO */
163 		PCSMS_OFFRRCVD,	/* PCSME_RPADO  Receive ordinary PADO */
164 		PCSMS_OFFRRCVD,	/* PCSME_RPADS  Receive PADS */
165 		PCSMS_OFFRRCVD,	/* PCSME_RPADSN Receive bad PADS */
166 	},
167 /* 3 PCSMS_REQSENT  PADR sent */
168 	{
169 		PCSMS_DEAD,	/* PCSME_CLOSE  User close */
170 		PCSMS_INITSENT,	/* PCSME_OPEN   User open */
171 		PCSMS_REQSENT,	/* PCSME_TOP    Timeout+ */
172 		PCSMS_REQSENT,	/* PCSME_TOM    Timeout- */
173 		PCSMS_DEAD,	/* PCSME_RPADT  Receive PADT */
174 		PCSMS_REQSENT,	/* PCSME_RPADOP Receive desired PADO */
175 		PCSMS_REQSENT,	/* PCSME_RPADO  Receive ordinary PADO */
176 		PCSMS_CONVERS,	/* PCSME_RPADS  Receive PADS */
177 		PCSMS_REQSENT,	/* PCSME_RPADSN Receive bad PADS */
178 	},
179 /* 4 PCSMS_CONVERS  Conversational */
180 	{
181 		PCSMS_DEAD,	/* PCSME_CLOSE  User close */
182 		PCSMS_INITSENT,	/* PCSME_OPEN   User open */
183 		PCSMS_CONVERS,	/* PCSME_TOP    Timeout+ */
184 		PCSMS_CONVERS,	/* PCSME_TOM    Timeout- */
185 		PCSMS_DEAD,	/* PCSME_RPADT  Receive PADT */
186 		PCSMS_CONVERS,	/* PCSME_RPADOP Receive desired PADO */
187 		PCSMS_CONVERS,	/* PCSME_RPADO  Receive ordinary PADO */
188 		PCSMS_CONVERS,	/* PCSME_RPADS  Receive PADS */
189 		PCSMS_CONVERS,	/* PCSME_RPADSN Receive bad PADS */
190 	},
191 };
192 
193 static uint8_t client_action[PCSMS__MAX][PCSME__MAX] = {
194 /* 0 PCSMS_DEAD Initial state */
195 	{
196 		PCSMA_NONE,	/* PCSME_CLOSE  User close */
197 		PCSMA_SPADI,	/* PCSME_OPEN   User open */
198 		PCSMA_NONE,	/* PCSME_TOP    Timeout+ */
199 		PCSMA_NONE,	/* PCSME_TOM    Timeout- */
200 		PCSMA_NONE,	/* PCSME_RPADT  Receive PADT */
201 		PCSMA_NONE,	/* PCSME_RPADOP Receive desired PADO */
202 		PCSMA_NONE,	/* PCSME_RPADO  Receive ordinary PADO */
203 		PCSMA_NONE,	/* PCSME_RPADS  Receive PADS */
204 		PCSMA_NONE,	/* PCSME_RPADSN Receive bad PADS */
205 	},
206 /* 1 PCSMS_INITSENT PADI sent */
207 	{
208 		PCSMA_FAIL,	/* PCSME_CLOSE  User close */
209 		PCSMA_SPADI,	/* PCSME_OPEN   User open */
210 		PCSMA_SPADI,	/* PCSME_TOP    Timeout+ */
211 		PCSMA_FAIL,	/* PCSME_TOM    Timeout- */
212 		PCSMA_FAIL,	/* PCSME_RPADT  Receive PADT */
213 		PCSMA_SPADRP,	/* PCSME_RPADOP Receive desired PADO */
214 		PCSMA_ADD,	/* PCSME_RPADO  Receive ordinary PADO */
215 		PCSMA_NONE,	/* PCSME_RPADS  Receive PADS */
216 		PCSMA_NONE,	/* PCSME_RPADSN Receive bad PADS */
217 	},
218 /* 2 PCSMS_OFFRRCVD PADO received */
219 	{
220 		PCSMA_FAIL,	/* PCSME_CLOSE  User close */
221 		PCSMA_SPADI,	/* PCSME_OPEN   User open */
222 		PCSMA_SPADR,	/* PCSME_TOP    Timeout+ */
223 		PCSMA_SPADR,	/* PCSME_TOM    Timeout- */
224 		PCSMA_FAIL,	/* PCSME_RPADT  Receive PADT */
225 		PCSMA_SPADRP,	/* PCSME_RPADOP Receive desired PADO */
226 		PCSMA_ADD,	/* PCSME_RPADO  Receive ordinary PADO */
227 		PCSMA_NONE,	/* PCSME_RPADS  Receive PADS */
228 		PCSMA_NONE,	/* PCSME_RPADSN Receive bad PADS */
229 	},
230 /* 3 PCSMS_REQSENT  PADR sent */
231 	{
232 		PCSMA_FAIL,	/* PCSME_CLOSE  User close */
233 		PCSMA_SPADI,	/* PCSME_OPEN   User open */
234 		PCSMA_SPADR,	/* PCSME_TOP    Timeout+ */
235 		PCSMA_SPADRN,	/* PCSME_TOM    Timeout- */
236 		PCSMA_FAIL,	/* PCSME_RPADT  Receive PADT */
237 		PCSMA_ADD,	/* PCSME_RPADOP Receive desired PADO */
238 		PCSMA_ADD,	/* PCSME_RPADO  Receive ordinary PADO */
239 		PCSMA_OPEN,	/* PCSME_RPADS  Receive PADS */
240 		PCSMA_SPADRN,	/* PCSME_RPADSN Receive bad PADS */
241 	},
242 /* 4 PCSMS_CONVERS  Conversational */
243 	{
244 		PCSMA_FAIL,	/* PCSME_CLOSE  User close */
245 		PCSMA_SPADI,	/* PCSME_OPEN   User open */
246 		PCSMA_FAIL,	/* PCSME_TOP    Timeout+ */
247 		PCSMA_FAIL,	/* PCSME_TOM    Timeout- */
248 		PCSMA_FAIL,	/* PCSME_RPADT  Receive PADT */
249 		PCSMA_NONE,	/* PCSME_RPADOP Receive desired PADO */
250 		PCSMA_NONE,	/* PCSME_RPADO  Receive ordinary PADO */
251 		PCSMA_NONE,	/* PCSME_RPADS  Receive PADS */
252 		PCSMA_NONE,	/* PCSME_RPADSN Receive bad PADS */
253 	},
254 };
255 
256 /*
257  * PPPoE Message structure -- holds data from a received PPPoE
258  * message.  These are copied and saved when queuing offers from
259  * possible servers.
260  */
261 typedef struct poesm_s {
262 	struct poesm_s	*poemsg_next;	/* Next message in list */
263 	const poep_t	*poemsg_data;	/* Pointer to PPPoE packet */
264 	int		poemsg_len;	/* Length of packet */
265 	ppptun_atype	poemsg_sender;	/* Address of sender */
266 	const char	*poemsg_iname;	/* Name of input interface */
267 } poemsg_t;
268 
269 /*
270  * PPPoE State Machine structure -- holds state of PPPoE negotiation;
271  * currently, there's exactly one of these per pppoec instance.
272  */
273 typedef struct {
274 	int		poesm_state;		/* PCSMS_* */
275 	int		poesm_timer;		/* Milliseconds to next TO */
276 	int		poesm_count;		/* Retry countdown */
277 	int		poesm_interval;		/* Reload value */
278 	uint32_t	poesm_sequence;		/* Sequence for PADR */
279 
280 	poemsg_t	*poesm_firstoff;	/* Queue of valid offers; */
281 	poemsg_t	*poesm_lastoff;		/* first is best offer */
282 	poemsg_t	*poesm_tried;		/* Tried and failed offers */
283 
284 	int		poesm_localid;		/* Local session ID (driver) */
285 } poesm_t;
286 
287 /*
288  * Convert an internal PPPoE event code number into a printable
289  * string.
290  */
291 static const char *
292 poe_event(int event)
293 {
294 	static const char *poeevent[PCSME__MAX] = {
295 		"Close", "Open", "TO+", "TO-", "rPADT",
296 		"rPADO+", "rPADO", "rPADS", "rPADS-"
297 	};
298 
299 	if (event < 0 || event >= PCSME__MAX) {
300 		return ("?");
301 	}
302 	return (poeevent[event]);
303 }
304 
305 /*
306  * Convert an internal PPPoE state number into a printable string.
307  */
308 static const char *
309 poe_state(int state)
310 {
311 	static const char *poestate[PCSMS__MAX] = {
312 		"Dead", "InitSent", "OffrRcvd", "ReqSent", "Convers",
313 	};
314 
315 	if (state < 0 || state >= PCSMS__MAX) {
316 		return ("?");
317 	}
318 	return (poestate[state]);
319 }
320 
321 /*
322  * Convert an internal PPPoE action number into a printable string.
323  */
324 static const char *
325 poe_action(int act)
326 {
327 	static const char *poeaction[PCSMA__MAX] = {
328 		"None", "Fail", "SendPADI", "Add", "SendPADR",
329 		"SendPADR+", "SendPADR-", "Open"
330 	};
331 
332 	if (act < 0 || act >= PCSMA__MAX) {
333 		return ("?");
334 	}
335 	return (poeaction[act]);
336 }
337 
338 /*
339  * This calls mygetmsg (which discards partial messages as needed) and
340  * logs errors as appropriate.
341  */
342 static int
343 pppoec_getmsg(int fd, struct strbuf *ctrl, struct strbuf *data, int *flags)
344 {
345 	int retv;
346 
347 	for (;;) {
348 		retv = mygetmsg(fd, ctrl, data, flags);
349 		if (retv == 0)
350 			break;
351 		if (retv < 0) {
352 			if (errno == EINTR)
353 				continue;
354 			logstrerror("getmsg");
355 			break;
356 		}
357 		if (verbose) {
358 			if (!(retv & (MORECTL | MOREDATA)))
359 				logerr("%s: discard: "
360 				    "unexpected status %d\n", myname, retv);
361 			else
362 				logerr("%s: discard: "
363 				    "truncated %s%smessage\n", myname,
364 				    retv & MORECTL ? "control " : "",
365 				    retv & MOREDATA ? "data " : "");
366 		}
367 	}
368 	return (retv);
369 }
370 
371 /*
372  * Connect the control path to the lower stream of interest.  This
373  * must be called after opening the tunnel driver in order to
374  * establish the interface to be used for signaling.  Returns local
375  * session ID number.
376  */
377 static int
378 set_control(const char *dname)
379 {
380 	struct ppptun_peer ptp;
381 	union ppptun_name ptn;
382 
383 	/* Fetch the local session ID first. */
384 	(void) memset(&ptp, '\0', sizeof (ptp));
385 	ptp.ptp_style = PTS_PPPOE;
386 	if (strioctl(tunfd, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
387 	    0) {
388 		logstrerror("PPPTUN_SPEER");
389 		exit(1);
390 	}
391 
392 	/* Connect to lower stream. */
393 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
394 	    dname);
395 	if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
396 		logerr("%s: PPPTUN_SCTL %s: %s\n", myname,
397 		    ptn.ptn_name, mystrerror(errno));
398 		exit(1);
399 	}
400 	return (ptp.ptp_lsessid);
401 }
402 
403 /*
404  * Check if standard input is actually a viable connection to the
405  * tunnel driver.  This is the normal mode of operation with pppd; the
406  * tunnel driver is opened by pppd as the tty and pppoec is exec'd as
407  * the connect script.
408  */
409 static void
410 check_stdin(void)
411 {
412 	struct ppptun_info pti;
413 	union ppptun_name ptn;
414 
415 	if (strioctl(0, PPPTUN_GDATA, &ptn, 0, sizeof (ptn)) < 0) {
416 		if (errno == EINVAL)
417 			logerr("%s: PPPoE operation requires "
418 			    "the use of a tunneling device\n", myname);
419 		else
420 			logstrerror("PPPTUN_GDATA");
421 		exit(1);
422 	}
423 	if (ptn.ptn_name[0] != '\0') {
424 		if (strioctl(0, PPPTUN_GINFO, &pti, 0, sizeof (pti)) < 0) {
425 			logstrerror("PPPTUN_GINFO");
426 			exit(1);
427 		}
428 		if (pti.pti_style != PTS_PPPOE) {
429 			logerr("%s: Cannot connect to server "
430 			    "using PPPoE; stream already set to style %d\n",
431 			    myname, pti.pti_style);
432 			exit(1);
433 		}
434 		if (verbose)
435 			logerr("%s: Warning:  PPPoE data link "
436 			    "already connected\n", myname);
437 		exit(0);
438 	}
439 	/* Standard input is the tunnel driver; use it. */
440 	tunfd = 0;
441 }
442 
443 /*
444  * Write a summary of a PPPoE message to the given file.  This is used
445  * for logging and to display received offers in the inquiry (-i) mode.
446  */
447 static void
448 display_pppoe(FILE *out, const poep_t *poep, int plen, const ppptun_atype *pap)
449 {
450 	int ttyp;
451 	int tlen;
452 	const uint8_t *tagp;
453 	const uint8_t *dp;
454 	const char *str;
455 	poer_t poer;
456 	uint32_t mask;
457 
458 	if (out == stderr)
459 		logerr(" ");	/* Give us a timestamp */
460 	/* Print name of sender. */
461 	(void) fprintf(out, "%-16s ", ehost(pap));
462 
463 	/* Loop through tags and print each. */
464 	tagp = (const uint8_t *)(poep + 1);
465 	while (poe_tagcheck(poep, plen, tagp)) {
466 		ttyp = POET_GET_TYPE(tagp);
467 		if (ttyp == POETT_END)
468 			break;
469 		tlen = POET_GET_LENG(tagp);
470 		dp = POET_DATA(tagp);
471 		str = NULL;
472 		switch (ttyp) {
473 		case POETT_SERVICE:	/* Service-Name */
474 			str = "Svc";
475 			break;
476 		case POETT_ACCESS:	/* AC-Name */
477 			str = "Name";
478 			break;
479 		case POETT_UNIQ:	/* Host-Uniq */
480 			str = "Uniq";
481 			break;
482 		case POETT_COOKIE:	/* AC-Cookie */
483 			str = "Cookie";
484 			break;
485 		case POETT_VENDOR:	/* Vendor-Specific */
486 			break;
487 		case POETT_RELAY:	/* Relay-Session-Id */
488 			str = "Relay";
489 			break;
490 		case POETT_NAMERR:	/* Service-Name-Error */
491 			str = "SvcNameErr";
492 			break;
493 		case POETT_SYSERR:	/* AC-System-Error */
494 			str = "SysErr";
495 			break;
496 		case POETT_GENERR:	/* Generic-Error */
497 			str = "GenErr";
498 			break;
499 		case POETT_MULTI:	/* Multicast-Capable */
500 			break;
501 		case POETT_HURL:	/* Host-URL */
502 			str = "URL";
503 			break;
504 		case POETT_MOTM:	/* Message-Of-The-Minute */
505 			str = "Mesg";
506 			break;
507 		case POETT_RTEADD:	/* IP-Route-Add */
508 			break;
509 		}
510 		switch (ttyp) {
511 		case POETT_NAMERR:	/* Service-Name-Error */
512 		case POETT_SYSERR:	/* AC-System-Error */
513 			if (tlen > 0 && *dp == '\0')
514 				tlen = 0;
515 			/* FALLTHROUGH */
516 		case POETT_SERVICE:	/* Service-Name */
517 		case POETT_ACCESS:	/* AC-Name */
518 		case POETT_GENERR:	/* Generic-Error */
519 		case POETT_MOTM:	/* Message-Of-The-Minute */
520 		case POETT_HURL:	/* Host-URL */
521 			(void) fprintf(out, "%s:\"%.*s\" ", str, tlen, dp);
522 			break;
523 		case POETT_UNIQ:	/* Host-Uniq */
524 		case POETT_COOKIE:	/* AC-Cookie */
525 		case POETT_RELAY:	/* Relay-Session-Id */
526 			(void) fprintf(out, "%s:", str);
527 			while (--tlen >= 0)
528 				(void) fprintf(out, "%02X", *dp++);
529 			(void) putc(' ', out);
530 			break;
531 		case POETT_VENDOR:	/* Vendor-Specific */
532 			(void) fputs("Vendor:", out);
533 			if (tlen >= 4) {
534 				if (*dp++ != 0) {
535 					(void) fprintf(out, "(%02X?)", dp[-1]);
536 				}
537 				(void) fprintf(out, "%x-%x-%x:", dp[0], dp[1],
538 				    dp[2]);
539 				tlen -= 4;
540 				dp += 3;
541 			}
542 			while (--tlen >= 0)
543 				(void) fprintf(out, "%02X", *dp++);
544 			(void) putc(' ', out);
545 			break;
546 		case POETT_MULTI:	/* Multicast-Capable */
547 			(void) fprintf(out, "Multi:%d ", *dp);
548 			break;
549 		case POETT_RTEADD:	/* IP-Route-Add */
550 			if (tlen != sizeof (poer)) {
551 				(void) fprintf(out, "RTE%d? ", tlen);
552 				break;
553 			}
554 			(void) memcpy(&poer, dp, sizeof (poer));
555 			(void) fputs("RTE:", out);
556 			if (poer.poer_dest_network == 0)
557 				(void) fputs("default", out);
558 			else
559 				(void) fputs(ihost(poer.poer_dest_network),
560 				    out);
561 			mask = ntohl(poer.poer_subnet_mask);
562 			if (mask != 0 && mask != (uint32_t)~0) {
563 				if ((~mask & (~mask + 1)) == 0)
564 					(void) fprintf(out, "/%d",
565 					    sizeof (struct in_addr) * NBBY +
566 					    1 - ffs(mask));
567 				else
568 					(void) fprintf(out, "/%s",
569 					    ihost(poer.poer_subnet_mask));
570 			}
571 			(void) fprintf(out, ",%s,%u ",
572 			    ihost(poer.poer_gateway), ntohl(poer.poer_metric));
573 			break;
574 		default:
575 			(void) fprintf(out, "%s:%d ", poe_tagname(ttyp), tlen);
576 			break;
577 		}
578 		tagp = POET_NEXT(tagp);
579 	}
580 	(void) putc('\n', out);
581 }
582 
583 /*
584  * Transmit a PPPoE message to the indicated destination.  Used for
585  * PADI and PADR messages.
586  */
587 static int
588 send_pppoe(const poep_t *poep, const char *msgname,
589     const ppptun_atype *destaddr)
590 {
591 	struct strbuf ctrl;
592 	struct strbuf data;
593 	struct ppptun_control *ptc;
594 
595 	/* Set up the control data expected by the driver. */
596 	ptc = (struct ppptun_control *)pkt_octl;
597 	(void) memset(ptc, '\0', sizeof (*ptc));
598 	ptc->ptc_discrim = PPPOE_DISCRIM;
599 	ptc->ptc_action = PTCA_CONTROL;
600 	ptc->ptc_address = *destaddr;
601 	ctrl.len = sizeof (*ptc);
602 	ctrl.buf = (caddr_t)ptc;
603 	data.len = poe_length(poep) + sizeof (*poep);
604 	data.buf = (caddr_t)poep;
605 	if (verbose)
606 		logerr("%s: Sending %s to %s: %d bytes\n",
607 		    myname, msgname, ehost(destaddr), data.len);
608 	if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
609 		logstrerror("putmsg");
610 		return (-1);
611 	}
612 	return (0);
613 }
614 
615 /*
616  * Create and transmit a PPPoE Active Discovery Initiation packet.
617  * This is broadcasted to all hosts on the LAN.
618  */
619 static int
620 send_padi(int localid)
621 {
622 	poep_t *poep;
623 	ppptun_atype destaddr;
624 
625 	poep = poe_mkheader(pkt_output, POECODE_PADI, 0);
626 	(void) poe_add_str(poep, POETT_SERVICE, "");
627 	(void) poe_add_long(poep, POETT_UNIQ, localid);
628 	(void) memset(&destaddr, '\0', sizeof (destaddr));
629 	(void) memcpy(destaddr.pta_pppoe.ptma_mac, ether_bcast,
630 	    sizeof (destaddr.pta_pppoe.ptma_mac));
631 	return (send_pppoe(poep, "PADI", &destaddr));
632 }
633 
634 /*
635  * This is used by the procedure below -- when the alarm goes off,
636  * just exit.  (This was once a dummy procedure and used the EINTR
637  * side-effect to terminate the loop, but that's not reliable, since
638  * the EINTR could be caught and ignored by the calls to standard
639  * output.)
640  */
641 /* ARGSUSED */
642 static void
643 alarm_hand(int dummy)
644 {
645 	exit(0);
646 }
647 
648 /*
649  * Send out a single PADI and listen for servers.  This implements the
650  * "inquiry" (-i) mode.
651  */
652 static void
653 find_all_servers(int localid)
654 {
655 	struct strbuf ctrl;
656 	struct strbuf data;
657 	poep_t *poep;
658 	int flags;
659 	struct sigaction act;
660 	struct ppptun_control *ptc;
661 
662 	/* Set a default 3-second timer */
663 	(void) memset(&act, '\0', sizeof (act));
664 	act.sa_handler = alarm_hand;
665 	(void) sigaction(SIGALRM, &act, NULL);
666 	(void) alarm((pado_wait_time + 999) / 1000);
667 
668 	/* Broadcast a single request. */
669 	if (send_padi(localid) != 0)
670 		return;
671 
672 	/* Loop over responses and print them. */
673 	for (;;) {
674 		ctrl.maxlen = PKT_OCTL_LEN;
675 		ctrl.buf = (caddr_t)pkt_octl;
676 		data.maxlen = PKT_INPUT_LEN;
677 		data.buf = (caddr_t)pkt_input;
678 		flags = 0;
679 
680 		if (pppoec_getmsg(tunfd, &ctrl, &data, &flags) < 0)
681 			break;
682 
683 		/* Ignore unwanted responses from the driver. */
684 		if (ctrl.len != sizeof (*ptc)) {
685 			if (verbose)
686 				logerr("%s: unexpected %d byte"
687 				    " control message from driver.\n", myname,
688 				    ctrl.len);
689 			continue;
690 		}
691 		ptc = (struct ppptun_control *)pkt_octl;
692 		poep = (poep_t *)pkt_input;
693 
694 		/* If it's an offer, then print it out. */
695 		if (poe_code(poep) == POECODE_PADO) {
696 			display_pppoe(stdout, poep, data.len,
697 			    &ptc->ptc_address);
698 		}
699 	}
700 }
701 
702 /*
703  * Parse a server filter from the command line.  The passed-in string
704  * must be allocated and unchanged, since a pointer to it is saved in
705  * the filter data structure.  The string is also parsed for a MAC
706  * address, if possible.
707  */
708 static void
709 parse_filter(const char *str, int exceptflag)
710 {
711 	struct server_filter *sfnew;
712 	const char *cp;
713 	const char *wordstart;
714 	const char *wordend;
715 	int len;
716 	char hbuf[MAXHOSTNAMELEN];
717 	uchar_t *ucp;
718 	uchar_t *mcp;
719 
720 	/* Allocate the new filter structure. */
721 	sfnew = (struct server_filter *)calloc(1, sizeof (*sfnew));
722 	if (sfnew == NULL) {
723 		logstrerror("filter allocation");
724 		exit(1);
725 	}
726 
727 	/* Save the string for AC-Name comparison. */
728 	sfnew->sf_name = str;
729 
730 	sfnew->sf_isexcept = exceptflag == 0 ? 0 : 1;
731 
732 	/* Extract just one word. */
733 	cp = str;
734 	while (isspace(*cp))
735 		cp++;
736 	wordstart = cp;
737 	while (*cp != '\0' && !isspace(*cp))
738 		cp++;
739 	wordend = cp;
740 	if ((len = wordend - wordstart) >= sizeof (hbuf))
741 		len = sizeof (hbuf) - 1;
742 	(void) strlcpy(hbuf, wordstart, len);
743 	hbuf[len] = '\0';
744 
745 	/* Try to translate this as an Ethernet host or address. */
746 	mcp = sfnew->sf_mask.ether_addr_octet;
747 	if (ether_hostton(hbuf, &sfnew->sf_mac) == 0) {
748 		mcp[0] = mcp[1] = mcp[2] = mcp[3] = mcp[4] = mcp[5] = 0xFF;
749 		sfnew->sf_hasmac = 1;
750 	} else {
751 		ucp = sfnew->sf_mac.ether_addr_octet;
752 		len = wordend - wordstart;
753 		cp = wordstart;
754 		while (cp < wordend) {
755 			if (ucp >= sfnew->sf_mac.ether_addr_octet +
756 			    sizeof (sfnew->sf_mac))
757 				break;
758 			if (*cp == '*') {
759 				*mcp++ = *ucp++ = 0;
760 				cp++;
761 			} else {
762 				if (!isxdigit(*cp))
763 					break;
764 				*ucp = hexdecode(*cp++);
765 				if (cp < wordend && isxdigit(*cp)) {
766 					*ucp = (*ucp << 4) |
767 					    hexdecode(*cp++);
768 				}
769 				ucp++;
770 				*mcp++ = 0xFF;
771 			}
772 			if (cp < wordend) {
773 				if (*cp != ':' || cp + 1 == wordend)
774 					break;
775 				cp++;
776 			}
777 		}
778 		if (cp >= wordend)
779 			sfnew->sf_hasmac = 1;
780 		else if (verbose)
781 			logerr("%s: treating '%.*s' as server "
782 			    "name only, not MAC address\n", myname, len,
783 			    wordstart);
784 	}
785 
786 	/* Add to end of list. */
787 	if (sftail == NULL)
788 		sfhead = sfnew;
789 	else
790 		sftail->sf_next = sfnew;
791 	sftail = sfnew;
792 }
793 
794 /*
795  * Create a copy of a given PPPoE message.  This is used for enqueuing
796  * received PADO (offers) from possible servers.
797  */
798 static poemsg_t *
799 save_message(const poemsg_t *pmsg)
800 {
801 	poemsg_t *newmsg;
802 	char *cp;
803 
804 	newmsg = (poemsg_t *)malloc(sizeof (*pmsg) + pmsg->poemsg_len +
805 		strlen(pmsg->poemsg_iname) + 1);
806 	if (newmsg != NULL) {
807 		newmsg->poemsg_next = NULL;
808 		newmsg->poemsg_data = (const poep_t *)(newmsg + 1);
809 		(void) memcpy(newmsg + 1, pmsg->poemsg_data, pmsg->poemsg_len);
810 		newmsg->poemsg_len = pmsg->poemsg_len;
811 		cp = (char *)newmsg->poemsg_data + pmsg->poemsg_len;
812 		newmsg->poemsg_iname = (const char *)cp;
813 		(void) strcpy(cp, pmsg->poemsg_iname);
814 		(void) memcpy(&newmsg->poemsg_sender, &pmsg->poemsg_sender,
815 		    sizeof (newmsg->poemsg_sender));
816 	}
817 	return (newmsg);
818 }
819 
820 /*
821  * Create and send a PPPoE Active Discovery Request (PADR) message to
822  * the sender of the given PADO.  Some tags -- Service-Name,
823  * AC-Cookie, and Relay-Session-Id -- must be copied from PADO to
824  * PADR.  Others are not.  The Service-Name must be selected from the
825  * offered services in the PADO based on the user's requested service
826  * name.  If the server offered "wildcard" service, then we ask for
827  * this only if we can't find the user's requested service.
828  *
829  * Returns 1 if we can't send a valid PADR in response to the given
830  * PADO.  The offer should be ignored and the next one tried.
831  */
832 static int
833 send_padr(poesm_t *psm, const poemsg_t *pado)
834 {
835 	poep_t *poep;
836 	boolean_t haswild;
837 	boolean_t hassvc;
838 	const uint8_t *tagp;
839 	int ttyp;
840 	int tlen;
841 
842 	/*
843 	 * Increment sequence number for PADR so that we don't mistake
844 	 * old replies for valid ones if the server is very slow.
845 	 */
846 	psm->poesm_sequence++;
847 
848 	poep = poe_mkheader(pkt_output, POECODE_PADR, 0);
849 	(void) poe_two_longs(poep, POETT_UNIQ, psm->poesm_localid,
850 	    psm->poesm_sequence);
851 
852 	haswild = B_FALSE;
853 	hassvc = B_FALSE;
854 	tagp = (const uint8_t *)(pado->poemsg_data + 1);
855 	while (poe_tagcheck(pado->poemsg_data, pado->poemsg_len, tagp)) {
856 		ttyp = POET_GET_TYPE(tagp);
857 		if (ttyp == POETT_END)
858 			break;
859 		tlen = POET_GET_LENG(tagp);
860 		switch (ttyp) {
861 		case POETT_SERVICE:	/* Service-Name */
862 			/* Allow only one */
863 			if (hassvc)
864 				break;
865 			if (tlen == 0) {
866 				haswild = B_TRUE;
867 				break;
868 			}
869 			if (service[0] == '\0' ||
870 			    (tlen == strlen(service) &&
871 				memcmp(service, POET_DATA(tagp), tlen) == 0)) {
872 				(void) poe_tag_copy(poep, tagp);
873 				hassvc = B_TRUE;
874 			}
875 			break;
876 		/* Ones we should discard */
877 		case POETT_ACCESS:	/* AC-Name */
878 		case POETT_UNIQ:	/* Host-Uniq */
879 		case POETT_NAMERR:	/* Service-Name-Error */
880 		case POETT_SYSERR:	/* AC-System-Error */
881 		case POETT_GENERR:	/* Generic-Error */
882 		case POETT_HURL:	/* Host-URL */
883 		case POETT_MOTM:	/* Message-Of-The-Minute */
884 		case POETT_RTEADD:	/* IP-Route-Add */
885 		case POETT_VENDOR:	/* Vendor-Specific */
886 		case POETT_MULTI:	/* Multicast-Capable */
887 		default:		/* Anything else we don't understand */
888 			break;
889 		/* Ones we should copy */
890 		case POETT_COOKIE:	/* AC-Cookie */
891 		case POETT_RELAY:	/* Relay-Session-Id */
892 			(void) poe_tag_copy(poep, tagp);
893 			break;
894 		}
895 		tagp = POET_NEXT(tagp);
896 	}
897 	if (!hassvc) {
898 		if (haswild)
899 			(void) poe_add_str(poep, POETT_SERVICE, "");
900 		else
901 			return (1);
902 	}
903 
904 	return (send_pppoe(poep, "PADR", &pado->poemsg_sender));
905 }
906 
907 /*
908  * ********************************************************************
909  * act_* functions implement the actions driven by the state machine
910  * tables.  See "action_table" below.
911  *
912  * All action routines must return the next state value.
913  * ********************************************************************
914  */
915 
916 /* ARGSUSED */
917 static int
918 act_none(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
919 {
920 	return (nextst);
921 }
922 
923 /* ARGSUSED */
924 static int
925 act_fail(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
926 {
927 	if (verbose)
928 		logerr("%s: unrecoverable error\n", myname);
929 	return (PCSMS_DEAD);
930 }
931 
932 /* ARGSUSED */
933 static int
934 act_spadi(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
935 {
936 	if (send_padi(psm->poesm_localid) != 0)
937 		return (PCSMS_DEAD);
938 	/*
939 	 * If this is the first time, then initialize the retry count
940 	 * and interval.
941 	 */
942 	if (psm->poesm_state == PCSMS_DEAD) {
943 		psm->poesm_count = 3;
944 		psm->poesm_interval = pado_wait_time;
945 	} else {
946 		if ((psm->poesm_interval <<= 1) > RESTART_LIMIT)
947 			psm->poesm_interval = RESTART_LIMIT;
948 	}
949 	psm->poesm_timer = psm->poesm_interval;
950 	(void) gettimeofday(&tvstart, NULL);
951 	return (nextst);
952 }
953 
954 /* ARGSUSED */
955 static int
956 act_add(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
957 {
958 	pmsg = save_message(pmsg);
959 	if (pmsg != NULL) {
960 		if (psm->poesm_lastoff == NULL)
961 			psm->poesm_firstoff = pmsg;
962 		else
963 			psm->poesm_lastoff->poemsg_next = pmsg;
964 		psm->poesm_lastoff = pmsg;
965 	}
966 	return (nextst);
967 }
968 
969 /* ARGSUSED */
970 static int
971 act_spadr(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
972 {
973 	poemsg_t *msgp;
974 	int retv;
975 
976 	for (;;) {
977 		if ((msgp = psm->poesm_firstoff) == NULL)
978 			return (PCSMS_DEAD);
979 		retv = send_padr(psm, msgp);
980 		if (retv < 0)
981 			return (PCSMS_DEAD);
982 		if (retv == 0)
983 			break;
984 		/* Can't send this request; try looking at next offer. */
985 		psm->poesm_firstoff = msgp->poemsg_next;
986 		msgp->poemsg_next = psm->poesm_tried;
987 		psm->poesm_tried = msgp;
988 	}
989 	if (psm->poesm_state != PCSMS_REQSENT) {
990 		psm->poesm_count = 3;
991 		psm->poesm_interval = pads_wait_time;
992 	} else {
993 		if ((psm->poesm_interval <<= 1) > RESTART_LIMIT)
994 			psm->poesm_interval = RESTART_LIMIT;
995 	}
996 	psm->poesm_timer = psm->poesm_interval;
997 	(void) gettimeofday(&tvstart, NULL);
998 	return (nextst);
999 }
1000 
1001 /* ARGSUSED */
1002 static int
1003 act_spadrp(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
1004 {
1005 	int retv;
1006 
1007 	retv = send_padr(psm, pmsg);
1008 	if (retv < 0)
1009 		return (PCSMS_DEAD);
1010 	pmsg = save_message(pmsg);
1011 	if (retv > 0) {
1012 		/*
1013 		 * Cannot use this one; mark as tried and continue as
1014 		 * if we never saw it.
1015 		 */
1016 		pmsg->poemsg_next = psm->poesm_tried;
1017 		psm->poesm_tried = pmsg;
1018 		return (psm->poesm_state);
1019 	}
1020 	pmsg->poemsg_next = psm->poesm_firstoff;
1021 	psm->poesm_firstoff = pmsg;
1022 	if (psm->poesm_lastoff == NULL)
1023 		psm->poesm_lastoff = pmsg;
1024 	psm->poesm_count = 3;
1025 	psm->poesm_timer = psm->poesm_interval = pads_wait_time;
1026 	(void) gettimeofday(&tvstart, NULL);
1027 	return (nextst);
1028 }
1029 
1030 /* ARGSUSED */
1031 static int
1032 act_spadrn(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
1033 {
1034 	poemsg_t *msgp;
1035 	int retv;
1036 
1037 	if ((msgp = psm->poesm_firstoff) == NULL)
1038 		return (PCSMS_DEAD);
1039 	do {
1040 		psm->poesm_firstoff = msgp->poemsg_next;
1041 		msgp->poemsg_next = psm->poesm_tried;
1042 		psm->poesm_tried = msgp;
1043 		if ((msgp = psm->poesm_firstoff) == NULL)
1044 			return (PCSMS_DEAD);
1045 		retv = send_padr(psm, msgp);
1046 		if (retv < 0)
1047 			return (PCSMS_DEAD);
1048 	} while (retv != 0);
1049 	psm->poesm_count = 3;
1050 	psm->poesm_timer = psm->poesm_interval = pads_wait_time;
1051 	(void) gettimeofday(&tvstart, NULL);
1052 	return (nextst);
1053 }
1054 
1055 /*
1056  * For safety -- remove end of line from strings passed back to pppd.
1057  */
1058 static void
1059 remove_eol(char *str, size_t len)
1060 {
1061 	while (len > 0) {
1062 		if (*str == '\n')
1063 			*str = '$';
1064 		str++;
1065 		len--;
1066 	}
1067 }
1068 
1069 /* ARGSUSED */
1070 static int
1071 act_open(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
1072 {
1073 	struct ppptun_peer ptp;
1074 	union ppptun_name ptn;
1075 	const char *cp;
1076 	FILE *fp;
1077 	const uint8_t *tagp, *vp;
1078 	int tlen, ttyp;
1079 	char *access;
1080 	uint32_t val;
1081 	size_t acc_len, serv_len;
1082 
1083 	/*
1084 	 * The server has now assigned its session ID for the data
1085 	 * (PPP) portion of this tunnel.  Send that ID down to the
1086 	 * driver.
1087 	 */
1088 	(void) memset(&ptp, '\0', sizeof (ptp));
1089 	ptp.ptp_lsessid = psm->poesm_localid;
1090 	ptp.ptp_rsessid = poe_session_id(pmsg->poemsg_data);
1091 	(void) memcpy(&ptp.ptp_address, &pmsg->poemsg_sender,
1092 	    sizeof (ptp.ptp_address));
1093 	ptp.ptp_style = PTS_PPPOE;
1094 	if (strioctl(tunfd, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
1095 	    0) {
1096 		logstrerror("PPPTUN_SPEER");
1097 		return (PCSMS_DEAD);
1098 	}
1099 
1100 	/*
1101 	 * Data communication is now possible on this session.
1102 	 * Connect the data portion to the correct lower stream.
1103 	 */
1104 	if ((cp = strchr(pmsg->poemsg_iname, ':')) == NULL)
1105 		cp = pmsg->poemsg_iname + strlen(pmsg->poemsg_iname);
1106 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoe",
1107 	    cp - pmsg->poemsg_iname, pmsg->poemsg_iname);
1108 	if (strioctl(tunfd, PPPTUN_SDATA, &ptn, sizeof (ptn), 0) < 0) {
1109 		logerr("%s: PPPTUN_SDATA %s: %s\n",
1110 		    myname, ptn.ptn_name, mystrerror(errno));
1111 		return (PCSMS_DEAD);
1112 	}
1113 	if (verbose)
1114 		logerr("%s: Connection open; session %04X on "
1115 		    "%s\n", myname, ptp.ptp_rsessid, ptn.ptn_name);
1116 
1117 	/*
1118 	 * Walk through the PADS message to get the access server name
1119 	 * and the service.  If there are multiple instances of either
1120 	 * tag, then take the last access server and the first
1121 	 * non-null service.
1122 	 */
1123 	access = "";
1124 	acc_len = 0;
1125 	serv_len = strlen(service);
1126 	tagp = (const uint8_t *)(pmsg->poemsg_data + 1);
1127 	while (poe_tagcheck(pmsg->poemsg_data, pmsg->poemsg_len, tagp)) {
1128 		ttyp = POET_GET_TYPE(tagp);
1129 		if (ttyp == POETT_END)
1130 			break;
1131 		tlen = POET_GET_LENG(tagp);
1132 		if (ttyp == POETT_ACCESS) {
1133 			access = (char *)POET_DATA(tagp);
1134 			acc_len = tlen;
1135 		}
1136 		if (serv_len == 0 && ttyp == POETT_SERVICE && tlen != 0) {
1137 			service = (char *)POET_DATA(tagp);
1138 			serv_len = tlen;
1139 		}
1140 		tagp = POET_NEXT(tagp);
1141 	}
1142 
1143 	/*
1144 	 * Remove end of line to make sure that integrity of values
1145 	 * passed back to pppd can't be compromised by the PPPoE
1146 	 * server.
1147 	 */
1148 	remove_eol(service, serv_len);
1149 	remove_eol(access, acc_len);
1150 
1151 	/*
1152 	 * pppd has given us a pipe as fd 3, and we're expected to
1153 	 * write out the values of the following environment
1154 	 * variables:
1155 	 *	IF_AND_SERVICE
1156 	 *	SERVICE_NAME
1157 	 *	AC_NAME
1158 	 *	AC_MAC
1159 	 *	SESSION_ID
1160 	 *	VENDOR_SPECIFIC_1 ... N
1161 	 * See usr.bin/pppd/plugins/pppoe.c for more information.
1162 	 */
1163 	if ((fp = fdopen(3, "w")) != NULL) {
1164 		(void) fprintf(fp, "%.*s:%.*s\n",
1165 		    cp - pmsg->poemsg_iname, pmsg->poemsg_iname, serv_len,
1166 		    service);
1167 		(void) fprintf(fp, "%.*s\n", serv_len, service);
1168 		(void) fprintf(fp, "%.*s\n", acc_len, access);
1169 		(void) fprintf(fp, "%s\n", ehost(&pmsg->poemsg_sender));
1170 		(void) fprintf(fp, "%d\n", poe_session_id(pmsg->poemsg_data));
1171 		tagp = (const uint8_t *)(pmsg->poemsg_data + 1);
1172 		while (poe_tagcheck(pmsg->poemsg_data, pmsg->poemsg_len,
1173 		    tagp)) {
1174 			ttyp = POET_GET_TYPE(tagp);
1175 			if (ttyp == POETT_END)
1176 				break;
1177 			tlen = POET_GET_LENG(tagp);
1178 			if (ttyp == POETT_VENDOR && tlen >= 4) {
1179 				(void) memcpy(&val, POET_DATA(tagp), 4);
1180 				(void) fprintf(fp, "%08lX:",
1181 				    (unsigned long)ntohl(val));
1182 				tlen -= 4;
1183 				vp = POET_DATA(tagp) + 4;
1184 				while (--tlen >= 0)
1185 					(void) fprintf(fp, "%02X", *vp++);
1186 				(void) putc('\n', fp);
1187 			}
1188 			tagp = POET_NEXT(tagp);
1189 		}
1190 		(void) fclose(fp);
1191 	}
1192 
1193 	return (nextst);
1194 }
1195 
1196 static int (* const action_table[PCSMA__MAX])(poesm_t *psm, poemsg_t *pmsg,
1197     int event, int nextst) = {
1198 	    act_none, act_fail, act_spadi, act_add, act_spadr, act_spadrp,
1199 	    act_spadrn, act_open
1200 };
1201 
1202 /*
1203  * Dispatch an event and a corresponding message on a given state
1204  * machine.
1205  */
1206 static void
1207 handle_event(poesm_t *psm, int event, poemsg_t *pmsg)
1208 {
1209 	int nextst;
1210 
1211 	if (verbose)
1212 		logerr("%s: PPPoE Event %s (%d) in state %s "
1213 		    "(%d): action %s (%d)\n", myname, poe_event(event), event,
1214 		    poe_state(psm->poesm_state), psm->poesm_state,
1215 		    poe_action(client_action[psm->poesm_state][event]),
1216 		    client_action[psm->poesm_state][event]);
1217 
1218 	nextst = (*action_table[client_action[psm->poesm_state][event]])(psm,
1219 	    pmsg, event, client_next_state[psm->poesm_state][event]);
1220 
1221 	if (verbose)
1222 		logerr("%s: PPPoE State change %s (%d) -> %s (%d)\n", myname,
1223 		    poe_state(psm->poesm_state), psm->poesm_state,
1224 		    poe_state(nextst), nextst);
1225 
1226 	psm->poesm_state = nextst;
1227 
1228 	/*
1229 	 * Life-altering states are handled here.  If we reach dead
1230 	 * state again after starting, then we failed.  If we reach
1231 	 * conversational state, then we're open.
1232 	 */
1233 	if (nextst == PCSMS_DEAD) {
1234 		if (verbose)
1235 			logerr("%s: action failed\n", myname);
1236 		exit(1);
1237 	}
1238 	if (nextst == PCSMS_CONVERS) {
1239 		if (verbose)
1240 			logerr("%s: connected\n", myname);
1241 		exit(0);
1242 	}
1243 }
1244 
1245 /*
1246  * Check for error message tags in the PPPoE packet.  We must ignore
1247  * offers that merely report errors, and need to log errors in any
1248  * case.
1249  */
1250 static int
1251 error_check(poemsg_t *pmsg)
1252 {
1253 	const uint8_t *tagp;
1254 	int ttyp;
1255 
1256 	tagp = (const uint8_t *)(pmsg->poemsg_data + 1);
1257 	while (poe_tagcheck(pmsg->poemsg_data, pmsg->poemsg_len, tagp)) {
1258 		ttyp = POET_GET_TYPE(tagp);
1259 		if (ttyp == POETT_END)
1260 			break;
1261 		if (ttyp == POETT_NAMERR || ttyp == POETT_SYSERR ||
1262 		    ttyp == POETT_GENERR) {
1263 			if (verbose)
1264 				display_pppoe(stderr, pmsg->poemsg_data,
1265 				    pmsg->poemsg_len, &pmsg->poemsg_sender);
1266 			return (-1);
1267 		}
1268 		tagp = POET_NEXT(tagp);
1269 	}
1270 	return (0);
1271 }
1272 
1273 /*
1274  * Extract sequence number, if any, from PADS message, so that we can
1275  * relate it to the PADR that we sent.
1276  */
1277 static uint32_t
1278 get_sequence(const poemsg_t *pmsg)
1279 {
1280 	const uint8_t *tagp;
1281 	int ttyp;
1282 	uint32_t vals[2];
1283 
1284 	tagp = (const uint8_t *)(pmsg->poemsg_data + 1);
1285 	while (poe_tagcheck(pmsg->poemsg_data, pmsg->poemsg_len, tagp)) {
1286 		ttyp = POET_GET_TYPE(tagp);
1287 		if (ttyp == POETT_END)
1288 			break;
1289 		if (ttyp == POETT_UNIQ) {
1290 			if (POET_GET_LENG(tagp) < sizeof (vals))
1291 				break;
1292 			(void) memcpy(vals, POET_DATA(tagp), sizeof (vals));
1293 			return (ntohl(vals[1]));
1294 		}
1295 		tagp = POET_NEXT(tagp);
1296 	}
1297 	return (0);
1298 }
1299 
1300 /*
1301  * Server filter cases:
1302  *
1303  *	No filters -- all servers generate RPADO+ event; select the
1304  *	first responding server.
1305  *
1306  *	Only "except" filters -- matching servers are RPADO, others
1307  *	are RPADO+.
1308  *
1309  *	Mix of filters -- those matching "pass" are RPADO+, those
1310  *	matching "except" are RPADO, and all others are also RPADO.
1311  *
1312  * If the "only" keyword was given, then RPADO becomes -1; only RPADO+
1313  * events occur.
1314  */
1315 static int
1316 use_server(poemsg_t *pado)
1317 {
1318 	struct server_filter *sfp;
1319 	const uchar_t *sndp;
1320 	const uchar_t *macp;
1321 	const uchar_t *maskp;
1322 	int i;
1323 	int passmatched;
1324 	const uint8_t *tagp;
1325 	int ttyp;
1326 
1327 	/*
1328 	 * If no service mentioned in offer, then we can't use it.
1329 	 */
1330 	tagp = (const uint8_t *)(pado->poemsg_data + 1);
1331 	ttyp = POETT_END;
1332 	while (poe_tagcheck(pado->poemsg_data, pado->poemsg_len, tagp)) {
1333 		ttyp = POET_GET_TYPE(tagp);
1334 		if (ttyp == POETT_END || ttyp == POETT_SERVICE)
1335 			break;
1336 		tagp = POET_NEXT(tagp);
1337 	}
1338 	if (ttyp != POETT_SERVICE)
1339 		return (-1);
1340 
1341 	passmatched = 0;
1342 	for (sfp = sfhead; sfp != NULL; sfp = sfp->sf_next) {
1343 		passmatched |= !sfp->sf_isexcept;
1344 		if (sfp->sf_hasmac) {
1345 			sndp = pado->poemsg_sender.pta_pppoe.ptma_mac;
1346 			macp = sfp->sf_mac.ether_addr_octet;
1347 			maskp = sfp->sf_mask.ether_addr_octet;
1348 			i = sizeof (pado->poemsg_sender.pta_pppoe.ptma_mac);
1349 			for (; i > 0; i--)
1350 				if (((*macp++ ^ *sndp++) & *maskp++) != 0)
1351 					break;
1352 			if (i <= 0)
1353 				break;
1354 		}
1355 	}
1356 
1357 	if (sfp == NULL) {
1358 		/*
1359 		 * No match encountered; if only exclude rules have
1360 		 * been seen, then accept this offer.
1361 		 */
1362 		if (!passmatched)
1363 			return (PCSME_RPADOP);
1364 	} else {
1365 		if (!sfp->sf_isexcept)
1366 			return (PCSME_RPADOP);
1367 	}
1368 	if (onlyflag)
1369 		return (-1);
1370 	return (PCSME_RPADO);
1371 }
1372 
1373 /*
1374  * This is the normal event loop.  It initializes the state machine
1375  * and sends in an Open event to kick things off.  Then it drops into
1376  * a loop to dispatch events for the state machine.
1377  */
1378 static void
1379 find_server(int localid)
1380 {
1381 	poesm_t psm;
1382 	struct pollfd pfd[1];
1383 	struct timeval tv, tvnow;
1384 	int retv;
1385 	poemsg_t pmsg;
1386 	struct strbuf ctrl;
1387 	struct strbuf data;
1388 	poep_t *poep;
1389 	int flags;
1390 	uint32_t seqval;
1391 	struct ppptun_control *ptc;
1392 
1393 	(void) memset(&psm, '\0', sizeof (psm));
1394 
1395 	/*
1396 	 * Initialize the sequence number with something handy.  It
1397 	 * doesn't need to be absolutely unique, since the localid
1398 	 * value actually demultiplexes everything.  This just makes
1399 	 * the operation a little safer.
1400 	 */
1401 	psm.poesm_sequence = getpid() << 16;
1402 	psm.poesm_localid = localid;
1403 
1404 	/* Start the state machine */
1405 	handle_event(&psm, PCSME_OPEN, NULL);
1406 
1407 	/* Enter event polling loop. */
1408 	pfd[0].fd = tunfd;
1409 	pfd[0].events = POLLIN;
1410 	for (;;) {
1411 		/* Wait for timeout or message */
1412 		retv = poll(pfd, 1, psm.poesm_timer > 0 ? psm.poesm_timer :
1413 		    INFTIM);
1414 		if (retv < 0) {
1415 			logstrerror("poll");
1416 			break;
1417 		}
1418 
1419 		/* Handle a timeout */
1420 		if (retv == 0) {
1421 			psm.poesm_timer = 0;
1422 			handle_event(&psm, --psm.poesm_count > 0 ? PCSME_TOP :
1423 			    PCSME_TOM, NULL);
1424 			continue;
1425 		}
1426 
1427 		/* Adjust the timer for the time we slept. */
1428 		if (psm.poesm_timer > 0) {
1429 			(void) gettimeofday(&tvnow, NULL);
1430 			tv = tvnow;
1431 			if ((tv.tv_sec -= tvstart.tv_sec) < 0) {
1432 				/* Darn */
1433 				tv.tv_sec = 1;
1434 				tv.tv_usec = 0;
1435 			} else if ((tv.tv_usec -= tvstart.tv_usec) < 0) {
1436 				tv.tv_usec += 1000000;
1437 				if (--tv.tv_sec < 0)
1438 					tv.tv_sec = 0;
1439 			}
1440 			psm.poesm_timer -= tv.tv_sec*1000 + tv.tv_usec/1000;
1441 			tvstart = tvnow;
1442 		}
1443 
1444 		/* Read in the message from the server. */
1445 		ctrl.maxlen = PKT_OCTL_LEN;
1446 		ctrl.buf = (caddr_t)pkt_octl;
1447 		data.maxlen = PKT_INPUT_LEN;
1448 		data.buf = (caddr_t)pkt_input;
1449 		flags = 0;
1450 
1451 		if (pppoec_getmsg(tunfd, &ctrl, &data, &flags) < 0)
1452 			break;
1453 
1454 		if (ctrl.len != sizeof (*ptc)) {
1455 			if (verbose)
1456 				logerr("%s: discard: ctrl len %d\n", myname,
1457 				    ctrl.len);
1458 			continue;
1459 		}
1460 		poep = (poep_t *)pkt_input;
1461 		(void) memset(&pmsg, '\0', sizeof (pmsg));
1462 		pmsg.poemsg_next = NULL;
1463 		pmsg.poemsg_data = poep;
1464 		pmsg.poemsg_len = data.len;
1465 		ptc = (struct ppptun_control *)pkt_octl;
1466 		if (ptc->ptc_action != PTCA_CONTROL) {
1467 			if (verbose)
1468 				logerr("%s: discard: unexpected action %d\n",
1469 				    myname, ptc->ptc_action);
1470 			continue;
1471 		}
1472 		pmsg.poemsg_iname = ptc->ptc_name;
1473 		if (verbose)
1474 			logerr("%s: Received %s from %s/%s\n",
1475 			    myname, poe_codename(poep->poep_code),
1476 			    ehost(&ptc->ptc_address), pmsg.poemsg_iname);
1477 		pmsg.poemsg_sender = ptc->ptc_address;
1478 
1479 		/* Check for messages from unexpected peers. */
1480 		if ((poep->poep_code == POECODE_PADT ||
1481 		    poep->poep_code == POECODE_PADS) &&
1482 		    (psm.poesm_firstoff == NULL ||
1483 			memcmp(&psm.poesm_firstoff->poemsg_sender,
1484 			    &pmsg.poemsg_sender,
1485 			    sizeof (pmsg.poemsg_sender)) != 0)) {
1486 			if (verbose) {
1487 				logerr("%s: Unexpected peer %s", myname,
1488 				    ehost(&ptc->ptc_address));
1489 				logerr(" != %s\n",
1490 				    ehost(&psm.poesm_firstoff->poemsg_sender));
1491 			}
1492 			continue;
1493 		}
1494 
1495 		/* Eliminate stale PADS responses. */
1496 		if (poep->poep_code == POECODE_PADS) {
1497 			seqval = get_sequence(&pmsg);
1498 			if (seqval != psm.poesm_sequence) {
1499 				if (verbose) {
1500 					if (seqval == 0)
1501 						logerr(
1502 						    "%s: PADS has no sequence "
1503 						    "number.\n", myname);
1504 					else
1505 						logerr(
1506 						    "%s: PADS has sequence "
1507 						    "%08X instead of %08X.\n",
1508 						    myname, seqval,
1509 						    psm.poesm_sequence);
1510 				}
1511 				continue;
1512 			}
1513 		}
1514 
1515 		/* Dispatch message event. */
1516 		retv = error_check(&pmsg);
1517 		switch (poep->poep_code) {
1518 		case POECODE_PADT:
1519 			handle_event(&psm, PCSME_RPADT, &pmsg);
1520 			break;
1521 		case POECODE_PADS:
1522 			/*
1523 			 * Got a PPPoE Active Discovery Session-
1524 			 * confirmation message.  It's a PADS event if
1525 			 * everything's in order.  It's a PADS- event
1526 			 * if the message is merely reporting an
1527 			 * error.
1528 			 */
1529 			handle_event(&psm, retv != 0 ? PCSME_RPADSN :
1530 			    PCSME_RPADS, &pmsg);
1531 			break;
1532 		case POECODE_PADO:
1533 			/* Ignore offers that merely report errors. */
1534 			if (retv != 0)
1535 				break;
1536 			/* Ignore offers from servers we don't want. */
1537 			if ((retv = use_server(&pmsg)) < 0)
1538 				break;
1539 			/* Dispatch either RPADO or RAPDO+ event. */
1540 			handle_event(&psm, retv, &pmsg);
1541 			break;
1542 
1543 		default:
1544 			if (verbose)
1545 				logerr("%s: Unexpected code %s (%d)\n", myname,
1546 				    poe_codename(poep->poep_code),
1547 				    poep->poep_code);
1548 			break;
1549 		}
1550 	}
1551 	exit(1);
1552 }
1553 
1554 static void
1555 usage(void)
1556 {
1557 	logerr("Usage:\n"
1558 	    "\t%s [-os#] [-v] <dev> [<service> [<server> [only]]]\n\n"
1559 	    "    or\n\n"
1560 	    "\t%s [-o#] [-v] -i <dev>\n", myname, myname);
1561 	exit(1);
1562 }
1563 
1564 /*
1565  * In addition to the usual 0-2 file descriptors, pppd will leave fd 3
1566  * open on a pipe to receive the environment variables.  See
1567  * pppoe_device_pipe() in pppd/plugins/pppoe.c and device_pipe_hook in
1568  * pppd/main.c.
1569  */
1570 int
1571 main(int argc, char **argv)
1572 {
1573 	int inquiry_mode, exceptflag, arg, localid;
1574 	char *cp;
1575 
1576 	log_to_stderr(LOGLVL_DBG);
1577 
1578 	if ((myname = *argv) == NULL)
1579 		myname = "pppoec";
1580 
1581 	inquiry_mode = 0;
1582 	while ((arg = getopt(argc, argv, "io:s:v")) != EOF)
1583 		switch (arg) {
1584 		case 'i':
1585 			inquiry_mode++;
1586 			break;
1587 		case 'v':
1588 			verbose++;
1589 			break;
1590 		case 'o':
1591 			pado_wait_time = strtol(optarg, &cp, 0);
1592 			if (pado_wait_time <= 0 || *cp != '\0' ||
1593 			    cp == optarg) {
1594 				logerr("%s: illegal PADO wait time: %s\n",
1595 				    myname, optarg);
1596 				exit(1);
1597 			}
1598 			break;
1599 		case 's':
1600 			pads_wait_time = strtol(optarg, &cp, 0);
1601 			if (pads_wait_time <= 0 || *cp != '\0' ||
1602 			    cp == optarg) {
1603 				logerr("%s: illegal PADS wait time: %s\n",
1604 				    myname, optarg);
1605 				exit(1);
1606 			}
1607 			break;
1608 		case '?':
1609 			usage();
1610 		}
1611 
1612 	/* Handle inquiry mode. */
1613 	if (inquiry_mode) {
1614 		if (optind != argc-1)
1615 			usage();
1616 		if (pado_wait_time == 0)
1617 			pado_wait_time = PADI_INQUIRY_DWELL;
1618 
1619 		/* Invoked by user; open the tunnel driver myself. */
1620 		tunfd = open(tunnam, O_RDWR | O_NOCTTY);
1621 		if (tunfd == -1) {
1622 			logstrerror(tunnam);
1623 			exit(1);
1624 		}
1625 
1626 		/*
1627 		 * Set up the control stream for PPPoE negotiation
1628 		 * (set_control), then broadcast a query for all servers
1629 		 * and listen for replies (find_all_servers).
1630 		 */
1631 		find_all_servers(set_control(argv[optind]));
1632 		return (0);
1633 	}
1634 
1635 	if (pado_wait_time == 0)
1636 		pado_wait_time = PADI_RESTART_TIME;
1637 
1638 	if (optind >= argc)
1639 		usage();
1640 
1641 	/* Make sure we've got a usable tunnel driver on stdin. */
1642 	check_stdin();
1643 
1644 	/* Set up the control stream for PPPoE negotiation. */
1645 	localid = set_control(argv[optind++]);
1646 
1647 	/* Pick the service, if any. */
1648 	if (optind < argc)
1649 		service = argv[optind++];
1650 
1651 	/* Parse out the filters. */
1652 	if (optind < argc) {
1653 		if (strcasecmp(argv[argc - 1], "only") == 0) {
1654 			argc--;
1655 			onlyflag = 1;
1656 		}
1657 		exceptflag = 0;
1658 		for (; optind < argc; optind++) {
1659 			if (!exceptflag &&
1660 			    strcasecmp(argv[optind], "except") == 0) {
1661 				exceptflag = 1;
1662 			} else {
1663 				parse_filter(argv[optind], exceptflag);
1664 				exceptflag = 0;
1665 			}
1666 		}
1667 	}
1668 
1669 	/* Enter the main loop. */
1670 	find_server(localid);
1671 
1672 	return (0);
1673 }
1674