* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
/*
* TODO:
*/
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 */
static bool usepeerdns; /* Ask peer for DNS addrs */
+static bool usepeerwins; /* Ask peer for WINS addrs */
static int ipcp_is_up; /* have called np_up() */
static int ipcp_is_open; /* haven't called np_finished() */
static bool ask_for_local; /* request our address from peer */
"disable defaultroute option", OPT_ALIAS | OPT_A2CLR,
&ipcp_wantoptions[0].default_route },
+#ifdef __linux__
+ { "replacedefaultroute", o_bool,
+ &ipcp_wantoptions[0].replace_default_route,
+ "Replace default route", OPT_PRIV | 1
+ },
+ { "noreplacedefaultroute", o_bool,
+ &ipcp_wantoptions[0].replace_default_route,
+ "Do not replace default route", 0 },
+#endif
{ "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,
{ "usepeerdns", o_bool, &usepeerdns,
"Ask peer for DNS address(es)", 1 },
+ { "usepeerwins", o_bool, &usepeerwins,
+ "Ask peer for WINS address(es)", 1 },
+
{ "netmask", o_special, (void *)setnetmask,
"set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str },
ip_active_pkt
};
-static void ipcp_clear_addrs (int, u_int32_t, u_int32_t);
+static void ipcp_clear_addrs (int, u_int32_t, u_int32_t, bool);
static void ipcp_script (char *, int); /* Run an up/down script */
static void ipcp_script_done (void *);
ipcp_options *go = &ipcp_gotoptions[f->unit];
ipcp_options *ao = &ipcp_allowoptions[f->unit];
- wo->req_addr = (wo->neg_addr || wo->old_addrs) &&
- (ao->neg_addr || ao->old_addrs);
+ wo->req_addr = ((wo->neg_addr || wo->old_addrs) &&
+ (ao->neg_addr || ao->old_addrs)) ||
+ (wo->hisaddr && !wo->accept_remote);
if (wo->ouraddr == 0)
wo->accept_local = 1;
if (wo->hisaddr == 0)
wo->accept_remote = 1;
wo->req_dns1 = usepeerdns; /* Request DNS addresses from the peer */
wo->req_dns2 = usepeerdns;
+ wo->req_wins1 = usepeerwins; /* Request WINS addresses from the peer */
+ wo->req_wins2 = usepeerwins;
*go = *wo;
if (!ask_for_local)
go->ouraddr = 0;
LENCIADDR(go->neg_addr) +
LENCIDNS(go->req_dns1) +
LENCIDNS(go->req_dns2) +
- LENCIWINS(go->winsaddr[0]) +
- LENCIWINS(go->winsaddr[1])) ;
+ LENCIWINS(go->req_wins1) +
+ LENCIWINS(go->req_wins2)) ;
}
neg = 0; \
}
-#define ADDCIWINS(opt, addr) \
- if (addr) { \
+#define ADDCIWINS(opt, neg, addr) \
+ if (neg) { \
if (len >= CILEN_ADDR) { \
u_int32_t l; \
PUTCHAR(opt, ucp); \
PUTLONG(l, ucp); \
len -= CILEN_ADDR; \
} else \
- addr = 0; \
+ neg = 0; \
}
ADDCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
- ADDCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+ ADDCIWINS(CI_MS_WINS1, go->req_wins1, go->winsaddr[0]);
- ADDCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+ ADDCIWINS(CI_MS_WINS2, go->req_wins2, go->winsaddr[1]);
*lenp -= len;
}
goto bad; \
}
-#define ACKCIWINS(opt, addr) \
- if (addr) { \
+#define ACKCIWINS(opt, neg, addr) \
+ if (neg) { \
u_int32_t l; \
if ((len -= CILEN_ADDR) < 0) \
goto bad; \
ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
- ACKCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+ ACKCIWINS(CI_MS_WINS1, go->req_wins1, go->winsaddr[0]);
- ACKCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+ ACKCIWINS(CI_MS_WINS2, go->req_wins2, go->winsaddr[1]);
/*
* If there are any remaining CIs, then this packet is bad.
static int
ipcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject)
{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
ipcp_options *go = &ipcp_gotoptions[f->unit];
u_char cimaxslotindex, cicflag;
u_char citype, cilen, *next;
u_short cishort;
- u_int32_t ciaddr1, ciaddr2, l, cidnsaddr;
+ u_int32_t ciaddr1, ciaddr2, l, cidnsaddr, ciwinsaddr;
ipcp_options no; /* options we've seen Naks for */
ipcp_options try; /* options to request next time */
code \
}
+#define NAKCIWINS(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciwinsaddr = htonl(l); \
+ no.neg = 1; \
+ code \
+ }
+
/*
* Accept the peer's idea of {our,his} address, if different
* from our idea, only if the accept_{local,remote} flag is set.
}
);
+ NAKCIWINS(CI_MS_WINS1, req_wins1,
+ if (treat_as_reject) {
+ try.req_wins1 = 0;
+ } else {
+ try.winsaddr[0] = ciwinsaddr;
+ }
+ );
+
+ NAKCIWINS(CI_MS_WINS2, req_wins2,
+ if (treat_as_reject) {
+ try.req_wins2 = 0;
+ } else {
+ try.winsaddr[1] = ciwinsaddr;
+ }
+ );
+
/*
* There may be remaining CIs, if the peer is requesting negotiation
* on an option that we didn't include in our request packet.
GETLONG(l, p);
ciaddr1 = htonl(l);
if (ciaddr1 && go->accept_local)
- try.ouraddr = ciaddr1;
+ try.ouraddr = wo->old_addrs ? ciaddr1 : 0;
GETLONG(l, p);
ciaddr2 = htonl(l);
if (ciaddr2 && go->accept_remote)
ciaddr1 = htonl(l);
if (ciaddr1 && go->accept_local)
try.ouraddr = ciaddr1;
- if (try.ouraddr != 0)
+ if (try.ouraddr != 0 && wo->neg_addr)
try.neg_addr = 1;
no.neg_addr = 1;
break;
no.req_dns2 = 1;
break;
case CI_MS_WINS1:
+ if (go->req_wins1 || no.req_wins1 || cilen != CILEN_ADDR)
+ goto bad;
+ GETLONG(l, p);
+ try.winsaddr[0] = htonl(l);
+ try.req_wins1 = 1;
+ no.req_wins1 = 1;
+ break;
case CI_MS_WINS2:
- if (cilen != CILEN_ADDR)
+ if (go->req_wins2 || no.req_wins2 || cilen != CILEN_ADDR)
goto bad;
GETLONG(l, p);
- ciaddr1 = htonl(l);
- if (ciaddr1)
- try.winsaddr[citype == CI_MS_WINS2] = ciaddr1;
+ try.winsaddr[1] = htonl(l);
+ try.req_wins2 = 1;
+ no.req_wins2 = 1;
break;
}
p = next;
return 0;
}
-
/*
* ipcp_rejci - Reject some of our CIs.
* Callback from fsm_rconfnakrej.
try.neg = 0; \
}
-#define REJCIWINS(opt, addr) \
- if (addr && \
+#define REJCIWINS(opt, neg, addr) \
+ if (go->neg && \
((cilen = p[1]) == CILEN_ADDR) && \
len >= cilen && \
p[0] == opt) { \
/* Check rejected value. */ \
if (cilong != addr) \
goto bad; \
- try.winsaddr[opt == CI_MS_WINS2] = 0; \
+ try.neg = 0; \
}
REJCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
- REJCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+ REJCIWINS(CI_MS_WINS1, req_wins1, go->winsaddr[0]);
- REJCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+ REJCIWINS(CI_MS_WINS2, req_wins2, go->winsaddr[1]);
/*
* If there are any remaining CIs, then this packet is bad.
if (ciaddr2 != wo->ouraddr) {
if (ciaddr2 == 0 || !wo->accept_local) {
orc = CONFNAK;
- if (!reject_if_disagree) {
+ if (!reject_if_disagree && wo->old_addrs) {
DECPTR(sizeof(u_int32_t), p);
tl = ntohl(wo->ouraddr);
PUTLONG(tl, p);
/* Microsoft primary or secondary WINS request */
d = citype == CI_MS_WINS2;
- /* If we do not have a DNS address then we cannot send it */
+ /* If we do not have a WINS address then we cannot send it */
if (ao->winsaddr[d] == 0 ||
cilen != CILEN_ADDR) { /* Check CI length */
orc = CONFREJ; /* Reject CI */
* option safely.
*/
if (rc != CONFREJ && !ho->neg_addr && !ho->old_addrs &&
- wo->req_addr && !reject_if_disagree && !noremoteip) {
+ wo->req_addr && !reject_if_disagree &&
+ ((wo->hisaddr && !wo->accept_remote) || !noremoteip)) {
if (rc == CONFACK) {
rc = CONFNAK;
ucp = inp; /* reset pointer */
if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
return 0;
if (wo->default_route)
- if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr))
+ if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr,
+ wo->replace_default_route))
default_route_set[u] = 1;
if (wo->proxy_arp)
if (sifproxyarp(u, wo->hisaddr))
/*
* We must have a non-zero IP address for both ends of the link.
*/
+
+ if (wo->hisaddr && !wo->accept_remote && (!(ho->neg_addr || ho->old_addrs) || ho->hisaddr != wo->hisaddr)) {
+ error("Peer refused to agree to his IP address");
+ ipcp_close(f->unit, "Refused his IP address");
+ return;
+ }
if (!ho->neg_addr && !ho->old_addrs)
ho->hisaddr = wo->hisaddr;
create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
}
+ if (go->winsaddr[0])
+ script_setenv("WINS1", ip_ntoa(go->winsaddr[0]), 0);
+ if (go->winsaddr[1])
+ script_setenv("WINS2", ip_ntoa(go->winsaddr[1]), 0);
+ if (usepeerwins && (go->winsaddr[0] || go->winsaddr[1]))
+ script_setenv("USEPEERWINS", "1", 0);
+
/*
* Check that the peer is allowed to use the IP address it wants.
*/
*/
if (demand) {
if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
- ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr);
+ ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr,
+ wo->replace_default_route);
if (go->ouraddr != wo->ouraddr) {
warn("Local IP address changed to %I", go->ouraddr);
script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
wo->ouraddr = go->ouraddr;
} else
script_unsetenv("OLDIPLOCAL");
- if (ho->hisaddr != wo->hisaddr && wo->hisaddr != 0) {
+ if (ho->hisaddr != wo->hisaddr) {
warn("Remote IP address changed to %I", ho->hisaddr);
- script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
+ if (wo->hisaddr != 0)
+ script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
wo->hisaddr = ho->hisaddr;
} else
script_unsetenv("OLDIPREMOTE");
/* assign a default route through the interface if required */
if (ipcp_wantoptions[f->unit].default_route)
- if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr,
+ wo->replace_default_route))
default_route_set[f->unit] = 1;
/* Make a proxy ARP entry if requested. */
/* assign a default route through the interface if required */
if (ipcp_wantoptions[f->unit].default_route)
- if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr,
+ wo->replace_default_route))
default_route_set[f->unit] = 1;
/* Make a proxy ARP entry if requested. */
sifnpmode(f->unit, PPP_IP, NPMODE_DROP);
sifdown(f->unit);
ipcp_clear_addrs(f->unit, ipcp_gotoptions[f->unit].ouraddr,
- ipcp_hisoptions[f->unit].hisaddr);
+ ipcp_hisoptions[f->unit].hisaddr, 0);
}
/* Execute the ip-down script */
* proxy arp entries, etc.
*/
static void
-ipcp_clear_addrs(int unit, u_int32_t ouraddr, u_int32_t hisaddr)
+ipcp_clear_addrs(int unit, u_int32_t ouraddr, u_int32_t hisaddr, bool replacedefaultroute)
{
if (proxy_arp_set[unit]) {
cifproxyarp(unit, hisaddr);
proxy_arp_set[unit] = 0;
}
- if (default_route_set[unit]) {
+ /* If replacedefaultroute, sifdefaultroute will be called soon
+ * with replacedefaultroute set and that will overwrite the current
+ * default route. This is the case only when doing demand, otherwise
+ * during demand, this cifdefaultroute would restore the old default
+ * route which is not what we want in this case. In the non-demand
+ * case, we'll delete the default route and restore the old if there
+ * is one saved by an sifdefaultroute with replacedefaultroute.
+ */
+ if (!replacedefaultroute && default_route_set[unit]) {
cifdefaultroute(unit, ouraddr, hisaddr);
default_route_set[unit] = 0;
}