/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* PPPoE Client-mode "chat" utility for use with Solaris PPP 4.0.
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <strings.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stropts.h>
#include <netdb.h>
#include <netinet/if_ether.h>
#include "common.h"
#include "logging.h"
/*
* This value, currently set to the characters "POE1," is used to
* distinguish among control messages from multiple lower streams
* (LAC-like behavior), but isn't currently used.
*/
/* milliseconds between retries */
/* default inquiry mode timer in milliseconds. */
/* maximum timer value in milliseconds */
struct server_filter {
};
/* List of filters defined on command line. */
/*
* PPPoE Client State Machine
*/
/* Client events */
/* Client states */
/* Client actions */
/* 0 PCSMS_DEAD Initial state */
{
PCSMS_DEAD, /* PCSME_CLOSE User close */
PCSMS_INITSENT, /* PCSME_OPEN User open */
PCSMS_DEAD, /* PCSME_TOP Timeout+ */
PCSMS_DEAD, /* PCSME_TOM Timeout- */
PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
PCSMS_DEAD, /* PCSME_RPADOP Receive desired PADO */
PCSMS_DEAD, /* PCSME_RPADO Receive ordinary PADO */
PCSMS_DEAD, /* PCSME_RPADS Receive PADS */
PCSMS_DEAD, /* PCSME_RPADSN Receive bad PADS */
},
/* 1 PCSMS_INITSENT PADI sent */
{
PCSMS_DEAD, /* PCSME_CLOSE User close */
PCSMS_INITSENT, /* PCSME_OPEN User open */
PCSMS_INITSENT, /* PCSME_TOP Timeout+ */
PCSMS_DEAD, /* PCSME_TOM Timeout- */
PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
PCSMS_REQSENT, /* PCSME_RPADOP Receive desired PADO */
PCSMS_OFFRRCVD, /* PCSME_RPADO Receive ordinary PADO */
PCSMS_INITSENT, /* PCSME_RPADS Receive PADS */
PCSMS_INITSENT, /* PCSME_RPADSN Receive bad PADS */
},
/* 2 PCSMS_OFFRRCVD PADO received */
{
PCSMS_DEAD, /* PCSME_CLOSE User close */
PCSMS_INITSENT, /* PCSME_OPEN User open */
PCSMS_REQSENT, /* PCSME_TOP Timeout+ */
PCSMS_REQSENT, /* PCSME_TOM Timeout- */
PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
PCSMS_REQSENT, /* PCSME_RPADOP Receive desired PADO */
PCSMS_OFFRRCVD, /* PCSME_RPADO Receive ordinary PADO */
PCSMS_OFFRRCVD, /* PCSME_RPADS Receive PADS */
PCSMS_OFFRRCVD, /* PCSME_RPADSN Receive bad PADS */
},
/* 3 PCSMS_REQSENT PADR sent */
{
PCSMS_DEAD, /* PCSME_CLOSE User close */
PCSMS_INITSENT, /* PCSME_OPEN User open */
PCSMS_REQSENT, /* PCSME_TOP Timeout+ */
PCSMS_REQSENT, /* PCSME_TOM Timeout- */
PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
PCSMS_REQSENT, /* PCSME_RPADOP Receive desired PADO */
PCSMS_REQSENT, /* PCSME_RPADO Receive ordinary PADO */
PCSMS_CONVERS, /* PCSME_RPADS Receive PADS */
PCSMS_REQSENT, /* PCSME_RPADSN Receive bad PADS */
},
/* 4 PCSMS_CONVERS Conversational */
{
PCSMS_DEAD, /* PCSME_CLOSE User close */
PCSMS_INITSENT, /* PCSME_OPEN User open */
PCSMS_CONVERS, /* PCSME_TOP Timeout+ */
PCSMS_CONVERS, /* PCSME_TOM Timeout- */
PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
PCSMS_CONVERS, /* PCSME_RPADOP Receive desired PADO */
PCSMS_CONVERS, /* PCSME_RPADO Receive ordinary PADO */
PCSMS_CONVERS, /* PCSME_RPADS Receive PADS */
PCSMS_CONVERS, /* PCSME_RPADSN Receive bad PADS */
},
};
/* 0 PCSMS_DEAD Initial state */
{
PCSMA_NONE, /* PCSME_CLOSE User close */
PCSMA_SPADI, /* PCSME_OPEN User open */
PCSMA_NONE, /* PCSME_TOP Timeout+ */
PCSMA_NONE, /* PCSME_TOM Timeout- */
PCSMA_NONE, /* PCSME_RPADT Receive PADT */
PCSMA_NONE, /* PCSME_RPADOP Receive desired PADO */
PCSMA_NONE, /* PCSME_RPADO Receive ordinary PADO */
PCSMA_NONE, /* PCSME_RPADS Receive PADS */
PCSMA_NONE, /* PCSME_RPADSN Receive bad PADS */
},
/* 1 PCSMS_INITSENT PADI sent */
{
PCSMA_FAIL, /* PCSME_CLOSE User close */
PCSMA_SPADI, /* PCSME_OPEN User open */
PCSMA_SPADI, /* PCSME_TOP Timeout+ */
PCSMA_FAIL, /* PCSME_TOM Timeout- */
PCSMA_FAIL, /* PCSME_RPADT Receive PADT */
PCSMA_SPADRP, /* PCSME_RPADOP Receive desired PADO */
PCSMA_ADD, /* PCSME_RPADO Receive ordinary PADO */
PCSMA_NONE, /* PCSME_RPADS Receive PADS */
PCSMA_NONE, /* PCSME_RPADSN Receive bad PADS */
},
/* 2 PCSMS_OFFRRCVD PADO received */
{
PCSMA_FAIL, /* PCSME_CLOSE User close */
PCSMA_SPADI, /* PCSME_OPEN User open */
PCSMA_SPADR, /* PCSME_TOP Timeout+ */
PCSMA_SPADR, /* PCSME_TOM Timeout- */
PCSMA_FAIL, /* PCSME_RPADT Receive PADT */
PCSMA_SPADRP, /* PCSME_RPADOP Receive desired PADO */
PCSMA_ADD, /* PCSME_RPADO Receive ordinary PADO */
PCSMA_NONE, /* PCSME_RPADS Receive PADS */
PCSMA_NONE, /* PCSME_RPADSN Receive bad PADS */
},
/* 3 PCSMS_REQSENT PADR sent */
{
PCSMA_FAIL, /* PCSME_CLOSE User close */
PCSMA_SPADI, /* PCSME_OPEN User open */
PCSMA_SPADR, /* PCSME_TOP Timeout+ */
PCSMA_SPADRN, /* PCSME_TOM Timeout- */
PCSMA_FAIL, /* PCSME_RPADT Receive PADT */
PCSMA_ADD, /* PCSME_RPADOP Receive desired PADO */
PCSMA_ADD, /* PCSME_RPADO Receive ordinary PADO */
PCSMA_OPEN, /* PCSME_RPADS Receive PADS */
PCSMA_SPADRN, /* PCSME_RPADSN Receive bad PADS */
},
/* 4 PCSMS_CONVERS Conversational */
{
PCSMA_FAIL, /* PCSME_CLOSE User close */
PCSMA_SPADI, /* PCSME_OPEN User open */
PCSMA_FAIL, /* PCSME_TOP Timeout+ */
PCSMA_FAIL, /* PCSME_TOM Timeout- */
PCSMA_FAIL, /* PCSME_RPADT Receive PADT */
PCSMA_NONE, /* PCSME_RPADOP Receive desired PADO */
PCSMA_NONE, /* PCSME_RPADO Receive ordinary PADO */
PCSMA_NONE, /* PCSME_RPADS Receive PADS */
PCSMA_NONE, /* PCSME_RPADSN Receive bad PADS */
},
};
/*
* PPPoE Message structure -- holds data from a received PPPoE
* message. These are copied and saved when queuing offers from
* possible servers.
*/
typedef struct poesm_s {
} poemsg_t;
/*
* PPPoE State Machine structure -- holds state of PPPoE negotiation;
* currently, there's exactly one of these per pppoec instance.
*/
typedef struct {
} poesm_t;
/*
* Convert an internal PPPoE event code number into a printable
* string.
*/
static const char *
{
"Close", "Open", "TO+", "TO-", "rPADT",
"rPADO+", "rPADO", "rPADS", "rPADS-"
};
return ("?");
}
}
/*
* Convert an internal PPPoE state number into a printable string.
*/
static const char *
{
"Dead", "InitSent", "OffrRcvd", "ReqSent", "Convers",
};
return ("?");
}
}
/*
* Convert an internal PPPoE action number into a printable string.
*/
static const char *
{
"None", "Fail", "SendPADI", "Add", "SendPADR",
"SendPADR+", "SendPADR-", "Open"
};
return ("?");
}
}
/*
* This calls mygetmsg (which discards partial messages as needed) and
* logs errors as appropriate.
*/
static int
{
int retv;
for (;;) {
if (retv == 0)
break;
if (retv < 0) {
continue;
logstrerror("getmsg");
break;
}
if (verbose) {
logerr("%s: discard: "
else
logerr("%s: discard: "
"truncated %s%smessage\n", myname,
}
}
return (retv);
}
/*
* Connect the control path to the lower stream of interest. This
* must be called after opening the tunnel driver in order to
* establish the interface to be used for signaling. Returns local
* session ID number.
*/
static int
{
/* Fetch the local session ID first. */
0) {
logstrerror("PPPTUN_SPEER");
exit(1);
}
/* Connect to lower stream. */
dname);
exit(1);
}
return (ptp.ptp_lsessid);
}
/*
* Check if standard input is actually a viable connection to the
* tunnel driver. This is the normal mode of operation with pppd; the
* tunnel driver is opened by pppd as the tty and pppoec is exec'd as
* the connect script.
*/
static void
check_stdin(void)
{
logerr("%s: PPPoE operation requires "
"the use of a tunneling device\n", myname);
else
logstrerror("PPPTUN_GDATA");
exit(1);
}
logstrerror("PPPTUN_GINFO");
exit(1);
}
logerr("%s: Cannot connect to server "
"using PPPoE; stream already set to style %d\n",
exit(1);
}
if (verbose)
logerr("%s: Warning: PPPoE data link "
"already connected\n", myname);
exit(0);
}
/* Standard input is the tunnel driver; use it. */
tunfd = 0;
}
/*
* Write a summary of a PPPoE message to the given file. This is used
* for logging and to display received offers in the inquiry (-i) mode.
*/
static void
{
int ttyp;
int tlen;
const char *str;
/* Print name of sender. */
/* Loop through tags and print each. */
break;
switch (ttyp) {
case POETT_SERVICE: /* Service-Name */
str = "Svc";
break;
case POETT_ACCESS: /* AC-Name */
str = "Name";
break;
case POETT_UNIQ: /* Host-Uniq */
str = "Uniq";
break;
case POETT_COOKIE: /* AC-Cookie */
str = "Cookie";
break;
case POETT_VENDOR: /* Vendor-Specific */
break;
case POETT_RELAY: /* Relay-Session-Id */
str = "Relay";
break;
case POETT_NAMERR: /* Service-Name-Error */
str = "SvcNameErr";
break;
case POETT_SYSERR: /* AC-System-Error */
str = "SysErr";
break;
case POETT_GENERR: /* Generic-Error */
str = "GenErr";
break;
case POETT_MULTI: /* Multicast-Capable */
break;
case POETT_HURL: /* Host-URL */
str = "URL";
break;
case POETT_MOTM: /* Message-Of-The-Minute */
str = "Mesg";
break;
case POETT_RTEADD: /* IP-Route-Add */
break;
}
switch (ttyp) {
case POETT_NAMERR: /* Service-Name-Error */
case POETT_SYSERR: /* AC-System-Error */
tlen = 0;
/* FALLTHROUGH */
case POETT_SERVICE: /* Service-Name */
case POETT_ACCESS: /* AC-Name */
case POETT_GENERR: /* Generic-Error */
case POETT_MOTM: /* Message-Of-The-Minute */
case POETT_HURL: /* Host-URL */
break;
case POETT_UNIQ: /* Host-Uniq */
case POETT_COOKIE: /* AC-Cookie */
case POETT_RELAY: /* Relay-Session-Id */
while (--tlen >= 0)
break;
case POETT_VENDOR: /* Vendor-Specific */
if (tlen >= 4) {
if (*dp++ != 0) {
}
dp[2]);
tlen -= 4;
dp += 3;
}
while (--tlen >= 0)
break;
case POETT_MULTI: /* Multicast-Capable */
break;
case POETT_RTEADD: /* IP-Route-Add */
break;
}
if (poer.poer_dest_network == 0)
else
out);
else
}
break;
default:
break;
}
}
}
/*
* Transmit a PPPoE message to the indicated destination. Used for
* PADI and PADR messages.
*/
static int
const ppptun_atype *destaddr)
{
/* Set up the control data expected by the driver. */
if (verbose)
logerr("%s: Sending %s to %s: %d bytes\n",
logstrerror("putmsg");
return (-1);
}
return (0);
}
/*
* Create and transmit a PPPoE Active Discovery Initiation packet.
* This is broadcasted to all hosts on the LAN.
*/
static int
{
}
/*
* This is used by the procedure below -- when the alarm goes off,
* just exit. (This was once a dummy procedure and used the EINTR
* side-effect to terminate the loop, but that's not reliable, since
* the EINTR could be caught and ignored by the calls to standard
* output.)
*/
/* ARGSUSED */
static void
{
exit(0);
}
/*
* Send out a single PADI and listen for servers. This implements the
* "inquiry" (-i) mode.
*/
static void
{
int flags;
/* Set a default 3-second timer */
/* Broadcast a single request. */
return;
/* Loop over responses and print them. */
for (;;) {
flags = 0;
break;
/* Ignore unwanted responses from the driver. */
if (verbose)
logerr("%s: unexpected %d byte"
" control message from driver.\n", myname,
continue;
}
/* If it's an offer, then print it out. */
&ptc->ptc_address);
}
}
}
/*
* Parse a server filter from the command line. The passed-in string
* must be allocated and unchanged, since a pointer to it is saved in
* the filter data structure. The string is also parsed for a MAC
* address, if possible.
*/
static void
{
const char *cp;
const char *wordstart;
const char *wordend;
int len;
/* Allocate the new filter structure. */
logstrerror("filter allocation");
exit(1);
}
/* Save the string for AC-Name comparison. */
/* Extract just one word. */
cp++;
cp++;
/* Try to translate this as an Ethernet host or address. */
} else {
break;
if (*cp == '*') {
cp++;
} else {
break;
}
ucp++;
*mcp++ = 0xFF;
}
break;
cp++;
}
}
else if (verbose)
logerr("%s: treating '%.*s' as server "
}
/* Add to end of list. */
else
}
/*
* Create a copy of a given PPPoE message. This is used for enqueuing
* received PADO (offers) from possible servers.
*/
static poemsg_t *
{
char *cp;
sizeof (newmsg->poemsg_sender));
}
return (newmsg);
}
/*
* Create and send a PPPoE Active Discovery Request (PADR) message to
* the sender of the given PADO. Some tags -- Service-Name,
* AC-Cookie, and Relay-Session-Id -- must be copied from PADO to
* PADR. Others are not. The Service-Name must be selected from the
* offered services in the PADO based on the user's requested service
* name. If the server offered "wildcard" service, then we ask for
* this only if we can't find the user's requested service.
*
* Returns 1 if we can't send a valid PADR in response to the given
* PADO. The offer should be ignored and the next one tried.
*/
static int
{
int ttyp;
int tlen;
/*
* Increment sequence number for PADR so that we don't mistake
* old replies for valid ones if the server is very slow.
*/
psm->poesm_sequence++;
break;
switch (ttyp) {
case POETT_SERVICE: /* Service-Name */
/* Allow only one */
if (hassvc)
break;
if (tlen == 0) {
break;
}
if (service[0] == '\0' ||
}
break;
/* Ones we should discard */
case POETT_ACCESS: /* AC-Name */
case POETT_UNIQ: /* Host-Uniq */
case POETT_NAMERR: /* Service-Name-Error */
case POETT_SYSERR: /* AC-System-Error */
case POETT_GENERR: /* Generic-Error */
case POETT_HURL: /* Host-URL */
case POETT_MOTM: /* Message-Of-The-Minute */
case POETT_RTEADD: /* IP-Route-Add */
case POETT_VENDOR: /* Vendor-Specific */
case POETT_MULTI: /* Multicast-Capable */
default: /* Anything else we don't understand */
break;
/* Ones we should copy */
case POETT_COOKIE: /* AC-Cookie */
case POETT_RELAY: /* Relay-Session-Id */
break;
}
}
if (!hassvc) {
else
return (1);
}
}
/*
* ********************************************************************
* act_* functions implement the actions driven by the state machine
* tables. See "action_table" below.
*
* All action routines must return the next state value.
* ********************************************************************
*/
/* ARGSUSED */
static int
{
return (nextst);
}
/* ARGSUSED */
static int
{
if (verbose)
return (PCSMS_DEAD);
}
/* ARGSUSED */
static int
{
return (PCSMS_DEAD);
/*
* If this is the first time, then initialize the retry count
* and interval.
*/
} else {
}
return (nextst);
}
/* ARGSUSED */
static int
{
else
}
return (nextst);
}
/* ARGSUSED */
static int
{
int retv;
for (;;) {
return (PCSMS_DEAD);
if (retv < 0)
return (PCSMS_DEAD);
if (retv == 0)
break;
/* Can't send this request; try looking at next offer. */
}
} else {
}
return (nextst);
}
/* ARGSUSED */
static int
{
int retv;
if (retv < 0)
return (PCSMS_DEAD);
if (retv > 0) {
/*
* Cannot use this one; mark as tried and continue as
* if we never saw it.
*/
return (psm->poesm_state);
}
return (nextst);
}
/* ARGSUSED */
static int
{
int retv;
return (PCSMS_DEAD);
do {
return (PCSMS_DEAD);
if (retv < 0)
return (PCSMS_DEAD);
} while (retv != 0);
return (nextst);
}
/*
* For safety -- remove end of line from strings passed back to pppd.
*/
static void
{
while (len > 0) {
if (*str == '\n')
*str = '$';
str++;
len--;
}
}
/* ARGSUSED */
static int
{
const char *cp;
char *access;
/*
* The server has now assigned its session ID for the data
* (PPP) portion of this tunnel. Send that ID down to the
* driver.
*/
sizeof (ptp.ptp_address));
0) {
logstrerror("PPPTUN_SPEER");
return (PCSMS_DEAD);
}
/*
* Data communication is now possible on this session.
* Connect the data portion to the correct lower stream.
*/
logerr("%s: PPPTUN_SDATA %s: %s\n",
return (PCSMS_DEAD);
}
if (verbose)
logerr("%s: Connection open; session %04X on "
/*
* Walk through the PADS message to get the access server name
* and the service. If there are multiple instances of either
* tag, then take the last access server and the first
* non-null service.
*/
access = "";
acc_len = 0;
break;
if (ttyp == POETT_ACCESS) {
}
}
}
/*
* Remove end of line to make sure that integrity of values
* passed back to pppd can't be compromised by the PPPoE
* server.
*/
/*
* pppd has given us a pipe as fd 3, and we're expected to
* write out the values of the following environment
* variables:
* IF_AND_SERVICE
* SERVICE_NAME
* AC_NAME
* AC_MAC
* SESSION_ID
* VENDOR_SPECIFIC_1 ... N
*/
service);
tagp)) {
break;
tlen -= 4;
while (--tlen >= 0)
}
}
}
return (nextst);
}
};
/*
* Dispatch an event and a corresponding message on a given state
* machine.
*/
static void
{
int nextst;
if (verbose)
logerr("%s: PPPoE Event %s (%d) in state %s "
if (verbose)
/*
* Life-altering states are handled here. If we reach dead
* state again after starting, then we failed. If we reach
* conversational state, then we're open.
*/
if (nextst == PCSMS_DEAD) {
if (verbose)
exit(1);
}
if (nextst == PCSMS_CONVERS) {
if (verbose)
exit(0);
}
}
/*
* Check for error message tags in the PPPoE packet. We must ignore
* offers that merely report errors, and need to log errors in any
* case.
*/
static int
{
int ttyp;
break;
ttyp == POETT_GENERR) {
if (verbose)
return (-1);
}
}
return (0);
}
/*
* Extract sequence number, if any, from PADS message, so that we can
* relate it to the PADR that we sent.
*/
static uint32_t
{
int ttyp;
break;
if (ttyp == POETT_UNIQ) {
break;
}
}
return (0);
}
/*
* Server filter cases:
*
* No filters -- all servers generate RPADO+ event; select the
* first responding server.
*
* Only "except" filters -- matching servers are RPADO, others
* are RPADO+.
*
* Mix of filters -- those matching "pass" are RPADO+, those
* matching "except" are RPADO, and all others are also RPADO.
*
* If the "only" keyword was given, then RPADO becomes -1; only RPADO+
* events occur.
*/
static int
{
int i;
int passmatched;
int tlen;
int ttyp;
/*
* If no service mentioned in offer, then we can't use it.
*/
break;
if (ttyp == POETT_SERVICE) {
/*
* If the user has requested a specific service, then
* this selection is exclusive. We never use the
* wildcard for this.
*/
break;
/* just in case we run off the end */
}
}
if (ttyp != POETT_SERVICE) {
if (verbose)
logerr("%s: Discard unusable offer from %s; service "
return (-1);
}
passmatched = 0;
for (; i > 0; i--)
break;
if (i <= 0)
break;
}
}
/*
* No match encountered; if only exclude rules have
* been seen, then accept this offer.
*/
if (!passmatched)
return (PCSME_RPADOP);
} else {
if (!sfp->sf_isexcept)
return (PCSME_RPADOP);
}
if (onlyflag) {
if (verbose)
logerr("%s: Discard unusable offer from %s; server not "
return (-1);
}
return (PCSME_RPADO);
}
/*
* This is the normal event loop. It initializes the state machine
* and sends in an Open event to kick things off. Then it drops into
* a loop to dispatch events for the state machine.
*/
static void
{
int retv;
int flags;
/*
* Initialize the sequence number with something handy. It
* doesn't need to be absolutely unique, since the localid
* value actually demultiplexes everything. This just makes
* the operation a little safer.
*/
/* Start the state machine */
/* Enter event polling loop. */
for (;;) {
/* Wait for timeout or message */
INFTIM);
if (retv < 0) {
logstrerror("poll");
break;
}
/* Handle a timeout */
if (retv == 0) {
psm.poesm_timer = 0;
continue;
}
/* Adjust the timer for the time we slept. */
if (psm.poesm_timer > 0) {
/* Darn */
}
}
/* Read in the message from the server. */
flags = 0;
break;
if (verbose)
continue;
}
if (verbose)
logerr("%s: discard: unexpected action %d\n",
continue;
}
if (verbose)
logerr("%s: Received %s from %s/%s\n",
/* Check for messages from unexpected peers. */
if (verbose) {
logerr(" != %s\n",
}
continue;
}
/* Eliminate stale PADS responses. */
if (verbose) {
if (seqval == 0)
"%s: PADS has no sequence "
"number.\n", myname);
else
"%s: PADS has sequence "
"%08X instead of %08X.\n",
}
continue;
}
}
/* Dispatch message event. */
case POECODE_PADT:
break;
case POECODE_PADS:
/*
* Got a PPPoE Active Discovery Session-
* confirmation message. It's a PADS event if
* everything's in order. It's a PADS- event
* if the message is merely reporting an
* error.
*/
PCSME_RPADS, &pmsg);
break;
case POECODE_PADO:
/* Ignore offers that merely report errors. */
if (retv != 0)
break;
/* Ignore offers from servers we don't want. */
break;
/* Dispatch either RPADO or RAPDO+ event. */
break;
default:
if (verbose)
break;
}
}
exit(1);
}
static void
usage(void)
{
logerr("Usage:\n"
"\t%s [-os#] [-v] <dev> [<service> [<server> [only]]]\n\n"
" or\n\n"
exit(1);
}
/*
* In addition to the usual 0-2 file descriptors, pppd will leave fd 3
* open on a pipe to receive the environment variables. See
*/
int
{
char *cp;
myname = "pppoec";
inquiry_mode = 0;
switch (arg) {
case 'i':
inquiry_mode++;
break;
case 'v':
verbose++;
break;
case 'o':
logerr("%s: illegal PADO wait time: %s\n",
exit(1);
}
break;
case 's':
logerr("%s: illegal PADS wait time: %s\n",
exit(1);
}
break;
case '?':
usage();
}
/* Handle inquiry mode. */
if (inquiry_mode) {
usage();
if (pado_wait_time == 0)
/* Invoked by user; open the tunnel driver myself. */
if (tunfd == -1) {
exit(1);
}
/*
* Set up the control stream for PPPoE negotiation
* (set_control), then broadcast a query for all servers
* and listen for replies (find_all_servers).
*/
return (0);
}
if (pado_wait_time == 0)
usage();
/* Make sure we've got a usable tunnel driver on stdin. */
check_stdin();
/* Set up the control stream for PPPoE negotiation. */
/* Pick the service, if any. */
/* Parse out the filters. */
argc--;
onlyflag = 1;
}
exceptflag = 0;
if (!exceptflag &&
exceptflag = 1;
} else {
exceptflag = 0;
}
}
}
/* Enter the main loop. */
return (0);
}