]> git.ozlabs.org Git - ppp.git/commitdiff
Initial revision
authorPaul Mackerras <paulus@samba.org>
Thu, 11 Nov 1993 03:56:47 +0000 (03:56 +0000)
committerPaul Mackerras <paulus@samba.org>
Thu, 11 Nov 1993 03:56:47 +0000 (03:56 +0000)
25 files changed:
pppd/Makefile.bsd [new file with mode: 0644]
pppd/auth.c [new file with mode: 0644]
pppd/chap.c [new file with mode: 0644]
pppd/chap.h [new file with mode: 0644]
pppd/fsm.c [new file with mode: 0644]
pppd/fsm.h [new file with mode: 0644]
pppd/ipcp.c [new file with mode: 0644]
pppd/ipcp.h [new file with mode: 0644]
pppd/lcp.c [new file with mode: 0644]
pppd/lcp.h [new file with mode: 0644]
pppd/magic.c [new file with mode: 0644]
pppd/magic.h [new file with mode: 0644]
pppd/main.c [new file with mode: 0644]
pppd/md5.c [new file with mode: 0644]
pppd/md5.h [new file with mode: 0644]
pppd/options.c [new file with mode: 0644]
pppd/patchlevel.h [new file with mode: 0644]
pppd/pathnames.h [new file with mode: 0644]
pppd/ppp.h [new file with mode: 0644]
pppd/pppd.h [new file with mode: 0644]
pppd/sys-bsd.c [new file with mode: 0644]
pppd/sys-str.c [new file with mode: 0644]
pppd/upap.c [new file with mode: 0644]
pppd/upap.h [new file with mode: 0644]
pppstats/pppstats.c [new file with mode: 0644]

