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