* ipcp.c - PPP IP Control Protocol.
* Copyright (c) 1989 Carnegie Mellon University.
* 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.
* Fix IP address negotiation (wantoptions or hisoptions).
* Don't set zero IP addresses.
* Send NAKs for unsent CIs.
ipcp_options ipcp_wantoptions
[NPPP
]; /* Options that we want to request */
ipcp_options ipcp_gotoptions
[NPPP
]; /* Options that peer ack'd */
ipcp_options ipcp_allowoptions
[NPPP
]; /* Options that we allow peer to
ipcp_options ipcp_hisoptions
[NPPP
]; /* Options that we ack'd */
* VJ compression protocol mode for negotiation. See ipcp.h for a
* description of each mode.
static int vj_mode
= IPCP_VJMODE_RFC1332
;
static int vj_opt_len
= 6; /* holds length in octets for valid vj */
/* compression frame depending on mode */
static int vj_opt_val
= IPCP_VJ_COMP
;
/* compression negotiation frames */
/* depending on vj_mode */
static void ipcp_resetci
__ARGS((fsm
*)); /* Reset our Configuration Information */
static int ipcp_cilen
__ARGS((fsm
*)); /* Return length of our CI */
static void ipcp_addci
__ARGS((fsm
*, u_char
*)); /* Add our CIs */
static int ipcp_ackci
__ARGS((fsm
*, u_char
*, int)); /* Ack some CIs */
static void ipcp_nakci
__ARGS((fsm
*, u_char
*, int)); /* Nak some CIs */
static void ipcp_rejci
__ARGS((fsm
*, u_char
*, int)); /* Reject some CIs */
static u_char ipcp_reqci
__ARGS((fsm
*, u_char
*, int *)); /* Check the requested CIs */
static void ipcp_up
__ARGS((fsm
*)); /* We're UP */
static void ipcp_down
__ARGS((fsm
*)); /* We're DOWN */
static fsm ipcp_fsm
[NPPP
]; /* IPCP fsm structure */
static fsm_callbacks ipcp_callbacks
= { /* IPCP callback routines */
ipcp_resetci
, /* Reset our Configuration Information */
ipcp_cilen
, /* Length of our Configuration Information */
ipcp_addci
, /* Add our Configuration Information */
ipcp_ackci
, /* ACK our Configuration Information */
ipcp_nakci
, /* NAK our Configuration Information */
ipcp_rejci
, /* Reject our Configuration Information */
ipcp_reqci
, /* Request peer's Configuration Information */
ipcp_up
, /* Called when fsm reaches OPEN state */
ipcp_down
, /* Called when fsm leaves OPEN state */
NULL
, /* Called when fsm reaches CLOSED state */
NULL
, /* Called when Protocol-Reject received */
NULL
/* Retransmission is necessary */
static char b1
[64], b2
[64], w
= 0;
char *b
= (w
++&1) ? b1
: b2
;
sprintf(b
, "%d.%d.%d.%d",
* ipcp_init - Initialize IPCP.
fsm
*f
= &ipcp_fsm
[unit
];
ipcp_options
*wo
= &ipcp_wantoptions
[unit
];
ipcp_options
*ao
= &ipcp_allowoptions
[unit
];
f
->timeouttime
= DEFTIMEOUT
;
f
->maxconfreqtransmits
= DEFMAXCONFIGREQS
;
f
->maxtermtransmits
= DEFMAXTERMTRANSMITS
;
f
->maxnakloops
= DEFMAXNAKLOOPS
;
f
->callbacks
= &ipcp_callbacks
;
wo
->maxslotindex
= MAX_STATES
- 1; /* really max index */
/* max slots and slot-id compression are currently hardwired in */
/* ppp_if.c to 16 and 1, this needs to be changed (among other */
ao
->neg_addrs
= 1; /* accept old style dual addr */
ao
->neg_addr
= 1; /* accept new style single addr */
ao
->maxslotindex
= MAX_STATES
- 1;
fsm_init(&ipcp_fsm
[unit
]);
* ipcp_vj_setmode - set option length and option value for vj
* compression negotiation frames depending on mode
case IPCP_VJMODE_OLD
: /* with wrong code (0x0037) */
vj_opt_val
= IPCP_VJ_COMP_OLD
;
case IPCP_VJMODE_RFC1172
: /* as per rfc1172 */
vj_opt_val
= IPCP_VJ_COMP
;
case IPCP_VJMODE_RFC1332
: /* draft mode vj compression */
vj_opt_len
= 6; /* negotiation includes values for */
/* maxslot and slot number compression */
vj_opt_val
= IPCP_VJ_COMP
;
IPCPDEBUG((LOG_WARNING
, "Unknown vj compression mode %d. Please report \
* ipcp_activeopen - Actively open IPCP.
fsm_activeopen(&ipcp_fsm
[unit
]);
* ipcp_passiveopen - Passively open IPCP.
void ipcp_passiveopen(unit
)
fsm_passiveopen(&ipcp_fsm
[unit
]);
* ipcp_close - Close IPCP.
fsm_close(&ipcp_fsm
[unit
]);
* ipcp_lowerup - The lower layer is up.
fsm_lowerup(&ipcp_fsm
[unit
]);
* ipcp_lowerdown - The lower layer is down.
fsm_lowerdown(&ipcp_fsm
[unit
]);
* ipcp_input - Input IPCP packet.
fsm_input(&ipcp_fsm
[unit
], p
, len
);
* ipcp_protrej - A Protocol-Reject was received for IPCP.
* Simply pretend that LCP went down.
fsm_lowerdown(&ipcp_fsm
[unit
]);
* ipcp_resetci - Reset our CI.
ipcp_gotoptions
[f
->unit
] = ipcp_wantoptions
[f
->unit
];
* ipcp_cilen - Return length of our CI.
ipcp_options
*go
= &ipcp_gotoptions
[f
->unit
];
#define LENCISHORT(neg) (neg ? vj_opt_len : 0)
#define LENCIADDRS(neg) (neg ? 10 : 0)
#define LENCIADDR(neg) (neg ? 6 : 0)
return (LENCIADDRS(go
->neg_addrs
) +
LENCIADDR(go
->neg_addr
) +
* ipcp_addci - Add our desired CIs to a packet.
ipcp_options
*go
= &ipcp_gotoptions
[f
->unit
];
#define ADDCISHORT(opt, neg, val, maxslotindex, cflag) \
PUTCHAR(vj_opt_len, ucp); \
if (vj_mode == IPCP_VJMODE_RFC1332) { \
PUTCHAR(maxslotindex, ucp); \
#define ADDCIADDRS(opt, neg, val1, val2) \
PUTCHAR(2 + 2 * sizeof (long), ucp); \
#define ADDCIADDR(opt, neg, val) \
PUTCHAR(2 + sizeof (long), ucp); \
ADDCIADDRS(CI_ADDRS
, go
->neg_addrs
, go
->ouraddr
, go
->hisaddr
)
ADDCIADDR(CI_ADDR
, go
->neg_addr
, go
->ouraddr
)
ADDCISHORT(CI_COMPRESSTYPE
, go
->neg_vj
, vj_opt_val
,
go
->maxslotindex
, go
->cflag
)
* ipcp_ackci - Ack our CIs.
ipcp_options
*go
= &ipcp_gotoptions
[f
->unit
];
u_short cilen
, citype
, cishort
;
u_char cimaxslotindex
, cicflag
;
* 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.
#define ACKCISHORT(opt, neg, val, maxslotindex, cflag) \
if ((len -= vj_opt_len) < 0) \
if (cilen != vj_opt_len || \
if (vj_mode == IPCP_VJMODE_RFC1332) { \
GETCHAR(cimaxslotindex, p); \
if (cimaxslotindex > maxslotindex) \
#define ACKCIADDRS(opt, neg, val1, val2) \
if ((len -= 2 + 2 * sizeof (long)) < 0) \
if (cilen != 2 + 2 * sizeof (long) || \
#define ACKCIADDR(opt, neg, val) \
if ((len -= 2 + sizeof (long)) < 0) \
if (cilen != 2 + sizeof (long) || \
ACKCIADDRS(CI_ADDRS
, go
->neg_addrs
, go
->ouraddr
, go
->hisaddr
)
ACKCIADDR(CI_ADDR
, go
->neg_addr
, go
->ouraddr
)
ACKCISHORT(CI_COMPRESSTYPE
, go
->neg_vj
, vj_opt_val
, go
->maxslotindex
, go
->cflag
)
* If there are any remaining CIs, then this packet is bad.
IPCPDEBUG((LOG_INFO
, "ipcp_ackci: received bad Ack!"));
if (vj_mode
== IPCP_VJMODE_RFC1332
)
IPCPDEBUG((LOG_INFO
, "ipcp_ackci: citype %d, cilen %l",
if (citype
== CI_COMPRESSTYPE
) {
IPCPDEBUG((LOG_INFO
, "ipcp_ackci: compress_type %d", cishort
));
if (vj_mode
== IPCP_VJMODE_RFC1332
)
IPCPDEBUG((LOG_INFO
, ", maxslotindex %d, cflag %d",
cishort
, cimaxslotindex
, cicflag
));
* ipcp_nakci - NAK some of our CIs.
ipcp_options
*go
= &ipcp_gotoptions
[f
->unit
];
u_char cimaxslotindex
, cicflag
;
* 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.
#define NAKCISHORT(opt, neg, code) \
if (vj_mode == IPCP_VJMODE_RFC1332) { \
GETCHAR(cimaxslotindex, p); \
#define NAKCIADDRS(opt, neg, code) \
len >= 2 + 2 * sizeof (long) && \
p[1] == 2 + 2 * sizeof (long) && \
len -= 2 + 2 * sizeof (long); \
#define NAKCIADDR(opt, neg, code) \
len >= 2 + sizeof (long) && \
p[1] == 2 + sizeof (long) && \
len -= 2 + sizeof (long); \
NAKCIADDRS(CI_ADDRS
, go
->neg_addrs
,
if (!go
->ouraddr
) { /* Didn't know our address? */
syslog(LOG_INFO
, "local IP address %s", ip_ntoa(ciaddr1
));
if (ciaddr2
) { /* Does he know his? */
syslog(LOG_INFO
, "remote IP address %s", ip_ntoa(ciaddr2
));
NAKCIADDR(CI_ADDR
, go
->neg_addr
,
logf(LOG_INFO
, "acquired IP address %s", ip_ntoa(ciaddr1
));
if (!go
->ouraddr
) { /* Didn't know our address? */
syslog(LOG_INFO
, "remote IP address %s", ip_ntoa(ciaddr1
));
NAKCISHORT(CI_COMPRESSTYPE
, go
->neg_vj
,
if (cishort
!= vj_opt_val
)
go
->maxslotindex
= cimaxslotindex
; /* this is what it */
go
->cflag
= cicflag
; /* wants */
* If there are any remaining CIs, then this packet is bad.
IPCPDEBUG((LOG_INFO
, "ipcp_nakci: received bad Nak!"));
* ipcp_rejci - Reject some of our CIs.
ipcp_options
*go
= &ipcp_gotoptions
[f
->unit
];
u_char cimaxslotindex
, ciflag
;
* 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.
#define REJCISHORT(opt, neg, val, maxslot, cflag) \
/* Check rejected value. */ \
if (vj_mode == IPCP_VJMODE_RFC1332) { \
GETCHAR(cimaxslotindex, p); \
if (cimaxslotindex != maxslot) \
#define REJCIADDRS(opt, neg, val1, val2) \
len >= 2 + 2 * sizeof (long) && \
p[1] == 2 + 2 * sizeof (long) && \
len -= 2 + 2 * sizeof (long); \
/* Check rejected value. */ \
/* Check rejected value. */ \
#define REJCIADDR(opt, neg, val) \
len >= 2 + sizeof (long) && \
p[1] == 2 + sizeof (long) && \
len -= 2 + sizeof (long); \
/* Check rejected value. */ \
REJCIADDRS(CI_ADDRS
, go
->neg_addrs
, go
->ouraddr
, go
->hisaddr
)
REJCIADDR(CI_ADDR
, go
->neg_addr
, go
->ouraddr
)
REJCISHORT(CI_COMPRESSTYPE
, go
->neg_vj
, vj_opt_val
, go
->maxslotindex
, go
->cflag
)
* If there are any remaining CIs, then this packet is bad.
IPCPDEBUG((LOG_INFO
, "ipcp_rejci: received bad Reject!"));
* ipcp_reqci - Check the peer's requested CIs and send appropriate response.
* Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
u_char
*inp
; /* Requested CIs */
int *len
; /* Length of requested CIs */
ipcp_options
*wo
= &ipcp_wantoptions
[f
->unit
];
ipcp_options
*ho
= &ipcp_hisoptions
[f
->unit
];
ipcp_options
*ao
= &ipcp_allowoptions
[f
->unit
];
ipcp_options
*go
= &ipcp_gotoptions
[f
->unit
];
u_char
*cip
; /* Pointer to Current CI */
u_short cilen
, citype
; /* Parsed len, type */
u_short cishort
; /* Parsed short value */
u_long tl
, ciaddr1
, ciaddr2
; /* Parsed address values */
int rc
= CONFACK
; /* Final packet return code */
int orc
; /* Individual option return code */
u_char
*p
= inp
; /* Pointer to next char to parse */
u_char
*ucp
= inp
; /* Pointer to current output char */
int l
= *len
; /* Length left */
u_char maxslotindex
, cflag
;
* Process all his options.
orc
= CONFACK
; /* Assume success */
cip
= p
; /* Remember begining of CI */
if (l
< 2 || /* Not enough data for CI header or */
p
[1] < 2 || /* CI length too small or */
p
[1] > l
) { /* CI length too big? */
IPCPDEBUG((LOG_INFO
, "ipcp_reqci: bad CI length!"));
orc
= CONFREJ
; /* Reject bad CI */
cilen
= l
; /* Reject till end of packet */
l
= 0; /* Don't loop again */
GETCHAR(citype
, p
); /* Parse CI type */
GETCHAR(cilen
, p
); /* Parse CI length */
l
-= cilen
; /* Adjust remaining length */
cilen
-= 2; /* Adjust cilen to just data */
switch (citype
) { /* Check CI type */
logf(LOG_INFO
, "ipcp: received ADDRS ");
cilen
!= 2 * sizeof (long))
INCPTR(cilen
, p
); /* Skip rest of CI */
orc
= CONFREJ
; /* Reject CI */
* If he has no address, or if we both have his address but
* disagree about it, then NAK it with our idea.
* In particular, if we don't know his address, but he does,
GETLONG(tl
, p
); /* Parse source address (his) */
(wo
->neg_addrs
&& wo
->hisaddr
&& ciaddr1
!= wo
->hisaddr
))
DECPTR(sizeof (long), p
);
tl
= wo
->neg_addrs
? ntohl(wo
->hisaddr
) : 0;
* If he doesn't know our address, or if we both have our address
* but disagree about it, then NAK it with our idea.
GETLONG(tl
, p
); /* Parse desination address (ours) */
logf(LOG_INFO
, "(%s:%s)", ip_ntoa(ciaddr1
), ip_ntoa(ciaddr2
));
(wo
->neg_addrs
&& wo
->ouraddr
&& ciaddr2
!= wo
->ouraddr
))
DECPTR(sizeof (long), p
);
logf(LOG_INFO
, "ipcp: received ADDR ");
cilen
!= sizeof (long)) { /* Check CI length */
INCPTR(cilen
, p
); /* Skip rest of CI */
orc
= CONFREJ
; /* Reject CI */
* If he has no address, or if we both have his address but
* disagree about it, then NAK it with our idea.
* In particular, if we don't know his address, but he does,
GETLONG(tl
, p
); /* Parse source address (his) */
logf(LOG_INFO
, "(%s)", ip_ntoa(ciaddr1
));
(wo
->neg_addr
&& wo
->hisaddr
&& ciaddr1
!= wo
->hisaddr
)) {
DECPTR(sizeof (long), p
);
tl
= wo
->neg_addr
? ntohl(wo
->hisaddr
) : 0;
logf(LOG_INFO
, "ipcp: received COMPRESSTYPE ");
cilen
!= (vj_opt_len
- 2)) {
logf(LOG_INFO
, "(%d)", cishort
);
* Compresstype must be vj_opt_val.
if (cishort
!= vj_opt_val
) {
DECPTR(sizeof (short), p
);
if (vj_mode
== IPCP_VJMODE_RFC1332
) {
GETCHAR(maxslotindex
, p
);
if (maxslotindex
> wo
->maxslotindex
) {
PUTCHAR(wo
->maxslotindex
, p
);
ho
->maxslotindex
= maxslotindex
;
if (cflag
!= wo
->cflag
) {
cilen
+= 2; /* Adjust cilen whole CI */
logf(LOG_INFO
, " (%s)\n",
orc
== CONFACK
? "ACK" : (orc
== CONFNAK
? "NAK" : "Reject"));
if (orc
== CONFACK
&& /* Good CI */
rc
!= CONFACK
) /* but prior CI wasnt? */
continue; /* Don't send this one */
if (orc
== CONFNAK
) { /* Nak this CI? */
if (rc
== CONFREJ
) /* Rejecting prior CI? */
continue; /* Don't send this one */
if (rc
== CONFACK
) { /* Ack'd all prior CIs? */
rc
= CONFNAK
; /* Not anymore... */
if (orc
== CONFREJ
&& /* Reject this CI */
rc
!= CONFREJ
) { /* but no prior ones? */
memcpy(ucp
, cip
, (size_t)cilen
);
/* Update output pointer */
* XXX If we wanted to send additional NAKs (for unsent CIs), the
* code would go here. This must be done with care since it might
* require a longer packet than we received.
*len
= ucp
- inp
; /* Compute output length */
syslog(LOG_INFO
, "ipcp: returning Configure-%s",
rc
== CONFNAK
? "NAK" : "Reject");
return (rc
); /* Return final code */
* ipcp_up - IPCP has come UP.
syslog(LOG_INFO
, "ipcp: up");
if (ipcp_hisoptions
[f
->unit
].hisaddr
== 0)
ipcp_hisoptions
[f
->unit
].hisaddr
= ipcp_wantoptions
[f
->unit
].hisaddr
;
syslog(LOG_INFO
, "local IP address %s",
ip_ntoa(ipcp_gotoptions
[f
->unit
].ouraddr
));
syslog(LOG_INFO
, "remote IP address %s",
ip_ntoa(ipcp_hisoptions
[f
->unit
].hisaddr
));
SIFADDR(f
->unit
, ipcp_gotoptions
[f
->unit
].ouraddr
,
ipcp_hisoptions
[f
->unit
].hisaddr
);
/* set new netmask if specified */
mask
= GetMask(ipcp_gotoptions
[f
->unit
].ouraddr
);
/* set tcp compression */
SIFVJCOMP(f
->unit
, ipcp_hisoptions
[f
->unit
].neg_vj
);
* ipcp_down - IPCP has gone DOWN.
syslog(LOG_INFO
, "ipcp: down");
CIFADDR(f
->unit
, ipcp_gotoptions
[f
->unit
].ouraddr
,
ipcp_hisoptions
[f
->unit
].hisaddr
);