diff --git a/pppd/Makefile.bsd b/pppd/Makefile.bsd
new file mode 100644 (file)
index 0000000..54652bf
--- /dev/null
@@ -0,0 +1,17 @@
+#      $Id: Makefile.bsd,v 1.1 1993/11/11 03:54:25 paulus Exp $
+
+COPTS?= -g
+CFLAGS+= -DDEBUGALL
+
+PROG=  pppd
+SRCS=  main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c \
+       auth.c options.c sys-bsd.c
+MAN8=  pppd.0
+SUBDIR=        pppstats chat
+BINMODE=4555
+BINOWN=        root
+
+LDADD= -lcrypt -lutil
+DPADD= ${LIBCRYPT} ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/pppd/auth.c b/pppd/auth.c
new file mode 100644 (file)
index 0000000..cdeb2b6
--- /dev/null
@@ -0,0 +1,828 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * 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 the Australian National 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.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: auth.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "ppp.h"
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "upap.h"
+#include "chap.h"
+#include "ipcp.h"
+#include "pathnames.h"
+
+#ifdef sparc
+#include <alloca.h>
+#ifndef __GNUC__
+/* why alloca.h doesn't define what alloca() returns is a mystery */
+char *alloca __ARGS((int));
+#endif /*__GNUC__*/
+#endif /*sparc*/
+
+/* Used for storing a sequence of words.  Usually malloced. */
+struct wordlist {
+    struct wordlist    *next;
+    char               word[1];
+};
+
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
+
+#define ISWILD(word)   (word[0] == '*' && word[1] == 0)
+
+#define FALSE  0
+#define TRUE   1
+
+extern char user[];
+extern char passwd[];
+extern char devname[];
+extern char our_name[];
+extern char remote_name[];
+extern char hostname[];
+extern int uselogin;
+extern int usehostname;
+extern int auth_required;
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NPPP];
+static int logged_in;
+static struct wordlist *addresses[NPPP];
+
+/* Bits in auth_pending[] */
+#define UPAP_WITHPEER  1
+#define UPAP_PEER      2
+#define CHAP_WITHPEER  4
+#define CHAP_PEER      8
+
+/* Prototypes */
+void check_access __ARGS((FILE *, char *));
+
+static int  login __ARGS((char *, char *, char **, int *));
+static void logout __ARGS((void));
+static int  get_upap_passwd __ARGS((void));
+static int  have_upap_secret __ARGS((void));
+static int  have_chap_secret __ARGS((char *, char *));
+static int  scan_authfile __ARGS((FILE *, char *, char *, char *,
+                                 struct wordlist **, char *));
+static void free_wordlist __ARGS((struct wordlist *));
+
+extern char *crypt __ARGS((char *, char *));
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ * Do what's necessary to bring the physical layer up.
+ */
+void
+link_required(unit)
+    int unit;
+{
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void
+link_terminated(unit)
+    int unit;
+{
+    if (logged_in)
+       logout();
+    if (lcp_wantoptions[unit].restart) {
+       lcp_lowerdown(unit);
+       lcp_lowerup(unit);
+    } else
+       EXIT(unit);
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(unit)
+    int unit;
+{
+    int auth;
+    lcp_options *wo = &lcp_wantoptions[unit];
+    lcp_options *go = &lcp_gotoptions[unit];
+    lcp_options *ho = &lcp_hisoptions[unit];
+
+    if (auth_required && !(go->neg_chap || go->neg_upap)) {
+       /*
+        * We wanted the peer to authenticate himself, and he refused:
+        * tell him to go away.
+        */
+       syslog(LOG_WARNING, "peer refused to authenticate");
+       lcp_close(unit);
+       return;
+    }
+
+    auth = 0;
+    if (go->neg_chap) {
+       ChapAuthPeer(unit, our_name, go->chap_mdtype);
+       auth |= CHAP_PEER;
+    } else if (go->neg_upap) {
+       upap_authpeer(unit);
+       auth |= UPAP_PEER;
+    }
+    if (ho->neg_chap) {
+       ChapAuthWithPeer(unit, our_name, ho->chap_mdtype);
+       auth |= CHAP_WITHPEER;
+    } else if (ho->neg_upap) {
+       upap_authwithpeer(unit, user, passwd);
+       auth |= UPAP_WITHPEER;
+    }
+    auth_pending[unit] = auth;
+
+    if (!auth)
+       ipcp_open(unit);
+}
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void
+auth_peer_fail(unit, protocol)
+    int unit, protocol;
+{
+    /*
+     * Authentication failure: take the link down
+     */
+    lcp_close(unit);
+}
+
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(unit, protocol)
+    int unit, protocol;
+{
+    int bit;
+
+    switch (protocol) {
+    case CHAP:
+       bit = CHAP_PEER;
+       break;
+    case UPAP:
+       bit = UPAP_PEER;
+       break;
+    default:
+       syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x",
+              protocol);
+       return;
+    }
+
+    /*
+     * If there is no more authentication still to be done,
+     * proceed to the network phase.
+     */
+    if ((auth_pending[unit] &= ~bit) == 0)
+       ipcp_open(unit);
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void
+auth_withpeer_fail(unit, protocol)
+    int unit, protocol;
+{
+    /*
+     * We've failed to authenticate ourselves to our peer.
+     * He'll probably take the link down, and there's not much
+     * we can do except wait for that.
+     */
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(unit, protocol)
+    int unit, protocol;
+{
+    int bit;
+
+    switch (protocol) {
+    case CHAP:
+       bit = CHAP_WITHPEER;
+       break;
+    case UPAP:
+       bit = UPAP_WITHPEER;
+       break;
+    default:
+       syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x",
+              protocol);
+    }
+
+    /*
+     * If there is no more authentication still being done,
+     * proceed to the network phase.
+     */
+    if ((auth_pending[unit] &= ~bit) == 0)
+       ipcp_open(unit);
+}
+
+
+/*
+ * check_auth_options - called to check authentication options.
+ */
+void
+check_auth_options()
+{
+    lcp_options *wo = &lcp_wantoptions[0];
+    lcp_options *ao = &lcp_allowoptions[0];
+
+    /* Default our_name to hostname, and user to our_name */
+    if (our_name[0] == 0 || usehostname)
+       strcpy(our_name, hostname);
+    if (user[0] == 0)
+       strcpy(user, our_name);
+
+    /* If authentication is required, ask peer for CHAP or PAP. */
+    if (auth_required && !wo->neg_chap && !wo->neg_upap) {
+       wo->neg_chap = 1;
+       wo->neg_upap = 1;
+    }
+
+    /*
+     * Check whether we have appropriate secrets to use
+     * to authenticate ourselves and/or the peer.
+     */
+    if (ao->neg_upap && passwd[0] == 0 && !get_upap_passwd())
+       ao->neg_upap = 0;
+    if (wo->neg_upap && !uselogin && !have_upap_secret())
+       wo->neg_upap = 0;
+    if (ao->neg_chap && !have_chap_secret(our_name, remote_name))
+       ao->neg_chap = 0;
+    if (wo->neg_chap && !have_chap_secret(remote_name, our_name))
+       wo->neg_chap = 0;
+
+    if (auth_required && !wo->neg_chap && !wo->neg_upap) {
+       fprintf(stderr, "\
+pppd: peer authentication required but no authentication files accessible\n");
+       exit(1);
+    }
+
+}
+
+
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file.  If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ *     UPAP_AUTHNAK: Authentication failed.
+ *     UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen)
+    int unit;
+    char *auser;
+    int userlen;
+    char *apasswd;
+    int passwdlen;
+    char **msg;
+    int *msglen;
+{
+    int ret;
+    char *filename;
+    FILE *f;
+    struct wordlist *addrs;
+    char passwd[256], user[256];
+    char secret[MAXWORDLEN];
+    static int attempts = 0;
+
+    /*
+     * Make copies of apasswd and auser, then null-terminate them.
+     */
+    BCOPY(apasswd, passwd, passwdlen);
+    passwd[passwdlen] = '\0';
+    BCOPY(auser, user, userlen);
+    user[userlen] = '\0';
+
+    /*
+     * Open the file of upap secrets and scan for a suitable secret
+     * for authenticating this user.
+     */
+    filename = _PATH_UPAPFILE;
+    addrs = NULL;
+    ret = UPAP_AUTHACK;
+    f = fopen(filename, "r");
+    if (f == NULL) {
+       if (!uselogin) {
+           syslog(LOG_ERR, "Can't open upap password file %s: %m", filename);
+           ret = UPAP_AUTHNAK;
+       }
+
+    } else {
+       check_access(f, filename);
+       if (scan_authfile(f, user, our_name, secret, &addrs, filename) < 0
+           || (secret[0] != 0 && strcmp(passwd, secret) != 0
+               && strcmp(crypt(passwd, secret), secret) != 0)) {
+           syslog(LOG_WARNING, "upap authentication failure for %s", user);
+           ret = UPAP_AUTHNAK;
+       }
+       fclose(f);
+    }
+
+    if (uselogin && ret == UPAP_AUTHACK) {
+       ret = login(user, passwd, msg, msglen);
+       if (ret == UPAP_AUTHNAK) {
+           syslog(LOG_WARNING, "upap login failure for %s", user);
+       }
+    }
+
+    if (ret == UPAP_AUTHNAK) {
+       *msg = "Login incorrect";
+       *msglen = strlen(*msg);
+       /*
+        * Frustrate passwd stealer programs.
+        * Allow 10 tries, but start backing off after 3 (stolen from login).
+        * On 10'th, drop the connection.
+        */
+       if (attempts++ >= 10) {
+           syslog(LOG_WARNING, "%d LOGIN FAILURES ON %s, %s",
+                  attempts, devname, user);
+           quit();
+       }
+       if (attempts > 3)
+           sleep((u_int) (attempts - 3) * 5);
+       if (addrs != NULL)
+           free_wordlist(addrs);
+
+    } else {
+       attempts = 0;                   /* Reset count */
+       *msg = "Login ok";
+       *msglen = strlen(*msg);
+       if (addresses[unit] != NULL)
+           free_wordlist(addresses[unit]);
+       addresses[unit] = addrs;
+    }
+
+    return ret;
+}
+
+
+/*
+ * login - Check the user name and password against the system
+ * password database, and login the user if OK.
+ *
+ * returns:
+ *     UPAP_AUTHNAK: Login failed.
+ *     UPAP_AUTHACK: Login succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+static int
+login(user, passwd, msg, msglen)
+    char *user;
+    char *passwd;
+    char **msg;
+    int *msglen;
+{
+    struct passwd *pw;
+    char *epasswd;
+    char *tty;
+
+    if ((pw = getpwnam(user)) == NULL) {
+       return (UPAP_AUTHNAK);
+    }
+
+    /*
+     * XXX If no passwd, let them login without one.
+     */
+    if (pw->pw_passwd == '\0') {
+       return (UPAP_AUTHACK);
+    }
+
+    epasswd = crypt(passwd, pw->pw_passwd);
+    if (strcmp(epasswd, pw->pw_passwd)) {
+       return (UPAP_AUTHNAK);
+    }
+
+    syslog(LOG_INFO, "user %s logged in", user);
+
+    /*
+     * Write a wtmp entry for this user.
+     */
+    tty = strrchr(devname, '/');
+    if (tty == NULL)
+       tty = devname;
+    else
+       tty++;
+    logwtmp(tty, user, "");            /* Add wtmp login entry */
+    logged_in = TRUE;
+
+    return (UPAP_AUTHACK);
+}
+
+/*
+ * logout - Logout the user.
+ */
+static void
+logout()
+{
+    char *tty;
+
+    tty = strrchr(devname, '/');
+    if (tty == NULL)
+       tty = devname;
+    else
+       tty++;
+    logwtmp(tty, "", "");              /* Wipe out wtmp logout entry */
+    logged_in = FALSE;
+}
+
+
+/*
+ * get_upap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP.  Returns 1 on success, 0 if no suitable password
+ * could be found.
+ */
+static int
+get_upap_passwd()
+{
+    char *filename;
+    FILE *f;
+    struct wordlist *addrs;
+    char secret[MAXWORDLEN];
+
+    filename = _PATH_UPAPFILE;
+    addrs = NULL;
+    f = fopen(filename, "r");
+    if (f == NULL)
+       return 0;
+    check_access(f, filename);
+    if (scan_authfile(f, user, remote_name, secret, NULL, filename) < 0)
+       return 0;
+    strncpy(passwd, secret, MAXSECRETLEN);
+    passwd[MAXSECRETLEN-1] = 0;
+    return 1;
+}
+
+
+/*
+ * have_upap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_upap_secret()
+{
+    FILE *f;
+    int ret;
+    char *filename;
+
+    filename = _PATH_UPAPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+       return 0;
+
+    ret = scan_authfile(f, NULL, our_name, NULL, NULL, filename);
+    fclose(f);
+    if (ret < 0)
+       return 0;
+
+    return 1;
+}
+
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'.  Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+int
+have_chap_secret(client, server)
+    char *client;
+    char *server;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+
+    filename = _PATH_CHAPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+       return 0;
+
+    if (client[0] == 0)
+       client = NULL;
+    else if (server[0] == 0)
+       server = NULL;
+
+    ret = scan_authfile(f, client, server, NULL, NULL, filename);
+    fclose(f);
+    if (ret < 0)
+       return 0;
+
+    return 1;
+}
+
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_secret(unit, client, server, secret, secret_len, save_addrs)
+    int unit;
+    char *client;
+    char *server;
+    char *secret;
+    int *secret_len;
+{
+    FILE *f;
+    int ret, len;
+    char *filename;
+    struct wordlist *addrs;
+    char secbuf[MAXWORDLEN];
+
+    filename = _PATH_CHAPFILE;
+    addrs = NULL;
+    secbuf[0] = 0;
+
+    f = fopen(filename, "r");
+    if (f == NULL) {
+       syslog(LOG_ERR, "Can't open chap secret file %s: %m", filename);
+       return 0;
+    }
+    check_access(f, filename);
+
+    ret = scan_authfile(f, client, server, secbuf, &addrs, filename);
+    fclose(f);
+    if (ret < 0)
+       return 0;
+
+    if (save_addrs) {
+       if (addresses[unit] != NULL)
+           free_wordlist(addresses[unit]);
+       addresses[unit] = addrs;
+    }
+
+    len = strlen(secbuf);
+    if (len > MAXSECRETLEN) {
+       syslog(LOG_ERR, "Secret for %s on %s is too long", client, server);
+       len = MAXSECRETLEN;
+    }
+    BCOPY(secbuf, secret, len);
+    *secret_len = len;
+
+    return 1;
+}
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address.  Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(unit, addr)
+    int unit;
+    u_long addr;
+{
+    u_long a;
+    struct hostent *hp;
+    struct wordlist *addrs;
+
+    if ((addrs = addresses[unit]) == NULL)
+       return 1;               /* no restriction */
+
+    for (; addrs != NULL; addrs = addrs->next) {
+       /* "-" means no addresses authorized */
+       if (strcmp(addrs->word, "-") == 0)
+           break;
+       if ((a = inet_addr(addrs->word)) == -1) {
+           if ((hp = gethostbyname(addrs->word)) == NULL) {
+               syslog(LOG_WARNING, "unknown host %s in auth. address list",
+                      addrs->word);
+               continue;
+           } else
+               a = *(u_long *)hp->h_addr;
+       }
+       if (addr == a)
+           return 1;
+    }
+    return 0;                  /* not in list => can't have it */
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+void
+check_access(f, filename)
+    FILE *f;
+    char *filename;
+{
+    struct stat sbuf;
+
+    if (fstat(fileno(f), &sbuf) < 0) {
+       syslog(LOG_WARNING, "cannot stat secret file %s: %m", filename);
+    } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+       syslog(LOG_WARNING, "Warning - secret file %s has world and/or group access", filename);
+    }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'.  The return value is -1
+ * if no secret is found, otherwise >= 0.  The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs.  
+ */
+static int
+scan_authfile(f, client, server, secret, addrs, filename)
+    FILE *f;
+    char *client;
+    char *server;
+    char *secret;
+    struct wordlist **addrs;
+    char *filename;
+{
+    int newline, xxx;
+    int got_flag, best_flag;
+    FILE *sf;
+    struct wordlist *ap, *addr_list, *addr_last;
+    char word[MAXWORDLEN];
+    char atfile[MAXWORDLEN];
+
+    if (addrs != NULL)
+       *addrs = NULL;
+    addr_list = NULL;
+    if (!getword(f, word, &newline, filename))
+       return -1;              /* file is empty??? */
+    newline = 1;
+    best_flag = -1;
+    for (;;) {
+       /*
+        * Skip until we find a word at the start of a line.
+        */
+       while (!newline && getword(f, word, &newline, filename))
+           ;
+       if (!newline)
+           break;              /* got to end of file */
+
+       /*
+        * Got a client - check if it's a match or a wildcard.
+        */
+       got_flag = 0;
+       if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+           newline = 0;
+           continue;
+       }
+       if (!ISWILD(word))
+           got_flag = NONWILD_CLIENT;
+
+       /*
+        * Now get a server and check if it matches.
+        */
+       if (!getword(f, word, &newline, filename))
+           break;
+       if (newline)
+           continue;
+       if (server != NULL && strcmp(word, server) != 0 && !ISWILD(word))
+           continue;
+       if (!ISWILD(word))
+           got_flag |= NONWILD_SERVER;
+
+       /*
+        * Got some sort of a match - see if it's better than what
+        * we have already.
+        */
+       if (got_flag <= best_flag)
+           continue;
+
+       /*
+        * Get the secret.
+        */
+       if (!getword(f, word, &newline, filename))
+           break;
+       if (newline)
+           continue;
+
+       /*
+        * Special syntax: @filename means read secret from file.
+        */
+       if (word[0] == '@') {
+           strcpy(atfile, word+1);
+           if ((sf = fopen(atfile, "r")) == NULL) {
+               syslog(LOG_WARNING, "can't open indirect secret file %s",
+                      atfile);
+               continue;
+           }
+           check_access(sf, atfile);
+           if (!getword(sf, word, &xxx, atfile)) {
+               syslog(LOG_WARNING, "no secret in indirect secret file %s",
+                      atfile);
+               fclose(sf);
+               continue;
+           }
+           fclose(sf);
+       }
+       if (secret != NULL)
+           strcpy(secret, word);
+               
+       best_flag = got_flag;
+
+       /*
+        * Now read address authorization info and make a wordlist.
+        */
+       if (addr_list)
+           free_wordlist(addr_list);
+       addr_list = NULL;
+       for (;;) {
+           if (!getword(f, word, &newline, filename) || newline)
+               break;
+           ap = (struct wordlist *) malloc(sizeof(struct wordlist)
+                                           + strlen(word));
+           if (ap == NULL)
+               novm("authorized addresses");
+           ap->next = NULL;
+           strcpy(ap->word, word);
+           if (addr_list == NULL)
+               addr_list = ap;
+           else
+               addr_last->next = ap;
+           addr_last = ap;
+       }
+       if (!newline)
+           break;
+    }
+
+    if (addrs != NULL)
+       *addrs = addr_list;
+    else if (addr_list != NULL)
+       free_wordlist(addr_list);
+
+    return best_flag;
+}
+
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(wp)
+    struct wordlist *wp;
+{
+    struct wordlist *next;
+
+    while (wp != NULL) {
+       next = wp->next;
+       free(wp);
+       wp = next;
+    }
+}
diff --git a/pppd/chap.c b/pppd/chap.c
new file mode 100644 (file)
index 0000000..e0ea4af
--- /dev/null
@@ -0,0 +1,754 @@
+/*
+ * chap.c - Crytographic Handshake Authentication Protocol.
+ *
+ * Copyright (c) 1991 Gregory M. Christy.
+ * All rights reserved.
+ *
+ * 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 Gregory M. Christy.  The name of the author 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: chap.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "ppp.h"
+#include "pppd.h"
+#include "chap.h"
+#include "md5.h"
+
+chap_state chap[NPPP];         /* CHAP state; one for each unit */
+
+static void ChapChallengeTimeout __ARGS((caddr_t));
+static void ChapResponseTimeout __ARGS((caddr_t));
+static void ChapReceiveChallenge __ARGS((chap_state *, u_char *, int, int));
+static void ChapReceiveResponse __ARGS((chap_state *, u_char *, int, int));
+static void ChapReceiveSuccess __ARGS((chap_state *, u_char *, int, int));
+static void ChapReceiveFailure __ARGS((chap_state *, u_char *, int, int));
+static void ChapSendStatus __ARGS((chap_state *, int));
+static void ChapSendChallenge __ARGS((chap_state *));
+static void ChapSendResponse __ARGS((chap_state *));
+static void ChapGenChallenge __ARGS((chap_state *));
+
+extern double drand48 __ARGS((void));
+extern void srand48 __ARGS((long));
+
+/*
+ * ChapInit - Initialize a CHAP unit.
+ */
+void
+ChapInit(unit)
+    int unit;
+{
+    chap_state *cstate = &chap[unit];
+
+    BZERO(cstate, sizeof(*cstate));
+    cstate->unit = unit;
+    cstate->clientstate = CHAPCS_INITIAL;
+    cstate->serverstate = CHAPSS_INITIAL;
+    cstate->timeouttime = CHAP_DEFTIMEOUT;
+    cstate->max_transmits = CHAP_DEFTRANSMITS;
+    srand48((long) time(NULL));        /* joggle random number generator */
+}
+
+
+/*
+ * ChapAuthWithPeer - Authenticate us with our peer (start client).
+ *
+ */
+void
+ChapAuthWithPeer(unit, our_name, digest)
+    int unit;
+    char *our_name;
+    int digest;
+{
+    chap_state *cstate = &chap[unit];
+
+    cstate->resp_name = our_name;
+    cstate->resp_type = digest;
+
+    if (cstate->clientstate == CHAPCS_INITIAL ||
+       cstate->clientstate == CHAPCS_PENDING) {
+       /* lower layer isn't up - wait until later */
+       cstate->clientstate = CHAPCS_PENDING;
+       return;
+    }
+
+    /*
+     * We get here as a result of LCP coming up.
+     * So even if CHAP was open before, we will 
+     * have to re-authenticate ourselves.
+     */
+    cstate->clientstate = CHAPCS_LISTEN;
+}
+
+
+/*
+ * ChapAuthPeer - Authenticate our peer (start server).
+ */
+void
+ChapAuthPeer(unit, our_name, digest)
+    int unit;
+    char *our_name;
+    int digest;
+{
+    chap_state *cstate = &chap[unit];
+  
+    cstate->chal_name = our_name;
+    cstate->chal_type = digest;
+
+    if (cstate->serverstate == CHAPSS_INITIAL ||
+       cstate->serverstate == CHAPSS_PENDING) {
+       /* lower layer isn't up - wait until later */
+       cstate->serverstate = CHAPSS_PENDING;
+       return;
+    }
+
+    ChapGenChallenge(cstate);
+    ChapSendChallenge(cstate);         /* crank it up dude! */
+    cstate->serverstate = CHAPSS_INITIAL_CHAL;
+}
+
+
+/*
+ * ChapChallengeTimeout - Timeout expired on sending challenge.
+ */
+static void
+ChapChallengeTimeout(arg)
+    caddr_t arg;
+{
+    chap_state *cstate = (chap_state *) arg;
+  
+    /* if we aren't sending challenges, don't worry.  then again we */
+    /* probably shouldn't be here either */
+    if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
+       cstate->serverstate != CHAPSS_RECHALLENGE)
+       return;
+
+    if (cstate->chal_transmits >= cstate->max_transmits) {
+       /* give up on peer */
+       syslog(LOG_ERR, "Peer failed to respond to CHAP challenge");
+       cstate->serverstate = CHAPSS_BADAUTH;
+       auth_peer_fail(cstate->unit, CHAP);
+       return;
+    }
+
+    ChapSendChallenge(cstate);         /* Re-send challenge */
+}
+
+
+/*
+ * ChapResponseTimeout - Timeout expired on sending response.
+ */
+static void
+ChapResponseTimeout(arg)
+    caddr_t arg;
+{
+    chap_state *cstate = (chap_state *) arg;
+
+    /* if we aren't sending a response, don't worry. */
+    if (cstate->clientstate != CHAPCS_RESPONSE)
+       return;
+
+    ChapSendResponse(cstate);          /* re-send response */
+}
+
+
+/*
+ * ChapRechallenge - Time to challenge the peer again.
+ */
+static void
+ChapRechallenge(arg)
+    caddr_t arg;
+{
+    chap_state *cstate = (chap_state *) arg;
+
+    /* if we aren't sending a response, don't worry. */
+    if (cstate->serverstate != CHAPSS_OPEN)
+       return;
+
+    ChapGenChallenge(cstate);
+    ChapSendChallenge(cstate);
+    cstate->serverstate = CHAPSS_RECHALLENGE;
+
+    if (cstate->chal_interval != 0)
+       TIMEOUT(ChapRechallenge, (caddr_t) cstate, cstate->chal_interval);
+}
+
+
+/*
+ * ChapLowerUp - The lower layer is up.
+ *
+ * Start up if we have pending requests.
+ */
+void
+ChapLowerUp(unit)
+    int unit;
+{
+    chap_state *cstate = &chap[unit];
+  
+    if (cstate->clientstate == CHAPCS_INITIAL)
+       cstate->clientstate = CHAPCS_CLOSED;
+    else if (cstate->clientstate == CHAPCS_PENDING)
+       cstate->clientstate = CHAPCS_LISTEN;
+
+    if (cstate->serverstate == CHAPSS_INITIAL)
+       cstate->serverstate = CHAPSS_CLOSED;
+    else if (cstate->serverstate == CHAPSS_PENDING) {
+       ChapGenChallenge(cstate);
+       ChapSendChallenge(cstate);
+       cstate->serverstate = CHAPSS_INITIAL_CHAL;
+    }
+}
+
+
+/*
+ * ChapLowerDown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+void
+ChapLowerDown(unit)
+    int unit;
+{
+    chap_state *cstate = &chap[unit];
+  
+    /* Timeout(s) pending?  Cancel if so. */
+    if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
+       cstate->serverstate == CHAPSS_RECHALLENGE)
+       UNTIMEOUT(ChapChallengeTimeout, (caddr_t) cstate);
+    else if (cstate->serverstate == CHAPSS_OPEN
+            && cstate->chal_interval != 0)
+       UNTIMEOUT(ChapRechallenge, (caddr_t) cstate);
+    if (cstate->clientstate == CHAPCS_RESPONSE)
+       UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate);
+
+    cstate->clientstate = CHAPCS_INITIAL;
+    cstate->serverstate = CHAPSS_INITIAL;
+}
+
+
+/*
+ * ChapProtocolReject - Peer doesn't grok CHAP.
+ */
+void
+ChapProtocolReject(unit)
+    int unit;
+{
+    chap_state *cstate = &chap[unit];
+
+    if (cstate->serverstate != CHAPSS_INITIAL &&
+       cstate->serverstate != CHAPSS_CLOSED)
+       auth_peer_fail(unit, CHAP);
+    if (cstate->clientstate != CHAPCS_INITIAL &&
+       cstate->clientstate != CHAPCS_CLOSED)
+       auth_withpeer_fail(unit, CHAP);
+    ChapLowerDown(unit);               /* shutdown chap */
+}
+
+
+/*
+ * ChapInput - Input CHAP packet.
+ */
+void
+ChapInput(unit, inpacket, packet_len)
+    int unit;
+    u_char *inpacket;
+    int packet_len;
+{
+    chap_state *cstate = &chap[unit];
+    u_char *inp;
+    u_char code, id;
+    int len;
+  
+    /*
+     * Parse header (code, id and length).
+     * If packet too short, drop it.
+     */
+    inp = inpacket;
+    if (packet_len < CHAP_HEADERLEN) {
+       CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header."));
+       return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    GETSHORT(len, inp);
+    if (len < CHAP_HEADERLEN) {
+       CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length."));
+       return;
+    }
+    if (len > packet_len) {
+       CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet."));
+       return;
+    }
+    len -= CHAP_HEADERLEN;
+  
+    /*
+     * Action depends on code (as in fact it usually does :-).
+     */
+    switch (code) {
+    case CHAP_CHALLENGE:
+       ChapReceiveChallenge(cstate, inp, id, len);
+       break;
+    
+    case CHAP_RESPONSE:
+       ChapReceiveResponse(cstate, inp, id, len);
+       break;
+    
+    case CHAP_FAILURE:
+       ChapReceiveFailure(cstate, inp, id, len);
+       break;
+
+    case CHAP_SUCCESS:
+       ChapReceiveSuccess(cstate, inp, id, len);
+       break;
+
+    default:                           /* Need code reject? */
+       syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code);
+       break;
+    }
+}
+
+
+/*
+ * ChapReceiveChallenge - Receive Challenge and send Response.
+ */
+static void
+ChapReceiveChallenge(cstate, inp, id, len)
+    chap_state *cstate;
+    u_char *inp;
+    int id;
+    int len;
+{
+    int rchallenge_len;
+    u_char *rchallenge;
+    int secret_len;
+    char secret[MAXSECRETLEN];
+    char rhostname[256];
+    MD5_CTX mdContext;
+    CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id));
+    if (cstate->clientstate == CHAPCS_CLOSED ||
+       cstate->clientstate == CHAPCS_PENDING) {
+       CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d",
+                  cstate->clientstate));
+       return;
+    }
+
+    if (len < 2) {
+       CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
+       return;
+    }
+
+    GETCHAR(rchallenge_len, inp);
+    len -= sizeof (u_char) + rchallenge_len;   /* now name field length */
+    if (len < 0) {
+       CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
+       return;
+    }
+    rchallenge = inp;
+    INCPTR(rchallenge_len, inp);
+
+    if (len >= sizeof(rhostname))
+       len = sizeof(rhostname) - 1;
+    BCOPY(inp, rhostname, len);
+    rhostname[len] = '\000';
+
+    CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field: %s",
+              rhostname));
+
+    /* get secret for authenticating ourselves with the specified host */
+    if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
+                   secret, &secret_len, 0)) {
+       secret_len = 0;         /* assume null secret if can't find one */
+       syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s",
+              rhostname);
+    }
+
+    /* cancel response send timeout if necessary */
+    if (cstate->clientstate == CHAPCS_RESPONSE)
+       UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate);
+
+    cstate->resp_id = id;
+    cstate->resp_transmits = 0;
+
+    /*  generate MD based on negotiated type */
+    switch (cstate->resp_type) { 
+
+    case CHAP_DIGEST_MD5:              /* only MD5 is defined for now */
+       MD5Init(&mdContext);
+       MD5Update(&mdContext, &cstate->resp_id, 1);
+       MD5Update(&mdContext, secret, secret_len);
+       MD5Update(&mdContext, rchallenge, rchallenge_len);
+       MD5Final(&mdContext);
+       BCOPY(mdContext.digest, cstate->response, MD5_SIGNATURE_SIZE);
+       cstate->resp_length = MD5_SIGNATURE_SIZE;
+       break;
+
+    default:
+       CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type));
+       return;
+    }
+
+    ChapSendResponse(cstate);
+}
+
+
+/*
+ * ChapReceiveResponse - Receive and process response.
+ */
+static void
+ChapReceiveResponse(cstate, inp, id, len)
+    chap_state *cstate;
+    u_char *inp;
+    int id;
+    int len;
+{
+    u_char *remmd, remmd_len;
+    int secret_len, old_state;
+    int code;
+    char rhostname[256];
+    u_char buf[256];
+    MD5_CTX mdContext;
+    u_char msg[256];
+    char secret[MAXSECRETLEN];
+
+    CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id));
+
+    if (cstate->serverstate == CHAPSS_CLOSED ||
+       cstate->serverstate == CHAPSS_PENDING) {
+       CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d",
+                  cstate->serverstate));
+       return;
+    }
+
+    if (id != cstate->chal_id)
+       return;                 /* doesn't match ID of last challenge */
+
+    /*
+     * If we have received a duplicate or bogus Response,
+     * we have to send the same answer (Success/Failure)
+     * as we did for the first Response we saw.
+     */
+    if (cstate->serverstate == CHAPSS_OPEN) {
+       ChapSendStatus(cstate, CHAP_SUCCESS);
+       return;
+    }
+    if (cstate->serverstate == CHAPSS_BADAUTH) {
+       ChapSendStatus(cstate, CHAP_FAILURE);
+       return;
+    }
+
+    if (len < 2) {
+       CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
+       return;
+    }
+    GETCHAR(remmd_len, inp);           /* get length of MD */
+    remmd = inp;                       /* get pointer to MD */
+    INCPTR(remmd_len, inp);
+
+    len -= sizeof (u_char) + remmd_len;
+    if (len < 0) {
+       CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
+       return;
+    }
+
+    UNTIMEOUT(ChapChallengeTimeout, (caddr_t) cstate);
+
+    if (len >= sizeof(rhostname))
+       len = sizeof(rhostname) - 1;
+    BCOPY(inp, rhostname, len);
+    rhostname[len] = '\000';
+
+    CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s",
+              rhostname));
+
+    /*
+     * Get secret for authenticating them with us,
+     * do the hash ourselves, and compare the result.
+     */
+    code = CHAP_FAILURE;
+    if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
+                  secret, &secret_len, 1)) {
+       syslog(LOG_WARNING, "No CHAP secret found for authenticating %s",
+              rhostname);
+    } else {
+
+       /*  generate MD based on negotiated type */
+       switch (cstate->chal_type) { 
+
+       case CHAP_DIGEST_MD5:           /* only MD5 is defined for now */
+           if (remmd_len != MD5_SIGNATURE_SIZE)
+               break;                  /* it's not even the right length */
+           MD5Init(&mdContext);
+           MD5Update(&mdContext, &cstate->chal_id, 1);
+           MD5Update(&mdContext, secret, secret_len);
+           MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+           MD5Final(&mdContext); 
+
+           /* compare local and remote MDs and send the appropriate status */
+           if (bcmp (mdContext.digest, remmd, MD5_SIGNATURE_SIZE) == 0)
+               code = CHAP_SUCCESS;    /* they are the same! */
+           break;
+
+       default:
+           CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type));
+       }
+    }
+
+    ChapSendStatus(cstate, code);
+
+    if (code == CHAP_SUCCESS) {
+       old_state = cstate->serverstate;
+       cstate->serverstate = CHAPSS_OPEN;
+       if (old_state == CHAPSS_INITIAL_CHAL) {
+           auth_peer_success(cstate->unit, CHAP);
+       }
+       if (cstate->chal_interval != 0)
+           TIMEOUT(ChapRechallenge, (caddr_t) cstate, cstate->chal_interval);
+
+    } else {
+       syslog(LOG_ERR, "CHAP peer authentication failed");
+       cstate->serverstate = CHAPSS_BADAUTH;
+       auth_peer_fail(cstate->unit, CHAP);
+    }
+}
+
+/*
+ * ChapReceiveSuccess - Receive Success
+ */
+static void
+ChapReceiveSuccess(cstate, inp, id, len)
+    chap_state *cstate;
+    u_char *inp;
+    u_char id;
+    int len;
+{
+
+    CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id));
+
+    if (cstate->clientstate == CHAPCS_OPEN)
+       /* presumably an answer to a duplicate response */
+       return;
+
+    if (cstate->clientstate != CHAPCS_RESPONSE) {
+       /* don't know what this is */
+       CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n",
+                  cstate->clientstate));
+       return;
+    }
+
+    /*
+     * Print message.
+     */
+    if (len > 0)
+       PRINTMSG(inp, len);
+
+    cstate->clientstate = CHAPCS_OPEN;
+
+    auth_withpeer_success(cstate->unit, CHAP);
+}
+
+
+/*
+ * ChapReceiveFailure - Receive failure.
+ */
+static void
+ChapReceiveFailure(cstate, inp, id, len)
+    chap_state *cstate;
+    u_char *inp;
+    u_char id;
+    int len;
+{
+    u_char msglen;
+    u_char *msg;
+  
+    CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id));
+
+    if (cstate->clientstate != CHAPCS_RESPONSE) {
+       /* don't know what this is */
+       CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n",
+                  cstate->clientstate));
+       return;
+    }
+
+    /*
+     * Print message.
+     */
+    if (len > 0)
+       PRINTMSG(inp, len);
+
+    syslog(LOG_ERR, "CHAP authentication failed");
+    auth_withpeer_fail(cstate->unit, CHAP);
+}
+
+
+/*
+ * ChapSendChallenge - Send an Authenticate challenge.
+ */
+static void
+ChapSendChallenge(cstate)
+    chap_state *cstate;
+{
+    u_char *outp;
+    int chal_len, name_len;
+    int outlen;
+
+    chal_len = cstate->chal_len;
+    name_len = strlen(cstate->chal_name);
+    outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
+    outp = outpacket_buf;
+
+    MAKEHEADER(outp, CHAP);            /* paste in a CHAP header */
+
+    PUTCHAR(CHAP_CHALLENGE, outp);
+    PUTCHAR(cstate->chal_id, outp);
+    PUTSHORT(outlen, outp);
+
+    PUTCHAR(chal_len, outp);           /* put length of challenge */
+    BCOPY(cstate->challenge, outp, chal_len);
+    INCPTR(chal_len, outp);
+
+    BCOPY(cstate->chal_name, outp, name_len);  /* append hostname */
+
+    output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN);
+  
+    CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id));
+
+    TIMEOUT(ChapChallengeTimeout, (caddr_t) cstate, cstate->timeouttime);
+    ++cstate->chal_transmits;
+}
+
+
+/*
+ * ChapSendStatus - Send a status response (ack or nak).
+ */
+static void
+ChapSendStatus(cstate, code)
+    chap_state *cstate;
+    int code;
+{
+    u_char *outp;
+    int outlen, msglen;
+    char msg[256];
+
+    if (code == CHAP_SUCCESS)
+       sprintf(msg, "Welcome to %s.", hostname);
+    else
+       sprintf(msg, "I don't like you.  Go 'way.");
+    msglen = strlen(msg);
+
+    outlen = CHAP_HEADERLEN + msglen;
+    outp = outpacket_buf;
+
+    MAKEHEADER(outp, CHAP);    /* paste in a header */
+  
+    PUTCHAR(code, outp);
+    PUTCHAR(cstate->chal_id, outp);
+    PUTSHORT(outlen, outp);
+    BCOPY(msg, outp, msglen);
+    output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN);
+  
+    CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code,
+              cstate->chal_id));
+}
+
+/*
+ * ChapGenChallenge is used to generate a pseudo-random challenge string of
+ * a pseudo-random length between min_len and max_len.  The challenge
+ * string and its length are stored in *cstate, and various other fields of
+ * *cstate are initialized.
+ */
+
+static void
+ChapGenChallenge(cstate)
+    chap_state *cstate;
+{
+    int chal_len;
+    u_char *ptr = cstate->challenge;
+    unsigned int i;
+
+    /* pick a random challenge length between MIN_CHALLENGE_LENGTH and 
+       MAX_CHALLENGE_LENGTH */  
+    chal_len =  (unsigned) ((drand48() *
+                            (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+                           MIN_CHALLENGE_LENGTH);
+    cstate->chal_len = chal_len;
+    cstate->chal_id = ++cstate->id;
+    cstate->chal_transmits = 0;
+
+    /* generate a random string */
+    for (i = 0; i < chal_len; i++ )
+       *ptr++ = (char) (drand48() * 0xff);
+}
+
+/*
+ * ChapSendResponse - send a response packet with values as specified
+ * in *cstate.
+ */
+/* ARGSUSED */
+static void
+ChapSendResponse(cstate)
+    chap_state *cstate;
+{
+    u_char *outp;
+    int outlen, md_len, name_len;
+
+    md_len = cstate->resp_length;
+    name_len = strlen(cstate->resp_name);
+    outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
+    outp = outpacket_buf;
+
+    MAKEHEADER(outp, CHAP);
+
+    PUTCHAR(CHAP_RESPONSE, outp);      /* we are a response */
+    PUTCHAR(cstate->resp_id, outp);    /* copy id from challenge packet */
+    PUTSHORT(outlen, outp);            /* packet length */
+
+    PUTCHAR(md_len, outp);             /* length of MD */
+    BCOPY(cstate->response, outp, md_len);     /* copy MD to buffer */
+    INCPTR(md_len, outp);
+
+    BCOPY(cstate->resp_name, outp, name_len); /* append our name */
+
+    /* send the packet */
+    output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN);
+
+    cstate->clientstate = CHAPCS_RESPONSE;
+    TIMEOUT(ChapResponseTimeout, (caddr_t) cstate, cstate->timeouttime);
+    ++cstate->resp_transmits;
+}
+
+#ifdef NO_DRAND48
+
+double drand48()
+{
+  return (double)random() / (double)0x7fffffffL; /* 2**31-1 */
+}
+
+void srand48(seedval)
+long seedval;
+{
+  srand((int)seedval);
+}
+
+#endif
diff --git a/pppd/chap.h b/pppd/chap.h
new file mode 100644 (file)
index 0000000..e91d4fb
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * chap.h - Cryptographic Handshake Authentication Protocol definitions.
+ *          based on November 1991 draft of PPP Authentication RFC
+ *
+ * Copyright (c) 1991 Gregory M. Christy
+ * All rights reserved.
+ *
+ * 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 the author.
+ *
+ * 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.
+ *
+ * $Id: chap.h,v 1.1 1993/11/11 03:54:25 paulus Exp $
+ */
+
+#ifndef __CHAP_INCLUDE__
+
+/* Code + ID + length */
+#define CHAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+/*
+ * CHAP codes.
+ */
+
+#define CHAP_DIGEST_MD5                5       /* use MD5 algorithm */
+#define MD5_SIGNATURE_SIZE     16      /* 16 bytes in a MD5 message digest */
+
+#define CHAP_CHALLENGE         1
+#define CHAP_RESPONSE          2
+#define CHAP_SUCCESS           3
+#define CHAP_FAILURE           4
+
+/*
+ *  Challenge lengths (for challenges we send) and other limits.
+ */
+#define MIN_CHALLENGE_LENGTH   32
+#define MAX_CHALLENGE_LENGTH   64
+#define MAX_RESPONSE_LENGTH    16      /* sufficient for MD5 */
+
+/*
+ * Each interface is described by a chap structure.
+ */
+
+typedef struct chap_state {
+    int unit;                  /* Interface unit number */
+    int clientstate;           /* Client state */
+    int serverstate;           /* Server state */
+    u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
+    u_char chal_len;           /* challenge length */
+    u_char chal_id;            /* ID of last challenge */
+    u_char chal_type;          /* hash algorithm for challenges */
+    u_char id;                 /* Current id */
+    char *chal_name;           /* Our name to use with challenge */
+    int chal_interval;         /* Time until we challenge peer again */
+    int timeouttime;           /* Timeout time in seconds */
+    int max_transmits;         /* Maximum # of challenge transmissions */
+    int chal_transmits;                /* Number of transmissions of challenge */
+    int resp_transmits;                /* Number of transmissions of response */
+    u_char response[MAX_RESPONSE_LENGTH];      /* Response to send */
+    u_char resp_length;                /* length of response */
+    u_char resp_id;            /* ID for response messages */
+    u_char resp_type;          /* hash algorithm for responses */
+    char *resp_name;           /* Our name to send with response */
+} chap_state;
+
+
+/*
+ * Client (peer) states.
+ */
+#define CHAPCS_INITIAL         0       /* Lower layer down, not opened */
+#define CHAPCS_CLOSED          1       /* Lower layer up, not opened */
+#define CHAPCS_PENDING         2       /* Auth us to peer when lower up */
+#define CHAPCS_LISTEN          3       /* Listening for a challenge */
+#define CHAPCS_RESPONSE                4       /* Sent response, waiting for status */
+#define CHAPCS_OPEN            5       /* We've received Success */
+
+/*
+ * Server (authenticator) states.
+ */
+#define CHAPSS_INITIAL         0       /* Lower layer down, not opened */
+#define CHAPSS_CLOSED          1       /* Lower layer up, not opened */
+#define CHAPSS_PENDING         2       /* Auth peer when lower up */
+#define CHAPSS_INITIAL_CHAL    3       /* We've sent the first challenge */
+#define CHAPSS_OPEN            4       /* We've sent a Success msg */
+#define CHAPSS_RECHALLENGE     5       /* We've sent another challenge */
+#define CHAPSS_BADAUTH         6       /* We've sent a Failure msg */
+
+/*
+ * Timeouts.
+ */
+#define CHAP_DEFTIMEOUT                3       /* Timeout time in seconds */
+#define CHAP_DEFTRANSMITS      10      /* max # times to send challenge */
+
+extern chap_state chap[];
+
+void ChapInit __ARGS((int));
+void ChapAuthWithPeer __ARGS((int, char *, int));
+void ChapAuthPeer __ARGS((int, char *, int));
+void ChapLowerUp __ARGS((int));
+void ChapLowerDown __ARGS((int));
+void ChapInput __ARGS((int, u_char *, int));
+void ChapProtocolReject __ARGS((int));
+
+#define __CHAP_INCLUDE__
+#endif /* __CHAP_INCLUDE__ */
diff --git a/pppd/fsm.c b/pppd/fsm.c
new file mode 100644 (file)
index 0000000..23d6773
--- /dev/null
@@ -0,0 +1,769 @@
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: fsm.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+/*#include <malloc.h>*/
+#include <syslog.h>
+
+#include "ppp.h"
+#include "pppd.h"
+#include "fsm.h"
+
+extern char *proto_name();
+
+static void fsm_timeout __ARGS((caddr_t));
+static void fsm_rconfreq __ARGS((fsm *, int, u_char *, int));
+static void fsm_rconfack __ARGS((fsm *, int, u_char *, int));
+static void fsm_rconfnakrej __ARGS((fsm *, int, int, u_char *, int));
+static void fsm_rtermreq __ARGS((fsm *, int));
+static void fsm_rtermack __ARGS((fsm *));
+static void fsm_rcoderej __ARGS((fsm *, u_char *, int));
+static void fsm_sconfreq __ARGS((fsm *, int));
+
+#define PROTO_NAME(f)  ((f)->callbacks->proto_name)
+
+int peer_mru[NPPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(f)
+    fsm *f;
+{
+    f->state = INITIAL;
+    f->flags = 0;
+    f->id = 0;                         /* XXX Start with random id? */
+    f->timeouttime = DEFTIMEOUT;
+    f->maxconfreqtransmits = DEFMAXCONFREQS;
+    f->maxtermtransmits = DEFMAXTERMREQS;
+    f->maxnakloops = DEFMAXNAKLOOPS;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case INITIAL:
+       f->state = CLOSED;
+       break;
+
+    case STARTING:
+       if( f->flags & OPT_SILENT )
+           f->state = STOPPED;
+       else {
+           /* Send an initial configure-request */
+           fsm_sconfreq(f, 0);
+           f->state = REQSENT;
+       }
+       break;
+
+    default:
+       FSMDEBUG((LOG_INFO, "%s: Up event in state %d!",
+                 PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case CLOSED:
+       f->state = INITIAL;
+       break;
+
+    case STOPPED:
+       f->state = STARTING;
+       if( f->callbacks->starting )
+           (*f->callbacks->starting)(f);
+       break;
+
+    case CLOSING:
+       f->state = INITIAL;
+       UNTIMEOUT(fsm_timeout, (caddr_t) f);    /* Cancel timeout */
+       break;
+
+    case STOPPING:
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+       f->state = STARTING;
+       UNTIMEOUT(fsm_timeout, (caddr_t) f);    /* Cancel timeout */
+       break;
+
+    case OPENED:
+       if( f->callbacks->down )
+           (*f->callbacks->down)(f);
+       f->state = STARTING;
+       break;
+
+    default:
+       FSMDEBUG((LOG_INFO, "%s: Down event in state %d!",
+                 PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case INITIAL:
+       f->state = STARTING;
+       if( f->callbacks->starting )
+           (*f->callbacks->starting)(f);
+       break;
+
+    case CLOSED:
+       if( f->flags & OPT_SILENT )
+           f->state = STOPPED;
+       else {
+           /* Send an initial configure-request */
+           fsm_sconfreq(f, 0);
+           f->state = REQSENT;
+       }
+       break;
+
+    case CLOSING:
+       f->state = STOPPING;
+       /* fall through */
+    case STOPPED:
+    case OPENED:
+       if( f->flags & OPT_RESTART ){
+           fsm_lowerdown(f);
+           fsm_lowerup(f);
+       }
+       break;
+    }
+}
+
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the CLOSED state.
+ */
+void
+fsm_close(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case STARTING:
+       f->state = INITIAL;
+       break;
+    case STOPPED:
+       f->state = CLOSED;
+       break;
+    case STOPPING:
+       f->state = CLOSING;
+       break;
+
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+    case OPENED:
+       if( f->state != OPENED )
+           UNTIMEOUT(fsm_timeout, (caddr_t) f);        /* Cancel timeout */
+       else if( f->callbacks->down )
+           (*f->callbacks->down)(f);   /* Inform upper layers we're down */
+
+       /* Init restart counter, send Terminate-Request */
+       f->retransmits = f->maxtermtransmits;
+       fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0);
+       TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime);
+       --f->retransmits;
+
+       f->state = CLOSING;
+       break;
+    }
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(arg)
+    caddr_t arg;
+{
+    fsm *f = (fsm *) arg;
+
+    switch (f->state) {
+    case CLOSING:
+    case STOPPING:
+       if( f->retransmits <= 0 ){
+           /*
+            * We've waited for an ack long enough.  Peer probably heard us.
+            */
+           f->state = (f->state == CLOSING)? CLOSED: STOPPED;
+           if( f->callbacks->finished )
+               (*f->callbacks->finished)(f);
+       } else {
+           /* Send Terminate-Request */
+           fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0);
+           TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime);
+           --f->retransmits;
+       }
+       break;
+
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+       if (f->retransmits <= 0) {
+           syslog(LOG_WARNING, "%s: timeout sending Config-Requests",
+                  PROTO_NAME(f));
+           f->state = STOPPED;
+           if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
+               (*f->callbacks->finished)(f);
+
+       } else {
+           /* Retransmit the configure-request */
+           if (f->callbacks->retransmit)
+               (*f->callbacks->retransmit)(f);
+           fsm_sconfreq(f, 1);         /* Re-send Configure-Request */
+           if( f->state == ACKRCVD )
+               f->state = REQSENT;
+       }
+       break;
+
+    default:
+       FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d!",
+                 PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void
+fsm_input(f, inpacket, l)
+    fsm *f;
+    u_char *inpacket;
+    int l;
+{
+    u_char *inp, *outp;
+    u_char code, id;
+    int len;
+
+    /*
+     * Parse header (code, id and length).
+     * If packet too short, drop it.
+     */
+    inp = inpacket;
+    if (l < HEADERLEN) {
+       FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.",
+                 f->protocol));
+       return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    GETSHORT(len, inp);
+    if (len < HEADERLEN) {
+       FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.",
+                 f->protocol));
+       return;
+    }
+    if (len > l) {
+       FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.",
+                 f->protocol));
+       return;
+    }
+    len -= HEADERLEN;          /* subtract header length */
+
+    if( f->state == INITIAL || f->state == STARTING ){
+       FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d.",
+                 f->protocol, f->state));
+       return;
+    }
+
+    /*
+     * Action depends on code.
+     */
+    switch (code) {
+    case CONFREQ:
+       fsm_rconfreq(f, id, inp, len);
+       break;
+    
+    case CONFACK:
+       fsm_rconfack(f, id, inp, len);
+       break;
+    
+    case CONFNAK:
+    case CONFREJ:
+       fsm_rconfnakrej(f, code, id, inp, len);
+       break;
+    
+    case TERMREQ:
+       fsm_rtermreq(f, id);
+       break;
+    
+    case TERMACK:
+       fsm_rtermack(f);
+       break;
+    
+    case CODEREJ:
+       fsm_rcoderej(f, inp, len);
+       break;
+    
+    default:
+       if( !f->callbacks->extcode
+          || !(*f->callbacks->extcode)(f, code, id, inp, len) )
+           fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+       break;
+    }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(f, id, inp, len)
+    fsm *f;
+    u_char id;
+    u_char *inp;
+    int len;
+{
+    u_char *outp;
+    int code, reject_if_disagree;
+
+    FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f), id));
+    switch( f->state ){
+    case CLOSED:
+       /* Go away, we're closed */
+       fsm_sdata(f, TERMACK, id, NULL, 0);
+       return;
+    case CLOSING:
+    case STOPPING:
+       return;
+
+    case OPENED:
+       /* Go down and restart negotiation */
+       if( f->callbacks->down )
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
+       break;
+
+    case STOPPED:
+       /* Negotiation started by our peer */
+       fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
+       f->state = REQSENT;
+       break;
+    }
+
+    /*
+     * Pass the requested configuration options
+     * to protocol-specific code for checking.
+     */
+    if (f->callbacks->reqci){          /* Check CI */
+       reject_if_disagree = (f->nakloops >= f->maxnakloops);
+       code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+    } else if (len)
+       code = CONFREJ;                 /* Reject all CI */
+
+    /* send the Ack, Nak or Rej to the peer */
+    fsm_sdata(f, code, id, inp, len);
+
+    if (code == CONFACK) {
+       if (f->state == ACKRCVD) {
+           UNTIMEOUT(fsm_timeout, (caddr_t) f);        /* Cancel timeout */
+           f->state = OPENED;
+           if (f->callbacks->up)
+               (*f->callbacks->up)(f); /* Inform upper layers */
+       } else
+           f->state = ACKSENT;
+       f->nakloops = 0;
+
+    } else {
+       /* we sent CONFACK or CONFREJ */
+       if (f->state != ACKRCVD)
+           f->state = REQSENT;
+       if( code == CONFNAK )
+           ++f->nakloops;
+    }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(f, id, inp, len)
+    fsm *f;
+    int id;
+    u_char *inp;
+    int len;
+{
+    FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d.",
+             PROTO_NAME(f), id));
+
+    if (id != f->reqid)                /* Expected id? */
+       return;                 /* Nope, toss... */
+    if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ){
+       /* Ack is bad - ignore it */
+       FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)",
+                 PROTO_NAME(f), len));
+       return;
+    }
+
+    switch (f->state) {
+    case CLOSED:
+    case STOPPED:
+       fsm_sdata(f, TERMACK, id, NULL, 0);
+       break;
+
+    case REQSENT:
+       f->state = ACKRCVD;
+       f->retransmits = f->maxconfreqtransmits;
+       break;
+
+    case ACKRCVD:
+       /* Huh? an extra Ack? oh well... */
+       fsm_sconfreq(f, 0);
+       f->state = REQSENT;
+       break;
+
+    case ACKSENT:
+       UNTIMEOUT(fsm_timeout, (caddr_t) f);    /* Cancel timeout */
+       f->state = OPENED;
+       f->retransmits = f->maxconfreqtransmits;
+       if (f->callbacks->up)
+           (*f->callbacks->up)(f);     /* Inform upper layers */
+       break;
+
+    case OPENED:
+       /* Go down and restart negotiation */
+       if (f->callbacks->down)
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
+       f->state = REQSENT;
+       break;
+    }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(f, code, id, inp, len)
+    fsm *f;
+    int code, id;
+    u_char *inp;
+    int len;
+{
+    int (*proc)();
+
+    FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d.",
+             PROTO_NAME(f), id));
+
+    if (id != f->reqid)                /* Expected id? */
+       return;                 /* Nope, toss... */
+    proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
+    if( !proc || !proc(f, inp, len) ){
+       /* Nak/reject is bad - ignore it */
+       FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)",
+                 PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
+       return;
+    }
+
+    switch (f->state) {
+    case CLOSED:
+    case STOPPED:
+       fsm_sdata(f, TERMACK, id, NULL, 0);
+       break;
+
+    case REQSENT:
+    case ACKSENT:
+       /* They didn't agree to what we wanted - try another request */
+       UNTIMEOUT(fsm_timeout, (caddr_t) f);    /* Cancel timeout */
+       fsm_sconfreq(f, 0);             /* Send Configure-Request */
+       break;
+
+    case ACKRCVD:
+       /* Got a Nak/reject when we had already had an Ack?? oh well... */
+       fsm_sconfreq(f, 0);
+       f->state = REQSENT;
+       break;
+
+    case OPENED:
+       /* Go down and restart negotiation */
+       if (f->callbacks->down)
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
+       f->state = REQSENT;
+       break;
+    }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(f, id)
+    fsm *f;
+    int id;
+{
+    FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d.",
+             PROTO_NAME(f), id));
+
+    fsm_sdata(f, TERMACK, id, NULL, 0);
+    switch (f->state) {
+    case ACKRCVD:
+    case ACKSENT:
+       f->state = REQSENT;             /* Start over but keep trying */
+       break;
+
+    case OPENED:
+       syslog(LOG_INFO, "%s terminated at peer's request", PROTO_NAME(f));
+       if (f->callbacks->down)
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       f->retransmits = 0;
+       f->state = STOPPING;
+       TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime);
+       break;
+    }
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void
+fsm_rtermack(f)
+    fsm *f;
+{
+    FSMDEBUG((LOG_INFO, "fsm_rtermack(%s).", PROTO_NAME(f)));
+
+    switch (f->state) {
+    case CLOSING:
+       f->state = CLOSED;
+       if( f->callbacks->finished )
+           (*f->callbacks->finished)(f);
+       break;
+    case STOPPING:
+       f->state = STOPPED;
+       if( f->callbacks->finished )
+           (*f->callbacks->finished)(f);
+       break;
+
+    case ACKRCVD:
+       f->state = REQSENT;
+       break;
+
+    case OPENED:
+       if (f->callbacks->down)
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       fsm_sconfreq(f, 0);
+       break;
+    }
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void
+fsm_rcoderej(f, inp, len)
+    fsm *f;
+    u_char *inp;
+    int len;
+{
+    u_char code, id;
+
+    FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s).", PROTO_NAME(f)));
+
+    if (len < HEADERLEN) {
+       FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!"));
+       return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    syslog(LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d",
+          PROTO_NAME(f), code, id);
+
+    if( f->state == ACKRCVD )
+       f->state = REQSENT;
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case CLOSING:
+       UNTIMEOUT(fsm_timeout, (caddr_t) f);    /* Cancel timeout */
+       /* fall through */
+    case CLOSED:
+       f->state = CLOSED;
+       if( f->callbacks->finished )
+           (*f->callbacks->finished)(f);
+       break;
+
+    case STOPPING:
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+       UNTIMEOUT(fsm_timeout, (caddr_t) f);    /* Cancel timeout */
+       /* fall through */
+    case STOPPED:
+       f->state = STOPPED;
+       if( f->callbacks->finished )
+           (*f->callbacks->finished)(f);
+       break;
+
+    case OPENED:
+       if( f->callbacks->down )
+           (*f->callbacks->down)(f);
+
+       /* Init restart counter, send Terminate-Request */
+       f->retransmits = f->maxtermtransmits;
+       fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0);
+       TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime);
+       --f->retransmits;
+
+       f->state = STOPPING;
+       break;
+
+    default:
+       FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d!",
+                 PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(f, retransmit)
+    fsm *f;
+    int retransmit;
+{
+    u_char *outp;
+    int outlen, cilen;
+
+    if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
+       /* Not currently negotiating - reset options */
+       if( f->callbacks->resetci )
+           (*f->callbacks->resetci)(f);
+       f->nakloops = 0;
+    }
+
+    if( !retransmit ){
+       /* New request - reset retransmission counter, use new ID */
+       f->retransmits = f->maxconfreqtransmits;
+       f->reqid = ++f->id;
+    }
+
+    /*
+     * Make up the request packet
+     */
+    if( f->callbacks->cilen && f->callbacks->addci ){
+       cilen = (*f->callbacks->cilen)(f);
+       if( cilen > peer_mru[f->unit] - HEADERLEN )
+           cilen = peer_mru[f->unit] - HEADERLEN;
+       outp = outpacket_buf + DLLHEADERLEN + HEADERLEN;
+       if (f->callbacks->addci)
+           (*f->callbacks->addci)(f, outp, &cilen);
+    } else
+       cilen = 0;
+
+    /* send the request to our peer */
+    fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
+
+    /* start the retransmit timer */
+    --f->retransmits;
+    TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime);
+
+    FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d",
+             PROTO_NAME(f), f->reqid));
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata(f, code, id, data, datalen)
+    fsm *f;
+    u_char code, id;
+    u_char *data;
+    int datalen;
+{
+    u_char *outp;
+    int outlen;
+
+    /* Adjust length to be smaller than MTU */
+    outp = outpacket_buf;
+    if (datalen > peer_mru[f->unit] - HEADERLEN)
+       datalen = peer_mru[f->unit] - HEADERLEN;
+    if (datalen && data != outp + DLLHEADERLEN + HEADERLEN)
+       BCOPY(data, outp + DLLHEADERLEN + HEADERLEN, datalen);
+    outlen = datalen + HEADERLEN;
+    MAKEHEADER(outp, f->protocol);
+    PUTCHAR(code, outp);
+    PUTCHAR(id, outp);
+    PUTSHORT(outlen, outp);
+    output(f->unit, outpacket_buf, outlen + DLLHEADERLEN);
+
+    FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d, id %d.",
+             PROTO_NAME(f), code, id));
+}
diff --git a/pppd/fsm.h b/pppd/fsm.h
new file mode 100644 (file)
index 0000000..3ff104a
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * $Id: fsm.h,v 1.1 1993/11/11 03:54:25 paulus Exp $
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define HEADERLEN      (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ *  CP (LCP, IPCP, etc.) codes.
+ */
+#define CONFREQ                1       /* Configuration Request */
+#define CONFACK                2       /* Configuration Ack */
+#define CONFNAK                3       /* Configuration Nak */
+#define CONFREJ                4       /* Configuration Reject */
+#define TERMREQ                5       /* Termination Request */
+#define TERMACK                6       /* Termination Ack */
+#define CODEREJ                7       /* Code Reject */
+#define PROTREJ                8       /* Protocol Reject */
+#define ECHOREQ                9       /* Echo Request */
+#define ECHOREP                10      /* Echo Reply */
+#define DISCREQ                11      /* Discard Request */
+#define KEEPALIVE      12      /* Keepalive */
+
+
+/*
+ * Each FSM is described by a fsm_callbacks and a fsm structure.
+ */
+typedef struct fsm_callbacks {
+    void (*resetci)();         /* Reset our Configuration Information */
+    int  (*cilen)();           /* Length of our Configuration Information */
+    void (*addci)();           /* Add our Configuration Information */
+    int  (*ackci)();           /* ACK our Configuration Information */
+    int  (*nakci)();           /* NAK our Configuration Information */
+    int  (*rejci)();           /* Reject our Configuration Information */
+    int  (*reqci)();           /* Request peer's Configuration Information */
+    void (*up)();              /* Called when fsm reaches OPENED state */
+    void (*down)();            /* Called when fsm leaves OPENED state */
+    void (*starting)();                /* Called when we want the lower layer */
+    void (*finished)();                /* Called when we don't want the lower layer */
+    void (*protreject)();      /* Called when Protocol-Reject received */
+    void (*retransmit)();      /* Retransmission is necessary */
+    int  (*extcode)();         /* Called when unknown code received */
+    char *proto_name;          /* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+typedef struct fsm {
+    int unit;                  /* Interface unit number */
+    int protocol;              /* Data Link Layer Protocol field value */
+    int state;                 /* State */
+    int flags;                 /* Contains option bits */
+    u_char id;                 /* Current id */
+    u_char reqid;              /* Current request id */
+    int timeouttime;           /* Timeout time in milliseconds */
+    int maxconfreqtransmits;   /* Maximum Configure-Request transmissions */
+    int retransmits;           /* Number of retransmissions left */
+    int maxtermtransmits;      /* Maximum Terminate-Request transmissions */
+    int nakloops;              /* Number of nak loops since last ack */
+    int maxnakloops;           /* Maximum number of nak loops tolerated */
+    fsm_callbacks *callbacks;  /* Callback routines */
+} fsm;
+
+
+/*
+ * Link states.
+ */
+#define INITIAL                0       /* Down, hasn't been opened */
+#define STARTING       1       /* Down, been opened */
+#define CLOSED         2       /* Up, hasn't been opened */
+#define STOPPED                3       /* Open, waiting for down event */
+#define CLOSING                4       /* Terminating the connection, not open */
+#define STOPPING       5       /* Terminating, but open */
+#define REQSENT                6       /* We've sent a Config Request */
+#define ACKRCVD                7       /* We've received a Config Ack */
+#define ACKSENT                8       /* We've sent a Config Ack */
+#define OPENED         9       /* Connection available */
+
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE    1       /* Don't die if we don't get a response */
+#define OPT_RESTART    2       /* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT     4       /* Wait for peer to speak first */
+
+
+/*
+ * Timeouts.
+ */
+#define DEFTIMEOUT     3       /* Timeout time in seconds */
+#define DEFMAXTERMREQS 2       /* Maximum Terminate-Request transmissions */
+#define DEFMAXCONFREQS 10      /* Maximum Configure-Request transmissions */
+#define DEFMAXNAKLOOPS 10      /* Maximum number of nak loops */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init __ARGS((fsm *));
+void fsm_lowerup __ARGS((fsm *));
+void fsm_lowerdown __ARGS((fsm *));
+void fsm_open __ARGS((fsm *));
+void fsm_close __ARGS((fsm *));
+void fsm_input __ARGS((fsm *, u_char *, int));
+void fsm_protreject __ARGS((fsm *));
+void fsm_sdata __ARGS((fsm *, int, int, u_char *, int));
+
+
+/*
+ * Variables
+ */
+extern int peer_mru[];         /* currently negotiated peer MRU (per unit) */
diff --git a/pppd/ipcp.c b/pppd/ipcp.c
new file mode 100644 (file)
index 0000000..08befff
--- /dev/null
@@ -0,0 +1,1056 @@
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: ipcp.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+/*
+ * TODO:
+ * Fix IP address negotiation (wantoptions or hisoptions).
+ * Don't set zero IP addresses.
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_ppp.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include <string.h>
+
+#include "pppd.h"
+#include "ppp.h"
+#include "fsm.h"
+#include "ipcp.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 */
+
+/* local vars */
+static int cis_received[NPPP];         /* # Conf-Reqs received */
+
+/*
+ * 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 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 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 */
+    NULL,                      /* Called when Protocol-Reject received */
+    NULL,                      /* Retransmission is necessary */
+    NULL,                      /* Called to handle protocol-specific codes */
+    "IPCP"                     /* String name of protocol */
+};
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID     2
+#define CILEN_COMPRESS 4       /* min length for compression protocol opt. */
+#define CILEN_VJ       6       /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR     6       /* new-style single address option */
+#define CILEN_ADDRS    10      /* old-style dual address option */
+
+
+#define CODENAME(x)    ((x) == CONFACK ? "ACK" : \
+                        (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * Make a string representation of a network IP address.
+ */
+char *
+ip_ntoa(ipaddr)
+u_long ipaddr;
+{
+    static char b[64];
+
+    ipaddr = ntohl(ipaddr);
+
+    sprintf(b, "%d.%d.%d.%d",
+           (u_char)(ipaddr >> 24),
+           (u_char)(ipaddr >> 16),
+           (u_char)(ipaddr >> 8),
+           (u_char)(ipaddr));
+    return b;
+}
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+void
+ipcp_init(unit)
+    int unit;
+{
+    fsm *f = &ipcp_fsm[unit];
+    ipcp_options *wo = &ipcp_wantoptions[unit];
+    ipcp_options *ao = &ipcp_allowoptions[unit];
+
+    f->unit = unit;
+    f->protocol = IPCP;
+    f->callbacks = &ipcp_callbacks;
+    fsm_init(&ipcp_fsm[unit]);
+
+    wo->neg_addr = 1;
+    wo->old_addrs = 0;
+    wo->ouraddr = 0;
+    wo->hisaddr = 0;
+
+    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;
+
+    /* max slots and slot-id compression are currently hardwired in */
+    /* ppp_if.c to 16 and 1, this needs to be changed (among other */
+    /* things) gmc */
+
+    ao->neg_addr = 1;
+    ao->neg_vj = 1;
+    ao->maxslotindex = MAX_STATES - 1;
+    ao->cflag = 1;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+void
+ipcp_open(unit)
+    int unit;
+{
+    fsm_open(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+void
+ipcp_close(unit)
+    int unit;
+{
+    fsm_close(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+void
+ipcp_lowerup(unit)
+    int unit;
+{
+    fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+void
+ipcp_lowerdown(unit)
+    int unit;
+{
+    fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+void
+ipcp_input(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+void
+ipcp_protrej(unit)
+    int unit;
+{
+    fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ */
+static void
+ipcp_resetci(f)
+    fsm *f;
+{
+    ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+    wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+    ipcp_gotoptions[f->unit] = *wo;
+    cis_received[f->unit] = 0;
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ */
+static int
+ipcp_cilen(f)
+    fsm *f;
+{
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+
+#define LENCIVJ(neg, old)      (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg, old)    (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+
+    return (LENCIADDR(go->neg_addr, go->old_addrs) +
+           LENCIVJ(go->neg_vj, go->old_vj));
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipcp_addci(f, ucp, lenp)
+    fsm *f;
+    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) \
+    if (neg) { \
+       int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+       if (len >= vjlen) { \
+           PUTCHAR(opt, ucp); \
+           PUTCHAR(vjlen, ucp); \
+           PUTSHORT(val, ucp); \
+           if (!old) { \
+               PUTCHAR(maxslotindex, ucp); \
+               PUTCHAR(cflag, ucp); \
+           } \
+           len -= vjlen; \
+       } else \
+           neg = 0; \
+    }
+
+#define ADDCIADDR(opt, neg, old, val1, val2) \
+    if (neg) { \
+       int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+       if (len >= addrlen) { \
+           u_long l; \
+           PUTCHAR(opt, ucp); \
+           PUTCHAR(addrlen, ucp); \
+           l = ntohl(val1); \
+           PUTLONG(l, ucp); \
+           if (old) { \
+               l = ntohl(val2); \
+               PUTLONG(l, ucp); \
+           } \
+           len -= addrlen; \
+       } else \
+           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);
+
+    ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+           go->maxslotindex, go->cflag);
+
+    *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ *     0 - Ack was bad.
+ *     1 - Ack was good.
+ */
+static int
+ipcp_ackci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    u_short cilen, citype, cishort;
+    u_long cilong;
+    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 ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+    if (neg) { \
+       int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+       if ((len -= vjlen) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != vjlen || \
+           citype != opt)  \
+           goto bad; \
+       GETSHORT(cishort, p); \
+       if (cishort != val) \
+           goto bad; \
+       if (!old) { \
+           GETCHAR(cimaxslotindex, p); \
+           if (cimaxslotindex != maxslotindex) \
+               goto bad; \
+           GETCHAR(cicflag, p); \
+           if (cicflag != cflag) \
+               goto bad; \
+       } \
+    }
+
+#define ACKCIADDR(opt, neg, old, val1, val2) \
+    if (neg) { \
+       int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+       u_long l; \
+       if ((len -= addrlen) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != addrlen || \
+           citype != opt) \
+           goto bad; \
+       GETLONG(l, p); \
+       cilong = htonl(l); \
+       if (val1 != cilong) \
+           goto bad; \
+       if (old) { \
+           GETLONG(l, p); \
+           cilong = htonl(l); \
+           if (val2 != cilong) \
+               goto bad; \
+       } \
+    }
+
+    ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+             go->old_addrs, go->ouraddr, go->hisaddr);
+
+    ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+           go->maxslotindex, go->cflag);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    return (1);
+
+bad:
+    IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!"));
+    return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the OPENED state.
+ *
+ * Returns:
+ *     0 - Nak was bad.
+ *     1 - Nak was good.
+ */
+static int
+ipcp_nakci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    u_char cimaxslotindex, cicflag;
+    u_char citype, cilen, *next;
+    u_short cishort;
+    u_long ciaddr1, ciaddr2, l;
+    ipcp_options no;           /* options we've seen Naks for */
+    ipcp_options try;          /* options to request next time */
+
+    BZERO(&no, sizeof(no));
+    try = *go;
+
+    /*
+     * 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 NAKCIADDR(opt, neg, old, code) \
+    if (go->neg && \
+       len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
+       p[1] == cilen && \
+       p[0] == opt) { \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETLONG(l, p); \
+       ciaddr1 = htonl(l); \
+       if (old) { \
+           GETLONG(l, p); \
+           ciaddr2 = htonl(l); \
+           no.old_addrs = 1; \
+       } else \
+           ciaddr2 = 0; \
+       no.neg = 1; \
+       code \
+    }
+
+#define NAKCIVJ(opt, neg, code) \
+    if (go->neg && \
+       ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+       len >= cilen && \
+       p[0] == opt) { \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       if (cilen == CILEN_VJ) { \
+           GETCHAR(cimaxslotindex, p); \
+            GETCHAR(cicflag, p); \
+        } \
+       no.neg = 1; \
+        code \
+    }
+
+    /*
+     * Accept the peer's idea of our address if we don't know it.
+     * Accept the peer's idea of his address if he knows it.
+     */
+    NAKCIADDR(CI_ADDR, neg_addr, go->old_addrs,
+             if (!go->ouraddr && ciaddr1) {    /* Do we know our address? */
+                 go->ouraddr = ciaddr1;
+                 IPCPDEBUG((LOG_INFO, "local IP address %s",
+                            ip_ntoa(ciaddr1)));
+             }
+             if (ciaddr2) {                    /* Does he know his? */
+                 go->hisaddr = ciaddr2;
+                 IPCPDEBUG((LOG_INFO, "remote IP address %s",
+                            ip_ntoa(ciaddr2)));
+             }
+             );
+
+    /*
+     * Accept the peer's value of maxslotindex provided that it
+     * is less than what we asked for.  Turn off slot-ID compression
+     * if the peer wants.  Send old-style compress-type option if
+     * the peer wants.
+     */
+    NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+           if (cilen == CILEN_VJ) {
+               if (cishort == IPCP_VJ_COMP) {
+                   try.old_vj = 0;
+                   if (cimaxslotindex < go->maxslotindex)
+                       try.maxslotindex = cimaxslotindex;
+                   if (!cicflag)
+                       try.cflag = 0;
+               } else {
+                   try.neg_vj = 0;
+               }
+           } else {
+               if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+                   try.old_vj = 1;
+                   try.vj_protocol = cishort;
+               } else {
+                   try.neg_vj = 0;
+               }
+           }
+           );
+
+    /*
+     * There may be remaining CIs, if the peer is requesting negotiation
+     * on an option that we didn't include in our request packet.
+     * If they want to negotiate about IP addresses, we comply.
+     * If they want us to ask for compression, we refuse.
+     */
+    while (len > CILEN_VOID) {
+       GETCHAR(citype, p);
+       GETCHAR(cilen, p);
+       if( (len -= cilen) < 0 )
+           goto bad;
+       next = p + cilen - 2;
+
+       switch (citype) {
+       case CI_COMPRESSTYPE:
+           if (go->neg_vj || no.neg_vj ||
+               (cilen != CILEN_VJ && cilen != CILEN_COMPRESS))
+               goto bad;
+           no.neg_vj = 1;
+           break;
+       case CI_ADDRS:
+           if (go->neg_addr && go->old_addrs || no.old_addrs
+               || cilen != CILEN_ADDRS)
+               goto bad;
+           try.neg_addr = 1;
+           try.old_addrs = 1;
+           GETLONG(l, p);
+           ciaddr1 = htonl(l);
+           if (ciaddr1)
+               try.ouraddr = ciaddr1;
+           GETLONG(l, p);
+           ciaddr2 = htonl(l);
+           if (ciaddr2)
+               try.hisaddr = ciaddr2;
+           no.old_addrs = 1;
+           break;
+       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)
+               try.ouraddr = ciaddr1;
+           no.neg_addr = 1;
+           break;
+       default:
+           goto bad;
+       }
+       p = next;
+    }
+
+    /* If there is still anything left, this packet is bad. */
+    if (len != 0)
+       goto bad;
+
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     */
+    if (f->state != OPENED)
+       *go = try;
+
+    return 1;
+
+bad:
+    IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!"));
+    return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ */
+static int
+ipcp_rejci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    u_char cimaxslotindex, ciflag, cilen;
+    u_short cishort;
+    u_long cilong;
+    ipcp_options try;          /* options to request next time */
+
+    try = *go;
+    /*
+     * 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 REJCIADDR(opt, neg, old, val1, val2) \
+    if (go->neg && \
+       len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
+       p[1] == cilen && \
+       p[0] == opt) { \
+       u_long l; \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETLONG(l, p); \
+       cilong = htonl(l); \
+       /* Check rejected value. */ \
+       if (cilong != val1) \
+           goto bad; \
+       if (old) { \
+           GETLONG(l, p); \
+           cilong = htonl(l); \
+           /* Check rejected value. */ \
+           if (cilong != val2) \
+               goto bad; \
+       } \
+       try.neg = 0; \
+    }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+    if (go->neg && \
+       p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+       len >= p[1] && \
+       p[0] == opt) { \
+       len -= p[1]; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       /* Check rejected value. */  \
+       if (cishort != val) \
+           goto bad; \
+       if (!old) { \
+          GETCHAR(cimaxslotindex, p); \
+          if (cimaxslotindex != maxslot) \
+            goto bad; \
+          GETCHAR(ciflag, p); \
+          if (ciflag != cflag) \
+            goto bad; \
+        } \
+       try.neg = 0; \
+     }
+
+    REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
+             go->old_addrs, go->ouraddr, go->hisaddr);
+
+    REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+           go->maxslotindex, go->cflag);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    /*
+     * Now we can update state.
+     */
+    if (f->state != OPENED)
+       *go = try;
+    return 1;
+
+bad:
+    IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!"));
+    return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipcp_reqci(f, inp, len, reject_if_disagree)
+    fsm *f;
+    u_char *inp;               /* Requested CIs */
+    int *len;                  /* Length of requested CIs */
+    int reject_if_disagree;
+{
+    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, *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 */
+    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;
+
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+    
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    while (l) {
+       orc = CONFACK;                  /* Assume success */
+       cip = p = next;                 /* 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 */
+           goto endswitch;
+       }
+       GETCHAR(citype, p);             /* Parse CI type */
+       GETCHAR(cilen, p);              /* Parse CI length */
+       l -= cilen;                     /* Adjust remaining length */
+       next += cilen;                  /* Step to next CI */
+
+       switch (citype) {               /* Check CI type */
+       case CI_ADDRS:
+           IPCPDEBUG((LOG_INFO, "ipcp: received ADDRS "));
+           if (!ao->neg_addr ||
+               cilen != CILEN_ADDRS) { /* Check CI length */
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+
+           /*
+            * 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,
+            * then accept it.
+            */
+           GETLONG(tl, p);             /* Parse source address (his) */
+           ciaddr1 = htonl(tl);
+           IPCPDEBUG((LOG_INFO, "(%s:", ip_ntoa(ciaddr1)));
+           if (wo->hisaddr && ciaddr1 != wo->hisaddr) {
+               orc = CONFNAK;
+               if (!reject_if_disagree) {
+                   DECPTR(sizeof (long), p);
+                   tl = ntohl(wo->hisaddr);
+                   PUTLONG(tl, p);
+               }
+           }
+
+           /*
+            * 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) */
+           ciaddr2 = htonl(tl);
+           IPCPDEBUG((LOG_INFO, "%s)", ip_ntoa(ciaddr2)));
+           if (wo->ouraddr && ciaddr2 != wo->ouraddr) {
+               orc = CONFNAK;
+               if (!reject_if_disagree) {
+                   DECPTR(sizeof (long), p);
+                   tl = ntohl(wo->ouraddr);
+                   PUTLONG(tl, p);
+               }
+           }
+           if (orc == CONFNAK)
+               break;
+
+           ho->neg_addr = 1;
+           ho->old_addrs = 1;
+           ho->hisaddr = ciaddr1;
+           ho->ouraddr = ciaddr2;
+           break;
+
+       case CI_ADDR:
+           IPCPDEBUG((LOG_INFO, "ipcp: received ADDR "));
+
+           if (!ao->neg_addr ||
+               cilen != CILEN_ADDR) {  /* Check CI length */
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+
+           /*
+            * 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,
+            * then accept it.
+            */
+           GETLONG(tl, p);     /* Parse source address (his) */
+           ciaddr1 = htonl(tl);
+           IPCPDEBUG((LOG_INFO, "(%s)", ip_ntoa(ciaddr1)));
+           if (wo->hisaddr && ciaddr1 != wo->hisaddr) {
+               orc = CONFNAK;
+               if (!reject_if_disagree) {
+                   DECPTR(sizeof (long), p);
+                   tl = ntohl(wo->hisaddr);
+                   PUTLONG(tl, p);
+               }
+           }
+       
+           if (orc == CONFNAK)
+               break;
+
+           ho->neg_addr = 1;
+           ho->hisaddr = ciaddr1;
+           break;
+       
+       case CI_COMPRESSTYPE:
+           IPCPDEBUG((LOG_INFO, "ipcp: received COMPRESSTYPE "));
+           if (!ao->neg_vj ||
+               (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+               orc = CONFREJ;
+               break;
+           }
+           GETSHORT(cishort, p);
+           IPCPDEBUG((LOG_INFO, "(%d)", cishort));
+
+           if (!(cishort == IPCP_VJ_COMP ||
+                 (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+               orc = CONFREJ;
+               break;
+           }
+
+           ho->neg_vj = 1;
+           ho->vj_protocol = cishort;
+           if (cilen == CILEN_VJ) {
+               GETCHAR(maxslotindex, p);
+               if (maxslotindex > ao->maxslotindex) { 
+                   orc = CONFNAK;
+                   if (!reject_if_disagree){
+                       DECPTR(1, p);
+                       PUTCHAR(ao->maxslotindex, p);
+                   }
+               }
+               GETCHAR(cflag, p);
+               if (cflag && !ao->cflag) {
+                   orc = CONFNAK;
+                   if (!reject_if_disagree){
+                       DECPTR(1, p);
+                       PUTCHAR(wo->cflag, p);
+                   }
+               }
+               if (orc == CONFNAK)
+                   break;
+               ho->maxslotindex = maxslotindex;
+               ho->cflag = wo->cflag;
+           }
+           break;
+
+       default:
+           orc = CONFREJ;
+           break;
+       }
+
+endswitch:
+       IPCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc)));
+
+       if (orc == CONFACK &&           /* Good CI */
+           rc != CONFACK)              /*  but prior CI wasnt? */
+           continue;                   /* Don't send this one */
+
+       if (orc == CONFNAK) {           /* Nak this CI? */
+           if (reject_if_disagree)     /* Getting fed up with sending NAKs? */
+               orc = CONFREJ;          /* Get tough if so */
+           else {
+               if (rc == CONFREJ)      /* Rejecting prior CI? */
+                   continue;           /* Don't send this one */
+               if (rc == CONFACK) {    /* Ack'd all prior CIs? */
+                   rc = CONFNAK;       /* Not anymore... */
+                   ucp = inp;          /* Backup */
+               }
+           }
+       }
+
+       if (orc == CONFREJ &&           /* Reject this CI */
+           rc != CONFREJ) {            /*  but no prior ones? */
+           rc = CONFREJ;
+           ucp = inp;                  /* Backup */
+       }
+
+       /* Need to move CI? */
+       if (ucp != cip)
+           BCOPY(cip, ucp, cilen);     /* Move it */
+
+       /* Update output pointer */
+       INCPTR(cilen, ucp);
+    }
+
+    /*
+     * If we aren't rejecting this packet, and we want to negotiate
+     * their address, and they didn't send their address, then we
+     * send a NAK with a CI_ADDR option appended.  We assume the
+     * input buffer is long enough that we can append the extra
+     * option safely.
+     */
+    if (rc != CONFREJ && !ho->neg_addr &&
+       wo->req_addr && !reject_if_disagree) {
+       if (rc == CONFACK) {
+           rc = CONFNAK;
+           ucp = inp;                  /* reset pointer */
+           wo->req_addr = 0;           /* don't ask again */
+       }
+       PUTCHAR(CI_ADDR, ucp);
+       PUTCHAR(CILEN_ADDR, ucp);
+       tl = ntohl(wo->hisaddr);
+       PUTLONG(tl, ucp);
+    }
+
+    *len = ucp - inp;                  /* Compute output length */
+    IPCPDEBUG((LOG_INFO, "ipcp: returning Configure-%s", CODENAME(rc)));
+    return (rc);                       /* Return final code */
+}
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(f)
+    fsm *f;
+{
+    u_long mask;
+    ipcp_options *ho = &ipcp_hisoptions[f->unit];
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+
+    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->hisaddr == 0)
+       ho->hisaddr = ipcp_wantoptions[f->unit].hisaddr;
+
+    if (ho->hisaddr == 0) {
+       syslog(LOG_ERR, "Could not determine remote IP address");
+       ipcp_close(f->unit);
+       return;
+    }
+    if (go->ouraddr == 0) {
+       syslog(LOG_ERR, "Could not determine local IP address");
+       ipcp_close(f->unit);
+       return;
+    }
+
+    /*
+     * Check that the peer is allowed to use the IP address he 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);
+       return;
+    }
+
+    syslog(LOG_NOTICE, "local  IP address %s", ip_ntoa(go->ouraddr));
+    syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(ho->hisaddr));
+
+    /*
+     * Set IP addresses and (if specified) netmask.
+     */
+    mask = GetMask(go->ouraddr);
+    if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+       IPCPDEBUG((LOG_WARNING, "sifaddr failed"));
+       ipcp_close(f->unit);
+       return;
+    }
+
+    /* set tcp compression */
+    sifvjcomp(f->unit, ho->neg_vj, ho->cflag);
+
+    /* bring the interface up for IP */
+    if (!sifup(f->unit)) {
+       IPCPDEBUG((LOG_WARNING, "sifup failed"));
+       ipcp_close(f->unit);
+       return;
+    }
+
+    /* 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;
+
+    /* Make a proxy ARP entry if requested. */
+    if (ipcp_wantoptions[f->unit].proxy_arp)
+       if (sifproxyarp(f->unit, ho->hisaddr))
+           go->proxy_arp = 1;
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipcp_down(f)
+    fsm *f;
+{
+    u_long ouraddr, hisaddr;
+
+    IPCPDEBUG((LOG_INFO, "ipcp: down"));
+
+    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);
+}
diff --git a/pppd/ipcp.h b/pppd/ipcp.h
new file mode 100644 (file)
index 0000000..8ebfe63
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * $Id: ipcp.h,v 1.1 1993/11/11 03:54:25 paulus Exp $
+ */
+
+/*
+ * Options.
+ */
+#define CI_ADDRS       1       /* IP Addresses */
+#define CI_COMPRESSTYPE        2       /* Compression Type */
+#define        CI_ADDR         3
+
+#define MAX_STATES 16          /* from slcompress.h */
+
+#define IPCP_VJMODE_OLD 1      /* "old" mode (option # = 0x0037) */
+#define IPCP_VJMODE_RFC1172 2  /* "old-rfc"mode (option # = 0x002d) */
+#define IPCP_VJMODE_RFC1332 3  /* "new-rfc"mode (option # = 0x002d, */
+                                /*  maxslot and slot number compression) */
+
+#define IPCP_VJ_COMP 0x002d    /* current value for VJ compression option*/
+#define IPCP_VJ_COMP_OLD 0x0037        /* "old" (i.e, broken) value for VJ */
+                               /* compression option*/ 
+
+typedef struct ipcp_options {
+    int neg_addr : 1;          /* Negotiate IP Address? */
+    int old_addrs : 1;         /* Use old (IP-Addresses) option? */
+    int req_addr : 1;          /* Ask peer to send IP address? */
+    int default_route : 1;     /* Assign default route through interface? */
+    int proxy_arp : 1;         /* Make proxy ARP entry for peer? */
+    int neg_vj : 1;            /* Van Jacobson Compression? */
+    int old_vj : 1;            /* use old (short) form of VJ option? */
+    u_short vj_protocol;       /* protocol value to use in VJ option */
+    u_char maxslotindex, cflag;        /* values for RFC1332 VJ compression neg. */
+    u_long ouraddr, hisaddr;   /* Addresses in NETWORK BYTE ORDER */
+} ipcp_options;
+
+extern fsm ipcp_fsm[];
+extern ipcp_options ipcp_wantoptions[];
+extern ipcp_options ipcp_gotoptions[];
+extern ipcp_options ipcp_allowoptions[];
+extern ipcp_options ipcp_hisoptions[];
+
+void ipcp_init __ARGS((int));
+void ipcp_open __ARGS((int));
+void ipcp_close __ARGS((int));
+void ipcp_lowerup __ARGS((int));
+void ipcp_lowerdown __ARGS((int));
+void ipcp_input __ARGS((int, u_char *, int));
+void ipcp_protrej __ARGS((int));
diff --git a/pppd/lcp.c b/pppd/lcp.c
new file mode 100644 (file)
index 0000000..c475f4c
--- /dev/null
@@ -0,0 +1,1281 @@
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: lcp.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+/*
+ * TODO:
+ * Option tracing.
+ * Test restart.
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_ppp.h>
+#include <netinet/in.h>
+
+#include <string.h>
+
+#include "pppd.h"
+#include "ppp.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "magic.h"
+#include "chap.h"
+#include "upap.h"
+#include "ipcp.h"
+
+/* global vars */
+fsm lcp_fsm[NPPP];                     /* LCP fsm structure (global)*/
+lcp_options lcp_wantoptions[NPPP];     /* Options that we want to request */
+lcp_options lcp_gotoptions[NPPP];      /* Options that peer ack'd */
+lcp_options lcp_allowoptions[NPPP];    /* Options we allow peer to request */
+lcp_options lcp_hisoptions[NPPP];      /* Options that we ack'd */
+
+/*
+ * Callbacks for fsm code.  (CI = Configuration Information)
+ */
+static void lcp_resetci __ARGS((fsm *));       /* Reset our CI */
+static int  lcp_cilen __ARGS((fsm *));         /* Return length of our CI */
+static void lcp_addci __ARGS((fsm *, u_char *, int *)); /* Add our CI to pkt */
+static int  lcp_ackci __ARGS((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int  lcp_nakci __ARGS((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int  lcp_rejci __ARGS((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int  lcp_reqci __ARGS((fsm *, u_char *, int *, int)); /* Rcv peer CI */
+static void lcp_up __ARGS((fsm *));            /* We're UP */
+static void lcp_down __ARGS((fsm *));          /* We're DOWN */
+static void lcp_starting __ARGS((fsm *));      /* We need lower layer up */
+static void lcp_finished __ARGS((fsm *));      /* We need lower layer down */
+static int  lcp_extcode __ARGS((fsm *, int, int, u_char *, int));
+static void lcp_rprotrej __ARGS((fsm *, u_char *, int));
+
+static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
+    lcp_resetci,               /* Reset our Configuration Information */
+    lcp_cilen,                 /* Length of our Configuration Information */
+    lcp_addci,                 /* Add our Configuration Information */
+    lcp_ackci,                 /* ACK our Configuration Information */
+    lcp_nakci,                 /* NAK our Configuration Information */
+    lcp_rejci,                 /* Reject our Configuration Information */
+    lcp_reqci,                 /* Request peer's Configuration Information */
+    lcp_up,                    /* Called when fsm reaches OPENED state */
+    lcp_down,                  /* Called when fsm leaves OPENED state */
+    lcp_starting,              /* Called when we want the lower layer up */
+    lcp_finished,              /* Called when we want the lower layer down */
+    NULL,                      /* Called when Protocol-Reject received */
+    NULL,                      /* Retransmission is necessary */
+    lcp_extcode,               /* Called to handle LCP-specific codes */
+    "LCP"                      /* String name of protocol */
+};
+
+int lcp_warnloops = DEFWARNLOOPS; /* Warn about a loopback this often */
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID     2
+#define CILEN_SHORT    4       /* CILEN_VOID + sizeof(short) */
+#define CILEN_CHAP     5       /* CILEN_VOID + sizeof(short) + 1 */
+#define CILEN_LONG     6       /* CILEN_VOID + sizeof(long) */
+#define CILEN_LQR      8       /* CILEN_VOID + sizeof(short) + sizeof(long) */
+
+#define CODENAME(x)    ((x) == CONFACK ? "ACK" : \
+                        (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+void
+lcp_init(unit)
+    int unit;
+{
+    fsm *f = &lcp_fsm[unit];
+    lcp_options *wo = &lcp_wantoptions[unit];
+    lcp_options *ao = &lcp_allowoptions[unit];
+
+    f->unit = unit;
+    f->protocol = LCP;
+    f->callbacks = &lcp_callbacks;
+
+    fsm_init(f);
+
+    wo->passive = 0;
+    wo->silent = 0;
+    wo->restart = 0;                   /* Set to 1 in kernels or multi-line
+                                          implementations */
+    wo->neg_mru = 1;
+    wo->mru = DEFMRU;
+    wo->neg_asyncmap = 1;
+    wo->asyncmap = 0;
+    wo->neg_chap = 0;                  /* Set to 1 on server */
+    wo->neg_upap = 0;                  /* Set to 1 on server */
+    wo->chap_mdtype = CHAP_DIGEST_MD5;
+    wo->neg_magicnumber = 1;
+    wo->neg_pcompression = 1;
+    wo->neg_accompression = 1;
+    wo->neg_lqr = 0;                   /* no LQR implementation yet */
+
+    ao->neg_mru = 1;
+    ao->mru = MAXMRU;
+    ao->neg_asyncmap = 1;
+    ao->asyncmap = 0;
+    ao->neg_chap = 1;
+    ao->chap_mdtype = CHAP_DIGEST_MD5;
+    ao->neg_upap = 1;
+    ao->neg_magicnumber = 1;
+    ao->neg_pcompression = 1;
+    ao->neg_accompression = 1;
+    ao->neg_lqr = 0;                   /* no LQR implementation yet */
+
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void
+lcp_open(unit)
+    int unit;
+{
+    fsm *f = &lcp_fsm[unit];
+    lcp_options *wo = &lcp_wantoptions[unit];
+
+    f->flags = 0;
+    if (wo->passive)
+       f->flags |= OPT_PASSIVE;
+    if (wo->silent)
+       f->flags |= OPT_SILENT;
+    fsm_open(f);
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void
+lcp_close(unit)
+    int unit;
+{
+    fsm_close(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void
+lcp_lowerup(unit)
+    int unit;
+{
+    sifdown(unit);
+    ppp_send_config(unit, MTU, 0xffffffff, 0, 0);
+    ppp_recv_config(unit, MTU, 0, 0, 0);
+    peer_mru[unit] = MTU;
+
+    fsm_lowerup(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void
+lcp_lowerdown(unit)
+    int unit;
+{
+    fsm_lowerdown(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+void
+lcp_input(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    fsm_input(&lcp_fsm[unit], p, len);
+}
+
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int
+lcp_extcode(f, code, id, inp, len)
+    fsm *f;
+    int code, id;
+    u_char *inp;
+    int len;
+{
+    switch( code ){
+    case PROTREJ:
+       lcp_rprotrej(f, inp, len);
+       break;
+    
+    case ECHOREQ:
+       if( f->state != OPENED )
+           break;
+       LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id));
+       fsm_sdata(f, ECHOREP, id, inp, len);
+       break;
+    
+    case ECHOREP:
+    case DISCREQ:
+       break;
+
+    default:
+       return 0;
+    }
+    return 1;
+}
+
+    
+/*
+ * lcp_rprotrej - Receive an Protocol-Reject.
+ *
+ * Figure out which protocol is rejected and inform it.
+ */
+static void
+lcp_rprotrej(f, inp, len)
+    fsm *f;
+    u_char *inp;
+    int len;
+{
+    u_short prot;
+
+    LCPDEBUG((LOG_INFO, "lcp_rprotrej."));
+
+    if (len < sizeof (u_short)) {
+       LCPDEBUG((LOG_INFO,
+                 "lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
+       return;
+    }
+
+    GETSHORT(prot, inp);
+
+    LCPDEBUG((LOG_INFO,
+             "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!",
+             prot));
+
+    /*
+     * Protocol-Reject packets received in any state other than the LCP
+     * OPENED state SHOULD be silently discarded.
+     */
+    if( f->state != OPENED ){
+       LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d",
+                 f->state));
+       return;
+    }
+
+    DEMUXPROTREJ(f->unit, prot);       /* Inform protocol */
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+/*ARGSUSED*/
+void
+lcp_protrej(unit)
+    int unit;
+{
+    /*
+     * Can't reject LCP!
+     */
+    LCPDEBUG((LOG_WARNING,
+             "lcp_protrej: Received Protocol-Reject for LCP!"));
+    fsm_protreject(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void
+lcp_sprotrej(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    /*
+     * Send back the protocol and the information field of the
+     * rejected packet.  We only get here if LCP is in the OPENED state.
+     */
+    p += 2;
+    len -= 2;
+
+    fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id,
+             p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void
+  lcp_resetci(f)
+fsm *f;
+{
+    lcp_wantoptions[f->unit].magicnumber = magic();
+    lcp_wantoptions[f->unit].numloops = 0;
+    lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit];
+    peer_mru[f->unit] = MTU;
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int
+lcp_cilen(f)
+    fsm *f;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+
+#define LENCIVOID(neg) (neg ? CILEN_VOID : 0)
+#define LENCICHAP(neg) (neg ? CILEN_CHAP : 0)
+#define LENCISHORT(neg)        (neg ? CILEN_SHORT : 0)
+#define LENCILONG(neg) (neg ? CILEN_LONG : 0)
+#define LENCILQR(neg)  (neg ? CILEN_LQR: 0)
+    /*
+     * NB: we only ask for one of CHAP and UPAP, even if we will
+     * accept either.
+     */
+    return (LENCISHORT(go->neg_mru) +
+           LENCILONG(go->neg_asyncmap) +
+           LENCICHAP(go->neg_chap) +
+           LENCISHORT(!go->neg_chap && go->neg_upap) +
+           LENCILQR(go->neg_lqr) +
+           LENCILONG(go->neg_magicnumber) +
+           LENCIVOID(go->neg_pcompression) +
+           LENCIVOID(go->neg_accompression));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void
+lcp_addci(f, ucp, lenp)
+    fsm *f;
+    u_char *ucp;
+    int *lenp;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_VOID, ucp); \
+    }
+#define ADDCISHORT(opt, neg, val) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_SHORT, ucp); \
+       PUTSHORT(val, ucp); \
+    }
+#define ADDCICHAP(opt, neg, val, digest) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_CHAP, ucp); \
+       PUTSHORT(val, ucp); \
+       PUTCHAR(digest, ucp); \
+    }
+#define ADDCILONG(opt, neg, val) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_LONG, ucp); \
+       PUTLONG(val, ucp); \
+    }
+#define ADDCILQR(opt, neg, val) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_LQR, ucp); \
+       PUTSHORT(LQR, ucp); \
+       PUTLONG(val, ucp); \
+    }
+
+    ADDCISHORT(CI_MRU, go->neg_mru, go->mru);
+    ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap);
+    ADDCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype);
+    ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, UPAP);
+    ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+    ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+    ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+    ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+    if (ucp - start_ucp != *lenp) {
+       /* this should never happen, because peer_mtu should be 1500 */
+       syslog(LOG_ERR, "Bug in lcp_addci: wrong length");
+    }
+}
+
+
+/*
+ * lcp_ackci - Ack our CIs.
+ * This should not modify any state if the Ack is bad.
+ *
+ * Returns:
+ *     0 - Ack was bad.
+ *     1 - Ack was good.
+ */
+static int
+lcp_ackci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    u_char cilen, citype, cichar;
+    u_short cishort;
+    u_long cilong;
+
+    /*
+     * 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 ACKCIVOID(opt, neg) \
+    if (neg) { \
+       if ((len -= CILEN_VOID) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_VOID || \
+           citype != opt) \
+           goto bad; \
+    }
+#define ACKCISHORT(opt, neg, val) \
+    if (neg) { \
+       if ((len -= CILEN_SHORT) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_SHORT || \
+           citype != opt) \
+           goto bad; \
+       GETSHORT(cishort, p); \
+       if (cishort != val) \
+           goto bad; \
+    }
+#define ACKCICHAP(opt, neg, val, digest) \
+    if (neg) { \
+       if ((len -= CILEN_CHAP) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_CHAP || \
+           citype != opt) \
+           goto bad; \
+       GETSHORT(cishort, p); \
+       if (cishort != val) \
+           goto bad; \
+       GETCHAR(cichar, p); \
+       if (cichar != digest) \
+         goto bad; \
+    }
+#define ACKCILONG(opt, neg, val) \
+    if (neg) { \
+       if ((len -= CILEN_LONG) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_LONG || \
+           citype != opt) \
+           goto bad; \
+       GETLONG(cilong, p); \
+       if (cilong != val) \
+           goto bad; \
+    }
+#define ACKCILQR(opt, neg, val) \
+    if (neg) { \
+       if ((len -= CILEN_LQR) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_LQR || \
+           citype != opt) \
+           goto bad; \
+       GETSHORT(cishort, p); \
+       if (cishort != LQR) \
+           goto bad; \
+       GETLONG(cilong, p); \
+       if (cilong != val) \
+         goto bad; \
+    }
+
+    ACKCISHORT(CI_MRU, go->neg_mru, go->mru);
+    ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap);
+    ACKCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype);
+    ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, UPAP);
+    ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+    ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+    ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+    ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    return (1);
+bad:
+    LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!"));
+    return (0);
+}
+
+
+/*
+ * lcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ *     0 - Nak was bad.
+ *     1 - Nak was good.
+ */
+static int
+lcp_nakci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    lcp_options *wo = &lcp_wantoptions[f->unit];
+    u_char cilen, citype, cichar, *next;
+    u_short cishort;
+    u_long cilong;
+    lcp_options no;            /* options we've seen Naks for */
+    lcp_options try;           /* options to request next time */
+    int looped_back = 0;
+
+    BZERO(&no, sizeof(no));
+    try = *go;
+
+    /*
+     * 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 NAKCIVOID(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_VOID && \
+       p[1] == CILEN_VOID && \
+       p[0] == opt) { \
+       len -= CILEN_VOID; \
+       INCPTR(CILEN_VOID, p); \
+       no.neg = 1; \
+       code \
+    }
+#define NAKCICHAP(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_CHAP && \
+       p[1] == CILEN_CHAP && \
+       p[0] == opt) { \
+       len -= CILEN_CHAP; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETCHAR(cichar, p); \
+       no.neg = 1; \
+       code \
+    }
+#define NAKCISHORT(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_SHORT && \
+       p[1] == CILEN_SHORT && \
+       p[0] == opt) { \
+       len -= CILEN_SHORT; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       no.neg = 1; \
+       code \
+    }
+#define NAKCILONG(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_LONG && \
+       p[1] == CILEN_LONG && \
+       p[0] == opt) { \
+       len -= CILEN_LONG; \
+       INCPTR(2, p); \
+       GETLONG(cilong, p); \
+       no.neg = 1; \
+       code \
+    }
+#define NAKCILQR(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_LQR && \
+       p[1] == CILEN_LQR && \
+       p[0] == opt) { \
+       len -= CILEN_LQR; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETLONG(cilong, p); \
+       no.neg = 1; \
+       code \
+    }
+
+    /*
+     * We don't care if they want to send us smaller packets than
+     * we want.  Therefore, accept any MRU less than what we asked for,
+     * but then ignore the new value when setting the MRU in the kernel.
+     * If they send us a bigger MRU than what we asked, accept it, up to
+     * the limit of the default MRU we'd get if we didn't negotiate.
+     */
+    NAKCISHORT(CI_MRU, neg_mru,
+              if (cishort <= wo->mru || cishort < DEFMRU)
+                  try.mru = cishort;
+              );
+    /*
+     * Add any characters they want to our (receive-side) asyncmap.
+     */
+    NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
+             try.asyncmap = go->asyncmap | cilong;
+             );
+    /*
+     * If they can't cope with our CHAP hash algorithm, we'll have
+     * to stop asking for CHAP.  We haven't got any other algorithm.
+     */
+    NAKCICHAP(CI_AUTHTYPE, neg_chap,
+             try.neg_chap = 0;
+             );
+    /*
+     * Peer shouldn't send Nak for UPAP, protocol compression or
+     * address/control compression requests; they should send
+     * a Reject instead.  If they send a Nak, treat it as a Reject.
+     */
+    if (!go->neg_chap ){
+       NAKCISHORT(CI_AUTHTYPE, neg_upap,
+                  try.neg_upap = 0;
+                  );
+    }
+    /*
+     * If they can't cope with our link quality protocol, we'll have
+     * to stop asking for LQR.  We haven't got any other protocol.
+     * If they Nak the reporting period, take their value XXX ?
+     */
+    NAKCILONG(CI_QUALITY, neg_lqr,
+             if (cishort != LQR)
+                 try.neg_lqr = 0;
+             else
+                 try.lqr_period = cilong;
+             );
+    /*
+     * Check for a looped-back line.
+     */
+    NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
+             try.magicnumber = magic();
+             ++try.numloops;
+             looped_back = 1;
+             );
+
+    NAKCIVOID(CI_PCOMPRESSION, neg_pcompression,
+             try.neg_pcompression = 0;
+             );
+    NAKCIVOID(CI_ACCOMPRESSION, neg_accompression,
+             try.neg_accompression = 0;
+             );
+
+    /*
+     * There may be remaining CIs, if the peer is requesting negotiation
+     * on an option that we didn't include in our request packet.
+     * If we see an option that we requested, or one we've already seen
+     * in this packet, then this packet is bad.
+     * If we wanted to respond by starting to negotiate on the requested
+     * option(s), we could, but we don't, because except for the
+     * authentication type and quality protocol, if we are not negotiating
+     * an option, it is because we were told not to.
+     * For the authentication type, the Nak from the peer means
+     * `let me authenticate myself with you' which is a bit pointless.
+     * For the quality protocol, the Nak means `ask me to send you quality
+     * reports', but if we didn't ask for them, we don't want them.
+     */
+    while (len > CILEN_VOID) {
+       GETCHAR(citype, p);
+       GETCHAR(cilen, p);
+       if( (len -= cilen) < 0 )
+           goto bad;
+       next = p + cilen - 2;
+
+       switch (citype) {
+       case CI_MRU:
+           if (go->neg_mru || no.neg_mru || cilen != CILEN_SHORT)
+               goto bad;
+           break;
+       case CI_ASYNCMAP:
+           if (go->neg_asyncmap || no.neg_asyncmap || cilen != CILEN_LONG)
+               goto bad;
+           break;
+       case CI_AUTHTYPE:
+           if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap)
+               goto bad;
+           break;
+       case CI_MAGICNUMBER:
+           if (go->neg_magicnumber || no.neg_magicnumber ||
+               cilen != CILEN_LONG)
+               goto bad;
+           break;
+       case CI_PCOMPRESSION:
+           if (go->neg_pcompression || no.neg_pcompression
+               || cilen != CILEN_VOID)
+               goto bad;
+           break;
+       case CI_ACCOMPRESSION:
+           if (go->neg_accompression || no.neg_accompression
+               || cilen != CILEN_VOID)
+               goto bad;
+           break;
+       case CI_QUALITY:
+           if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
+               goto bad;
+           break;
+       default:
+           goto bad;
+       }
+       p = next;
+    }
+
+    /* If there is still anything left, this packet is bad. */
+    if (len != 0)
+       goto bad;
+
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     */
+    if (f->state != OPENED) {
+       *go = try;
+       if (looped_back && try.numloops % lcp_warnloops == 0)
+           LCPDEBUG((LOG_INFO, "The line appears to be looped back."));
+    }
+
+    return 1;
+
+bad:
+    LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!"));
+    return 0;
+}
+
+
+/*
+ * lcp_rejci - Peer has Rejected some of our CIs.
+ * This should not modify any state if the Reject is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ *     0 - Reject was bad.
+ *     1 - Reject was good.
+ */
+static int
+lcp_rejci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    u_char cichar;
+    u_short cishort;
+    u_long cilong;
+    u_char *start = p;
+    int plen = len;
+    lcp_options try;           /* options to request next time */
+
+    try = *go;
+
+    /*
+     * 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 REJCIVOID(opt, neg) \
+    if (go->neg && \
+       len >= CILEN_VOID && \
+       p[1] == CILEN_VOID && \
+       p[0] == opt) { \
+       len -= CILEN_VOID; \
+       INCPTR(CILEN_VOID, p); \
+       try.neg = 0; \
+       LCPDEBUG((LOG_INFO, "lcp_rejci rejected void opt %d", opt)); \
+    }
+#define REJCISHORT(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_SHORT && \
+       p[1] == CILEN_SHORT && \
+       p[0] == opt) { \
+       len -= CILEN_SHORT; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       /* Check rejected value. */ \
+       if (cishort != val) \
+           goto bad; \
+       try.neg = 0; \
+       LCPDEBUG((LOG_INFO,"lcp_rejci rejected short opt %d", opt)); \
+    }
+#define REJCICHAP(opt, neg, val, digest) \
+    if (go->neg && \
+       len >= CILEN_CHAP && \
+       p[1] == CILEN_CHAP && \
+       p[0] == opt) { \
+       len -= CILEN_CHAP; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETCHAR(cichar, p); \
+       /* Check rejected value. */ \
+       if (cishort != val || cichar != digest) \
+           goto bad; \
+       try.neg = 0; \
+       LCPDEBUG((LOG_INFO,"lcp_rejci rejected chap opt %d", opt)); \
+    }
+#define REJCILONG(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_LONG && \
+       p[1] == CILEN_LONG && \
+       p[0] == opt) { \
+       len -= CILEN_LONG; \
+       INCPTR(2, p); \
+       GETLONG(cilong, p); \
+       /* Check rejected value. */ \
+       if (cilong != val) \
+           goto bad; \
+       try.neg = 0; \
+       LCPDEBUG((LOG_INFO,"lcp_rejci rejected long opt %d", opt)); \
+    }
+#define REJCILQR(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_LQR && \
+       p[1] == CILEN_LQR && \
+       p[0] == opt) { \
+       len -= CILEN_LQR; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETLONG(cilong, p); \
+       /* Check rejected value. */ \
+       if (cishort != LQR || cichar != val) \
+           goto bad; \
+       try.neg = 0; \
+       LCPDEBUG((LOG_INFO,"lcp_rejci rejected LQR opt %d", opt)); \
+    }
+
+    REJCISHORT(CI_MRU, neg_mru, go->mru);
+    REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+    REJCICHAP(CI_AUTHTYPE, neg_chap, CHAP, go->chap_mdtype);
+    if (!go->neg_chap) {
+       REJCISHORT(CI_AUTHTYPE, neg_upap, UPAP);
+    }
+    REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+    REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+    REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+    REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    /*
+     * Now we can update state.
+     */
+    if (f->state != OPENED)
+       *go = try;
+    return 1;
+
+bad:
+    LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!"));
+    LCPDEBUG((LOG_WARNING, "lcp_rejci: plen %d len %d off %d",
+             plen, len, p - start));
+    return 0;
+}
+
+
+/*
+ * lcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+lcp_reqci(f, inp, lenp, reject_if_disagree)
+    fsm *f;
+    u_char *inp;               /* Requested CIs */
+    int *lenp;                 /* Length of requested CIs */
+    int reject_if_disagree;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    lcp_options *ho = &lcp_hisoptions[f->unit];
+    lcp_options *ao = &lcp_allowoptions[f->unit];
+    u_char *cip, *next;                /* Pointer to current and next CIs */
+    u_char cilen, citype, cichar;/* Parsed len, type, char value */
+    u_short cishort;           /* Parsed short value */
+    u_long cilong;             /* Parse long value */
+    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 = *lenp;             /* Length left */
+
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    while (l) {
+       orc = CONFACK;                  /* Assume success */
+       cip = p = next;                 /* 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? */
+           LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!"));
+           orc = CONFREJ;              /* Reject bad CI */
+           cilen = l;                  /* Reject till end of packet */
+           l = 0;                      /* Don't loop again */
+           goto endswitch;
+       }
+       GETCHAR(citype, p);             /* Parse CI type */
+       GETCHAR(cilen, p);              /* Parse CI length */
+       l -= cilen;                     /* Adjust remaining length */
+       next += cilen;                  /* Step to next CI */
+
+       switch (citype) {               /* Check CI type */
+       case CI_MRU:
+           LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MRU"));
+           if (!ao->neg_mru ||         /* Allow option? */
+               cilen != CILEN_SHORT) { /* Check CI length */
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+           GETSHORT(cishort, p);       /* Parse MRU */
+           LCPDEBUG((LOG_INFO, "(%d)", cishort));
+
+           /*
+            * He must be able to receive at least our minimum.
+            * No need to check a maximum.  If he sends a large number,
+            * we'll just ignore it.
+            */
+           if (cishort < MINMRU) {
+               orc = CONFNAK;          /* Nak CI */
+               if( !reject_if_disagree ){
+                   DECPTR(sizeof (short), p);  /* Backup */
+                   PUTSHORT(MINMRU, p);        /* Give him a hint */
+               }
+               break;
+           }
+           ho->neg_mru = 1;            /* Remember he sent MRU */
+           ho->mru = cishort;          /* And remember value */
+           break;
+
+       case CI_ASYNCMAP:
+           LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ASYNCMAP"));
+           if (!ao->neg_asyncmap ||
+               cilen != CILEN_LONG) {
+               orc = CONFREJ;
+               break;
+           }
+           GETLONG(cilong, p);
+           LCPDEBUG((LOG_INFO, "(%lx)", cilong));
+
+           /*
+            * Asyncmap must have set at least the bits
+            * which are set in lcp_allowoptions[unit].asyncmap.
+            */
+           if ((ao->asyncmap & ~cilong) != 0) {
+               orc = CONFNAK;
+               if( !reject_if_disagree ){
+                   DECPTR(sizeof (long), p);
+                   PUTLONG(ao->asyncmap | cilong, p);
+               }
+               break;
+           }
+           ho->neg_asyncmap = 1;
+           ho->asyncmap = cilong;
+           break;
+
+       case CI_AUTHTYPE:
+           LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE"));
+           if (cilen < CILEN_SHORT ||
+               !(ao->neg_upap || ao->neg_chap)) {
+               orc = CONFREJ;
+               break;
+           }
+           GETSHORT(cishort, p);
+           LCPDEBUG((LOG_INFO, "(%x)", cishort));
+
+           /*
+            * Authtype must be UPAP or CHAP.
+            *
+            * Note: if both ao->neg_upap and ao->neg_chap are set,
+            * and the peer sends a Configure-Request with two
+            * authenticate-protocol requests, one for CHAP and one
+            * for UPAP, then we will reject the second request.
+            * Whether we end up doing CHAP or UPAP depends then on
+            * the ordering of the CIs in the peer's Configure-Request.
+            */
+
+           if (cishort == UPAP) {
+               if (!ao->neg_upap ||    /* we don't want to do PAP */
+                   ho->neg_chap ||     /* or we've already accepted CHAP */
+                   cilen != CILEN_SHORT) {
+                   LCPDEBUG((LOG_WARNING,
+                             "lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
+                   orc = CONFREJ;
+                   break;
+               }
+               ho->neg_upap = 1;
+               break;
+           }
+           if (cishort == CHAP) {
+               if (!ao->neg_chap ||    /* we don't want to do CHAP */
+                   ho->neg_upap ||     /* or we've already accepted UPAP */
+                   cilen != CILEN_CHAP) {
+                   LCPDEBUG((LOG_INFO,
+                             "lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
+                   orc = CONFREJ;
+                   break;
+               }
+               GETCHAR(cichar, p);     /* get digest type*/
+               if (cichar != ao->chap_mdtype) {
+                   orc = CONFNAK;
+                   if( !reject_if_disagree ){
+                       DECPTR(sizeof (u_char), p);
+                       PUTCHAR(ao->chap_mdtype, p);
+                   }
+                   break;
+               }
+               ho->chap_mdtype = cichar; /* save md type */
+               ho->neg_chap = 1;
+               break;
+           }
+
+           /*
+            * We don't recognize the protocol they're asking for.
+            * Reject it.
+            */
+           orc = CONFREJ;
+           break;
+
+       case CI_QUALITY:
+           LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd QUALITY"));
+           if (!ao->neg_lqr ||
+               cilen != CILEN_LQR) {
+               orc = CONFREJ;
+               break;
+           }
+
+           GETSHORT(cishort, p);
+           GETLONG(cilong, p);
+           LCPDEBUG((LOG_INFO, "(%x %lx)", cishort, cilong));
+           if (cishort != LQR) {
+               orc = CONFREJ;
+               break;
+           }
+
+           /*
+            * Check the reporting period.
+            * XXX When should we Nak this, and what with?
+            */
+           break;
+
+       case CI_MAGICNUMBER:
+           LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MAGICNUMBER"));
+           if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
+               cilen != CILEN_LONG) {
+               orc = CONFREJ;
+               break;
+           }
+           GETLONG(cilong, p);
+           LCPDEBUG((LOG_INFO, "(%lx)", cilong));
+
+           /*
+            * He must have a different magic number.
+            */
+           if (go->neg_magicnumber &&
+               cilong == go->magicnumber) {
+               orc = CONFNAK;
+               DECPTR(sizeof (long), p);
+               cilong = magic();       /* Don't put magic() inside macro! */
+               PUTLONG(cilong, p);
+               break;
+           }
+           ho->neg_magicnumber = 1;
+           ho->magicnumber = cilong;
+           break;
+
+
+       case CI_PCOMPRESSION:
+           LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd PCOMPRESSION"));
+           if (!ao->neg_pcompression ||
+               cilen != CILEN_VOID) {
+               orc = CONFREJ;
+               break;
+           }
+           ho->neg_pcompression = 1;
+           break;
+
+       case CI_ACCOMPRESSION:
+           LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ACCOMPRESSION"));
+           if (!ao->neg_accompression ||
+               cilen != CILEN_VOID) {
+               orc = CONFREJ;
+               break;
+           }
+           ho->neg_accompression = 1;
+           break;
+
+       default:
+           LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd unknown option %d",
+                     citype));
+           orc = CONFREJ;
+           break;
+       }
+
+endswitch:
+       LCPDEBUG((LOG_INFO, " (%s)", CODENAME(orc)));
+       if (orc == CONFACK &&           /* Good CI */
+           rc != CONFACK)              /*  but prior CI wasnt? */
+           continue;                   /* Don't send this one */
+
+       if (orc == CONFNAK) {           /* Nak this CI? */
+           if (reject_if_disagree)     /* Getting fed up with sending NAKs? */
+               orc = CONFREJ;          /* Get tough if so */
+           else {
+               if (rc == CONFREJ)      /* Rejecting prior CI? */
+                   continue;           /* Don't send this one */
+               if (rc == CONFACK) {    /* Ack'd all prior CIs? */
+                   rc = CONFNAK;       /* Not anymore... */
+                   ucp = inp;          /* Backup */
+               }
+           }
+       }
+       if (orc == CONFREJ &&           /* Reject this CI */
+           rc != CONFREJ) {            /*  but no prior ones? */
+           rc = CONFREJ;
+           ucp = inp;                  /* Backup */
+       }
+       if (ucp != cip)                 /* Need to move CI? */
+           BCOPY(cip, ucp, cilen);     /* Move it */
+       INCPTR(cilen, ucp);             /* Update output pointer */
+    }
+
+    /*
+     * 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.  At present there
+     * are no cases where we want to ask the peer to negotiate an option.
+     */
+
+    *lenp = ucp - inp;                 /* Compute output length */
+    LCPDEBUG((LOG_INFO, "lcp_reqci: returning CONF%s.", CODENAME(rc)));
+    return (rc);                       /* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ *
+ * Start UPAP, IPCP, etc.
+ */
+static void
+lcp_up(f)
+    fsm *f;
+{
+    lcp_options *wo = &lcp_wantoptions[f->unit];
+    lcp_options *ho = &lcp_hisoptions[f->unit];
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    lcp_options *ao = &lcp_allowoptions[f->unit];
+
+    /*
+     * Set our MTU to the smaller of the MTU we wanted and
+     * the MRU our peer wanted.  If we negotiated an MRU,
+     * set our MRU to the larger of value we wanted and
+     * the value we got in the negotiation.
+     */
+    ppp_send_config(f->unit, (ho->neg_mru? MIN(ao->mru, ho->mru): MTU),
+                   (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
+                   ho->neg_pcompression, ho->neg_accompression);
+    ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): MTU),
+                   (go->neg_asyncmap? go->asyncmap: 0xffffffff),
+                   go->neg_pcompression, go->neg_accompression);
+
+    if (ho->neg_mru)
+       peer_mru[f->unit] = ho->mru;
+
+    ChapLowerUp(f->unit);      /* Enable CHAP */
+    upap_lowerup(f->unit);     /* Enable UPAP */
+    ipcp_lowerup(f->unit);     /* Enable IPCP */
+
+    link_established(f->unit);
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void
+lcp_down(f)
+    fsm *f;
+{
+    ipcp_lowerdown(f->unit);
+    ChapLowerDown(f->unit);
+    upap_lowerdown(f->unit);
+
+    sifdown(f->unit);
+    ppp_send_config(f->unit, MTU, 0xffffffff, 0, 0);
+    ppp_recv_config(f->unit, MTU, 0, 0, 0);
+    peer_mru[f->unit] = MTU;
+    syslog(LOG_NOTICE, "Connection terminated.");
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void
+lcp_starting(f)
+    fsm *f;
+{
+    link_required(f->unit);
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void
+lcp_finished(f)
+    fsm *f;
+{
+    link_terminated(f->unit);
+}
+
diff --git a/pppd/lcp.h b/pppd/lcp.h
new file mode 100644 (file)
index 0000000..f7d5ea2
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * $Id: lcp.h,v 1.1 1993/11/11 03:54:25 paulus Exp $
+ */
+
+/*
+ * Options.
+ */
+#define CI_MRU         1       /* Maximum Receive Unit */
+#define CI_ASYNCMAP    2       /* Async Control Character Map */
+#define CI_AUTHTYPE    3       /* Authentication Type */
+#define CI_QUALITY     4       /* Quality Protocol */
+#define CI_MAGICNUMBER 5       /* Magic Number */
+#define CI_KEEPALIVE   6       /* Keep Alive Parameters - OBSOLETE */
+#define CI_PCOMPRESSION        7       /* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8     /* Address/Control Field Compression */
+
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+    int passive : 1;           /* Don't die if we don't get a response */
+    int silent : 1;            /* Wait for the other end to start first */
+    int restart : 1;           /* Restart vs. exit after close */
+    int neg_mru : 1;           /* Negotiate the MRU? */
+    int neg_asyncmap : 1;      /* Negotiate the async map? */
+    int neg_upap : 1;          /* Ask for UPAP authentication? */
+    int neg_chap : 1;          /* Ask for CHAP authentication? */
+    int neg_magicnumber : 1;   /* Ask for magic number? */
+    int neg_pcompression : 1;  /* HDLC Protocol Field Compression? */
+    int neg_accompression : 1; /* HDLC Address/Control Field Compression? */
+    int neg_lqr : 1;           /* Negotiate use of Link Quality Reports */
+    u_short mru;               /* Value of MRU */
+    char chap_mdtype;          /* which MD type (hashing algorithm) */
+    u_long asyncmap;           /* Value of async map */
+    u_long magicnumber;
+    int numloops;              /* Number of loops during magic number neg. */
+    u_long lqr_period;         /* Reporting period for link quality */
+} lcp_options;
+
+extern fsm lcp_fsm[];
+extern lcp_options lcp_wantoptions[];
+extern lcp_options lcp_gotoptions[];
+extern lcp_options lcp_allowoptions[];
+extern lcp_options lcp_hisoptions[];
+
+#define DEFMRU 1500            /* Try for this */
+#define MINMRU 128             /* No MRUs below this */
+#define MAXMRU 16384           /* Normally limit MRU to this */
+
+void lcp_init __ARGS((int));
+void lcp_open __ARGS((int));
+void lcp_close __ARGS((int));
+void lcp_lowerup __ARGS((int));
+void lcp_lowerdown __ARGS((int));
+void lcp_input __ARGS((int, u_char *, int));
+void lcp_protrej __ARGS((int));
+void lcp_sprotrej __ARGS((int, u_char *, int));
+
+extern int lcp_warnloops;      /* Warn about a loopback this often */
+#define DEFWARNLOOPS   10      /* Default value for above */
diff --git a/pppd/magic.c b/pppd/magic.c
new file mode 100644 (file)
index 0000000..20dfd6f
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: magic.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "magic.h"
+
+
+static u_long next;            /* Next value to return */
+
+extern u_long gethostid __ARGS((void));
+extern long random __ARGS((void));
+extern void srandom __ARGS((int));
+
+
+/*
+ * magic_init - Initialize the magic number generator.
+ *
+ * Computes first magic number and seed for random number generator.
+ * Attempts to compute a random number seed which will not repeat.
+ * The current method uses the current hostid and current time.
+ */
+void magic_init()
+{
+    struct timeval tv;
+
+    next = gethostid();
+    if (gettimeofday(&tv, NULL)) {
+       perror("gettimeofday");
+       exit(1);
+    }
+    next ^= (u_long) tv.tv_sec ^ (u_long) tv.tv_usec;
+
+    srandom((int) next);
+}
+
+
+/*
+ * magic - Returns the next magic number.
+ */
+u_long magic()
+{
+    u_long m;
+
+    m = next;
+    next = (u_long) random();
+    return (m);
+}
diff --git a/pppd/magic.h b/pppd/magic.h
new file mode 100644 (file)
index 0000000..d41aa39
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * $Id: magic.h,v 1.1 1993/11/11 03:54:25 paulus Exp $
+ */
+#include "args.h"
+
+void magic_init __ARGS((void));        /* Initialize the magic number generator */
+u_long magic __ARGS((void));   /* Returns the next magic number */
diff --git a/pppd/main.c b/pppd/main.c
new file mode 100644 (file)
index 0000000..638e296
--- /dev/null
@@ -0,0 +1,1227 @@
+/*
+ * main.c - Point-to-Point Protocol main module
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: main.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+#define SETSID
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <utmp.h>
+
+#ifdef STREAMS
+#undef SGTTY
+#endif
+
+#ifdef SGTTY
+#include <sgtty.h>
+#else
+#ifndef sun
+#include <sys/ioctl.h>
+#endif
+#include <termios.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include "callout.h"
+
+#include <net/if.h>
+#include <net/if_ppp.h>
+
+#include <string.h>
+
+#ifndef BSD
+#define BSD 43
+#endif /*BSD*/
+
+#include "ppp.h"
+#include "magic.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+
+#include "pppd.h"
+#include "pathnames.h"
+#include "patchlevel.h"
+
+
+#ifndef TRUE
+#define TRUE (1)
+#endif /*TRUE*/
+
+#ifndef FALSE
+#define FALSE (0)
+#endif /*FALSE*/
+
+#ifdef PIDPATH
+static char *pidpath = PIDPATH;        /* filename in which pid will be stored */
+#else
+static char *pidpath = _PATH_PIDFILE;
+#endif /* PIDFILE */
+
+/* interface vars */
+char ifname[IFNAMSIZ];         /* Interface name */
+int ifunit;                    /* Interface unit number */
+
+char *progname;                        /* Name of this program */
+char hostname[MAXNAMELEN];     /* Our hostname */
+char our_name[MAXNAMELEN];
+char remote_name[MAXNAMELEN];
+
+static pid_t   pid;            /* Our pid */
+static pid_t   pgrpid;         /* Process Group ID */
+static char pidfilename[MAXPATHLEN];
+
+char devname[MAXPATHLEN] = "/dev/tty"; /* Device name */
+int default_device = TRUE;     /* use default device (stdin/out) */
+
+int fd;                                /* Device file descriptor */
+int s;                         /* Socket file descriptor */
+
+#ifdef SGTTY
+static struct sgttyb initsgttyb;       /* Initial TTY sgttyb */
+#else
+static struct termios inittermios;     /* Initial TTY termios */
+#endif
+
+static int initfdflags = -1;   /* Initial file descriptor flags */
+
+static int restore_term;       /* 1 => we've munged the terminal */
+
+u_char outpacket_buf[MTU+DLLHEADERLEN]; /* buffer for outgoing packet */
+static u_char inpacket_buf[MTU+DLLHEADERLEN]; /* buffer for incoming packet */
+
+int hungup;                    /* terminal has been hung up */
+
+/* configured variables */
+
+int debug = 0;                 /* Debug flag */
+char user[MAXNAMELEN];         /* username for PAP */
+char passwd[MAXSECRETLEN];     /* password for PAP */
+char *connector = NULL;                /* "connect" command */
+int inspeed = 0;               /* Input/Output speed */
+u_long netmask = 0;            /* netmask to use on ppp interface */
+int crtscts = 0;               /* use h/w flow control */
+int nodetach = 0;              /* don't fork */
+int modem = 0;                 /* use modem control lines */
+int auth_required = 0;         /* require peer to authenticate */
+int defaultroute = 0;          /* assign default route through interface */
+int proxyarp = 0;              /* set entry in arp table */
+int persist = 0;               /* re-initiate on termination */
+int answer = 0;                        /* wait for incoming call */
+int uselogin = 0;              /* check PAP info against /etc/passwd */
+
+
+/* prototypes */
+static void hup __ARGS((int, int, struct sigcontext *, char *));
+static void intr __ARGS((int, int, struct sigcontext *, char *));
+static void term __ARGS((int, int, struct sigcontext *, char *));
+static void alrm __ARGS((int, int, struct sigcontext *, char *));
+static void io __ARGS((int, int, struct sigcontext *, char *));
+static void incdebug __ARGS((int));
+static void nodebug __ARGS((int));
+void establish_ppp __ARGS((void));
+
+void cleanup __ARGS((int, caddr_t));
+void die __ARGS((int));
+void dumpbuffer __ARGS((unsigned char *, int, int));
+
+#ifdef STREAMS
+extern char    *ttyname __ARGS((int));
+#endif
+extern char    *getlogin __ARGS((void));
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ */
+static struct protent {
+    u_short protocol;
+    void (*init)();
+    void (*input)();
+    void (*protrej)();
+} prottbl[] = {
+    { LCP, lcp_init, lcp_input, lcp_protrej },
+    { IPCP, ipcp_init, ipcp_input, ipcp_protrej },
+    { UPAP, upap_init, upap_input, upap_protrej },
+    { CHAP, ChapInit, ChapInput, ChapProtocolReject },
+};
+
+
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    int mask, i;
+    struct sigvec sv;
+    struct cmd *cmdp;
+    FILE *pidfile;
+    char *p;
+
+    /*
+     * Initialize syslog system and magic number package.
+     */
+#if BSD >= 43 || defined(sun)
+    openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
+    setlogmask(LOG_UPTO(LOG_INFO));
+#else
+    openlog("pppd", LOG_PID);
+#define LOG_UPTO(x) (x)
+#define setlogmask(x) (x)
+#endif
+
+#ifdef STREAMS
+    p = ttyname(fileno(stdin));
+    if (p)
+       strcpy(devname, p);
+#endif
+  
+    magic_init();
+
+    if (gethostname(hostname, MAXNAMELEN) < 0 ) {
+       syslog(LOG_ERR, "couldn't get hostname: %m");
+       die(1);
+    }
+    hostname[MAXNAMELEN-1] = 0;
+
+    pid = getpid();
+
+    /*
+     * Initialize to the standard option set, then parse, in order,
+     * the system options file, the user's options file, and the command
+     * line arguments.
+     */
+    for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++)
+       (*prottbl[i].init)(0);
+  
+    progname = *argv;
+
+    if (!options_from_file(_PATH_SYSOPTIONS) ||
+       !options_from_user() ||
+       !parse_args(argc-1, argv+1))
+       die(1);
+    check_auth_options();
+    setipdefault();
+
+    p = getlogin();
+    if (p == NULL)
+       p = "(unknown)";
+    syslog(LOG_NOTICE, "pppd %s.%d started by %s, uid %d",
+          VERSION, PATCHLEVEL, p, getuid());
+
+#ifdef SETSID
+    /*
+     * Make sure we can set the serial device to be our controlling terminal.
+     */
+    if (default_device) {
+       /*
+        * No device name was specified:
+        * we are in the device's session already.
+        */
+       if ((pgrpid = getpgrp(0)) < 0) {
+           syslog(LOG_ERR, "getpgrp(0): %m");
+           die(1);
+       }
+       if (pgrpid != pid) 
+           syslog(LOG_WARNING, "warning... not a process group leader");
+
+    } else {
+       /*
+        * Not default device: make sure we're not a process group leader,
+        * then become session leader of a new session (so we can make
+        * our device its controlling terminal and thus get SIGHUPs).
+        */
+       if (!nodetach) {
+           /* fork so we're not a process group leader */
+           if (pid = fork()) {
+               exit(0);        /* parent is finished */
+           }
+           if (pid < 0) {
+               syslog(LOG_ERR, "fork: %m");
+               die(1);
+           }
+           pid = getpid();     /* otherwise pid is 0 in child */
+       } else {
+           /*
+            * try to put ourself into our parent's process group,
+            * so we're not a process group leader
+            */
+           if (setpgrp(pid, getppid()) < 0)
+               syslog(LOG_WARNING, "setpgrp: %m");
+       }
+
+       /* create new session */
+       if ((pgrpid = setsid()) < 0) {
+           syslog(LOG_ERR, "setsid(): %m");
+           die(1);
+       }
+    }
+#endif
+
+    /* Get an internet socket for doing socket ioctl's on. */
+    if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+       syslog(LOG_ERR, "socket : %m");
+       die(1);
+    }
+  
+    /*
+     * Compute mask of all interesting signals and install signal handlers
+     * for each.  Only one signal handler may be active at a time.  Therefore,
+     * all other signals should be masked when any handler is executing.
+     */
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGHUP);
+    sigaddset(&mask, SIGINT);
+    sigaddset(&mask, SIGALRM);
+    sigaddset(&mask, SIGIO);
+#ifdef STREAMS
+    sigaddset(&mask, SIGPOLL);
+#endif
+
+#define SIGNAL(s, handler)     { \
+       sv.sv_handler = handler; \
+       if (sigvec(s, &sv, NULL) < 0) { \
+           syslog(LOG_ERR, "sigvec(%d): %m", s); \
+           die(1); \
+       } \
+    }
+
+    sv.sv_mask = mask;
+    sv.sv_flags = 0;
+    SIGNAL(SIGHUP, hup);               /* Hangup */
+    SIGNAL(SIGINT, intr);              /* Interrupt */
+    SIGNAL(SIGTERM, term);             /* Terminate */
+    SIGNAL(SIGALRM, alrm);             /* Timeout */
+    SIGNAL(SIGIO, io);                 /* Input available */
+#ifdef STREAMS
+    SIGNAL(SIGPOLL, io);               /* Input available */
+#endif
+
+    signal(SIGUSR1, incdebug);         /* Increment debug flag */
+    signal(SIGUSR2, nodebug);          /* Reset debug flag */
+  
+
+    /*
+     * Open the serial device and set it up to be the ppp interface.
+     */
+    if ((fd = open(devname, O_RDWR /*| O_NDELAY*/)) < 0) {
+       syslog(LOG_ERR, "open(%s): %m", devname);
+       die(1);
+    }
+    hungup = 0;
+
+    /* set device to be controlling tty */
+    if (!default_device && ioctl(fd, TIOCSCTTY) < 0) {
+       syslog(LOG_ERR, "ioctl(TIOCSCTTY): %m");
+       die(1);
+    }
+
+    /* set line speed, flow control, etc. */
+    set_up_tty(fd);
+
+    /* run connection script */
+    if (connector) {
+       syslog(LOG_INFO, "Connecting with <%s>", connector);
+
+       /* drop dtr to hang up in case modem is off hook */
+       if (!default_device && modem) {
+           setdtr(fd, FALSE);
+           sleep(1);
+           setdtr(fd, TRUE);
+       }
+
+       if (set_up_connection(connector, fd, fd) < 0) {
+           syslog(LOG_ERR, "could not set up connection");
+           setdtr(fd, FALSE);
+           die(1);
+       }
+
+       syslog(LOG_INFO, "Connected...");
+       sleep(1);               /* give it time to set up its terminal */
+    }
+  
+    /* set up the serial device as a ppp interface */
+    establish_ppp();
+
+    syslog(LOG_INFO, "Using interface ppp%d", ifunit);
+    (void) sprintf(ifname, "ppp%d", ifunit);
+
+    /* write pid to file */
+    (void) sprintf(pidfilename, "%s/%s.pid", pidpath, ifname);
+    if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+       fprintf(pidfile, "%d\n", pid);
+       (void) fclose(pidfile);
+    } else {
+       syslog(LOG_ERR, "unable to create pid file: %m");
+       pidfilename[0] = 0;
+    }
+
+    /*
+     * Set process group of device to our process group so we can get SIGIOs.
+     */
+#ifdef SETSID
+    if (default_device) {
+       int id = tcgetpgrp(fd);
+       if (id != pgrpid) {
+           syslog(LOG_WARNING,
+                  "warning: not foreground process group leader");
+       }
+    } else {
+       if (tcsetpgrp(fd, pgrpid) < 0) {
+           syslog(LOG_ERR, "tcsetpgrp(): %m");
+           die(1);
+       }
+    }
+#else
+    /* set process group on tty so we get SIGIO's */
+    if (ioctl(fd, TIOCSPGRP, &pgrpid) < 0) {
+       syslog(LOG_ERR, "ioctl(TIOCSPGRP): %m");
+       die(1);
+    }
+#endif
+
+    /*
+     * Record initial device flags, then set device to cause SIGIO
+     * signals to be generated.
+     */
+    if ((initfdflags = fcntl(fd, F_GETFL)) == -1) {
+       syslog(LOG_ERR, "fcntl(F_GETFL): %m");
+       die(1);
+    }
+    if (fcntl(fd, F_SETFL, FNDELAY | FASYNC) == -1) {
+       syslog(LOG_ERR, "fcntl(F_SETFL, FNDELAY | FASYNC): %m");
+       die(1);
+    }
+  
+    /*
+     * Block all signals, start opening the connection, and  wait for
+     * incoming signals (reply, timeout, etc.).
+     */
+    syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devname);
+    sigprocmask(SIG_BLOCK, &mask, NULL); /* Block signals now */
+    lcp_lowerup(0);                    /* XXX Well, sort of... */
+    lcp_open(0);                       /* Start protocol */
+    for (;;) {
+       sigpause(0);                    /* Wait for next signal */
+    }
+}
+
+#if B9600 == 9600
+/*
+ * XXX assume speed_t values numerically equal bits per second
+ * (so we can ask for any speed).
+ */
+#define translate_speed(bps)   (bps)
+
+#else
+/*
+ * List of valid speeds.
+ */
+struct speed {
+    int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+    { 50, B50 },
+#endif
+#ifdef B75
+    { 75, B75 },
+#endif
+#ifdef B110
+    { 110, B110 },
+#endif
+#ifdef B134
+    { 134, B134 },
+#endif
+#ifdef B150
+    { 150, B150 },
+#endif
+#ifdef B200
+    { 200, B200 },
+#endif
+#ifdef B300
+    { 300, B300 },
+#endif
+#ifdef B600
+    { 600, B600 },
+#endif
+#ifdef B1200
+    { 1200, B1200 },
+#endif
+#ifdef B1800
+    { 1800, B1800 },
+#endif
+#ifdef B2000
+    { 2000, B2000 },
+#endif
+#ifdef B2400
+    { 2400, B2400 },
+#endif
+#ifdef B3600
+    { 3600, B3600 },
+#endif
+#ifdef B4800
+    { 4800, B4800 },
+#endif
+#ifdef B7200
+    { 7200, B7200 },
+#endif
+#ifdef B9600
+    { 9600, B9600 },
+#endif
+#ifdef B19200
+    { 19200, B19200 },
+#endif
+#ifdef B38400
+    { 38400, B38400 },
+#endif
+#ifdef EXTA
+    { 19200, EXTA },
+#endif
+#ifdef EXTB
+    { 38400, EXTB },
+#endif
+#ifdef B57600
+    { 57600, B57600 },
+#endif
+#ifdef B115200
+    { 115200, B115200 },
+#endif
+    { 0, 0 }
+};
+
+/*
+ * Translate from bits/second to a speed_t.
+ */
+int
+translate_speed(bps)
+    int bps;
+{
+    struct speed *speedp;
+
+    if (bps == 0)
+       return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+       if (bps == speedp->speed_int)
+           return speedp->speed_val;
+    syslog(LOG_WARNING, "speed %d not supported", bps);
+    return 0;
+}
+#endif
+
+/*
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc.
+ */
+set_up_tty(fd)
+    int fd;
+{
+#ifndef SGTTY
+    int speed;
+    struct termios tios;
+
+    if (tcgetattr(fd, &tios) < 0) {
+       syslog(LOG_ERR, "tcgetattr: %m");
+       die(1);
+    }
+
+    if (!restore_term)
+       inittermios = tios;
+
+    tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL | CRTSCTS);
+    tios.c_cflag |= CS8 | CREAD | HUPCL;
+    if (crtscts)
+       tios.c_cflag |= CRTSCTS;
+    if (!modem)
+       tios.c_cflag |= CLOCAL;
+    tios.c_iflag = IGNBRK | IGNPAR;
+    tios.c_oflag = 0;
+    tios.c_lflag = 0;
+    tios.c_cc[VMIN] = 1;
+    tios.c_cc[VTIME] = 0;
+    speed = translate_speed(inspeed);
+    if (speed) {
+       cfsetospeed(&tios, speed);
+       cfsetispeed(&tios, speed);
+    }
+
+    if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
+       syslog(LOG_ERR, "tcsetattr: %m");
+       die(1);
+    }
+#else  /* SGTTY */
+    int speed;
+    struct sgttyb sgttyb;
+
+    /*
+     * Put the tty in raw mode.
+     */
+    if (ioctl(fd, TIOCGETP, &sgttyb) < 0) {
+       syslog(LOG_ERR, "ioctl(TIOCGETP): %m");
+       die(1);
+    }
+
+    if (!restore_term)
+       initsgttyb = sgttyb;
+
+    sgttyb.sg_flags = RAW | ANYP;
+    speed = translate_speed(inspeed);
+    if (speed)
+       sgttyb.sg_ispeed = speed;
+
+    if (ioctl(fd, TIOCSETP, &sgttyb) < 0) {
+       syslog(LOG_ERR, "ioctl(TIOCSETP): %m");
+       die(1);
+    }
+#endif
+    restore_term = TRUE;
+}
+
+
+/*
+ * quit - Clean up state and exit.
+ */
+void 
+quit()
+{
+    die(0);
+}
+
+/*
+ * die - like quit, except we can specify an exit status.
+ */
+void
+die(status)
+    int status;
+{
+    cleanup(0, NULL);
+    syslog(LOG_INFO, "Exit.");
+    exit(status);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+/* ARGSUSED */
+void
+cleanup(status, arg)
+    int status;
+    caddr_t arg;
+{
+    if (fd != 0) {
+       /* drop dtr to hang up */
+       if (modem)
+           setdtr(fd, FALSE);
+
+       if (fcntl(fd, F_SETFL, initfdflags) < 0)
+           syslog(LOG_ERR, "fcntl(F_SETFL, fdflags): %m");
+
+       disestablish_ppp();
+
+       if (restore_term) {
+#ifndef SGTTY
+           if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
+               syslog(LOG_ERR, "tcsetattr: %m");
+#else
+           if (ioctl(fd, TIOCSETP, &initsgttyb) < 0)
+               syslog(LOG_ERR, "ioctl(TIOCSETP): %m");
+#endif
+       }
+
+       close(fd);
+       fd = 0;
+    }
+
+    if (pidfilename[0] != 0 && unlink(pidfilename) < 0) 
+       syslog(LOG_WARNING, "unable to unlink pid file: %m");
+    pidfilename[0] = 0;
+}
+
+
+static struct callout *callout = NULL;         /* Callout list */
+static struct timeval schedtime;               /* Time last timeout was set */
+
+/*
+ * timeout - Schedule a timeout.
+ *
+ * Note that this timeout takes the number of seconds, NOT hz (as in
+ * the kernel).
+ */
+void
+timeout(func, arg, time)
+    void (*func)();
+    caddr_t arg;
+    int time;
+{
+    struct itimerval itv;
+    struct callout *newp, **oldpp;
+  
+    MAINDEBUG((LOG_DEBUG, "Timeout %x:%x in %d seconds.",
+              (int) func, (int) arg, time));
+  
+    /*
+     * Allocate timeout.
+     */
+    if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) {
+       syslog(LOG_ERR, "Out of memory in timeout()!");
+       die(1);
+    }
+    newp->c_arg = arg;
+    newp->c_func = func;
+  
+    /*
+     * Find correct place to link it in and decrement its time by the
+     * amount of time used by preceding timeouts.
+     */
+    for (oldpp = &callout;
+        *oldpp && (*oldpp)->c_time <= time;
+        oldpp = &(*oldpp)->c_next)
+       time -= (*oldpp)->c_time;
+    newp->c_time = time;
+    newp->c_next = *oldpp;
+    if (*oldpp)
+       (*oldpp)->c_time -= time;
+    *oldpp = newp;
+  
+    /*
+     * If this is now the first callout then we have to set a new
+     * itimer.
+     */
+    if (callout == newp) {
+       itv.it_interval.tv_sec = itv.it_interval.tv_usec =
+           itv.it_value.tv_usec = 0;
+       itv.it_value.tv_sec = callout->c_time;
+       MAINDEBUG((LOG_DEBUG, "Setting itimer for %d seconds.",
+                  itv.it_value.tv_sec));
+       if (setitimer(ITIMER_REAL, &itv, NULL)) {
+           syslog(LOG_ERR, "setitimer(ITIMER_REAL): %m");
+           die(1);
+       }
+       if (gettimeofday(&schedtime, NULL)) {
+           syslog(LOG_ERR, "gettimeofday: %m");
+           die(1);
+       }
+    }
+}
+
+
+/*
+ * untimeout - Unschedule a timeout.
+ */
+void
+untimeout(func, arg)
+    void (*func)();
+    caddr_t arg;
+{
+    struct itimerval itv;
+    struct callout **copp, *freep;
+    int reschedule = 0;
+  
+    MAINDEBUG((LOG_DEBUG, "Untimeout %x:%x.", (int) func, (int) arg));
+  
+    /*
+     * If the first callout is unscheduled then we have to set a new
+     * itimer.
+     */
+    if (callout &&
+       callout->c_func == func &&
+       callout->c_arg == arg)
+       reschedule = 1;
+  
+    /*
+     * Find first matching timeout.  Add its time to the next timeouts
+     * time.
+     */
+    for (copp = &callout; *copp; copp = &(*copp)->c_next)
+       if ((*copp)->c_func == func &&
+           (*copp)->c_arg == arg) {
+           freep = *copp;
+           *copp = freep->c_next;
+           if (*copp)
+               (*copp)->c_time += freep->c_time;
+           (void) free((char *) freep);
+           break;
+       }
+  
+    if (reschedule) {
+       itv.it_interval.tv_sec = itv.it_interval.tv_usec =
+           itv.it_value.tv_usec = 0;
+       itv.it_value.tv_sec = callout ? callout->c_time : 0;
+       MAINDEBUG((LOG_DEBUG, "Setting itimer for %d seconds.",
+                  itv.it_value.tv_sec));
+       if (setitimer(ITIMER_REAL, &itv, NULL)) {
+           syslog(LOG_ERR, "setitimer(ITIMER_REAL): %m");
+           die(1);
+       }
+       if (gettimeofday(&schedtime, NULL)) {
+           syslog(LOG_ERR, "gettimeofday: %m");
+           die(1);
+       }
+    }
+}
+
+
+/*
+ * adjtimeout - Decrement the first timeout by the amount of time since
+ * it was scheduled.
+ */
+void
+adjtimeout()
+{
+    struct timeval tv;
+    int timediff;
+  
+    if (callout == NULL)
+       return;
+    /*
+     * Make sure that the clock hasn't been warped dramatically.
+     * Account for recently expired, but blocked timer by adding
+     * small fudge factor.
+     */
+    if (gettimeofday(&tv, NULL)) {
+       syslog(LOG_ERR, "gettimeofday: %m");
+       die(1);
+    }
+    timediff = tv.tv_sec - schedtime.tv_sec;
+    if (timediff < 0 ||
+       timediff > callout->c_time + 1)
+       return;
+  
+    callout->c_time -= timediff;       /* OK, Adjust time */
+}
+
+
+/*
+ * hup - Catch SIGHUP signal.
+ *
+ * Indicates that the physical layer has been disconnected.
+ */
+/*ARGSUSED*/
+static void
+hup(sig, code, scp, addr)
+    int sig, code;
+    struct sigcontext *scp;
+    char *addr;
+{
+    syslog(LOG_INFO, "Hangup (SIGHUP)");
+
+    hungup = 1;                        /* they hung up on us! */
+    persist = 0;               /* don't try to restart */
+    adjtimeout();              /* Adjust timeouts */
+    lcp_lowerdown(0);          /* Reset connection */
+    quit();                    /* and die */
+}
+
+
+/*
+ * term - Catch SIGTERM signal.
+ *
+ * Indicates that we should initiate a graceful disconnect and exit.
+ */
+/*ARGSUSED*/
+static void
+term(sig, code, scp, addr)
+    int sig, code;
+    struct sigcontext *scp;
+    char *addr;
+{
+    syslog(LOG_INFO, "Terminating link.");
+    persist = 0;               /* don't try to restart */
+    adjtimeout();              /* Adjust timeouts */
+    lcp_close(0);              /* Close connection */
+}
+
+
+/*
+ * intr - Catch SIGINT signal (DEL/^C).
+ *
+ * Indicates that we should initiate a graceful disconnect and exit.
+ */
+/*ARGSUSED*/
+static void
+intr(sig, code, scp, addr)
+    int sig, code;
+    struct sigcontext *scp;
+    char *addr;
+{
+    syslog(LOG_INFO, "Interrupt received: terminating link");
+    persist = 0;               /* don't try to restart */
+    adjtimeout();              /* Adjust timeouts */
+    lcp_close(0);              /* Close connection */
+}
+
+
+/*
+ * alrm - Catch SIGALRM signal.
+ *
+ * Indicates a timeout.
+ */
+/*ARGSUSED*/
+static void
+alrm(sig, code, scp, addr)
+    int sig, code;
+    struct sigcontext *scp;
+    char *addr;
+{
+    struct itimerval itv;
+    struct callout *freep;
+
+    MAINDEBUG((LOG_DEBUG, "Alarm"));
+
+    /*
+     * Call and free first scheduled timeout and any that were scheduled
+     * for the same time.
+     */
+    while (callout) {
+       freep = callout;        /* Remove entry before calling */
+       callout = freep->c_next;
+       (*freep->c_func)(freep->c_arg);
+       (void) free((char *) freep);
+       if (callout && callout->c_time)
+           break;
+    }
+  
+    /*
+     * Set a new itimer if there are more timeouts scheduled.
+     */
+    if (callout) {
+       itv.it_interval.tv_sec = itv.it_interval.tv_usec = 0;
+       itv.it_value.tv_usec = 0;
+       itv.it_value.tv_sec = callout->c_time;
+       MAINDEBUG((LOG_DEBUG, "Setting itimer for %d seconds.",
+                  itv.it_value.tv_sec));
+       if (setitimer(ITIMER_REAL, &itv, NULL)) {
+           syslog(LOG_ERR, "setitimer(ITIMER_REAL): %m");
+           die(1);
+       }
+       if (gettimeofday(&schedtime, NULL)) {
+           syslog(LOG_ERR, "gettimeofday: %m");
+           die(1);
+       }
+    }
+}
+
+
+/*
+ * io - Catch SIGIO signal.
+ *
+ * Indicates that incoming data is available.
+ */
+/*ARGSUSED*/
+static void
+io(sig, code, scp, addr)
+    int sig, code;
+    struct sigcontext *scp;
+    char *addr;
+{
+    int len, i;
+    u_char *p;
+    u_short protocol;
+    fd_set fdset;
+    struct timeval notime;
+    int ready;
+
+    MAINDEBUG((LOG_DEBUG, "IO signal received"));
+    adjtimeout();              /* Adjust timeouts */
+
+    /* we do this to see if the SIGIO handler is being invoked for input */
+    /* ready, or for the socket buffer hitting the low-water mark. */
+
+    notime.tv_sec = 0;
+    notime.tv_usec = 0;
+    FD_ZERO(&fdset);
+    FD_SET(fd, &fdset);
+  
+    if ((ready = select(32, &fdset, (fd_set *) NULL, (fd_set *) NULL,
+                     &notime)) == -1) {
+       syslog(LOG_ERR, "Error in io() select: %m");
+       die(1);
+    }
+    
+    if (ready == 0) {
+       MAINDEBUG((LOG_DEBUG, "IO non-input ready SIGIO occured."));
+       return;
+    }
+
+    /* Yup, this is for real */
+    for (;;) {                 /* Read all available packets */
+       p = inpacket_buf;       /* point to beginning of packet buffer */
+
+       len = read_packet(inpacket_buf);
+       if (len < 0)
+           return;
+
+       if (len == 0) {
+           syslog(LOG_ERR, "End of file on fd!");
+           die(1);
+       }
+
+       if (len < DLLHEADERLEN) {
+           MAINDEBUG((LOG_INFO, "io(): Received short packet."));
+           return;
+       }
+
+       p += 2;                         /* Skip address and control */
+       GETSHORT(protocol, p);
+       len -= DLLHEADERLEN;
+
+       /*
+        * Toss all non-LCP packets unless LCP is OPEN.
+        */
+       if (protocol != LCP && lcp_fsm[0].state != OPENED) {
+           MAINDEBUG((LOG_INFO,
+                      "io(): Received non-LCP packet when LCP not open."));
+           if (debug)
+               dumpbuffer(inpacket_buf, len + DLLHEADERLEN, LOG_INFO);
+           return;
+       }
+
+       /*
+        * Upcall the proper protocol input routine.
+        */
+       for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++)
+           if (prottbl[i].protocol == protocol) {
+               (*prottbl[i].input)(0, p, len);
+               break;
+           }
+
+       if (i == sizeof (prottbl) / sizeof (struct protent)) {
+           syslog(LOG_WARNING, "input: Unknown protocol (%x) received!",
+                  protocol);
+           lcp_sprotrej(0, p - DLLHEADERLEN, len + DLLHEADERLEN);
+       }
+    }
+}
+
+/*
+ * demuxprotrej - Demultiplex a Protocol-Reject.
+ */
+void
+demuxprotrej(unit, protocol)
+    int unit;
+    u_short protocol;
+{
+    int i;
+
+    /*
+     * Upcall the proper Protocol-Reject routine.
+     */
+    for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++)
+       if (prottbl[i].protocol == protocol) {
+           (*prottbl[i].protrej)(unit);
+           return;
+       }
+
+    syslog(LOG_WARNING,
+          "demuxprotrej: Unrecognized Protocol-Reject for protocol %d!",
+          protocol);
+}
+
+
+/*
+ * incdebug - Catch SIGUSR1 signal.
+ *
+ * Increment debug flag.
+ */
+/*ARGSUSED*/
+static void
+incdebug(sig)
+    int sig;
+{
+    syslog(LOG_INFO, "Debug turned ON, Level %d", debug);
+    setlogmask(LOG_UPTO(LOG_DEBUG));
+    debug++;
+}
+
+
+/*
+ * nodebug - Catch SIGUSR2 signal.
+ *
+ * Turn off debugging.
+ */
+/*ARGSUSED*/
+static void
+nodebug(sig)
+    int sig;
+{
+    setlogmask(LOG_UPTO(LOG_WARNING));
+    debug = 0;
+}
+
+
+/*
+ * set_up_connection - run a program to initialize the serial connector
+ */
+int
+set_up_connection(program, in, out)
+    char *program;
+    int in, out;
+{
+    int pid;
+    int status;
+    sigset_t mask;
+
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGINT);
+    sigaddset(&mask, SIGHUP);
+    sigprocmask(SIG_BLOCK, &mask, &mask);
+
+    pid = fork();
+  
+    if (pid < 0) {
+       syslog(LOG_ERR, "fork: %m");
+       die(1);
+    }
+  
+    if (pid == 0) {
+       setreuid(getuid(), getuid());
+       setregid(getgid(), getgid());
+       sigprocmask(SIG_SETMASK, &mask, NULL);
+       dup2(in, 0);
+       dup2(out, 1);
+       execl("/bin/sh", "sh", "-c", program, (char *)0);
+       syslog(LOG_ERR, "could not exec /bin/sh: %m");
+       _exit(99);
+       /* NOTREACHED */
+    }
+
+    while (waitpid(pid, &status, 0) != pid) {
+       if (errno == EINTR)
+           continue;
+       syslog(LOG_ERR, "waiting for connection process: %m");
+       die(1);
+    }
+    sigprocmask(SIG_SETMASK, &mask, NULL);
+
+    return (status == 0 ? 0 : -1);
+}
+
+
+/*
+ * Return user specified netmask. A value of zero means no netmask has
+ * been set. 
+ */
+/* ARGSUSED */
+u_long
+GetMask(addr)
+    u_long addr;
+{
+    return(netmask);
+}
+
+/*
+ * dumpbuffer - print contents of a buffer in hex to standard output.
+ */
+void
+dumpbuffer(buffer, size, level)
+    unsigned char *buffer;
+    int size;
+    int level;
+{
+    register int i;
+    char line[256], *p;
+
+    printf("%d bytes:\n", size);
+    while (size > 0)
+    {
+       p = line;
+       sprintf(p, "%08lx: ", buffer);
+       p += 10;
+               
+       for (i = 0; i < 8; i++, p += 3)
+           if (size - i <= 0)
+               sprintf(p, "xx ");
+           else
+               sprintf(p, "%02x ", buffer[i]);
+
+       for (i = 0; i < 8; i++)
+           if (size - i <= 0)
+               *p++ = 'x';
+           else
+               *p++ = (' ' <= buffer[i] && buffer[i] <= '~') ?
+                   buffer[i] : '.';
+
+       *p++ = 0;
+       buffer += 8;
+       size -= 8;
+
+/*     syslog(level, "%s\n", line); */
+       printf("%s\n", line);
+       fflush(stdout);
+    }
+}
+
+
+/*
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+setdtr(fd, on)
+int fd, on;
+{
+    int modembits = TIOCM_DTR;
+
+    ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
+}
+
+#include <varargs.h>
+
+char line[256];
+char *p;
+
+logf(level, fmt, va_alist)
+int level;
+char *fmt;
+va_dcl
+{
+    va_list pvar;
+    char buf[256];
+
+    va_start(pvar);
+    vsprintf(buf, fmt, pvar);
+    va_end(pvar);
+
+    p = line + strlen(line);
+    strcat(p, buf);
+
+    if (buf[strlen(buf)-1] == '\n') {
+       syslog(level, "%s", line);
+       line[0] = 0;
+    }
+}
+
+void
+novm(msg)
+    char *msg;
+{
+    syslog(LOG_ERR, "Virtual memory exhausted allocating %s\n", msg);
+    die(1);
+}
diff --git a/pppd/md5.c b/pppd/md5.c
new file mode 100644 (file)
index 0000000..94bb2fe
--- /dev/null
@@ -0,0 +1,298 @@
+
+
+/*
+ ***********************************************************************
+ ** md5.c -- the source code for MD5 routines                         **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm              **
+ ** Created: 2/17/90 RLR                                              **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.  **
+ **                                                                   **
+ ** License to copy and use this software is granted provided that    **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message-     **
+ ** Digest Algorithm" in all material mentioning or referencing this  **
+ ** software or this function.                                        **
+ **                                                                   **
+ ** License is also granted to make and use derivative works          **
+ ** provided that such works are identified as "derived from the RSA  **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all          **
+ ** material mentioning or referencing the derived work.              **
+ **                                                                   **
+ ** RSA Data Security, Inc. makes no representations concerning       **
+ ** either the merchantability of this software or the suitability    **
+ ** of this software for any particular purpose.  It is provided "as  **
+ ** is" without express or implied warranty of any kind.              **
+ **                                                                   **
+ ** These notices must be retained in any copies of any part of this  **
+ ** documentation and/or software.                                    **
+ ***********************************************************************
+ */
+
+#include "md5.h"
+
+/*
+ ***********************************************************************
+ **  Message-digest routines:                                         **
+ **  To form the message digest for a message M                       **
+ **    (1) Initialize a context buffer mdContext using MD5Init        **
+ **    (2) Call MD5Update on mdContext and M                          **
+ **    (3) Call MD5Final on mdContext                                 **
+ **  The message digest is now in mdContext->digest[0...15]           **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform ();
+
+static unsigned char PADDING[64] = {
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+  {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) \
+  {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) \
+  {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) \
+  {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+
+/* The routine MD5Init initializes the message-digest context
+   mdContext. All fields are set to zero.
+ */
+void MD5Init (mdContext)
+MD5_CTX *mdContext;
+{
+  mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+  /* Load magic initialization constants.
+   */
+  mdContext->buf[0] = (UINT4)0x67452301;
+  mdContext->buf[1] = (UINT4)0xefcdab89;
+  mdContext->buf[2] = (UINT4)0x98badcfe;
+  mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+/* The routine MD5Update updates the message-digest context to
+   account for the presence of each of the characters inBuf[0..inLen-1]
+   in the message whose digest is being computed.
+ */
+void MD5Update (mdContext, inBuf, inLen)
+MD5_CTX *mdContext;
+unsigned char *inBuf;
+unsigned int inLen;
+{
+  UINT4 in[16];
+  int mdi;
+  unsigned int i, ii;
+
+  /* compute number of bytes mod 64 */
+  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+  /* update number of bits */
+  if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+    mdContext->i[1]++;
+  mdContext->i[0] += ((UINT4)inLen << 3);
+  mdContext->i[1] += ((UINT4)inLen >> 29);
+
+  while (inLen--) {
+    /* add new character to buffer, increment mdi */
+    mdContext->in[mdi++] = *inBuf++;
+
+    /* transform if necessary */
+    if (mdi == 0x40) {
+      for (i = 0, ii = 0; i < 16; i++, ii += 4)
+        in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+                (((UINT4)mdContext->in[ii+2]) << 16) |
+                (((UINT4)mdContext->in[ii+1]) << 8) |
+                ((UINT4)mdContext->in[ii]);
+      Transform (mdContext->buf, in);
+      mdi = 0;
+    }
+  }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+   ends with the desired message digest in mdContext->digest[0...15].
+ */
+void MD5Final (mdContext)
+MD5_CTX *mdContext;
+{
+  UINT4 in[16];
+  int mdi;
+  unsigned int i, ii;
+  unsigned int padLen;
+
+  /* save number of bits */
+  in[14] = mdContext->i[0];
+  in[15] = mdContext->i[1];
+
+  /* compute number of bytes mod 64 */
+  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+  /* pad out to 56 mod 64 */
+  padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+  MD5Update (mdContext, PADDING, padLen);
+
+  /* append length in bits and transform */
+  for (i = 0, ii = 0; i < 14; i++, ii += 4)
+    in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+            (((UINT4)mdContext->in[ii+2]) << 16) |
+            (((UINT4)mdContext->in[ii+1]) << 8) |
+            ((UINT4)mdContext->in[ii]);
+  Transform (mdContext->buf, in);
+
+  /* store buffer in digest */
+  for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+    mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+    mdContext->digest[ii+1] =
+      (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+    mdContext->digest[ii+2] =
+      (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+    mdContext->digest[ii+3] =
+      (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+  }
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void Transform (buf, in)
+UINT4 *buf;
+UINT4 *in;
+{
+  UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+  /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+  FF ( a, b, c, d, in[ 0], S11, 3614090360U); /* 1 */
+  FF ( d, a, b, c, in[ 1], S12, 3905402710U); /* 2 */
+  FF ( c, d, a, b, in[ 2], S13,  606105819U); /* 3 */
+  FF ( b, c, d, a, in[ 3], S14, 3250441966U); /* 4 */
+  FF ( a, b, c, d, in[ 4], S11, 4118548399U); /* 5 */
+  FF ( d, a, b, c, in[ 5], S12, 1200080426U); /* 6 */
+  FF ( c, d, a, b, in[ 6], S13, 2821735955U); /* 7 */
+  FF ( b, c, d, a, in[ 7], S14, 4249261313U); /* 8 */
+  FF ( a, b, c, d, in[ 8], S11, 1770035416U); /* 9 */
+  FF ( d, a, b, c, in[ 9], S12, 2336552879U); /* 10 */
+  FF ( c, d, a, b, in[10], S13, 4294925233U); /* 11 */
+  FF ( b, c, d, a, in[11], S14, 2304563134U); /* 12 */
+  FF ( a, b, c, d, in[12], S11, 1804603682U); /* 13 */
+  FF ( d, a, b, c, in[13], S12, 4254626195U); /* 14 */
+  FF ( c, d, a, b, in[14], S13, 2792965006U); /* 15 */
+  FF ( b, c, d, a, in[15], S14, 1236535329U); /* 16 */
+
+  /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+  GG ( a, b, c, d, in[ 1], S21, 4129170786U); /* 17 */
+  GG ( d, a, b, c, in[ 6], S22, 3225465664U); /* 18 */
+  GG ( c, d, a, b, in[11], S23,  643717713U); /* 19 */
+  GG ( b, c, d, a, in[ 0], S24, 3921069994U); /* 20 */
+  GG ( a, b, c, d, in[ 5], S21, 3593408605U); /* 21 */
+  GG ( d, a, b, c, in[10], S22,   38016083U); /* 22 */
+  GG ( c, d, a, b, in[15], S23, 3634488961U); /* 23 */
+  GG ( b, c, d, a, in[ 4], S24, 3889429448U); /* 24 */
+  GG ( a, b, c, d, in[ 9], S21,  568446438U); /* 25 */
+  GG ( d, a, b, c, in[14], S22, 3275163606U); /* 26 */
+  GG ( c, d, a, b, in[ 3], S23, 4107603335U); /* 27 */
+  GG ( b, c, d, a, in[ 8], S24, 1163531501U); /* 28 */
+  GG ( a, b, c, d, in[13], S21, 2850285829U); /* 29 */
+  GG ( d, a, b, c, in[ 2], S22, 4243563512U); /* 30 */
+  GG ( c, d, a, b, in[ 7], S23, 1735328473U); /* 31 */
+  GG ( b, c, d, a, in[12], S24, 2368359562U); /* 32 */
+
+  /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+  HH ( a, b, c, d, in[ 5], S31, 4294588738U); /* 33 */
+  HH ( d, a, b, c, in[ 8], S32, 2272392833U); /* 34 */
+  HH ( c, d, a, b, in[11], S33, 1839030562U); /* 35 */
+  HH ( b, c, d, a, in[14], S34, 4259657740U); /* 36 */
+  HH ( a, b, c, d, in[ 1], S31, 2763975236U); /* 37 */
+  HH ( d, a, b, c, in[ 4], S32, 1272893353U); /* 38 */
+  HH ( c, d, a, b, in[ 7], S33, 4139469664U); /* 39 */
+  HH ( b, c, d, a, in[10], S34, 3200236656U); /* 40 */
+  HH ( a, b, c, d, in[13], S31,  681279174U); /* 41 */
+  HH ( d, a, b, c, in[ 0], S32, 3936430074U); /* 42 */
+  HH ( c, d, a, b, in[ 3], S33, 3572445317U); /* 43 */
+  HH ( b, c, d, a, in[ 6], S34,   76029189U); /* 44 */
+  HH ( a, b, c, d, in[ 9], S31, 3654602809U); /* 45 */
+  HH ( d, a, b, c, in[12], S32, 3873151461U); /* 46 */
+  HH ( c, d, a, b, in[15], S33,  530742520U); /* 47 */
+  HH ( b, c, d, a, in[ 2], S34, 3299628645U); /* 48 */
+
+  /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+  II ( a, b, c, d, in[ 0], S41, 4096336452U); /* 49 */
+  II ( d, a, b, c, in[ 7], S42, 1126891415U); /* 50 */
+  II ( c, d, a, b, in[14], S43, 2878612391U); /* 51 */
+  II ( b, c, d, a, in[ 5], S44, 4237533241U); /* 52 */
+  II ( a, b, c, d, in[12], S41, 1700485571U); /* 53 */
+  II ( d, a, b, c, in[ 3], S42, 2399980690U); /* 54 */
+  II ( c, d, a, b, in[10], S43, 4293915773U); /* 55 */
+  II ( b, c, d, a, in[ 1], S44, 2240044497U); /* 56 */
+  II ( a, b, c, d, in[ 8], S41, 1873313359U); /* 57 */
+  II ( d, a, b, c, in[15], S42, 4264355552U); /* 58 */
+  II ( c, d, a, b, in[ 6], S43, 2734768916U); /* 59 */
+  II ( b, c, d, a, in[13], S44, 1309151649U); /* 60 */
+  II ( a, b, c, d, in[ 4], S41, 4149444226U); /* 61 */
+  II ( d, a, b, c, in[11], S42, 3174756917U); /* 62 */
+  II ( c, d, a, b, in[ 2], S43,  718787259U); /* 63 */
+  II ( b, c, d, a, in[ 9], S44, 3951481745U); /* 64 */
+
+  buf[0] += a;
+  buf[1] += b;
+  buf[2] += c;
+  buf[3] += d;
+}
+
+/*
+ ***********************************************************************
+ ** End of md5.c                                                      **
+ ******************************** (cut) ********************************
+ */
diff --git a/pppd/md5.h b/pppd/md5.h
new file mode 100644 (file)
index 0000000..535b18d
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5                    **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm              **
+ ** Created: 2/17/90 RLR                                              **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version               **
+ ** Revised (for MD5): RLR 4/27/91                                    **
+ **   -- G modified to have y&~z instead of y&z                       **
+ **   -- FF, GG, HH modified to add in last register done             **
+ **   -- Access pattern: round 2 works mod 5, round 3 works mod 3     **
+ **   -- distinct additive constant for each step                     **
+ **   -- round 4 added, working mod 7                                 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.  **
+ **                                                                   **
+ ** License to copy and use this software is granted provided that    **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message-     **
+ ** Digest Algorithm" in all material mentioning or referencing this  **
+ ** software or this function.                                        **
+ **                                                                   **
+ ** License is also granted to make and use derivative works          **
+ ** provided that such works are identified as "derived from the RSA  **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all          **
+ ** material mentioning or referencing the derived work.              **
+ **                                                                   **
+ ** RSA Data Security, Inc. makes no representations concerning       **
+ ** either the merchantability of this software or the suitability    **
+ ** of this software for any particular purpose.  It is provided "as  **
+ ** is" without express or implied warranty of any kind.              **
+ **                                                                   **
+ ** These notices must be retained in any copies of any part of this  **
+ ** documentation and/or software.                                    **
+ ***********************************************************************
+ */
+
+#ifndef __MD5_INCLUDE__
+
+/* typedef a 32-bit type */
+typedef unsigned long int UINT4;
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+  UINT4 i[2];                   /* number of _bits_ handled mod 2^64 */
+  UINT4 buf[4];                                    /* scratch buffer */
+  unsigned char in[64];                              /* input buffer */
+  unsigned char digest[16];     /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5Init ();
+void MD5Update ();
+void MD5Final ();
+
+#define __MD5_INCLUDE__
+#endif /* __MD5_INCLUDE__ */
diff --git a/pppd/options.c b/pppd/options.c
new file mode 100644 (file)
index 0000000..2661fa4
--- /dev/null
@@ -0,0 +1,1153 @@
+/*
+ * options.c - handles option processing for PPP.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: options.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <syslog.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "ppp.h"
+#include "pppd.h"
+#include "pathnames.h"
+#include "patchlevel.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+
+#define FALSE  0
+#define TRUE   1
+
+
+/*
+ * Prototypes
+ */
+static int setdebug __ARGS((void));
+static int setpassive __ARGS((void));
+static int setsilent __ARGS((void));
+static int noopt __ARGS((void));
+static int setnovj __ARGS((void));
+static int reqpap __ARGS((void));
+static int nopap __ARGS((void));
+static int setupapfile __ARGS((char **));
+static int nochap __ARGS((void));
+static int reqchap __ARGS((void));
+static int setspeed __ARGS((char *));
+static int noaccomp __ARGS((void));
+static int noasyncmap __ARGS((void));
+static int noipaddr __ARGS((void));
+static int nomagicnumber __ARGS((void));
+static int setasyncmap __ARGS((char **));
+static int setmru __ARGS((char **));
+static int nomru __ARGS((void));
+static int nopcomp __ARGS((void));
+static int setconnector __ARGS((char **));
+static int setdomain __ARGS((char **));
+static int setnetmask __ARGS((char **));
+static int setcrtscts __ARGS((void));
+static int setnodetach __ARGS((void));
+static int setmodem __ARGS((void));
+static int setlocal __ARGS((void));
+static int setname __ARGS((char **));
+static int setuser __ARGS((char **));
+static int setremote __ARGS((char **));
+static int setauth __ARGS((void));
+static int readfile __ARGS((char **));
+static int setdefaultroute __ARGS((void));
+static int setproxyarp __ARGS((void));
+static int setpersist __ARGS((void));
+static int setdologin __ARGS((void));
+static int setusehostname __ARGS((void));
+static int setlcptimeout __ARGS((char **));
+static int setlcpterm __ARGS((char **));
+static int setlcpconf __ARGS((char **));
+static int setlcpfails __ARGS((char **));
+static int setipcptimeout __ARGS((char **));
+static int setipcpterm __ARGS((char **));
+static int setipcpconf __ARGS((char **));
+static int setipcpfails __ARGS((char **));
+static int setpaptimeout __ARGS((char **));
+static int setpapreqs __ARGS((char **));
+static int setchaptimeout __ARGS((char **));
+static int setchapchal __ARGS((char **));
+static int setchapintv __ARGS((char **));
+
+static int number_option __ARGS((char *, long *, int));
+
+
+/*
+ * Option variables
+ */
+extern char *progname;
+extern int debug;
+extern int modem;
+extern int crtscts;
+extern int nodetach;
+extern char *connector;
+extern int inspeed;
+extern char devname[];
+extern int default_device;
+extern u_long netmask;
+extern int detach;
+extern char user[];
+extern char passwd[];
+extern int auth_required;
+extern int proxyarp;
+extern int persist;
+extern int uselogin;
+extern char our_name[];
+extern char remote_name[];
+int usehostname;
+
+/*
+ * Valid arguments.
+ */
+static struct cmd {
+    char *cmd_name;
+    int num_args;
+    int (*cmd_func)();
+} cmds[] = {
+    "-all", 0, noopt,          /* Don't request/allow any options */
+    "-ac", 0, noaccomp,                /* Disable Address/Control compress */
+    "-am", 0, noasyncmap,      /* Disable asyncmap negotiation */
+    "-as", 1, setasyncmap,     /* set the desired async map */
+    "-d", 0, setdebug,         /* Increase debugging level */
+    "-detach", 0, setnodetach, /* don't fork */
+    "-ip", 0, noipaddr,                /* Disable IP address negotiation */
+    "-mn", 0, nomagicnumber,   /* Disable magic number negotiation */
+    "-mru", 0, nomru,          /* Disable mru negotiation */
+    "-p", 0, setpassive,       /* Set passive mode */
+    "-pc", 0, nopcomp,         /* Disable protocol field compress */
+    "+ua", 1, setupapfile,     /* Get PAP user and password from file */
+    "+pap", 0, reqpap,         /* Require PAP auth from peer */
+    "-pap", 0, nopap,          /* Don't allow UPAP authentication with peer */
+    "+chap", 0, reqchap,       /* Require CHAP authentication from peer */
+    "-chap", 0, nochap,                /* Don't allow CHAP authentication with peer */
+    "-vj", 0, setnovj,         /* disable VJ compression */
+    "asyncmap", 1, setasyncmap,        /* set the desired async map */
+    "connect", 1, setconnector,        /* A program to set up a connection */
+    "crtscts", 0, setcrtscts,  /* set h/w flow control */
+    "debug", 0, setdebug,      /* Increase debugging level */
+    "domain", 1, setdomain,    /* Add given domain name to hostname*/
+    "mru", 1, setmru,          /* Set MRU value for negotiation */
+    "netmask", 1, setnetmask,  /* set netmask */
+    "passive", 0, setpassive,  /* Set passive mode */
+    "silent", 0, setsilent,    /* Set silent mode */
+    "modem", 0, setmodem,      /* Use modem control lines */
+    "local", 0, setlocal,      /* Don't use modem control lines */
+    "name", 1, setname,                /* Set local name for authentication */
+    "user", 1, setuser,                /* Set username for PAP auth with peer */
+    "usehostname", 0, setusehostname,  /* Must use hostname for auth. */
+    "remotename", 1, setremote,        /* Set remote name for authentication */
+    "auth", 0, setauth,                /* Require authentication from peer */
+    "file", 1, readfile,       /* Take options from a file */
+    "defaultroute", 0, setdefaultroute,        /* Add default route */
+    "proxyarp", 0, setproxyarp,        /* Add proxy ARP entry */
+    "persist", 0, setpersist,  /* Keep on reopening connection after close */
+    "login", 0, setdologin,    /* Use system password database for UPAP */
+    "lcp-restart", 1, setlcptimeout,   /* Set timeout for LCP */
+    "lcp-max-terminate", 1, setlcpterm,        /* Set max #xmits for term-reqs */
+    "lcp-max-configure", 1, setlcpconf,        /* Set max #xmits for conf-reqs */
+    "lcp-max-failure", 1, setlcpfails, /* Set max #conf-naks for LCP */
+    "ipcp-restart", 1, setipcptimeout, /* Set timeout for IPCP */
+    "ipcp-max-terminate", 1, setipcpterm, /* Set max #xmits for term-reqs */
+    "ipcp-max-configure", 1, setipcpconf, /* Set max #xmits for conf-reqs */
+    "ipcp-max-failure", 1, setipcpfails,  /* Set max #conf-naks for IPCP */
+    "pap-restart", 1, setpaptimeout,   /* Set timeout for UPAP */
+    "pap-max-authreq", 1, setpapreqs,  /* Set max #xmits for auth-reqs */
+    "chap-restart", 1, setchaptimeout, /* Set timeout for CHAP */
+    "chap-max-challenge", 1, setchapchal, /* Set max #xmits for challenge */
+    "chap-interval", 1, setchapintv,   /* Set interval for rechallenge */
+    NULL
+};
+
+
+static char *usage_string = "\
+pppd version %s patch level %d\n\
+Usage: %s [ arguments ], where arguments are:\n\
+       <device>        Communicate over the named device\n\
+       <speed>         Set the baud rate to <speed>\n\
+       <loc>:<rem>     Set the local and/or remote interface IP\n\
+                       addresses.  Either one may be omitted.\n\
+       asyncmap <n>    Set the desired async map to hex <n>\n\
+       auth            Require authentication from peer\n\
+        connect <p>     Invoke shell command <p> to set up the serial line\n\
+       crtscts         Use hardware RTS/CTS flow control\n\
+       defaultroute    Add default route through interface\n\
+       file <f>        Take options from file <f>\n\
+       modem           Use modem control lines\n\
+       mru <n>         Set MRU value to <n> for negotiation\n\
+       netmask <n>     Set interface netmask to <n>\n\
+See pppd(8) for more options.\n\
+";
+
+/*
+Options omitted:
+       -all            Don't request/allow any options\n\
+       -ac             Disable Address/Control compression\n\
+       -am             Disable asyncmap negotiation\n\
+       -as <n>         Set the desired async map to hex <n>\n\
+       -d              Increase debugging level\n\
+       -detach         Don't fork to background\n\
+       -ip             Disable IP address negotiation\n\
+       -mn             Disable magic number negotiation\n\
+       -mru            Disable mru negotiation\n\
+       -p              Set passive mode\n\
+       -pc             Disable protocol field compression\n\
+       +ua <f>         Get username and password for authenticating\n\
+                       with peer using PAP from file <f>\n\
+       +pap            Require PAP authentication from peer\n\
+       -pap            Don't agree to authenticating with peer using PAP\n\
+       +chap           Require CHAP authentication from peer\n\
+       -chap           Don't agree to authenticating with peer using CHAP\n\
+        -vj             disable VJ compression\n\
+       -auth           Don't agree to authenticate with peer\n\
+       debug           Increase debugging level\n\
+        domain <d>      Append domain name <d> to hostname for authentication\n\
+       passive         Set passive mode\n\
+       local           Don't use modem control lines\n\
+       proxyarp        Add proxy ARP entry\n\
+*/
+
+
+/*
+ * parse_args - parse a string of arguments, from the command
+ * line or from a file.
+ */
+int
+parse_args(argc, argv)
+    int argc;
+    char **argv;
+{
+    char *arg, *val;
+    struct cmd *cmdp;
+
+    while (argc > 0) {
+       arg = *argv++;
+       --argc;
+
+       /*
+        * First see if it's a command.
+        */
+       for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+           if (!strcmp(arg, cmdp->cmd_name))
+               break;
+
+       if (cmdp->cmd_name != NULL) {
+           if (argc < cmdp->num_args) {
+               fprintf(stderr, "Too few parameters for command %s\n", arg);
+               return 0;
+           }
+           if (!(*cmdp->cmd_func)(argv))
+               return 0;
+           argc -= cmdp->num_args;
+           argv += cmdp->num_args;
+
+       } else {
+           /*
+            * Maybe a tty name, speed or IP address?
+            */
+           if (!setdevname(arg) && !setspeed(arg) && !setipaddr(arg)) {
+               fprintf(stderr, "%s: unrecognized command\n", arg);
+               usage();
+               return 0;
+           }
+       }
+    }
+    return 1;
+}
+
+/*
+ * usage - print out a message telling how to use the program.
+ */
+usage()
+{
+    fprintf(stderr, usage_string, VERSION, PATCHLEVEL, progname);
+}
+
+/*
+ * options_from_file - Read a string of options from a file,
+ * and interpret them.
+ */
+int
+options_from_file(filename)
+    char *filename;
+{
+    FILE *f;
+    int i, newline;
+    struct cmd *cmdp;
+    char *argv[MAXARGS];
+    char args[MAXARGS][MAXWORDLEN];
+    char cmd[MAXWORDLEN];
+
+    if ((f = fopen(filename, "r")) == NULL) {
+       if (errno == ENOENT)
+           return 1;
+       perror(filename);
+       exit(1);
+    }
+    while (getword(f, cmd, &newline, filename)) {
+       /*
+        * First see if it's a command.
+        */
+       for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+           if (!strcmp(cmd, cmdp->cmd_name))
+               break;
+
+       if (cmdp->cmd_name != NULL) {
+           for (i = 0; i < cmdp->num_args; ++i) {
+               if (!getword(f, args[i], &newline, filename)) {
+                   fprintf(stderr,
+                           "In file %s: too few parameters for command %s\n",
+                           filename, cmd);
+                   fclose(f);
+                   return 0;
+               }
+               argv[i] = args[i];
+           }
+           if (!(*cmdp->cmd_func)(argv)) {
+               fclose(f);
+               return 0;
+           }
+
+       } else {
+           /*
+            * Maybe a tty name, speed or IP address?
+            */
+           if (!setdevname(cmd) && !setspeed(cmd) && !setipaddr(cmd)) {
+               fprintf(stderr, "In file %s: unrecognized command %s\n",
+                       filename, cmd);
+               fclose(f);
+               return 0;
+           }
+       }
+    }
+    return 1;
+}
+
+/*
+ * options_from_user - See if the use has a ~/.ppprc file,
+ * and if so, interpret options from it.
+ */
+int
+options_from_user()
+{
+    char *user, *path, *file;
+    int ret;
+
+    if ((user = getenv("HOME")) == NULL)
+       return;
+    file = "/.ppprc";
+    path = malloc(strlen(user) + strlen(file) + 1);
+    if (path == NULL)
+       novm("init file name");
+    strcpy(path, user);
+    strcat(path, file);
+    ret = options_from_file(path);
+    free(path);
+    return ret;
+}
+
+/*
+ * Read a word from a file.
+ * Words are delimited by white-space or by quotes (").
+ * Quotes, white-space and \ may be escaped with \.
+ * \<newline> is ignored.
+ */
+int
+getword(f, word, newlinep, filename)
+    FILE *f;
+    char *word;
+    int *newlinep;
+    char *filename;
+{
+    int c, len, escape;
+    int quoted;
+
+    *newlinep = 0;
+    len = 0;
+    escape = 0;
+    quoted = 0;
+
+    /*
+     * First skip white-space and comments
+     */
+    while ((c = getc(f)) != EOF) {
+       if (c == '\\') {
+           /*
+            * \<newline> is ignored; \ followed by anything else
+            * starts a word.
+            */
+           if ((c = getc(f)) == '\n')
+               continue;
+           word[len++] = '\\';
+           escape = 1;
+           break;
+       }
+       if (c == '\n')
+           *newlinep = 1;      /* next word starts a line */
+       else if (c == '#') {
+           /* comment - ignore until EOF or \n */
+           while ((c = getc(f)) != EOF && c != '\n')
+               ;
+           if (c == EOF)
+               break;
+           *newlinep = 1;
+       } else if (!isspace(c))
+           break;
+    }
+
+    /*
+     * End of file or error - fail
+     */
+    if (c == EOF) {
+       if (ferror(f)) {
+           perror(filename);
+           die(1);
+       }
+       return 0;
+    }
+
+    for (;;) {
+       /*
+        * Is this character escaped by \ ?
+        */
+       if (escape) {
+           if (c == '\n')
+               --len;                  /* ignore \<newline> */
+           else if (c == '"' || isspace(c) || c == '\\')
+               word[len-1] = c;        /* put special char in word */
+           else {
+               if (len < MAXWORDLEN-1)
+                   word[len] = c;
+               ++len;
+           }
+           escape = 0;
+       } else if (c == '"') {
+           quoted = !quoted;
+       } else if (!quoted && (isspace(c) || c == '#')) {
+           ungetc(c, f);
+           break;
+       } else {
+           if (len < MAXWORDLEN-1)
+               word[len] = c;
+           ++len;
+           if (c == '\\')
+               quoted = 1;
+       }
+       if ((c = getc(f)) == EOF)
+           break;
+    }
+
+    if (ferror(f)) {
+       perror(filename);
+       die(1);
+    }
+
+    if (len >= MAXWORDLEN) {
+       word[MAXWORDLEN-1] = 0;
+       fprintf(stderr, "%s: warning: word in file %s too long (%.20s...)\n",
+               progname, filename, word);
+    } else
+       word[len] = 0;
+
+    return 1;
+}
+
+/*
+ * number_option - parse a numeric parameter for an option
+ */
+static int
+number_option(str, valp, base)
+    char *str;
+    long *valp;
+    int base;
+{
+    char *ptr;
+
+    *valp = strtol(str, &ptr, base);
+    if (ptr == str) {
+       fprintf(stderr, "%s: invalid number: %s\n", progname, str);
+       return 0;
+    }
+    return 1;
+}
+
+
+/*
+ * int_option - like number_option, but valp is int *,
+ * the base is assumed to be 0, and *valp is not changed
+ * if there is an error.
+ */
+static int
+int_option(str, valp)
+    char *str;
+    int *valp;
+{
+    long v;
+
+    if (!number_option(str, &v, 0))
+       return 0;
+    *valp = (int) v;
+    return 1;
+}
+
+
+/*
+ * The following procedures execute commands.
+ */
+
+/*
+ * readfile - take commands from a file.
+ */
+static int
+readfile(argv)
+    char **argv;
+{
+    return options_from_file(*argv);
+}
+
+/*
+ * setdebug - Set debug (command line argument).
+ */
+static int
+setdebug()
+{
+    debug++;
+    setlogmask(LOG_UPTO(LOG_DEBUG));
+    return (1);
+}
+
+/*
+ * noopt - Disable all options.
+ */
+static int
+noopt()
+{
+    BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
+    BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
+    BZERO((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options));
+    BZERO((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options));
+    return (1);
+}
+
+/*
+ * noaccomp - Disable Address/Control field compression negotiation.
+ */
+static int
+noaccomp()
+{
+    lcp_wantoptions[0].neg_accompression = 0;
+    lcp_allowoptions[0].neg_accompression = 0;
+    return (1);
+}
+
+
+/*
+ * noasyncmap - Disable async map negotiation.
+ */
+static int
+noasyncmap()
+{
+    lcp_wantoptions[0].neg_asyncmap = 0;
+    lcp_allowoptions[0].neg_asyncmap = 0;
+    return (1);
+}
+
+
+/*
+ * noipaddr - Disable IP address negotiation.
+ */
+static int
+noipaddr()
+{
+    ipcp_wantoptions[0].neg_addr = 0;
+    ipcp_allowoptions[0].neg_addr = 0;
+    return (1);
+}
+
+
+/*
+ * nomagicnumber - Disable magic number negotiation.
+ */
+static int
+nomagicnumber()
+{
+    lcp_wantoptions[0].neg_magicnumber = 0;
+    lcp_allowoptions[0].neg_magicnumber = 0;
+    return (1);
+}
+
+
+/*
+ * nomru - Disable mru negotiation.
+ */
+static int
+nomru()
+{
+    lcp_wantoptions[0].neg_mru = 0;
+    lcp_allowoptions[0].neg_mru = 0;
+    return (1);
+}
+
+
+/*
+ * setmru - Set MRU for negotiation.
+ */
+static int
+setmru(argv)
+    char **argv;
+{
+    long mru;
+
+    if (!number_option(*argv, &mru, 0))
+       return 0;
+    lcp_wantoptions[0].mru = mru;
+    lcp_wantoptions[0].neg_mru = 1;
+    return (1);
+}
+
+
+/*
+ * nopcomp - Disable Protocol field compression negotiation.
+ */
+static int
+nopcomp()
+{
+    lcp_wantoptions[0].neg_pcompression = 0;
+    lcp_allowoptions[0].neg_pcompression = 0;
+    return (1);
+}
+
+
+/*
+ * setpassive - Set passive mode (don't give up if we time out sending
+ * LCP configure-requests).
+ */
+static int
+setpassive()
+{
+    lcp_wantoptions[0].passive = 1;
+    return (1);
+}
+
+
+/*
+ * setsilent - Set silent mode (don't start sending LCP configure-requests
+ * until we get one from the peer).
+ */
+static int
+setsilent()
+{
+    lcp_wantoptions[0].silent = 1;
+    return 1;
+}
+
+
+/*
+ * nopap - Disable PAP authentication with peer.
+ */
+static int
+nopap()
+{
+    lcp_allowoptions[0].neg_upap = 0;
+    return (1);
+}
+
+
+/*
+ * reqpap - Require PAP authentication from peer.
+ */
+static int
+reqpap()
+{
+    lcp_wantoptions[0].neg_upap = 1;
+    auth_required = 1;
+}
+
+
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(argv)
+    char **argv;
+{
+    FILE * ufile;
+    int l;
+
+    lcp_allowoptions[0].neg_upap = 1;
+
+    /* open user info file */
+    if ((ufile = fopen(*argv, "r")) == NULL) {
+       fprintf(stderr, "unable to open user login data file %s\n", *argv);
+       exit(1);
+    }
+    check_access(ufile, *argv);
+
+    /* get username */
+    if (fgets(user, MAXNAMELEN - 1, ufile) == NULL
+       || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){
+       fprintf(stderr, "Unable to read user login data file %s.\n", *argv);
+       exit(2);
+    }
+    fclose(ufile);
+
+    /* get rid of newlines */
+    l = strlen(user);
+    if (l > 0 && user[l-1] == '\n')
+       user[l-1] = 0;
+    l = strlen(passwd);
+    if (l > 0 && passwd[l-1] == '\n')
+       passwd[l-1] = 0;
+
+    return (1);
+}
+
+
+/*
+ * nochap - Disable CHAP authentication with peer.
+ */
+static int
+nochap()
+{
+    lcp_allowoptions[0].neg_chap = 0;
+    return (1);
+}
+
+
+/*
+ * reqchap - Require CHAP authentication from peer.
+ */
+static int
+reqchap()
+{
+    lcp_wantoptions[0].neg_chap = 1;
+    auth_required = 1;
+    return (1);
+}
+
+
+/*
+ * setnovj - diable vj compression
+ */
+static int
+setnovj()
+{
+    ipcp_wantoptions[0].neg_vj = 0;
+    ipcp_allowoptions[0].neg_vj = 0;
+    return (1);
+}
+
+/*
+ * setconnector - Set a program to connect to a serial line
+ */
+static int
+setconnector(argv)
+    char **argv;
+{
+    connector = strdup(*argv);
+    if (connector == NULL)
+       novm("connector string");
+  
+    return (1);
+}
+
+
+/*
+ * setdomain - Set domain name to append to hostname 
+ */
+static int
+setdomain(argv)
+    char **argv;
+{
+    strncat(hostname, *argv, MAXNAMELEN - strlen(hostname));
+    hostname[MAXNAMELEN-1] = 0;
+    return (1);
+}
+
+static int
+setasyncmap(argv)
+    char **argv;
+{
+    long asyncmap;
+
+    if (!number_option(*argv, &asyncmap, 16))
+       return 0;
+    lcp_wantoptions[0].asyncmap |= asyncmap;
+    lcp_wantoptions[0].neg_asyncmap = 1;
+    return(1);
+}
+
+/*
+ * setspeed - Set the speed.
+ */
+static int
+setspeed(arg)
+    char *arg;
+{
+    char *ptr;
+    int spd;
+
+    spd = strtol(arg, &ptr, 0);
+    if (ptr == arg || *ptr != 0 || spd == 0)
+       return 0;
+    inspeed = spd;
+    return 1;
+}
+
+
+/*
+ * setdevname - Set the device name.
+ */
+int
+setdevname(cp)
+    char *cp;
+{
+    struct stat statbuf;
+    char *tty, *ttyname();
+    char dev[MAXPATHLEN];
+  
+    if (strncmp("/dev/", cp, 5) != 0) {
+       strcpy(dev, "/dev/");
+       strncat(dev, cp, MAXPATHLEN - 5);
+       dev[MAXPATHLEN-1] = 0;
+       cp = dev;
+    }
+
+    /*
+     * Check if there is a device by this name.
+     */
+    if (stat(cp, &statbuf) < 0) {
+       if (errno == ENOENT)
+           return (0);
+       syslog(LOG_ERR, cp);
+       exit(1);
+    }
+  
+    (void) strncpy(devname, cp, MAXPATHLEN);
+    devname[MAXPATHLEN-1] = 0;
+    default_device = FALSE;
+  
+    return (1);
+}
+
+
+/*
+ * setipaddr - Set the IP address
+ */
+int
+setipaddr(arg)
+    char *arg;
+{
+    struct hostent *hp;
+    char *colon, *index();
+    u_long local, remote;
+    ipcp_options *wo = &ipcp_wantoptions[0];
+  
+    /*
+     * IP address pair separated by ":".
+     */
+    if ((colon = index(arg, ':')) == NULL)
+       return (0);
+  
+    /*
+     * If colon first character, then no local addr.
+     */
+    if (colon != arg) {
+       *colon = '\0';
+       if ((local = inet_addr(arg)) == -1) {
+           if ((hp = gethostbyname(arg)) == NULL) {
+               fprintf(stderr, "unknown host: %s", arg);
+               local = 0;
+           } else {
+               local = *(long *)hp->h_addr;
+               if (our_name[0] == 0) {
+                   strncpy(our_name, arg, MAXNAMELEN);
+                   our_name[MAXNAMELEN-1] = 0;
+               }
+           }
+       }
+       if (local != 0)
+           wo->ouraddr = local;
+       *colon = ':';
+    }
+  
+    /*
+     * If colon last character, then no remote addr.
+     */
+    if (*++colon != '\0') {
+       if ((remote = inet_addr(colon)) == -1) {
+           if ((hp = gethostbyname(colon)) == NULL) {
+               fprintf(stderr, "unknown host: %s", colon);
+               remote = 0;
+           } else {
+               remote = *(long *)hp->h_addr;
+               if (remote_name[0] == 0) {
+                   strncpy(remote_name, colon, MAXNAMELEN);
+                   remote_name[MAXNAMELEN-1] = 0;
+               }
+           }
+       }
+       if (remote != 0)
+           wo->hisaddr = remote;
+    }
+
+    return (1);
+}
+
+
+/*
+ * setipdefault - default our local IP address based on our hostname.
+ */
+void
+setipdefault()
+{
+    struct hostent *hp;
+    u_long local;
+    ipcp_options *wo = &ipcp_wantoptions[0];
+
+    /*
+     * If local IP address already given, don't bother.
+     */
+    if (wo->ouraddr != 0)
+       return;
+
+    /*
+     * 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.
+     */
+    if ((hp = gethostbyname(hostname)) == NULL)
+       return;
+    local = *(long *)hp->h_addr;
+    if (local != 0)
+       wo->ouraddr = local;
+}
+
+
+/*
+ * setnetmask - set the netmask to be used on the interface.
+ */
+static int
+setnetmask(argv)
+    char **argv;
+{
+    u_long mask;
+       
+    if ((mask = inet_addr(*argv)) == -1) {
+       fprintf(stderr, "Invalid netmask %s\n", *argv);
+       exit(1);
+    }
+
+    netmask = mask;
+    return (1);
+}
+
+static int
+setcrtscts()
+{
+    crtscts = 1;
+    return (1);
+}
+
+static int
+setnodetach()
+{
+    nodetach = 1;
+    return (1);
+}
+
+static int
+setmodem()
+{
+    modem = 1;
+    return 1;
+}
+
+static int
+setlocal()
+{
+    modem = 0;
+    return 1;
+}
+
+static int
+setusehostname()
+{
+    usehostname = 1;
+    return 1;
+}
+
+static int
+setname(argv)
+    char **argv;
+{
+    if (our_name[0] == 0) {
+       strncpy(our_name, argv[0], MAXNAMELEN);
+       our_name[MAXNAMELEN-1] = 0;
+    }
+    return 1;
+}
+
+static int
+setuser(argv)
+    char **argv;
+{
+    strncpy(user, argv[0], MAXNAMELEN);
+    user[MAXNAMELEN-1] = 0;
+    return 1;
+}
+
+static int
+setremote(argv)
+    char **argv;
+{
+    strncpy(remote_name, argv[0], MAXNAMELEN);
+    remote_name[MAXNAMELEN-1] = 0;
+    return 1;
+}
+
+static int
+setauth()
+{
+    auth_required = 1;
+    return 1;
+}
+
+static int
+setdefaultroute()
+{
+    ipcp_wantoptions[0].default_route = 1;
+    return 1;
+}
+
+static int
+setproxyarp()
+{
+    ipcp_wantoptions[0].proxy_arp = 1;
+    return 1;
+}
+
+static int
+setpersist()
+{
+    persist = 1;
+    return 1;
+}
+
+static int
+setdologin()
+{
+    uselogin = 1;
+    return 1;
+}
+
+/*
+ * Functions to set timeouts, max transmits, etc.
+ */
+static int
+setlcptimeout(argv)
+    char **argv;
+{
+    return int_option(*argv, &lcp_fsm[0].timeouttime, 0);
+}
+
+static int setlcpterm(argv)
+    char **argv;
+{
+    return int_option(*argv, &lcp_fsm[0].maxtermtransmits, 0);
+}
+
+static int setlcpconf(argv)
+    char **argv;
+{
+    return int_option(*argv, &lcp_fsm[0].maxconfreqtransmits, 0);
+}
+
+static int setlcpfails(argv)
+    char **argv;
+{
+    return int_option(*argv, &lcp_fsm[0].maxnakloops, 0);
+}
+
+static int setipcptimeout(argv)
+    char **argv;
+{
+    return int_option(*argv, &ipcp_fsm[0].timeouttime, 0);
+}
+
+static int setipcpterm(argv)
+    char **argv;
+{
+    return int_option(*argv, &ipcp_fsm[0].maxtermtransmits, 0);
+}
+
+static int setipcpconf(argv)
+    char **argv;
+{
+    return int_option(*argv, &ipcp_fsm[0].maxconfreqtransmits, 0);
+}
+
+static int setipcpfails(argv)
+    char **argv;
+{
+    return int_option(*argv, &lcp_fsm[0].maxnakloops, 0);
+}
+
+static int setpaptimeout(argv)
+    char **argv;
+{
+    return int_option(*argv, &upap[0].us_timeouttime, 0);
+}
+
+static int setpapreqs(argv)
+    char **argv;
+{
+    return int_option(*argv, &upap[0].us_maxtransmits, 0);
+}
+
+static int setchaptimeout(argv)
+    char **argv;
+{
+    return int_option(*argv, &chap[0].timeouttime, 0);
+}
+
+static int setchapchal(argv)
+    char **argv;
+{
+    return int_option(*argv, &chap[0].max_transmits, 0);
+}
+
+static int setchapintv(argv)
+    char **argv;
+{
+    return int_option(*argv, &chap[0].chal_interval, 0);
+}
diff --git a/pppd/patchlevel.h b/pppd/patchlevel.h
new file mode 100644 (file)
index 0000000..ac3c40c
--- /dev/null
@@ -0,0 +1,5 @@
+/* $Id: patchlevel.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ */
+#define        PATCHLEVEL      1
+
+#define VERSION        "2.0"
+#define DATE   "9 Nov 93"
diff --git a/pppd/pathnames.h b/pppd/pathnames.h
new file mode 100644 (file)
index 0000000..b5a9348
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * define path names
+ *
+ * $Id: pathnames.h,v 1.1 1993/11/11 03:54:25 paulus Exp $
+ */
+
+#ifdef STREAMS
+#define _PATH_PIDFILE  "/etc/ppp"
+#else
+#define _PATH_PIDFILE  "/var/run"
+#endif
+
+#define _PATH_UPAPFILE         "/etc/ppp/pap-secrets"
+#define _PATH_CHAPFILE         "/etc/ppp/chap-secrets"
+#define _PATH_SYSOPTIONS "/etc/ppp/options"
diff --git a/pppd/ppp.h b/pppd/ppp.h
new file mode 100644 (file)
index 0000000..3d8f870
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * ppp.h - PPP global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * $Id: ppp.h,v 1.1 1993/11/11 03:54:25 paulus Exp $
+ */
+
+#ifndef __PPP_H__
+#define __PPP_H__
+
+#define NPPP   1               /* One PPP interface supported (per process) */
+
+/*
+ * Data Link Layer header = Address, Control, Protocol.
+ */
+#define ALLSTATIONS    0xff    /* All-Stations Address */
+#define UI             0x03    /* Unnumbered Information */
+#define LCP            0xc021  /* Link Control Protocol */
+#define IPCP           0x8021  /* IP Control Protocol */
+#define UPAP           0xc023  /* User/Password Authentication Protocol */
+#define CHAP            0xc223  /* Crytpographic Handshake Protocol */
+#define LQR            0xc025  /* Link Quality Report protocol */
+#define IP_VJ_COMP     0x002d  /* VJ TCP compressed IP packet */
+#define DLLHEADERLEN   (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+#define MTU            1500    /* Default MTU */
+
+#endif /* __PPP_H__ */
diff --git a/pppd/pppd.h b/pppd/pppd.h
new file mode 100644 (file)
index 0000000..c5ea8b6
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * $Id: pppd.h,v 1.1 1993/11/11 03:54:25 paulus Exp $
+ */
+
+/*
+ * TODO:
+ */
+
+#ifndef __PPPD_H__
+#define __PPPD_H__
+#include "args.h"
+
+#include <sys/param.h>         /* for MAXPATHLEN and BSD4_4, if defined */
+
+#define NPPP   1               /* One PPP interface supported (per process) */
+
+/*
+ * Limits.
+ */
+#define MAXWORDLEN     1024    /* max length of word in file (incl null) */
+#define MAXARGS                1       /* max # args to a command */
+#define MAXNAMELEN     256     /* max length of hostname or name for auth */
+#define MAXSECRETLEN   256     /* max length of password or secret */
+
+extern int debug;              /* Debug flag */
+extern int ifunit;             /* Interface unit number */
+extern char ifname[];          /* Interface name */
+extern int fd;                 /* Device file descriptor */
+extern int s;                  /* socket descriptor */
+extern char hostname[];                /* hostname */
+extern u_char outpacket_buf[]; /* buffer for outgoing packets */
+
+void quit __ARGS((void));      /* Cleanup and exit */
+void timeout __ARGS((void (*)(), caddr_t, int));
+                               /* Look-alike of kernel's timeout() */
+void untimeout __ARGS((void (*)(), caddr_t));
+                               /* Look-alike of kernel's untimeout() */
+void output __ARGS((int, u_char *, int));
+                               /* Output a PPP packet */
+void demuxprotrej __ARGS((int, int));
+                               /* Demultiplex a Protocol-Reject */
+int  check_passwd __ARGS((int, char *, int, char *, int, char **, int *));
+                               /* Check peer-supplied username/password */
+int  get_secret __ARGS((int, char *, char *, char *, int *, int));
+                               /* get "secret" for chap */
+u_long GetMask __ARGS((u_long)); /* get netmask for address */
+
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+       (c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+       *(cp)++ = (c); \
+}
+
+
+#define GETSHORT(s, cp) { \
+       (s) = *(cp)++ << 8; \
+       (s) |= *(cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+       *(cp)++ = (s) >> 8; \
+       *(cp)++ = (s); \
+}
+
+#define GETLONG(l, cp) { \
+       (l) = *(cp)++ << 8; \
+       (l) |= *(cp)++; (l) <<= 8; \
+       (l) |= *(cp)++; (l) <<= 8; \
+       (l) |= *(cp)++; \
+}
+#define PUTLONG(l, cp) { \
+       *(cp)++ = (l) >> 24; \
+       *(cp)++ = (l) >> 16; \
+       *(cp)++ = (l) >> 8; \
+       *(cp)++ = (l); \
+}
+
+#define INCPTR(n, cp)  ((cp) += (n))
+#define DECPTR(n, cp)  ((cp) -= (n))
+
+/*
+ * System dependent definitions for user-level 4.3BSD UNIX implementation.
+ */
+
+#define DEMUXPROTREJ(u, p)     demuxprotrej(u, p)
+
+#define TIMEOUT(r, f, t)       timeout((r), (f), (t))
+#define UNTIMEOUT(r, f)                untimeout((r), (f))
+
+#define BCOPY(s, d, l)         memcpy(d, s, l)
+#define BZERO(s, n)            memset(s, 0, n)
+#define EXIT(u)                        quit()
+
+#define PRINTMSG(m, l) { m[l] = '\0'; syslog(LOG_INFO, "Remote message: %s", m); }
+
+/*
+ * MAKEHEADER - Add Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+    PUTCHAR(ALLSTATIONS, p); \
+    PUTCHAR(UI, p); \
+    PUTSHORT(t, p); }
+
+
+#ifdef DEBUGALL
+#define DEBUGMAIN      1
+#define DEBUGFSM       1
+#define DEBUGLCP       1
+#define DEBUGIPCP      1
+#define DEBUGUPAP      1
+#define DEBUGCHAP      1
+#endif
+
+#ifndef LOG_PPP                        /* we use LOG_LOCAL2 for syslog by default */
+#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUG) \
+  || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \
+  || defined(DEBUGCHAP) 
+#define LOG_PPP LOG_LOCAL2
+#else
+#define LOG_PPP LOG_DAEMON
+#endif
+#endif /* LOG_PPP */
+
+#ifdef DEBUGMAIN
+#define MAINDEBUG(x)   if (debug) syslog x
+#else
+#define MAINDEBUG(x)
+#endif
+
+#ifdef DEBUGFSM
+#define FSMDEBUG(x)    if (debug) syslog x
+#else
+#define FSMDEBUG(x)
+#endif
+
+#ifdef DEBUGLCP
+#define LCPDEBUG(x)    if (debug) syslog x
+#else
+#define LCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPCP
+#define IPCPDEBUG(x)   if (debug) syslog x
+#else
+#define IPCPDEBUG(x)
+#endif
+
+#ifdef DEBUGUPAP
+#define UPAPDEBUG(x)   if (debug) syslog x
+#else
+#define UPAPDEBUG(x)
+#endif
+
+#ifdef DEBUGCHAP
+#define CHAPDEBUG(x)   if (debug) syslog x
+#else
+#define CHAPDEBUG(x)
+#endif
+
+#ifndef SIGTYPE
+#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE)
+#define SIGTYPE void
+#else
+#define SIGTYPE int
+#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */
+#endif /* SIGTYPE */
+
+#ifndef MIN
+#define MIN(a, b)      ((a) < (b)? (a): (b))
+#endif
+#ifndef MAX
+#define MAX(a, b)      ((a) > (b)? (a): (b))
+#endif
+
+#endif /* __PPP_H__ */
diff --git a/pppd/sys-bsd.c b/pppd/sys-bsd.c
new file mode 100644 (file)
index 0000000..350e0a0
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * sys-bsd.c - System-dependent procedures for setting up
+ * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: sys-bsd.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/if_ppp.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "ppp.h"
+
+static int initdisc;           /* Initial TTY discipline */
+
+
+/*
+ * establish_ppp - Turn the serial port into a ppp interface.
+ */
+void
+establish_ppp()
+{
+    int pppdisc = PPPDISC;
+
+    if (ioctl(fd, TIOCGETD, &initdisc) < 0) {
+       syslog(LOG_ERR, "ioctl(TIOCGETD): %m");
+       die(1);
+    }
+    if (ioctl(fd, TIOCSETD, &pppdisc) < 0) {
+       syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+       die(1);
+    }
+
+    /*
+     * Find out which interface we were given.
+     */
+    if (ioctl(fd, PPPIOCGUNIT, &ifunit) < 0) { 
+       syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
+       die(1);
+    }
+}
+
+
+/*
+ * disestablish_ppp - Restore the serial port to normal operation.
+ * This shouldn't call die() because it's called from die().
+ */
+void
+disestablish_ppp()
+{
+    if (ioctl(fd, TIOCSETD, &initdisc) < 0)
+       syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+}
+
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    if (unit != 0)
+       MAINDEBUG((LOG_WARNING, "output: unit != 0!"));
+
+    if (write(fd, p, len) < 0) {
+       syslog(LOG_ERR, "write: %m");
+       die(1);
+    }
+}
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+    u_char *buf;
+{
+    int len;
+
+    if ((len = read(fd, buf, MTU + DLLHEADERLEN)) < 0) {
+       if (errno == EWOULDBLOCK) {
+           MAINDEBUG((LOG_DEBUG, "read(fd): EWOULDBLOCK"));
+           return -1;
+       }
+       syslog(LOG_ERR, "read(fd): %m");
+       die(1);
+    }
+    return len;
+}
+
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+    int unit, mtu;
+    u_long asyncmap;
+    int pcomp, accomp;
+{
+    u_int x;
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    ifr.ifr_mtu = mtu;
+    if (ioctl(s, SIOCSIFMTU, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFMTU): %m");
+       quit();
+    }
+
+    if (ioctl(fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
+       syslog(LOG_ERR, "ioctl(PPPIOCSASYNCMAP): %m");
+       quit();
+    }
+
+    if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+       syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
+       quit();
+    }
+    x = pcomp? x | SC_COMP_PROT: x &~ SC_COMP_PROT;
+    x = accomp? x | SC_COMP_AC: x &~ SC_COMP_AC;
+    if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+       syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+       quit();
+    }
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.  At present this does nothing.
+ */
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+    int unit, mru;
+    u_long asyncmap;
+    int pcomp, accomp;
+{
+#ifdef notyet
+    if (ioctl(fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
+       syslog(LOG_ERR, "ioctl(PPPIOCSRASYNCMAP): %m");
+       quit();
+    }
+
+    if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+       syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
+       quit();
+    }
+    x = !accomp? x | SC_REJ_COMP_AC: x &~ SC_REJ_COMP_AC;
+    if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+       syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+       quit();
+    }
+#endif /* notyet */
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, cidcomp)
+{
+    u_int x;
+
+    if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+       syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
+       return 0;
+    }
+    x = vjcomp ? x | SC_COMP_TCP: x &~ SC_COMP_TCP;
+    x = cidcomp? x & ~SC_NO_TCP_CCID: x | SC_NO_TCP_CCID;
+    if(ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+       syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * sifup - Config the interface up.
+ */
+int
+sifup(u)
+{
+    struct ifreq ifr;
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+       return 0;
+    }
+    ifr.ifr_flags |= IFF_UP;
+    if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * sifdown - Config the interface down.
+ */
+int
+sifdown(u)
+{
+    struct ifreq ifr;
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+       return 0;
+    }
+    ifr.ifr_flags &= ~IFF_UP;
+    if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
+ * if it exists.
+ */
+#define SET_SA_FAMILY(addr, family)            \
+    BZERO((char *) &(addr), sizeof(addr));     \
+    addr.sa_family = (family);                         \
+    addr.sa_len = sizeof(addr);
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+{
+    struct ifaliasreq ifra;
+
+    strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
+    SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
+    ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
+    SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
+    ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
+    if (m != 0) {
+       SET_SA_FAMILY(ifra.ifra_mask, AF_INET);
+       ((struct sockaddr_in *) &ifra.ifra_mask)->sin_addr.s_addr = m;
+    } else
+       BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
+    if (ioctl(s, SIOCAIFADDR, (caddr_t) &ifra) < 0) {
+       if (errno != EEXIST) {
+           syslog(LOG_ERR, "ioctl(SIOCAIFADDR): %m");
+           return 0;
+       }
+       syslog(LOG_WARNING, "ioctl(SIOCAIFADDR): Address already exists");
+    }
+    return 1;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+{
+    struct ifaliasreq ifra;
+
+    strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
+    SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
+    ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
+    SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
+    ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
+    BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
+    if (ioctl(s, SIOCDIFADDR, (caddr_t) &ifra) < 0) {
+       syslog(LOG_WARNING, "ioctl(SIOCDIFADDR): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, g)
+{
+    struct ortentry rt;
+
+    SET_SA_FAMILY(rt.rt_dst, AF_INET);
+    SET_SA_FAMILY(rt.rt_gateway, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
+    rt.rt_flags = RTF_GATEWAY;
+    if (ioctl(s, SIOCADDRT, &rt) < 0) {
+       syslog(LOG_ERR, "default route ioctl(SIOCADDRT): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, g)
+{
+    struct ortentry rt;
+
+    SET_SA_FAMILY(rt.rt_dst, AF_INET);
+    SET_SA_FAMILY(rt.rt_gateway, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
+    rt.rt_flags = RTF_GATEWAY;
+    if (ioctl(s, SIOCDELRT, &rt) < 0)
+       syslog(LOG_WARNING, "default route ioctl(SIOCDELRT): %m");
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+    int unit;
+    u_long hisaddr;
+{
+    struct arpreq arpreq;
+
+    BZERO(&arpreq, sizeof(arpreq));
+
+    /*
+     * Get the hardware address of an interface on the same subnet
+     * as our local address.
+     */
+    if (!get_ether_addr(hisaddr, &arpreq.arp_ha)) {
+       syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
+       return 0;
+    }
+
+    SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+    ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+    arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+    if (ioctl(s, SIOCSARP, (caddr_t)&arpreq) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
+       return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+    int unit;
+    u_long hisaddr;
+{
+    struct arpreq arpreq;
+
+    BZERO(&arpreq, sizeof(arpreq));
+    SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+    ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+    if (ioctl(s, SIOCDARP, (caddr_t)&arpreq) < 0) {
+       syslog(LOG_WARNING, "ioctl(SIOCDARP): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS                32
+
+int
+get_ether_addr(ipaddr, hwaddr)
+    u_long ipaddr;
+    struct sockaddr *hwaddr;
+{
+    struct ifreq *ifr, *ifend, *ifp;
+    u_long ina, mask;
+    struct sockaddr_dl *dla;
+    struct ifreq ifreq;
+    struct ifconf ifc;
+    struct ifreq ifs[MAX_IFS];
+
+    ifc.ifc_len = sizeof(ifs);
+    ifc.ifc_req = ifs;
+    if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m");
+       return 0;
+    }
+
+    /*
+     * Scan through looking for an interface with an Internet
+     * address on the same subnet as `ipaddr'.
+     */
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ) {
+       if (ifr->ifr_addr.sa_family == AF_INET) {
+           ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+           strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+           /*
+            * Check that the interface is up, and not point-to-point
+            * or loopback.
+            */
+           if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
+               continue;
+           if ((ifreq.ifr_flags &
+                (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+                != (IFF_UP|IFF_BROADCAST))
+               continue;
+           /*
+            * Get its netmask and check that it's on the right subnet.
+            */
+           if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
+               continue;
+           mask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+           if ((ipaddr & mask) != (ina & mask))
+               continue;
+
+           break;
+       }
+       ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
+    }
+
+    if (ifr >= ifend)
+       return 0;
+    syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name);
+
+    /*
+     * Now scan through again looking for a link-level address
+     * for this interface.
+     */
+    ifp = ifr;
+    for (ifr = ifc.ifc_req; ifr < ifend; ) {
+       if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
+           && ifr->ifr_addr.sa_family == AF_LINK) {
+           /*
+            * Found the link-level address - copy it out
+            */
+           dla = (struct sockaddr_dl *)&ifr->ifr_addr;
+           hwaddr->sa_len = sizeof(struct sockaddr);
+           hwaddr->sa_family = AF_UNSPEC;
+           BCOPY(LLADDR(dla), hwaddr->sa_data, dla->sdl_alen);
+           return 1;
+       }
+       ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
+    }
+
+    return 0;
+}
diff --git a/pppd/sys-str.c b/pppd/sys-str.c
new file mode 100644 (file)
index 0000000..ecee95b
--- /dev/null
@@ -0,0 +1,624 @@
+/*
+ * sys-str.c - System-dependent procedures for setting up
+ * PPP interfaces on systems which use the STREAMS ppp interface.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/*
+ * TODO:
+ */
+
+#include <errno.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "ppp.h"
+#include "ppp_str.h"
+
+#ifndef ifr_mtu
+#define ifr_mtu                ifr_metric
+#endif
+
+#define        MAXMODULES      10      /* max number of module names to save */
+static struct  modlist {
+    char       modname[FMNAMESZ+1];
+} str_modules[MAXMODULES];
+static int     str_module_count = 0;
+
+extern int hungup;
+
+/*
+ * establish_ppp - Turn the serial port into a ppp interface.
+ */
+void
+establish_ppp()
+{
+    /* go through and save the name of all the modules, then pop em */
+    for (;;) { 
+       if (ioctl(fd, I_LOOK, str_modules[str_module_count].modname) < 0 ||
+           ioctl(fd, I_POP, 0) < 0)
+           break;
+       MAINDEBUG((LOG_DEBUG, "popped stream module : %s",
+                  str_modules[str_module_count].modname));
+       str_module_count++;
+    }
+
+    MAINDEBUG((LOG_INFO, "about to push modules..."));
+
+    /* now push the async/fcs module */
+    if (ioctl(fd, I_PUSH, "pppasync") < 0) {
+       syslog(LOG_ERR, "ioctl(I_PUSH, ppp_async): %m");
+       die(1);
+    }
+    /* finally, push the ppp_if module that actually handles the */
+    /* network interface */ 
+    if (ioctl(fd, I_PUSH, "pppif") < 0) {
+       syslog(LOG_ERR, "ioctl(I_PUSH, ppp_if): %m");
+       die(1);
+    }
+    if (ioctl(fd, I_SETSIG, S_INPUT) < 0) {
+       syslog(LOG_ERR, "ioctl(I_SETSIG, S_INPUT): %m");
+       die(1);
+    }
+    /* read mode, message non-discard mode */
+    if (ioctl(fd, I_SRDOPT, RMSGN) < 0) {
+       syslog(LOG_ERR, "ioctl(I_SRDOPT, RMSGN): %m");
+       die(1);
+    }
+    /* Flush any waiting messages, or we'll never get SIGPOLL */
+    if (ioctl(fd, I_FLUSH, FLUSHRW) < 0) {
+       syslog(LOG_ERR, "ioctl(I_FLUSH, FLUSHRW): %m");
+       die(1);
+    }
+    /*
+     * Find out which interface we were given.
+     * (ppp_if handles this ioctl)
+     */
+    if (ioctl(fd, SIOCGETU, &ifunit) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCGETU): %m");
+       die(1);
+    }
+
+    /* if debug, set debug flags in driver */
+    {
+       int flags = debug ? 0x3 : 0;
+       if (ioctl(fd, SIOCSIFDEBUG, &flags) < 0) {
+           syslog(LOG_ERR, "ioctl(SIOCSIFDEBUG): %m");
+       }
+    }
+
+    MAINDEBUG((LOG_INFO, "done pushing modules, ifunit %d", ifunit));
+}
+
+/*
+ * disestablish_ppp - Restore the serial port to normal operation.
+ * It attempts to reconstruct the stream with the previously popped
+ * modules.  This shouldn't call die() because it's called from die().
+ */
+void
+disestablish_ppp()
+{
+    /*EMPTY*/
+
+    if (hungup) {
+       /* we can't push or pop modules after the stream has hung up */
+       str_module_count = 0;
+       return;
+    }
+
+    while (ioctl(fd, I_POP, 0) == 0)   /* pop any we pushed */
+       ;
+  
+    for (; str_module_count > 0; str_module_count--) {
+       if (ioctl(fd, I_PUSH, str_modules[str_module_count-1].modname)) {
+           syslog(LOG_ERR, "str_restore: couldn't push module %s: %m",
+                  str_modules[str_module_count-1].modname);
+       } else {
+           MAINDEBUG((LOG_INFO, "str_restore: pushed module %s",
+                      str_modules[str_module_count-1].modname));
+       }
+    }
+}
+
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    struct strbuf      str;
+
+    if (unit != 0)
+       MAINDEBUG((LOG_WARNING, "output: unit != 0!"));
+
+    str.len = len;
+    str.buf = (caddr_t) p;
+    if(putmsg(fd, NULL, &str, 0) < 0) {
+       syslog(LOG_ERR, "putmsg: %m");
+       die(1);
+    }
+}
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+    u_char *buf;
+{
+    struct strbuf str;
+    int len, i;
+
+    str.maxlen = MTU+DLLHEADERLEN;
+    str.buf = (caddr_t) buf;
+    i = 0;
+    len = getmsg(fd, NULL, &str, &i);
+    if (len < 0) {
+       if (errno == EAGAIN || errno == EWOULDBLOCK) {
+           return -1;
+       }
+       syslog(LOG_ERR, "getmsg(fd) %m");
+       die(1);
+    }
+    if (len) 
+       MAINDEBUG((LOG_DEBUG, "getmsg returned 0x%x",len));
+
+    if (str.len < 0) {
+       MAINDEBUG((LOG_DEBUG, "getmsg short return length %d", str.len));
+       return -1;
+    }
+
+    return str.len;
+}
+
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+    int unit, mtu;
+    u_long asyncmap;
+    int pcomp, accomp;
+{
+    char c;
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    ifr.ifr_mtu = mtu;
+    if (ioctl(s, SIOCSIFMTU, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFMTU): %m");
+       quit();
+    }
+
+    if(ioctl(fd, SIOCSIFASYNCMAP, (caddr_t) &asyncmap) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFASYNCMAP): %m");
+       quit();
+    }
+
+    c = pcomp;
+    if(ioctl(fd, SIOCSIFCOMPPROT, &c) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFCOMPPROT): %m");
+       quit();
+    }
+
+    c = accomp;
+    if(ioctl(fd, SIOCSIFCOMPAC, &c) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFCOMPAC): %m");
+       quit();
+    }
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.  At present this just sets the MRU.
+ */
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+    int unit, mru;
+    u_long asyncmap;
+    int pcomp, accomp;
+{
+    char c;
+
+    if (ioctl(fd, SIOCSIFMRU, &mru) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFMRU): %m");
+    }
+
+#ifdef notyet
+    if(ioctl(fd, SIOCSIFRASYNCMAP, (caddr_t) &asyncmap) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFRASYNCMAP): %m");
+    }
+
+    c = accomp;
+    if(ioctl(fd, SIOCSIFRCOMPAC, &c) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFRCOMPAC): %m");
+       quit();
+    }
+#endif /* notyet */
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, cidcomp)
+{
+    char x = vjcomp;
+
+    if(ioctl(fd, SIOCSIFVJCOMP, (caddr_t) &x) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFVJCOMP): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * sifup - Config the interface up.
+ */
+int
+sifup(u)
+{
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+       return 0;
+    }
+    ifr.ifr_flags |= IFF_UP;
+    if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * sifdown - Config the interface down.
+ */
+int
+sifdown(u)
+{
+    struct ifreq ifr;
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+       return 0;
+    }
+    ifr.ifr_flags &= ~IFF_UP;
+    if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * SET_SA_FAMILY - initialize a struct sockaddr, setting the sa_family field.
+ */
+#define SET_SA_FAMILY(addr, family)            \
+    BZERO((char *) &(addr), sizeof(addr));     \
+    addr.sa_family = (family);
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+{
+    int ret;
+    struct ifreq ifr;
+
+    ret = 1;
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
+    ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = o;
+    if (ioctl(s, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFADDR): %m");
+       ret = 0;
+    }
+    ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = h;
+    if (ioctl(s, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSIFDSTADDR): %m");
+       ret = 0;
+    }
+    if (m != 0) {
+       ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = m;
+       syslog(LOG_INFO, "Setting interface mask to %s\n", ip_ntoa(m));
+       if (ioctl(s, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) {
+           syslog(LOG_ERR, "ioctl(SIOCSIFNETMASK): %m");
+           ret = 0;
+       }
+    }
+    return ret;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+{
+    struct rtentry rt;
+
+    SET_SA_FAMILY(rt.rt_dst, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = h;
+    SET_SA_FAMILY(rt.rt_gateway, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = o;
+    rt.rt_flags = RTF_HOST;
+    if (ioctl(s, SIOCDELRT, (caddr_t) &rt) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCDELRT): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, g)
+{
+    struct rtentry rt;
+
+    SET_SA_FAMILY(rt.rt_dst, AF_INET);
+    SET_SA_FAMILY(rt.rt_gateway, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
+    rt.rt_flags = RTF_GATEWAY;
+    if (ioctl(s, SIOCADDRT, &rt) < 0) {
+       syslog(LOG_ERR, "default route ioctl(SIOCADDRT): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, g)
+{
+    struct rtentry rt;
+
+    SET_SA_FAMILY(rt.rt_dst, AF_INET);
+    SET_SA_FAMILY(rt.rt_gateway, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
+    rt.rt_flags = RTF_GATEWAY;
+    if (ioctl(s, SIOCDELRT, &rt) < 0) {
+       syslog(LOG_ERR, "default route ioctl(SIOCDELRT): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+    int unit;
+    u_long hisaddr;
+{
+    struct arpreq arpreq;
+
+    BZERO(&arpreq, sizeof(arpreq));
+
+    /*
+     * Get the hardware address of an interface on the same subnet
+     * as our local address.
+     */
+    if (!get_ether_addr(hisaddr, &arpreq.arp_ha)) {
+       syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
+       return 0;
+    }
+
+    SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+    ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+    arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+    if (ioctl(s, SIOCSARP, (caddr_t)&arpreq) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
+       return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+    int unit;
+    u_long hisaddr;
+{
+    struct arpreq arpreq;
+
+    BZERO(&arpreq, sizeof(arpreq));
+    SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+    ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+    if (ioctl(s, SIOCDARP, (caddr_t)&arpreq) < 0) {
+       syslog(LOG_ERR, "ioctl(SIOCDARP): %m");
+       return 0;
+    }
+    return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.  Code borrowed from myetheraddr.c
+ * in the cslip-2.6 distribution, which is subject to the following
+ * copyright notice:
+ *
+ * Copyright (c) 1990, 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may 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
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <fcntl.h>
+#include <nlist.h>
+#include <kvm.h>
+#include <arpa/inet.h>
+
+/* XXX SunOS 4.1 defines this and 3.5 doesn't... */
+#ifdef _nlist_h
+#define SUNOS4
+#endif
+
+#ifdef SUNOS4
+#include <netinet/in_var.h>
+#endif
+#include <netinet/if_ether.h>
+
+/* Cast a struct sockaddr to a structaddr_in */
+#define SATOSIN(sa) ((struct sockaddr_in *)(sa))
+
+/* Determine if "bits" is set in "flag" */
+#define ALLSET(flag, bits) (((flag) & (bits)) == (bits))
+
+static struct nlist nl[] = {
+#define N_IFNET 0
+       { "_ifnet" },
+       0
+};
+
+static void kread();
+
+int
+get_ether_addr(ipaddr, hwaddr)
+    u_long ipaddr;
+    struct sockaddr *hwaddr;
+{
+    register kvm_t *kd;
+    register struct ifnet *ifp;
+    register struct arpcom *ac;
+    struct arpcom arpcom;
+    struct in_addr *inp;
+#ifdef SUNOS4
+    register struct ifaddr *ifa;
+    register struct in_ifaddr *in;
+    union {
+       struct ifaddr ifa;
+       struct in_ifaddr in;
+    } ifaddr;
+#endif
+    u_long addr, mask;
+
+    /* Open kernel memory for reading */
+    kd = kvm_open(0, 0, 0, O_RDONLY, NULL);
+    if (kd == 0) {
+       syslog(LOG_ERR, "kvm_open: %m");
+       return 0;
+    }
+
+    /* Fetch namelist */
+    if (kvm_nlist(kd, nl) != 0) {
+       syslog(LOG_ERR, "kvm_nlist failed");
+       return 0;
+    }
+
+    ac = &arpcom;
+    ifp = &arpcom.ac_if;
+#ifdef SUNOS4
+    ifa = &ifaddr.ifa;
+    in = &ifaddr.in;
+#endif
+
+    if (kvm_read(kd, nl[N_IFNET].n_value, (char *)&addr, sizeof(addr))
+       != sizeof(addr)) {
+       syslog(LOG_ERR, "error reading ifnet addr");
+       return 0;
+    }
+    for ( ; addr; addr = (u_long)ifp->if_next) {
+       if (kvm_read(kd, addr, (char *)ac, sizeof(*ac)) != sizeof(*ac)) {
+           syslog(LOG_ERR, "error reading ifnet");
+           return 0;
+       }
+
+       /* Only look at configured, broadcast interfaces */
+       if (!ALLSET(ifp->if_flags, IFF_UP | IFF_BROADCAST))
+           continue;
+#ifdef SUNOS4
+       /* This probably can't happen... */
+       if (ifp->if_addrlist == 0)
+           continue;
+#endif
+
+       /* Get interface ip address */
+#ifdef SUNOS4
+       if (kvm_read(kd, (u_long)ifp->if_addrlist, (char *)&ifaddr,
+                    sizeof(ifaddr)) != sizeof(ifaddr)) {
+           syslog(LOG_ERR, "error reading ifaddr");
+           return 0;
+       }
+       inp = &SATOSIN(&ifa->ifa_addr)->sin_addr;
+#else
+       inp = &SATOSIN(&ifp->if_addr)->sin_addr;
+#endif
+
+       /* Check if this interface on the right subnet */
+#ifdef SUNOS4
+       mask = in->ia_subnetmask;
+#else
+       mask = ifp->if_subnetmask;
+#endif
+       if ((ipaddr & mask) != (inp->s_addr & mask))
+           continue;
+
+       /* Copy out the local ethernet address */
+       hwaddr->sa_family = AF_UNSPEC;
+       BCOPY((caddr_t) &arpcom.ac_enaddr, hwaddr->sa_data,
+             sizeof(arpcom.ac_enaddr));
+       return 1;               /* success! */
+    }
+
+    /* couldn't find one */
+    return 0;
+}
diff --git a/pppd/upap.c b/pppd/upap.c
new file mode 100644 (file)
index 0000000..47b3477
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: upap.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "ppp.h"
+#include "pppd.h"
+#include "upap.h"
+
+
+upap_state upap[NPPP];         /* UPAP state; one for each unit */
+
+
+static void upap_timeout __ARGS((caddr_t));
+static void upap_rauthreq __ARGS((upap_state *, u_char *, int, int));
+static void upap_rauthack __ARGS((upap_state *, u_char *, int, int));
+static void upap_rauthnak __ARGS((upap_state *, u_char *, int, int));
+static void upap_sauthreq __ARGS((upap_state *));
+static void upap_sresp __ARGS((upap_state *, int, int, char *, int));
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+void
+upap_init(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    u->us_unit = unit;
+    u->us_user = NULL;
+    u->us_userlen = 0;
+    u->us_passwd = NULL;
+    u->us_passwdlen = 0;
+    u->us_clientstate = UPAPCS_INITIAL;
+    u->us_serverstate = UPAPSS_INITIAL;
+    u->us_id = 0;
+    u->us_timeouttime = UPAP_DEFTIMEOUT;
+    u->us_maxtransmits = 10;
+}
+
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(unit, user, password)
+    int unit;
+    char *user, *password;
+{
+    upap_state *u = &upap[unit];
+
+    /* Save the username and password we're given */
+    u->us_user = user;
+    u->us_userlen = strlen(user);
+    u->us_passwd = password;
+    u->us_passwdlen = strlen(password);
+    u->us_transmits = 0;
+
+    /* Lower layer up yet? */
+    if (u->us_clientstate == UPAPCS_INITIAL ||
+       u->us_clientstate == UPAPCS_PENDING) {
+       u->us_clientstate = UPAPCS_PENDING;
+       return;
+    }
+
+    upap_sauthreq(u);                  /* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    /* Lower layer up yet? */
+    if (u->us_serverstate == UPAPSS_INITIAL ||
+       u->us_serverstate == UPAPSS_PENDING) {
+       u->us_serverstate = UPAPSS_PENDING;
+       return;
+    }
+
+    u->us_serverstate = UPAPSS_LISTEN;
+}
+
+
+/*
+ * upap_timeout - Timeout expired.
+ */
+static void
+upap_timeout(arg)
+    caddr_t arg;
+{
+    upap_state *u = (upap_state *) arg;
+
+    if (u->us_clientstate != UPAPCS_AUTHREQ)
+       return;
+
+    if (u->us_transmits >= u->us_maxtransmits) {
+       /* give up in disgust */
+       syslog(LOG_ERR, "No response to PAP authenticate-requests");
+       u->us_clientstate = UPAPCS_BADAUTH;
+       auth_withpeer_fail(u->us_unit, UPAP);
+       return;
+    }
+
+    upap_sauthreq(u);          /* Send Authenticate-Request */
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+void
+upap_lowerup(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    if (u->us_clientstate == UPAPCS_INITIAL)
+       u->us_clientstate = UPAPCS_CLOSED;
+    else if (u->us_clientstate == UPAPCS_PENDING) {
+       upap_sauthreq(u);       /* send an auth-request */
+    }
+
+    if (u->us_serverstate == UPAPSS_INITIAL)
+       u->us_serverstate = UPAPSS_CLOSED;
+    else if (u->us_serverstate == UPAPSS_PENDING)
+       u->us_serverstate = UPAPSS_LISTEN;
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+void
+upap_lowerdown(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    if (u->us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */
+       UNTIMEOUT(upap_timeout, (caddr_t) u);   /* Cancel timeout */
+
+    u->us_clientstate = UPAPCS_INITIAL;
+    u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen.  In any case, pretend lower layer went down.
+ */
+void
+upap_protrej(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    if (u->us_clientstate == UPAPCS_AUTHREQ) {
+       syslog(LOG_ERR, "PAP authentication failed due to protocol-reject");
+       auth_withpeer_fail(unit, UPAP);
+    }
+    if (u->us_serverstate == UPAPSS_LISTEN) {
+       syslog(LOG_ERR, "PAP authentication of peer failed (protocol-reject)");
+       auth_peer_fail(unit, UPAP);
+    }
+    upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+void
+upap_input(unit, inpacket, l)
+    int unit;
+    u_char *inpacket;
+    int l;
+{
+    upap_state *u = &upap[unit];
+    u_char *inp;
+    u_char code, id;
+    int len;
+
+    /*
+     * Parse header (code, id and length).
+     * If packet too short, drop it.
+     */
+    inp = inpacket;
+    if (l < UPAP_HEADERLEN) {
+       UPAPDEBUG((LOG_INFO, "upap_input: rcvd short header."));
+       return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    GETSHORT(len, inp);
+    if (len < UPAP_HEADERLEN) {
+       UPAPDEBUG((LOG_INFO, "upap_input: rcvd illegal length."));
+       return;
+    }
+    if (len > l) {
+       UPAPDEBUG((LOG_INFO, "upap_input: rcvd short packet."));
+       return;
+    }
+    len -= UPAP_HEADERLEN;
+
+    /*
+     * Action depends on code.
+     */
+    switch (code) {
+    case UPAP_AUTHREQ:
+       upap_rauthreq(u, inp, id, len);
+       break;
+
+    case UPAP_AUTHACK:
+       upap_rauthack(u, inp, id, len);
+       break;
+
+    case UPAP_AUTHNAK:
+       upap_rauthnak(u, inp, id, len);
+       break;
+
+    default:                           /* XXX Need code reject */
+       break;
+    }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(u, inp, id, len)
+    upap_state *u;
+    u_char *inp;
+    int id;
+    int len;
+{
+    u_char ruserlen, rpasswdlen;
+    char *ruser, *rpasswd;
+    int retcode;
+    char *msg;
+    int msglen;
+
+    UPAPDEBUG((LOG_INFO, "upap_rauth: Rcvd id %d.", id));
+
+    if (u->us_serverstate < UPAPSS_LISTEN)
+       return;
+
+    /*
+     * If we receive a duplicate authenticate-request, we are
+     * supposed to return the same status as for the first request.
+     */
+    if (u->us_serverstate == UPAPSS_OPEN) {
+       upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
+       return;
+    }
+    if (u->us_serverstate == UPAPSS_BADAUTH) {
+       upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
+       return;
+    }
+
+    /*
+     * Parse user/passwd.
+     */
+    if (len < sizeof (u_char)) {
+       UPAPDEBUG((LOG_INFO, "upap_rauth: rcvd short packet."));
+       return;
+    }
+    GETCHAR(ruserlen, inp);
+    len -= sizeof (u_char) + ruserlen + sizeof (u_char);;
+    if (len < 0) {
+       UPAPDEBUG((LOG_INFO, "upap_rauth: rcvd short packet."));
+       return;
+    }
+    ruser = (char *) inp;
+    INCPTR(ruserlen, inp);
+    GETCHAR(rpasswdlen, inp);
+    if (len < rpasswdlen) {
+       UPAPDEBUG((LOG_INFO, "upap_rauth: rcvd short packet."));
+       return;
+    }
+    rpasswd = (char *) inp;
+
+    /*
+     * Check the username and password given.
+     */
+    retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
+                          rpasswdlen, &msg, &msglen);
+
+    upap_sresp(u, retcode, id, msg, msglen);
+
+    if (retcode == UPAP_AUTHACK) {
+       u->us_serverstate = UPAPSS_OPEN;
+       auth_peer_success(u->us_unit, UPAP);
+    } else {
+       u->us_serverstate = UPAPSS_BADAUTH;
+       auth_peer_fail(u->us_unit, UPAP);
+    }
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void
+upap_rauthack(u, inp, id, len)
+    upap_state *u;
+    u_char *inp;
+    int id;
+    int len;
+{
+    u_char msglen;
+    char *msg;
+
+    UPAPDEBUG((LOG_INFO, "upap_rauthack: Rcvd id %d.", id));
+    if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+       return;
+
+    /*
+     * Parse message.
+     */
+    if (len < sizeof (u_char)) {
+       UPAPDEBUG((LOG_INFO, "upap_rauthack: rcvd short packet."));
+       return;
+    }
+    GETCHAR(msglen, inp);
+    len -= sizeof (u_char);
+    if (len < msglen) {
+       UPAPDEBUG((LOG_INFO, "upap_rauthack: rcvd short packet."));
+       return;
+    }
+    msg = (char *) inp;
+    PRINTMSG(msg, msglen);
+
+    u->us_clientstate = UPAPCS_OPEN;
+
+    auth_withpeer_success(u->us_unit, UPAP);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nakk.
+ */
+static void
+upap_rauthnak(u, inp, id, len)
+    upap_state *u;
+    u_char *inp;
+    int id;
+    int len;
+{
+    u_char msglen;
+    char *msg;
+
+    UPAPDEBUG((LOG_INFO, "upap_rauthnak: Rcvd id %d.", id));
+    if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+       return;
+
+    /*
+     * Parse message.
+     */
+    if (len < sizeof (u_char)) {
+       UPAPDEBUG((LOG_INFO, "upap_rauthnak: rcvd short packet."));
+       return;
+    }
+    GETCHAR(msglen, inp);
+    len -= sizeof (u_char);
+    if (len < msglen) {
+       UPAPDEBUG((LOG_INFO, "upap_rauthnak: rcvd short packet."));
+       return;
+    }
+    msg = (char *) inp;
+    PRINTMSG(msg, msglen);
+
+    u->us_clientstate = UPAPCS_BADAUTH;
+
+    syslog(LOG_ERR, "PAP authentication failed");
+    auth_withpeer_fail(u->us_unit, UPAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(u)
+    upap_state *u;
+{
+    u_char *outp;
+    int outlen;
+
+    outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
+       u->us_userlen + u->us_passwdlen;
+    outp = outpacket_buf;
+    
+    MAKEHEADER(outp, UPAP);
+
+    PUTCHAR(UPAP_AUTHREQ, outp);
+    PUTCHAR(++u->us_id, outp);
+    PUTSHORT(outlen, outp);
+    PUTCHAR(u->us_userlen, outp);
+    BCOPY(u->us_user, outp, u->us_userlen);
+    INCPTR(u->us_userlen, outp);
+    PUTCHAR(u->us_passwdlen, outp);
+    BCOPY(u->us_passwd, outp, u->us_passwdlen);
+
+    output(u->us_unit, outpacket_buf, outlen + DLLHEADERLEN);
+
+    UPAPDEBUG((LOG_INFO, "upap_sauth: Sent id %d.", u->us_id));
+
+    TIMEOUT(upap_timeout, (caddr_t) u, u->us_timeouttime);
+    ++u->us_transmits;
+    u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(u, code, id, msg, msglen)
+    upap_state *u;
+    u_char code, id;
+    char *msg;
+    int msglen;
+{
+    u_char *outp;
+    int outlen;
+
+    outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+    outp = outpacket_buf;
+    MAKEHEADER(outp, UPAP);
+
+    PUTCHAR(code, outp);
+    PUTCHAR(id, outp);
+    PUTSHORT(outlen, outp);
+    PUTCHAR(msglen, outp);
+    BCOPY(msg, outp, msglen);
+    output(u->us_unit, outpacket_buf, outlen + DLLHEADERLEN);
+
+    UPAPDEBUG((LOG_INFO, "upap_sresp: Sent code %d, id %d.", code, id));
+}
diff --git a/pppd/upap.h b/pppd/upap.h
new file mode 100644 (file)
index 0000000..c77c198
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * $Id: upap.h,v 1.1 1993/11/11 03:54:25 paulus Exp $
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ   1       /* Authenticate-Request */
+#define UPAP_AUTHACK   2       /* Authenticate-Ack */
+#define UPAP_AUTHNAK   3       /* Authenticate-Nak */
+
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+    int us_unit;               /* Interface unit number */
+    char *us_user;             /* User */
+    int us_userlen;            /* User length */
+    char *us_passwd;           /* Password */
+    int us_passwdlen;          /* Password length */
+    int us_clientstate;                /* Client state */
+    int us_serverstate;                /* Server state */
+    u_char us_id;              /* Current id */
+    int us_timeouttime;                /* Timeout time in milliseconds */
+    int us_transmits;          /* Number of auth-reqs sent */
+    int us_maxtransmits;       /* Maximum number of auth-reqs to send */
+} upap_state;
+
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL 0       /* Connection down */
+#define UPAPCS_CLOSED  1       /* Connection up, haven't requested auth */
+#define UPAPCS_PENDING 2       /* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ 3       /* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN    4       /* We've received an Ack */
+#define UPAPCS_BADAUTH 5       /* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL 0       /* Connection down */
+#define UPAPSS_CLOSED  1       /* Connection up, haven't requested auth */
+#define UPAPSS_PENDING 2       /* Connection down, have requested auth */
+#define UPAPSS_LISTEN  3       /* Listening for an Authenticate */
+#define UPAPSS_OPEN    4       /* We've sent an Ack */
+#define UPAPSS_BADAUTH 5       /* We've sent a Nak */
+
+
+/*
+ * Timeouts.
+ */
+#define UPAP_DEFTIMEOUT        3       /* Timeout time in seconds */
+
+
+extern upap_state upap[];
+
+void upap_init __ARGS((int));
+void upap_authwithpeer __ARGS((int, char *, char *));
+void upap_authpeer __ARGS((int));
+void upap_lowerup __ARGS((int));
+void upap_lowerdown __ARGS((int));
+void upap_input __ARGS((int, u_char *, int));
+void upap_protrej __ARGS((int));
diff --git a/pppstats/pppstats.c b/pppstats/pppstats.c
new file mode 100644 (file)
index 0000000..5fb2f77
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * print PPP statistics:
+ *     pppstats [-i interval] [-v] [interface] [system] [core] 
+ *
+ *     Brad Parker (brad@cayman.com) 6/92
+ *
+ * from the original "slstats" by Van Jaconson
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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 the University of California, Berkeley.  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.
+ *
+ *     Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *     - Initial distribution.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: pppstats.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+#endif
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#ifndef KVMLIB
+#include <machine/pte.h>
+#endif
+#ifdef sun
+#include <kvm.h>
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <signal.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+
+#define        VJC     1
+#include <net/slcompress.h>
+#include <net/if_ppp.h>
+
+#ifdef STREAMS
+#include <sys/stream.h>
+#include "ppp_str.h"
+#endif
+
+#ifdef STREAMS
+struct nlist nl[] = {
+#define N_SOFTC 0
+       { "_pii" },
+       "",
+};
+#else
+struct nlist nl[] = {
+#define N_SOFTC 0
+       { "_ppp_softc" },
+       "",
+};
+#endif
+
+#ifndef KVMLIB
+struct pte *Sysmap;
+int    kmem;
+extern off_t lseek();
+#endif
+
+#ifdef sun
+kvm_t  *kd;
+#endif
+
+#ifdef sun
+char   *system = "/vmunix";
+#else
+#ifdef __NetBSD__
+char   *system = "/netbsd";
+#else
+char   *system = "/386bsd";
+#endif
+#endif
+
+#ifndef KVMLIB
+char   *kmemf = "/dev/kmem";
+#else
+char   *kmemf;
+#endif
+int    kflag;
+int    vflag;
+unsigned interval = 5;
+int    unit;
+
+extern char *malloc();
+
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       --argc; ++argv;
+       while (argc > 0) {
+               if (strcmp(argv[0], "-v") == 0) {
+                       ++vflag;
+                       ++argv, --argc;
+                       continue;
+               }
+               if (strcmp(argv[0], "-i") == 0 && argv[1] &&
+                   isdigit(argv[1][0])) {
+                       interval = atoi(argv[1]);
+                       if (interval <= 0)
+                               usage();
+                       ++argv, --argc;
+                       ++argv, --argc;
+                       continue;
+               }
+               if (isdigit(argv[0][0])) {
+                       unit = atoi(argv[0]);
+                       if (unit < 0)
+                               usage();
+                       ++argv, --argc;
+                       continue;
+               }
+               if (kflag)
+                       usage();
+
+               system = *argv;
+               ++argv, --argc;
+               if (argc > 0) {
+                       kmemf = *argv++;
+                       --argc;
+                       kflag++;
+               }
+       }
+#ifndef KVMLIB
+       if (nlist(system, nl) < 0 || nl[0].n_type == 0) {
+               fprintf(stderr, "%s: no namelist\n", system);
+               exit(1);
+       }
+       kmem = open(kmemf, O_RDONLY);
+       if (kmem < 0) {
+               perror(kmemf);
+               exit(1);
+       }
+       if (kflag) {
+               off_t off;
+
+               Sysmap = (struct pte *)
+                  malloc((u_int)(nl[N_SYSSIZE].n_value * sizeof(struct pte)));
+               if (!Sysmap) {
+                       fputs("netstat: can't get memory for Sysmap.\n", stderr);
+                       exit(1);
+               }
+               off = nl[N_SYSMAP].n_value & ~KERNBASE;
+               (void)lseek(kmem, off, L_SET);
+               (void)read(kmem, (char *)Sysmap,
+                   (int)(nl[N_SYSSIZE].n_value * sizeof(struct pte)));
+       }
+#else
+#ifdef sun
+       /* SunOS */
+       if ((kd = kvm_open(system, kmemf, (char *)0, O_RDONLY, NULL)) == NULL) {
+         perror("kvm_open");
+         exit(1);
+       }
+#else
+       /* BSD4.3+ */
+       if (kvm_openfiles(system, kmemf, (char *)0) == -1) {
+         fprintf(stderr, "kvm_openfiles: %s", kvm_geterr());
+         exit(1);
+       }
+#endif
+
+#ifdef sun
+       if (kvm_nlist(kd, nl)) {
+#else
+       if (kvm_nlist(nl)) {
+#endif
+         fprintf(stderr, "pppstats: can't find symbols in nlist\n");
+         exit(1);
+       }
+#endif
+       intpr();
+       exit(0);
+}
+
+#ifndef KVMLIB
+/*
+ * Seek into the kernel for a value.
+ */
+off_t
+klseek(fd, base, off)
+       int fd, off;
+       off_t base;
+{
+       if (kflag) {
+               /* get kernel pte */
+               base &= ~KERNBASE;
+                base = ctob(Sysmap[btop(base)].pg_pfnum) + (base & PGOFSET);
+       }
+       return (lseek(fd, base, off));
+}
+#endif
+
+usage()
+{
+       fprintf(stderr,"usage: pppstats [-i interval] [-v] [unit] [system] [core]\n");
+       exit(1);
+}
+
+u_char signalled;                      /* set if alarm goes off "early" */
+
+#define V(offset) ((line % 20)? sc->offset - osc->offset : sc->offset)
+
+/*
+ * Print a running summary of interface statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval.  Assumes that interval is non-zero.
+ * First line printed at top of screen is always cumulative.
+ */
+intpr()
+{
+       register int line = 0;
+       int oldmask;
+#ifdef __STDC__
+       void catchalarm(int);
+#else
+       void catchalarm();
+#endif
+
+#ifdef STREAMS
+#define STRUCT struct ppp_if_info
+#else
+#define STRUCT struct ppp_softc
+#endif
+
+       STRUCT *sc, *osc;
+
+       nl[N_SOFTC].n_value += unit * sizeof(struct ppp_softc);
+       sc = (STRUCT *)malloc(sizeof(STRUCT));
+       osc = (STRUCT *)malloc(sizeof(STRUCT));
+
+       bzero((char *)osc, sizeof(STRUCT));
+
+       while (1) {
+#ifndef KVMLIB
+               if (klseek(kmem, (off_t)nl[N_SOFTC].n_value, 0) < 0)
+                       if(errno != EINTR)
+                               perror("kmem seek");
+               if (read(kmem, (char *)sc, sizeof(STRUCT)) <= 0)
+                       perror("kmem read");
+#else
+#ifdef sun
+               if (kvm_read(kd, nl[N_SOFTC].n_value,
+#else
+               if (kvm_read(nl[N_SOFTC].n_value,
+#endif
+                            sc, sizeof(STRUCT)) !=
+                   sizeof(STRUCT))
+                 perror("kvm_read");
+#endif
+
+               (void)signal(SIGALRM, catchalarm);
+               signalled = 0;
+               (void)alarm(interval);
+
+               if ((line % 20) == 0) {
+                       printf("%6.6s %6.6s %6.6s %6.6s %6.6s",
+                               "in", "pack", "comp", "uncomp", "err");
+                       if (vflag)
+                               printf(" %6.6s %6.6s", "toss", "ip");
+                       printf(" | %6.6s %6.6s %6.6s %6.6s %6.6s",
+                               "out", "pack", "comp", "uncomp", "ip");
+                       if (vflag)
+                               printf(" %6.6s %6.6s", "search", "miss");
+                       putchar('\n');
+               }
+
+#ifdef STREAMS
+#define        COMP    pii_sc_comp
+#define        STATS   pii_ifnet
+#else
+#define        COMP    sc_comp
+#define        STATS   sc_if
+#endif
+
+               printf("%6d %6d %6d %6d %6d",
+#if BSD > 43
+                       V(STATS.if_ibytes),
+#else
+                       0,
+#endif
+                       V(STATS.if_ipackets),
+                       V(COMP.sls_compressedin),
+                       V(COMP.sls_uncompressedin),
+                       V(COMP.sls_errorin));
+               if (vflag)
+                       printf(" %6d %6d",
+                               V(COMP.sls_tossed),
+                               V(STATS.if_ipackets) -
+                                 V(COMP.sls_compressedin) -
+                                 V(COMP.sls_uncompressedin) -
+                                 V(COMP.sls_errorin));
+               printf(" | %6d %6d %6d %6d %6d",
+#if BSD > 43
+                       V(STATS.if_obytes),
+#else
+                       0,
+#endif
+                       V(STATS.if_opackets),
+                       V(COMP.sls_compressed),
+                       V(COMP.sls_packets) - V(COMP.sls_compressed),
+                       V(STATS.if_opackets) - V(COMP.sls_packets));
+               if (vflag)
+                       printf(" %6d %6d",
+                               V(COMP.sls_searches),
+                               V(COMP.sls_misses));
+
+               putchar('\n');
+               fflush(stdout);
+               line++;
+               oldmask = sigblock(sigmask(SIGALRM));
+               if (! signalled) {
+                       sigpause(0);
+               }
+               sigsetmask(oldmask);
+               signalled = 0;
+               (void)alarm(interval);
+               bcopy((char *)sc, (char *)osc, sizeof(STRUCT));
+       }
+}
+
+/*
+ * Called if an interval expires before sidewaysintpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+void catchalarm(arg)
+int arg;
+{
+       signalled = 1;
+}