From 0b63a24d54ba4708c88e31bdd74b0145956c1478 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 11 Nov 1993 03:56:47 +0000 Subject: [PATCH] Initial revision --- pppd/Makefile.bsd | 17 + pppd/auth.c | 828 ++++++++++++++++++++++++++++ pppd/chap.c | 754 +++++++++++++++++++++++++ pppd/chap.h | 111 ++++ pppd/fsm.c | 769 ++++++++++++++++++++++++++ pppd/fsm.h | 132 +++++ pppd/ipcp.c | 1056 +++++++++++++++++++++++++++++++++++ pppd/ipcp.h | 65 +++ pppd/lcp.c | 1281 +++++++++++++++++++++++++++++++++++++++++++ pppd/lcp.h | 78 +++ pppd/magic.c | 70 +++ pppd/magic.h | 24 + pppd/main.c | 1227 +++++++++++++++++++++++++++++++++++++++++ pppd/md5.c | 298 ++++++++++ pppd/md5.h | 58 ++ pppd/options.c | 1153 ++++++++++++++++++++++++++++++++++++++ pppd/patchlevel.h | 5 + pppd/pathnames.h | 15 + pppd/ppp.h | 41 ++ pppd/pppd.h | 199 +++++++ pppd/sys-bsd.c | 482 ++++++++++++++++ pppd/sys-str.c | 624 +++++++++++++++++++++ pppd/upap.c | 484 ++++++++++++++++ pppd/upap.h | 89 +++ pppstats/pppstats.c | 354 ++++++++++++ 25 files changed, 10214 insertions(+) create mode 100644 pppd/Makefile.bsd create mode 100644 pppd/auth.c create mode 100644 pppd/chap.c create mode 100644 pppd/chap.h create mode 100644 pppd/fsm.c create mode 100644 pppd/fsm.h create mode 100644 pppd/ipcp.c create mode 100644 pppd/ipcp.h create mode 100644 pppd/lcp.c create mode 100644 pppd/lcp.h create mode 100644 pppd/magic.c create mode 100644 pppd/magic.h create mode 100644 pppd/main.c create mode 100644 pppd/md5.c create mode 100644 pppd/md5.h create mode 100644 pppd/options.c create mode 100644 pppd/patchlevel.h create mode 100644 pppd/pathnames.h create mode 100644 pppd/ppp.h create mode 100644 pppd/pppd.h create mode 100644 pppd/sys-bsd.c create mode 100644 pppd/sys-str.c create mode 100644 pppd/upap.c create mode 100644 pppd/upap.h create mode 100644 pppstats/pppstats.c diff --git a/pppd/Makefile.bsd b/pppd/Makefile.bsd new file mode 100644 index 0000000..54652bf --- /dev/null +++ b/pppd/Makefile.bsd @@ -0,0 +1,17 @@ +# $Id: Makefile.bsd,v 1.1 1993/11/11 03:54:25 paulus Exp $ + +COPTS?= -g +CFLAGS+= -DDEBUGALL + +PROG= pppd +SRCS= main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c \ + auth.c options.c sys-bsd.c +MAN8= pppd.0 +SUBDIR= pppstats chat +BINMODE=4555 +BINOWN= root + +LDADD= -lcrypt -lutil +DPADD= ${LIBCRYPT} ${LIBUTIL} + +.include diff --git a/pppd/auth.c b/pppd/auth.c new file mode 100644 index 0000000..cdeb2b6 --- /dev/null +++ b/pppd/auth.c @@ -0,0 +1,828 @@ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: auth.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 +#ifndef __GNUC__ +/* why alloca.h doesn't define what alloca() returns is a mystery */ +char *alloca __ARGS((int)); +#endif /*__GNUC__*/ +#endif /*sparc*/ + +/* Used for storing a sequence of words. Usually malloced. */ +struct wordlist { + struct wordlist *next; + char word[1]; +}; + +/* Bits in scan_authfile return value */ +#define NONWILD_SERVER 1 +#define NONWILD_CLIENT 2 + +#define ISWILD(word) (word[0] == '*' && word[1] == 0) + +#define FALSE 0 +#define TRUE 1 + +extern char user[]; +extern char passwd[]; +extern char devname[]; +extern char our_name[]; +extern char remote_name[]; +extern char hostname[]; +extern int uselogin; +extern int usehostname; +extern int auth_required; + +/* Records which authentication operations haven't completed yet. */ +static int auth_pending[NPPP]; +static int logged_in; +static struct wordlist *addresses[NPPP]; + +/* Bits in auth_pending[] */ +#define UPAP_WITHPEER 1 +#define UPAP_PEER 2 +#define CHAP_WITHPEER 4 +#define CHAP_PEER 8 + +/* Prototypes */ +void check_access __ARGS((FILE *, char *)); + +static int login __ARGS((char *, char *, char **, int *)); +static void logout __ARGS((void)); +static int get_upap_passwd __ARGS((void)); +static int have_upap_secret __ARGS((void)); +static int have_chap_secret __ARGS((char *, char *)); +static int scan_authfile __ARGS((FILE *, char *, char *, char *, + struct wordlist **, char *)); +static void free_wordlist __ARGS((struct wordlist *)); + +extern char *crypt __ARGS((char *, char *)); + +/* + * An Open on LCP has requested a change from Dead to Establish phase. + * Do what's necessary to bring the physical layer up. + */ +void +link_required(unit) + int unit; +{ +} + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void +link_terminated(unit) + int unit; +{ + if (logged_in) + logout(); + if (lcp_wantoptions[unit].restart) { + lcp_lowerdown(unit); + lcp_lowerup(unit); + } else + EXIT(unit); +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void +link_established(unit) + int unit; +{ + int auth; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ho = &lcp_hisoptions[unit]; + + if (auth_required && !(go->neg_chap || go->neg_upap)) { + /* + * We wanted the peer to authenticate himself, and he refused: + * tell him to go away. + */ + syslog(LOG_WARNING, "peer refused to authenticate"); + lcp_close(unit); + return; + } + + auth = 0; + if (go->neg_chap) { + ChapAuthPeer(unit, our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } else if (go->neg_upap) { + upap_authpeer(unit); + auth |= UPAP_PEER; + } + if (ho->neg_chap) { + ChapAuthWithPeer(unit, our_name, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } else if (ho->neg_upap) { + upap_authwithpeer(unit, user, passwd); + auth |= UPAP_WITHPEER; + } + auth_pending[unit] = auth; + + if (!auth) + ipcp_open(unit); +} + +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void +auth_peer_fail(unit, protocol) + int unit, protocol; +{ + /* + * Authentication failure: take the link down + */ + lcp_close(unit); +} + +/* + * The peer has been successfully authenticated using `protocol'. + */ +void +auth_peer_success(unit, protocol) + int unit, protocol; +{ + int bit; + + switch (protocol) { + case CHAP: + bit = CHAP_PEER; + break; + case UPAP: + bit = UPAP_PEER; + break; + default: + syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x", + protocol); + return; + } + + /* + * If there is no more authentication still to be done, + * proceed to the network phase. + */ + if ((auth_pending[unit] &= ~bit) == 0) + ipcp_open(unit); +} + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void +auth_withpeer_fail(unit, protocol) + int unit, protocol; +{ + /* + * We've failed to authenticate ourselves to our peer. + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void +auth_withpeer_success(unit, protocol) + int unit, protocol; +{ + int bit; + + switch (protocol) { + case CHAP: + bit = CHAP_WITHPEER; + break; + case UPAP: + bit = UPAP_WITHPEER; + break; + default: + syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x", + protocol); + } + + /* + * If there is no more authentication still being done, + * proceed to the network phase. + */ + if ((auth_pending[unit] &= ~bit) == 0) + ipcp_open(unit); +} + + +/* + * check_auth_options - called to check authentication options. + */ +void +check_auth_options() +{ + lcp_options *wo = &lcp_wantoptions[0]; + lcp_options *ao = &lcp_allowoptions[0]; + + /* Default our_name to hostname, and user to our_name */ + if (our_name[0] == 0 || usehostname) + strcpy(our_name, hostname); + if (user[0] == 0) + strcpy(user, our_name); + + /* If authentication is required, ask peer for CHAP or PAP. */ + if (auth_required && !wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate ourselves and/or the peer. + */ + if (ao->neg_upap && passwd[0] == 0 && !get_upap_passwd()) + ao->neg_upap = 0; + if (wo->neg_upap && !uselogin && !have_upap_secret()) + wo->neg_upap = 0; + if (ao->neg_chap && !have_chap_secret(our_name, remote_name)) + ao->neg_chap = 0; + if (wo->neg_chap && !have_chap_secret(remote_name, our_name)) + wo->neg_chap = 0; + + if (auth_required && !wo->neg_chap && !wo->neg_upap) { + fprintf(stderr, "\ +pppd: peer authentication required but no authentication files accessible\n"); + exit(1); + } + +} + + +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +int +check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen) + int unit; + char *auser; + int userlen; + char *apasswd; + int passwdlen; + char **msg; + int *msglen; +{ + int ret; + char *filename; + FILE *f; + struct wordlist *addrs; + char passwd[256], user[256]; + char secret[MAXWORDLEN]; + static int attempts = 0; + + /* + * Make copies of apasswd and auser, then null-terminate them. + */ + BCOPY(apasswd, passwd, passwdlen); + passwd[passwdlen] = '\0'; + BCOPY(auser, user, userlen); + user[userlen] = '\0'; + + /* + * Open the file of upap secrets and scan for a suitable secret + * for authenticating this user. + */ + filename = _PATH_UPAPFILE; + addrs = NULL; + ret = UPAP_AUTHACK; + f = fopen(filename, "r"); + if (f == NULL) { + if (!uselogin) { + syslog(LOG_ERR, "Can't open upap password file %s: %m", filename); + ret = UPAP_AUTHNAK; + } + + } else { + check_access(f, filename); + if (scan_authfile(f, user, our_name, secret, &addrs, filename) < 0 + || (secret[0] != 0 && strcmp(passwd, secret) != 0 + && strcmp(crypt(passwd, secret), secret) != 0)) { + syslog(LOG_WARNING, "upap authentication failure for %s", user); + ret = UPAP_AUTHNAK; + } + fclose(f); + } + + if (uselogin && ret == UPAP_AUTHACK) { + ret = login(user, passwd, msg, msglen); + if (ret == UPAP_AUTHNAK) { + syslog(LOG_WARNING, "upap login failure for %s", user); + } + } + + if (ret == UPAP_AUTHNAK) { + *msg = "Login incorrect"; + *msglen = strlen(*msg); + /* + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + syslog(LOG_WARNING, "%d LOGIN FAILURES ON %s, %s", + attempts, devname, user); + quit(); + } + if (attempts > 3) + sleep((u_int) (attempts - 3) * 5); + if (addrs != NULL) + free_wordlist(addrs); + + } else { + attempts = 0; /* Reset count */ + *msg = "Login ok"; + *msglen = strlen(*msg); + if (addresses[unit] != NULL) + free_wordlist(addresses[unit]); + addresses[unit] = addrs; + } + + return ret; +} + + +/* + * login - Check the user name and password against the system + * password database, and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Login failed. + * UPAP_AUTHACK: Login succeeded. + * In either case, msg points to an appropriate message. + */ +static int +login(user, passwd, msg, msglen) + char *user; + char *passwd; + char **msg; + int *msglen; +{ + struct passwd *pw; + char *epasswd; + char *tty; + + if ((pw = getpwnam(user)) == NULL) { + return (UPAP_AUTHNAK); + } + + /* + * XXX If no passwd, let them login without one. + */ + if (pw->pw_passwd == '\0') { + return (UPAP_AUTHACK); + } + + epasswd = crypt(passwd, pw->pw_passwd); + if (strcmp(epasswd, pw->pw_passwd)) { + return (UPAP_AUTHNAK); + } + + syslog(LOG_INFO, "user %s logged in", user); + + /* + * Write a wtmp entry for this user. + */ + tty = strrchr(devname, '/'); + if (tty == NULL) + tty = devname; + else + tty++; + logwtmp(tty, user, ""); /* Add wtmp login entry */ + logged_in = TRUE; + + return (UPAP_AUTHACK); +} + +/* + * logout - Logout the user. + */ +static void +logout() +{ + char *tty; + + tty = strrchr(devname, '/'); + if (tty == NULL) + tty = devname; + else + tty++; + logwtmp(tty, "", ""); /* Wipe out wtmp logout entry */ + logged_in = FALSE; +} + + +/* + * get_upap_passwd - get a password for authenticating ourselves with + * our peer using PAP. Returns 1 on success, 0 if no suitable password + * could be found. + */ +static int +get_upap_passwd() +{ + char *filename; + FILE *f; + struct wordlist *addrs; + char secret[MAXWORDLEN]; + + filename = _PATH_UPAPFILE; + addrs = NULL; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + check_access(f, filename); + if (scan_authfile(f, user, remote_name, secret, NULL, filename) < 0) + return 0; + strncpy(passwd, secret, MAXSECRETLEN); + passwd[MAXSECRETLEN-1] = 0; + return 1; +} + + +/* + * have_upap_secret - check whether we have a PAP file with any + * secrets that we could possibly use for authenticating the peer. + */ +static int +have_upap_secret() +{ + FILE *f; + int ret; + char *filename; + + filename = _PATH_UPAPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + + ret = scan_authfile(f, NULL, our_name, NULL, NULL, filename); + fclose(f); + if (ret < 0) + return 0; + + return 1; +} + + +/* + * have_chap_secret - check whether we have a CHAP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +int +have_chap_secret(client, server) + char *client; + char *server; +{ + FILE *f; + int ret; + char *filename; + + filename = _PATH_CHAPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + + if (client[0] == 0) + client = NULL; + else if (server[0] == 0) + server = NULL; + + ret = scan_authfile(f, client, server, NULL, NULL, filename); + fclose(f); + if (ret < 0) + return 0; + + return 1; +} + + +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int +get_secret(unit, client, server, secret, secret_len, save_addrs) + int unit; + char *client; + char *server; + char *secret; + int *secret_len; +{ + FILE *f; + int ret, len; + char *filename; + struct wordlist *addrs; + char secbuf[MAXWORDLEN]; + + filename = _PATH_CHAPFILE; + addrs = NULL; + secbuf[0] = 0; + + f = fopen(filename, "r"); + if (f == NULL) { + syslog(LOG_ERR, "Can't open chap secret file %s: %m", filename); + return 0; + } + check_access(f, filename); + + ret = scan_authfile(f, client, server, secbuf, &addrs, filename); + fclose(f); + if (ret < 0) + return 0; + + if (save_addrs) { + if (addresses[unit] != NULL) + free_wordlist(addresses[unit]); + addresses[unit] = addrs; + } + + len = strlen(secbuf); + if (len > MAXSECRETLEN) { + syslog(LOG_ERR, "Secret for %s on %s is too long", client, server); + len = MAXSECRETLEN; + } + BCOPY(secbuf, secret, len); + *secret_len = len; + + return 1; +} + +/* + * auth_ip_addr - check whether the peer is authorized to use + * a given IP address. Returns 1 if authorized, 0 otherwise. + */ +int +auth_ip_addr(unit, addr) + int unit; + u_long addr; +{ + u_long a; + struct hostent *hp; + struct wordlist *addrs; + + if ((addrs = addresses[unit]) == NULL) + return 1; /* no restriction */ + + for (; addrs != NULL; addrs = addrs->next) { + /* "-" means no addresses authorized */ + if (strcmp(addrs->word, "-") == 0) + break; + if ((a = inet_addr(addrs->word)) == -1) { + if ((hp = gethostbyname(addrs->word)) == NULL) { + syslog(LOG_WARNING, "unknown host %s in auth. address list", + addrs->word); + continue; + } else + a = *(u_long *)hp->h_addr; + } + if (addr == a) + return 1; + } + return 0; /* not in list => can't have it */ +} + +/* + * check_access - complain if a secret file has too-liberal permissions. + */ +void +check_access(f, filename) + FILE *f; + char *filename; +{ + struct stat sbuf; + + if (fstat(fileno(f), &sbuf) < 0) { + syslog(LOG_WARNING, "cannot stat secret file %s: %m", filename); + } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + syslog(LOG_WARNING, "Warning - secret file %s has world and/or group access", filename); + } +} + + +/* + * scan_authfile - Scan an authorization file for a secret suitable + * for authenticating `client' on `server'. The return value is -1 + * if no secret is found, otherwise >= 0. The return value has + * NONWILD_CLIENT set if the secret didn't have "*" for the client, and + * NONWILD_SERVER set if the secret didn't have "*" for the server. + * Any following words on the line (i.e. address authorization + * info) are placed in a wordlist and returned in *addrs. + */ +static int +scan_authfile(f, client, server, secret, addrs, filename) + FILE *f; + char *client; + char *server; + char *secret; + struct wordlist **addrs; + char *filename; +{ + int newline, xxx; + int got_flag, best_flag; + FILE *sf; + struct wordlist *ap, *addr_list, *addr_last; + char word[MAXWORDLEN]; + char atfile[MAXWORDLEN]; + + if (addrs != NULL) + *addrs = NULL; + addr_list = NULL; + if (!getword(f, word, &newline, filename)) + return -1; /* file is empty??? */ + newline = 1; + best_flag = -1; + for (;;) { + /* + * Skip until we find a word at the start of a line. + */ + while (!newline && getword(f, word, &newline, filename)) + ; + if (!newline) + break; /* got to end of file */ + + /* + * Got a client - check if it's a match or a wildcard. + */ + got_flag = 0; + if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) { + newline = 0; + continue; + } + if (!ISWILD(word)) + got_flag = NONWILD_CLIENT; + + /* + * Now get a server and check if it matches. + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + if (server != NULL && strcmp(word, server) != 0 && !ISWILD(word)) + continue; + if (!ISWILD(word)) + got_flag |= NONWILD_SERVER; + + /* + * Got some sort of a match - see if it's better than what + * we have already. + */ + if (got_flag <= best_flag) + continue; + + /* + * Get the secret. + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + + /* + * Special syntax: @filename means read secret from file. + */ + if (word[0] == '@') { + strcpy(atfile, word+1); + if ((sf = fopen(atfile, "r")) == NULL) { + syslog(LOG_WARNING, "can't open indirect secret file %s", + atfile); + continue; + } + check_access(sf, atfile); + if (!getword(sf, word, &xxx, atfile)) { + syslog(LOG_WARNING, "no secret in indirect secret file %s", + atfile); + fclose(sf); + continue; + } + fclose(sf); + } + if (secret != NULL) + strcpy(secret, word); + + best_flag = got_flag; + + /* + * Now read address authorization info and make a wordlist. + */ + if (addr_list) + free_wordlist(addr_list); + addr_list = NULL; + for (;;) { + if (!getword(f, word, &newline, filename) || newline) + break; + ap = (struct wordlist *) malloc(sizeof(struct wordlist) + + strlen(word)); + if (ap == NULL) + novm("authorized addresses"); + ap->next = NULL; + strcpy(ap->word, word); + if (addr_list == NULL) + addr_list = ap; + else + addr_last->next = ap; + addr_last = ap; + } + if (!newline) + break; + } + + if (addrs != NULL) + *addrs = addr_list; + else if (addr_list != NULL) + free_wordlist(addr_list); + + return best_flag; +} + +/* + * free_wordlist - release memory allocated for a wordlist. + */ +static void +free_wordlist(wp) + struct wordlist *wp; +{ + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } +} diff --git a/pppd/chap.c b/pppd/chap.c new file mode 100644 index 0000000..e0ea4af --- /dev/null +++ b/pppd/chap.c @@ -0,0 +1,754 @@ +/* + * chap.c - Crytographic Handshake Authentication Protocol. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: chap.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include + +#include "ppp.h" +#include "pppd.h" +#include "chap.h" +#include "md5.h" + +chap_state chap[NPPP]; /* CHAP state; one for each unit */ + +static void ChapChallengeTimeout __ARGS((caddr_t)); +static void ChapResponseTimeout __ARGS((caddr_t)); +static void ChapReceiveChallenge __ARGS((chap_state *, u_char *, int, int)); +static void ChapReceiveResponse __ARGS((chap_state *, u_char *, int, int)); +static void ChapReceiveSuccess __ARGS((chap_state *, u_char *, int, int)); +static void ChapReceiveFailure __ARGS((chap_state *, u_char *, int, int)); +static void ChapSendStatus __ARGS((chap_state *, int)); +static void ChapSendChallenge __ARGS((chap_state *)); +static void ChapSendResponse __ARGS((chap_state *)); +static void ChapGenChallenge __ARGS((chap_state *)); + +extern double drand48 __ARGS((void)); +extern void srand48 __ARGS((long)); + +/* + * ChapInit - Initialize a CHAP unit. + */ +void +ChapInit(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + srand48((long) time(NULL)); /* joggle random number generator */ +} + + +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void +ChapAuthWithPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void +ChapAuthPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; +} + + +/* + * ChapChallengeTimeout - Timeout expired on sending challenge. + */ +static void +ChapChallengeTimeout(arg) + caddr_t arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) + return; + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + syslog(LOG_ERR, "Peer failed to respond to CHAP challenge"); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, CHAP); + return; + } + + ChapSendChallenge(cstate); /* Re-send challenge */ +} + + +/* + * ChapResponseTimeout - Timeout expired on sending response. + */ +static void +ChapResponseTimeout(arg) + caddr_t arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) + return; + + ChapSendResponse(cstate); /* re-send response */ +} + + +/* + * ChapRechallenge - Time to challenge the peer again. + */ +static void +ChapRechallenge(arg) + caddr_t arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) + return; + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; + + if (cstate->chal_interval != 0) + TIMEOUT(ChapRechallenge, (caddr_t) cstate, cstate->chal_interval); +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +void +ChapLowerUp(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) + cstate->clientstate = CHAPCS_CLOSED; + else if (cstate->clientstate == CHAPCS_PENDING) + cstate->clientstate = CHAPCS_LISTEN; + + if (cstate->serverstate == CHAPSS_INITIAL) + cstate->serverstate = CHAPSS_CLOSED; + else if (cstate->serverstate == CHAPSS_PENDING) { + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +void +ChapLowerDown(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) + UNTIMEOUT(ChapChallengeTimeout, (caddr_t) cstate); + else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) + UNTIMEOUT(ChapRechallenge, (caddr_t) cstate); + if (cstate->clientstate == CHAPCS_RESPONSE) + UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate); + + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +void +ChapProtocolReject(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) + auth_peer_fail(unit, CHAP); + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) + auth_withpeer_fail(unit, CHAP); + ChapLowerDown(unit); /* shutdown chap */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +void +ChapInput(unit, inpacket, packet_len) + int unit; + u_char *inpacket; + int packet_len; +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length.")); + return; + } + if (len > packet_len) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet.")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge and send Response. + */ +static void +ChapReceiveChallenge(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + int id; + int len; +{ + int rchallenge_len; + u_char *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + MD5_CTX mdContext; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id)); + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d", + cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= sizeof(rhostname)) + len = sizeof(rhostname) - 1; + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field: %s", + rhostname)); + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, + secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s", + rhostname); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) + UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate); + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->resp_id, 1); + MD5Update(&mdContext, secret, secret_len); + MD5Update(&mdContext, rchallenge, rchallenge_len); + MD5Final(&mdContext); + BCOPY(mdContext.digest, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type)); + return; + } + + ChapSendResponse(cstate); +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void +ChapReceiveResponse(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + int id; + int len; +{ + u_char *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + u_char buf[256]; + MD5_CTX mdContext; + u_char msg[256]; + char secret[MAXSECRETLEN]; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id)); + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d", + cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) + return; /* doesn't match ID of last challenge */ + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + ChapSendStatus(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + ChapSendStatus(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u_char) + remmd_len; + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")); + return; + } + + UNTIMEOUT(ChapChallengeTimeout, (caddr_t) cstate); + + if (len >= sizeof(rhostname)) + len = sizeof(rhostname) - 1; + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s", + rhostname)); + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, rhostname, cstate->chal_name, + secret, &secret_len, 1)) { + syslog(LOG_WARNING, "No CHAP secret found for authenticating %s", + rhostname); + } else { + + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) + break; /* it's not even the right length */ + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->chal_id, 1); + MD5Update(&mdContext, secret, secret_len); + MD5Update(&mdContext, cstate->challenge, cstate->chal_len); + MD5Final(&mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (bcmp (mdContext.digest, remmd, MD5_SIGNATURE_SIZE) == 0) + code = CHAP_SUCCESS; /* they are the same! */ + break; + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type)); + } + } + + ChapSendStatus(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, CHAP); + } + if (cstate->chal_interval != 0) + TIMEOUT(ChapRechallenge, (caddr_t) cstate, cstate->chal_interval); + + } else { + syslog(LOG_ERR, "CHAP peer authentication failed"); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, CHAP); + } +} + +/* + * ChapReceiveSuccess - Receive Success + */ +static void +ChapReceiveSuccess(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + u_char id; + int len; +{ + + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id)); + + if (cstate->clientstate == CHAPCS_OPEN) + /* presumably an answer to a duplicate response */ + return; + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n", + cstate->clientstate)); + return; + } + + /* + * Print message. + */ + if (len > 0) + PRINTMSG(inp, len); + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, CHAP); +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +static void +ChapReceiveFailure(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + u_char id; + int len; +{ + u_char msglen; + u_char *msg; + + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id)); + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n", + cstate->clientstate)); + return; + } + + /* + * Print message. + */ + if (len > 0) + PRINTMSG(inp, len); + + syslog(LOG_ERR, "CHAP authentication failed"); + auth_withpeer_fail(cstate->unit, CHAP); +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void +ChapSendChallenge(cstate) + chap_state *cstate; +{ + u_char *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; + outp = outpacket_buf; + + MAKEHEADER(outp, CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id)); + + TIMEOUT(ChapChallengeTimeout, (caddr_t) cstate, cstate->timeouttime); + ++cstate->chal_transmits; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void +ChapSendStatus(cstate, code) + chap_state *cstate; + int code; +{ + u_char *outp; + int outlen, msglen; + char msg[256]; + + if (code == CHAP_SUCCESS) + sprintf(msg, "Welcome to %s.", hostname); + else + sprintf(msg, "I don't like you. Go 'way."); + msglen = strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf; + + MAKEHEADER(outp, CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code, + cstate->chal_id)); +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len. The challenge + * string and its length are stored in *cstate, and various other fields of + * *cstate are initialized. + */ + +static void +ChapGenChallenge(cstate) + chap_state *cstate; +{ + int chal_len; + u_char *ptr = cstate->challenge; + unsigned int i; + + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) ((drand48() * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) + + MIN_CHALLENGE_LENGTH); + cstate->chal_len = chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++ ) + *ptr++ = (char) (drand48() * 0xff); +} + +/* + * ChapSendResponse - send a response packet with values as specified + * in *cstate. + */ +/* ARGSUSED */ +static void +ChapSendResponse(cstate) + chap_state *cstate; +{ + u_char *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; + outp = outpacket_buf; + + MAKEHEADER(outp, CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(ChapResponseTimeout, (caddr_t) cstate, cstate->timeouttime); + ++cstate->resp_transmits; +} + +#ifdef NO_DRAND48 + +double drand48() +{ + return (double)random() / (double)0x7fffffffL; /* 2**31-1 */ +} + +void srand48(seedval) +long seedval; +{ + srand((int)seedval); +} + +#endif diff --git a/pppd/chap.h b/pppd/chap.h new file mode 100644 index 0000000..e91d4fb --- /dev/null +++ b/pppd/chap.h @@ -0,0 +1,111 @@ +/* + * chap.h - Cryptographic Handshake Authentication Protocol definitions. + * based on November 1991 draft of PPP Authentication RFC + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chap.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ + +#ifndef __CHAP_INCLUDE__ + +/* Code + ID + length */ +#define CHAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths (for challenges we send) and other limits. + */ +#define MIN_CHALLENGE_LENGTH 32 +#define MAX_CHALLENGE_LENGTH 64 +#define MAX_RESPONSE_LENGTH 16 /* sufficient for MD5 */ + +/* + * Each interface is described by a chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u_char chal_len; /* challenge length */ + u_char chal_id; /* ID of last challenge */ + u_char chal_type; /* hash algorithm for challenges */ + u_char id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u_char resp_length; /* length of response */ + u_char resp_id; /* ID for response messages */ + u_char resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ +} chap_state; + + +/* + * Client (peer) states. + */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ + +/* + * Server (authenticator) states. + */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ + +/* + * Timeouts. + */ +#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */ +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ + +extern chap_state chap[]; + +void ChapInit __ARGS((int)); +void ChapAuthWithPeer __ARGS((int, char *, int)); +void ChapAuthPeer __ARGS((int, char *, int)); +void ChapLowerUp __ARGS((int)); +void ChapLowerDown __ARGS((int)); +void ChapInput __ARGS((int, u_char *, int)); +void ChapProtocolReject __ARGS((int)); + +#define __CHAP_INCLUDE__ +#endif /* __CHAP_INCLUDE__ */ diff --git a/pppd/fsm.c b/pppd/fsm.c new file mode 100644 index 0000000..23d6773 --- /dev/null +++ b/pppd/fsm.c @@ -0,0 +1,769 @@ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: fsm.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include +#include +/*#include */ +#include + +#include "ppp.h" +#include "pppd.h" +#include "fsm.h" + +extern char *proto_name(); + +static void fsm_timeout __ARGS((caddr_t)); +static void fsm_rconfreq __ARGS((fsm *, int, u_char *, int)); +static void fsm_rconfack __ARGS((fsm *, int, u_char *, int)); +static void fsm_rconfnakrej __ARGS((fsm *, int, int, u_char *, int)); +static void fsm_rtermreq __ARGS((fsm *, int)); +static void fsm_rtermack __ARGS((fsm *)); +static void fsm_rcoderej __ARGS((fsm *, u_char *, int)); +static void fsm_sconfreq __ARGS((fsm *, int)); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + +int peer_mru[NPPP]; + + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void +fsm_init(f) + fsm *f; +{ + f->state = INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->timeouttime = DEFTIMEOUT; + f->maxconfreqtransmits = DEFMAXCONFREQS; + f->maxtermtransmits = DEFMAXTERMREQS; + f->maxnakloops = DEFMAXNAKLOOPS; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void +fsm_lowerup(f) + fsm *f; +{ + switch( f->state ){ + case INITIAL: + f->state = CLOSED; + break; + + case STARTING: + if( f->flags & OPT_SILENT ) + f->state = STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Up event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void +fsm_lowerdown(f) + fsm *f; +{ + switch( f->state ){ + case CLOSED: + f->state = INITIAL; + break; + + case STOPPED: + f->state = STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case CLOSING: + f->state = INITIAL; + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + break; + + case STOPPING: + case REQSENT: + case ACKRCVD: + case ACKSENT: + f->state = STARTING; + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + break; + + case OPENED: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + f->state = STARTING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Down event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void +fsm_open(f) + fsm *f; +{ + switch( f->state ){ + case INITIAL: + f->state = STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case CLOSED: + if( f->flags & OPT_SILENT ) + f->state = STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + } + break; + + case CLOSING: + f->state = STOPPING; + /* fall through */ + case STOPPED: + case OPENED: + if( f->flags & OPT_RESTART ){ + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } +} + + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the CLOSED state. + */ +void +fsm_close(f) + fsm *f; +{ + switch( f->state ){ + case STARTING: + f->state = INITIAL; + break; + case STOPPED: + f->state = CLOSED; + break; + case STOPPING: + f->state = CLOSING; + break; + + case REQSENT: + case ACKRCVD: + case ACKSENT: + case OPENED: + if( f->state != OPENED ) + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + else if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0); + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + --f->retransmits; + + f->state = CLOSING; + break; + } +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void +fsm_timeout(arg) + caddr_t arg; +{ + fsm *f = (fsm *) arg; + + switch (f->state) { + case CLOSING: + case STOPPING: + if( f->retransmits <= 0 ){ + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == CLOSING)? CLOSED: STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + } else { + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0); + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + --f->retransmits; + } + break; + + case REQSENT: + case ACKRCVD: + case ACKSENT: + if (f->retransmits <= 0) { + syslog(LOG_WARNING, "%s: timeout sending Config-Requests", + PROTO_NAME(f)); + f->state = STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) + (*f->callbacks->finished)(f); + + } else { + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) + (*f->callbacks->retransmit)(f); + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == ACKRCVD ) + f->state = REQSENT; + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_input - Input packet. + */ +void +fsm_input(f, inpacket, l) + fsm *f; + u_char *inpacket; + int l; +{ + u_char *inp, *outp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < HEADERLEN) { + FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.", + f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.", + f->protocol)); + return; + } + if (len > l) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.", + f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == INITIAL || f->state == STARTING ){ + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d.", + f->protocol, f->state)); + return; + } + + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + if( !f->callbacks->extcode + || !(*f->callbacks->extcode)(f, code, id, inp, len) ) + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + break; + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void +fsm_rconfreq(f, id, inp, len) + fsm *f; + u_char id; + u_char *inp; + int len; +{ + u_char *outp; + int code, reject_if_disagree; + + FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f), id)); + switch( f->state ){ + case CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case CLOSING: + case STOPPING: + return; + + case OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci){ /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) + code = CONFREJ; /* Reject all CI */ + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, code, id, inp, len); + + if (code == CONFACK) { + if (f->state == ACKRCVD) { + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + f->state = OPENED; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + } else + f->state = ACKSENT; + f->nakloops = 0; + + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != ACKRCVD) + f->state = REQSENT; + if( code == CONFNAK ) + ++f->nakloops; + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void +fsm_rconfack(f, id, inp, len) + fsm *f; + int id; + u_char *inp; + int len; +{ + FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d.", + PROTO_NAME(f), id)); + + if (id != f->reqid) /* Expected id? */ + return; /* Nope, toss... */ + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ){ + /* Ack is bad - ignore it */ + FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)", + PROTO_NAME(f), len)); + return; + } + + switch (f->state) { + case CLOSED: + case STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case REQSENT: + f->state = ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case ACKRCVD: + /* Huh? an extra Ack? oh well... */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + break; + + case ACKSENT: + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + f->state = OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + break; + + case OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void +fsm_rconfnakrej(f, code, id, inp, len) + fsm *f; + int code, id; + u_char *inp; + int len; +{ + int (*proc)(); + + FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d.", + PROTO_NAME(f), id)); + + if (id != f->reqid) /* Expected id? */ + return; /* Nope, toss... */ + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if( !proc || !proc(f, inp, len) ){ + /* Nak/reject is bad - ignore it */ + FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)", + PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); + return; + } + + switch (f->state) { + case CLOSED: + case STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case REQSENT: + case ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + fsm_sconfreq(f, 0); /* Send Configure-Request */ + break; + + case ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + break; + + case OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void +fsm_rtermreq(f, id) + fsm *f; + int id; +{ + FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d.", + PROTO_NAME(f), id)); + + fsm_sdata(f, TERMACK, id, NULL, 0); + switch (f->state) { + case ACKRCVD: + case ACKSENT: + f->state = REQSENT; /* Start over but keep trying */ + break; + + case OPENED: + syslog(LOG_INFO, "%s terminated at peer's request", PROTO_NAME(f)); + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->retransmits = 0; + f->state = STOPPING; + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + break; + } +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void +fsm_rtermack(f) + fsm *f; +{ + FSMDEBUG((LOG_INFO, "fsm_rtermack(%s).", PROTO_NAME(f))); + + switch (f->state) { + case CLOSING: + f->state = CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + case STOPPING: + f->state = STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case ACKRCVD: + f->state = REQSENT; + break; + + case OPENED: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); + break; + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void +fsm_rcoderej(f, inp, len) + fsm *f; + u_char *inp; + int len; +{ + u_char code, id; + + FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s).", PROTO_NAME(f))); + + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + syslog(LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d", + PROTO_NAME(f), code, id); + + if( f->state == ACKRCVD ) + f->state = REQSENT; +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void +fsm_protreject(f) + fsm *f; +{ + switch( f->state ){ + case CLOSING: + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + /* fall through */ + case CLOSED: + f->state = CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case STOPPING: + case REQSENT: + case ACKRCVD: + case ACKSENT: + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + /* fall through */ + case STOPPED: + f->state = STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case OPENED: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0); + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + --f->retransmits; + + f->state = STOPPING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void +fsm_sconfreq(f, retransmit) + fsm *f; + int retransmit; +{ + u_char *outp; + int outlen, cilen; + + if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){ + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) + (*f->callbacks->resetci)(f); + f->nakloops = 0; + } + + if( !retransmit ){ + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + /* + * Make up the request packet + */ + if( f->callbacks->cilen && f->callbacks->addci ){ + cilen = (*f->callbacks->cilen)(f); + if( cilen > peer_mru[f->unit] - HEADERLEN ) + cilen = peer_mru[f->unit] - HEADERLEN; + outp = outpacket_buf + DLLHEADERLEN + HEADERLEN; + if (f->callbacks->addci) + (*f->callbacks->addci)(f, outp, &cilen); + } else + cilen = 0; + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + + FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d", + PROTO_NAME(f), f->reqid)); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void +fsm_sdata(f, code, id, data, datalen) + fsm *f; + u_char code, id; + u_char *data; + int datalen; +{ + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + outp = outpacket_buf; + if (datalen > peer_mru[f->unit] - HEADERLEN) + datalen = peer_mru[f->unit] - HEADERLEN; + if (datalen && data != outp + DLLHEADERLEN + HEADERLEN) + BCOPY(data, outp + DLLHEADERLEN + HEADERLEN, datalen); + outlen = datalen + HEADERLEN; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + output(f->unit, outpacket_buf, outlen + DLLHEADERLEN); + + FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d, id %d.", + PROTO_NAME(f), code, id)); +} diff --git a/pppd/fsm.h b/pppd/fsm.h new file mode 100644 index 0000000..3ff104a --- /dev/null +++ b/pppd/fsm.h @@ -0,0 +1,132 @@ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: fsm.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ + +/* + * Packet header = Code, id, length. + */ +#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define KEEPALIVE 12 /* Keepalive */ + + +/* + * Each FSM is described by a fsm_callbacks and a fsm structure. + */ +typedef struct fsm_callbacks { + void (*resetci)(); /* Reset our Configuration Information */ + int (*cilen)(); /* Length of our Configuration Information */ + void (*addci)(); /* Add our Configuration Information */ + int (*ackci)(); /* ACK our Configuration Information */ + int (*nakci)(); /* NAK our Configuration Information */ + int (*rejci)(); /* Reject our Configuration Information */ + int (*reqci)(); /* Request peer's Configuration Information */ + void (*up)(); /* Called when fsm reaches OPENED state */ + void (*down)(); /* Called when fsm leaves OPENED state */ + void (*starting)(); /* Called when we want the lower layer */ + void (*finished)(); /* Called when we don't want the lower layer */ + void (*protreject)(); /* Called when Protocol-Reject received */ + void (*retransmit)(); /* Retransmission is necessary */ + int (*extcode)(); /* Called when unknown code received */ + char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +typedef struct fsm { + int unit; /* Interface unit number */ + int protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + fsm_callbacks *callbacks; /* Callback routines */ +} fsm; + + +/* + * Link states. + */ +#define INITIAL 0 /* Down, hasn't been opened */ +#define STARTING 1 /* Down, been opened */ +#define CLOSED 2 /* Up, hasn't been opened */ +#define STOPPED 3 /* Open, waiting for down event */ +#define CLOSING 4 /* Terminating the connection, not open */ +#define STOPPING 5 /* Terminating, but open */ +#define REQSENT 6 /* We've sent a Config Request */ +#define ACKRCVD 7 /* We've received a Config Ack */ +#define ACKSENT 8 /* We've sent a Config Ack */ +#define OPENED 9 /* Connection available */ + + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/* + * Timeouts. + */ +#define DEFTIMEOUT 3 /* Timeout time in seconds */ +#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#define DEFMAXNAKLOOPS 10 /* Maximum number of nak loops */ + + +/* + * Prototypes + */ +void fsm_init __ARGS((fsm *)); +void fsm_lowerup __ARGS((fsm *)); +void fsm_lowerdown __ARGS((fsm *)); +void fsm_open __ARGS((fsm *)); +void fsm_close __ARGS((fsm *)); +void fsm_input __ARGS((fsm *, u_char *, int)); +void fsm_protreject __ARGS((fsm *)); +void fsm_sdata __ARGS((fsm *, int, int, u_char *, int)); + + +/* + * Variables + */ +extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ diff --git a/pppd/ipcp.c b/pppd/ipcp.c new file mode 100644 index 0000000..08befff --- /dev/null +++ b/pppd/ipcp.c @@ -0,0 +1,1056 @@ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: ipcp.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +/* + * TODO: + * Fix IP address negotiation (wantoptions or hisoptions). + * Don't set zero IP addresses. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "pppd.h" +#include "ppp.h" +#include "fsm.h" +#include "ipcp.h" + + +/* global vars */ +ipcp_options ipcp_wantoptions[NPPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NPPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NPPP]; /* Options we allow peer to request */ +ipcp_options ipcp_hisoptions[NPPP]; /* Options that we ack'd */ + +/* local vars */ +static int cis_received[NPPP]; /* # Conf-Reqs received */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci __ARGS((fsm *)); /* Reset our CI */ +static int ipcp_cilen __ARGS((fsm *)); /* Return length of our CI */ +static void ipcp_addci __ARGS((fsm *, u_char *, int *)); /* Add our CI */ +static int ipcp_ackci __ARGS((fsm *, u_char *, int)); /* Peer ack'd our CI */ +static int ipcp_nakci __ARGS((fsm *, u_char *, int)); /* Peer nak'd our CI */ +static int ipcp_rejci __ARGS((fsm *, u_char *, int)); /* Peer rej'd our CI */ +static int ipcp_reqci __ARGS((fsm *, u_char *, int *, int)); /* Rcv CI */ +static void ipcp_up __ARGS((fsm *)); /* We're UP */ +static void ipcp_down __ARGS((fsm *)); /* We're DOWN */ + + +fsm ipcp_fsm[NPPP]; /* IPCP fsm structure */ + +static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches OPENED state */ + ipcp_down, /* Called when fsm leaves OPENED state */ + NULL, /* Called when we want the lower layer up */ + NULL, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + +/* + * Make a string representation of a network IP address. + */ +char * +ip_ntoa(ipaddr) +u_long ipaddr; +{ + static char b[64]; + + ipaddr = ntohl(ipaddr); + + sprintf(b, "%d.%d.%d.%d", + (u_char)(ipaddr >> 24), + (u_char)(ipaddr >> 16), + (u_char)(ipaddr >> 8), + (u_char)(ipaddr)); + return b; +} + + +/* + * ipcp_init - Initialize IPCP. + */ +void +ipcp_init(unit) + int unit; +{ + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(&ipcp_fsm[unit]); + + wo->neg_addr = 1; + wo->old_addrs = 0; + wo->ouraddr = 0; + wo->hisaddr = 0; + + wo->neg_vj = 1; + wo->old_vj = 0; + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_STATES - 1; /* really max index */ + wo->cflag = 1; + + /* max slots and slot-id compression are currently hardwired in */ + /* ppp_if.c to 16 and 1, this needs to be changed (among other */ + /* things) gmc */ + + ao->neg_addr = 1; + ao->neg_vj = 1; + ao->maxslotindex = MAX_STATES - 1; + ao->cflag = 1; +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +void +ipcp_open(unit) + int unit; +{ + fsm_open(&ipcp_fsm[unit]); +} + + +/* + * ipcp_close - Take IPCP down. + */ +void +ipcp_close(unit) + int unit; +{ + fsm_close(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +void +ipcp_lowerup(unit) + int unit; +{ + fsm_lowerup(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +void +ipcp_lowerdown(unit) + int unit; +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +void +ipcp_input(unit, p, len) + int unit; + u_char *p; + int len; +{ + fsm_input(&ipcp_fsm[unit], p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +void +ipcp_protrej(unit) + int unit; +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_resetci - Reset our CI. + */ +static void +ipcp_resetci(f) + fsm *f; +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + ipcp_gotoptions[f->unit] = *wo; + cis_received[f->unit] = 0; +} + + +/* + * ipcp_cilen - Return length of our CI. + */ +static int +ipcp_cilen(f) + fsm *f; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) + + return (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj)); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + */ +static void +ipcp_addci(f, ucp, lenp) + fsm *f; + u_char *ucp; + int *lenp; +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + int len = *lenp; + +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else \ + neg = 0; \ + } + +#define ADDCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u_long l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else \ + neg = 0; \ + } + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +ipcp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_short cilen, citype, cishort; + u_long cilong; + u_char cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) \ + goto bad; \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) \ + goto bad; \ + } \ + } + +#define ACKCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u_long l; \ + if ((len -= addrlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) \ + goto bad; \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) \ + goto bad; \ + } \ + } + + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +ipcp_nakci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u_long ciaddr1, ciaddr2, l; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDR(opt, neg, old, code) \ + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else \ + ciaddr2 = 0; \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + if (cilen == CILEN_VJ) { \ + GETCHAR(cimaxslotindex, p); \ + GETCHAR(cicflag, p); \ + } \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of our address if we don't know it. + * Accept the peer's idea of his address if he knows it. + */ + NAKCIADDR(CI_ADDR, neg_addr, go->old_addrs, + if (!go->ouraddr && ciaddr1) { /* Do we know our address? */ + go->ouraddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "local IP address %s", + ip_ntoa(ciaddr1))); + } + if (ciaddr2) { /* Does he know his? */ + go->hisaddr = ciaddr2; + IPCPDEBUG((LOG_INFO, "remote IP address %s", + ip_ntoa(ciaddr2))); + } + ); + + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (cilen == CILEN_VJ) { + if (cishort == IPCP_VJ_COMP) { + try.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) + try.maxslotindex = cimaxslotindex; + if (!cicflag) + try.cflag = 0; + } else { + try.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try.old_vj = 1; + try.vj_protocol = cishort; + } else { + try.neg_vj = 0; + } + } + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) + goto bad; + no.neg_vj = 1; + break; + case CI_ADDRS: + if (go->neg_addr && go->old_addrs || no.old_addrs + || cilen != CILEN_ADDRS) + goto bad; + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1) + try.ouraddr = ciaddr1; + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2) + try.hisaddr = ciaddr2; + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) + goto bad; + try.neg_addr = 1; + try.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1) + try.ouraddr = ciaddr1; + no.neg_addr = 1; + break; + default: + goto bad; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) + goto bad; + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != OPENED) + *go = try; + + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + */ +static int +ipcp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, ciflag, cilen; + u_short cishort; + u_long cilong; + ipcp_options try; /* options to request next time */ + + try = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDR(opt, neg, old, val1, val2) \ + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == cilen && \ + p[0] == opt) { \ + u_long l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) \ + goto bad; \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) \ + goto bad; \ + } \ + try.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) \ + goto bad; \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) \ + goto bad; \ + } \ + try.neg = 0; \ + } + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != OPENED) + *go = try; + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +ipcp_reqci(f, inp, len, reject_if_disagree) + fsm *f; + u_char *inp; /* Requested CIs */ + int *len; /* Length of requested CIs */ + int reject_if_disagree; +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u_long tl, ciaddr1, ciaddr2;/* Parsed address values */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u_char maxslotindex, cflag; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_ADDRS: + IPCPDEBUG((LOG_INFO, "ipcp: received ADDRS ")); + if (!ao->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG((LOG_INFO, "(%s:", ip_ntoa(ciaddr1))); + if (wo->hisaddr && ciaddr1 != wo->hisaddr) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof (long), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + IPCPDEBUG((LOG_INFO, "%s)", ip_ntoa(ciaddr2))); + if (wo->ouraddr && ciaddr2 != wo->ouraddr) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof (long), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } + if (orc == CONFNAK) + break; + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; + + case CI_ADDR: + IPCPDEBUG((LOG_INFO, "ipcp: received ADDR ")); + + if (!ao->neg_addr || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG((LOG_INFO, "(%s)", ip_ntoa(ciaddr1))); + if (wo->hisaddr && ciaddr1 != wo->hisaddr) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof (long), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } + + if (orc == CONFNAK) + break; + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + break; + + case CI_COMPRESSTYPE: + IPCPDEBUG((LOG_INFO, "ipcp: received COMPRESSTYPE ")); + if (!ao->neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + IPCPDEBUG((LOG_INFO, "(%d)", cishort)); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + if (orc == CONFNAK) + break; + ho->maxslotindex = maxslotindex; + ho->cflag = wo->cflag; + } + break; + + default: + orc = CONFREJ; + break; + } + +endswitch: + IPCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc))); + + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) /* Getting fed up with sending NAKs? */ + orc = CONFREJ; /* Get tough if so */ + else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) + BCOPY(cip, ucp, cilen); /* Move it */ + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && + wo->req_addr && !reject_if_disagree) { + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = ucp - inp; /* Compute output length */ + IPCPDEBUG((LOG_INFO, "ipcp: returning Configure-%s", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * ipcp_up - IPCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ +static void +ipcp_up(f) + fsm *f; +{ + u_long mask; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + + IPCPDEBUG((LOG_INFO, "ipcp: up")); + go->default_route = 0; + go->proxy_arp = 0; + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (ho->hisaddr == 0) + ho->hisaddr = ipcp_wantoptions[f->unit].hisaddr; + + if (ho->hisaddr == 0) { + syslog(LOG_ERR, "Could not determine remote IP address"); + ipcp_close(f->unit); + return; + } + if (go->ouraddr == 0) { + syslog(LOG_ERR, "Could not determine local IP address"); + ipcp_close(f->unit); + return; + } + + /* + * Check that the peer is allowed to use the IP address he wants. + */ + if (!auth_ip_addr(f->unit, ho->hisaddr)) { + syslog(LOG_ERR, "Peer is not authorized to use remote address %s", + ip_ntoa(ho->hisaddr)); + ipcp_close(f->unit); + return; + } + + syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(go->ouraddr)); + syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(ho->hisaddr)); + + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed")); + ipcp_close(f->unit); + return; + } + + /* set tcp compression */ + sifvjcomp(f->unit, ho->neg_vj, ho->cflag); + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG((LOG_WARNING, "sifup failed")); + ipcp_close(f->unit); + return; + } + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) + if (sifdefaultroute(f->unit, ho->hisaddr)) + go->default_route = 1; + + /* Make a proxy ARP entry if requested. */ + if (ipcp_wantoptions[f->unit].proxy_arp) + if (sifproxyarp(f->unit, ho->hisaddr)) + go->proxy_arp = 1; +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ +static void +ipcp_down(f) + fsm *f; +{ + u_long ouraddr, hisaddr; + + IPCPDEBUG((LOG_INFO, "ipcp: down")); + + ouraddr = ipcp_gotoptions[f->unit].ouraddr; + hisaddr = ipcp_hisoptions[f->unit].hisaddr; + if (ipcp_gotoptions[f->unit].proxy_arp) + cifproxyarp(f->unit, hisaddr); + if (ipcp_gotoptions[f->unit].default_route) + cifdefaultroute(f->unit, hisaddr); + sifdown(f->unit); + cifaddr(f->unit, ouraddr, hisaddr); +} diff --git a/pppd/ipcp.h b/pppd/ipcp.h new file mode 100644 index 0000000..8ebfe63 --- /dev/null +++ b/pppd/ipcp.h @@ -0,0 +1,65 @@ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: ipcp.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ + +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 + +#define MAX_STATES 16 /* from slcompress.h */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option*/ + +typedef struct ipcp_options { + int neg_addr : 1; /* Negotiate IP Address? */ + int old_addrs : 1; /* Use old (IP-Addresses) option? */ + int req_addr : 1; /* Ask peer to send IP address? */ + int default_route : 1; /* Assign default route through interface? */ + int proxy_arp : 1; /* Make proxy ARP entry for peer? */ + int neg_vj : 1; /* Van Jacobson Compression? */ + int old_vj : 1; /* use old (short) form of VJ option? */ + u_short vj_protocol; /* protocol value to use in VJ option */ + u_char maxslotindex, cflag; /* values for RFC1332 VJ compression neg. */ + u_long ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ +} ipcp_options; + +extern fsm ipcp_fsm[]; +extern ipcp_options ipcp_wantoptions[]; +extern ipcp_options ipcp_gotoptions[]; +extern ipcp_options ipcp_allowoptions[]; +extern ipcp_options ipcp_hisoptions[]; + +void ipcp_init __ARGS((int)); +void ipcp_open __ARGS((int)); +void ipcp_close __ARGS((int)); +void ipcp_lowerup __ARGS((int)); +void ipcp_lowerdown __ARGS((int)); +void ipcp_input __ARGS((int, u_char *, int)); +void ipcp_protrej __ARGS((int)); diff --git a/pppd/lcp.c b/pppd/lcp.c new file mode 100644 index 0000000..c475f4c --- /dev/null +++ b/pppd/lcp.c @@ -0,0 +1,1281 @@ +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: lcp.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +/* + * TODO: + * Option tracing. + * Test restart. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "pppd.h" +#include "ppp.h" +#include "fsm.h" +#include "lcp.h" +#include "magic.h" +#include "chap.h" +#include "upap.h" +#include "ipcp.h" + +/* global vars */ +fsm lcp_fsm[NPPP]; /* LCP fsm structure (global)*/ +lcp_options lcp_wantoptions[NPPP]; /* Options that we want to request */ +lcp_options lcp_gotoptions[NPPP]; /* Options that peer ack'd */ +lcp_options lcp_allowoptions[NPPP]; /* Options we allow peer to request */ +lcp_options lcp_hisoptions[NPPP]; /* Options that we ack'd */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci __ARGS((fsm *)); /* Reset our CI */ +static int lcp_cilen __ARGS((fsm *)); /* Return length of our CI */ +static void lcp_addci __ARGS((fsm *, u_char *, int *)); /* Add our CI to pkt */ +static int lcp_ackci __ARGS((fsm *, u_char *, int)); /* Peer ack'd our CI */ +static int lcp_nakci __ARGS((fsm *, u_char *, int)); /* Peer nak'd our CI */ +static int lcp_rejci __ARGS((fsm *, u_char *, int)); /* Peer rej'd our CI */ +static int lcp_reqci __ARGS((fsm *, u_char *, int *, int)); /* Rcv peer CI */ +static void lcp_up __ARGS((fsm *)); /* We're UP */ +static void lcp_down __ARGS((fsm *)); /* We're DOWN */ +static void lcp_starting __ARGS((fsm *)); /* We need lower layer up */ +static void lcp_finished __ARGS((fsm *)); /* We need lower layer down */ +static int lcp_extcode __ARGS((fsm *, int, int, u_char *, int)); +static void lcp_rprotrej __ARGS((fsm *, u_char *, int)); + +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches OPENED state */ + lcp_down, /* Called when fsm leaves OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +int lcp_warnloops = DEFWARNLOOPS; /* Warn about a loopback this often */ + +/* + * Length of each type of configuration option (in octets) + */ +#define CILEN_VOID 2 +#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */ +#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ +#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ +#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + +/* + * lcp_init - Initialize LCP. + */ +void +lcp_init(unit) + int unit; +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + wo->passive = 0; + wo->silent = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line + implementations */ + wo->neg_mru = 1; + wo->mru = DEFMRU; + wo->neg_asyncmap = 1; + wo->asyncmap = 0; + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->chap_mdtype = CHAP_DIGEST_MD5; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + wo->neg_lqr = 0; /* no LQR implementation yet */ + + ao->neg_mru = 1; + ao->mru = MAXMRU; + ao->neg_asyncmap = 1; + ao->asyncmap = 0; + ao->neg_chap = 1; + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->neg_upap = 1; + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_lqr = 0; /* no LQR implementation yet */ + +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void +lcp_open(unit) + int unit; +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + + f->flags = 0; + if (wo->passive) + f->flags |= OPT_PASSIVE; + if (wo->silent) + f->flags |= OPT_SILENT; + fsm_open(f); +} + + +/* + * lcp_close - Take LCP down. + */ +void +lcp_close(unit) + int unit; +{ + fsm_close(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void +lcp_lowerup(unit) + int unit; +{ + sifdown(unit); + ppp_send_config(unit, MTU, 0xffffffff, 0, 0); + ppp_recv_config(unit, MTU, 0, 0, 0); + peer_mru[unit] = MTU; + + fsm_lowerup(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void +lcp_lowerdown(unit) + int unit; +{ + fsm_lowerdown(&lcp_fsm[unit]); +} + + +/* + * lcp_input - Input LCP packet. + */ +void +lcp_input(unit, p, len) + int unit; + u_char *p; + int len; +{ + fsm_input(&lcp_fsm[unit], p, len); +} + + +/* + * lcp_extcode - Handle a LCP-specific code. + */ +static int +lcp_extcode(f, code, id, inp, len) + fsm *f; + int code, id; + u_char *inp; + int len; +{ + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if( f->state != OPENED ) + break; + LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id)); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + case DISCREQ: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void +lcp_rprotrej(f, inp, len) + fsm *f; + u_char *inp; + int len; +{ + u_short prot; + + LCPDEBUG((LOG_INFO, "lcp_rprotrej.")); + + if (len < sizeof (u_short)) { + LCPDEBUG((LOG_INFO, + "lcp_rprotrej: Rcvd short Protocol-Reject packet!")); + return; + } + + GETSHORT(prot, inp); + + LCPDEBUG((LOG_INFO, + "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!", + prot)); + + /* + * Protocol-Reject packets received in any state other than the LCP + * OPENED state SHOULD be silently discarded. + */ + if( f->state != OPENED ){ + LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d", + f->state)); + return; + } + + DEMUXPROTREJ(f->unit, prot); /* Inform protocol */ +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +/*ARGSUSED*/ +void +lcp_protrej(unit) + int unit; +{ + /* + * Can't reject LCP! + */ + LCPDEBUG((LOG_WARNING, + "lcp_protrej: Received Protocol-Reject for LCP!")); + fsm_protreject(&lcp_fsm[unit]); +} + + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void +lcp_sprotrej(unit, p, len) + int unit; + u_char *p; + int len; +{ + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the OPENED state. + */ + p += 2; + len -= 2; + + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, + p, len); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void + lcp_resetci(f) +fsm *f; +{ + lcp_wantoptions[f->unit].magicnumber = magic(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; + peer_mru[f->unit] = MTU; +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int +lcp_cilen(f) + fsm *f; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define LENCIVOID(neg) (neg ? CILEN_VOID : 0) +#define LENCICHAP(neg) (neg ? CILEN_CHAP : 0) +#define LENCISHORT(neg) (neg ? CILEN_SHORT : 0) +#define LENCILONG(neg) (neg ? CILEN_LONG : 0) +#define LENCILQR(neg) (neg ? CILEN_LQR: 0) + /* + * NB: we only ask for one of CHAP and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru) + + LENCILONG(go->neg_asyncmap) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!go->neg_chap && go->neg_upap) + + LENCILQR(go->neg_lqr) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void +lcp_addci(f, ucp, lenp) + fsm *f; + u_char *ucp; + int *lenp; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#define ADDCICHAP(opt, neg, val, digest) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(LQR, ucp); \ + PUTLONG(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, UPAP); + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + syslog(LOG_ERR, "Bug in lcp_addci: wrong length"); + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +lcp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cilen, citype, cichar; + u_short cishort; + u_long cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || \ + citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAP(opt, neg, val, digest) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || \ + citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, UPAP); + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); +bad: + LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +lcp_nakci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u_char cilen, citype, cichar, *next; + u_short cishort; + u_long cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try; /* options to request next time */ + int looped_back = 0; + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILQR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort < DEFMRU) + try.mru = cishort; + ); + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try.asyncmap = go->asyncmap | cilong; + ); + /* + * If they can't cope with our CHAP hash algorithm, we'll have + * to stop asking for CHAP. We haven't got any other algorithm. + */ + NAKCICHAP(CI_AUTHTYPE, neg_chap, + try.neg_chap = 0; + ); + /* + * Peer shouldn't send Nak for UPAP, protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + if (!go->neg_chap ){ + NAKCISHORT(CI_AUTHTYPE, neg_upap, + try.neg_upap = 0; + ); + } + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILONG(CI_QUALITY, neg_lqr, + if (cishort != LQR) + try.neg_lqr = 0; + else + try.lqr_period = cilong; + ); + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try.magicnumber = magic(); + ++try.numloops; + looped_back = 1; + ); + + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, + try.neg_pcompression = 0; + ); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, + try.neg_accompression = 0; + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if (go->neg_mru || no.neg_mru || cilen != CILEN_SHORT) + goto bad; + break; + case CI_ASYNCMAP: + if (go->neg_asyncmap || no.neg_asyncmap || cilen != CILEN_LONG) + goto bad; + break; + case CI_AUTHTYPE: + if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) + goto bad; + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) + goto bad; + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) + goto bad; + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) + goto bad; + break; + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) + goto bad; + break; + default: + goto bad; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) + goto bad; + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != OPENED) { + *go = try; + if (looped_back && try.numloops % lcp_warnloops == 0) + LCPDEBUG((LOG_INFO, "The line appears to be looped back.")); + } + + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int +lcp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cichar; + u_short cishort; + u_long cilong; + u_char *start = p; + int plen = len; + lcp_options try; /* options to request next time */ + + try = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO, "lcp_rejci rejected void opt %d", opt)); \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected short opt %d", opt)); \ + } +#define REJCICHAP(opt, neg, val, digest) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected chap opt %d", opt)); \ + } +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected long opt %d", opt)); \ + } +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != LQR || cichar != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected LQR opt %d", opt)); \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); + REJCICHAP(CI_AUTHTYPE, neg_chap, CHAP, go->chap_mdtype); + if (!go->neg_chap) { + REJCISHORT(CI_AUTHTYPE, neg_upap, UPAP); + } + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != OPENED) + *go = try; + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!")); + LCPDEBUG((LOG_WARNING, "lcp_rejci: plen %d len %d off %d", + plen, len, p - start)); + return 0; +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +lcp_reqci(f, inp, lenp, reject_if_disagree) + fsm *f; + u_char *inp; /* Requested CIs */ + int *lenp; /* Length of requested CIs */ + int reject_if_disagree; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + u_char cilen, citype, cichar;/* Parsed len, type, char value */ + u_short cishort; /* Parsed short value */ + u_long cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *lenp; /* Length left */ + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MRU")); + if (!ao->neg_mru || /* Allow option? */ + cilen != CILEN_SHORT) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + LCPDEBUG((LOG_INFO, "(%d)", cishort)); + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < MINMRU) { + orc = CONFNAK; /* Nak CI */ + if( !reject_if_disagree ){ + DECPTR(sizeof (short), p); /* Backup */ + PUTSHORT(MINMRU, p); /* Give him a hint */ + } + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ + break; + + case CI_ASYNCMAP: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ASYNCMAP")); + if (!ao->neg_asyncmap || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%lx)", cilong)); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + orc = CONFNAK; + if( !reject_if_disagree ){ + DECPTR(sizeof (long), p); + PUTLONG(ao->asyncmap | cilong, p); + } + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; + break; + + case CI_AUTHTYPE: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE")); + if (cilen < CILEN_SHORT || + !(ao->neg_upap || ao->neg_chap)) { + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + LCPDEBUG((LOG_INFO, "(%x)", cishort)); + + /* + * Authtype must be UPAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == UPAP) { + if (!ao->neg_upap || /* we don't want to do PAP */ + ho->neg_chap || /* or we've already accepted CHAP */ + cilen != CILEN_SHORT) { + LCPDEBUG((LOG_WARNING, + "lcp_reqci: rcvd AUTHTYPE PAP, rejecting...")); + orc = CONFREJ; + break; + } + ho->neg_upap = 1; + break; + } + if (cishort == CHAP) { + if (!ao->neg_chap || /* we don't want to do CHAP */ + ho->neg_upap || /* or we've already accepted UPAP */ + cilen != CILEN_CHAP) { + LCPDEBUG((LOG_INFO, + "lcp_reqci: rcvd AUTHTYPE CHAP, rejecting...")); + orc = CONFREJ; + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != ao->chap_mdtype) { + orc = CONFNAK; + if( !reject_if_disagree ){ + DECPTR(sizeof (u_char), p); + PUTCHAR(ao->chap_mdtype, p); + } + break; + } + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 1; + break; + } + + /* + * We don't recognize the protocol they're asking for. + * Reject it. + */ + orc = CONFREJ; + break; + + case CI_QUALITY: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd QUALITY")); + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + GETSHORT(cishort, p); + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%x %lx)", cishort, cilong)); + if (cishort != LQR) { + orc = CONFREJ; + break; + } + + /* + * Check the reporting period. + * XXX When should we Nak this, and what with? + */ + break; + + case CI_MAGICNUMBER: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MAGICNUMBER")); + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%lx)", cilong)); + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + orc = CONFNAK; + DECPTR(sizeof (long), p); + cilong = magic(); /* Don't put magic() inside macro! */ + PUTLONG(cilong, p); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd PCOMPRESSION")); + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ACCOMPRESSION")); + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + default: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd unknown option %d", + citype)); + orc = CONFREJ; + break; + } + +endswitch: + LCPDEBUG((LOG_INFO, " (%s)", CODENAME(orc))); + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) /* Getting fed up with sending NAKs? */ + orc = CONFREJ; /* Get tough if so */ + else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + if (ucp != cip) /* Need to move CI? */ + BCOPY(cip, ucp, cilen); /* Move it */ + INCPTR(cilen, ucp); /* Update output pointer */ + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. This must be done with care since it might + * require a longer packet than we received. At present there + * are no cases where we want to ask the peer to negotiate an option. + */ + + *lenp = ucp - inp; /* Compute output length */ + LCPDEBUG((LOG_INFO, "lcp_reqci: returning CONF%s.", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + * + * Start UPAP, IPCP, etc. + */ +static void +lcp_up(f) + fsm *f; +{ + lcp_options *wo = &lcp_wantoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + */ + ppp_send_config(f->unit, (ho->neg_mru? MIN(ao->mru, ho->mru): MTU), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffff), + ho->neg_pcompression, ho->neg_accompression); + ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): MTU), + (go->neg_asyncmap? go->asyncmap: 0xffffffff), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) + peer_mru[f->unit] = ho->mru; + + ChapLowerUp(f->unit); /* Enable CHAP */ + upap_lowerup(f->unit); /* Enable UPAP */ + ipcp_lowerup(f->unit); /* Enable IPCP */ + + link_established(f->unit); +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void +lcp_down(f) + fsm *f; +{ + ipcp_lowerdown(f->unit); + ChapLowerDown(f->unit); + upap_lowerdown(f->unit); + + sifdown(f->unit); + ppp_send_config(f->unit, MTU, 0xffffffff, 0, 0); + ppp_recv_config(f->unit, MTU, 0, 0, 0); + peer_mru[f->unit] = MTU; + syslog(LOG_NOTICE, "Connection terminated."); +} + + +/* + * lcp_starting - LCP needs the lower layer up. + */ +static void +lcp_starting(f) + fsm *f; +{ + link_required(f->unit); +} + + +/* + * lcp_finished - LCP has finished with the lower layer. + */ +static void +lcp_finished(f) + fsm *f; +{ + link_terminated(f->unit); +} + diff --git a/pppd/lcp.h b/pppd/lcp.h new file mode 100644 index 0000000..f7d5ea2 --- /dev/null +++ b/pppd/lcp.h @@ -0,0 +1,78 @@ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: lcp.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ + +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_KEEPALIVE 6 /* Keep Alive Parameters - OBSOLETE */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ + + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + int passive : 1; /* Don't die if we don't get a response */ + int silent : 1; /* Wait for the other end to start first */ + int restart : 1; /* Restart vs. exit after close */ + int neg_mru : 1; /* Negotiate the MRU? */ + int neg_asyncmap : 1; /* Negotiate the async map? */ + int neg_upap : 1; /* Ask for UPAP authentication? */ + int neg_chap : 1; /* Ask for CHAP authentication? */ + int neg_magicnumber : 1; /* Ask for magic number? */ + int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ + int neg_lqr : 1; /* Negotiate use of Link Quality Reports */ + u_short mru; /* Value of MRU */ + char chap_mdtype; /* which MD type (hashing algorithm) */ + u_long asyncmap; /* Value of async map */ + u_long magicnumber; + int numloops; /* Number of loops during magic number neg. */ + u_long lqr_period; /* Reporting period for link quality */ +} lcp_options; + +extern fsm lcp_fsm[]; +extern lcp_options lcp_wantoptions[]; +extern lcp_options lcp_gotoptions[]; +extern lcp_options lcp_allowoptions[]; +extern lcp_options lcp_hisoptions[]; + +#define DEFMRU 1500 /* Try for this */ +#define MINMRU 128 /* No MRUs below this */ +#define MAXMRU 16384 /* Normally limit MRU to this */ + +void lcp_init __ARGS((int)); +void lcp_open __ARGS((int)); +void lcp_close __ARGS((int)); +void lcp_lowerup __ARGS((int)); +void lcp_lowerdown __ARGS((int)); +void lcp_input __ARGS((int, u_char *, int)); +void lcp_protrej __ARGS((int)); +void lcp_sprotrej __ARGS((int, u_char *, int)); + +extern int lcp_warnloops; /* Warn about a loopback this often */ +#define DEFWARNLOOPS 10 /* Default value for above */ diff --git a/pppd/magic.c b/pppd/magic.c new file mode 100644 index 0000000..20dfd6f --- /dev/null +++ b/pppd/magic.c @@ -0,0 +1,70 @@ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: magic.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +#include +#include +#include + +#include "magic.h" + + +static u_long next; /* Next value to return */ + +extern u_long gethostid __ARGS((void)); +extern long random __ARGS((void)); +extern void srandom __ARGS((int)); + + +/* + * magic_init - Initialize the magic number generator. + * + * Computes first magic number and seed for random number generator. + * Attempts to compute a random number seed which will not repeat. + * The current method uses the current hostid and current time. + */ +void magic_init() +{ + struct timeval tv; + + next = gethostid(); + if (gettimeofday(&tv, NULL)) { + perror("gettimeofday"); + exit(1); + } + next ^= (u_long) tv.tv_sec ^ (u_long) tv.tv_usec; + + srandom((int) next); +} + + +/* + * magic - Returns the next magic number. + */ +u_long magic() +{ + u_long m; + + m = next; + next = (u_long) random(); + return (m); +} diff --git a/pppd/magic.h b/pppd/magic.h new file mode 100644 index 0000000..d41aa39 --- /dev/null +++ b/pppd/magic.h @@ -0,0 +1,24 @@ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: magic.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ +#include "args.h" + +void magic_init __ARGS((void)); /* Initialize the magic number generator */ +u_long magic __ARGS((void)); /* Returns the next magic number */ diff --git a/pppd/main.c b/pppd/main.c new file mode 100644 index 0000000..638e296 --- /dev/null +++ b/pppd/main.c @@ -0,0 +1,1227 @@ +/* + * main.c - Point-to-Point Protocol main module + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: main.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +#define SETSID + +#include +#include +#include +#include +#include +#include +#include + +#ifdef STREAMS +#undef SGTTY +#endif + +#ifdef SGTTY +#include +#else +#ifndef sun +#include +#endif +#include +#endif + +#include +#include +#include +#include +#include + +#include "callout.h" + +#include +#include + +#include + +#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 + +char line[256]; +char *p; + +logf(level, fmt, va_alist) +int level; +char *fmt; +va_dcl +{ + va_list pvar; + char buf[256]; + + va_start(pvar); + vsprintf(buf, fmt, pvar); + va_end(pvar); + + p = line + strlen(line); + strcat(p, buf); + + if (buf[strlen(buf)-1] == '\n') { + syslog(level, "%s", line); + line[0] = 0; + } +} + +void +novm(msg) + char *msg; +{ + syslog(LOG_ERR, "Virtual memory exhausted allocating %s\n", msg); + die(1); +} diff --git a/pppd/md5.c b/pppd/md5.c new file mode 100644 index 0000000..94bb2fe --- /dev/null +++ b/pppd/md5.c @@ -0,0 +1,298 @@ + + +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "md5.h" + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform (); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void MD5Init (mdContext) +MD5_CTX *mdContext; +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void MD5Update (mdContext, inBuf, inLen) +MD5_CTX *mdContext; +unsigned char *inBuf; +unsigned int inLen; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void MD5Final (mdContext) +MD5_CTX *mdContext; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, 3614090360U); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 3905402710U); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 606105819U); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 3250441966U); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 4118548399U); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 1200080426U); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 2821735955U); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 4249261313U); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 1770035416U); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 2336552879U); /* 10 */ + FF ( c, d, a, b, in[10], S13, 4294925233U); /* 11 */ + FF ( b, c, d, a, in[11], S14, 2304563134U); /* 12 */ + FF ( a, b, c, d, in[12], S11, 1804603682U); /* 13 */ + FF ( d, a, b, c, in[13], S12, 4254626195U); /* 14 */ + FF ( c, d, a, b, in[14], S13, 2792965006U); /* 15 */ + FF ( b, c, d, a, in[15], S14, 1236535329U); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 4129170786U); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 3225465664U); /* 18 */ + GG ( c, d, a, b, in[11], S23, 643717713U); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 3921069994U); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 3593408605U); /* 21 */ + GG ( d, a, b, c, in[10], S22, 38016083U); /* 22 */ + GG ( c, d, a, b, in[15], S23, 3634488961U); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 3889429448U); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 568446438U); /* 25 */ + GG ( d, a, b, c, in[14], S22, 3275163606U); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 4107603335U); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 1163531501U); /* 28 */ + GG ( a, b, c, d, in[13], S21, 2850285829U); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 4243563512U); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 1735328473U); /* 31 */ + GG ( b, c, d, a, in[12], S24, 2368359562U); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 4294588738U); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 2272392833U); /* 34 */ + HH ( c, d, a, b, in[11], S33, 1839030562U); /* 35 */ + HH ( b, c, d, a, in[14], S34, 4259657740U); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 2763975236U); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 1272893353U); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 4139469664U); /* 39 */ + HH ( b, c, d, a, in[10], S34, 3200236656U); /* 40 */ + HH ( a, b, c, d, in[13], S31, 681279174U); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 3936430074U); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 3572445317U); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 76029189U); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 3654602809U); /* 45 */ + HH ( d, a, b, c, in[12], S32, 3873151461U); /* 46 */ + HH ( c, d, a, b, in[15], S33, 530742520U); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 3299628645U); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 4096336452U); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 1126891415U); /* 50 */ + II ( c, d, a, b, in[14], S43, 2878612391U); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 4237533241U); /* 52 */ + II ( a, b, c, d, in[12], S41, 1700485571U); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 2399980690U); /* 54 */ + II ( c, d, a, b, in[10], S43, 4293915773U); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 2240044497U); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 1873313359U); /* 57 */ + II ( d, a, b, c, in[15], S42, 4264355552U); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 2734768916U); /* 59 */ + II ( b, c, d, a, in[13], S44, 1309151649U); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 4149444226U); /* 61 */ + II ( d, a, b, c, in[11], S42, 3174756917U); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 718787259U); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 3951481745U); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + *********************************************************************** + ** End of md5.c ** + ******************************** (cut) ******************************** + */ diff --git a/pppd/md5.h b/pppd/md5.h new file mode 100644 index 0000000..535b18d --- /dev/null +++ b/pppd/md5.h @@ -0,0 +1,58 @@ +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef __MD5_INCLUDE__ + +/* typedef a 32-bit type */ +typedef unsigned long int UINT4; + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (); +void MD5Update (); +void MD5Final (); + +#define __MD5_INCLUDE__ +#endif /* __MD5_INCLUDE__ */ diff --git a/pppd/options.c b/pppd/options.c new file mode 100644 index 0000000..2661fa4 --- /dev/null +++ b/pppd/options.c @@ -0,0 +1,1153 @@ +/* + * options.c - handles option processing for PPP. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: options.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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\ + Communicate over the named device\n\ + Set the baud rate to \n\ + : Set the local and/or remote interface IP\n\ + addresses. Either one may be omitted.\n\ + asyncmap Set the desired async map to hex \n\ + auth Require authentication from peer\n\ + connect

