--- /dev/null
+# $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>
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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));
+}
--- /dev/null
+/*
+ * 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) */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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));
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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,
+ ¬ime)) == -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);
+}
--- /dev/null
+
+
+/*
+ ***********************************************************************
+ ** 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) ********************************
+ */
--- /dev/null
+/*
+ ***********************************************************************
+ ** 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__ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/* $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"
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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));
+}
--- /dev/null
+/*
+ * 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));
--- /dev/null
+/*
+ * 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;
+}