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