Invoke shell command

to set up the serial line\n\ + crtscts Use hardware RTS/CTS flow control\n\ + defaultroute Add default route through interface\n\ + file Take options from file \n\ + modem Use modem control lines\n\ + mru Set MRU value to for negotiation\n\ + netmask Set interface netmask to \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 Set the desired async map to hex \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 Get username and password for authenticating\n\ + with peer using PAP from file \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 Append domain name 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 \. + * \ 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 == '\\') { + /* + * \ 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 \ */ + else if (c == '"' || isspace(c) || c == '\\') + word[len-1] = c; /* put special char in word */ + else { + if (len < MAXWORDLEN-1) + word[len] = c; + ++len; + } + escape = 0; + } else if (c == '"') { + quoted = !quoted; + } else if (!quoted && (isspace(c) || c == '#')) { + ungetc(c, f); + break; + } else { + if (len < MAXWORDLEN-1) + word[len] = c; + ++len; + if (c == '\\') + quoted = 1; + } + if ((c = getc(f)) == EOF) + break; + } + + if (ferror(f)) { + perror(filename); + die(1); + } + + if (len >= MAXWORDLEN) { + word[MAXWORDLEN-1] = 0; + fprintf(stderr, "%s: warning: word in file %s too long (%.20s...)\n", + progname, filename, word); + } else + word[len] = 0; + + return 1; +} + +/* + * number_option - parse a numeric parameter for an option + */ +static int +number_option(str, valp, base) + char *str; + long *valp; + int base; +{ + char *ptr; + + *valp = strtol(str, &ptr, base); + if (ptr == str) { + fprintf(stderr, "%s: invalid number: %s\n", progname, str); + return 0; + } + return 1; +} + + +/* + * int_option - like number_option, but valp is int *, + * the base is assumed to be 0, and *valp is not changed + * if there is an error. + */ +static int +int_option(str, valp) + char *str; + int *valp; +{ + long v; + + if (!number_option(str, &v, 0)) + return 0; + *valp = (int) v; + return 1; +} + + +/* + * The following procedures execute commands. + */ + +/* + * readfile - take commands from a file. + */ +static int +readfile(argv) + char **argv; +{ + return options_from_file(*argv); +} + +/* + * setdebug - Set debug (command line argument). + */ +static int +setdebug() +{ + debug++; + setlogmask(LOG_UPTO(LOG_DEBUG)); + return (1); +} + +/* + * noopt - Disable all options. + */ +static int +noopt() +{ + BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options)); + BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options)); + BZERO((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options)); + BZERO((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options)); + return (1); +} + +/* + * noaccomp - Disable Address/Control field compression negotiation. + */ +static int +noaccomp() +{ + lcp_wantoptions[0].neg_accompression = 0; + lcp_allowoptions[0].neg_accompression = 0; + return (1); +} + + +/* + * noasyncmap - Disable async map negotiation. + */ +static int +noasyncmap() +{ + lcp_wantoptions[0].neg_asyncmap = 0; + lcp_allowoptions[0].neg_asyncmap = 0; + return (1); +} + + +/* + * noipaddr - Disable IP address negotiation. + */ +static int +noipaddr() +{ + ipcp_wantoptions[0].neg_addr = 0; + ipcp_allowoptions[0].neg_addr = 0; + return (1); +} + + +/* + * nomagicnumber - Disable magic number negotiation. + */ +static int +nomagicnumber() +{ + lcp_wantoptions[0].neg_magicnumber = 0; + lcp_allowoptions[0].neg_magicnumber = 0; + return (1); +} + + +/* + * nomru - Disable mru negotiation. + */ +static int +nomru() +{ + lcp_wantoptions[0].neg_mru = 0; + lcp_allowoptions[0].neg_mru = 0; + return (1); +} + + +/* + * setmru - Set MRU for negotiation. + */ +static int +setmru(argv) + char **argv; +{ + long mru; + + if (!number_option(*argv, &mru, 0)) + return 0; + lcp_wantoptions[0].mru = mru; + lcp_wantoptions[0].neg_mru = 1; + return (1); +} + + +/* + * nopcomp - Disable Protocol field compression negotiation. + */ +static int +nopcomp() +{ + lcp_wantoptions[0].neg_pcompression = 0; + lcp_allowoptions[0].neg_pcompression = 0; + return (1); +} + + +/* + * setpassive - Set passive mode (don't give up if we time out sending + * LCP configure-requests). + */ +static int +setpassive() +{ + lcp_wantoptions[0].passive = 1; + return (1); +} + + +/* + * setsilent - Set silent mode (don't start sending LCP configure-requests + * until we get one from the peer). + */ +static int +setsilent() +{ + lcp_wantoptions[0].silent = 1; + return 1; +} + + +/* + * nopap - Disable PAP authentication with peer. + */ +static int +nopap() +{ + lcp_allowoptions[0].neg_upap = 0; + return (1); +} + + +/* + * reqpap - Require PAP authentication from peer. + */ +static int +reqpap() +{ + lcp_wantoptions[0].neg_upap = 1; + auth_required = 1; +} + + +/* + * setupapfile - specifies UPAP info for authenticating with peer. + */ +static int +setupapfile(argv) + char **argv; +{ + FILE * ufile; + int l; + + lcp_allowoptions[0].neg_upap = 1; + + /* open user info file */ + if ((ufile = fopen(*argv, "r")) == NULL) { + fprintf(stderr, "unable to open user login data file %s\n", *argv); + exit(1); + } + check_access(ufile, *argv); + + /* get username */ + if (fgets(user, MAXNAMELEN - 1, ufile) == NULL + || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){ + fprintf(stderr, "Unable to read user login data file %s.\n", *argv); + exit(2); + } + fclose(ufile); + + /* get rid of newlines */ + l = strlen(user); + if (l > 0 && user[l-1] == '\n') + user[l-1] = 0; + l = strlen(passwd); + if (l > 0 && passwd[l-1] == '\n') + passwd[l-1] = 0; + + return (1); +} + + +/* + * nochap - Disable CHAP authentication with peer. + */ +static int +nochap() +{ + lcp_allowoptions[0].neg_chap = 0; + return (1); +} + + +/* + * reqchap - Require CHAP authentication from peer. + */ +static int +reqchap() +{ + lcp_wantoptions[0].neg_chap = 1; + auth_required = 1; + return (1); +} + + +/* + * setnovj - diable vj compression + */ +static int +setnovj() +{ + ipcp_wantoptions[0].neg_vj = 0; + ipcp_allowoptions[0].neg_vj = 0; + return (1); +} + +/* + * setconnector - Set a program to connect to a serial line + */ +static int +setconnector(argv) + char **argv; +{ + connector = strdup(*argv); + if (connector == NULL) + novm("connector string"); + + return (1); +} + + +/* + * setdomain - Set domain name to append to hostname + */ +static int +setdomain(argv) + char **argv; +{ + strncat(hostname, *argv, MAXNAMELEN - strlen(hostname)); + hostname[MAXNAMELEN-1] = 0; + return (1); +} + +static int +setasyncmap(argv) + char **argv; +{ + long asyncmap; + + if (!number_option(*argv, &asyncmap, 16)) + return 0; + lcp_wantoptions[0].asyncmap |= asyncmap; + lcp_wantoptions[0].neg_asyncmap = 1; + return(1); +} + +/* + * setspeed - Set the speed. + */ +static int +setspeed(arg) + char *arg; +{ + char *ptr; + int spd; + + spd = strtol(arg, &ptr, 0); + if (ptr == arg || *ptr != 0 || spd == 0) + return 0; + inspeed = spd; + return 1; +} + + +/* + * setdevname - Set the device name. + */ +int +setdevname(cp) + char *cp; +{ + struct stat statbuf; + char *tty, *ttyname(); + char dev[MAXPATHLEN]; + + if (strncmp("/dev/", cp, 5) != 0) { + strcpy(dev, "/dev/"); + strncat(dev, cp, MAXPATHLEN - 5); + dev[MAXPATHLEN-1] = 0; + cp = dev; + } + + /* + * Check if there is a device by this name. + */ + if (stat(cp, &statbuf) < 0) { + if (errno == ENOENT) + return (0); + syslog(LOG_ERR, cp); + exit(1); + } + + (void) strncpy(devname, cp, MAXPATHLEN); + devname[MAXPATHLEN-1] = 0; + default_device = FALSE; + + return (1); +} + + +/* + * setipaddr - Set the IP address + */ +int +setipaddr(arg) + char *arg; +{ + struct hostent *hp; + char *colon, *index(); + u_long local, remote; + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * IP address pair separated by ":". + */ + if ((colon = index(arg, ':')) == NULL) + return (0); + + /* + * If colon first character, then no local addr. + */ + if (colon != arg) { + *colon = '\0'; + if ((local = inet_addr(arg)) == -1) { + if ((hp = gethostbyname(arg)) == NULL) { + fprintf(stderr, "unknown host: %s", arg); + local = 0; + } else { + local = *(long *)hp->h_addr; + if (our_name[0] == 0) { + strncpy(our_name, arg, MAXNAMELEN); + our_name[MAXNAMELEN-1] = 0; + } + } + } + if (local != 0) + wo->ouraddr = local; + *colon = ':'; + } + + /* + * If colon last character, then no remote addr. + */ + if (*++colon != '\0') { + if ((remote = inet_addr(colon)) == -1) { + if ((hp = gethostbyname(colon)) == NULL) { + fprintf(stderr, "unknown host: %s", colon); + remote = 0; + } else { + remote = *(long *)hp->h_addr; + if (remote_name[0] == 0) { + strncpy(remote_name, colon, MAXNAMELEN); + remote_name[MAXNAMELEN-1] = 0; + } + } + } + if (remote != 0) + wo->hisaddr = remote; + } + + return (1); +} + + +/* + * setipdefault - default our local IP address based on our hostname. + */ +void +setipdefault() +{ + struct hostent *hp; + u_long local; + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * If local IP address already given, don't bother. + */ + if (wo->ouraddr != 0) + return; + + /* + * Look up our hostname (possibly with domain name appended) + * and take the first IP address as our local IP address. + * If there isn't an IP address for our hostname, too bad. + */ + if ((hp = gethostbyname(hostname)) == NULL) + return; + local = *(long *)hp->h_addr; + if (local != 0) + wo->ouraddr = local; +} + + +/* + * setnetmask - set the netmask to be used on the interface. + */ +static int +setnetmask(argv) + char **argv; +{ + u_long mask; + + if ((mask = inet_addr(*argv)) == -1) { + fprintf(stderr, "Invalid netmask %s\n", *argv); + exit(1); + } + + netmask = mask; + return (1); +} + +static int +setcrtscts() +{ + crtscts = 1; + return (1); +} + +static int +setnodetach() +{ + nodetach = 1; + return (1); +} + +static int +setmodem() +{ + modem = 1; + return 1; +} + +static int +setlocal() +{ + modem = 0; + return 1; +} + +static int +setusehostname() +{ + usehostname = 1; + return 1; +} + +static int +setname(argv) + char **argv; +{ + if (our_name[0] == 0) { + strncpy(our_name, argv[0], MAXNAMELEN); + our_name[MAXNAMELEN-1] = 0; + } + return 1; +} + +static int +setuser(argv) + char **argv; +{ + strncpy(user, argv[0], MAXNAMELEN); + user[MAXNAMELEN-1] = 0; + return 1; +} + +static int +setremote(argv) + char **argv; +{ + strncpy(remote_name, argv[0], MAXNAMELEN); + remote_name[MAXNAMELEN-1] = 0; + return 1; +} + +static int +setauth() +{ + auth_required = 1; + return 1; +} + +static int +setdefaultroute() +{ + ipcp_wantoptions[0].default_route = 1; + return 1; +} + +static int +setproxyarp() +{ + ipcp_wantoptions[0].proxy_arp = 1; + return 1; +} + +static int +setpersist() +{ + persist = 1; + return 1; +} + +static int +setdologin() +{ + uselogin = 1; + return 1; +} + +/* + * Functions to set timeouts, max transmits, etc. + */ +static int +setlcptimeout(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].timeouttime, 0); +} + +static int setlcpterm(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].maxtermtransmits, 0); +} + +static int setlcpconf(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].maxconfreqtransmits, 0); +} + +static int setlcpfails(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].maxnakloops, 0); +} + +static int setipcptimeout(argv) + char **argv; +{ + return int_option(*argv, &ipcp_fsm[0].timeouttime, 0); +} + +static int setipcpterm(argv) + char **argv; +{ + return int_option(*argv, &ipcp_fsm[0].maxtermtransmits, 0); +} + +static int setipcpconf(argv) + char **argv; +{ + return int_option(*argv, &ipcp_fsm[0].maxconfreqtransmits, 0); +} + +static int setipcpfails(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].maxnakloops, 0); +} + +static int setpaptimeout(argv) + char **argv; +{ + return int_option(*argv, &upap[0].us_timeouttime, 0); +} + +static int setpapreqs(argv) + char **argv; +{ + return int_option(*argv, &upap[0].us_maxtransmits, 0); +} + +static int setchaptimeout(argv) + char **argv; +{ + return int_option(*argv, &chap[0].timeouttime, 0); +} + +static int setchapchal(argv) + char **argv; +{ + return int_option(*argv, &chap[0].max_transmits, 0); +} + +static int setchapintv(argv) + char **argv; +{ + return int_option(*argv, &chap[0].chal_interval, 0); +} diff --git a/pppd/patchlevel.h b/pppd/patchlevel.h new file mode 100644 index 0000000..ac3c40c --- /dev/null +++ b/pppd/patchlevel.h @@ -0,0 +1,5 @@ +/* $Id: patchlevel.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ */ +#define PATCHLEVEL 1 + +#define VERSION "2.0" +#define DATE "9 Nov 93" diff --git a/pppd/pathnames.h b/pppd/pathnames.h new file mode 100644 index 0000000..b5a9348 --- /dev/null +++ b/pppd/pathnames.h @@ -0,0 +1,15 @@ +/* + * define path names + * + * $Id: pathnames.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ + +#ifdef STREAMS +#define _PATH_PIDFILE "/etc/ppp" +#else +#define _PATH_PIDFILE "/var/run" +#endif + +#define _PATH_UPAPFILE "/etc/ppp/pap-secrets" +#define _PATH_CHAPFILE "/etc/ppp/chap-secrets" +#define _PATH_SYSOPTIONS "/etc/ppp/options" diff --git a/pppd/ppp.h b/pppd/ppp.h new file mode 100644 index 0000000..3d8f870 --- /dev/null +++ b/pppd/ppp.h @@ -0,0 +1,41 @@ +/* + * ppp.h - PPP global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: ppp.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ + +#ifndef __PPP_H__ +#define __PPP_H__ + +#define NPPP 1 /* One PPP interface supported (per process) */ + +/* + * Data Link Layer header = Address, Control, Protocol. + */ +#define ALLSTATIONS 0xff /* All-Stations Address */ +#define UI 0x03 /* Unnumbered Information */ +#define LCP 0xc021 /* Link Control Protocol */ +#define IPCP 0x8021 /* IP Control Protocol */ +#define UPAP 0xc023 /* User/Password Authentication Protocol */ +#define CHAP 0xc223 /* Crytpographic Handshake Protocol */ +#define LQR 0xc025 /* Link Quality Report protocol */ +#define IP_VJ_COMP 0x002d /* VJ TCP compressed IP packet */ +#define DLLHEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) +#define MTU 1500 /* Default MTU */ + +#endif /* __PPP_H__ */ diff --git a/pppd/pppd.h b/pppd/pppd.h new file mode 100644 index 0000000..c5ea8b6 --- /dev/null +++ b/pppd/pppd.h @@ -0,0 +1,199 @@ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: pppd.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ + +/* + * TODO: + */ + +#ifndef __PPPD_H__ +#define __PPPD_H__ +#include "args.h" + +#include /* for MAXPATHLEN and BSD4_4, if defined */ + +#define NPPP 1 /* One PPP interface supported (per process) */ + +/* + * Limits. + */ +#define MAXWORDLEN 1024 /* max length of word in file (incl null) */ +#define MAXARGS 1 /* max # args to a command */ +#define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#define MAXSECRETLEN 256 /* max length of password or secret */ + +extern int debug; /* Debug flag */ +extern int ifunit; /* Interface unit number */ +extern char ifname[]; /* Interface name */ +extern int fd; /* Device file descriptor */ +extern int s; /* socket descriptor */ +extern char hostname[]; /* hostname */ +extern u_char outpacket_buf[]; /* buffer for outgoing packets */ + +void quit __ARGS((void)); /* Cleanup and exit */ +void timeout __ARGS((void (*)(), caddr_t, int)); + /* Look-alike of kernel's timeout() */ +void untimeout __ARGS((void (*)(), caddr_t)); + /* Look-alike of kernel's untimeout() */ +void output __ARGS((int, u_char *, int)); + /* Output a PPP packet */ +void demuxprotrej __ARGS((int, int)); + /* Demultiplex a Protocol-Reject */ +int check_passwd __ARGS((int, char *, int, char *, int, char **, int *)); + /* Check peer-supplied username/password */ +int get_secret __ARGS((int, char *, char *, char *, int *, int)); + /* get "secret" for chap */ +u_long GetMask __ARGS((u_long)); /* get netmask for address */ + + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (s) >> 8; \ + *(cp)++ = (s); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp)++ << 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (l) >> 24; \ + *(cp)++ = (l) >> 16; \ + *(cp)++ = (l) >> 8; \ + *(cp)++ = (l); \ +} + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +/* + * System dependent definitions for user-level 4.3BSD UNIX implementation. + */ + +#define DEMUXPROTREJ(u, p) demuxprotrej(u, p) + +#define TIMEOUT(r, f, t) timeout((r), (f), (t)) +#define UNTIMEOUT(r, f) untimeout((r), (f)) + +#define BCOPY(s, d, l) memcpy(d, s, l) +#define BZERO(s, n) memset(s, 0, n) +#define EXIT(u) quit() + +#define PRINTMSG(m, l) { m[l] = '\0'; syslog(LOG_INFO, "Remote message: %s", m); } + +/* + * MAKEHEADER - Add Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(ALLSTATIONS, p); \ + PUTCHAR(UI, p); \ + PUTSHORT(t, p); } + + +#ifdef DEBUGALL +#define DEBUGMAIN 1 +#define DEBUGFSM 1 +#define DEBUGLCP 1 +#define DEBUGIPCP 1 +#define DEBUGUPAP 1 +#define DEBUGCHAP 1 +#endif + +#ifndef LOG_PPP /* we use LOG_LOCAL2 for syslog by default */ +#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUG) \ + || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \ + || defined(DEBUGCHAP) +#define LOG_PPP LOG_LOCAL2 +#else +#define LOG_PPP LOG_DAEMON +#endif +#endif /* LOG_PPP */ + +#ifdef DEBUGMAIN +#define MAINDEBUG(x) if (debug) syslog x +#else +#define MAINDEBUG(x) +#endif + +#ifdef DEBUGFSM +#define FSMDEBUG(x) if (debug) syslog x +#else +#define FSMDEBUG(x) +#endif + +#ifdef DEBUGLCP +#define LCPDEBUG(x) if (debug) syslog x +#else +#define LCPDEBUG(x) +#endif + +#ifdef DEBUGIPCP +#define IPCPDEBUG(x) if (debug) syslog x +#else +#define IPCPDEBUG(x) +#endif + +#ifdef DEBUGUPAP +#define UPAPDEBUG(x) if (debug) syslog x +#else +#define UPAPDEBUG(x) +#endif + +#ifdef DEBUGCHAP +#define CHAPDEBUG(x) if (debug) syslog x +#else +#define CHAPDEBUG(x) +#endif + +#ifndef SIGTYPE +#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) +#define SIGTYPE void +#else +#define SIGTYPE int +#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */ +#endif /* SIGTYPE */ + +#ifndef MIN +#define MIN(a, b) ((a) < (b)? (a): (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) > (b)? (a): (b)) +#endif + +#endif /* __PPP_H__ */ diff --git a/pppd/sys-bsd.c b/pppd/sys-bsd.c new file mode 100644 index 0000000..350e0a0 --- /dev/null +++ b/pppd/sys-bsd.c @@ -0,0 +1,482 @@ +/* + * sys-bsd.c - System-dependent procedures for setting up + * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.) + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: sys-bsd.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "ppp.h" + +static int initdisc; /* Initial TTY discipline */ + + +/* + * establish_ppp - Turn the serial port into a ppp interface. + */ +void +establish_ppp() +{ + int pppdisc = PPPDISC; + + if (ioctl(fd, TIOCGETD, &initdisc) < 0) { + syslog(LOG_ERR, "ioctl(TIOCGETD): %m"); + die(1); + } + if (ioctl(fd, TIOCSETD, &pppdisc) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSETD): %m"); + die(1); + } + + /* + * Find out which interface we were given. + */ + if (ioctl(fd, PPPIOCGUNIT, &ifunit) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m"); + die(1); + } +} + + +/* + * disestablish_ppp - Restore the serial port to normal operation. + * This shouldn't call die() because it's called from die(). + */ +void +disestablish_ppp() +{ + if (ioctl(fd, TIOCSETD, &initdisc) < 0) + syslog(LOG_ERR, "ioctl(TIOCSETD): %m"); +} + + +/* + * output - Output PPP packet. + */ +void +output(unit, p, len) + int unit; + u_char *p; + int len; +{ + if (unit != 0) + MAINDEBUG((LOG_WARNING, "output: unit != 0!")); + + if (write(fd, p, len) < 0) { + syslog(LOG_ERR, "write: %m"); + die(1); + } +} + + +/* + * read_packet - get a PPP packet from the serial device. + */ +int +read_packet(buf) + u_char *buf; +{ + int len; + + if ((len = read(fd, buf, MTU + DLLHEADERLEN)) < 0) { + if (errno == EWOULDBLOCK) { + MAINDEBUG((LOG_DEBUG, "read(fd): EWOULDBLOCK")); + return -1; + } + syslog(LOG_ERR, "read(fd): %m"); + die(1); + } + return len; +} + + +/* + * ppp_send_config - configure the transmit characteristics of + * the ppp interface. + */ +void +ppp_send_config(unit, mtu, asyncmap, pcomp, accomp) + int unit, mtu; + u_long asyncmap; + int pcomp, accomp; +{ + u_int x; + struct ifreq ifr; + + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + ifr.ifr_mtu = mtu; + if (ioctl(s, SIOCSIFMTU, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFMTU): %m"); + quit(); + } + + if (ioctl(fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSASYNCMAP): %m"); + quit(); + } + + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); + quit(); + } + x = pcomp? x | SC_COMP_PROT: x &~ SC_COMP_PROT; + x = accomp? x | SC_COMP_AC: x &~ SC_COMP_AC; + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + quit(); + } +} + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. At present this does nothing. + */ +void +ppp_recv_config(unit, mru, asyncmap, pcomp, accomp) + int unit, mru; + u_long asyncmap; + int pcomp, accomp; +{ +#ifdef notyet + if (ioctl(fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSRASYNCMAP): %m"); + quit(); + } + + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); + quit(); + } + x = !accomp? x | SC_REJ_COMP_AC: x &~ SC_REJ_COMP_AC; + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + quit(); + } +#endif /* notyet */ +} + +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp(u, vjcomp, cidcomp) +{ + u_int x; + + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); + return 0; + } + x = vjcomp ? x | SC_COMP_TCP: x &~ SC_COMP_TCP; + x = cidcomp? x & ~SC_NO_TCP_CCID: x | SC_NO_TCP_CCID; + if(ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + return 0; + } + return 1; +} + +/* + * sifup - Config the interface up. + */ +int +sifup(u) +{ + struct ifreq ifr; + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m"); + return 0; + } + ifr.ifr_flags |= IFF_UP; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); + return 0; + } + return 1; +} + +/* + * sifdown - Config the interface down. + */ +int +sifdown(u) +{ + struct ifreq ifr; + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m"); + return 0; + } + ifr.ifr_flags &= ~IFF_UP; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); + return 0; + } + return 1; +} + +/* + * SET_SA_FAMILY - set the sa_family field of a struct sockaddr, + * if it exists. + */ +#define SET_SA_FAMILY(addr, family) \ + BZERO((char *) &(addr), sizeof(addr)); \ + addr.sa_family = (family); \ + addr.sa_len = sizeof(addr); + +/* + * sifaddr - Config the interface IP addresses and netmask. + */ +int +sifaddr(u, o, h, m) +{ + struct ifaliasreq ifra; + + strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name)); + SET_SA_FAMILY(ifra.ifra_addr, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o; + SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h; + if (m != 0) { + SET_SA_FAMILY(ifra.ifra_mask, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_mask)->sin_addr.s_addr = m; + } else + BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask)); + if (ioctl(s, SIOCAIFADDR, (caddr_t) &ifra) < 0) { + if (errno != EEXIST) { + syslog(LOG_ERR, "ioctl(SIOCAIFADDR): %m"); + return 0; + } + syslog(LOG_WARNING, "ioctl(SIOCAIFADDR): Address already exists"); + } + return 1; +} + +/* + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + */ +int +cifaddr(u, o, h) +{ + struct ifaliasreq ifra; + + strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name)); + SET_SA_FAMILY(ifra.ifra_addr, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o; + SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h; + BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask)); + if (ioctl(s, SIOCDIFADDR, (caddr_t) &ifra) < 0) { + syslog(LOG_WARNING, "ioctl(SIOCDIFADDR): %m"); + return 0; + } + return 1; +} + +/* + * sifdefaultroute - assign a default route through the address given. + */ +int +sifdefaultroute(u, g) +{ + struct ortentry rt; + + SET_SA_FAMILY(rt.rt_dst, AF_INET); + SET_SA_FAMILY(rt.rt_gateway, AF_INET); + ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g; + rt.rt_flags = RTF_GATEWAY; + if (ioctl(s, SIOCADDRT, &rt) < 0) { + syslog(LOG_ERR, "default route ioctl(SIOCADDRT): %m"); + return 0; + } + return 1; +} + +/* + * cifdefaultroute - delete a default route through the address given. + */ +int +cifdefaultroute(u, g) +{ + struct ortentry rt; + + SET_SA_FAMILY(rt.rt_dst, AF_INET); + SET_SA_FAMILY(rt.rt_gateway, AF_INET); + ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g; + rt.rt_flags = RTF_GATEWAY; + if (ioctl(s, SIOCDELRT, &rt) < 0) + syslog(LOG_WARNING, "default route ioctl(SIOCDELRT): %m"); +} + +/* + * sifproxyarp - Make a proxy ARP entry for the peer. + */ +int +sifproxyarp(unit, hisaddr) + int unit; + u_long hisaddr; +{ + struct arpreq arpreq; + + BZERO(&arpreq, sizeof(arpreq)); + + /* + * Get the hardware address of an interface on the same subnet + * as our local address. + */ + if (!get_ether_addr(hisaddr, &arpreq.arp_ha)) { + syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP"); + return 0; + } + + SET_SA_FAMILY(arpreq.arp_pa, AF_INET); + ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr; + arpreq.arp_flags = ATF_PERM | ATF_PUBL; + if (ioctl(s, SIOCSARP, (caddr_t)&arpreq) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSARP): %m"); + return 0; + } + + return 1; +} + +/* + * cifproxyarp - Delete the proxy ARP entry for the peer. + */ +int +cifproxyarp(unit, hisaddr) + int unit; + u_long hisaddr; +{ + struct arpreq arpreq; + + BZERO(&arpreq, sizeof(arpreq)); + SET_SA_FAMILY(arpreq.arp_pa, AF_INET); + ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr; + if (ioctl(s, SIOCDARP, (caddr_t)&arpreq) < 0) { + syslog(LOG_WARNING, "ioctl(SIOCDARP): %m"); + return 0; + } + return 1; +} + +/* + * get_ether_addr - get the hardware address of an interface on the + * the same subnet as ipaddr. + */ +#define MAX_IFS 32 + +int +get_ether_addr(ipaddr, hwaddr) + u_long ipaddr; + struct sockaddr *hwaddr; +{ + struct ifreq *ifr, *ifend, *ifp; + u_long ina, mask; + struct sockaddr_dl *dla; + struct ifreq ifreq; + struct ifconf ifc; + struct ifreq ifs[MAX_IFS]; + + ifc.ifc_len = sizeof(ifs); + ifc.ifc_req = ifs; + if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { + syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m"); + return 0; + } + + /* + * Scan through looking for an interface with an Internet + * address on the same subnet as `ipaddr'. + */ + ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < ifend; ) { + if (ifr->ifr_addr.sa_family == AF_INET) { + ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr; + strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); + /* + * Check that the interface is up, and not point-to-point + * or loopback. + */ + if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0) + continue; + if ((ifreq.ifr_flags & + (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP)) + != (IFF_UP|IFF_BROADCAST)) + continue; + /* + * Get its netmask and check that it's on the right subnet. + */ + if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0) + continue; + mask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr; + if ((ipaddr & mask) != (ina & mask)) + continue; + + break; + } + ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len); + } + + if (ifr >= ifend) + return 0; + syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name); + + /* + * Now scan through again looking for a link-level address + * for this interface. + */ + ifp = ifr; + for (ifr = ifc.ifc_req; ifr < ifend; ) { + if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 + && ifr->ifr_addr.sa_family == AF_LINK) { + /* + * Found the link-level address - copy it out + */ + dla = (struct sockaddr_dl *)&ifr->ifr_addr; + hwaddr->sa_len = sizeof(struct sockaddr); + hwaddr->sa_family = AF_UNSPEC; + BCOPY(LLADDR(dla), hwaddr->sa_data, dla->sdl_alen); + return 1; + } + ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len); + } + + return 0; +} diff --git a/pppd/sys-str.c b/pppd/sys-str.c new file mode 100644 index 0000000..ecee95b --- /dev/null +++ b/pppd/sys-str.c @@ -0,0 +1,624 @@ +/* + * sys-str.c - System-dependent procedures for setting up + * PPP interfaces on systems which use the STREAMS ppp interface. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include +#include + +/* XXX SunOS 4.1 defines this and 3.5 doesn't... */ +#ifdef _nlist_h +#define SUNOS4 +#endif + +#ifdef SUNOS4 +#include +#endif +#include + +/* Cast a struct sockaddr to a structaddr_in */ +#define SATOSIN(sa) ((struct sockaddr_in *)(sa)) + +/* Determine if "bits" is set in "flag" */ +#define ALLSET(flag, bits) (((flag) & (bits)) == (bits)) + +static struct nlist nl[] = { +#define N_IFNET 0 + { "_ifnet" }, + 0 +}; + +static void kread(); + +int +get_ether_addr(ipaddr, hwaddr) + u_long ipaddr; + struct sockaddr *hwaddr; +{ + register kvm_t *kd; + register struct ifnet *ifp; + register struct arpcom *ac; + struct arpcom arpcom; + struct in_addr *inp; +#ifdef SUNOS4 + register struct ifaddr *ifa; + register struct in_ifaddr *in; + union { + struct ifaddr ifa; + struct in_ifaddr in; + } ifaddr; +#endif + u_long addr, mask; + + /* Open kernel memory for reading */ + kd = kvm_open(0, 0, 0, O_RDONLY, NULL); + if (kd == 0) { + syslog(LOG_ERR, "kvm_open: %m"); + return 0; + } + + /* Fetch namelist */ + if (kvm_nlist(kd, nl) != 0) { + syslog(LOG_ERR, "kvm_nlist failed"); + return 0; + } + + ac = &arpcom; + ifp = &arpcom.ac_if; +#ifdef SUNOS4 + ifa = &ifaddr.ifa; + in = &ifaddr.in; +#endif + + if (kvm_read(kd, nl[N_IFNET].n_value, (char *)&addr, sizeof(addr)) + != sizeof(addr)) { + syslog(LOG_ERR, "error reading ifnet addr"); + return 0; + } + for ( ; addr; addr = (u_long)ifp->if_next) { + if (kvm_read(kd, addr, (char *)ac, sizeof(*ac)) != sizeof(*ac)) { + syslog(LOG_ERR, "error reading ifnet"); + return 0; + } + + /* Only look at configured, broadcast interfaces */ + if (!ALLSET(ifp->if_flags, IFF_UP | IFF_BROADCAST)) + continue; +#ifdef SUNOS4 + /* This probably can't happen... */ + if (ifp->if_addrlist == 0) + continue; +#endif + + /* Get interface ip address */ +#ifdef SUNOS4 + if (kvm_read(kd, (u_long)ifp->if_addrlist, (char *)&ifaddr, + sizeof(ifaddr)) != sizeof(ifaddr)) { + syslog(LOG_ERR, "error reading ifaddr"); + return 0; + } + inp = &SATOSIN(&ifa->ifa_addr)->sin_addr; +#else + inp = &SATOSIN(&ifp->if_addr)->sin_addr; +#endif + + /* Check if this interface on the right subnet */ +#ifdef SUNOS4 + mask = in->ia_subnetmask; +#else + mask = ifp->if_subnetmask; +#endif + if ((ipaddr & mask) != (inp->s_addr & mask)) + continue; + + /* Copy out the local ethernet address */ + hwaddr->sa_family = AF_UNSPEC; + BCOPY((caddr_t) &arpcom.ac_enaddr, hwaddr->sa_data, + sizeof(arpcom.ac_enaddr)); + return 1; /* success! */ + } + + /* couldn't find one */ + return 0; +} diff --git a/pppd/upap.c b/pppd/upap.c new file mode 100644 index 0000000..47b3477 --- /dev/null +++ b/pppd/upap.c @@ -0,0 +1,484 @@ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = "$Id: upap.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include + +#include "ppp.h" +#include "pppd.h" +#include "upap.h" + + +upap_state upap[NPPP]; /* UPAP state; one for each unit */ + + +static void upap_timeout __ARGS((caddr_t)); +static void upap_rauthreq __ARGS((upap_state *, u_char *, int, int)); +static void upap_rauthack __ARGS((upap_state *, u_char *, int, int)); +static void upap_rauthnak __ARGS((upap_state *, u_char *, int, int)); +static void upap_sauthreq __ARGS((upap_state *)); +static void upap_sresp __ARGS((upap_state *, int, int, char *, int)); + + +/* + * upap_init - Initialize a UPAP unit. + */ +void +upap_init(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; +} + + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void +upap_authwithpeer(unit, user, password) + int unit; + char *user, *password; +{ + upap_state *u = &upap[unit]; + + /* Save the username and password we're given */ + u->us_user = user; + u->us_userlen = strlen(user); + u->us_passwd = password; + u->us_passwdlen = strlen(password); + u->us_transmits = 0; + + /* Lower layer up yet? */ + if (u->us_clientstate == UPAPCS_INITIAL || + u->us_clientstate == UPAPCS_PENDING) { + u->us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(u); /* Start protocol */ +} + + +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void +upap_authpeer(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + /* Lower layer up yet? */ + if (u->us_serverstate == UPAPSS_INITIAL || + u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_PENDING; + return; + } + + u->us_serverstate = UPAPSS_LISTEN; +} + + +/* + * upap_timeout - Timeout expired. + */ +static void +upap_timeout(arg) + caddr_t arg; +{ + upap_state *u = (upap_state *) arg; + + if (u->us_clientstate != UPAPCS_AUTHREQ) + return; + + if (u->us_transmits >= u->us_maxtransmits) { + /* give up in disgust */ + syslog(LOG_ERR, "No response to PAP authenticate-requests"); + u->us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(u->us_unit, UPAP); + return; + } + + upap_sauthreq(u); /* Send Authenticate-Request */ +} + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +void +upap_lowerup(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_INITIAL) + u->us_clientstate = UPAPCS_CLOSED; + else if (u->us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(u); /* send an auth-request */ + } + + if (u->us_serverstate == UPAPSS_INITIAL) + u->us_serverstate = UPAPSS_CLOSED; + else if (u->us_serverstate == UPAPSS_PENDING) + u->us_serverstate = UPAPSS_LISTEN; +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +void +upap_lowerdown(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */ + UNTIMEOUT(upap_timeout, (caddr_t) u); /* Cancel timeout */ + + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +void +upap_protrej(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) { + syslog(LOG_ERR, "PAP authentication failed due to protocol-reject"); + auth_withpeer_fail(unit, UPAP); + } + if (u->us_serverstate == UPAPSS_LISTEN) { + syslog(LOG_ERR, "PAP authentication of peer failed (protocol-reject)"); + auth_peer_fail(unit, UPAP); + } + upap_lowerdown(unit); +} + + +/* + * upap_input - Input UPAP packet. + */ +void +upap_input(unit, inpacket, l) + int unit; + u_char *inpacket; + int l; +{ + upap_state *u = &upap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "upap_input: rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "upap_input: rcvd illegal length.")); + return; + } + if (len > l) { + UPAPDEBUG((LOG_INFO, "upap_input: rcvd short packet.")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: + upap_rauthreq(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + break; + } +} + + +/* + * upap_rauth - Receive Authenticate. + */ +static void +upap_rauthreq(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char ruserlen, rpasswdlen; + char *ruser, *rpasswd; + int retcode; + char *msg; + int msglen; + + UPAPDEBUG((LOG_INFO, "upap_rauth: Rcvd id %d.", id)); + + if (u->us_serverstate < UPAPSS_LISTEN) + return; + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (u->us_serverstate == UPAPSS_OPEN) { + upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (u->us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "upap_rauth: rcvd short packet.")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char);; + if (len < 0) { + UPAPDEBUG((LOG_INFO, "upap_rauth: rcvd short packet.")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG((LOG_INFO, "upap_rauth: rcvd short packet.")); + return; + } + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, + rpasswdlen, &msg, &msglen); + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, UPAP); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + auth_peer_fail(u->us_unit, UPAP); + } +} + + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void +upap_rauthack(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char msglen; + char *msg; + + UPAPDEBUG((LOG_INFO, "upap_rauthack: Rcvd id %d.", id)); + if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "upap_rauthack: rcvd short packet.")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "upap_rauthack: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(u->us_unit, UPAP); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nakk. + */ +static void +upap_rauthnak(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char msglen; + char *msg; + + UPAPDEBUG((LOG_INFO, "upap_rauthnak: Rcvd id %d.", id)); + if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "upap_rauthnak: rcvd short packet.")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "upap_rauthnak: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_BADAUTH; + + syslog(LOG_ERR, "PAP authentication failed"); + auth_withpeer_fail(u->us_unit, UPAP); +} + + +/* + * upap_sauthreq - Send an Authenticate-Request. + */ +static void +upap_sauthreq(u) + upap_state *u; +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + u->us_userlen + u->us_passwdlen; + outp = outpacket_buf; + + MAKEHEADER(outp, UPAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++u->us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(u->us_userlen, outp); + BCOPY(u->us_user, outp, u->us_userlen); + INCPTR(u->us_userlen, outp); + PUTCHAR(u->us_passwdlen, outp); + BCOPY(u->us_passwd, outp, u->us_passwdlen); + + output(u->us_unit, outpacket_buf, outlen + DLLHEADERLEN); + + UPAPDEBUG((LOG_INFO, "upap_sauth: Sent id %d.", u->us_id)); + + TIMEOUT(upap_timeout, (caddr_t) u, u->us_timeouttime); + ++u->us_transmits; + u->us_clientstate = UPAPCS_AUTHREQ; +} + + +/* + * upap_sresp - Send a response (ack or nak). + */ +static void +upap_sresp(u, code, id, msg, msglen) + upap_state *u; + u_char code, id; + char *msg; + int msglen; +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + outp = outpacket_buf; + MAKEHEADER(outp, UPAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + BCOPY(msg, outp, msglen); + output(u->us_unit, outpacket_buf, outlen + DLLHEADERLEN); + + UPAPDEBUG((LOG_INFO, "upap_sresp: Sent code %d, id %d.", code, id)); +} diff --git a/pppd/upap.h b/pppd/upap.h new file mode 100644 index 0000000..c77c198 --- /dev/null +++ b/pppd/upap.h @@ -0,0 +1,89 @@ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: upap.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ + +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + + +/* + * Each interface is described by upap structure. + */ +typedef struct upap_state { + int us_unit; /* Interface unit number */ + char *us_user; /* User */ + int us_userlen; /* User length */ + char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + u_char us_id; /* Current id */ + int us_timeouttime; /* Timeout time in milliseconds */ + int us_transmits; /* Number of auth-reqs sent */ + int us_maxtransmits; /* Maximum number of auth-reqs to send */ +} upap_state; + + +/* + * Client states. + */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ + +/* + * Server states. + */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ + + +/* + * Timeouts. + */ +#define UPAP_DEFTIMEOUT 3 /* Timeout time in seconds */ + + +extern upap_state upap[]; + +void upap_init __ARGS((int)); +void upap_authwithpeer __ARGS((int, char *, char *)); +void upap_authpeer __ARGS((int)); +void upap_lowerup __ARGS((int)); +void upap_lowerdown __ARGS((int)); +void upap_input __ARGS((int, u_char *, int)); +void upap_protrej __ARGS((int)); diff --git a/pppstats/pppstats.c b/pppstats/pppstats.c new file mode 100644 index 0000000..5fb2f77 --- /dev/null +++ b/pppstats/pppstats.c @@ -0,0 +1,354 @@ +/* + * print PPP statistics: + * pppstats [-i interval] [-v] [interface] [system] [core] + * + * Brad Parker (brad@cayman.com) 6/92 + * + * from the original "slstats" by Van Jaconson + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + */ + +#ifndef lint +static char rcsid[] = "$Id: pppstats.c,v 1.1 1993/11/11 03:54:25 paulus Exp $"; +#endif + +#include +#include +#include +#include +#include +#ifndef KVMLIB +#include +#endif +#ifdef sun +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VJC 1 +#include +#include + +#ifdef STREAMS +#include +#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; +} -- 2.39.2