lcp.c revision f53eecf557986dac6ededb388fedd6ca63be0350
/*
* lcp.c - PPP Link Control Protocol.
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 1989 Carnegie Mellon University.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by Carnegie Mellon University. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
* TODO:
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#ifndef USE_CRYPT
#include <des.h>
#endif
#ifdef SOL2
#include <errno.h>
#endif
#endif
#include "pppd.h"
#include "fsm.h"
#include "lcp.h"
#include "chap.h"
#include "magic.h"
#include "patchlevel.h"
#endif
/*
* Special failure codes for logging link failure reasons.
*/
bool peer_nak_auth; /* Peer sent nak for our auth request */
bool unsolicited_nak_auth; /* Peer asked us to authenticate */
bool peer_reject_auth; /* Peer sent reject for auth */
bool rejected_peers_auth; /* We sent a reject to the peer */
bool naked_peers_auth; /* We sent a nak to the peer */
/*
* LCP-related command-line options.
*/
int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
bool lax_recv = 0; /* accept control chars in asyncmap */
#define ACCM_TEST_FAILS 5
#define _tostr2(x) #x
static int noident = 0; /* 1 to disable; 2 to reject */
static int sentident = 0; /* counts the # of ident codes sent */
/* set if we're allowed to send an unsolicited Configure-Nak for MRU. */
static bool unsolicit_mru;
static bool do_msft_workaround = 1;
bool noendpoint = 0; /* don't send/accept endpoint discriminator */
static char *callback_strings[] = {
};
/* This is used in packet printing even if NEGOTIATE_FCS isn't enabled */
static char *fcsalt_strings[] = {
};
#ifdef NEGOTIATE_FCS
#endif
/* Backward compatibility for Linux */
#ifndef PPP_MAXMRU
#define PPP_MINMTU 64
#define PPP_MINMRU 128
#endif
static option_t lcp_option_list[] = {
/* LCP options */
"Disable asyncmap negotiation",
"Disable asyncmap negotiation",
"Set asyncmap (for received packets)" },
"Set asyncmap (for received packets)" },
"Disable magic number option (looped-back line detect)",
"Disable magic number option (looped-back line detect)",
"Disable MRU negotiation (use default 1500)",
"Disable MRU negotiation (use default 1500)",
"Set MRU (maximum received packet size) for negotiation",
PPP_MAXMTU, PPP_MINMTU },
"Disable protocol field compression",
"Disable protocol field compression",
"Set passive mode", 1 },
"Set passive mode", 1 },
"Set silent mode", 1 },
"List of character codes to escape on transmission" },
"Number of consecutive echo failures for link failure" },
"Set time in seconds between LCP echo requests" },
"Disable use of LCP Echo-Request asyncmap checking",
"Use only small Echo-Requests for asyncmap checking",
"Set time in seconds between LCP retransmissions" },
"Maximum number of LCP terminate-request transmissions" },
"Maximum number of LCP configure-request transmissions" },
"Set limit on number of LCP configure-naks" },
"Accept all received control characters", 1 },
#ifdef HAVE_MULTILINK
"Maximum received packet size for multilink bundle",
"Use short sequence numbers in multilink headers",
"Don't use short sequence numbers in multilink headers",
#endif /* HAVE_MULTILINK */
"Endpoint discriminator for multilink", },
"Don't send or accept multilink endpoint discriminator", 1 },
#ifdef NEGOTIATE_FCS
"Disable FCS Alternatives option (use default CRC-16)",
"Set allowable FCS types; crc16, crc32, null, or number" },
"Set FCS type(s) desired; crc16, crc32, null, or number" },
#endif
#ifdef MUX_FRAME
/*
* if pppmux option is turned on, then the parameter to this
* is time value in microseconds
*/
&lcp_allowoptions[0].pppmux, 0, 0 },
#endif
{NULL}
};
/* global vars */
/*
* These variables allow a plugin to assert limits on the maximum
*/
int absmax_mru = PPP_MAXMRU;
int absmax_mtu = PPP_MAXMTU;
static int lcp_echos_pending = 0; /* Number of outstanding echo msgs */
static int lcp_echo_number = 0; /* ID number of next echo frame */
static int lcp_echo_timer_running = 0; /* set if a timer is running */
static bool lcp_echo_accm_test = 0; /* flag if still testing ACCM */
static int lcp_echo_badreplies = 0; /* number of bad replies from peer */
/*
* The maximum number of bad replies we tolerate before bringing the
* link down.
*/
#define LCP_ECHO_MAX_BADREPLIES 10
/*
* Callbacks for fsm code. (CI = Configuration Information)
*/
/*
* routines to send LCP echos to peer
*/
static void lcp_echo_lowerup __P((int));
static void lcp_echo_lowerdown __P((int));
static void LcpEchoTimeout __P((void *));
/*
* routines to send and receive additional LCP packets described in
* section 1 of rfc1570.
*/
static void lcp_timeremaining __P((void *));
lcp_resetci, /* Reset our Configuration Information */
lcp_cilen, /* Length of our Configuration Information */
lcp_addci, /* Add our Configuration Information */
lcp_ackci, /* ACK our Configuration Information */
lcp_nakci, /* NAK our Configuration Information */
lcp_rejci, /* Reject our Configuration Information */
lcp_reqci, /* Request peer's Configuration Information */
lcp_up, /* Called when fsm reaches OPENED state */
lcp_down, /* Called when fsm leaves OPENED state */
lcp_starting, /* Called when we want the lower layer up */
lcp_finished, /* Called when we want the lower layer down */
NULL, /* Retransmission is necessary */
lcp_extcode, /* Called to handle LCP-specific codes */
"LCP", /* String name of protocol */
lcp_coderej, /* Peer rejected a code number */
};
/*
* Protocol entry points.
* Some of these are called directly.
*/
static void lcp_protrej __P((int));
void (*) __P((void *, const char *, ...)), void *));
struct protent lcp_protent = {
PPP_LCP, /* Protocol Number for LCP */
lcp_init, /* Initializes LCP */
lcp_input, /* Processes a received LCP packet */
lcp_protrej, /* Process a received Protocol-reject */
lcp_lowerup, /* Called after the serial device has been set up */
lcp_lowerdown, /* Called when the link is brought down */
lcp_open, /* Called after lcp_lowerup when bringing up the link */
lcp_close, /* Called when the link goes down */
lcp_printpkt, /* Print a packet in human readable form */
NULL, /* Process a received data packet */
1, /* LCP is enabled by default */
"LCP", /* Name of the protocol */
NULL, /* Name of the corresponding data protocol */
lcp_option_list, /* List of LCP command-line options */
NULL, /* Assigns default values for options */
NULL, /* Configures demand-dial */
NULL /* Bring up the link for this packet? */
};
int lcp_loopbackfail = DEFLOOPBACKFAIL;
/*
* Length of each type of configuration option (in octets)
*/
#define CILEN_VOID 2
#define CILEN_CHAR 3
#define CILEN_CBCP 3
/*
* setescape - add chars to the set we escape on transmission.
*/
/*ARGSUSED*/
static int
char **argv;
{
int n, ret;
char *p, *endp;
p = *argv;
ret = 1;
while (*p != '\0') {
if (p == endp) {
option_error("escape parameter contains invalid hex number '%s'",
p);
return 0;
}
p = endp;
if (n < 0 || n == 0x5E || n > 0xFF) {
option_error("can't escape character 0x%x", n);
ret = 0;
} else
while (*p == ',' || *p == ' ')
++p;
}
return ret;
}
/*
* setasyncmap - set async map negotiated
*/
/*ARGSUSED*/
static int
char **argv;
{
char *endp;
option_error("invalid numeric parameter '%s' for 'asyncmap' option",
*argv);
return 0;
}
do_msft_workaround = 0;
return 1;
}
/*ARGSUSED*/
static int
char **argv;
{
return 1;
}
return 0;
}
#ifdef NEGOTIATE_FCS
static int
char *arg;
{
if (*arg != '\0') {
val = 0;
while (*arg != '\0') {
len = 0;
break;
} else {
break;
}
break;
}
return (1);
}
}
}
return (0);
}
/*ARGSUSED*/
static int
char **argv;
{
}
/*ARGSUSED*/
static int
char **argv;
{
}
#endif
/*
* lcp_init - Initialize LCP.
*/
static void
int unit;
{
f->callbacks = &lcp_callbacks;
fsm_init(f);
/*
* Leave allowed MRU (MTU) at zero; configuration option sets it
* non-zero if we should nak for something else.
*/
#ifdef SOL2
/* Check if DES wasn't exported */
errno = 0;
setkey("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
if (errno == 0)
#endif
{
#ifdef CHAPMS
#endif
#ifdef CHAPMSV2
#endif
}
#endif
#ifdef CBCP_SUPPORT
#endif
#ifdef NEGOTIATE_FCS
#endif
}
/*
* lcp_open - LCP is allowed to come up.
*/
void
int unit;
{
f->flags = 0;
f->flags |= OPT_PASSIVE;
f->flags |= OPT_SILENT;
fsm_open(f);
}
/*
* lcp_close - Take LCP down.
*/
void
int unit;
char *reason;
{
if (phase != PHASE_DEAD)
/*
* This action is not strictly according to the FSM in RFC1548,
* but it does mean that the program terminates if you do a
* been established.
*/
lcp_finished(f);
} else
}
/*
* lcp_lowerup - The lower layer is up.
*/
void
int unit;
{
/*
* Don't use A/C or protocol compression on transmission,
* but accept A/C and protocol compressed packets
* if we are going to ask for A/C and protocol compression.
*/
#ifdef NEGOTIATE_FCS
#endif
}
/*
* lcp_lowerdown - The lower layer is down.
*/
void
int unit;
{
}
/*
* lcp_input - Input LCP packet.
*/
static void
int unit;
u_char *p;
int len;
{
}
/*
* lcp_extcode - Handle a LCP-specific code.
*/
static int
fsm *f;
int len;
{
switch( code ){
case CODE_PROTREJ:
break;
case CODE_ECHOREQ:
break;
break;
case CODE_ECHOREP:
LcpLinkFailure(f);
lcp_echos_pending = 0;
lcp_echo_badreplies = 0;
}
}
break;
case CODE_DISCREQ:
break;
case CODE_IDENT:
/* More than one 'noident' tells us to reject the code number. */
if (noident > 1)
return 0;
break;
case CODE_TIMEREMAIN:
break;
default:
return 0;
}
return 1;
}
/*
* lcp_rprotrej - Receive an Protocol-Reject.
*
* Figure out which protocol is rejected and inform it.
*/
static void
fsm *f;
int len;
{
int i;
if (len < 2) {
dbglog("lcp_rprotrej: Rcvd short Protocol-Reject packet!");
return;
}
/*
* Protocol-Reject packets received in any state other than the LCP
* OPENED state SHOULD be silently discarded.
*/
dbglog("Protocol-Reject discarded: LCP in state %s",
return;
}
/*
* Upcall the proper Protocol-Reject routine.
*/
return;
}
}
/*
* lcp_protrej - A Protocol-Reject was received.
*/
/*ARGSUSED*/
static void
int unit;
{
/*
* Can't reject LCP!
*/
error("Received Protocol-Reject for LCP!");
}
/*
* lcp_coderej - A Code-Reject was received.
*/
/*ARGSUSED*/
static int
fsm *f;
int code;
int id;
int len;
{
/* The peer cannot reject these code numbers. */
return 1;
switch (code) {
case CODE_ECHOREQ:
/*
* If the peer rejects an Echo-Request, then stop doing that.
*/
if (lcp_echo_timer_running != 0) {
UNTIMEOUT (LcpEchoTimeout, f);
lcp_echo_interval = 0;
}
break;
}
return 0;
}
/*
* lcp_sprotrej - Send a Protocol-Reject for some protocol.
*/
void
int unit;
u_char *p;
int len;
{
/*
* Send back the protocol and the information field of the
* rejected packet. We only get here if LCP is in the OPENED state.
*/
p += 2;
len -= 2;
p, len);
}
/*
* lcp_resetci - Reset our CI.
*/
static void
lcp_resetci(f)
fsm *f;
{
sentident = 0;
if (!multilink) {
}
if (noendpoint)
ao->neg_endpoint = 0;
unsolicit_mru = 1;
auth_reset(f->unit);
}
/*
* lcp_cilen - Return length of our CI.
*/
static int
lcp_cilen(f)
fsm *f;
{
/*
* NB: we only ask for one of CHAP and UPAP, even if we will
* accept either.
*/
!go->neg_mschapv2) +
#ifdef MUX_FRAME
#endif
}
/*
* lcp_addci - Add our desired CIs to a packet.
*/
static void
fsm *f;
int *lenp;
{
if (neg) { \
}
if (neg) { \
}
if (neg) { \
}
if (neg) { \
}
if (neg) { \
}
if (neg) { \
}
if (neg) { \
int i; \
for (i = 0; i < len; ++i) \
}
/* go->chap_mdtype always points to a useful value */
/* We can't both say zero for LQR period. */
#ifdef MUX_FRAME
#endif
/* this should never happen, because peer_mtu should be 1500 */
error("Bug in lcp_addci: wrong length");
}
}
/*
* lcp_ackci - Ack our CIs.
* This should not modify any state if the Ack is bad.
*
* Returns:
* 0 - Ack was bad.
* 1 - Ack was good.
*/
static int
fsm *f;
u_char *p;
int len;
{
#ifdef MUX_FRAME
#endif
/*
* CIs must be in exactly the same order that we sent.
* Check packet length and CI length at each step.
* If we find any deviations, then this packet is bad.
*/
if (neg) { \
if ((len -= CILEN_VOID) < 0) \
goto bad; \
if (cilen != CILEN_VOID || \
goto bad; \
}
if (neg) { \
if ((len -= CILEN_SHORT) < 0) \
goto bad; \
if (cilen != CILEN_SHORT || \
goto bad; \
goto bad; \
}
if (neg) { \
if ((len -= CILEN_SHORT) < 0) \
goto bad; \
if (cilen != CILEN_SHORT || \
goto bad; \
goto bad; \
peer_nak_auth = 0; \
peer_reject_auth = 0; \
}
if (neg) { \
if ((len -= CILEN_CHAR) < 0) \
goto bad; \
if (cilen != CILEN_CHAR || \
goto bad; \
goto bad; \
}
if (neg) { \
if ((len -= CILEN_CHAP) < 0) \
goto bad; \
if (cilen != CILEN_CHAP || \
goto bad; \
goto bad; \
goto bad; \
peer_nak_auth = 0; \
peer_reject_auth = 0; \
}
if (neg) { \
if ((len -= CILEN_LONG) < 0) \
goto bad; \
if (cilen != CILEN_LONG || \
goto bad; \
goto bad; \
}
if (neg) { \
goto bad; \
goto bad; \
goto bad; \
goto bad; \
}
if (neg) { \
int i; \
goto bad; \
goto bad; \
goto bad; \
for (i = 0; i < vlen; ++i) { \
goto bad; \
} \
}
/* go->chap_mdtype always points to a useful value */
#ifdef MUX_FRAME
#endif
/*
* If there are any remaining CIs, then this packet is bad.
*/
if (len != 0)
goto bad;
return (1);
bad:
dbglog("lcp_acki: received bad Ack!");
return (0);
}
/*
* lcp_nakci - Peer has sent a NAK for some of our CIs.
* This should not modify any state if the Nak is bad
* or if LCP is in the OPENED state.
*
* Returns:
* 0 - Nak was bad.
* 1 - Nak was good.
*/
static int
fsm *f;
u_char *p;
int len;
{
int looped_back = 0;
int cilen;
/*
* Any Nak'd CIs must be in exactly the same order that we sent.
* Check packet length and CI length at each step.
* If we find any deviations, then this packet is bad.
*/
len >= CILEN_VOID && \
p[1] == CILEN_VOID && \
p[0] == opt) { \
len -= CILEN_VOID; \
INCPTR(CILEN_VOID, p); \
}
len >= CILEN_CHAR && \
p[1] == CILEN_CHAR && \
p[0] == opt) { \
len -= CILEN_CHAR; \
INCPTR(2, p); \
code \
}
len >= CILEN_SHORT && \
p[1] == CILEN_SHORT && \
p[0] == opt) { \
len -= CILEN_SHORT; \
INCPTR(2, p); \
code \
}
len >= CILEN_LONG && \
p[1] == CILEN_LONG && \
p[0] == opt) { \
len -= CILEN_LONG; \
INCPTR(2, p); \
code \
}
p[1] == CILEN_LQR && \
p[0] == opt) { \
INCPTR(2, p); \
code \
}
len >= CILEN_CHAR && \
p[0] == opt && \
p[1] >= CILEN_CHAR && \
p[1] <= len) { \
len -= p[1]; \
INCPTR(p[1], p); \
}
/*
* We don't care if they want to send us smaller packets than
* we want. Therefore, accept any MRU less than what we asked for,
* but then ignore the new value when setting the MRU in the kernel.
* If they send us a bigger MRU than what we asked, accept it, up to
* the limit of the default MRU we'd get if we didn't negotiate.
*/
);
}
/*
* Add any characters they want to our (receive-side) asyncmap.
*/
);
}
/*
* If they've nak'd our authentication-protocol, check whether
* they are proposing a different protocol, or a different
* hash algorithm for CHAP.
*/
p[1] <= len) {
cilen = p[1];
INCPTR(2, p);
peer_nak_auth = 1;
/*
* If we were asking for CHAP, they obviously don't want to do it.
* If we weren't asking for CHAP, then we were asking for PAP,
* in which case this Nak is bad.
*/
goto bad;
try.neg_mschap = 0;
try.neg_mschapv2 = 0;
/* stop asking for that type */
switch (go->chap_mdtype) {
case CHAP_DIGEST_MD5:
break;
case CHAP_MICROSOFT:
try.neg_mschap = 0;
break;
case CHAP_MICROSOFT_V2:
try.neg_mschapv2 = 0;
break;
}
/* Allow >= on length here for broken and silly peers. */
p += cilen - CILEN_CHAP;
/* Try his requested algorithm. */
} else {
goto try_another;
}
} else {
/*
* We don't recognize what they're suggesting.
* Stop asking for what we were asking for.
*/
switch (go->chap_mdtype) {
case CHAP_DIGEST_MD5:
if (wo->neg_mschap) {
break;
}
/*FALLTHROUGH*/
case CHAP_MICROSOFT:
try.neg_mschap = 0;
if (wo->neg_mschapv2) {
break;
}
/*FALLTHROUGH*/
case CHAP_MICROSOFT_V2:
try.neg_mschapv2 = 0;
break;
}
} else
p += cilen - CILEN_SHORT;
}
}
/*
* If they can't cope with our link quality protocol, we'll have
* to stop asking for LQR. We haven't got any other protocol. If
* they Nak the reporting period, then the following logic
* applies:
* If he suggests zero and go->neg_fcs is true and
* ao->lqr_period isn't zero, then take his suggestion. If he
* suggests zero otherwise, ignore it. If he suggests a nonzero
* value and wo->lqr_period is zero, then take his suggestion. If
* he suggests a nonzero value otherwise that's less than
* wo->lqr_period, then ignore it.
*/
else if (cilong != 0 &&
);
/*
* Only implementing CBCP...not the rest of the callback options
*/
);
/*
* Check for a looped-back line.
*/
looped_back = 1;
);
/*
* Peer shouldn't send Nak for protocol compression or
* a Reject instead. If they send a Nak, treat it as a Reject.
*/
/*
* Remove any FCS types he doesn't like from our (receive-side)
* FCS list.
*/
#ifdef MUX_FRAME
/* Nacked MUX option */
#endif
/*
* Nak of the endpoint discriminator option is not permitted,
* treat it like a reject.
*/
/*
* Nak for MRRU option - accept their value if it is smaller
* than the one we want.
*/
);
}
/*
* Nak for short sequence numbers shouldn't be sent, treat it
* like a reject.
*/
/*
* There may be remaining CIs, if the peer is requesting negotiation
* on an option that we didn't include in our request packet.
* If we see an option that we requested, or one we've already seen
* in this packet, then this packet is bad.
* If we wanted to respond by starting to negotiate on the requested
* option(s), we could, but we don't, because except for the
* authentication type and quality protocol, if we are not negotiating
* an option, it is because we were told not to.
* For the authentication type, the Nak from the peer means
* `let me authenticate myself with you' which is a bit pointless.
* For the quality protocol, the Nak means `ask me to send you quality
* reports', but if we didn't ask for them, we don't want them.
* An option we don't recognize represents the peer asking to
* negotiate some option we don't support, so ignore it.
*/
while (len > CILEN_VOID) {
goto bad;
switch (citype) {
case CI_MRU:
goto bad;
notice("Peer sent unsolicited Nak for MRU less than default.");
}
break;
case CI_ASYNCMAP:
goto bad;
break;
case CI_AUTHTYPE:
unsolicited_nak_auth = 1;
if (cilen >= CILEN_SHORT) {
} else {
unsolicit_auth_proto = 0;
}
goto bad;
break;
case CI_MAGICNUMBER:
cilen != CILEN_LONG)
goto bad;
break;
case CI_PCOMPRESSION:
|| cilen != CILEN_VOID)
goto bad;
break;
case CI_ACCOMPRESSION:
|| cilen != CILEN_VOID)
goto bad;
break;
case CI_QUALITY:
goto bad;
break;
case CI_MRRU:
goto bad;
break;
case CI_SSNHF:
goto bad;
break;
case CI_EPDISC:
goto bad;
break;
case CI_FCSALTERN:
goto bad;
break;
#ifdef MUX_FRAME
case CI_MUXING:
goto bad;
break;
#endif
}
p = next;
}
/*
* OK, the Nak is good. Now we can update state.
* If there are any options left we ignore them.
*/
/*
* Note: the code once reset try.numloops to zero here if
* looped_back wasn't set. This is wrong because a mixture of
* looped-back and peer data (possible if half-duplex is used)
* will allow the link to come up, and it shouldn't.
*/
if (looped_back) {
notice("Serial line is looped back.");
}
}
}
return 1;
bad:
dbglog("lcp_nakci: received bad Nak!");
return 0;
}
/*
* lcp_rejci - Peer has Rejected some of our CIs.
* This should not modify any state if the Reject is bad
* or if LCP is in the OPENED state.
*
* Returns:
* 0 - Reject was bad.
* 1 - Reject was good.
*/
static int
fsm *f;
u_char *p;
int len;
{
/*
* Any Rejected CIs must be in exactly the same order that we sent.
* Check packet length and CI length at each step.
* If we find any deviations, then this packet is bad.
*/
len >= CILEN_VOID && \
p[1] == CILEN_VOID && \
p[0] == opt) { \
len -= CILEN_VOID; \
INCPTR(CILEN_VOID, p); \
}
len >= CILEN_CHAR && \
p[1] == CILEN_CHAR && \
p[0] == opt) { \
len -= CILEN_CHAR; \
INCPTR(2, p); \
/* Check rejected value. */ \
goto bad; \
}
len >= CILEN_SHORT && \
p[1] == CILEN_SHORT && \
p[0] == opt) { \
len -= CILEN_SHORT; \
INCPTR(2, p); \
/* Check rejected value. */ \
goto bad; \
}
len >= CILEN_SHORT && \
p[1] == CILEN_SHORT && \
p[0] == opt) { \
len -= CILEN_SHORT; \
INCPTR(2, p); \
/* Check rejected value. */ \
peer_reject_auth = 1; \
reject_auth_proto = cishort; \
goto bad; \
}
len >= CILEN_LONG && \
p[1] == CILEN_LONG && \
p[0] == opt) { \
len -= CILEN_LONG; \
INCPTR(2, p); \
/* Check rejected value. */ \
goto bad; \
}
p[1] == CILEN_LQR && \
p[0] == opt) { \
INCPTR(2, p); \
/* Check rejected value. */ \
goto bad; \
}
len >= CILEN_CBCP && \
p[1] == CILEN_CBCP && \
p[0] == opt) { \
len -= CILEN_CBCP; \
INCPTR(2, p); \
/* Check rejected value. */ \
goto bad; \
}
p[0] == opt && \
int i; \
INCPTR(2, p); \
goto bad; \
for (i = 0; i < vlen; ++i) { \
goto bad; \
} \
}
/* Received a Configure-Reject, try to send Identification now. */
sentident++;
}
/*
* There are broken peers (such as unbundled Solaris PPP) that
* send Configure-Reject for authentication when they really
* intend Configure-Nak. This code works around this problem.
*/
len -= CILEN_CHAP;
INCPTR(2, p);
peer_reject_auth = 1;
/* Check rejected value. */
goto bad;
/* Disable the one that he rejected */
switch (cichar) {
case CHAP_DIGEST_MD5:
break;
case CHAP_MICROSOFT:
try.neg_mschap = 0;
break;
case CHAP_MICROSOFT_V2:
try.neg_mschapv2 = 0;
break;
}
/* Try another, if we can. */
else if (try.neg_mschap)
else
}
}
#ifdef MUX_FRAME
#endif
/*
* If there are any remaining CIs, then this packet is bad.
*/
if (len != 0)
goto bad;
/*
* Now we can update state.
*/
return 1;
bad:
dbglog("lcp_rejci: received bad Reject!");
return 0;
}
/*
* lcp_reqci - Check the peer's requested CIs and send appropriate response.
*
* Returns: CODE_CONFACK, CODE_CONFNAK or CODE_CONFREJ and input
* packet modified appropriately. If reject_if_disagree is non-zero,
* doesn't return CODE_CONFNAK; returns CODE_CONFREJ if it can't
* return CODE_CONFACK.
*/
static int
fsm *f;
u_char *p; /* Requested CIs */
int *lenp; /* Length of requested CIs */
int dont_nak;
{
int len;
/*
* Loop through options once to find out if peer is offering
* Multilink, and repair values as needed.
*/
p0 = p;
/*
* RFC 1661 page 40 -- if the option extends beyond the
* packet, then discard the entire packet.
*/
dbglog("discarding LCP Configure-Request due to truncated option");
return (0);
}
prev = p;
else
}
}
if (cilen < 2)
cilen = 2;
}
ret = CODE_CONFACK;
nakp = nak_buffer;
/*
* Reset all his options.
*/
/*
* Process all his options.
*/
prev = p;
switch (citype) { /* Check CI type */
case CI_MRU:
break;
}
} else {
/* extract the MRU from the option */
/*
* If the offered MRU is less than our desired MTU, we
* should nak. This is especially helpful if we're
* doing demand-dial, since those queued up packets
* might be discarded otherwise.
*/
}
}
/*
* If we're going to send a nak with something less than
* or equal to the default PPP MTU, then just reject instead.
*/
if (newret == CODE_CONFNAK) {
}
break;
case CI_ASYNCMAP:
if (!ao->neg_asyncmap) {
break;
}
if (cilen != CILEN_LONG) {
cilong = 0;
} else {
/*
* Asyncmap must have set at least the bits
* which are set in lcp_allowoptions[unit].asyncmap.
*/
}
/*
* Workaround for common broken Microsoft software -- if
* the peer is sending us a nonzero ACCM, then he *needs*
* us to send the same to him. Adjust our Configure-
* Request message and restart LCP.
*/
dbglog("adjusted requested asyncmap from %X to %X",
do_msft_workaround = 0;
f->flags &= ~OPT_SILENT;
info("possibly broken peer detected; restarting LCP");
fsm_lowerdown(f);
fsm_lowerup(f);
return (0);
}
if (newret == CODE_CONFNAK) {
}
break;
case CI_AUTHTYPE:
ao->neg_mschapv2)) {
rejected_peers_auth = 1;
if (cilen >= CILEN_SHORT) {
} else {
rejected_auth_proto = 0;
}
/*
* Reject the option if we're not willing to authenticate.
*/
break;
}
rejected_peers_auth = 0;
naked_peers_auth = 0;
if (cilen >= CILEN_SHORT) {
/* Extract the authentication protocol from the option */
ho->neg_mschapv2) {
dbglog("Rejecting extra authentication protocol option");
break;
}
/*
* Authtype must be PAP or CHAP.
*
* Note: if both ao->neg_upap and ao->neg_*chap* are
* set, and the peer sends a Configure-Request with
* two authenticate-protocol requests, one for CHAP
* and one for UPAP, then we will reject the second
* request. Whether we end up doing CHAP or UPAP
* depends then on the ordering of the CIs in the
* peer's Configure-Request.
*
* We're supposed to list all of the protocols we can
* possibly use in the returned Configure-Nak. This
* part of RFC 1661 (section 5.3) is in conflict with
* the section that says the options shouldn't be
* reordered, so it's often ignored.
*/
if (cilen != CILEN_SHORT)
goto try_pap_anyway;
break;
}
/* Test >= here to allow for broken peers. */
if (cilen >= CILEN_CHAP &&
else if (cichar == CHAP_MICROSOFT_V2 &&
ho->neg_mschapv2) {
break;
}
}
}
}
/*
* We don't recognize the protocol they're asking for.
* Nak it with something we're willing to do.
* (At this point we know ao->neg_upap || ao->neg_chap.)
*/
} else {
}
naked_peers_auth = 1;
break;
case CI_QUALITY:
break;
}
} else {
/* Check the LQM protocol */
}
/* Check the reporting period; we can't both send zero */
cilong = 500;
}
}
if (newret == CODE_CONFNAK) {
}
break;
case CI_MAGICNUMBER:
break;
}
if (cilen < CILEN_LONG) {
/*
* If we send Magic-Number, then we must not reject it
* when the peer sends it to us, even if his version
* looks odd to us. Ack if the cilen is wrong in this
* case. If we're not sending Magic-Number, then we don't
* much care what his value is anyway.
*/
break;
}
if (cilen > CILEN_LONG)
break;
/*
* He must have a different magic number. Make sure we
* give him a good one to use.
*/
}
if (newret == CODE_CONFNAK) {
/*
* We don't need to bump the numloops counter here
* since it's already done upon reception of a nak.
*/
}
break;
case CI_PCOMPRESSION:
if (!ao->neg_pcompression) {
break;
}
if (cilen != CILEN_VOID) {
}
break;
case CI_ACCOMPRESSION:
if (!ao->neg_accompression) {
break;
}
if (cilen != CILEN_VOID) {
}
break;
case CI_FCSALTERN:
break;
}
if (cilen != CILEN_CHAR) {
} else {
/* If he has bits we don't like, tell him to stop. */
break;
}
}
}
if (newret == CODE_CONFNAK) {
}
break;
case CI_MRRU:
break;
}
if (cilen != CILEN_SHORT) {
} else {
}
}
if (cishort < PPP_MINMTU) {
}
if (newret == CODE_CONFNAK) {
}
break;
case CI_SSNHF:
break;
}
if (cilen != CILEN_VOID) {
}
break;
case CI_EPDISC:
if (!ao->neg_endpoint) {
break;
}
int i;
break;
}
break;
#ifdef MUX_FRAME
case CI_MUXING:
break;
}
/* remember his option */
break;
#endif
default:
break;
}
/* Cope with confused peers. */
if (cilen < 2)
cilen = 2;
/*
* If this is an Ack'able CI, but we're sending back a Nak,
* don't include this CI.
*/
continue;
if (newret == CODE_CONFNAK) {
/*
* Continue naking the Magic Number option until the cows come
* home -- rejecting it is wrong.
*/
} else {
/* Ignore subsequent Nak'able things if rejecting. */
if (ret == CODE_CONFREJ)
continue;
ret = CODE_CONFNAK;
}
}
if (newret == CODE_CONFREJ) {
ret = CODE_CONFREJ;
}
}
/*
* If the peer hasn't negotiated his MRU, and we'd like an MTU
* that's larger than the default, try sending an unsolicited
* Nak for what we want.
*/
!dont_nak && unsolicit_mru) {
unsolicit_mru = 0; /* don't ask again */
ret = CODE_CONFNAK;
}
switch (ret) {
case CODE_CONFACK:
break;
case CODE_CONFNAK:
/*
* Copy the Nak'd options from the nak_buffer to the caller's buffer.
*/
break;
case CODE_CONFREJ:
/* We're about to send Configure-Reject; send Identification */
sentident++;
}
break;
}
return (ret); /* Return final code */
}
/*
* lcp_up - LCP has come UP.
*/
static void
lcp_up(f)
fsm *f;
{
if (!go->neg_magicnumber)
go->magicnumber = 0;
if (!ho->neg_magicnumber)
ho->magicnumber = 0;
/*
* Set our MTU to the smaller of the MTU we wanted and
* the MRU our peer wanted. If we negotiated an MRU,
* set our MRU to the larger of value we wanted and
* the value we got in the negotiation.
*/
if (mtu > absmax_mtu)
mtu = absmax_mtu;
if (mru > absmax_mru)
mru = absmax_mru;
#ifdef NEGOTIATE_FCS
#endif
#ifdef MUX_FRAME
#endif
/* LCP is Up; send Identification */
if (!noident) {
sentident++;
}
link_established(f->unit);
}
/*
* lcp_down - LCP has gone DOWN.
*
* Alert other protocols.
*/
static void
lcp_down(f)
fsm *f;
{
int mtu;
lcp_echo_lowerdown(f->unit);
#ifdef NEGOTIATE_FCS
#endif
}
/*
* lcp_starting - LCP needs the lower layer up.
*/
static void
lcp_starting(f)
fsm *f;
{
link_required(f->unit);
}
/*
* lcp_finished - LCP has finished with the lower layer.
*/
static void
lcp_finished(f)
fsm *f;
{
link_terminated(f->unit);
}
/*
* lcp_printpkt - print the contents of an LCP packet.
*/
static int
u_char *p;
int plen;
void *arg;
{
return 0;
pstart = p;
return 0;
switch (code) {
case CODE_CONFREQ:
case CODE_CONFACK:
case CODE_CONFNAK:
case CODE_CONFREJ:
/* print option list */
while (len >= 2) {
p -= 2;
break;
}
switch (code) {
case CI_MRU:
if (olen >= CILEN_SHORT) {
p += 2;
}
break;
case CI_ASYNCMAP:
if (olen >= CILEN_LONG) {
p += 2;
}
break;
case CI_AUTHTYPE:
if (olen >= CILEN_SHORT) {
p += 2;
switch (cishort) {
case PPP_PAP:
break;
case PPP_CHAP:
if (p < optend) {
switch (*p) {
case CHAP_DIGEST_MD5:
++p;
break;
case CHAP_MICROSOFT:
++p;
break;
case CHAP_MICROSOFT_V2:
++p;
break;
}
}
break;
#ifdef PPP_EAP
case PPP_EAP:
break;
#endif
case 0xC027:
break;
case 0xC123:
break;
default:
}
}
break;
case CI_QUALITY:
if (olen >= CILEN_SHORT) {
p += 2;
switch (cishort) {
case PPP_LQR:
break;
default:
}
}
break;
case CI_CALLBACK:
if (olen >= CILEN_CHAR) {
p += 2;
if (cichar <= 6 &&
} else {
}
}
break;
case CI_MAGICNUMBER:
if (olen >= CILEN_LONG) {
p += 2;
}
break;
case CI_PCOMPRESSION:
if (olen >= CILEN_VOID) {
p += 2;
}
break;
case CI_ACCOMPRESSION:
if (olen >= CILEN_VOID) {
p += 2;
}
break;
case CI_FCSALTERN:
if (olen >= CILEN_CHAR) {
char **cpp;
int needcomma = 0;
p += 2;
needcomma = 1;
}
cichar);
}
break;
case CI_NUMBERED:
if (olen >= CILEN_SHORT) {
p += 2;
}
break;
case CI_MRRU:
if (olen >= CILEN_SHORT) {
p += 2;
}
break;
case CI_SSNHF:
if (olen >= CILEN_VOID) {
p += 2;
}
break;
case CI_EPDISC:
if (olen >= CILEN_CHAR) {
p += 2;
}
}
break;
case CI_LINKDISC:
if (olen >= CILEN_SHORT) {
p += 2;
}
break;
case CI_COBS:
if (olen >= CILEN_CHAR) {
p += 2;
}
break;
case CI_PFXELISION:
if (olen >= CILEN_CHAR) {
p += 2;
}
break;
case CI_MPHDRFMT:
if (olen >= CILEN_SHORT) {
p += 2;
switch (cichar) {
case 2:
break;
case 6:
break;
default:
break;
}
}
break;
case CI_I18N:
if (olen >= CILEN_LONG) {
p += 2;
if (olen > CILEN_LONG) {
p = optend;
}
}
break;
case CI_SDL:
if (olen >= CILEN_VOID) {
p += 2;
}
break;
case CI_MUXING:
if (olen >= CILEN_VOID) {
p += 2;
}
break;
}
while (p < optend) {
}
}
break;
case CODE_TERMACK:
case CODE_TERMREQ:
p += len;
len = 0;
}
break;
case CODE_ECHOREQ:
case CODE_ECHOREP:
case CODE_DISCREQ:
if (len >= 4) {
len -= 4;
}
break;
case CODE_IDENT:
if (len >= 4) {
len -= 4;
} else
break;
p += len;
len = 0;
}
break;
case CODE_TIMEREMAIN:
if (len >= 4) {
len -= 4;
} else
break;
if (len >= 4) {
len -= 4;
} else
break;
p += len;
len = 0;
}
break;
}
/* print the rest of the bytes in the packet */
for (i = 0; i < len && i < 32; ++i) {
}
if (i < len) {
p += len - i;
}
return p - pstart;
}
/*
* Time to shut down the link because there is nothing out there.
*/
static void
LcpLinkFailure (f)
fsm *f;
{
char *close_message;
close_message = "Receiving malformed Echo-Replies";
} else if (lcp_echo_accm_test) {
/*
* If this is an asynchronous line and we've missed all of
* the initial echo requests, then this is probably due to
* a bad ACCM.
*/
notice("Peer not responding to initial Echo-Requests.");
notice("Negotiated asyncmap may be incorrect for this link.");
close_message = "Peer not responding; perhaps bad asyncmap";
} else {
notice("Serial link appears to be disconnected.");
close_message = "Peer not responding";
}
}
}
/*
* Timer expired for the LCP echo requests from this process.
*/
static void
LcpEchoCheck (f)
fsm *f;
{
return;
LcpSendEchoRequest (f);
/*
* Start the timer for the next interval.
*/
warn("assertion lcp_echo_timer_running==0 failed");
}
/*
* LcpEchoTimeout - Timer expired on the LCP echo
*/
static void
void *arg;
{
if (lcp_echo_timer_running != 0) {
}
}
/*
* LcpEchoReply - LCP has received a reply to the echo
*/
/*ARGSUSED*/
static int
fsm *f;
int id;
int len;
{
/* Check the magic number - don't count replies from ourselves. */
if (len < 4) {
return (0);
}
warn("appear to have received our own echo-reply!");
return (0);
}
/* Reset the number of outstanding echo frames */
lcp_echos_pending = 0;
if (lcp_echo_accm_test) {
dbglog("lcp: validated asyncmap setting");
lcp_echo_accm_test = 0;
if (lcp_echo_fails == 0)
lcp_echo_interval = 0;
}
return (1);
}
/*
* LcpSendEchoRequest - Send an echo request frame to the peer
*/
static void
fsm *f;
{
int i;
/*
* Detect the failure of the peer at this point. If we're not currently
* performing the ACCM test, then we just check for the user's echo-failure
* point. If we are performing the ACCM test, then use ACCM_TEST_FAILS if
* the user hasn't specified a different failure point.
*/
i = lcp_echo_fails;
if (i == 0)
i = ACCM_TEST_FAILS;
if ((!lcp_echo_accm_test && lcp_echo_fails != 0 &&
lcp_echos_pending >= lcp_echo_fails) ||
(lcp_echo_accm_test && lcp_echos_pending >= i)) {
LcpLinkFailure(f);
lcp_echos_pending = 0;
lcp_echo_badreplies = 0;
}
/*
* Make and send the echo request frame.
*/
/* Send some test packets so we can fail the link early. */
if (lcp_echo_accm_test) {
switch (use_accm_test) {
case 1:
/* Only the characters covered by negotiated ACCM */
for (i = 0; i < 32; i++)
*pktp++ = i;
break;
case 2:
/* All characters */
for (i = 0; i < 256; i++)
*pktp++ = i;
break;
}
}
}
}
/*
* lcp_echo_lowerup - Start the timer for the LCP frame
*/
static void
int unit;
{
/* Clear the parameters for generating echo frames */
lcp_echos_pending = 0;
lcp_echo_number = 0;
/* If a timeout interval is specified then start the timer */
LcpEchoCheck(f);
}
/*
* lcp_echo_lowerdown - Stop the timer for the LCP frame
*/
static void
int unit;
{
if (lcp_echo_timer_running != 0) {
UNTIMEOUT (LcpEchoTimeout, f);
}
}
/*
* LcpSendIdentification - Send LCP Identification string to peer.
*/
static void
fsm *f;
{
int idlen;
/*
* Make and send the Identification frame.
*/
else
lcp_magic = 0;
}
/*ARGSUSED*/
static void
fsm *f;
int id;
int len;
{
/* Check the magic number - don't count replies from ourselves. */
if (len < 4) {
return;
}
len -= 4;
warn("appear to have received our own Identification!");
return;
}
}
/*
* Send a Time-Remaining LCP packet. We don't include a message.
*/
static void
fsm *f;
{
return;
}
/*ARGSUSED*/
static void
fsm *f;
int id;
int len;
{
/* Check the magic number - don't count replies from ourselves. */
if (len < 8) {
return;
}
warn("appear to have received our own Time-Remain!");
return;
}
if (len > 8) {
} else {
}
}
/*
* lcp_timeremaining - timeout handler which sends LCP Time-Remaining
* packet.
*/
static void
void *arg;
{
int unit;
}
/*
* lcp_settimeremaining - set a timeout to send an LCP Time-Remaining
* packet. The first argument, connecttime, is the time remaining
* at the time this function is called. The second argument is the
* desired time remaining when the packet should be sent out.
*/
void
int unit;
{
if (connecttime == time_remaining) {
} else {
}
}