977 lines
23 KiB
C
977 lines
23 KiB
C
/* PPP Internet Protocol Control Protocol
|
|
*/
|
|
|
|
#ident "$Revision: 1.20 $"
|
|
|
|
#include <stropts.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "ppp.h"
|
|
|
|
|
|
static void ipcp_scr(struct ppp*);
|
|
static void ipcp_sca(struct ppp*, struct fsm*);
|
|
static void ipcp_act(struct ppp*, enum fsm_action);
|
|
|
|
static struct fsm_ops ipcp_ops = {
|
|
ipcp_scr,
|
|
ipcp_sca,
|
|
fsm_scn,
|
|
fsm_str,
|
|
fsm_sta,
|
|
fsm_scj,
|
|
fsm_ser,
|
|
ipcp_act
|
|
};
|
|
|
|
|
|
#define I ppp->ipcp
|
|
#define PNAME ppp->ipcp.fsm.name
|
|
#define STRUCT_PKT struct ipcp_pkt
|
|
#define STRUCT_CF struct ipcp_cf
|
|
#define BASE_P(bufp) (&(bufp)->cf[0])
|
|
|
|
/* shape of an IPCP packet */
|
|
STRUCT_PKT {
|
|
u_char code;
|
|
u_char id;
|
|
u_short len;
|
|
STRUCT_CF { /* Configure-Req, Ack, Nak, Reject */
|
|
u_char type;
|
|
u_char len;
|
|
union {
|
|
u_char info[1];
|
|
struct {
|
|
u_char proto[2];
|
|
u_char vj_slots;
|
|
u_char vj_compslot;
|
|
} comp;
|
|
u_char addr[4];
|
|
struct {
|
|
u_char src[4];
|
|
u_char dst[4];
|
|
} addrs;
|
|
} cf_un;
|
|
} cf[1];
|
|
};
|
|
|
|
|
|
/* IPCP Configuration Options */
|
|
#define IPCP_CO_ADDRS_T 1 /* Addresses */
|
|
#define IPCP_CO_ADDRS_L 10
|
|
#define IPCP_CO_COMP_T 2 /* Compression-Protocol */
|
|
#define IPCP_CO_COMP_L 6
|
|
#define IPCP_CO_COMP_VJ PPP_VJC_COMP
|
|
#define IPCP_CO_ADDR_T 3 /* Address */
|
|
#define IPCP_CO_ADDR_L 6
|
|
|
|
static char *
|
|
ipcp_opt_name(u_int type)
|
|
{
|
|
static char buf[32];
|
|
|
|
switch (type) {
|
|
case IPCP_CO_ADDRS_T:
|
|
return "ADDRS";
|
|
case IPCP_CO_COMP_T:
|
|
return "COMP";
|
|
case IPCP_CO_ADDR_T:
|
|
return "ADDR";
|
|
default:
|
|
(void)sprintf(buf,"unknown #%#x",type);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
|
|
/* Convert an IP address to a string.
|
|
* One feature of this routine is that it has several static
|
|
* buffers so that it can be called more than once for a single
|
|
* printf().
|
|
*/
|
|
char*
|
|
ip2str(u_long ip_addr)
|
|
{
|
|
static int i;
|
|
static struct {
|
|
char addr[20];
|
|
} str[6];
|
|
static struct in_addr addr;
|
|
|
|
if (ip_addr == 0)
|
|
return "0";
|
|
|
|
i = (i+1) % (sizeof(str)/sizeof(str[0]));
|
|
|
|
addr.s_addr = ip_addr;
|
|
(void)strcpy(str[i].addr, inet_ntoa(addr));
|
|
return str[i].addr;
|
|
}
|
|
|
|
|
|
/* check and accept an address negotiation from a Configure-Request or
|
|
* a Configure-Nak.
|
|
*/
|
|
static int /* 0=bad */
|
|
ck_host(struct ppp *ppp,
|
|
char x,
|
|
__uint32_t addr,
|
|
char *ptype)
|
|
{
|
|
int i;
|
|
char *owner;
|
|
struct ok_host *okp;
|
|
struct sockaddr_in *h;
|
|
int *defp;
|
|
char *strp;
|
|
|
|
|
|
if (x == 'r') {
|
|
owner = "its";
|
|
okp = remhost_nams;
|
|
i = num_remhost_nams;
|
|
h = &remhost;
|
|
defp = &def_remhost;
|
|
strp = remhost_str;
|
|
} else {
|
|
owner = "our";
|
|
okp = lochost_nams;
|
|
i = num_lochost_nams;
|
|
h = &lochost;
|
|
defp = &def_lochost;
|
|
strp = lochost_str;
|
|
}
|
|
|
|
/* A zero address is never good. It always means the peer
|
|
* does not know its address. We always send an initial
|
|
* Configure-Request for an address, even if is 0, meaning
|
|
* "please tell me what address to use on my end." Receiving
|
|
* 0 means that the peer did not hear our request for 0 or
|
|
* does not have any idea either.
|
|
*/
|
|
if (addr == 0) {
|
|
if (I.fsm.nak_recv > 2 || I.fsm.nak_sent > 2)
|
|
log_complain(PNAME,"peer does not know %s IP address",
|
|
owner);
|
|
return 0;
|
|
}
|
|
|
|
if (*defp) {
|
|
/* If we defaulted the address,
|
|
* then accept what the peer says.
|
|
*/
|
|
h->sin_addr.s_addr = addr;
|
|
#ifdef _HAVE_SIN_LEN
|
|
h->sin_len = _SIN_ADDR_SIZE;
|
|
#endif
|
|
h->sin_family = AF_INET;
|
|
*defp = 0;
|
|
(void)strcpy(strp, ip2str(addr));
|
|
log_debug(2,PNAME, " set %s address to %s from %s",
|
|
owner, strp, ptype);
|
|
return 1;
|
|
|
|
} else if (h->sin_addr.s_addr == 0) {
|
|
/* Validate the address from peer against list of possibles.
|
|
*/
|
|
while (i > 0) {
|
|
if (((okp->addr ^ addr) & okp->mask) == 0) {
|
|
h->sin_addr.s_addr = addr;
|
|
#ifdef _HAVE_SIN_LEN
|
|
h->sin_len = _SIN_ADDR_SIZE
|
|
#endif
|
|
h->sin_family = AF_INET;
|
|
*defp = 0;
|
|
(void)strcpy(strp, ip2str(addr));
|
|
log_debug(2,PNAME,
|
|
" set %s address to %s from %s",
|
|
owner, strp, ptype);
|
|
return 1;
|
|
}
|
|
okp++;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
if (h->sin_addr.s_addr == addr) {
|
|
(void)strcpy(strp, ip2str(addr));
|
|
log_debug(2,PNAME," accept %s address %s from %s",
|
|
owner, strp, ptype);
|
|
return 1;
|
|
|
|
} else {
|
|
if (h->sin_addr.s_addr == 0) {
|
|
log_complain(PNAME," peer offering unacceptible %s"
|
|
" for %s address",
|
|
ip2str(addr), owner);
|
|
} else {
|
|
log_cd(I.fsm.nak_sent > 1,2,PNAME,
|
|
" peer says %s instead of %s for %s address",
|
|
ip2str(addr), strp, owner);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* set IP parameters
|
|
*/
|
|
void
|
|
ipcp_param(struct ppp *ppp)
|
|
{
|
|
I.fsm.restart_ms = ppp->conf.restart_ms;
|
|
I.fsm.restart_ms_lim = ppp->conf.restart_ms_lim;
|
|
|
|
I.rx_comp = I.conf.rx_vj_comp;
|
|
I.rx_slots = I.conf.rx_vj_slots;
|
|
I.rx_compslot = I.conf.rx_vj_compslot;
|
|
|
|
I.tx_slots = I.conf.tx_vj_slots;
|
|
I.tx_compslot = I.conf.tx_vj_compslot;
|
|
|
|
I.seen_addr_rej = 0;
|
|
#ifdef DO_ADDRS
|
|
I.seen_addrs_rej = 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
/* get IP going
|
|
*/
|
|
void
|
|
ipcp_init(struct ppp *ppp)
|
|
{
|
|
I.fsm.protocol = PPP_IPCP;
|
|
I.fsm.ops = &ipcp_ops;
|
|
|
|
I.went_up = 0;
|
|
|
|
ipcp_param(ppp);
|
|
|
|
fsm_init(&I.fsm);
|
|
}
|
|
|
|
|
|
void
|
|
ipcp_go(struct ppp *l_ppp)
|
|
{
|
|
struct ppp *ppp = mp_ppp ? mp_ppp : l_ppp; /* MP state machine */
|
|
|
|
(void)set_mp(l_ppp);
|
|
|
|
if (ppp->ipcp.fsm.state <= FSM_INITIAL_0)
|
|
ipcp_event(ppp,FSM_OPEN);
|
|
if (ppp->ipcp.fsm.state <= FSM_STARTING_1)
|
|
ipcp_event(ppp,FSM_UP);
|
|
}
|
|
|
|
|
|
/* See if IP should be turned on in the kernel.
|
|
* This kludge lets things be done after the IPCP FSM has
|
|
* entirely finished doing what it needs to do.
|
|
*/
|
|
static void
|
|
ck_up(struct ppp *l_ppp)
|
|
{
|
|
struct ppp *ppp = mp_ppp ? mp_ppp : l_ppp; /* MP state machine */
|
|
char str[100];
|
|
int lvl;
|
|
|
|
if (I.went_up == 1) {
|
|
I.went_up = 2;
|
|
|
|
(void)sprintf(str, "rx_vj_comp=%c,tx=%c "
|
|
"rx_compslot=%c,tx=%c rx_slots=%d,tx=%d",
|
|
ppp->ipcp.rx_comp ? 'y' : 'n',
|
|
ppp->ipcp.tx_comp ? 'y' : 'n',
|
|
ppp->ipcp.rx_compslot ? 'y' : 'n',
|
|
ppp->ipcp.tx_compslot ? 'y' : 'n',
|
|
ppp->ipcp.rx_slots, ppp->ipcp.tx_slots);
|
|
|
|
/* always display the message for the first link of a bundle
|
|
*/
|
|
if (interact && numdevs == 1)
|
|
lvl = 0;
|
|
else
|
|
lvl = 1;
|
|
log_debug(lvl, PNAME,
|
|
debug ? "ready %s to %s, %s" : "ready %s to %s",
|
|
lochost_str, remhost_str, str);
|
|
}
|
|
|
|
if (link_mux(l_ppp) < 0
|
|
|| !ccp_go(l_ppp)) { /* tell kernel to release IP */
|
|
ipcp_event(ppp,FSM_CLOSE); /* die if sick */
|
|
set_tr_msg(&l_ppp->lcp.fsm, "IP system failure");
|
|
lcp_event(l_ppp,FSM_CLOSE);
|
|
|
|
} else {
|
|
/* we now know the IP address, so update utmp.
|
|
*/
|
|
dologin(l_ppp);
|
|
}
|
|
}
|
|
|
|
|
|
/* process result from FSM
|
|
*/
|
|
static void
|
|
ipcp_act(struct ppp *ppp,
|
|
enum fsm_action act)
|
|
{
|
|
log_debug(2, PNAME,"action %s", fsm_action_name(act));
|
|
|
|
switch (act) {
|
|
case FSM_TLU:
|
|
I.went_up = 1;
|
|
break;
|
|
|
|
case FSM_TLD:
|
|
if (I.went_up == 1)
|
|
I.went_up = 0;
|
|
|
|
/* stop ordinary IP traffic for IPCP */
|
|
(void)do_strioctl_ok(ppp, SIOC_PPP_IP_TX_OK,
|
|
0,"ioctl(IPCP TLD IP_TX_OK off)");
|
|
break;
|
|
|
|
case FSM_TLS:
|
|
break;
|
|
|
|
case FSM_TLF:
|
|
ipcp_event(ppp,FSM_CLOSE);
|
|
lcp_event(ppp,FSM_CLOSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Make something happen to the IPCP machine.
|
|
*/
|
|
void
|
|
ipcp_event(struct ppp *l_ppp,
|
|
enum fsm_event ev)
|
|
{
|
|
struct ppp *ppp = mp_ppp ? mp_ppp : l_ppp; /* MP state machine */
|
|
|
|
switch (ev) {
|
|
case FSM_UP: /* authentication finished */
|
|
case FSM_DOWN: /* LCP down */
|
|
case FSM_OPEN: /* start work */
|
|
case FSM_CLOSE: /* start to shut down */
|
|
fsm_run(ppp,&I.fsm, ev);
|
|
break;
|
|
|
|
case FSM_TO_P: /* the restart timer has expired */
|
|
if (I.fsm.restart <= 0)
|
|
ev = FSM_TO_M;
|
|
fsm_run(ppp,&I.fsm,ev);
|
|
break;
|
|
|
|
default:
|
|
log_complain(PNAME,"unknown event #%d", ev);
|
|
}
|
|
}
|
|
|
|
|
|
/* parse a received Configure-Request
|
|
*/
|
|
static enum fsm_event
|
|
ipcp_parse_cr(struct ppp *ppp)
|
|
{
|
|
STRUCT_CF *icfp;
|
|
STRUCT_CF *jcfp = BASE_P(JBUF_CP);
|
|
STRUCT_CF *ncfp = BASE_P(NBUF_CP);
|
|
u_int m, n;
|
|
#ifdef DO_ADDRS
|
|
char abuf[sizeof("its=123.123.123.123,our=123.123.123.123")];
|
|
#endif
|
|
int seen_vj = 0;
|
|
int nak_addr = 0;
|
|
int complained;
|
|
# define BAD_LEN(cond, L) {if (icfp->len cond (L)) { \
|
|
log_complain(PNAME, " reject %s CR option with strange length=%d",\
|
|
ipcp_opt_name(icfp->type), icfp->len); \
|
|
complained = 1; \
|
|
break;}}
|
|
|
|
|
|
log_debug(2,PNAME,"receive Configure-Request ID=%#x", IBUF_CP->id);
|
|
|
|
/* "all options are always negotiated at once," so a missing
|
|
* option implies the default
|
|
*/
|
|
I.tx_comp = 0;
|
|
|
|
for (icfp = BASE_P(IBUF_CP);
|
|
icfp < (STRUCT_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(PNAME," bad Configure-Request option"
|
|
" length %#x", icfp->len);
|
|
return FSM_RUC;
|
|
}
|
|
|
|
complained = 0;
|
|
switch (icfp->type) {
|
|
case IPCP_CO_COMP_T:
|
|
seen_vj = 1;
|
|
BAD_LEN(!=, IPCP_CO_COMP_L);
|
|
m = GET_S(icfp,comp.proto);
|
|
if (m != IPCP_CO_COMP_VJ) {
|
|
log_cd(I.fsm.nak_sent > 1,2,PNAME,
|
|
" reject header compression type %#x",
|
|
m);
|
|
complained = 1;
|
|
break;
|
|
}
|
|
/* Reject unknown compressions or VJ if off.
|
|
* Limit number of slots.
|
|
*/
|
|
if (!I.conf.tx_vj_comp) {
|
|
log_cd(I.fsm.nak_sent > 1,2,PNAME,
|
|
" reject VJ header compression"
|
|
" since it is off");
|
|
complained = 1;
|
|
break;
|
|
}
|
|
|
|
/* We are always willing to turn off slot #
|
|
* compression. Since we do not have to send
|
|
* compressed slot IDs even if the peer wants
|
|
* us to, do not bother disagreeing if it wants
|
|
* them to receive them and we do not want to
|
|
* send them.
|
|
*/
|
|
if (icfp->cf_un.comp.vj_compslot != 1)
|
|
I.tx_compslot = 0;
|
|
|
|
/* Do not worry about increasing the number of
|
|
* slots, since we do not need to send as many
|
|
* as the peer wants. Refuse to let them be reduced
|
|
* below the minimum the kernel code can handle.
|
|
*/
|
|
n = icfp->cf_un.comp.vj_slots+1;
|
|
if (n < MIN_VJ_SLOT) {
|
|
log_cd(I.fsm.nak_sent > 1,2,PNAME,
|
|
" instead of %d,"
|
|
" Nak for %d slot VJ compression"
|
|
" %s compressed slot IDs",
|
|
n,
|
|
MIN_VJ_SLOT,
|
|
I.tx_compslot ? "with" : "without");
|
|
n = MIN_VJ_SLOT;
|
|
ADD_CO_SHORT(ncfp,IPCP_CO_COMP_VJ,comp.proto);
|
|
ncfp->cf_un.comp.vj_slots = n-1;
|
|
ncfp->cf_un.comp.vj_compslot = I.tx_compslot;
|
|
GEN_CO(ncfp, IPCP_CO_COMP_T, comp);
|
|
} else if (n == 17) {
|
|
/* Previous versions of this code were off
|
|
* by one, and talked about 17 slots when
|
|
* they meant 16. Actually sending them
|
|
* 17-slots causes a black hole for those
|
|
* TCP connections that win slot 16.
|
|
*/
|
|
n = 16;
|
|
log_debug(2,PNAME, " accept"
|
|
" 17 slot VJ header compression"
|
|
" (but use 16)"
|
|
" %s compressed slot IDs",
|
|
I.tx_compslot ? "with" : "without");
|
|
} else {
|
|
log_debug(2,PNAME, " accept"
|
|
" %d slot VJ header compression"
|
|
" %s compressed slot IDs",
|
|
n,
|
|
I.tx_compslot ? "with" : "without");
|
|
}
|
|
I.tx_comp = 1;
|
|
I.tx_slots = n;
|
|
continue;
|
|
|
|
#ifdef DO_ADDRS
|
|
case IPCP_CO_ADDRS_T:
|
|
BAD_LEN(!=, IPCP_CO_ADDRS_L);
|
|
m = GET_L(icfp,addrs.src);
|
|
n = GET_L(icfp,addrs.dst);
|
|
/* If he does not know his address, give him
|
|
* our best guess. If we have no idea, then Nak(0)
|
|
* regardless of the standard, since the only
|
|
* alternative is to kill the link. We cannot run
|
|
* without both IP addresses.
|
|
*/
|
|
if (!ck_host(ppp,'r',m,"ADDRS Request")
|
|
|| !ck_host(ppp,'l',n,"ADDRS Request")
|
|
|| remhost.sin_addr.s_addr != m
|
|
|| lochost.sin_addr.s_addr != n) {
|
|
ADD_CO_LONG(ncfp, remhost.sin_addr.s_addr,
|
|
addrs.src);
|
|
ADD_CO_LONG(ncfp, lochost.sin_addr.s_addr,
|
|
addrs.dst);
|
|
GEN_CO(ncfp, IPCP_CO_ADDRS_T, addrs);
|
|
(void)sprintf(abuf,"its=%s,our=%s",
|
|
ip2str(m),ip2str(n));
|
|
log_debug(2,PNAME,
|
|
" Nak ADDRS %s with"
|
|
" its=%s,our=%s",
|
|
abuf, remhost_str, lochost_str);
|
|
nak_addr = 1;
|
|
}
|
|
continue;
|
|
#endif
|
|
|
|
case IPCP_CO_ADDR_T:
|
|
BAD_LEN(!=, IPCP_CO_ADDR_L);
|
|
m = GET_L(icfp,addr);
|
|
/* If he does not know his address, give him
|
|
* our best guess. If we have no idea, then Nak(0)
|
|
* regardless of the standard, since the only
|
|
* alternative is to kill the link. We cannot run
|
|
* without both IP addresses.
|
|
*/
|
|
if (!ck_host(ppp,'r',m,"ADDR Request")
|
|
|| remhost.sin_addr.s_addr != m) {
|
|
ADD_CO_LONG(ncfp, remhost.sin_addr.s_addr,
|
|
addr);
|
|
GEN_CO(ncfp, IPCP_CO_ADDR_T, addr);
|
|
log_debug(2,PNAME," Nak %s with %s",
|
|
ip2str(m),
|
|
ip2str(remhost.sin_addr.s_addr));
|
|
nak_addr = 1;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Reject an option we do not understand.
|
|
*/
|
|
bcopy(icfp, jcfp, icfp->len);
|
|
ADV(jcfp);
|
|
if (!complained)
|
|
log_cd(I.fsm.nak_sent > 1, 2,PNAME,
|
|
" reject %s CR option",
|
|
ipcp_opt_name(icfp->type));
|
|
}
|
|
|
|
/* If we do not know the remote IP address, and have not already
|
|
* added a query to our reply,
|
|
* or if we have defaulted the address and have peer has ,
|
|
* then add a query.
|
|
*
|
|
* Use the old protocol if the peer has rejected the new one.
|
|
* If both protocols have been rejected, then use the new one,
|
|
* since we cannot run without both IP addresses.
|
|
*/
|
|
if (!nak_addr) {
|
|
#ifdef DO_ADDRS
|
|
if (I.seen_addr_rej && !I.seen_addrs_rej) {
|
|
if (remhost.sin_addr.s_addr == 0
|
|
|| lochost.sin_addr.s_addr == 0
|
|
|| (I.fsm.nak_sent < ppp->conf.max_fail-2
|
|
&& (def_remhost || def_lochost))) {
|
|
ADD_CO_LONG(ncfp, remhost.sin_addr.s_addr,
|
|
addrs.src);
|
|
ADD_CO_LONG(ncfp, lochost.sin_addr.s_addr,
|
|
addrs.dst);
|
|
GEN_CO(ncfp, IPCP_CO_ADDRS_T, addrs);
|
|
log_debug(2,PNAME,
|
|
" Nak ADDRS its=%s,our=%s",
|
|
ip2str(remhost.sin_addr.s_addr),
|
|
ip2str(lochost.sin_addr.s_addr));
|
|
}
|
|
} else {
|
|
#endif
|
|
if (remhost.sin_addr.s_addr == 0
|
|
|| (I.fsm.nak_sent < ppp->conf.max_fail-2
|
|
&& !I.seen_addr_rej
|
|
&& def_remhost)) {
|
|
ADD_CO_LONG(ncfp, 0, addr);
|
|
GEN_CO(ncfp, IPCP_CO_ADDR_T, addr);
|
|
log_debug(2,PNAME," Nak ADDR 0");
|
|
#ifdef DO_ADDRS
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* If sending a Nak for other reasons, then include a request
|
|
* for VJ header compression.
|
|
*/
|
|
if (I.conf.tx_vj_comp
|
|
&& !seen_vj
|
|
&& ncfp != BASE_P(NBUF_CP)) {
|
|
ADD_CO_SHORT(ncfp,IPCP_CO_COMP_VJ,comp.proto);
|
|
ncfp->cf_un.comp.vj_slots = I.tx_slots-1;
|
|
ncfp->cf_un.comp.vj_compslot = I.tx_compslot;
|
|
GEN_CO(ncfp, IPCP_CO_COMP_T, comp);
|
|
log_debug(2,PNAME, " Nak for %d slot VJ compression"
|
|
" %s compressed slot IDs",
|
|
I.tx_slots,
|
|
I.tx_compslot ? "with" : "without");
|
|
}
|
|
|
|
rejbuf.proto = PPP_IPCP;
|
|
JBUF_CP->len = 4+((char*)jcfp - (char*)BASE_P(JBUF_CP));
|
|
|
|
nakbuf.proto = PPP_IPCP;
|
|
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
|
|
ipcp_parse_cn(struct ppp *ppp)
|
|
{
|
|
STRUCT_CF *icfp;
|
|
int i;
|
|
u_long m;
|
|
# define BAD_LEN(cond,L) {if (icfp->len cond (L)) { \
|
|
log_complain(PNAME, " ignore %s Nak with strange length=%d", \
|
|
ipcp_opt_name(icfp->type), icfp->len); \
|
|
break;}}
|
|
|
|
|
|
log_debug(2,PNAME,"receive Configure-NAK ID=%#x", IBUF_CP->id);
|
|
I.fsm.nak_recv++;
|
|
|
|
for (icfp = BASE_P(IBUF_CP);
|
|
icfp < (STRUCT_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(PNAME," bad Configure-Nak option"
|
|
" length %#x", icfp->len);
|
|
break;
|
|
}
|
|
|
|
switch (icfp->type) {
|
|
#ifdef DO_ADDRS
|
|
case IPCP_CO_ADDRS_T:
|
|
/* If we do not know his address,
|
|
* then take his answer.
|
|
*/
|
|
BAD_LEN(!=,IPCP_CO_ADDRS_L);
|
|
(void)ck_host(ppp,'l',GET_L(icfp,addrs.src),
|
|
"ADDRS Nak");
|
|
(void)ck_host(ppp,'r',GET_L(icfp,addrs.dst),
|
|
"ADDRS Nak");
|
|
break;
|
|
#endif
|
|
|
|
case IPCP_CO_ADDR_T:
|
|
BAD_LEN(!=,IPCP_CO_ADDR_L);
|
|
(void)ck_host(ppp,'l',GET_L(icfp,addr), "ADDR Nak");
|
|
break;
|
|
|
|
case IPCP_CO_COMP_T:
|
|
BAD_LEN(!=,IPCP_CO_COMP_L);
|
|
m = GET_S(icfp,comp.proto);
|
|
if (m != IPCP_CO_COMP_VJ) {
|
|
log_complain(PNAME," peer wants to send"
|
|
" unknown header compression"
|
|
" type %#x", m);
|
|
I.rx_comp = 0;
|
|
continue;
|
|
}
|
|
if (!I.conf.rx_vj_comp) {
|
|
log_debug(2,PNAME,
|
|
" ignore VJ compression;"
|
|
" turned off");
|
|
break;
|
|
}
|
|
m = 0;
|
|
i = icfp->cf_un.comp.vj_compslot;
|
|
if (i != 0 && !I.rx_compslot) {
|
|
log_cd(I.fsm.nak_sent > 1,2,PNAME,
|
|
" peer demands"
|
|
" compressed VJ slot IDs");
|
|
m++;
|
|
} else if (i == 0 && I.rx_compslot) {
|
|
log_debug(2,PNAME,
|
|
" peer refuses"
|
|
" compressed VJ slot IDs");
|
|
I.rx_compslot = 0;
|
|
m++;
|
|
}
|
|
i = icfp->cf_un.comp.vj_slots+1;
|
|
if (i < MIN_VJ_SLOT
|
|
|| i > I.conf.rx_vj_slots) {
|
|
I.rx_slots = ((i < MIN_VJ_SLOT)
|
|
? MIN_VJ_SLOT
|
|
: I.conf.rx_vj_slots);
|
|
log_complain(PNAME,
|
|
" peer wants to send"
|
|
" %d VJ slots; try %d",
|
|
i, I.rx_slots);
|
|
m++;
|
|
} else if (i != I.rx_slots) {
|
|
log_debug(2,PNAME, " peer wants"
|
|
" %d VJ compression slots",
|
|
i);
|
|
I.rx_slots = i;
|
|
m++;
|
|
}
|
|
|
|
if (!m)
|
|
log_complain(PNAME," vacuous COMP"
|
|
"configure Nak");
|
|
if (I.fsm.nak_recv > 2) {
|
|
log_debug(2,PNAME," too many COMP"
|
|
"Naks--give up");
|
|
I.rx_comp = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
log_complain(PNAME, " ignore %s Nak",
|
|
ipcp_opt_name(icfp->type));
|
|
break;
|
|
}
|
|
}
|
|
#undef BAD_LEN
|
|
}
|
|
|
|
|
|
/* parse a received Configure-Reject
|
|
*/
|
|
static void
|
|
ipcp_parse_confj(struct ppp *ppp)
|
|
{
|
|
STRUCT_CF *icfp;
|
|
|
|
|
|
log_debug(2,PNAME,"receive Configure-Reject ID=%#x", IBUF_CP->id);
|
|
I.fsm.nak_recv++;
|
|
|
|
for (icfp = BASE_P(IBUF_CP);
|
|
icfp < (STRUCT_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(PNAME,
|
|
" bad Configure-Reject option length"
|
|
" %#x", icfp->len);
|
|
break;
|
|
}
|
|
switch (icfp->type) {
|
|
#ifdef DO_ADDRS
|
|
case IPCP_CO_ADDRS_T:
|
|
log_debug(2,PNAME," peer is rejecting ADDRS");
|
|
I.seen_addrs_rej = 1;
|
|
break;
|
|
#endif
|
|
|
|
case IPCP_CO_COMP_T:
|
|
log_debug(2,PNAME," peer is rejecting header "
|
|
"compression");
|
|
I.rx_comp = 0;
|
|
break;
|
|
|
|
case IPCP_CO_ADDR_T:
|
|
log_debug(2,PNAME," peer is rejecting ADDR option");
|
|
I.seen_addr_rej = 1;
|
|
break;
|
|
|
|
default:
|
|
log_complain(PNAME,
|
|
" bogus Configure-Reject type %#x",
|
|
icfp->type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* FSM action to send a Configure-Ack
|
|
*/
|
|
static void
|
|
ipcp_sca(struct ppp *ppp,
|
|
struct fsm *fsm)
|
|
{
|
|
/* do not worry about MP bits before first bringing the link up
|
|
*/
|
|
if (ppp->dv.devfd >= 0)
|
|
ibuf.bits = 0;
|
|
fsm_sca(ppp,fsm);
|
|
}
|
|
|
|
|
|
/* FSM action to send a Configure-Request
|
|
*/
|
|
static void
|
|
ipcp_scr(struct ppp *ppp)
|
|
{
|
|
STRUCT_CF *p = BASE_P(OBUF_CP);
|
|
|
|
I.fsm.id++;
|
|
log_debug(2,PNAME,"send Configure-Request ID=%#x", I.fsm.id);
|
|
|
|
if (I.rx_comp) {
|
|
ADD_CO_SHORT(p, IPCP_CO_COMP_VJ, comp.proto);
|
|
p->cf_un.comp.vj_compslot = I.rx_compslot;
|
|
p->cf_un.comp.vj_slots = I.rx_slots-1;
|
|
GEN_CO(p, IPCP_CO_COMP_T, comp);
|
|
log_debug(2,PNAME," %d slot VJ compression %s"
|
|
" compressed slot IDs",
|
|
I.rx_slots,
|
|
I.rx_compslot ? "with" : "without");
|
|
}
|
|
|
|
/* If we do not know both IP numbers,
|
|
* then always send one kind or the other request.
|
|
* If we have not seen a rejection of the new kind,
|
|
* then send the new kind of request, to detect
|
|
* misconfigurations.
|
|
* If we have seen a rejection of the old kind and do not have a
|
|
* local address, then send a new kind of request.
|
|
* If we have seen a rejection of the new kind,
|
|
* and we already know both addresses, then do not worry.
|
|
*/
|
|
if (I.conf.no_addr > 0
|
|
&& remhost.sin_addr.s_addr != 0
|
|
&& lochost.sin_addr.s_addr != 0) {
|
|
; /* use default if no negotiating */
|
|
} else if (!I.seen_addr_rej
|
|
#ifdef DO_ADDRS
|
|
|| (I.seen_addrs_rej && lochost.sin_addr.s_addr == 0)
|
|
#else
|
|
|| lochost.sin_addr.s_addr == 0
|
|
#endif
|
|
) {
|
|
ADD_CO_LONG(p, lochost.sin_addr.s_addr, addr);
|
|
GEN_CO(p, IPCP_CO_ADDR_T, addr);
|
|
log_debug(2,PNAME," ADDR our address %s",
|
|
lochost_str);
|
|
#ifdef DO_ADDRS
|
|
} else if (!I.seen_addrs_rej
|
|
&& (remhost.sin_addr.s_addr == 0
|
|
|| lochost.sin_addr.s_addr == 0)) {
|
|
ADD_CO_LONG(p, lochost.sin_addr.s_addr,
|
|
addrs.src);
|
|
ADD_CO_LONG(p, remhost.sin_addr.s_addr,
|
|
addrs.dst);
|
|
GEN_CO(p, IPCP_CO_ADDRS_T, addrs);
|
|
log_debug(2,PNAME," ADDRS: our=%s,its=%s",
|
|
lochost_str, remhost_str);
|
|
#endif
|
|
}
|
|
|
|
obuf.proto = PPP_IPCP;
|
|
obuf.bits = mp_ncp_bits;
|
|
OBUF_CP->code = PPP_CODE_CR;
|
|
OBUF_CP->id = I.fsm.id;
|
|
OBUF_CP->len = 4+((char*)p - (char*)BASE_P(OBUF_CP));
|
|
ppp_send(ppp,&obuf,OBUF_CP->len);
|
|
}
|
|
|
|
|
|
/* process incoming IPCP frame
|
|
*/
|
|
void
|
|
ipcp_ipkt(struct ppp *l_ppp)
|
|
{
|
|
/* change the ID after recognizing it, to trash extras */
|
|
#define CK_ID() {if (fsm_ck_id(&I.fsm)) { \
|
|
ppp->ipcp.cnts.bad_id++; \
|
|
return; \
|
|
}}
|
|
|
|
struct ppp *ppp = mp_ppp ? mp_ppp : l_ppp; /* MP state machine */
|
|
|
|
|
|
if (ibuf_info_len < (int)IBUF_CP->len) {
|
|
log_complain(PNAME,
|
|
"dropping %s with %d bytes but claiming %d",
|
|
fsm_code_name(IBUF_CP->code),
|
|
ibuf_info_len, IBUF_CP->len);
|
|
ppp->ipcp.cnts.bad_len++; /* bad packet length */
|
|
return;
|
|
}
|
|
|
|
if (ppp->phase != NET_PHASE) {
|
|
log_debug(2,PNAME, "discard %s because in %s, not %s phase",
|
|
fsm_code_name(IBUF_CP->code),
|
|
phase_name(ppp->phase), phase_name(NET_PHASE));
|
|
return;
|
|
}
|
|
|
|
switch (IBUF_CP->code) {
|
|
case PPP_CODE_CR: /* Configure-Request */
|
|
fsm_run(ppp, &I.fsm, ipcp_parse_cr(ppp));
|
|
break;
|
|
|
|
case PPP_CODE_CA: /* Configure-Ack */
|
|
CK_ID();
|
|
log_debug(2,PNAME,"receive Configure-Ack ID=%#x",IBUF_CP->id);
|
|
I.fsm.nak_recv = 0;
|
|
if (lochost.sin_addr.s_addr == 0) {
|
|
log_complain(PNAME,
|
|
" peer did not tell us our IP address;"
|
|
" ignore the Configure-Ack");
|
|
break;
|
|
}
|
|
def_lochost = 0;
|
|
fsm_run(ppp, &I.fsm, FSM_RCA);
|
|
break;
|
|
|
|
case PPP_CODE_CN: /* Configure-Nak */
|
|
CK_ID();
|
|
ipcp_parse_cn(ppp);
|
|
if (I.fsm.nak_recv > ppp->conf.max_fail) {
|
|
log_complain(PNAME,"giving after %d Configure-NAKs",
|
|
I.fsm.nak_recv);
|
|
fsm_run(ppp, &I.fsm, FSM_RXJ_M);
|
|
} else {
|
|
fsm_run(ppp, &I.fsm, FSM_RCN);
|
|
}
|
|
break;
|
|
|
|
case PPP_CODE_CONFJ: /* Configure-Reject */
|
|
CK_ID();
|
|
ipcp_parse_confj(ppp);
|
|
if (I.fsm.nak_recv > ppp->conf.max_fail) {
|
|
log_complain(PNAME,"giving after %d Configure-NAKs",
|
|
I.fsm.nak_recv);
|
|
fsm_run(ppp, &I.fsm, FSM_RXJ_M);
|
|
} else {
|
|
fsm_run(ppp, &I.fsm, FSM_RCN);
|
|
}
|
|
break;
|
|
|
|
case PPP_CODE_TR: /* Terminate-Request */
|
|
log_ipkt(1,PNAME,"receive Terminate-Request:");
|
|
fsm_run(ppp, &I.fsm, FSM_RTR);
|
|
break;
|
|
|
|
case PPP_CODE_TA: /* Terminate-Ack */
|
|
log_ipkt(2,PNAME,"receive Terminate-Ack:");
|
|
fsm_run(ppp, &I.fsm, FSM_RTA);
|
|
break;
|
|
|
|
case PPP_CODE_CJ: /* Code-Reject */
|
|
/* fail, since this IPCP is minimal */
|
|
ppp->ipcp.cnts.rcvd_code_rej++;
|
|
fsm_rcj(ppp, &I.fsm);
|
|
break;
|
|
|
|
default:
|
|
ppp->ipcp.cnts.rcvd_bad++;
|
|
log_complain(PNAME,"bad %s",
|
|
fsm_code_name(IBUF_CP->code));
|
|
fsm_run(ppp, &I.fsm, FSM_RUC);
|
|
break;
|
|
}
|
|
#undef CK_ID
|
|
|
|
ck_up(l_ppp);
|
|
}
|