1
0
Files
irix-657m-src/eoe/cmd/ppp/lcp.c
2022-09-29 17:59:04 +03:00

2165 lines
54 KiB
C

/* PPP Link Control Protocol processing
*/
#ident "$Revision: 1.31 $"
#include <math.h>
#include <ctype.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <net/if.h>
#include "ppp.h"
extern unsigned sysid(unsigned char id[16]);
extern struct timeval rcv_msg_ts;
static void lcp_scr(struct ppp*);
static void lcp_str(struct ppp*, struct fsm*);
static void lcp_ser(struct ppp*, struct fsm*);
static void lcp_act(struct ppp*, enum fsm_action);
static struct fsm_ops lcp_ops = {
lcp_scr,
fsm_sca,
fsm_scn,
lcp_str,
fsm_sta,
fsm_scj,
lcp_ser,
lcp_act
};
#define L ppp->lcp
#define A ppp->auth
#define LNAME L.fsm.name
#define STRUCT_PKT struct lcp_pkt
#define STRUCT_CF struct lcp_cf
#define BASE_P(bufp) (&(bufp)->lcp_un.cf[0])
#define EARLY_NAK_SENT() (L.fsm.nak_sent < 2)
#define EARLY_NAK_RECV() (L.fsm.nak_recv < 3)
#define MIGHT_RX_PRED1 (ppp0.ccp.conf.rx & SIOC_PPP_CCP_PRED1)
#define MIGHT_TX_PRED1 (ppp0.ccp.conf.tx & SIOC_PPP_CCP_PRED1)
/* LCP Configuration Options */
#define LCP_CO_MRU_T 1 /* Maximum-Receive-Unit */
#define LCP_CO_MRU_L 4
#define LCP_CO_ACCM_T 2 /* Async-Control-Character-Map */
#define LCP_CO_ACCM_L 6
#define LCP_CO_AUTH_T 3
#define LCP_CO_PAP_L 4 /* Password Auth. Protocol */
#define LCP_CO_PAP_P PPP_PAP
#define LCP_CO_CHAP_L 5 /* Challenge-Handshake Auth. */
#define LCP_CO_CHAP_MD5 5
#define LCP_CO_CHAP_P PPP_CHAP
#define LCP_CO_LQ_T 4
#define LCP_CO_MAGIC_T 5 /* Magic-Number */
#define LCP_CO_MAGIC_L 6
#define LCP_CO_PCOMP_T 7 /* Protocol-Field-Compression */
#define LCP_CO_PCOMP_L 2
#define LCP_CO_ACOMP_T 8 /* Address-and-Control-Field comp. */
#define LCP_CO_ACOMP_L 2
#define LCP_CO_FCS_ALT_T 9
#define LCP_CO_S_D_PAD_T 10 /* self-describing padding */
#define LCP_CO_N_MODE_T 11 /* numbered mode */
#define LCP_CO_ML_T 12
#define LCP_CO_CALLBACK_T 13
#define LCP_CONNECT_TIME_T 14
#define LCP_COMPOUND_T 15
#define LCP_NOMICAL_T 16
#define LCP_CO_MRRU_T 17 /* Multilik MRRU */
#define LCP_CO_MRRU_L 4
#define LCP_CO_SSNHF_T 18 /* Multilink short sequence # */
#define LCP_CO_SSNHF_L 2
#define LCP_CO_ENDPOINT_T 19 /* Multilink Endpoint Discriminator */
#define LCP_CO_ENDPOINT_MAX ((SYSNAME_LEN-10)/2)
#define LCP_CO_BACP_LINK_T 23
static char *
lcp_opt_name(u_int type)
{
static char buf[32];
switch (type) {
case LCP_CO_MRU_T:
return "MRU";
case LCP_CO_ACCM_T:
return "ACCM";
case LCP_CO_AUTH_T:
return "authentication";
case LCP_CO_LQ_T:
return "link quality monitoring";
case LCP_CO_MAGIC_T:
return "magic-numbers";
case LCP_CO_PCOMP_T:
return "protocol field compression";
case LCP_CO_ACOMP_T:
return "address and control field compression";
case LCP_CO_FCS_ALT_T:
return "FCS alternatives";
case LCP_CO_S_D_PAD_T:
return "self-describing pad";
case LCP_CO_N_MODE_T:
return "numbered mode";
case LCP_CO_ML_T:
return "multi-link procedure";
case LCP_CO_CALLBACK_T:
return "callback";
case LCP_CONNECT_TIME_T:
return "connect time";
case LCP_COMPOUND_T:
return "compound frames";
case LCP_NOMICAL_T:
return "nominal data encapsulation";
case LCP_CO_MRRU_T:
return "MRRU";
case LCP_CO_SSNHF_T:
return "MP short sequence #s";
case LCP_CO_ENDPOINT_T:
return "Endpoint Discriminator";
case LCP_CO_BACP_LINK_T:
return "BACP Link Discriminator";
default:
(void)sprintf(buf,"unknown option #%#x",type);
return buf;
}
}
/* shape of an LCP packet */
STRUCT_PKT {
u_char code;
u_char id;
u_short len;
union {
STRUCT_CF { /* Configure-Req, Ack, Nak, Reject */
u_char type;
u_char len;
union {
u_char info[1];
u_char accm[4];
u_char mru[2];
u_char auth[2];
struct {
u_char proto[2];
} pap;
struct {
u_char proto[2];
u_char algorithm;
} chap;
u_char magic[4];
u_char s_d_pad;
u_char mrru[2];
struct ep {
u_char class;
u_char addr[LCP_CO_ENDPOINT_MAX];
} ep;
struct {
u_char class;
u_char addr[4];
} ep_ip;
struct {
u_char class;
u_char addr[6];
} ep_mac;
struct {
u_char class;
char irix[4];
u_char sn[4];
} ep_sgi;
} cf_un;
} cf[1];
struct { /* Protocol-Reject */
u_char proto[2];
u_char info[1];
} pj;
struct { /* Echo-Request and Echo-Reply */
__uint32_t magic;
struct timeval ts; /* 0 or more bytes of data */
} echo;
struct { /* Code-Reject */
u_char cj_code;
u_char cj_id;
u_short cj_len;
u_char cj_info[1];
} cj;
struct ppp_ident ident; /* Identification */
} lcp_un;
};
/* make a string to display 256-bit TX ACCM
*/
static char*
tx_accm_str(PPP_FM accm[PPP_ACCM_LEN])
{
static char str[2+10*8+1];
PPP_FM laccm[PPP_ACCM_LEN];
int i;
/* do not mention the escaping of the frame and escape bytes
* unless debugging is very high.
*/
bzero(laccm,sizeof(laccm));
if (debug < 6) {
PPP_SET_ACCM(laccm,PPP_FLAG);
PPP_SET_ACCM(laccm,PPP_ESC);
}
for (i = 0; i < PPP_ACCM_LEN; i++)
laccm[i] = accm[i] & ~laccm[i];
(void)sprintf(&str[2], "%08x%08x%08x%08x%08x%08x%08x%08x",
laccm[7], laccm[6],
laccm[5], laccm[4],
laccm[3], laccm[2],
laccm[1], laccm[0]);
i = strspn(&str[2], "0")+2;
if (str[i] == '\0') {
i--;
} else {
str[--i] = 'x';
str[--i] = '0';
}
return &str[i];
}
char *
phase_name(enum ppp_phase phase)
{
static char *names[] = {
"Dead",
"Establish",
"Authenticate",
"Terminate",
"Network"
};
static char bad[] = "xxxxxxxxxx";
if (phase <= NET_PHASE)
return names[phase];
sprintf(bad, "%d", phase);
return bad;
}
void
log_phase(struct ppp *ppp,
enum ppp_phase new_phase)
{
log_debug(2,ppp->link_name,"entering %s Phase",
phase_name(new_phase), 0,0);
ppp->phase = new_phase;
}
/* Set LCP transmit parameters and anything else that should be requested
* each time LCP is reset.
* LCP is reset when a link is first started and upon the receipt of
* a Configure-Request.
*/
void
lcp_param(struct ppp *ppp)
{
int min_frag_size;
L.fsm.restart_ms = ppp->conf.restart_ms;
L.fsm.restart_ms_lim = ppp->conf.restart_ms_lim;
/* cannot start MP on a second link or if IPCP or CCP are alive */
L.refuse_mp = (!mp_on && mp_known);
L.neg_mp = (!L.refuse_mp && !L.seen_mp_rej);
L.refuse_recv_ssn = (L.refuse_mp
|| !ppp->conf.recv_ssn
|| (!mp_recv_ssn && mp_known));
L.neg_recv_ssn = !L.refuse_recv_ssn;
L.neg_send_ssn = (ppp->conf.send_ssn
&& L.neg_mp
&& (mp_send_ssn || !mp_known));
/* Compute the various hoped-for and required MTUs and MRUs.
*
* ppp->conf.ip_mtu is used to reduce the IP MTU below 1500.
*
* ppp->conf.frag_size sets the minimum fragment size,
* and so bounds below the per-link MTU. You cannot
* force a length of more than 1500. If the minimum fragment
* size is not set, then try for 1500 but go along with the
* peer if it wants something smaller.
*
* Notice if the kernel is already committed to an IP MTU.
* If MP is possible, the MTU is unrestrained, but the MTRU
* is controlled by the kernel's value.
*/
min_frag_size = (ppp->conf.frag_size < PPP_MIN_MTU
? PPP_MIN_MTU
: MIN(ppp->conf.frag_size, PPP_DEF_MRU));
if (kern_ip_mtu > 0) {
L.min_mtru = kern_ip_mtu;
L.tgt_mtru = kern_ip_mtu;
} else if (ppp->conf.ip_mtu < PPP_MIN_MTU) {
L.min_mtru = min_frag_size;
L.tgt_mtru = MAX(L.min_mtru, PPP_DEF_MRU);
} else {
L.min_mtru = MIN(ppp->conf.ip_mtu, PPP_DEF_MRU);
L.tgt_mtru = ppp->conf.ip_mtu;
}
L.min_mtu = L.neg_mp ? min_frag_size : L.min_mtru;
L.tgt_mtu = MAX(L.min_mtu, L.tgt_mtru);
L.tgt_mru = L.tgt_mtu;
L.tgt_mrru = L.tgt_mtru;
if (L.neg_mtru == 0)
L.neg_mtru = MIN(L.tgt_mtru, PPP_DEF_MRU);
if (L.neg_mtu == 0)
L.neg_mtu = MIN(L.tgt_mtu, PPP_DEF_MRU);
/* adjust for optional protocols */
if (L.neg_mp) {
L.tgt_mtu += PPP_MP_MAX_OVHD;
L.tgt_mru += PPP_MP_MAX_OVHD;
}
if (MIGHT_TX_PRED1) {
L.tgt_mtu += PRED_OVHD;
L.tgt_mtru += PRED_OVHD;
}
if (MIGHT_RX_PRED1) {
L.tgt_mru += PRED_OVHD;
L.tgt_mrru += PRED_OVHD;
}
/* The actual values of the MRU and MRRU do not matter to us,
* because we can always accept any size up to the maximum.
* The object of all of this is to pick values that the peer
* will accept with arguing. The values of the MTU and MTRU do
* matter, because we cannot send packets larger than their limits.
*
* If our MRU is larger than the default, then inflate it to reduce
* Naks from peers that like bridging. Do not inflate unless
* it is already not the default to avoid sending an unneeded
* configure-request.
*/
if (L.tgt_mru != PPP_DEF_MRU && L.tgt_mru < PPP_NONDEF_MRU)
L.tgt_mru = PPP_NONDEF_MRU;
/* Make the target MRU at least the default to speed negotiations,
* since the default is always possible.
*/
if (L.tgt_mtu < PPP_DEF_MRU)
L.tgt_mtu = PPP_DEF_MRU;
/* Always inflate the MRRU because it is always sent if
* it is relevant--i.e. when multilink is possible.
*/
if (L.tgt_mrru < PPP_NONDEF_MRU)
L.tgt_mrru = PPP_NONDEF_MRU;
/* Configure-Nak values from the peer or what we have sent
* in Configure-Requests should stick.
*/
if (L.neg_mru != 0)
L.tgt_mru = L.neg_mru;
if (L.neg_mrru != 0)
L.tgt_mrru = L.neg_mrru;
if (L.tgt_mtu > PPP_MAX_MTU)
L.tgt_mtu = PPP_MAX_MTU;
if (L.tgt_mtru > PPP_MAX_MTU)
L.tgt_mtru = PPP_MAX_MTU;
if (L.tgt_mru > PPP_MAX_MTU)
L.tgt_mru = PPP_MAX_MTU;
if (L.tgt_mrru > PPP_MAX_MTU)
L.tgt_mrru = PPP_MAX_MTU;
if (L.conf.echo_int < 0)
L.conf.echo_int = DEF_ECHO_INT;
if (L.conf.echo_fail < 0 && L.conf.echo_int != 0)
L.conf.echo_fail = MAX(3, DEF_ECHO_RTT/L.conf.echo_int);
}
/* send link parameters to the kernel, for all input and later IP output
*/
int /* <0 on failure */
lcp_set(struct ppp *ppp,
int force) /* 1=print results even if unchanged */
{
char dbg[80];
struct ppp_arg arg;
int res;
if (ppp->dv.sync == SYNC_DEFAULT)
log_complain(LNAME, "sync unknown before SIOC_PPP_LCP");
bzero(&arg,sizeof(arg));
if (ppp->dv.sync == SYNC_ON) {
arg.v.lcp.bits |= SIOC_PPP_LCP_SYNC;
} else {
if (ppp->dv.sync == SYNC_OFF
&& (!PPP_ASYNC_MAP(PPP_ESC, L.tx_accm)
|| !PPP_ASYNC_MAP(PPP_FLAG, L.tx_accm)))
log_complain(LNAME, "bad ACCM");
bcopy(L.tx_accm, arg.v.lcp.tx_accm,
sizeof(arg.v.lcp.tx_accm));
arg.v.lcp.rx_accm = L.rx_accm;
}
if (L.neg_acomp)
arg.v.lcp.bits |= SIOC_PPP_LCP_ACOMP;
if (L.neg_pcomp)
arg.v.lcp.bits |= SIOC_PPP_LCP_PCOMP;
res = 0;
if (force
|| bcmp(&arg.v.lcp, &L.arg_lcp_set, sizeof(L.arg_lcp_set))) {
(void)sprintf(dbg, "%s acomp=%d pcomp=%d rx_ACCM=%#x",
ppp->dv.sync ? "sync" : "async",
L.neg_acomp, L.neg_pcomp,
arg.v.lcp.rx_accm);
log_debug(2,LNAME, "set %s tx=%s", dbg,
tx_accm_str(arg.v.lcp.tx_accm));
if (0 <= (res = do_strioctl(ppp, SIOC_PPP_LCP, &arg,
"ioctl(SIOC_PPP_LCP)")))
bcopy(&arg.v.lcp, &L.arg_lcp_set,
sizeof(L.arg_lcp_set));
}
return res;
}
/* reset ACCM to the most conservative value
*/
int /* <0 on failure */
reset_accm(struct ppp *ppp,
int force) /* 1=print results even if unchanged */
{
lcp_param(ppp);
if (ppp->dv.sync == SYNC_DEFAULT)
log_complain(LNAME, "sync unknown before ACCM set");
L.neg_pcomp = 0;
L.neg_acomp = 0;
if (ppp->dv.sync == SYNC_ON) {
bzero(L.tx_accm, sizeof(L.tx_accm));
L.rx_accm = 0;
L.cr_accm = 0;
} else {
bcopy(L.conf.accm, L.tx_accm, sizeof(L.tx_accm));
L.tx_accm[0] = PPP_ACCM_DEF;
PPP_SET_ACCM(L.tx_accm,PPP_FLAG);
PPP_SET_ACCM(L.tx_accm,PPP_ESC);
if (L.parity_accm)
L.tx_accm[PPP_ACCM_LEN/2] = PPP_ACCM_DEF;
if (L.use_rx_accm == RX_ACCM_ON)
L.rx_accm = PPP_ACCM_DEF;
if (L.use_rx_accm != RX_ACCM_SET)
L.cr_accm = 0;
}
return lcp_set(ppp, force);
}
static int /* 1=got an address */
get_loc_mac(void)
{
if (have_loc_mac == 0) {
if (if_mac_addr(0,0,&loc_mac) != 0) {
have_loc_mac = 1;
} else {
have_loc_mac = -1;
bzero(&loc_mac,sizeof(loc_mac));
}
}
return have_loc_mac;
}
/* get LCP going
*/
void
lcp_init(struct ppp *ppp)
{
L.fsm.protocol = PPP_LCP;
L.fsm.ops = &lcp_ops;
/* At first the RX ACCM should be very conservative. */
if (L.use_rx_accm == RX_ACCM_SET)
L.use_rx_accm = RX_ACCM_ON;
L.cr_accm = 0;
L.my_magic = sysid(0);
L.bad_magic = 0;
L.auth_suggest = 0;
L.auth_refuse = 0;
A.peer_proto = 0;
L.seen_epdis_rej = 0;
L.seen_mru_nak_rej = 0;
L.seen_mp_rej = 0;
L.seen_pcomp_rej = 0;
L.seen_acomp_rej = 0;
L.ident_off = L.conf.ident_off;
L.ident_id = 0;
L.echo_seen = 0;
L.echo_next_id += L.conf.echo_fail+1;
bzero(&L.ident_rcvd, sizeof(L.ident_rcvd));
bzero(&L.arg_lcp_set,sizeof(L.arg_lcp_set));
bzero(&L.arg_mp_set,sizeof(L.arg_mp_set));
L.arg_mp_set.max_frag = PPP_MAX_MTU;
ppp->loc_epdis[0] = '\0';
ppp->rem_epdis[0] = '\0';
L.neg_mtu = PPP_DEF_MRU;
L.neg_mru = 0;
L.neg_mtru = PPP_DEF_MRU;
L.neg_mrru = 0;
lcp_param(ppp);
fsm_init(&L.fsm);
A.timer.tv_sec = TIME_NEVER;
}
/* send protocol reject if LCP is in OPENED state
*/
void
lcp_pj(struct ppp *ppp, char* name)
{
if (L.fsm.state != FSM_OPENED_9)
return;
obuf.proto = PPP_LCP;
obuf.bits = (mp_ncp_bits != 0) ? ibuf.bits : 0;
OBUF_CP->code = PPP_CODE_PJ;
OBUF_CP->id = ++L.fsm.id;
OBUF_CP->len = MIN(L.neg_mtu-2, 6+ibuf_info_len);
OBUF_CP->lcp_un.pj.proto[0] = ibuf.proto>>8;
OBUF_CP->lcp_un.pj.proto[1] = ibuf.proto;
bcopy(&ibuf.un.info[0], &OBUF_CP->lcp_un.pj.info[0],
OBUF_CP->len-6);
log_debug(2,name,"send Protocol-Reject ID=%#x", L.fsm.id);
ppp_send(ppp, &obuf, OBUF_CP->len);
}
/* Make something happen to the LCP machine.
*/
void
lcp_event(struct ppp *ppp,
enum fsm_event ev)
{
switch (ev) {
case FSM_UP: /* the modem is alive */
case FSM_DOWN: /* the modem died */
case FSM_OPEN: /* start work */
fsm_run(ppp,&L.fsm,ev);
break;
case FSM_CLOSE: /* start to shut down */
fsm_run(ppp,&L.fsm,ev);
log_phase(ppp,TERM_PHASE);
break;
case FSM_TO_P: /* the restart timer has expired */
if (L.fsm.restart <= 0)
ev = FSM_TO_M;
fsm_run(ppp,&L.fsm,ev);
break;
default:
log_complain(LNAME,"unknown event #%d", ev);
}
}
static struct {
int min; /* minimum length */
int max; /* maximum length */
char *name; /* name of type */
} ep_info[] = {
{0, 0, "Null"}, /* class 0 */
{1, 20, "Locally Assigned"}, /* class 1 */
{4, 4, "IP"}, /* class 2 */
{6, 6, "MAC"}, /* class 3 */
{4, 20, "Magic"}, /* class 4 */
{1, 15, "Phone #"} /* class 5 */
};
/* convert Endpoint Discriminator to ASCII
*/
static void
ep2ascii(char *buf,
struct ep *ep,
int len)
{
int t;
int i;
u_int n;
struct in_addr its_addr;
switch (ep->class) {
case LCP_CO_ENDPOINT_NULL:
*buf = '\0';
return;
case LCP_CO_ENDPOINT_IP:
its_addr.s_addr = ((ep->addr[0]<<24)
+ (ep->addr[1]<<16)
+ (ep->addr[2]<<8)
+ ep->addr[3]);
(void)strcpy(buf, inet_ntoa(its_addr));
return;
case LCP_CO_ENDPOINT_MAC:
(void)sprintf(buf,"%x:%x:%x:%x:%x:%x",
ep->addr[0],ep->addr[1],ep->addr[2],
ep->addr[3],ep->addr[4],ep->addr[5]);
return;
case LCP_CO_ENDPOINT_MAGIC:
(void)sprintf(buf, "%d-%d", ep->class, len);
i = 0;
while (len > 1) {
buf += strlen(buf);
n = ep->addr[i++];
while (i%4 != 0 && len > 1) {
n = (n<<8) + ep->addr[i++];
len--;
}
(void)sprintf(buf,"-%#x", n);
}
return;
case LCP_CO_ENDPOINT_LOC:
case LCP_CO_ENDPOINT_PHONE:
default:
break;
}
(void)sprintf(buf, "%d-%d-", ep->class, len);
buf += strlen(buf);
/* trim trailing zeros */
while (len > 1 && ep->addr[len-2] == 0)
len--;
/* translate the rest to ASCII, recognizing safe ASCII bytes
*/
t = 0;
for (i = 1; i < len; i++) {
if (!t && (isalnum(ep->addr[i-1])
|| ep->addr[i-1] == '+'
|| ep->addr[i-1] == '-'
|| ep->addr[i-1] == '_'
|| ep->addr[i-1] == '@'
|| ep->addr[i-1] == '='
|| ep->addr[i-1] == '#'
|| ep->addr[i-1] == '%')) {
*buf++ = ep->addr[i-1];
} else {
t = 1;
(void)sprintf(buf, "%02x", ep->addr[i-1]);
buf += 2;
}
}
*buf = '\0';
}
/* process result from FSM
*/
static void
lcp_act(struct ppp *ppp,
enum fsm_action act)
{
static char pat[] = "MTU=%d MRU=%d %s%s%s%s%s%s%s%s";
char str[sizeof(pat)+8*12+10*8+6*8];
char bigxmit_str[8+8+1+1];
char mtru_str[4+8+1+1];
char mrru_str[4+8+1+1];
int i;
struct ppp *ppp1;
log_debug(2, LNAME,"action %s", fsm_action_name(act));
switch (act) {
case FSM_TLU:
/* dump configuration */
(void)sprintf(bigxmit_str, "bigxmit=%d ", bigxmit);
if (L.neg_mp) {
(void)sprintf(mtru_str, "MTRU=%d ", L.neg_mtru);
(void)sprintf(mrru_str, "MRRU=%d ", L.neg_mrru);
} else {
mtru_str[0] = '\0';
mrru_str[0] = '\0';
}
(void)sprintf(str, pat, L.neg_mtu, L.neg_mru,
mtru_str, mrru_str,
(bigxmit > 0) ? bigxmit_str : "",
telnettos ? "TOS " : "",
L.neg_pcomp ? "PCOMP " : "",
L.neg_acomp ? "ACOMP " : "",
(L.neg_mp && L.neg_send_ssn) ? "send-SSN " : "",
(L.neg_mp && L.neg_recv_ssn) ? "recv-SSN" : "");
log_debug(1, LNAME,"%s", str);
if (ppp->dv.sync == SYNC_ON) {
(void)strcpy(str,"sync ");
} else {
/* stop using LCP default ACCM */
bcopy(L.conf.accm, L.tx_accm, sizeof(L.tx_accm));
L.tx_accm[0] |= L.cr_accm;
if (L.parity_accm)
L.tx_accm[PPP_ACCM_LEN/2] |= L.cr_accm;
if (L.use_rx_accm == RX_ACCM_ON
|| L.use_rx_accm == RX_ACCM_SET) {
L.use_rx_accm = RX_ACCM_SET;
L.rx_accm = L.cr_accm | L.conf.accm[0];
}
(void)sprintf(str, "rx_ACCM=%#x tx=%s",
(int)L.rx_accm,
tx_accm_str(L.tx_accm));
PPP_SET_ACCM(L.tx_accm,PPP_FLAG);
PPP_SET_ACCM(L.tx_accm,PPP_ESC);
}
log_debug(1, LNAME, "my magic #=%#x,peer's=%#x %s",
L.my_magic, L.his_magic, str);
if (lcp_set(ppp,0) < 0) {
ipcp_event(ppp, FSM_CLOSE);
break;
}
lcp_send_ident(ppp, L.my_magic);
L.echo_old_id = (L.echo_next_id += L.conf.echo_fail);
L.echo_timer.tv_sec = TIME_NOW;
(void)strcpy(ppp->loc_epdis, loc_epdis);
(void)strcpy(ppp->rem_epdis, rem_epdis);
log_phase(ppp,AUTH_PHASE);
auth_start(ppp); /* start authenticating */
break;
case FSM_TLD:
/* clear ACCM, stop authentication, and block IP on this link
*/
(void)reset_accm(ppp,0);
A.timer.tv_sec = TIME_NEVER;
if (ppp->dv.dev_index != -1)
(void)do_strioctl_ok(ppp, SIOC_PPP_TX_OK,
0, "ioctl(LCP TLD TX_OK off)");
/* Count active links in the MP bundle.
*/
i = 0;
if (mp_on) {
for (ppp1 = &ppp0;
ppp1 != 0;
ppp1 = (struct ppp*)ppp1->dv.next) {
if (ppp1 != ppp
&& ppp1->lcp.fsm.state == FSM_OPENED_9)
i++;
}
}
/* If this is the only active link in the bundle
* (or using BF&I multilink), tell the kernel to kill IP
* and restart all NCPs
*/
if (i == 0) {
ipcp_event(ppp,FSM_DOWN);
if (ppp->ccp.fsm.state >= FSM_STARTING_1)
ccp_event(ppp,FSM_DOWN);
}
break;
case FSM_TLS:
if (0 > reset_accm(ppp,0)) {
ipcp_event(ppp, FSM_CLOSE);
break;
}
log_phase(ppp,ESTAB_PHASE);
break;
case FSM_TLF:
log_phase(ppp,DEAD_PHASE);
hangup(ppp);
break;
}
}
/* parse a received Configure-Request
*/
static enum fsm_event
lcp_parse_cr(struct ppp *ppp)
{
struct lcp_cf *icfp;
struct lcp_cf *jcfp = BASE_P(JBUF_CP);
struct lcp_cf *ncfp = BASE_P(NBUF_CP);
register u_int l;
register int i;
int complained, seen_mtu, seen_mtru;
char offered[50], buf[SYSNAME_LEN+1];
# define BAD_LEN(cond, L) {if (icfp->len cond (L)) { \
log_complain(LNAME, " reject %s with strange length=%d", \
lcp_opt_name(icfp->type), icfp->len); \
complained = 1; \
break;}}
/* skip the NAK since we are sending a REJECT */
# define SKIP_NAK() (jcfp != BASE_P(JBUF_CP))
log_debug(2,LNAME,"receive Configure-Request ID=%#x", IBUF_CP->id);
/* "all options are always negotiated at once," so a missing
* option implies the default.
*/
seen_mtu = 0;
L.neg_mtu = MIN(L.tgt_mtu, PPP_DEF_MRU);
seen_mtru = 0;
L.neg_mtru = MIN(L.tgt_mtru, PPP_DEF_MRU);
if (!mp_on || !mp_known)
L.neg_mp = 0;
L.neg_send_ssn = 0;
L.cr_accm = (ppp->dv.sync == SYNC_ON) ? 0 : PPP_ACCM_DEF;
A.peer_proto = 0;
L.neg_pcomp = 0;
L.neg_acomp = 0;
for (icfp = BASE_P(IBUF_CP);
icfp < (struct lcp_cf*)&ibuf.un.info[ibuf_info_len];
ADV(icfp)) {
/* forget entire packet if the length of one option is bad */
if (icfp->len < 2) {
log_complain(LNAME, " bad option length %d",
icfp->len);
return FSM_RUC;
}
complained = 0;
switch (icfp->type) {
case LCP_CO_MRU_T:
/* This is the MRU of the peer or our MTU.
*
* Immediately agree to any MTU if it is bigger than
* what we want (or later, need).
*
* If the value of the peer is too small, Nak it in
* the early rounds. If the value we need is no
* larger than the default, then eventually reject the
* option to force the peer to use the default.
* If we need more than the default, Nak forever.
*/
BAD_LEN(!=,LCP_CO_MRU_L);
l = GET_S(icfp,mru);
seen_mtu = 1;
/* Give the peer a chance to give our absolute minimum.
*/
i = L.tgt_mtu;
if (l < i && !EARLY_NAK_SENT())
i = MAX(L.min_mtu, l);
if (l < i) {
if (i > PPP_DEF_MRU) {
if (SKIP_NAK())
continue;
log_cd(!EARLY_NAK_SENT(),2,LNAME,
" Nak MTU=%d with %d",
l, i);
ADD_CO_SHORT(ncfp, i, mru);
GEN_CO(ncfp, LCP_CO_MRU_T, mru);
continue;
}
log_complain(LNAME, " reject peer's MTU=%d"
" instead of our minimum %d"
" to force %d, but use %d",
l, L.min_mtu,
PPP_DEF_MRU, L.neg_mtu);
complained = 1;
break;
}
/* Accept it, but do not use it
* if it is bigger than we need.
*/
if (l > L.tgt_mtu) {
L.neg_mtu = L.tgt_mtu;
log_debug(2,LNAME,
" accept MTU=%d but use %d",
l, L.neg_mtu);
} else {
L.neg_mtu = l;
log_debug(2,LNAME,
" accept MTU=%d", L.neg_mtu);
}
continue;
case LCP_CO_MRRU_T:
BAD_LEN(!=,LCP_CO_MRRU_L);
l = GET_S(icfp,mrru);
if (L.refuse_mp) {
/* Reject multilink if not ok */
log_cd(!EARLY_NAK_SENT(),2,LNAME,
" reject MP MRRU of %d%s",
l,
(L.seen_mp_rej
? " because peer rejected MP" : ""));
complained = 1;
break;
}
L.neg_mp = 1; /* note multilink */
seen_mtru = 1;
/* Give the peer a chance to give our absolute minimum.
*/
i = L.tgt_mtru;
if (l < i && !EARLY_NAK_SENT())
i = MAX(L.min_mtru, l);
if (l < i) {
/* Keep trying for a useful MP MTRU if we
* have not tried hard or if we have
* MP on other links.
*/
if (EARLY_NAK_SENT() || (mp_on && mp_known)) {
if (SKIP_NAK())
continue;
log_cd(!EARLY_NAK_SENT(),2,LNAME,
" Nak MP MTRU=%d with %d",
l, i);
ADD_CO_SHORT(ncfp, i, mrru);
GEN_CO(ncfp, LCP_CO_MRRU_T, mrru);
continue;
}
/* Otherwise, give up on MP */
log_complain(LNAME, " Reject MP MTRU=%d"
" instead of %d; give up on MP",
l, i);
L.neg_mp = 0;
L.neg_send_ssn = 0;
complained = 1;
break;
}
/* Accept it, but do not use it
* if it is bigger than we want.
*/
if (l > L.tgt_mtru) {
L.neg_mtru = L.tgt_mtru;
log_debug(2,LNAME,
" accept MP MTRU=%d but use %d",
l, L.neg_mtru);
} else {
L.neg_mtru = l;
log_debug(2,LNAME, " accept MP MTRU=%d",
L.neg_mtru);
}
continue;
case LCP_CO_ACCM_T:
BAD_LEN(!=,LCP_CO_ACCM_L);
L.cr_accm = GET_L(icfp,accm);
/* Turn off ACCM quickly to ensure we do not
* discard un-escaped bytes from the other guy.
* After hearing his TX ACCM, make a note to not
* ignore bytes that he might not be escaping.
* This is to deal with the race of our switching
* to a conservative RX ACCM before he switches
* to a conservative TX ACCM.
*/
if (L.use_rx_accm == RX_ACCM_ON
|| L.use_rx_accm == RX_ACCM_SET) {
L.use_rx_accm = RX_ACCM_SET;
L.rx_accm = L.cr_accm | L.conf.accm[0];
}
/* Nak if our peer does not want enough bits.
* Generate the Nak now to ensure we do it
* in the right order.
*/
if (0 != (L.conf.accm[0] & ~L.cr_accm)
&& ppp->dv.sync == SYNC_OFF) {
if (SKIP_NAK())
continue;
ADD_CO_LONG(ncfp, L.conf.accm[0] | L.cr_accm,
accm);
GEN_CO(ncfp, LCP_CO_ACCM_T, accm);
log_debug(2,LNAME," accept tx ACCM=0x%08x"
" but suggest 0x%08x",
L.cr_accm,
(L.conf.accm[0] | L.cr_accm));
} else {
log_debug(2,LNAME," accept tx ACCM=0x%08x",
L.cr_accm);
}
continue;
case LCP_CO_AUTH_T:
BAD_LEN(<,4);
/* identify the offered authentication */
i = GET_S(icfp,auth);
if (i == LCP_CO_CHAP_P) {
if (icfp->len != LCP_CO_CHAP_L) {
i = 0;
sprintf(offered, "CHAP (length %d)",
icfp->len);
} else if (icfp->cf_un.chap.algorithm
!= LCP_CO_CHAP_MD5) {
i = 0;
sprintf(offered,"CHAP (algorithm %d)",
icfp->cf_un.chap.algorithm);
} else {
strcpy(offered, "CHAP");
}
} else if (i == LCP_CO_PAP_P) {
if (icfp->len != LCP_CO_PAP_L) {
sprintf(offered,"PAP (length %d)",
icfp->len);
i = 0;
} else {
strcpy(offered,"PAP");
}
} else {
strcpy(offered,"unknown");
}
/* If we have some authentication information,
* or if we might reconfigure and get it, and
* we recognize the protocol,
* then remember to send authenticate.
* If we have some authentication info but
* of the wrong kind, then Nak the request.
*/
if (i == LCP_CO_CHAP_P
&& A.want_sendchap_response == 1) {
A.peer_proto = PPP_CHAP;
log_debug(2,LNAME,
" note CHAP (MD5)");
continue;
}
if (i == LCP_CO_PAP_P
&& A.want_sendpap == 1) {
A.peer_proto = PPP_PAP;
log_debug(2,LNAME,
" note PAP");
continue;
}
if (A.want_sendchap_response == 1) {
if (SKIP_NAK())
continue;
ADD_CO_SHORT(ncfp, LCP_CO_CHAP_P, chap.proto);
ncfp->cf_un.chap.algorithm = LCP_CO_CHAP_MD5;
GEN_CO(ncfp, LCP_CO_AUTH_T, chap);
log_debug(2,LNAME,
" Nak %s by suggesting CHAP (MD5)",
offered);
continue;
}
if (A.want_sendpap == 1) {
if (SKIP_NAK())
continue;
ADD_CO_SHORT(ncfp, LCP_CO_PAP_P, pap.proto);
GEN_CO(ncfp, LCP_CO_AUTH_T, pap);
log_debug(2,LNAME,
" Nak %s by suggesting PAP",
offered);
continue;
}
/* Otherwise, reject it and do not proceed
* past the Establish Phase.
*/
log_cd(!EARLY_NAK_SENT(),2,LNAME,
" reject request of %s from us",
offered);
complained = 1;
break;
case LCP_CO_LQ_T:
/* reject it */
log_cd(!EARLY_NAK_SENT(),2,LNAME,
" Reject link quality");
complained = 1;
break;
case LCP_CO_MAGIC_T:
BAD_LEN(!=,LCP_CO_MAGIC_L);
L.his_magic = GET_L(icfp,magic);
if (L.his_magic == 0) {
log_complain(LNAME, " peer requesting"
" bogus magic #=0");
continue;
}
/* reject magic if it has been turned off */
if (L.my_magic == 0) {
log_complain(LNAME, " reject magic #s since"
" peer rejected them");
complained = 1;
break;
}
if (L.his_magic != L.my_magic) {
log_debug(2,LNAME, " peer's magic #=%#x",
L.his_magic);
} else {
if ((L.fsm.nak_sent >= ppp->conf.max_fail
|| !EARLY_NAK_SENT())
&& !L.bad_magic) {
log_complain(LNAME,
" Probable loopback;"
" both magic #s=%#x",
L.his_magic);
L.bad_magic = 1;
} else {
log_debug(2,LNAME,
" both magic #s=%#x",
L.his_magic);
}
if (L.fsm.nak_sent >= ppp->conf.max_fail-1
&& L.fsm.nak_sent >= 4) {
log_complain(LNAME," Stop worrying"
" about magic #s"
" and hope");
L.my_magic = 0;
} else {
if (SKIP_NAK())
continue;
log_debug(2,LNAME,
" Nak for new magic #");
L.my_magic = newmagic();
ADD_CO_LONG(ncfp, L.my_magic, magic);
GEN_CO(ncfp, LCP_CO_MAGIC_T, magic);
}
}
continue;
case LCP_CO_PCOMP_T:
BAD_LEN(!=,LCP_CO_PCOMP_L);
if (L.conf.pcomp) {
L.neg_pcomp = 1;
log_debug(2,LNAME," send compressed"
" protocol fields");
} else {
/* we do not have to send it */
log_debug(2,LNAME," ignore request to send"
" compressed protocol fields");
}
continue;
case LCP_CO_ACOMP_T:
BAD_LEN(!=,LCP_CO_ACOMP_L);
if (L.conf.acomp != 0) {
L.neg_acomp = 1;
log_debug(2,LNAME, " send compressed"
" address fields");
} else {
/* we do not have to send it */
log_debug(2,LNAME, " ignore request to send"
" compressed address fields");
}
continue;
case LCP_CO_SSNHF_T:
/* Go along with the peer if it wants to receive
* short MP sequence numbers, unless we already
* have an active MP bundle.
*/
BAD_LEN(!=,LCP_CO_SSNHF_L);
if (!L.refuse_mp)
L.neg_mp = 1; /* note multilink */
if (ppp->conf.send_ssn
&& !L.refuse_mp
&& (mp_send_ssn || !mp_known)) {
L.neg_send_ssn = 1;
log_debug(2,LNAME," will send short"
" MP sequence #s");
continue;
}
/* Reject if not ok */
log_cd(!EARLY_NAK_SENT(),2,LNAME,
" reject short MP sequence #s%s",
L.seen_mp_rej ? " because peer rejected MP":"");
complained = 1;
break;
case LCP_CO_ENDPOINT_T:
l = icfp->len;
i = icfp->cf_un.ep.class;
if (i > LCP_MAX_CO_ENDPOINT) {
log_complain(LNAME,
" reject bogus Endpoint"
" Discriminator type %d", i);
break;
}
if (l-3 < ep_info[i].min
|| l-3 >ep_info[i].max
|| (i == LCP_CO_ENDPOINT_MAGIC
&& (l-3)%4 != 0)) {
log_complain(LNAME,
" reject bogus %s"
" Endpoint Discriminator length"
" of %d",
ep_info[i].name, l-3);
complained = 1;
break;
}
if (i == LCP_CO_ENDPOINT_NULL) {
log_debug(2,LNAME,
" ignore %s Endpoint"
" Discriminator",
ep_info[LCP_CO_ENDPOINT_NULL].name);
continue;
}
ep2ascii(buf, &icfp->cf_un.ep, l-2);
if (!ppp->conf.recv_epdis) {
log_debug(2,LNAME,
" reject %s Endpoint Discriminator"
" %s; turned off",
ep_info[i].name, buf);
complained = 1;
break;
}
if (rem_epdis[0] != '\0'
&& strcmp(rem_epdis, buf)) {
char *msg = epdis_msg(ep_info[i].name,
buf, rem_epdis);
/* Allow the peer to change its discriminator
* only when starting the first link in a
* bundle.
* That means this is the only link, no other
* processes are dialing, and this LCP
* state machine must not have received
* reached OPENED.
*/
if (add_pid > 0 || numdevs > 1
|| (ppp->rem_epdis[0] != '\0'
&& strcmp(ppp->rem_epdis, buf))) {
set_tr_msg(&L.fsm, "inconsistent %.*s",
TR_MSG_MAX-14, msg);
return FSM_CLOSE;
}
log_debug(2,LNAME,
" inconsistent %s", msg);
rm_rend("ep-", rem_epdis);
} else {
log_debug(2,LNAME,
" %s Endpoint Discriminator %s",
ep_info[i].name, buf);
}
strncpy(rem_epdis, buf, sizeof(rem_epdis));
(void)add_rend_name("ep-", rem_epdis);
continue;
}
/* Reject an option we do not understand.
*/
bcopy(icfp, jcfp, icfp->len);
ADV(jcfp);
if (!complained)
log_cd(!EARLY_NAK_SENT(),2,LNAME,
" reject %s",
lcp_opt_name(icfp->type));
}
if (!SKIP_NAK()) {
/* If we did not receive an MRU option, have not been trying
* hard, and the default is smaller than we would like,
* then ask for a larger value
*/
if (!seen_mtu) {
if (EARLY_NAK_SENT())
i = L.tgt_mtu;
else
i = MAX(L.min_mtu, PPP_DEF_MRU);
if (i > MAX(L.neg_mtu, PPP_DEF_MRU)) {
log_debug(2, LNAME, " Nak for MTU=%d", i);
ADD_CO_SHORT(ncfp, i, mru);
GEN_CO(ncfp, LCP_CO_MRU_T, mru);
L.neg_mtu = i;
}
}
/* Deal with MP if we have not already.
* If we are sending a Configure-Reject for anything
* (including MP), SKIP_NAK() will have kept us from coming
* here.
*/
if (!seen_mtru && !L.refuse_mp && !L.seen_mp_rej) {
if (EARLY_NAK_SENT())
i = L.tgt_mtru;
else
i = MAX(L.min_mtru, PPP_DEF_MRU);
log_debug(2,LNAME," Nak for MP MTRU=%d", i);
ADD_CO_SHORT(ncfp, i, mrru);
GEN_CO(ncfp, LCP_CO_MRRU_T, mrru);
L.neg_mtru = i;
}
/* Ask to send short sequence numbers if we want them and there
* is a chance the peer might allow them.
* If other links in the bundle have them, ask in any case.
*/
if (!L.neg_send_ssn
&& ppp->conf.send_ssn
&& L.neg_mp
&& (mp_send_ssn || !mp_known)
&& (ncfp != BASE_P(NBUF_CP)
|| EARLY_NAK_SENT()
|| (mp_send_ssn && mp_known))) {
log_debug(2,LNAME,
" Nak to send short MP sequence #s");
GEN_CO_BOOL(ncfp, LCP_CO_SSNHF_T);
}
}
/* install the new ACCM and so forth */
(void)lcp_set(ppp,0);
rejbuf.proto = PPP_LCP;
JBUF_CP->len = 4+((char*)jcfp - (char*)BASE_P(JBUF_CP));
nakbuf.proto = PPP_LCP;
NBUF_CP->len = 4+((char*)ncfp - (char*)BASE_P(NBUF_CP));
return ((JBUF_CP->len == 4 && NBUF_CP->len == 4)
? FSM_RCR_P
: FSM_RCR_M);
#undef BAD_LEN
}
/* parse a received Configure-Nak
*/
static void
lcp_parse_cn(struct ppp *ppp)
{
struct lcp_cf *icfp;
int i;
# define BAD_LEN(cond,L) {if (icfp->len cond (L)) { \
log_complain(LNAME, " ignore %s Nak with strange length=%d", \
lcp_opt_name(icfp->type), icfp->len); \
break;}}
log_debug(2,LNAME,"receive Configure-NAK ID=%#x", IBUF_CP->id,0,0);
L.fsm.nak_recv++;
for (icfp = BASE_P(IBUF_CP);
icfp < (struct lcp_cf*)&ibuf.un.info[ibuf_info_len];
ADV(icfp)) {
/* forget entire packet if the length of one option is bad */
if (icfp->len < 2) {
log_complain(LNAME," bad CN option length %#x",
icfp->len);
break;
}
switch (icfp->type) {
case LCP_CO_MRU_T:
/* The packets that peer wants us to send us should
* be no larger than we want to receive.
*
* Refuse an impossibly large MRU.
*
* However, we can always receive big packets and
* there is no way to force the peer to send big
* packets, so go along with too large or small
* a value to get the link up.
*/
BAD_LEN(!=,LCP_CO_MRU_L);
i = GET_S(icfp,mru);
if (i < PPP_MIN_MTU) {
/* a value of 0 would mess up lcp_param() */
log_cd(!EARLY_NAK_RECV(),2,LNAME,
" ignore bad MRU=%d smaller than %d",
i, PPP_MIN_MTU);
break;
}
if (i <= PPP_MAX_MTU) {
L.tgt_mru = L.neg_mru = i;
log_debug(2,LNAME," accept MRU=%d", i);
break;
}
log_cd(!EARLY_NAK_RECV(),2,LNAME,
" ignore bad MRU=%d bigger than %d",
i, PPP_MAX_MTU);
break;
case LCP_CO_MRRU_T:
/* The MRRU option implies MP.
*/
BAD_LEN(!=,LCP_CO_MRRU_L);
i = GET_S(icfp,mrru);
if (L.refuse_mp) {
log_cd(!EARLY_NAK_RECV(),2,LNAME,
" ignore Nak for MP MRRU=%d%s",
i,
(L.seen_mp_rej
? " because peer rejected MP" : ""));
break;
}
L.neg_mp = 1;
/* The packets that peer wants us to send us should
* be no larger than we want to receive.
*
* Refuse an impossibly large MRRU.
*
* However, we can always receive big packets and
* there is no way to force the peer to send big
* packets, so go along with too a large or small
* but tolerable value to get the link up.
*/
if (i <= PPP_MAX_MTU) {
L.tgt_mrru = L.neg_mrru = i;
log_debug(2,LNAME," accept MP MRRU=%d", i);
break;
}
log_cd(!EARLY_NAK_RECV(),2,LNAME,
" ignore bad MP MRRU=%d but note MP", i);
break;
case LCP_CO_ACCM_T:
BAD_LEN(!=,LCP_CO_ACCM_L);
i = GET_L(icfp,accm);
if (ppp->dv.sync == SYNC_ON) {
log_debug(2,LNAME, " ignore rx ACCM=0x%08x"
" on sync line", i);
break;
}
L.cr_accm = i;
log_debug(2,LNAME," accept rx ACCM=0x%08x", i);
break;
case LCP_CO_AUTH_T:
BAD_LEN(<,4);
L.auth_refuse = A.our_proto;
i = GET_S(icfp,auth);
if (i == LCP_CO_CHAP_P) {
L.auth_suggest = PPP_CHAP;
log_cd(!EARLY_NAK_RECV(), 2,LNAME,
" peer wants to send CHAP responses");
} else if (i == LCP_CO_PAP_P) {
L.auth_suggest = PPP_PAP;
log_cd(!EARLY_NAK_RECV(), 2,LNAME,
" peer wants to send PAP requests");
} else {
log_complain(LNAME, " peer wants to send"
" authentication type %#x", i);
}
break;
case LCP_CO_MAGIC_T:
BAD_LEN(!=,LCP_CO_MAGIC_L);
if (L.my_magic == 0) {
log_complain(LNAME, " ignore magic #s"
" that peer rejected");
break;
}
L.his_magic = GET_L(icfp,magic);
if (L.his_magic == L.my_magic) {
do {
L.my_magic = newmagic();
} while (L.his_magic == L.my_magic);
log_debug(2,LNAME,
" accept peer's magic #=%#x"
" and pick new %#x",
L.his_magic, L.my_magic);
} else {
log_debug(2,LNAME,
" accept peer's magic #=%#x",
L.his_magic);
}
break;
case LCP_CO_SSNHF_T:
BAD_LEN(!=,LCP_CO_SSNHF_L);
if (L.refuse_recv_ssn || L.refuse_mp) {
if (!L.refuse_mp) {
L.neg_mp = 1;
log_debug(2,LNAME, " ignore"
" short MP sequence #s"
" but note MP");
} else {
log_debug(2,LNAME, " ignore"
" short MP sequence #s%s",
(L.seen_mp_rej ?
" because peer rejected MP"
: ""));
}
} else {
L.neg_mp = 1;
L.neg_recv_ssn = 1;
log_debug(2,LNAME,
" note MP short sequence #s");
}
break;
case LCP_CO_ENDPOINT_T:
default:
log_cd(EARLY_NAK_RECV(),2,LNAME, " ignore %s Nak",
lcp_opt_name(icfp->type));
break;
}
}
#undef BAD_LEN
}
/* parse a received Configure-Reject
*/
static void
lcp_parse_confj(struct ppp *ppp)
{
struct lcp_cf *icfp;
int i;
char *str;
log_debug(2,LNAME,"receive Configure-Reject ID=%#x", IBUF_CP->id);
L.fsm.nak_recv++;
icfp = BASE_P(IBUF_CP);
if (icfp >= (struct lcp_cf*)&ibuf.un.info[ibuf_info_len]) {
log_complain(LNAME,"empty Configure-Reject ID=%#x",
IBUF_CP->id);
}
for (;
icfp < (struct lcp_cf*)&ibuf.un.info[ibuf_info_len];
ADV(icfp)) {
/* forget entire packet if the length of one option is bad */
if (icfp->len < 2) {
log_complain(LNAME, " bad Configure-Reject option"
" length %#x",
icfp->len);
break;
}
str = 0;
switch (i = icfp->type) {
case LCP_CO_MRU_T:
L.tgt_mru = L.neg_mru = PPP_DEF_MRU;
L.seen_mru_nak_rej = 1;
break;
case LCP_CO_ACCM_T:
L.cr_accm = ((ppp->dv.sync == SYNC_ON)
? 0 : PPP_ACCM_DEF);
break;
case LCP_CO_AUTH_T:
/* For other Configure-Request options,
* we would stop asking. Authentication
* is different.
*/
L.auth_refuse = A.our_proto;
if (A.our_proto == PPP_CHAP) {
str = "peer rejected CHAP";
} else {
str = "peer rejected PAP";
}
break;
case LCP_CO_MAGIC_T:
L.my_magic = 0; /* no more magic numbers */
break;
case LCP_CO_PCOMP_T:
L.neg_pcomp = 0;
L.seen_pcomp_rej = 1;
break;
case LCP_CO_ACOMP_T:
L.neg_acomp = 0;
L.seen_acomp_rej = 1;
break;
case LCP_CO_MRRU_T:
/* accept rejection of MP only if possible */
if (mp_on && mp_known) {
str = "had MP; ignore rejection of MP and";
} else {
L.seen_mp_rej = 1;
L.refuse_mp = 1;
L.neg_mp = 0;
}
break;
case LCP_CO_SSNHF_T:
if (mp_recv_ssn && mp_known) {
str = "2nd link; ignore rejection of";
} else {
L.neg_recv_ssn = 0;
}
break;
case LCP_CO_ENDPOINT_T:
L.seen_epdis_rej = 1;
break;
default:
str = "ignore unsolicited rejection of";
break;
}
if (str == 0) {
log_debug(2,LNAME, " peer rejected %s",
lcp_opt_name(i));
} else {
log_complain(LNAME, " %s %s", str, lcp_opt_name(i));
}
}
/* review target parameters, especially MTU and MTRU */
lcp_param(ppp);
}
/* FSM action to send a Configure-Request
*/
static void
lcp_scr(struct ppp *ppp)
{
struct lcp_cf *p = BASE_P(OBUF_CP);
L.fsm.id++;
log_debug(2,LNAME,"send Configure-Request ID=%#x", L.fsm.id);
L.neg_mru = L.tgt_mru;
if (L.tgt_mru != PPP_DEF_MRU) {
ADD_CO_SHORT(p, L.tgt_mru, mru);
GEN_CO(p, LCP_CO_MRU_T, mru);
log_debug(2,LNAME," MRU=%d", L.tgt_mru);
}
L.neg_mrru = L.tgt_mrru;
if (L.neg_mp) {
ADD_CO_SHORT(p, L.tgt_mrru, mrru);
GEN_CO(p, LCP_CO_MRRU_T, mrru);
log_debug(2,LNAME," MP MRRU=%d", L.tgt_mrru);
if (L.neg_recv_ssn) {
GEN_CO_BOOL(p, LCP_CO_SSNHF_T);
log_debug(2,LNAME, " short MP sequence #s");
}
}
/* Always ask for authentication if we want it.
*
* If the choice between PAP and CHAP was not explicit in the
* control file (because only the passwords and user names were
* mentioned but not the choice), then prefer PAP.
* If both CHAP and PAP were explicitly configured, then try
* CHAP first.
*
* Do not honor a Configure-Reject from the peer rejection by
* ceasing requests for authentication. Instead wait for the
* link to go away as the peer continues to refuse authentication.
*
* If CHAP is refused by the peer, then switch to PAP.
*/
if ((A.want_recvchap_response == 1
&& (A.want_recvpap == 0
|| (!L.prefer_recvpap && L.auth_refuse != PPP_CHAP)
|| L.auth_refuse == PPP_PAP
|| L.auth_suggest == PPP_CHAP))
|| (A.want_recvchap_response < 0
&& A.want_recvpap <= 0
&& L.auth_suggest == PPP_CHAP)) {
A.our_proto = PPP_CHAP;
ADD_CO_SHORT(p, LCP_CO_CHAP_P, chap.proto);
p->cf_un.chap.algorithm = LCP_CO_CHAP_MD5;
GEN_CO(p, LCP_CO_AUTH_T, chap);
log_debug(2,LNAME," receive CHAP responses");
} else if (A.want_recvpap == 1
|| (A.want_recvpap < 0 && reconfigure)) {
A.our_proto = PPP_PAP;
ADD_CO_SHORT(p, LCP_CO_PAP_P, pap.proto);
GEN_CO(p, LCP_CO_AUTH_T, pap);
log_debug(2,LNAME," receive PAP requests");
} else {
A.our_proto = 0;
}
if ((ppp->dv.sync == SYNC_OFF && L.conf.accm[0] != PPP_ACCM_DEF)
|| (ppp->dv.sync == SYNC_ON && L.conf.accm[0] != 0)) {
ADD_CO_LONG(p, L.conf.accm[0] | L.cr_accm, accm);
GEN_CO(p, LCP_CO_ACCM_T, accm);
log_debug(2,LNAME," rx ACCM=0x%08x",
L.conf.accm[0] | L.cr_accm);
}
if (L.my_magic != 0) {
ADD_CO_LONG(p, L.my_magic, magic);
GEN_CO(p, LCP_CO_MAGIC_T, magic);
log_debug(2,LNAME," my magic #=%#x", L.my_magic);
}
if (L.conf.pcomp != 0
&& !L.seen_pcomp_rej) {
GEN_CO_BOOL(p, LCP_CO_PCOMP_T);
log_debug(2,LNAME, " receive compressed protocol fields");
}
if ((L.conf.acomp > 0
|| (L.conf.acomp < 0 && ppp->dv.sync == SYNC_OFF))
&& !L.seen_acomp_rej) {
GEN_CO_BOOL(p, LCP_CO_ACOMP_T);
log_debug(2,LNAME," receive compressed address fields");
}
/* Send Endpoint Discriminator of our IP address, if known.
* By default, do not send it if we are not trying for MP,
* because peers that do not support MP are likely to waste time
* sending a Configure-Reject for it.
* If we send them once, then continue sending them.
*/
if (!L.seen_epdis_rej
&& (loc_epdis[0] != '\0'
|| (L.neg_mp && ppp->conf.send_epdis < 0)
|| ppp->conf.send_epdis > 0)
&& (ppp->conf.send_epdis != LCP_CO_ENDPOINT_IP
|| (!def_lochost && lochost.sin_addr.s_addr != 0))) {
if (ppp->conf.send_epdis == LCP_CO_ENDPOINT_IP) {
p->cf_un.ep.class = LCP_CO_ENDPOINT_IP;
bcopy(&lochost.sin_addr.s_addr, p->cf_un.ep_ip.addr,
sizeof(p->cf_un.ep_ip.addr));
ep2ascii(loc_epdis, &p->cf_un.ep,
sizeof(p->cf_un.ep_ip));
GEN_CO(p, LCP_CO_ENDPOINT_T, ep_ip);
} else if (get_loc_mac() > 0
&& (ppp->conf.send_epdis == LCP_CO_ENDPOINT_MAC
|| ppp->conf.send_epdis < 0)) {
ppp->conf.send_epdis = LCP_CO_ENDPOINT_MAC;
p->cf_un.ep.class = LCP_CO_ENDPOINT_MAC;
bcopy(&loc_mac, p->cf_un.ep_mac.addr,
sizeof(p->cf_un.ep_mac.addr));
ep2ascii(loc_epdis, &p->cf_un.ep,
sizeof(p->cf_un.ep_mac));
GEN_CO(p, LCP_CO_ENDPOINT_T, ep_mac);
} else {
ppp->conf.send_epdis = LCP_CO_ENDPOINT_LOC;
p->cf_un.ep.class = LCP_CO_ENDPOINT_LOC;
(void)strncpy(p->cf_un.ep_sgi.irix, "IRIX",
sizeof(p->cf_un.ep_sgi.irix));
ADD_CO_LONG(p,sysid(0),ep_sgi.sn);
ep2ascii(loc_epdis, &p->cf_un.ep,
sizeof(p->cf_un.ep_sgi));
GEN_CO(p, LCP_CO_ENDPOINT_T, ep_sgi);
}
log_debug(2,LNAME, " %s Endpoint Discriminator %s",
ep_info[ppp->conf.send_epdis].name, loc_epdis);
}
obuf.proto = PPP_LCP;
obuf.bits = 0;
OBUF_CP->code = PPP_CODE_CR;
OBUF_CP->id = L.fsm.id;
OBUF_CP->len = 4+((char*)p - (char*)BASE_P(OBUF_CP));
ppp_send(ppp,&obuf,OBUF_CP->len);
}
/* FSM action to send a Terminate-Request,
*/
static void
lcp_str(struct ppp *ppp,
struct fsm *fsm)
{
(void)reset_accm(ppp,0);
fsm_str(ppp,fsm);
}
/* Complain if the other guy rejects a protocol he asked for
* or one we did not send
*/
static void
complain_rej(struct ppp *ppp,
int proto,
char *nm,
struct fsm *fsm)
{
log_complain(LNAME,"receive mysterious Protocol-Reject "
"%sfor protocol %#x%s",
ibuf.bits == 0 ? "" : "(MP) ",
proto, nm);
if (fsm != 0)
fsm_run(ppp, fsm, FSM_RXJ_M);
}
/* process incoming LCP frame
*/
void
lcp_ipkt(struct ppp *ppp)
{
/* change the ID after recognizing it to trash extras */
#define CK_ID() {if (fsm_ck_id(&L.fsm)) { \
L.cnts.bad_id++; \
return; \
}}
int i;
if (ibuf_info_len < (int)IBUF_CP->len) {
log_complain(LNAME,"dropping %s with %d bytes but claiming %d",
fsm_code_name(IBUF_CP->code),
ibuf_info_len, IBUF_CP->len);
L.cnts.bad_len++; /* bad packet length */
return;
}
switch (IBUF_CP->code) {
case PPP_CODE_CR: /* Configure-Request */
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
log_ipkt(0,LNAME,"receive bad Configure-Request:");
break;
}
fsm_run(ppp, &L.fsm, lcp_parse_cr(ppp));
break;
case PPP_CODE_CA: /* Configure-Ack */
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
log_ipkt(0,LNAME,"receive Configure-Ack:");
break;
}
CK_ID();
log_debug(2,LNAME,"receive Configure-ACK ID=%#x",IBUF_CP->id);
L.bad_magic = 0;
L.fsm.nak_recv = 0;
fsm_run(ppp, &L.fsm, FSM_RCA);
break;
case PPP_CODE_CN: /* Configure-Nak */
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
log_ipkt(0,LNAME,"receive Configure-Nak:");
break;
}
CK_ID();
lcp_parse_cn(ppp);
if (L.fsm.nak_recv > ppp->conf.max_fail) {
log_complain(LNAME,"giving up after %d"
" Configure-NAKs or -Rejects",
L.fsm.nak_recv);
fsm_run(ppp, &L.fsm, FSM_RXJ_M);
} else {
fsm_run(ppp, &L.fsm, FSM_RCN);
}
break;
case PPP_CODE_CONFJ: /* Configure-Reject */
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
log_ipkt(0,LNAME,"receive Configure-Reject:");
break;
}
CK_ID();
lcp_parse_confj(ppp);
if (L.fsm.nak_recv > ppp->conf.max_fail) {
log_complain(LNAME,"giving up after %d"
" Configure-NAKs or Rejects",
L.fsm.nak_recv);
fsm_run(ppp, &L.fsm, FSM_RXJ_M);
} else {
fsm_run(ppp, &L.fsm, FSM_RCN);
}
lcp_send_ident(ppp, 0);
break;
case PPP_CODE_TR: /* Terminate-Request */
log_ipkt(ibuf.bits == 0 ? 1 : 0,
LNAME,"receive Terminate-Request:");
if (ibuf.bits != 0) /* MP encapsulation is forbidden */
break;
fsm_run(ppp, &L.fsm, FSM_RTR);
break;
case PPP_CODE_TA: /* Terminate-Ack */
log_ipkt(ibuf.bits == 0 ? 2 : 0,
LNAME,"receive Terminate-Ack:");
if (ibuf.bits != 0) /* MP encapsulation is forbidden */
break;
fsm_run(ppp, &L.fsm, FSM_RTA);
break;
case PPP_CODE_CJ: /* Code-Reject */
/* If the peer is rejecting an identification packet,
* stop sending them.
*/
if (ibuf_info_len > 4+8
&& IBUF_CP->lcp_un.cj.cj_code == PPP_CODE_IDENT) {
log_debug(2,LNAME,"receive %s for %s",
fsm_code_name(PPP_CODE_CJ),
fsm_code_name(IBUF_CP->lcp_un.cj.cj_code));
L.ident_off = 1;
fsm_run(ppp, &L.fsm, FSM_RXJ_P);
break;
}
L.cnts.rcvd_code_rej++;
fsm_rcj(ppp, &L.fsm);
break;
case PPP_CODE_PJ: /* Protocol-Reject */
L.cnts.rcvd_prot_rej++;
i = ((IBUF_CP->lcp_un.pj.proto[0]<<8)
+ IBUF_CP->lcp_un.pj.proto[1]);
switch (i) {
case PPP_CCP:
log_debug(1,LNAME,"receive Protocol-Reject for CCP");
ccp_event(ppp, FSM_DOWN);
ccp_event(ppp, FSM_CLOSE);
fsm_run(ppp, &L.fsm, FSM_RXJ_P);
break;
case PPP_CP:
log_debug(1,LNAME,"receive Protocol-Reject for CP");
ccp_event(ppp, FSM_DOWN);
ccp_event(ppp, FSM_CLOSE);
fsm_run(ppp, &L.fsm, FSM_RXJ_P);
break;
case PPP_LCP:
complain_rej(ppp,i,", LCP, ", &L.fsm);
break;
case PPP_IP:
complain_rej(ppp,i,", IP, ", &L.fsm);
break;
case PPP_IPCP:
complain_rej(ppp,i,", IPCP, ", &L.fsm);
break;
case PPP_VJC_COMP:
complain_rej(ppp,i,", VJC_COMP, ", &L.fsm);
break;
case PPP_VJC_UNCOMP:
complain_rej(ppp,i,", VJC_UNCOMP, ", &L.fsm);
break;
case PPP_PAP:
complain_rej(ppp,i,", PAP, ", &L.fsm);
break;
case PPP_CHAP:
complain_rej(ppp,i,", CHAP, ", &L.fsm);
break;
default:
complain_rej(ppp,i,"", 0);
break;
}
break;
case PPP_CODE_EREQ: /* Echo-Request */
case PPP_CODE_EREP: /* Echo-Reply */
fsm_run(ppp, &L.fsm, FSM_RXR);
break;
case PPP_CODE_DIS: /* Discard-Request */
log_ipkt(2,LNAME,"receive Discard-Request:");
L.cnts.rcvd_dis++;
fsm_run(ppp, &L.fsm, FSM_RXR);
break;
case PPP_CODE_IDENT:
i = MIN(IBUF_CP->len-4, PPP_IDENT_LEN);
log_debug((L.ident_rcvd_len != i
|| bcmp(&IBUF_CP->lcp_un.ident,&L.ident_rcvd,i)
? 1 : 2),
LNAME, "receive Identification: ID=%#x %#x \"%s\"",
IBUF_CP->id,
IBUF_CP->lcp_un.ident.magic,
ascii_str(IBUF_CP->lcp_un.ident.msg,
i-sizeof(IBUF_CP->lcp_un.ident.magic)));
bcopy(&IBUF_CP->lcp_un.ident, &L.ident_rcvd, i);
L.ident_rcvd_len = i;
fsm_run(ppp, &L.fsm, FSM_RXR);
break;
default:
L.cnts.rcvd_bad++;
log_complain(LNAME,"bad %s", fsm_code_name(IBUF_CP->code));
fsm_run(ppp, &L.fsm, FSM_RUC);
break;
}
#undef CK_ID
}
/* FSM action to deal with Echo-Replies and similar noise
*/
static void
lcp_ser(struct ppp *ppp,
struct fsm *fsm)
{
struct timeval delta;
float rtt;
int hi, id;
/* If the magic number is not what the peer said (or 0 if
* the peer did not say), the link is looped back or cross-
* connected.
*/
if (!L.bad_magic
&& IBUF_CP->lcp_un.echo.magic != 0
&& (ibuf.bits != 0 /* no magic numbers on MP */
|| IBUF_CP->lcp_un.echo.magic != L.his_magic)) {
L.bad_magic = 1;
log_complain(LNAME,
"bad Echo-Request magic #=%#x instead of %#x"
"--possible loopback",
IBUF_CP->lcp_un.echo.magic,
L.his_magic);
}
/* respond to an Echo Request */
if (IBUF_CP->code == PPP_CODE_EREQ) {
log_debug(3,LNAME,"respond to Echo-Request ID=%#x",
IBUF_CP->id);
IBUF_CP->code = PPP_CODE_EREP;
IBUF_CP->lcp_un.echo.magic = ((ibuf.bits == 0
&& fsm->state == FSM_OPENED_9)
? L.my_magic : 0),
IBUF_CP->len = MIN(MIN(L.neg_mtru, L.neg_mtu), IBUF_CP->len);
ppp_send(ppp, &ibuf, IBUF_CP->len);
return;
}
/* deal with an echo reply */
if (IBUF_CP->code == PPP_CODE_EREP) {
if (L.conf.echo_int == 0) {
log_complain(LNAME,
"receive unexpected Echo-Reply ID=%#x",
IBUF_CP->id);
return;
}
if (IBUF_CP->len != 4+sizeof(IBUF_CP->lcp_un.echo)) {
log_complain(LNAME, "receive bogus Echo-Reply len=%d",
IBUF_CP->len);
return;
}
id = IBUF_CP->id;
hi = L.echo_next_id;
if (hi < L.echo_old_id) {
if (id < hi)
id += 256;
hi += 256;
}
if (L.echo_old_id > id || hi < id) {
log_complain(LNAME, "receive bogus Echo-Reply ID=%#x"
" not in range [%d-%d)",
IBUF_CP->id,
L.echo_old_id, L.echo_next_id);
return;
}
timevalsub(&delta, &rcv_msg_ts, &IBUF_CP->lcp_un.echo.ts);
rtt = delta.tv_sec*1.0+delta.tv_usec/1000000.0;
if (rtt <= 0
|| rtt > (L.conf.echo_fail+1)*L.conf.echo_int*1.0) {
log_complain(LNAME, "receive bogus Echo-Reply"
" timestamp with delta=%.3f sec",
rtt);
return;
}
log_debug(3,LNAME,"receive Echo-Reply ID=%#x RTT=%.3f sec",
IBUF_CP->id, rtt);
L.echo_rtt = rtt;
L.echo_old_id = IBUF_CP->id+1;
L.echo_seen = 1;
}
}
/* send an Echo-Request to see if the peer is alive
*/
void
lcp_send_echo(struct ppp *ppp)
{
u_char nechos;
int secs;
if (L.conf.echo_int == 0
|| ppp->phase != NET_PHASE) {
L.echo_timer.tv_sec = TIME_NEVER;
return;
}
/* If we have sent several echos, and if there has been enough
* time for the peer to respond, either kill the link or stop
* trying.
*/
nechos = L.echo_next_id-L.echo_old_id;
if (nechos >= L.conf.echo_fail) {
L.echo_timer.tv_sec = TIME_NEVER;
secs = nechos*L.conf.echo_int;
if (!L.echo_seen) {
/* If the peer never responds, eventually stop trying.
*/
log_debug(1,LNAME, "stop Echo-Requests after"
" %d in %d seconds failed",
nechos, secs);
} else {
/* If the peer stops responding, hang up the phone.
*/
log_complain(LNAME, "assume line is dead after"
" %d Echo-Requests failed in %d seconds",
nechos, secs);
set_tr_msg(&L.fsm, "Echo-Replies stopped");
lcp_event(ppp, FSM_CLOSE);
}
return;
}
obuf.proto = PPP_LCP;
obuf.bits = 0;
OBUF_CP->code = PPP_CODE_EREQ;
OBUF_CP->id = L.echo_next_id++;
OBUF_CP->len = 4+sizeof(OBUF_CP->lcp_un.echo);
OBUF_CP->lcp_un.echo.magic = L.my_magic;
get_clk();
OBUF_CP->lcp_un.echo.ts = clk;
log_debug(3,LNAME, "send Echo-Request ID=%#x", OBUF_CP->id);
ppp_send(ppp, &obuf, OBUF_CP->len);
fsm_settimer(&L.echo_timer, L.conf.echo_int*1000);
}
void
lcp_send_ident(struct ppp *ppp,
int magic)
{
struct utsname buf;
if (L.ident_off)
return;
bzero(&buf,sizeof(buf));
(void)uname(&buf);
obuf.proto = PPP_LCP;
obuf.bits = 0;
OBUF_CP->code = PPP_CODE_IDENT;
OBUF_CP->id = ++L.ident_id;
OBUF_CP->lcp_un.ident.magic = magic;
OBUF_CP->len = 8+sprintf((char *)OBUF_CP->lcp_un.ident.msg,
"%s link #%d "RSTR" %s %s %s %s",
ourhost_nam, ppp->link_num,
buf.sysname, buf.release, buf.version,
buf.machine);
log_debug(2,LNAME,"send Identification: ID=%#x %#x \"%s\"",
OBUF_CP->id,
OBUF_CP->lcp_un.ident.magic,
OBUF_CP->lcp_un.ident.msg);
ppp_send(ppp, &obuf, OBUF_CP->len);
}