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