* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: ipcp.c,v 1.52 1999/12/23 01:25:33 paulus Exp $"
+#define RCSID "$Id: ipcp.c,v 1.58 2001/04/27 23:13:06 paulus Exp $"
/*
* TODO:
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/types.h>
/* global vars */
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_allowoptions[NUM_PPP]; /* Options we allow peer to request */
ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+u_int32_t netmask = 0; /* IP netmask to set on interface */
+
bool disable_defaultip = 0; /* Don't use hostname for default IP adrs */
/* Hook for a plugin to know when IP protocol has come up */
/* Hook for a plugin to know when IP protocol has come down */
void (*ip_down_hook) __P((void)) = NULL;
+/* Hook for a plugin to choose the remote IP address */
+void (*ip_choose_hook) __P((u_int32_t *)) = NULL;
+
+/* Notifiers for when IPCP goes up and down */
+struct notifier *ip_up_notifier = NULL;
+struct notifier *ip_down_notifier = NULL;
+
/* local vars */
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 int ipcp_is_up; /* have called np_up() */
+static bool ask_for_local; /* request our address from peer */
+static char vj_value[8]; /* string form of vj option value */
+static char netmask_str[20]; /* string form of netmask value */
/*
* Callbacks for fsm code. (CI = Configuration Information)
static int setvjslots __P((char **));
static int setdnsaddr __P((char **));
static int setwinsaddr __P((char **));
+static int setnetmask __P((char **));
+static int setipaddr __P((char *, char **, int));
+static void printipaddr __P((option_t *, void (*)(void *, char *,...),void *));
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" },
+ "Disable IP and IPCP", OPT_ALIAS },
+
{ "novj", o_bool, &ipcp_wantoptions[0].neg_vj,
- "Disable VJ compression", OPT_A2COPY, &ipcp_allowoptions[0].neg_vj },
+ "Disable VJ compression", OPT_A2CLR, &ipcp_allowoptions[0].neg_vj },
{ "-vj", o_bool, &ipcp_wantoptions[0].neg_vj,
- "Disable VJ compression", OPT_A2COPY, &ipcp_allowoptions[0].neg_vj },
+ "Disable VJ compression", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_allowoptions[0].neg_vj },
+
{ "novjccomp", o_bool, &ipcp_wantoptions[0].cflag,
- "Disable VJ connection-ID compression", OPT_A2COPY,
+ "Disable VJ connection-ID compression", OPT_A2CLR,
&ipcp_allowoptions[0].cflag },
{ "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag,
- "Disable VJ connection-ID compression", OPT_A2COPY,
+ "Disable VJ connection-ID compression", OPT_ALIAS | OPT_A2CLR,
&ipcp_allowoptions[0].cflag },
- { "vj-max-slots", 1, setvjslots,
- "Set maximum VJ header slots" },
+
+ { "vj-max-slots", o_special, (void *)setvjslots,
+ "Set maximum VJ header slots",
+ OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, vj_value },
+
{ "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" },
+ "Set ip script parameter", OPT_PRIO },
+
{ "noipdefault", o_bool, &disable_defaultip,
"Don't use name for default IP adrs", 1 },
- { "ms-dns", 1, setdnsaddr,
+
+ { "ms-dns", 1, (void *)setdnsaddr,
"DNS address for the peer's use" },
- { "ms-wins", 1, setwinsaddr,
+ { "ms-wins", 1, (void *)setwinsaddr,
"Nameserver for SMB over TCP/IP for peer" },
+
{ "ipcp-restart", o_int, &ipcp_fsm[0].timeouttime,
- "Set timeout for IPCP" },
+ "Set timeout for IPCP", OPT_PRIO },
{ "ipcp-max-terminate", o_int, &ipcp_fsm[0].maxtermtransmits,
- "Set max #xmits for term-reqs" },
+ "Set max #xmits for term-reqs", OPT_PRIO },
{ "ipcp-max-configure", o_int, &ipcp_fsm[0].maxconfreqtransmits,
- "Set max #xmits for conf-reqs" },
+ "Set max #xmits for conf-reqs", OPT_PRIO },
{ "ipcp-max-failure", o_int, &ipcp_fsm[0].maxnakloops,
- "Set max #conf-naks for IPCP" },
+ "Set max #conf-naks for IPCP", OPT_PRIO },
+
{ "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,
+ "disable defaultroute option", OPT_A2CLR,
&ipcp_wantoptions[0].default_route },
{ "-defaultroute", o_bool, &ipcp_allowoptions[0].default_route,
- "disable defaultroute option", OPT_A2COPY,
+ "disable defaultroute option", OPT_ALIAS | OPT_A2CLR,
&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,
+ "disable proxyarp option", OPT_A2CLR,
&ipcp_wantoptions[0].proxy_arp },
{ "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
- "disable proxyarp option", OPT_A2COPY,
+ "disable proxyarp option", OPT_ALIAS | OPT_A2CLR,
&ipcp_wantoptions[0].proxy_arp },
+
{ "usepeerdns", o_bool, &usepeerdns,
"Ask peer for DNS address(es)", 1 },
+
+ { "netmask", o_special, (void *)setnetmask,
+ "set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str },
+
+ { "IP addresses", o_wild, (void *) &setipaddr,
+ "set local and remote IP addresses",
+ OPT_NOARG | OPT_A2PRINTER, (void *) &printipaddr },
+
{ NULL }
};
}
ipcp_wantoptions [0].maxslotindex =
ipcp_allowoptions[0].maxslotindex = value - 1;
+ slprintf(vj_value, sizeof(vj_value), "%d", value);
return 1;
}
dns = *(u_int32_t *)hp->h_addr;
}
- /* if there is no primary then update it. */
- if (ipcp_allowoptions[0].dnsaddr[0] == 0)
+ /* We take the last 2 values given, the 2nd-last as the primary
+ and the last as the secondary. If only one is given it
+ becomes both primary and secondary. */
+ if (ipcp_allowoptions[0].dnsaddr[1] == 0)
ipcp_allowoptions[0].dnsaddr[0] = dns;
+ else
+ ipcp_allowoptions[0].dnsaddr[0] = ipcp_allowoptions[0].dnsaddr[1];
- /* always set the secondary address value to the same value. */
+ /* always set the secondary address value. */
ipcp_allowoptions[0].dnsaddr[1] = dns;
return (1);
wins = *(u_int32_t *)hp->h_addr;
}
- /* if there is no primary then update it. */
- if (ipcp_allowoptions[0].winsaddr[0] == 0)
+ /* We take the last 2 values given, the 2nd-last as the primary
+ and the last as the secondary. If only one is given it
+ becomes both primary and secondary. */
+ if (ipcp_allowoptions[0].winsaddr[1] == 0)
ipcp_allowoptions[0].winsaddr[0] = wins;
+ else
+ ipcp_allowoptions[0].winsaddr[0] = ipcp_allowoptions[0].winsaddr[1];
- /* always set the secondary address value to the same value. */
+ /* always set the secondary address value. */
ipcp_allowoptions[0].winsaddr[1] = wins;
return (1);
}
+/*
+ * setipaddr - Set the IP address
+ * If doit is 0, the call is to check whether this option is
+ * potentially an IP address specification.
+ */
+static int
+setipaddr(arg, argv, doit)
+ char *arg;
+ char **argv;
+ int doit;
+{
+ struct hostent *hp;
+ char *colon;
+ u_int32_t local, remote;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+ static int prio_local = 0, prio_remote = 0;
+
+ /*
+ * IP address pair separated by ":".
+ */
+ if ((colon = strchr(arg, ':')) == NULL)
+ return 0;
+ if (!doit)
+ return 1;
+
+ /*
+ * If colon first character, then no local addr.
+ */
+ if (colon != arg && option_priority >= prio_local) {
+ *colon = '\0';
+ if ((local = inet_addr(arg)) == (u_int32_t) -1) {
+ if ((hp = gethostbyname(arg)) == NULL) {
+ option_error("unknown host: %s", arg);
+ return 0;
+ }
+ local = *(u_int32_t *)hp->h_addr;
+ }
+ if (bad_ip_adrs(local)) {
+ option_error("bad local IP address %s", ip_ntoa(local));
+ return 0;
+ }
+ if (local != 0)
+ wo->ouraddr = local;
+ *colon = ':';
+ prio_local = option_priority;
+ }
+
+ /*
+ * If colon last character, then no remote addr.
+ */
+ if (*++colon != '\0' && option_priority >= prio_remote) {
+ if ((remote = inet_addr(colon)) == (u_int32_t) -1) {
+ if ((hp = gethostbyname(colon)) == NULL) {
+ option_error("unknown host: %s", colon);
+ return 0;
+ }
+ remote = *(u_int32_t *)hp->h_addr;
+ if (remote_name[0] == 0)
+ strlcpy(remote_name, colon, sizeof(remote_name));
+ }
+ if (bad_ip_adrs(remote)) {
+ option_error("bad remote IP address %s", ip_ntoa(remote));
+ return 0;
+ }
+ if (remote != 0)
+ wo->hisaddr = remote;
+ prio_remote = option_priority;
+ }
+
+ return 1;
+}
+
+static void
+printipaddr(opt, printer, arg)
+ option_t *opt;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ if (wo->ouraddr != 0)
+ printer(arg, "%I", wo->ouraddr);
+ printer(arg, ":");
+ if (wo->hisaddr != 0)
+ printer(arg, "%I", wo->hisaddr);
+}
+
+/*
+ * setnetmask - set the netmask to be used on the interface.
+ */
+static int
+setnetmask(argv)
+ char **argv;
+{
+ u_int32_t mask;
+ int n;
+ char *p;
+
+ /*
+ * Unfortunately, if we use inet_addr, we can't tell whether
+ * a result of all 1s is an error or a valid 255.255.255.255.
+ */
+ p = *argv;
+ n = parse_dotted_ip(p, &mask);
+
+ mask = htonl(mask);
+
+ if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
+ option_error("invalid netmask value '%s'", *argv);
+ return 0;
+ }
+
+ netmask = mask;
+ slprintf(netmask_str, sizeof(netmask_str), "%I", mask);
+
+ return (1);
+}
+
+int
+parse_dotted_ip(p, vp)
+ char *p;
+ u_int32_t *vp;
+{
+ int n;
+ u_int32_t v, b;
+ char *endp, *p0 = p;
+
+ v = 0;
+ for (n = 3;; --n) {
+ b = strtoul(p, &endp, 0);
+ if (endp == p)
+ return 0;
+ if (b > 255) {
+ if (n < 3)
+ return 0;
+ /* accept e.g. 0xffffff00 */
+ *vp = b;
+ return endp - p0;
+ }
+ v |= b << (n * 8);
+ p = endp;
+ if (n == 0)
+ break;
+ if (*p != '.')
+ return 0;
+ ++p;
+ }
+ *vp = v;
+ return p - p0;
+}
+
/*
* ipcp_init - Initialize IPCP.
ipcp_options *go = &ipcp_gotoptions[f->unit];
wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
- if (wo->ouraddr == 0 || disable_defaultip)
+ 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;
*go = *wo;
- if (disable_defaultip)
+ if (!ask_for_local)
go->ouraddr = 0;
+ if (ip_choose_hook)
+ ip_choose_hook(&wo->hisaddr);
}
* Default our local IP address based on our hostname.
* If local IP address already given, don't bother.
*/
- if (wo->ouraddr == 0) {
+ 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.
wo->ouraddr = local;
}
}
+ ask_for_local = wo->ouraddr != 0 || !disable_defaultip;
}
/* make up an arbitrary address for us */
wo->ouraddr = htonl(0x0a404040 + ifunit);
wo->accept_local = 1;
- disable_defaultip = 1; /* don't tell the peer this address */
+ ask_for_local = 0; /* don't tell the peer this address */
}
if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
return 0;
if (!ho->neg_addr)
ho->hisaddr = wo->hisaddr;
- if (ho->hisaddr == 0) {
- error("Could not determine remote IP address");
- ipcp_close(f->unit, "Could not determine remote IP address");
- return;
- }
if (go->ouraddr == 0) {
error("Could not determine local IP address");
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));
+ if (ho->hisaddr == 0) {
+ ho->hisaddr = htonl(0x0a404040 + ifunit);
+ warn("Could not determine remote IP address: defaulting to %I",
+ ho->hisaddr);
+ }
+ script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
+ script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
if (usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
- script_setenv("USEPEERDNS", "1");
+ script_setenv("USEPEERDNS", "1", 0);
if (go->dnsaddr[0])
- script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]));
+ script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
if (go->dnsaddr[1])
- script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]));
+ script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
}
ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr);
if (go->ouraddr != wo->ouraddr) {
warn("Local IP address changed to %I", go->ouraddr);
- script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr));
+ script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
wo->ouraddr = go->ouraddr;
} else
script_unsetenv("OLDIPLOCAL");
if (ho->hisaddr != wo->hisaddr) {
warn("Remote IP address changed to %I", ho->hisaddr);
- script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr));
+ script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
wo->hisaddr = ho->hisaddr;
} else
script_unsetenv("OLDIPREMOTE");
np_up(f->unit, PPP_IP);
ipcp_is_up = 1;
+ notify(ip_up_notifier, 0);
if (ip_up_hook)
ip_up_hook();
/* XXX a bit IPv4-centric here, we only need to get the stats
* before the interface is marked down. */
update_link_stats(f->unit);
+ notify(ip_down_notifier, 0);
if (ip_down_hook)
ip_down_hook();
if (ipcp_is_up) {
case TERMREQ:
if (len > 0 && *p >= ' ' && *p < 0x7f) {
printer(arg, " ");
- print_string(p, len, printer, arg);
+ print_string((char *)p, len, printer, arg);
p += len;
len = 0;
}