*/
#ifndef lint
-static char rcsid[] = "$Id: ipcp.c,v 1.27 1996/07/01 05:32:17 paulus Exp $";
+static char rcsid[] = "$Id: ipcp.c,v 1.35 1998/11/07 06:59:26 paulus Exp $";
#endif
/*
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/tcp.h>
+#include <arpa/inet.h>
#include "pppd.h"
#include "fsm.h"
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[NUM_PPP]; /* # Conf-Reqs received */
static int default_route_set[NUM_PPP]; /* Have set up a default route */
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_script __P((fsm *, char *)); /* Run an up/down script */
static void ipcp_finished __P((fsm *)); /* Don't need lower layer */
fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
"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.
*/
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.
*/
#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.
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.
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;
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 "));
}
if (demand && wo->hisaddr == 0) {
- fprintf(stderr, "%s: remote IP address required for demand-dialling\n",
- progname);
+ option_error("remote IP address required for demand-dialling\n");
exit(1);
}
+#if 0
if (demand && wo->accept_remote) {
- fprintf(stderr, "%s: ipcp-accept-remote is incompatible with demand\n",
- progname);
+ option_error("ipcp-accept-remote is incompatible with demand\n");
exit(1);
}
+#endif
}
if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
return 0;
- if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
- return 0;
if (!sifup(u))
return 0;
+ if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
+ return 0;
if (wo->default_route)
- if (sifdefaultroute(u, wo->hisaddr))
+ if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr))
default_route_set[u] = 1;
if (wo->proxy_arp)
if (sifproxyarp(u, wo->hisaddr))
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 it wants.
*/
if (demand) {
if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
- syslog(LOG_ERR, "Failed to negotiate desired IP addresses");
- ipcp_close(f->unit, "Wrong IP addresses");
- return;
+ 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;
+ }
+
+ /* 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;
+
}
demand_rexmit(PPP_IP);
sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
- }
-#ifndef _linux_ /* Linux destroys routes when the device goes down. */
- else /* always use the code which adds the routes. */
-#endif
- {
+
+ } 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)) {
ipcp_close(f->unit, "Interface configuration failed");
return;
}
+
+#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, ho->hisaddr))
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
default_route_set[f->unit] = 1;
/* Make a proxy ARP entry if requested. */
* Execute the ip-up script, like this:
* /etc/ppp/ip-up interface tty speed local-IP remote-IP
*/
- ipcp_script(f, _PATH_IPUP);
-
+ if (ipcp_script_state == s_down && ipcp_script_pid == 0) {
+ ipcp_script_state = s_up;
+ ipcp_script(_PATH_IPUP);
+ }
}
ipcp_down(f)
fsm *f;
{
- u_int32_t ouraddr, hisaddr;
-
IPCPDEBUG((LOG_INFO, "ipcp: down"));
np_down(f->unit, PPP_IP);
sifvjcomp(f->unit, 0, 0, 0);
*/
if (demand) {
sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE);
-
} else {
- ouraddr = ipcp_gotoptions[f->unit].ouraddr;
- hisaddr = ipcp_hisoptions[f->unit].hisaddr;
- if (proxy_arp_set[f->unit]) {
- cifproxyarp(f->unit, hisaddr);
- proxy_arp_set[f->unit] = 0;
- }
- if (default_route_set[f->unit]) {
- cifdefaultroute(f->unit, hisaddr);
- default_route_set[f->unit] = 0;
- }
sifdown(f->unit);
- cifaddr(f->unit, ouraddr, hisaddr);
+ ipcp_clear_addrs(f->unit);
}
/* Execute the ip-down script */
- ipcp_script(f, _PATH_IPDOWN);
+ 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[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_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(f, script)
- fsm *f;
+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[f->unit].ouraddr));
- strcpy(strremote, ip_ntoa(ipcp_hisoptions[f->unit].hisaddr));
+ strcpy(strlocal, ip_ntoa(ipcp_gotoptions[0].ouraddr));
+ strcpy(strremote, ip_ntoa(ipcp_hisoptions[0].hisaddr));
argv[0] = script;
argv[1] = ifname;
argv[5] = strremote;
argv[6] = ipparam;
argv[7] = NULL;
- run_program(script, argv, 0);
+ ipcp_script_pid = run_program(script, argv, 0, ipcp_script_done, NULL);
}
/*
if (olen == CILEN_ADDRS) {
p += 2;
GETLONG(cilong, p);
- printer(arg, "addrs %s", ip_ntoa(htonl(cilong)));
+ printer(arg, "addrs %I", htonl(cilong));
GETLONG(cilong, p);
- printer(arg, " %s", ip_ntoa(htonl(cilong)));
+ printer(arg, " %I", htonl(cilong));
}
break;
case CI_COMPRESSTYPE:
if (olen == CILEN_ADDR) {
p += 2;
GETLONG(cilong, p);
- printer(arg, "addr %s", ip_ntoa(htonl(cilong)));
+ 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);
* We don't bring the link up for IP fragments or for TCP FIN packets
* with no data.
*/
-#ifndef IP_OFFMASK
+#define IP_HDRLEN 20 /* bytes */
#define IP_OFFMASK 0x1fff
-#endif
+#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;
{
- struct ip *ip;
- struct tcphdr *tcp;
+ u_char *tcp;
int hlen;
- if (len < sizeof(struct ip) + PPP_HDRLEN)
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP_HDRLEN)
return 0;
- ip = (struct ip *) (pkt + PPP_HDRLEN);
- if ((ntohs(ip->ip_off) & IP_OFFMASK) != 0)
+ if ((get_ipoff(pkt) & IP_OFFMASK) != 0)
return 0;
- if (ip->ip_p != IPPROTO_TCP)
+ if (get_ipproto(pkt) != IPPROTO_TCP)
return 1;
- hlen = ip->ip_hl * 4;
- if (len < hlen + sizeof(struct tcphdr) + PPP_HDRLEN)
+ hlen = get_iphl(pkt) * 4;
+ if (len < hlen + TCP_HDRLEN)
return 0;
- tcp = (struct tcphdr *) (pkt + PPP_HDRLEN + hlen);
- if ((tcp->th_flags & TH_FIN) != 0 && hlen + tcp->th_off * 4 == len)
+ tcp = pkt + hlen;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4)
return 0;
return 1;
}