X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fipcp.c;h=0d1c128b89754d3daf3fb2ac67d8bd88f2011f50;hp=d46fffe58db1b9388e90e4590fbd40fb5b66088b;hb=4e3c66a04c0b86108858081d434bc45983471990;hpb=747ce87447095a7e54b33d148bf8d8a1b77364ea diff --git a/pppd/ipcp.c b/pppd/ipcp.c index d46fffe..0d1c128 100644 --- a/pppd/ipcp.c +++ b/pppd/ipcp.c @@ -18,7 +18,7 @@ */ #ifndef lint -static char rcsid[] = "$Id: ipcp.c,v 1.3 1994/02/14 04:45:31 paulus Exp $"; +static char rcsid[] = "$Id: ipcp.c,v 1.35 1998/11/07 06:59:26 paulus Exp $"; #endif /* @@ -26,49 +26,48 @@ static char rcsid[] = "$Id: ipcp.c,v 1.3 1994/02/14 04:45:31 paulus Exp $"; */ #include +#include #include -#include +#include +#include #include #include -#include - -#include -#include -#include #include - -#include +#include #include "pppd.h" -#include "ppp.h" #include "fsm.h" #include "ipcp.h" - +#include "pathnames.h" /* global vars */ -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 we allow peer to request */ -ipcp_options ipcp_hisoptions[NPPP]; /* Options that we ack'd */ +ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ + +bool disable_defaultip = 0; /* Don't use hostname for default IP adrs */ /* local vars */ -static int cis_received[NPPP]; /* # Conf-Reqs received */ +static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ +static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */ /* * Callbacks for fsm code. (CI = Configuration Information) */ -static void ipcp_resetci __ARGS((fsm *)); /* Reset our CI */ -static int ipcp_cilen __ARGS((fsm *)); /* Return length of our CI */ -static void ipcp_addci __ARGS((fsm *, u_char *, int *)); /* Add our CI */ -static int ipcp_ackci __ARGS((fsm *, u_char *, int)); /* Peer ack'd our CI */ -static int ipcp_nakci __ARGS((fsm *, u_char *, int)); /* Peer nak'd our CI */ -static int ipcp_rejci __ARGS((fsm *, u_char *, int)); /* Peer rej'd our CI */ -static int ipcp_reqci __ARGS((fsm *, u_char *, int *, int)); /* Rcv CI */ -static void ipcp_up __ARGS((fsm *)); /* We're UP */ -static void ipcp_down __ARGS((fsm *)); /* We're DOWN */ - - -fsm ipcp_fsm[NPPP]; /* IPCP fsm structure */ +static void ipcp_resetci __P((fsm *)); /* Reset our CI */ +static int ipcp_cilen __P((fsm *)); /* Return length of our CI */ +static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */ +static int ipcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ +static int ipcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */ +static int ipcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ +static int ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */ +static void ipcp_up __P((fsm *)); /* We're UP */ +static void ipcp_down __P((fsm *)); /* We're DOWN */ +static void ipcp_finished __P((fsm *)); /* Don't need lower layer */ + +fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ ipcp_resetci, /* Reset our Configuration Information */ @@ -81,13 +80,115 @@ static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ ipcp_up, /* Called when fsm reaches OPENED state */ ipcp_down, /* Called when fsm leaves OPENED state */ NULL, /* Called when we want the lower layer up */ - NULL, /* Called when we want the lower layer down */ + ipcp_finished, /* Called when we want the lower layer down */ NULL, /* Called when Protocol-Reject received */ NULL, /* Retransmission is necessary */ NULL, /* Called to handle protocol-specific codes */ "IPCP" /* String name of protocol */ }; +/* + * Command-line options. + */ +static int setvjslots __P((char **)); +static int setdnsaddr __P((char **)); +static int setwinsaddr __P((char **)); + +static option_t ipcp_option_list[] = { + { "noip", o_bool, &ipcp_protent.enabled_flag, + "Disable IP and IPCP" }, + { "-ip", o_bool, &ipcp_protent.enabled_flag, + "Disable IP and IPCP" }, + { "novj", o_bool, &ipcp_wantoptions[0].neg_vj, + "Disable VJ compression", OPT_A2COPY, &ipcp_allowoptions[0].neg_vj }, + { "-vj", o_bool, &ipcp_wantoptions[0].neg_vj, + "Disable VJ compression", OPT_A2COPY, &ipcp_allowoptions[0].neg_vj }, + { "novjccomp", o_bool, &ipcp_wantoptions[0].cflag, + "Disable VJ connection-ID compression", OPT_A2COPY, + &ipcp_allowoptions[0].cflag }, + { "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag, + "Disable VJ connection-ID compression", OPT_A2COPY, + &ipcp_allowoptions[0].cflag }, + { "vj-max-slots", 1, setvjslots, + "Set maximum VJ header slots" }, + { "ipcp-accept-local", o_bool, &ipcp_wantoptions[0].accept_local, + "Accept peer's address for us", 1 }, + { "ipcp-accept-remote", o_bool, &ipcp_wantoptions[0].accept_remote, + "Accept peer's address for it", 1 }, + { "ipparam", o_string, &ipparam, + "Set ip script parameter" }, + { "noipdefault", o_bool, &disable_defaultip, + "Don't use name for default IP adrs", 1 }, + { "ms-dns", 1, setdnsaddr, + "DNS address for the peer's use" }, + { "ms-wins", 1, setwinsaddr, + "Nameserver for SMB over TCP/IP for peer" }, + { "ipcp-restart", o_int, &ipcp_fsm[0].timeouttime, + "Set timeout for IPCP" }, + { "ipcp-max-terminate", o_int, &ipcp_fsm[0].maxtermtransmits, + "Set max #xmits for term-reqs" }, + { "ipcp-max-configure", o_int, &ipcp_fsm[0].maxconfreqtransmits, + "Set max #xmits for conf-reqs" }, + { "ipcp-max-failure", o_int, &ipcp_fsm[0].maxnakloops, + "Set max #conf-naks for IPCP" }, + { "defaultroute", o_bool, &ipcp_wantoptions[0].default_route, + "Add default route", OPT_ENABLE|1, &ipcp_allowoptions[0].default_route }, + { "nodefaultroute", o_bool, &ipcp_allowoptions[0].default_route, + "disable defaultroute option", OPT_A2COPY, + &ipcp_wantoptions[0].default_route }, + { "-defaultroute", o_bool, &ipcp_allowoptions[0].default_route, + "disable defaultroute option", OPT_A2COPY, + &ipcp_wantoptions[0].default_route }, + { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp, + "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp }, + { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp, + "disable proxyarp option", OPT_A2COPY, + &ipcp_wantoptions[0].proxy_arp }, + { "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp, + "disable proxyarp option", OPT_A2COPY, + &ipcp_wantoptions[0].proxy_arp }, + { NULL } +}; + +/* + * Protocol entry points from main code. + */ +static void ipcp_init __P((int)); +static void ipcp_open __P((int)); +static void ipcp_close __P((int, char *)); +static void ipcp_lowerup __P((int)); +static void ipcp_lowerdown __P((int)); +static void ipcp_input __P((int, u_char *, int)); +static void ipcp_protrej __P((int)); +static int ipcp_printpkt __P((u_char *, int, + void (*) __P((void *, char *, ...)), void *)); +static void ip_check_options __P((void)); +static int ip_demand_conf __P((int)); +static int ip_active_pkt __P((u_char *, int)); + +struct protent ipcp_protent = { + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, + ipcp_printpkt, + NULL, + 1, + "IPCP", + ipcp_option_list, + ip_check_options, + ip_demand_conf, + ip_active_pkt +}; + +static void ipcp_clear_addrs __P((int)); +static void ipcp_script __P((char *)); /* Run an up/down script */ +static void ipcp_script_done __P((void *)); + /* * Lengths of configuration options. */ @@ -101,13 +202,22 @@ static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ #define CODENAME(x) ((x) == CONFACK ? "ACK" : \ (x) == CONFNAK ? "NAK" : "REJ") +/* + * This state variable is used to ensure that we don't + * run an ipcp-up/down script while one is already running. + */ +static enum script_state { + s_down, + s_up, +} ipcp_script_state; +static pid_t ipcp_script_pid; /* * Make a string representation of a network IP address. */ char * ip_ntoa(ipaddr) -u_long ipaddr; +u_int32_t ipaddr; { static char b[64]; @@ -121,11 +231,97 @@ u_long ipaddr; return b; } +/* + * Option parsing. + */ + +/* + * setvjslots - set maximum number of connection slots for VJ compression + */ +static int +setvjslots(argv) + char **argv; +{ + int value; + + if (!int_option(*argv, &value)) + return 0; + if (value < 2 || value > 16) { + option_error("vj-max-slots value must be between 2 and 16"); + return 0; + } + ipcp_wantoptions [0].maxslotindex = + ipcp_allowoptions[0].maxslotindex = value - 1; + return 1; +} + +/* + * setdnsaddr - set the dns address(es) + */ +static int +setdnsaddr(argv) + char **argv; +{ + u_int32_t dns; + struct hostent *hp; + + dns = inet_addr(*argv); + if (dns == -1) { + if ((hp = gethostbyname(*argv)) == NULL) { + option_error("invalid address parameter '%s' for ms-dns option", + *argv); + return 0; + } + dns = *(u_int32_t *)hp->h_addr; + } + + /* if there is no primary then update it. */ + if (ipcp_allowoptions[0].dnsaddr[0] == 0) + ipcp_allowoptions[0].dnsaddr[0] = dns; + + /* always set the secondary address value to the same value. */ + ipcp_allowoptions[0].dnsaddr[1] = dns; + + return (1); +} + +/* + * setwinsaddr - set the wins address(es) + * This is primrarly used with the Samba package under UNIX or for pointing + * the caller to the existing WINS server on a Windows NT platform. + */ +static int +setwinsaddr(argv) + char **argv; +{ + u_int32_t wins; + struct hostent *hp; + + wins = inet_addr(*argv); + if (wins == -1) { + if ((hp = gethostbyname(*argv)) == NULL) { + option_error("invalid address parameter '%s' for ms-wins option", + *argv); + return 0; + } + wins = *(u_int32_t *)hp->h_addr; + } + + /* if there is no primary then update it. */ + if (ipcp_allowoptions[0].winsaddr[0] == 0) + ipcp_allowoptions[0].winsaddr[0] = wins; + + /* always set the secondary address value to the same value. */ + ipcp_allowoptions[0].winsaddr[1] = wins; + + return (1); +} + /* * ipcp_init - Initialize IPCP. */ -void +static void ipcp_init(unit) int unit; { @@ -134,17 +330,15 @@ ipcp_init(unit) ipcp_options *ao = &ipcp_allowoptions[unit]; f->unit = unit; - f->protocol = IPCP; + f->protocol = PPP_IPCP; f->callbacks = &ipcp_callbacks; fsm_init(&ipcp_fsm[unit]); - wo->neg_addr = 1; - wo->old_addrs = 0; - wo->ouraddr = 0; - wo->hisaddr = 0; + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + wo->neg_addr = 1; wo->neg_vj = 1; - wo->old_vj = 0; wo->vj_protocol = IPCP_VJ_COMP; wo->maxslotindex = MAX_STATES - 1; /* really max index */ wo->cflag = 1; @@ -157,13 +351,20 @@ ipcp_init(unit) ao->neg_vj = 1; ao->maxslotindex = MAX_STATES - 1; ao->cflag = 1; + + /* + * XXX These control whether the user may use the proxyarp + * and defaultroute options. + */ + ao->proxy_arp = 1; + ao->default_route = 1; } /* * ipcp_open - IPCP is allowed to come up. */ -void +static void ipcp_open(unit) int unit; { @@ -174,18 +375,19 @@ ipcp_open(unit) /* * ipcp_close - Take IPCP down. */ -void -ipcp_close(unit) +static void +ipcp_close(unit, reason) int unit; + char *reason; { - fsm_close(&ipcp_fsm[unit]); + fsm_close(&ipcp_fsm[unit], reason); } /* * ipcp_lowerup - The lower layer is up. */ -void +static void ipcp_lowerup(unit) int unit; { @@ -196,7 +398,7 @@ ipcp_lowerup(unit) /* * ipcp_lowerdown - The lower layer is down. */ -void +static void ipcp_lowerdown(unit) int unit; { @@ -207,7 +409,7 @@ ipcp_lowerdown(unit) /* * ipcp_input - Input IPCP packet. */ -void +static void ipcp_input(unit, p, len) int unit; u_char *p; @@ -222,7 +424,7 @@ ipcp_input(unit, p, len) * * Pretend the lower layer went down, so we shut up. */ -void +static void ipcp_protrej(unit) int unit; { @@ -257,10 +459,36 @@ ipcp_cilen(f) fsm *f; { ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; #define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) #define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + return (LENCIADDR(go->neg_addr, go->old_addrs) + LENCIVJ(go->neg_vj, go->old_vj)); } @@ -275,9 +503,7 @@ ipcp_addci(f, ucp, lenp) u_char *ucp; int *lenp; { - ipcp_options *wo = &ipcp_wantoptions[f->unit]; ipcp_options *go = &ipcp_gotoptions[f->unit]; - ipcp_options *ho = &ipcp_hisoptions[f->unit]; int len = *lenp; #define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ @@ -300,7 +526,7 @@ ipcp_addci(f, ucp, lenp) if (neg) { \ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ if (len >= addrlen) { \ - u_long l; \ + u_int32_t l; \ PUTCHAR(opt, ucp); \ PUTCHAR(addrlen, ucp); \ l = ntohl(val1); \ @@ -314,30 +540,6 @@ ipcp_addci(f, ucp, lenp) neg = 0; \ } - /* - * First see if we want to change our options to the old - * forms because we have received old forms from the peer. - */ - if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { - /* use the old style of address negotiation */ - go->neg_addr = 1; - go->old_addrs = 1; - } - if (wo->neg_vj && !go->neg_vj && !go->old_vj) { - /* try an older style of VJ negotiation */ - if (cis_received[f->unit] == 0) { - /* keep trying the new style until we see some CI from the peer */ - go->neg_vj = 1; - } else { - /* use the old style only if the peer did */ - if (ho->neg_vj && ho->old_vj) { - go->neg_vj = 1; - go->old_vj = 1; - go->vj_protocol = ho->vj_protocol; - } - } - } - ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, go->old_addrs, go->ouraddr, go->hisaddr); @@ -363,7 +565,7 @@ ipcp_ackci(f, p, len) { ipcp_options *go = &ipcp_gotoptions[f->unit]; u_short cilen, citype, cishort; - u_long cilong; + u_int32_t cilong; u_char cimaxslotindex, cicflag; /* @@ -398,7 +600,7 @@ ipcp_ackci(f, p, len) #define ACKCIADDR(opt, neg, old, val1, val2) \ if (neg) { \ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ - u_long l; \ + u_int32_t l; \ if ((len -= addrlen) < 0) \ goto bad; \ GETCHAR(citype, p); \ @@ -455,7 +657,7 @@ ipcp_nakci(f, p, len) u_char cimaxslotindex, cicflag; u_char citype, cilen, *next; u_short cishort; - u_long ciaddr1, ciaddr2, l; + u_int32_t ciaddr1, ciaddr2, l; ipcp_options no; /* options we've seen Naks for */ ipcp_options try; /* options to request next time */ @@ -494,10 +696,6 @@ ipcp_nakci(f, p, len) len -= cilen; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ - if (cilen == CILEN_VJ) { \ - GETCHAR(cimaxslotindex, p); \ - GETCHAR(cicflag, p); \ - } \ no.neg = 1; \ code \ } @@ -506,7 +704,7 @@ ipcp_nakci(f, p, len) * Accept the peer's idea of {our,his} address, if different * from our idea, only if the accept_{local,remote} flag is set. */ - NAKCIADDR(CI_ADDR, neg_addr, go->old_addrs, + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, if (go->accept_local && ciaddr1) { /* Do we know our address? */ try.ouraddr = ciaddr1; IPCPDEBUG((LOG_INFO, "local IP address %s", @@ -527,6 +725,8 @@ ipcp_nakci(f, p, len) */ NAKCIVJ(CI_COMPRESSTYPE, neg_vj, if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); if (cishort == IPCP_VJ_COMP) { try.old_vj = 0; if (cimaxslotindex < go->maxslotindex) @@ -567,7 +767,7 @@ ipcp_nakci(f, p, len) no.neg_vj = 1; break; case CI_ADDRS: - if (go->neg_addr && go->old_addrs || no.old_addrs + if ((go->neg_addr && go->old_addrs) || no.old_addrs || cilen != CILEN_ADDRS) goto bad; try.neg_addr = 1; @@ -585,16 +785,15 @@ ipcp_nakci(f, p, len) case CI_ADDR: if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) goto bad; - try.neg_addr = 1; try.old_addrs = 0; GETLONG(l, p); ciaddr1 = htonl(l); if (ciaddr1 && go->accept_local) try.ouraddr = ciaddr1; + if (try.ouraddr != 0) + try.neg_addr = 1; no.neg_addr = 1; break; - default: - goto bad; } p = next; } @@ -629,7 +828,7 @@ ipcp_rejci(f, p, len) ipcp_options *go = &ipcp_gotoptions[f->unit]; u_char cimaxslotindex, ciflag, cilen; u_short cishort; - u_long cilong; + u_int32_t cilong; ipcp_options try; /* options to request next time */ try = *go; @@ -643,7 +842,7 @@ ipcp_rejci(f, p, len) len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ p[1] == cilen && \ p[0] == opt) { \ - u_long l; \ + u_int32_t l; \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ @@ -728,13 +927,16 @@ ipcp_reqci(f, inp, len, reject_if_disagree) u_char *cip, *next; /* Pointer to current and next CIs */ u_short cilen, citype; /* Parsed len, type */ u_short cishort; /* Parsed short value */ - u_long tl, ciaddr1, ciaddr2;/* Parsed address values */ + u_int32_t tl, ciaddr1, ciaddr2;/* Parsed address values */ int rc = CONFACK; /* Final packet return code */ int orc; /* Individual option return code */ u_char *p; /* Pointer to next char to parse */ u_char *ucp = inp; /* Pointer to current output char */ int l = *len; /* Length left */ u_char maxslotindex, cflag; + int d; + + cis_received[f->unit] = 1; /* * Reset all his options. @@ -784,10 +986,17 @@ ipcp_reqci(f, inp, len, reject_if_disagree) && (ciaddr1 == 0 || !wo->accept_remote)) { orc = CONFNAK; if (!reject_if_disagree) { - DECPTR(sizeof (long), p); + DECPTR(sizeof(u_int32_t), p); tl = ntohl(wo->hisaddr); PUTLONG(tl, p); } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; } /* @@ -801,7 +1010,7 @@ ipcp_reqci(f, inp, len, reject_if_disagree) if (ciaddr2 == 0 || !wo->accept_local) { orc = CONFNAK; if (!reject_if_disagree) { - DECPTR(sizeof (long), p); + DECPTR(sizeof(u_int32_t), p); tl = ntohl(wo->ouraddr); PUTLONG(tl, p); } @@ -838,15 +1047,64 @@ ipcp_reqci(f, inp, len, reject_if_disagree) && (ciaddr1 == 0 || !wo->accept_remote)) { orc = CONFNAK; if (!reject_if_disagree) { - DECPTR(sizeof (long), p); + DECPTR(sizeof(u_int32_t), p); tl = ntohl(wo->hisaddr); PUTLONG(tl, p); } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; } ho->neg_addr = 1; ho->hisaddr = ciaddr1; break; + + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + IPCPDEBUG((LOG_INFO, "ipcp: received DNS%d Request ", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->dnsaddr[d]) { + DECPTR(sizeof(u_int32_t), p); + tl = ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + IPCPDEBUG((LOG_INFO, "ipcp: received WINS%d Request ", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u_int32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; case CI_COMPRESSTYPE: IPCPDEBUG((LOG_INFO, "ipcp: received COMPRESSTYPE ")); @@ -884,7 +1142,11 @@ ipcp_reqci(f, inp, len, reject_if_disagree) } } ho->maxslotindex = maxslotindex; - ho->cflag = wo->cflag; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_STATES - 1; + ho->cflag = 1; } break; @@ -953,6 +1215,78 @@ endswitch: } +/* + * ip_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void +ip_check_options() +{ + struct hostent *hp; + u_int32_t local; + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * Default our local IP address based on our hostname. + * If local IP address already given, don't bother. + */ + if (wo->ouraddr == 0 && !disable_defaultip) { + /* + * Look up our hostname (possibly with domain name appended) + * and take the first IP address as our local IP address. + * If there isn't an IP address for our hostname, too bad. + */ + wo->accept_local = 1; /* don't insist on this default value */ + if ((hp = gethostbyname(hostname)) != NULL) { + local = *(u_int32_t *)hp->h_addr; + if (local != 0 && !bad_ip_adrs(local)) + wo->ouraddr = local; + } + } + + if (demand && wo->hisaddr == 0) { + option_error("remote IP address required for demand-dialling\n"); + exit(1); + } +#if 0 + if (demand && wo->accept_remote) { + option_error("ipcp-accept-remote is incompatible with demand\n"); + exit(1); + } +#endif +} + + +/* + * ip_demand_conf - configure the interface as though + * IPCP were up, for use with dial-on-demand. + */ +static int +ip_demand_conf(u) + int u; +{ + ipcp_options *wo = &ipcp_wantoptions[u]; + + if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr))) + return 0; + if (!sifup(u)) + return 0; + if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE)) + return 0; + if (wo->default_route) + if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr)) + default_route_set[u] = 1; + if (wo->proxy_arp) + if (sifproxyarp(u, wo->hisaddr)) + proxy_arp_set[u] = 1; + + syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(wo->ouraddr)); + syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(wo->hisaddr)); + + return 1; +} + + /* * ipcp_up - IPCP has come UP. * @@ -962,73 +1296,143 @@ static void ipcp_up(f) fsm *f; { - u_long mask; + u_int32_t mask; ipcp_options *ho = &ipcp_hisoptions[f->unit]; ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + np_up(f->unit, PPP_IP); IPCPDEBUG((LOG_INFO, "ipcp: up")); - go->default_route = 0; - go->proxy_arp = 0; /* * We must have a non-zero IP address for both ends of the link. */ if (!ho->neg_addr) - ho->hisaddr = ipcp_wantoptions[f->unit].hisaddr; + ho->hisaddr = wo->hisaddr; if (ho->hisaddr == 0) { syslog(LOG_ERR, "Could not determine remote IP address"); - ipcp_close(f->unit); + ipcp_close(f->unit, "Could not determine remote IP address"); return; } if (go->ouraddr == 0) { syslog(LOG_ERR, "Could not determine local IP address"); - ipcp_close(f->unit); + ipcp_close(f->unit, "Could not determine local IP address"); return; } + script_setenv("IPLOCAL", ip_ntoa(go->ouraddr)); + script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr)); /* - * Check that the peer is allowed to use the IP address he wants. + * Check that the peer is allowed to use the IP address it wants. */ if (!auth_ip_addr(f->unit, ho->hisaddr)) { syslog(LOG_ERR, "Peer is not authorized to use remote address %s", ip_ntoa(ho->hisaddr)); - ipcp_close(f->unit); + ipcp_close(f->unit, "Unauthorized remote IP address"); return; } - syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(go->ouraddr)); - syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(ho->hisaddr)); + /* set tcp compression */ + sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); /* - * Set IP addresses and (if specified) netmask. + * If we are doing dial-on-demand, the interface is already + * configured, so we put out any saved-up packets, then set the + * interface to pass IP packets. */ - mask = GetMask(go->ouraddr); - if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { - IPCPDEBUG((LOG_WARNING, "sifaddr failed")); - ipcp_close(f->unit); - return; - } + if (demand) { + if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) { + if (go->ouraddr != wo->ouraddr) { + syslog(LOG_WARNING, "Local IP address changed to %s", + ip_ntoa(go->ouraddr)); + script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr)); + wo->ouraddr = go->ouraddr; + } else + script_unsetenv("OLDIPLOCAL"); + if (ho->hisaddr != wo->hisaddr) { + syslog(LOG_WARNING, "Remote IP address changed to %s", + ip_ntoa(ho->hisaddr)); + script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr)); + wo->hisaddr = ho->hisaddr; + } else + script_unsetenv("OLDIPREMOTE"); + ipcp_clear_addrs(f->unit); + + /* Set the interface to the new addresses */ + mask = GetMask(go->ouraddr); + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } - /* set tcp compression */ - sifvjcomp(f->unit, ho->neg_vj, ho->cflag); + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) + default_route_set[f->unit] = 1; - /* bring the interface up for IP */ - if (!sifup(f->unit)) { - IPCPDEBUG((LOG_WARNING, "sifup failed")); - ipcp_close(f->unit); - return; - } + /* Make a proxy ARP entry if requested. */ + if (ipcp_wantoptions[f->unit].proxy_arp) + if (sifproxyarp(f->unit, ho->hisaddr)) + proxy_arp_set[f->unit] = 1; - /* assign a default route through the interface if required */ - if (ipcp_wantoptions[f->unit].default_route) - if (sifdefaultroute(f->unit, ho->hisaddr)) - go->default_route = 1; + } + demand_rexmit(PPP_IP); + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + } else { + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + +#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } +#endif + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG((LOG_WARNING, "sifup failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } - /* Make a proxy ARP entry if requested. */ - if (ipcp_wantoptions[f->unit].proxy_arp) - if (sifproxyarp(f->unit, ho->hisaddr)) - go->proxy_arp = 1; +#if (defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } +#endif + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) + default_route_set[f->unit] = 1; + + /* Make a proxy ARP entry if requested. */ + if (ipcp_wantoptions[f->unit].proxy_arp) + if (sifproxyarp(f->unit, ho->hisaddr)) + proxy_arp_set[f->unit] = 1; + + syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(go->ouraddr)); + syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(ho->hisaddr)); + } + + /* + * Execute the ip-up script, like this: + * /etc/ppp/ip-up interface tty speed local-IP remote-IP + */ + if (ipcp_script_state == s_down && ipcp_script_pid == 0) { + ipcp_script_state = s_up; + ipcp_script(_PATH_IPUP); + } } @@ -1042,16 +1446,285 @@ static void ipcp_down(f) fsm *f; { - u_long ouraddr, hisaddr; - IPCPDEBUG((LOG_INFO, "ipcp: down")); + np_down(f->unit, PPP_IP); + sifvjcomp(f->unit, 0, 0, 0); + + /* + * If we are doing dial-on-demand, set the interface + * to queue up outgoing packets (for now). + */ + if (demand) { + sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE); + } else { + sifdown(f->unit); + ipcp_clear_addrs(f->unit); + } + + /* Execute the ip-down script */ + if (ipcp_script_state == s_up && ipcp_script_pid == 0) { + ipcp_script_state = s_down; + ipcp_script(_PATH_IPDOWN); + } +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, + * proxy arp entries, etc. + */ +static void +ipcp_clear_addrs(unit) + int unit; +{ + u_int32_t ouraddr, hisaddr; - ouraddr = ipcp_gotoptions[f->unit].ouraddr; - hisaddr = ipcp_hisoptions[f->unit].hisaddr; - if (ipcp_gotoptions[f->unit].proxy_arp) - cifproxyarp(f->unit, hisaddr); - if (ipcp_gotoptions[f->unit].default_route) - cifdefaultroute(f->unit, hisaddr); - sifdown(f->unit); - cifaddr(f->unit, ouraddr, hisaddr); + ouraddr = ipcp_gotoptions[unit].ouraddr; + hisaddr = ipcp_hisoptions[unit].hisaddr; + if (proxy_arp_set[unit]) { + cifproxyarp(unit, hisaddr); + proxy_arp_set[unit] = 0; + } + if (default_route_set[unit]) { + cifdefaultroute(unit, ouraddr, hisaddr); + default_route_set[unit] = 0; + } + cifaddr(unit, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void +ipcp_finished(f) + fsm *f; +{ + np_finished(f->unit, PPP_IP); +} + + +/* + * ipcp_script_done - called when the ip-up or ip-down script + * has finished. + */ +static void +ipcp_script_done(void *arg) +{ + ipcp_script_pid = 0; + switch (ipcp_script_state) { + case s_up: + if (ipcp_fsm[0].state != OPENED) { + ipcp_script_state = s_down; + ipcp_script(_PATH_IPDOWN); + } + break; + case s_down: + if (ipcp_fsm[0].state == OPENED) { + ipcp_script_state = s_up; + ipcp_script(_PATH_IPUP); + } + break; + } +} + + +/* + * ipcp_script - Execute a script with arguments + * interface-name tty-name speed local-IP remote-IP. + */ +static void +ipcp_script(script) + char *script; +{ + char strspeed[32], strlocal[32], strremote[32]; + char *argv[8]; + + sprintf(strspeed, "%d", baud_rate); + strcpy(strlocal, ip_ntoa(ipcp_gotoptions[0].ouraddr)); + strcpy(strremote, ip_ntoa(ipcp_hisoptions[0].hisaddr)); + + argv[0] = script; + argv[1] = ifname; + argv[2] = devnam; + argv[3] = strspeed; + argv[4] = strlocal; + argv[5] = strremote; + argv[6] = ipparam; + argv[7] = NULL; + ipcp_script_pid = run_program(script, argv, 0, ipcp_script_done, NULL); +} + +/* + * ipcp_printpkt - print the contents of an IPCP packet. + */ +static char *ipcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej" +}; + +static int +ipcp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u_int32_t cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ipcp_codenames) / sizeof(char *)) + printer(arg, " %s", ipcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_ADDRS: + if (olen == CILEN_ADDRS) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addrs %I", htonl(cilong)); + GETLONG(cilong, p); + printer(arg, " %I", htonl(cilong)); + } + break; + case CI_COMPRESSTYPE: + if (olen >= CILEN_COMPRESS) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "compress "); + switch (cishort) { + case IPCP_VJ_COMP: + printer(arg, "VJ"); + break; + case IPCP_VJ_COMP_OLD: + printer(arg, "old-VJ"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_ADDR: + if (olen == CILEN_ADDR) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addr %I", htonl(cilong)); + } + break; + case CI_MS_DNS1: + case CI_MS_DNS2: + p += 2; + GETLONG(cilong, p); + printer(arg, "ms-dns %I", htonl(cilong)); + break; + case CI_MS_WINS1: + case CI_MS_WINS2: + p += 2; + GETLONG(cilong, p); + printer(arg, "ms-wins %I", htonl(cilong)); + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string(p, len, printer, arg); + p += len; + len = 0; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} + +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#define IPPROTO_TCP 6 +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(pkt, len) + u_char *pkt; + int len; +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) + return 0; + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) + return 0; + if (get_ipproto(pkt) != IPPROTO_TCP) + return 1; + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) + return 0; + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) + return 0; + return 1; }