diff --git a/usr.sbin/pppd/RELNOTES b/usr.sbin/pppd/RELNOTES new file mode 100644 index 000000000000..b993de2b4689 --- /dev/null +++ b/usr.sbin/pppd/RELNOTES @@ -0,0 +1,295 @@ + pppd-2.1.1 release notes + Paul Mackerras 27 May 1994 + +This file details the new and changed features in pppd since version 1.3. +Briefly: + - the protocol code has been updated to conform with + RFCs 1548, 1549, 1332 and 1334 + - security has been improved + - functionality has been improved in various ways. + + +NEW FEATURES + +* The option negotiation automaton has been updated to RFC1548. LCP +now rejects the Quality Protocol option, since LQR is not implemented +yet. IPCP now uses the IP-Address option, and falls back to the old +IP-Addresses option if the IP-Address option is rejected. IPCP also +uses the new form of the VJ-Compression option. + +RFC1548 defines the "passive" option to mean that the automaton +outputs configure-request packets initially, but does not close down +if no answer is received. A valid configure-request received will +restart the negotiation. The "silent" option has been added with the +old meaning of "passive", i.e. the automaton will not output +configure-requests until it receives a valid one from the peer. + +* More systems are supported: in addition to SunOS 4.x and BSD/Net-2 +derived systems, Ultrix and Linux are supported, thanks to Robert +Olsson, Per Sundstrom, Michael Callahan and Al Longyear. + +* Options can be taken from files as well as the command line. pppd +reads options from the files /etc/ppp/options and ~/.ppprc before +looking at the command line, and /etc/ppp/options. after +interpreting the options on the command line. An options file is +parsed into a series of words, delimited by whitespace. Whitespace +can be included in a word by enclosing the word in quotes ("). +Backslash (\) quotes the following character. A hash (#) starts a +comment, which continues until the end of the line. In addition, the +`file' option causes pppd to read options from a file. pppd will +report and error and exit if ~/.ppprc or the file given as the +argument to the `file' option cannot be read by the user who invoked +pppd. + +* On those systems, such as NetBSD, where the serial line speed is +stored in the termios structure in bits per second (i.e. B9600 == +9600), it is possible to set any speed. + +* If desired, pppd will output LCP echo-request frames periodically +while the link is up, and take the link down if no replies are +received to a user-configurable number of echo-requests. This can be +used to detect that the serial connection has been broken on those +systems which don't have hardware modem control lines. + +AUTHENTICATION + +Previous versions of pppd have provided no control over which IP +addresses the peer can use. Thus it is possible for the peer to +impersonate another host on the local network, leading to various +security holes. In addition, the authentication mechanisms were quite +weak: if the peer refused to agree to authenticate, pppd would print a +warning message but still allow the link to come up. The CHAP +implementation also appeared to be quite broken (has anybody actually +used it?). + +This new version of pppd addresses these problems. My aim has been to +provide system administrators with sufficient access control that PPP +access to a server machine can be provided to legitimate users without +fear of compromising the security of the server or the network it's +on. In part this is provided by the /etc/ppp/options file, where the +administrator can place options to require authentication which cannot +be disabled by users. Thus the new pppd can made setuid-root and run +by users. + +The behaviour where pppd refuses to run unless the /etc/ppp/options +file is present and readable by pppd is now the default behaviour. If +you really want pppd to run without the presence of the +/etc/ppp/options file, you will have to include -DREQ_SYSOPTIONS=0 on +the compilation command line. + +The options related to authentication are: + + auth Require authentication from the peer. If neither + +chap or +pap is also given, either CHAP or PAP + authentication will be accepted. + +chap Require CHAP authentication from the peer. + +pap Require PAP authentication from the peer. + -chap Don't agree to authenticate ourselves with the peer + using CHAP. + -pap Don't agree to authenticate ourselves using PAP. + +ua Get username and password for authenticating ourselves + with the peer using PAP from file . + name Use as the local name for authentication. + usehostname Use this machine's hostname as the local name for + authentication. + remotename Use as the name of the peer for authentication. + login If the peer authenticates using PAP, check the + supplied username and password against the system + password database, and make a wtmp entry. + user Use as the username for authenticating ourselves + using PAP. + +The defaults are to agree to authenticate if requested, and to not +require authentication from the peer. However, pppd will not agree to +authenticate itself with a particular protocol if it has no secrets +which could be used to do so. + +Authentication is based on secrets, which are selected from secrets +files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP). +Both secrets files have the same format, and both can store secrets +for several combinations of server (authenticating peer) and client +(peer being authenticated). Note that each end can be both a server +and client, and that different protocols can be used in the two +directions if desired. + +A secrets file is parsed into words as for a options file. A secret +is specified by a line containing at least 3 words, in the order +client, server, secret. Any following words on the same line are +taken to be a list of acceptable IP addresses for that client. If +there are only 3 words on the line, it is assumed that any IP address +is OK; to disallow all IP addresses, use "-". If the secret starts +with an `@', what follows is assumed to be the name of a file from +which to read the secret. A "*" as the client or server name matches +any name. When selecting a secret, pppd takes the best match, i.e. +the match with the fewest wildcards. + +Thus a secrets file contains both secrets for use in authenticating +other hosts, plus secrets which we use for authenticating ourselves to +others. Which secret to use is chosen based on the names of the host +(the `local name') and its peer (the `remote name'). The local name +is set as follows: + + if the `usehostname' option is given, + then the local name is the hostname of this machine + (with the domain appended, if given) + + else if the `name' option is given, + then use the argument of the first `name' option seen + + else if the local IP address is specified with a + host name (e.g. `sirius:') + then use that host name + + else use the hostname of this machine + (with the domain appended, if given) + +When authenticating ourselves using PAP, there is also a `username' +which is the local name by default, but can be set with the `user' +option or the `+ua' option. + +The remote name is set as follows: + + if the `remotename' option is given, + then use the argument of the last `remotename' option seen + + else if the remote IP address is specified with a + host name (e.g. `avago:') + then use that host name + + else the remote name is the null string "". + +Secrets are selected from the PAP secrets file as follows: + +- For authenticating the peer, look for a secret with client == +username specified in the PAP authenticate-request, and server == +local name. + +- For authenticating ourselves to the peer, look for a secret with +client == our username, server == remote name. + +When authenticating the peer with PAP, a secret of "" matches any +password supplied by the peer. If the password doesn't match the +secret, the password is encrypted using crypt() and checked against +the secret again; thus secrets for authenticating the peer can be +stored in encrypted form. If the `login' option was specified, the +username and password are also checked against the system password +database. Thus, the system administrator can set up the pap-secrets +file to allow PPP access only to certain users, and to restrict the +set of IP addresses that each user can use. + +Secrets are selected from the CHAP secrets file as follows: + +- For authenticating the peer, look for a secret with client == name +specified in the CHAP-Response message, and server == local name. + +- For authenticating ourselves to the peer, look for a secret with +client == local name, and server == name specified in the +CHAP-Challenge message. + +Authentication must be satisfactorily completed before IPCP (or any +other Network Control Protocol) can be started. If authentication +fails, pppd will terminated the link (by closing LCP). If IPCP +negotiates an unacceptable IP address for the remote host, IPCP will +be closed. IP packets cannot be sent or received until IPCP is +successfully opened. + +(some examples needed here perhaps) + + +ROUTING + +Setting the addresses on a ppp interface is sufficient to create a +host route to the remote end of the link. Sometimes it is desirable +to add a default route through the remote host, as in the case of a +machine whose only connection to the Internet is through the ppp +interface. The `defaultroute' option causes pppd to create such a +default route when IPCP comes up, and delete it when the link is +terminated. + +In some cases it is desirable to use proxy ARP, for example on a +server machine connected to a LAN, in order to allow other hosts to +communicate with the remote host. The `proxyarp' option causes pppd +to look for a network interface (an interface supporting broadcast and +ARP, which is up and not a point-to-point or loopback interface) on +the same subnet as the remote host. If found, pppd creates a +permanent, published ARP entry with the IP address of the remote host +and the hardware address of the network interface found. + + +OTHER NEW AND CHANGED OPTIONS + + modem Use modem control lines (not fully implemented + yet) + local Don't use modem control lines + persist Keep reopening connection (not fully + implemented yet) + + lcp-restart Set timeout for LCP retransmissions to + seconds (default 3 seconds) + lcp-max-terminate Set maximum number of LCP terminate-request + transmissions (default 2) + lcp-max-configure Set maximum number of LCP configure-request + transmissions (default 10) + lcp-max-failure Set maximum number of LCP configure-Naks sent + before converting to configure-rejects + (default 10) + + ipcp-restart Set timeout for IPCP retransmissions to + seconds (default 3 seconds) + ipcp-max-terminate Set maximum number of IPCP + terminate-request transmissions (default 2) + ipcp-max-configure Set maximum number of IPCP + configure-request transmissions (default 10) + ipcp-max-failure Set maximum number of IPCP configure-Naks + sent before converting to configure-rejects + (default 10) + + upap-restart Set timeout for PAP retransmissions to + seconds (default 3 seconds) + upap-max-authreq Set maximum number of Authenticate-request + retransmissions (default 10) + + chap-restart Set timeout for CHAP retransmissions to + seconds (default 3 seconds) + chap-max-challenge Set maximum number of CHAP Challenge + retransmissions (default 10) + chap-interval Set the interval between CHAP rechallenges + (default 0, meaning infinity) + +The -ua option no longer exists. + + +SOFTWARE RESTRUCTURING + +Many of the source files for pppd have changed significantly from +ppp-1.3, upon which it is based. In particular: + +- the macros for system-dependent operations in pppd.h have mostly +been removed. Instead these operations are performed by procedures in +sys-bsd.c (for BSD-4.4ish systems like NetBSD, 386BSD, etc.) or +sys-str.c (for SunOS-based systems using STREAMS). (I got sick of +having to recompile everything every time I wanted to change one of +those horrible macros.) + +- most of the system-dependent code in main.c has also been removed to +sys-bsd.c and sys-str.c. + +- the option processing code in main.c has been removed to options.c. + +- the authentication code in main.c has been removed to auth.c, which +also contains substantial amounts of new code. + +- fsm.c has changed significantly, and lcp.c, ipcp.c, and upap.c have +changed somewhat. chap.c has also changed significantly. + + +STILL TO DO + +* sort out appropriate modem control and implement the persist option +properly; add an `answer' option for auto-answering a modem. + +* add an inactivity timeout and demand dialing. + +* implement link quality monitoring. + +* implement other network control protocols. diff --git a/usr.sbin/pppd/args.h b/usr.sbin/pppd/args.h new file mode 100644 index 000000000000..e8798382786b --- /dev/null +++ b/usr.sbin/pppd/args.h @@ -0,0 +1,12 @@ +/* + * neat macro from ka9q to "do the right thing" with ansi prototypes + * $Id: args.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ + */ + +#ifndef __ARGS +#ifdef __STDC__ +#define __ARGS(x) x +#else +#define __ARGS(x) () +#endif +#endif diff --git a/usr.sbin/pppd/auth.c b/usr.sbin/pppd/auth.c new file mode 100644 index 000000000000..950ef024e6b7 --- /dev/null +++ b/usr.sbin/pppd/auth.c @@ -0,0 +1,904 @@ +/* + * 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.6 1994/05/25 06:25:05 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 +#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 null_login __ARGS((int)); +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(); + phase = PHASE_DEAD; + syslog(LOG_NOTICE, "Connection terminated."); +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void +link_down(unit) + int unit; +{ + phase = PHASE_TERMINATE; +} + +/* + * 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 itself, and it refused: + * treat it as though it authenticated with PAP using a username + * of "" and a password of "". If that's not OK, boot it out. + */ + if (wo->neg_upap && !null_login(unit)) { + syslog(LOG_WARNING, "peer refused to authenticate"); + lcp_close(unit); + phase = PHASE_TERMINATE; + return; + } + } + + phase = PHASE_AUTHENTICATE; + 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) { + phase = PHASE_NETWORK; + 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); + phase = PHASE_TERMINATE; +} + +/* + * 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) { + phase = PHASE_NETWORK; + 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); + bit = 0; + } + + /* + * If there is no more authentication still being done, + * proceed to the network phase. + */ + if ((auth_pending[unit] &= ~bit) == 0) { + phase = PHASE_NETWORK; + 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; +} + + +/* + * null_login - Check if a username of "" and a password of "" are + * acceptable, and iff so, set the list of acceptable IP addresses + * and return 1. + */ +static int +null_login(unit) + int unit; +{ + char *filename; + FILE *f; + int i, ret; + struct wordlist *addrs; + char secret[MAXWORDLEN]; + + /* + * Open the file of upap secrets and scan for a suitable secret. + * We don't accept a wildcard client. + */ + filename = _PATH_UPAPFILE; + addrs = NULL; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + check_access(f, filename); + + i = scan_authfile(f, "", our_name, secret, &addrs, filename); + ret = i >= 0 && (i & NONWILD_CLIENT) != 0 && secret[0] == 0; + + if (ret) { + if (addresses[unit] != NULL) + free_wordlist(addresses[unit]); + addresses[unit] = addrs; + } + + fclose(f); + return ret; +} + + +/* + * 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. + */ +static 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; + int save_addrs; +{ + 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; + + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) + return 0; + + 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 */ +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(addr) + u_long addr; +{ + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + +/* + * 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 = addr_last = 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/usr.sbin/pppd/callout.h b/usr.sbin/pppd/callout.h new file mode 100644 index 000000000000..115d01c66653 --- /dev/null +++ b/usr.sbin/pppd/callout.h @@ -0,0 +1,18 @@ +/* Note: This is a copy of /usr/include/sys/callout.h with the c_func */ +/* member of struct callout changed from a pointer to a function of type int*/ +/* to a pointer to a function of type void (generic pointer) as per */ +/* ANSI C */ + +/* $Id: callout.h,v 1.1 1993/11/11 03:54:25 paulus Exp $ */ + +#ifndef _ppp_callout_h +#define _ppp_callout_h + +struct callout { + int c_time; /* incremental time */ + caddr_t c_arg; /* argument to routine */ + void (*c_func)(); /* routine (changed to void (*)() */ + struct callout *c_next; +}; + +#endif /*!_ppp_callout_h*/ diff --git a/usr.sbin/pppd/chap.c b/usr.sbin/pppd/chap.c new file mode 100644 index 000000000000..64f39bc51c1b --- /dev/null +++ b/usr.sbin/pppd/chap.c @@ -0,0 +1,823 @@ +/* + * 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.3 1994/04/18 04:01:07 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; + } + + UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate); + + /* + * 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; + } + + UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate); + + /* + * 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; +} + +/* + * ChapPrintPkt - print the contents of a CHAP packet. + */ +char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; + +int +ChapPrintPkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __ARGS((void *, char *, ...)); + void *arg; +{ + int code, id, len; + int clen, nlen; + u_char x; + + if (plen < CHAP_HEADERLEN) + return 0; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) + printer(arg, " %s", ChapCodenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) + break; + clen = p[0]; + if (len < clen + 1) + break; + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = "); + print_string((char *)p, nlen, printer, arg); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " "); + print_string((char *)p, len, printer, arg); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + + return len + CHAP_HEADERLEN; +} + +#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/usr.sbin/pppd/chap.h b/usr.sbin/pppd/chap.h new file mode 100644 index 000000000000..682ecb864d17 --- /dev/null +++ b/usr.sbin/pppd/chap.h @@ -0,0 +1,112 @@ +/* + * chap.h - Cryptographic Handshake Authentication Protocol definitions. + * + * 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.2 1994/04/11 07:13:44 paulus Exp $ + */ + +#ifndef __CHAP_INCLUDE__ + +/* Code + ID + length */ +#define CHAP_HEADERLEN 4 + +/* + * 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)); +int ChapPrintPkt __ARGS((u_char *, int, + void (*) __ARGS((void *, char *, ...)), void *)); + +#define __CHAP_INCLUDE__ +#endif /* __CHAP_INCLUDE__ */ diff --git a/usr.sbin/pppd/fsm.c b/usr.sbin/pppd/fsm.c new file mode 100644 index 000000000000..902ad64cbbc2 --- /dev/null +++ b/usr.sbin/pppd/fsm.c @@ -0,0 +1,773 @@ +/* + * 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.3 1994/05/24 11:21:10 paulus Exp $"; +#endif + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#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 */ + else + code = CONFACK; + + /* 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; + } + f->reqid = -1; + + 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; + } + f->reqid = -1; + + 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)); + + 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_sdata(f, TERMACK, id, NULL, 0); +} + + +/* + * 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 + */ + outp = outpacket_buf + DLLHEADERLEN + HEADERLEN; + 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; + 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/usr.sbin/pppd/fsm.h b/usr.sbin/pppd/fsm.h new file mode 100644 index 000000000000..a12966bef488 --- /dev/null +++ b/usr.sbin/pppd/fsm.h @@ -0,0 +1,127 @@ +/* + * 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.2 1994/04/11 07:18:35 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 */ + + +/* + * 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/usr.sbin/pppd/ipcp.c b/usr.sbin/pppd/ipcp.c new file mode 100644 index 000000000000..6c9abb15f2be --- /dev/null +++ b/usr.sbin/pppd/ipcp.c @@ -0,0 +1,1196 @@ +/* + * 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.8 1994/05/26 06:37:34 paulus Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "ppp.h" +#include "fsm.h" +#include "ipcp.h" +#include "pathnames.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 */ + +extern char ifname[]; +extern char devname[]; +extern int baud_rate; + +/* 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 */ +static void ipcp_script __ARGS((fsm *, char *)); /* Run an up/down script */ + +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; + if (wo->ouraddr == 0) + wo->accept_local = 1; + if (wo->hisaddr == 0) + wo->accept_remote = 1; + 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); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDR(CI_ADDR, neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "local IP address %s", + ip_ntoa(ciaddr1))); + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + try.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) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + 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 && go->accept_local) + try.ouraddr = ciaddr1; + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) + 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 && go->accept_local) + 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 (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + 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 (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof (long), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + 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 (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof (long), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } + + 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); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = wo->cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_STATES - 1; + ho->cflag = 1; + } + 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->neg_addr) + 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 it 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, ho->maxslotindex); + + /* 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; + + /* + * Execute the ip-up script, like this: + * /etc/ppp/ip-up interface tty speed local-IP remote-IP + */ + ipcp_script(f, _PATH_IPUP); + +} + + +/* + * 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); + + /* Execute the ip-down script */ + ipcp_script(f, _PATH_IPDOWN); +} + + +/* + * ipcp_script - Execute a script with arguments + * interface-name tty-name speed local-IP remote-IP. + */ +static void +ipcp_script(f, script) + fsm *f; + char *script; +{ + char strspeed[32], strlocal[32], strremote[32]; + char *argv[8]; + + sprintf(strspeed, "%d", baud_rate); + strcpy(strlocal, ip_ntoa(ipcp_gotoptions[f->unit].ouraddr)); + strcpy(strremote, ip_ntoa(ipcp_hisoptions[f->unit].hisaddr)); + + argv[0] = script; + argv[1] = ifname; + argv[2] = devname; + argv[3] = strspeed; + argv[4] = strlocal; + argv[5] = strremote; + argv[6] = NULL; + run_program(script, argv, 0); +} + +/* + * ipcp_printpkt - print the contents of an IPCP packet. + */ +char *ipcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej" +}; + +int +ipcp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer)(); + void *arg; +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u_long cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ipcp_codenames) / sizeof(char *)) + printer(arg, " %s", ipcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_ADDRS: + if (olen == CILEN_ADDRS) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addrs %s", ip_ntoa(htonl(cilong))); + GETLONG(cilong, p); + printer(arg, " %s", ip_ntoa(htonl(cilong))); + } + break; + case CI_COMPRESSTYPE: + if (olen >= CILEN_COMPRESS) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "compress "); + switch (cishort) { + case IPCP_VJ_COMP: + printer(arg, "VJ"); + break; + case IPCP_VJ_COMP_OLD: + printer(arg, "old-VJ"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_ADDR: + if (olen == CILEN_ADDR) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addr %s", ip_ntoa(htonl(cilong))); + } + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} diff --git a/usr.sbin/pppd/ipcp.h b/usr.sbin/pppd/ipcp.h new file mode 100644 index 000000000000..2d1988d5e0f4 --- /dev/null +++ b/usr.sbin/pppd/ipcp.h @@ -0,0 +1,68 @@ +/* + * 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.3 1994/04/18 04:05:15 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? */ + int accept_local : 1; /* accept peer's value for ouraddr */ + int accept_remote : 1; /* accept peer's value for hisaddr */ + 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)); +int ipcp_printpkt __ARGS((u_char *, int, void (*)(), void *)); diff --git a/usr.sbin/pppd/lcp.c b/usr.sbin/pppd/lcp.c new file mode 100644 index 000000000000..25815187e9a6 --- /dev/null +++ b/usr.sbin/pppd/lcp.c @@ -0,0 +1,1644 @@ +/* + * 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.8 1994/05/30 02:38:49 paulus Exp $"; +#endif + +/* + * TODO: + */ + +#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" + +#ifdef _linux_ /* Needs ppp ioctls */ +#include +#endif + +/* 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 */ +u_long xmit_accm[NPPP][8]; /* extended transmit ACCM */ + +static u_long lcp_echos_pending = 0; /* Number of outstanding echo msgs */ +static u_long lcp_echo_number = 0; /* ID number of next echo frame */ +static u_long lcp_echo_timer_running = 0; /* TRUE if a timer is running */ + +u_long lcp_echo_interval = 0; +u_long lcp_echo_fails = 0; + +/* + * 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)); + +/* + * routines to send LCP echos to peer + */ + +static void lcp_echo_lowerup __ARGS((int)); +static void lcp_echo_lowerdown __ARGS((int)); +static void LcpEchoTimeout __ARGS((caddr_t)); +static void lcp_received_echo_reply __ARGS((fsm *, int, u_char *, int)); +static void LcpSendEchoRequest __ARGS((fsm *)); +static void LcpLinkFailure __ARGS((fsm *)); + +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 = 0; + 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 */ + + memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); + xmit_accm[unit][3] = 0x60000000; +} + + +/* + * 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 *f = &lcp_fsm[unit]; + + if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do a + * lcp_close(0) in passive/silent mode when a connection hasn't + * been established. + */ + f->state = CLOSED; + lcp_finished(f); + + } else + fsm_close(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void +lcp_lowerup(unit) + int unit; +{ + sifdown(unit); + ppp_set_xaccm(unit, xmit_accm[unit]); + ppp_send_config(unit, MTU, 0xffffffff, 0, 0); + ppp_recv_config(unit, MTU, 0x00000000, 0, 0); + peer_mru[unit] = MTU; + lcp_allowoptions[unit].asyncmap = xmit_accm[unit][0]; + + 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; +{ + u_char *magp; + + 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)); + magp = inp; + PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); + if (len < CILEN_LONG) + len = CILEN_LONG; + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + 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 ? + */ + NAKCILQR(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 || cilong != 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]; + + if (!go->neg_magicnumber) + go->magicnumber = 0; + if (!ho->neg_magicnumber) + ho->magicnumber = 0; + + /* + * 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, MIN(ao->mru, (ho->neg_mru? ho->mru: MTU)), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffff), + ho->neg_pcompression, ho->neg_accompression); + /* + * If the asyncmap hasn't been negotiated, we really should + * set the receive asyncmap to ffffffff, but we set it to 0 + * for backwards contemptibility. + */ + ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): MTU), + (go->neg_asyncmap? go->asyncmap: 0x00000000), + 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 */ + lcp_echo_lowerup(f->unit); /* Enable echo messages */ + + link_established(f->unit); +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void +lcp_down(f) + fsm *f; +{ + lcp_echo_lowerdown(f->unit); + 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, 0x00000000, 0, 0); + peer_mru[f->unit] = MTU; + + link_down(f->unit); +} + + +/* + * 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); +} + + +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +char *lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq" +}; + +int +lcp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __ARGS((void *, char *, ...)); + void *arg; +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u_long cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) + printer(arg, " %s", lcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%x", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { + case UPAP: + printer(arg, "upap"); + break; + case CHAP: + printer(arg, "chap"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} + +/* + * Time to shut down the link because there is nothing out there. + */ + +static +void LcpLinkFailure (f) + fsm *f; +{ + if (f->state == OPENED) { + syslog (LOG_NOTICE, "Excessive lack of response to LCP echo frames."); + lcp_lowerdown(f->unit); /* Reset connection */ + } +} + +/* + * Timer expired for the LCP echo requests from this process. + */ + +static void +LcpEchoCheck (f) + fsm *f; +{ + u_long delta; +#ifdef __linux__ + struct ppp_ddinfo ddinfo; + u_long latest; +/* + * Read the time since the last packet was received. + */ + if (ioctl (fd, PPPIOCGTIME, &ddinfo) < 0) { + syslog (LOG_ERR, "ioctl(PPPIOCGTIME): %m"); + die (1); + } +/* + * Choose the most recient frame received. It may be an IP or NON-IP frame. + */ + latest = ddinfo.nip_rjiffies < ddinfo.ip_rjiffies ? ddinfo.nip_rjiffies + : ddinfo.ip_rjiffies; +/* + * Compute the time since the last packet was received. If the timer + * has expired then send the echo request and reset the timer to maximum. + */ + delta = (lcp_echo_interval * HZ) - latest; + if (delta < HZ || latest < 0L) { + LcpSendEchoRequest (f); + delta = lcp_echo_interval * HZ; + } + delta /= HZ; + +#else /* Other implementations do not have ability to find delta */ + LcpSendEchoRequest (f); + delta = lcp_echo_interval; +#endif + +/* + * Start the timer for the next interval. + */ + assert (lcp_echo_timer_running==0); + TIMEOUT (LcpEchoTimeout, (caddr_t) f, delta); + lcp_echo_timer_running = 1; +} + +/* + * LcpEchoTimeout - Timer expired on the LCP echo + */ + +static void +LcpEchoTimeout (arg) + caddr_t arg; +{ + if (lcp_echo_timer_running != 0) { + lcp_echo_timer_running = 0; + LcpEchoCheck ((fsm *) arg); + } +} + +/* + * LcpEchoReply - LCP has received a reply to the echo + */ + +static void +lcp_received_echo_reply (f, id, inp, len) + fsm *f; + int id; u_char *inp; int len; +{ + u_long magic; + + /* Check the magic number - don't count replies from ourselves. */ + if (len < CILEN_LONG) + return; + GETLONG(magic, inp); + if (lcp_gotoptions[f->unit].neg_magicnumber + && magic == lcp_gotoptions[f->unit].magicnumber) + return; + + /* Reset the number of outstanding echo frames */ + lcp_echos_pending = 0; +} + +/* + * LcpSendEchoRequest - Send an echo request frame to the peer + */ + +static void +LcpSendEchoRequest (f) + fsm *f; +{ + u_long lcp_magic; + u_char pkt[4], *pktp; + +/* + * Detect the failure of the peer at this point. + */ + if (lcp_echo_fails != 0) { + if (lcp_echos_pending++ >= lcp_echo_fails) { + LcpLinkFailure(f); + lcp_echos_pending = 0; + } + } +/* + * Make and send the echo request frame. + */ + if (f->state == OPENED) { + lcp_magic = lcp_gotoptions[f->unit].neg_magicnumber + ? lcp_gotoptions[f->unit].magicnumber + : 0L; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + + fsm_sdata(f, ECHOREQ, + lcp_echo_number++ & 0xFF, pkt, pktp - pkt); + } +} + +/* + * lcp_echo_lowerup - Start the timer for the LCP frame + */ + +static void +lcp_echo_lowerup (unit) + int unit; +{ + fsm *f = &lcp_fsm[unit]; + + /* Clear the parameters for generating echo frames */ + lcp_echos_pending = 0; + lcp_echo_number = 0; + lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (lcp_echo_interval != 0) + LcpEchoCheck (f); +} + +/* + * lcp_echo_lowerdown - Stop the timer for the LCP frame + */ + +static void +lcp_echo_lowerdown (unit) + int unit; +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_echo_timer_running != 0) { + UNTIMEOUT (LcpEchoTimeout, (caddr_t) f); + lcp_echo_timer_running = 0; + } +} diff --git a/usr.sbin/pppd/lcp.h b/usr.sbin/pppd/lcp.h new file mode 100644 index 000000000000..0499a7cbd0d2 --- /dev/null +++ b/usr.sbin/pppd/lcp.h @@ -0,0 +1,87 @@ +/* + * 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.4 1994/05/24 11:22:28 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_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ + +/* + * LCP-specific packet types. + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ + +/* + * 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 */ + u_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[]; +extern u_long xmit_accm[][8]; + +#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)); +int lcp_printpkt __ARGS((u_char *, int, + void (*) __ARGS((void *, char *, ...)), void *)); + +extern int lcp_warnloops; /* Warn about a loopback this often */ +#define DEFWARNLOOPS 10 /* Default value for above */ diff --git a/usr.sbin/pppd/lock.c b/usr.sbin/pppd/lock.c new file mode 100644 index 000000000000..0fbbef33b0e7 --- /dev/null +++ b/usr.sbin/pppd/lock.c @@ -0,0 +1,122 @@ +/* + * lock.c - lock/unlock the serial device. + * + * This code is derived from chat.c. + */ + +static char rcsid[] = "$Id: lock.c,v 1.1 1994/04/18 23:41:52 paulus Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef sun +# if defined(SUNOS) && SUNOS >= 41 +# ifndef HDB +# define HDB +# endif +# endif +#endif + +#ifndef LOCK_DIR +# ifdef __NetBSD__ +# define PIDSTRING +# define LOCK_PREFIX "/var/spool/lock/LCK.." +# else +# ifdef HDB +# define PIDSTRING +# define LOCK_PREFIX "/usr/spool/locks/LCK.." +# else /* HDB */ +# define LOCK_PREFIX "/usr/spool/uucp/LCK.." +# endif /* HDB */ +# endif +#endif /* LOCK_DIR */ + +static char *lock_file; + +/* + * Create a lock file for the named lock device + */ +int +lock(dev) + char *dev; +{ + char hdb_lock_buffer[12]; + int fd, pid, n; + char *p; + + if ((p = strrchr(dev, '/')) != NULL) + dev = p + 1; + lock_file = malloc(strlen(LOCK_PREFIX) + strlen(dev) + 1); + if (lock_file == NULL) + novm("lock file name"); + strcat(strcpy(lock_file, LOCK_PREFIX), dev); + + while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) { + if (errno == EEXIST + && (fd = open(lock_file, O_RDONLY, 0)) >= 0) { + /* Read the lock file to find out who has the device locked */ +#ifdef PIDSTRING + n = read(fd, hdb_lock_buffer, 11); + if (n > 0) { + hdb_lock_buffer[n] = 0; + pid = atoi(hdb_lock_buffer); + } +#else + n = read(fd, &pid, sizeof(pid)); +#endif + if (n <= 0) { + syslog(LOG_ERR, "Can't read pid from lock file %s", lock_file); + close(fd); + } else { + if (kill(pid, 0) == -1 && errno == ESRCH) { + /* pid no longer exists - remove the lock file */ + if (unlink(lock_file) == 0) { + close(fd); + syslog(LOG_NOTICE, "Removed stale lock on %s (pid %d)", + dev, pid); + continue; + } else + syslog(LOG_WARNING, "Couldn't remove stale lock on %s", + dev); + } else + syslog(LOG_NOTICE, "Device %s is locked by pid %d", + dev, pid); + } + close(fd); + } else + syslog(LOG_ERR, "Can't create lock file %s: %m", lock_file); + free(lock_file); + lock_file = NULL; + return -1; + } + +# ifdef PIDSTRING + sprintf(hdb_lock_buffer, "%10d\n", getpid()); + write(fd, hdb_lock_buffer, 11); +# else + pid = getpid(); + write(fd, &pid, sizeof pid); +# endif + + close(fd); + return 0; +} + +/* + * Remove our lockfile + */ +unlock() +{ + if (lock_file) { + unlink(lock_file); + free(lock_file); + lock_file = NULL; + } +} + diff --git a/usr.sbin/pppd/magic.c b/usr.sbin/pppd/magic.c new file mode 100644 index 000000000000..20dfd6fe3a48 --- /dev/null +++ b/usr.sbin/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/usr.sbin/pppd/magic.h b/usr.sbin/pppd/magic.h new file mode 100644 index 000000000000..d41aa39df7db --- /dev/null +++ b/usr.sbin/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/usr.sbin/pppd/main.c b/usr.sbin/pppd/main.c new file mode 100644 index 000000000000..1e1397e55053 --- /dev/null +++ b/usr.sbin/pppd/main.c @@ -0,0 +1,1445 @@ +/* + * 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.13 1994/05/27 01:02:14 paulus Exp $"; +#endif + +#define SETSID + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * If REQ_SYSOPTIONS is defined to 1, pppd will not run unless + * /etc/ppp/options exists. + */ +#ifndef REQ_SYSOPTIONS +#define REQ_SYSOPTIONS 1 +#endif + +#ifdef SGTTY +#include +#else +#ifndef sun +#include +#endif +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "callout.h" +#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 char pidfilename[MAXPATHLEN]; + +static pid_t pid; /* Our pid */ +static pid_t pgrpid; /* Process Group ID */ +uid_t uid; /* Our real user-id */ + +char devname[MAXPATHLEN] = "/dev/tty"; /* Device name */ +int default_device = TRUE; /* use default device (stdin/out) */ + +int fd = -1; /* Device file descriptor */ +int s; /* Socket file descriptor */ + +int phase; /* where the link is at */ + +#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 */ +static int n_children; /* # child processes still running */ + +/* configured variables */ + +int debug = 0; /* Debug flag */ +int kdebugflag = 0; /* Kernel debugging flag */ +char user[MAXNAMELEN]; /* username for PAP */ +char passwd[MAXSECRETLEN]; /* password for PAP */ +char *connector = NULL; /* "connect" command */ +char *disconnector = NULL; /* "disconnect" command */ +int inspeed = 0; /* Input/Output speed requested */ +int baud_rate; /* bits/sec currently used */ +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 */ +int lockflag = 0; /* lock the serial device */ + + +/* prototypes */ +static void hup __ARGS((int)); +static void intr __ARGS((int)); +static void term __ARGS((int)); +static void alrm __ARGS((int)); +static void io __ARGS((int)); +static void chld __ARGS((int)); +static void incdebug __ARGS((int)); +static void nodebug __ARGS((int)); +void establish_ppp __ARGS((void)); + +void reap_kids __ARGS((void)); +void cleanup __ARGS((int, caddr_t)); +void die __ARGS((int)); +void novm __ARGS((char *)); + +void log_packet __ARGS((u_char *, int, char *)); +void format_packet __ARGS((u_char *, int, + void (*) (void *, char *, ...), void *)); +void pr_log __ARGS((void *, char *, ...)); + +extern char *ttyname __ARGS((int)); +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)(); + int (*printpkt)(); + char *name; +} prottbl[] = { + { LCP, lcp_init, lcp_input, lcp_protrej, lcp_printpkt, "LCP" }, + { IPCP, ipcp_init, ipcp_input, ipcp_protrej, ipcp_printpkt, "IPCP" }, + { UPAP, upap_init, upap_input, upap_protrej, upap_printpkt, "PAP" }, + { CHAP, ChapInit, ChapInput, ChapProtocolReject, ChapPrintPkt, "CHAP" }, +}; + +#define N_PROTO (sizeof(prottbl) / sizeof(prottbl[0])) + +main(argc, argv) + int argc; + char *argv[]; +{ + int mask, i; + struct sigaction sa; + struct cmd *cmdp; + FILE *pidfile; + char *p; + struct passwd *pw; + + p = ttyname(0); + if (p) + strcpy(devname, p); + + if (gethostname(hostname, MAXNAMELEN) < 0 ) { + perror("couldn't get hostname"); + die(1); + } + hostname[MAXNAMELEN-1] = 0; + + pid = getpid(); + uid = getuid(); + + if (!ppp_available()) { + fprintf(stderr, "Sorry - PPP is not available on this system\n"); + exit(1); + } + + /* + * 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 < N_PROTO; i++) + (*prottbl[i].init)(0); + + progname = *argv; + + if (!options_from_file(_PATH_SYSOPTIONS, REQ_SYSOPTIONS, 0) || + !options_from_user() || + !parse_args(argc-1, argv+1) || + !options_for_tty()) + die(1); + check_auth_options(); + setipdefault(); + + /* + * Initialize syslog system and magic number package. + */ +#if !defined(ultrix) + 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 + if (debug) + setlogmask(LOG_UPTO(LOG_DEBUG)); + + magic_init(); + + p = getlogin(); + if (p == NULL) { + pw = getpwuid(uid); + if (pw != NULL && pw->pw_name != NULL) + p = pw->pw_name; + else + p = "(unknown)"; + } + syslog(LOG_NOTICE, "pppd %s.%d started by %s, uid %d", + VERSION, PATCHLEVEL, p, uid); + +#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); + } + + } 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 + + if (lockflag && !default_device) + if (lock(devname) < 0) + die(1); + + /* 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); + sigaddset(&mask, SIGCHLD); +#ifdef STREAMS + sigaddset(&mask, SIGPOLL); +#endif + +#define SIGNAL(s, handler) { \ + sa.sa_handler = handler; \ + if (sigaction(s, &sa, NULL) < 0) { \ + syslog(LOG_ERR, "sigaction(%d): %m", s); \ + die(1); \ + } \ + } + + sa.sa_mask = mask; + sa.sa_flags = 0; + SIGNAL(SIGHUP, hup); /* Hangup */ + SIGNAL(SIGINT, intr); /* Interrupt */ + SIGNAL(SIGTERM, term); /* Terminate */ + SIGNAL(SIGALRM, alrm); /* Timeout */ + SIGNAL(SIGIO, io); /* Input available */ + SIGNAL(SIGCHLD, chld); /* Death of child process */ +#ifdef STREAMS + SIGNAL(SIGPOLL, io); /* Input available */ +#endif + + signal(SIGUSR1, incdebug); /* Increment debug flag */ + signal(SIGUSR2, nodebug); /* Reset debug flag */ + + /* + * Block SIGIOs and SIGPOLLs for now + */ + sigemptyset(&mask); + sigaddset(&mask, SIGIO); +#ifdef STREAMS + sigaddset(&mask, SIGPOLL); +#endif + sigprocmask(SIG_BLOCK, &mask, NULL); + + /* + * 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; + +#ifdef TIOCSCTTY + /* set device to be controlling tty */ + if (!default_device && ioctl(fd, TIOCSCTTY) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSCTTY): %m"); + die(1); + } +#endif /* TIOCSCTTY */ + + /* run connection script */ + if (connector) { + MAINDEBUG((LOG_INFO, "Connecting with <%s>", connector)); + + /* set line speed, flow control, etc.; set CLOCAL for now */ + set_up_tty(fd, 1); + + /* drop dtr to hang up in case modem is off hook */ + if (!default_device && modem) { + setdtr(fd, FALSE); + sleep(1); + setdtr(fd, TRUE); + } + + if (device_script(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 line speed, flow control, etc.; clear CLOCAL if modem option */ + set_up_tty(fd, 0); + + /* 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 and SIGHUPs. + */ +#ifdef SETSID + if (default_device) { + int id = tcgetpgrp(fd); + if (id != pgrpid) { + syslog(LOG_WARNING, "warning: not in tty's process group"); + } + } 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); + } + +#ifdef _linux_ /* This is a kludge for Linux. FIXME !!! -- later. */ +#undef FASYNC +#define FASYNC 0 +#endif + + 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 (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) + sigpause(0); /* Wait for next signal */ + + /* + * Run disconnector script, if requested + */ + if (disconnector) { + if (device_script(disconnector, fd, fd) < 0) { + syslog(LOG_WARNING, "disconnect script failed"); + die(1); + } + + syslog(LOG_INFO, "Disconnected..."); + } + + quit(); +} + +#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) +#define baud_rate_of(speed) (speed) + +#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; +} + +/* + * Translate from a speed_t to bits/second. + */ +int +baud_rate_of(speed) + int speed; +{ + struct speed *speedp; + + if (speed == 0) + return 0; + for (speedp = speeds; speedp->speed_int; speedp++) + if (speed == speedp->speed_val) + return speedp->speed_int; + return 0; +} +#endif + +/* + * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity, + * at the requested speed, etc. If `local' is true, set CLOCAL + * regardless of whether the modem option was specified. + */ +set_up_tty(fd, local) + int fd, local; +{ +#ifndef SGTTY + int speed, x; + struct termios tios; + + if (tcgetattr(fd, &tios) < 0) { + syslog(LOG_ERR, "tcgetattr: %m"); + die(1); + } + + if (!restore_term) + inittermios = tios; + +#ifdef CRTSCTS + tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL | CRTSCTS); + if (crtscts == 1) + tios.c_cflag |= CRTSCTS; +#else + tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL); +#endif /* CRTSCTS */ + + tios.c_cflag |= CS8 | CREAD | HUPCL; + if (local || !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; + + if (crtscts == 2) { + tios.c_iflag |= IXOFF; + tios.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */ + tios.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */ + } + + speed = translate_speed(inspeed); + if (speed) { + cfsetospeed(&tios, speed); + cfsetispeed(&tios, speed); + } else { + speed = cfgetospeed(&tios); + } + + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { + syslog(LOG_ERR, "tcsetattr: %m"); + die(1); + } + +#ifdef ultrix + x = 0; + if (ioctl(fd, (crtscts || modem)? TIOCMODEM: TIOCNMODEM, &x) < 0) + syslog(LOG_WARNING, "TIOC(N)MODEM: %m"); + if (ioctl(fd, (local || !modem)? TIOCNCAR: TIOCCAR) < 0) + syslog(LOG_WARNING, "TIOC(N)CAR: %m"); +#endif + +#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; + else + speed = sgttyb.sg_ispeed; + + if (ioctl(fd, TIOCSETP, &sgttyb) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSETP): %m"); + die(1); + } +#endif + + baud_rate = baud_rate_of(speed); + restore_term = TRUE; +} + +/* + * 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); +} + + +/* + * 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 (initfdflags != -1 && fcntl(fd, F_SETFL, initfdflags) < 0) + syslog(LOG_WARNING, "fcntl(F_SETFL, fdflags): %m"); + initfdflags = -1; + + disestablish_ppp(); + + if (restore_term) { +#ifndef SGTTY + if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0) + syslog(LOG_WARNING, "tcsetattr: %m"); +#else + if (ioctl(fd, TIOCSETP, &initsgttyb) < 0) + syslog(LOG_WARNING, "ioctl(TIOCSETP): %m"); +#endif + } + + close(fd); + fd = -1; + } + + if (pidfilename[0] != 0 && unlink(pidfilename) < 0) + syslog(LOG_WARNING, "unable to unlink pid file: %m"); + pidfilename[0] = 0; + + if (lockflag && !default_device) + unlock(); +} + + +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 in timeout.", + 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 in untimeout.", + 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. + */ +static void +hup(sig) + int sig; +{ + 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. + */ +static void +term(sig) + int sig; +{ + 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. + */ +static void +intr(sig) + int sig; +{ + 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. + */ +static void +alrm(sig) + int sig; +{ + struct itimerval itv; + struct callout *freep, *list, *last; + + MAINDEBUG((LOG_DEBUG, "Alarm")); + + if (callout == NULL) + return; + /* + * Get the first scheduled timeout and any that were scheduled + * for the same time as a list, and remove them all from callout + * list. + */ + list = last = callout; + while (last->c_next != NULL && last->c_next->c_time == 0) + last = last->c_next; + callout = last->c_next; + last->c_next = NULL; + + /* + * 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 in alrm.", + 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); + } + } + + /* + * Now call all the timeout routines scheduled for this time. + */ + while (list) { + (*list->c_func)(list->c_arg); + freep = list; + list = list->c_next; + (void) free((char *) freep); + } + +} + + +/* + * chld - Catch SIGCHLD signal. + * Calls reap_kids to get status for any dead kids. + */ +static void +chld(sig) + int sig; +{ + reap_kids(); +} + + +/* + * io - Catch SIGIO signal. + * + * Indicates that incoming data is available. + */ +static void +io(sig) + int sig; +{ + 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 */ + + /* 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) { + MAINDEBUG((LOG_DEBUG, "End of file on fd!")); + lcp_lowerdown(0); + return; + } + + if (debug /*&& (debugflags & DBG_INPACKET)*/) + log_packet(p, len, "rcvd "); + + 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.")); + 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; +} + + +/* + * device_script - run a program to connect or disconnect the + * serial device. + */ +int +device_script(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) < 0) { + if (errno == EINTR) + continue; + syslog(LOG_ERR, "waiting for (dis)connection process: %m"); + die(1); + } + sigprocmask(SIG_SETMASK, &mask, NULL); + + return (status == 0 ? 0 : -1); +} + + +/* + * run-program - execute a program with given arguments, + * but don't wait for it. + * If the program can't be executed, logs an error unless + * must_exist is 0 and the program file doesn't exist. + */ +int +run_program(prog, args, must_exist) + char *prog; + char **args; + int must_exist; +{ + int pid; + + pid = fork(); + if (pid == -1) { + syslog(LOG_ERR, "can't fork to run %s: %m", prog); + return -1; + } + if (pid == 0) { + execv(prog, args); + if (must_exist || errno != ENOENT) + syslog(LOG_WARNING, "can't execute %s: %m", prog); + _exit(-1); + } + MAINDEBUG((LOG_DEBUG, "Script %s started; pid = %d", prog, pid)); + ++n_children; + return 0; +} + + +/* + * reap_kids - get status from any dead child processes, + * and log a message for abnormal terminations. + */ +void +reap_kids() +{ + int pid, status; + + if (n_children == 0) + return; + if ((pid = waitpid(-1, &status, WNOHANG)) == -1) { + if (errno != ECHILD) + syslog(LOG_ERR, "waitpid: %m"); + return; + } + if (pid > 0) { + --n_children; + if (WIFSIGNALED(status)) { + syslog(LOG_WARNING, "child process %d terminated with signal %d", + pid, WTERMSIG(status)); + } + } +} + + +/* + * log_packet - format a packet and log it. + */ + +char line[256]; /* line to be logged accumulated here */ +char *linep; + +void +log_packet(p, len, prefix) + u_char *p; + int len; + char *prefix; +{ + strcpy(line, prefix); + linep = line + strlen(line); + format_packet(p, len, pr_log, NULL); + if (linep != line) + syslog(LOG_DEBUG, "%s", line); +} + +/* + * format_packet - make a readable representation of a packet, + * calling `printer(arg, format, ...)' to output it. + */ +void +format_packet(p, len, printer, arg) + u_char *p; + int len; + void (*printer) __ARGS((void *, char *, ...)); + void *arg; +{ + int i, n; + u_short proto; + u_char x; + + if (len >= DLLHEADERLEN && p[0] == ALLSTATIONS && p[1] == UI) { + p += 2; + GETSHORT(proto, p); + len -= DLLHEADERLEN; + for (i = 0; i < N_PROTO; ++i) + if (proto == prottbl[i].protocol) + break; + if (i < N_PROTO) { + printer(arg, "[%s", prottbl[i].name); + n = (*prottbl[i].printpkt)(p, len, printer, arg); + printer(arg, "]"); + p += n; + len -= n; + } else { + printer(arg, "[proto=0x%x]", proto); + } + } + + for (; len > 0; --len) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } +} + +#ifdef __STDC__ +#include + +void +pr_log(void *arg, char *fmt, ...) +{ + int n; + va_list pvar; + char buf[256]; + + va_start(pvar, fmt); + vsprintf(buf, fmt, pvar); + va_end(pvar); + + n = strlen(buf); + if (linep + n + 1 > line + sizeof(line)) { + syslog(LOG_DEBUG, "%s", line); + linep = line; + } + strcpy(linep, buf); + linep += n; +} + +#else /* __STDC__ */ +#include + +void +pr_log(arg, fmt, va_alist) +void *arg; +char *fmt; +va_dcl +{ + int n; + va_list pvar; + char buf[256]; + + va_start(pvar); + vsprintf(buf, fmt, pvar); + va_end(pvar); + + n = strlen(buf); + if (linep + n + 1 > line + sizeof(line)) { + syslog(LOG_DEBUG, "%s", line); + linep = line; + } + strcpy(linep, buf); + linep += n; +} +#endif + +/* + * print_string - print a readable representation of a string using + * printer. + */ +void +print_string(p, len, printer, arg) + char *p; + int len; + void (*printer) __ARGS((void *, char *, ...)); + void *arg; +{ + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') + printer(arg, "%c", c); + else + printer(arg, "\\%.3o", c); + } + printer(arg, "\""); +} + +/* + * novm - log an error message saying we ran out of memory, and die. + */ +void +novm(msg) + char *msg; +{ + syslog(LOG_ERR, "Virtual memory exhausted allocating %s\n", msg); + die(1); +} diff --git a/usr.sbin/pppd/md5.c b/usr.sbin/pppd/md5.c new file mode 100644 index 000000000000..480c860c0aa9 --- /dev/null +++ b/usr.sbin/pppd/md5.c @@ -0,0 +1,304 @@ + + +/* + *********************************************************************** + ** 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); \ + } + +#ifdef __STDC__ +#define UL(x) x##U +#else +#define UL(x) x +#endif + +/* 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, UL(3614090360)); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ + FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ + FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ + FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ + FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ + FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ + FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ + GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ + GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ + GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ + GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ + GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ + GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ + HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ + HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ + HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ + HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ + HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ + HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ + II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ + II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ + II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ + II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ + II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ + II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ + II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ + II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ + II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ + II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ + II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ + II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ + II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ + II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ + II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + *********************************************************************** + ** End of md5.c ** + ******************************** (cut) ******************************** + */ diff --git a/usr.sbin/pppd/md5.h b/usr.sbin/pppd/md5.h new file mode 100644 index 000000000000..535b18d4b730 --- /dev/null +++ b/usr.sbin/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/usr.sbin/pppd/options.c b/usr.sbin/pppd/options.c new file mode 100644 index 000000000000..ef1fc8a3c008 --- /dev/null +++ b/usr.sbin/pppd/options.c @@ -0,0 +1,1452 @@ +/* + * 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.10 1994/05/27 00:43:34 paulus Exp $"; +#endif + +#include +#include +#include +#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 + +#ifdef ultrix +char *strdup __ARGS((char *)); +#endif + +#ifndef GIDSET_TYPE +#define GIDSET_TYPE int +#endif + +/* + * Prototypes + */ +static int setdebug __ARGS((void)); +static int setkdebug __ARGS((char **)); +static int setpassive __ARGS((void)); +static int setsilent __ARGS((void)); +static int noopt __ARGS((void)); +static int setnovj __ARGS((void)); +static int setnovjccomp __ARGS((void)); +static int setvjslots __ARGS((char **)); +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 setescape __ARGS((char **)); +static int setmru __ARGS((char **)); +static int setmtu __ARGS((char **)); +static int nomru __ARGS((void)); +static int nopcomp __ARGS((void)); +static int setconnector __ARGS((char **)); +static int setdisconnector __ARGS((char **)); +static int setdomain __ARGS((char **)); +static int setnetmask __ARGS((char **)); +static int setcrtscts __ARGS((void)); +static int setxonxoff __ARGS((void)); +static int setnodetach __ARGS((void)); +static int setmodem __ARGS((void)); +static int setlocal __ARGS((void)); +static int setlock __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 setnoipdflt __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 setipcpaccl __ARGS((void)); +static int setipcpaccr __ARGS((void)); +static int setlcpechointv __ARGS((char **)); +static int setlcpechofails __ARGS((char **)); + +static int number_option __ARGS((char *, long *, int)); +static int readable __ARGS((int fd)); + +/* + * Option variables + */ +extern char *progname; +extern int debug; +extern int kdebugflag; +extern int modem; +extern int lockflag; +extern int crtscts; +extern int nodetach; +extern char *connector; +extern char *disconnector; +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 u_long lcp_echo_interval; +extern u_long lcp_echo_fails; +extern char our_name[]; +extern char remote_name[]; +int usehostname; +int disable_defaultip; + +/* + * 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 */ + {"-vjccomp", 0, setnovjccomp}, /* disable VJ connection-ID compression */ + {"vj-max-slots", 1, setvjslots}, /* Set maximum VJ header slots */ + {"asyncmap", 1, setasyncmap}, /* set the desired async map */ + {"escape", 1, setescape}, /* set chars to escape on transmission */ + {"connect", 1, setconnector}, /* A program to set up a connection */ + {"disconnect", 1, setdisconnector}, /* program to disconnect serial dev. */ + {"crtscts", 0, setcrtscts}, /* set h/w flow control */ + {"xonxoff", 0, setxonxoff}, /* set s/w flow control */ + {"-crtscts", 0, setxonxoff}, /* another name for xonxoff */ + {"debug", 0, setdebug}, /* Increase debugging level */ + {"kdebug", 1, setkdebug}, /* Enable kernel-level debugging */ + {"domain", 1, setdomain}, /* Add given domain name to hostname*/ + {"mru", 1, setmru}, /* Set MRU value for negotiation */ + {"mtu", 1, setmtu}, /* Set our MTU */ + {"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 */ + {"lock", 0, setlock}, /* Lock serial device (with lock file) */ + {"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 */ + {"noipdefault", 0, setnoipdflt}, /* Don't use name for default IP adrs */ + {"lcp-echo-failure", 1, setlcpechofails}, /* consecutive echo failures */ + {"lcp-echo-interval", 1, setlcpechointv}, /* time for lcp echo events */ + {"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 */ + {"ipcp-accept-local", 0, setipcpaccl}, /* Accept peer's address for us */ + {"ipcp-accept-remote", 0, setipcpaccr}, /* Accept peer's address for it */ + {NULL, 0, NULL} +}; + + +#ifndef IMPLEMENTATION +#define IMPLEMENTATION "" +#endif + +static char *usage_string = "\ +pppd version %s patch level %d%s\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\ +"; + + +/* + * 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; + int ret; + + 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 ((ret = setdevname(arg)) == 0 + && (ret = setspeed(arg)) == 0 + && (ret = setipaddr(arg)) == 0) { + fprintf(stderr, "%s: unrecognized command\n", arg); + usage(); + return 0; + } + if (ret < 0) /* error */ + return 0; + } + } + return 1; +} + +/* + * usage - print out a message telling how to use the program. + */ +usage() +{ + fprintf(stderr, usage_string, VERSION, PATCHLEVEL, IMPLEMENTATION, + progname); +} + +/* + * options_from_file - Read a string of options from a file, + * and interpret them. + */ +int +options_from_file(filename, must_exist, check_prot) + char *filename; + int must_exist; + int check_prot; +{ + FILE *f; + int i, newline, ret; + struct cmd *cmdp; + char *argv[MAXARGS]; + char args[MAXARGS][MAXWORDLEN]; + char cmd[MAXWORDLEN]; + + if ((f = fopen(filename, "r")) == NULL) { + if (!must_exist && errno == ENOENT) + return 1; + perror(filename); + return 0; + } + if (check_prot && !readable(fileno(f))) { + fprintf(stderr, "%s: access denied\n", filename); + fclose(f); + return 0; + } + + 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 ((ret = setdevname(cmd)) == 0 + && (ret = setspeed(cmd)) == 0 + && (ret = setipaddr(cmd)) == 0) { + fprintf(stderr, "In file %s: unrecognized command %s\n", + filename, cmd); + fclose(f); + return 0; + } + if (ret < 0) /* error */ + 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; + struct passwd *pw; + + pw = getpwuid(getuid()); + if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) + return 1; + file = _PATH_USEROPT; + path = malloc(strlen(user) + strlen(file) + 2); + if (path == NULL) + novm("init file name"); + strcpy(path, user); + strcat(path, "/"); + strcat(path, file); + ret = options_from_file(path, 0, 1); + free(path); + return ret; +} + +/* + * options_for_tty - See if an options file exists for the serial + * device, and if so, interpret options from it. + */ +int +options_for_tty() +{ + char *dev, *path; + int ret; + + dev = strrchr(devname, '/'); + if (dev == NULL) + dev = devname; + else + ++dev; + if (strcmp(dev, "tty") == 0) + return 1; /* don't look for /etc/ppp/options.tty */ + path = malloc(strlen(_PATH_TTYOPT) + strlen(dev) + 1); + if (path == NULL) + novm("tty init file name"); + strcpy(path, _PATH_TTYOPT); + strcat(path, dev); + ret = options_from_file(path, 0, 0); + free(path); + return ret; +} + +/* + * readable - check if a file is readable by the real user. + */ +static int +readable(fd) + int fd; +{ + uid_t uid; + int ngroups, i; + struct stat sbuf; + GIDSET_TYPE groups[NGROUPS_MAX]; + + uid = getuid(); + if (uid == 0) + return 1; + if (fstat(fd, &sbuf) != 0) + return 0; + if (sbuf.st_uid == uid) + return sbuf.st_mode & S_IRUSR; + if (sbuf.st_gid == getgid()) + return sbuf.st_mode & S_IRGRP; + ngroups = getgroups(NGROUPS_MAX, groups); + for (i = 0; i < ngroups; ++i) + if (sbuf.st_gid == groups[i]) + return sbuf.st_mode & S_IRGRP; + return sbuf.st_mode & S_IROTH; +} + +/* + * 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 == '\\') + escape = 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, 1, 1); +} + +/* + * setdebug - Set debug (command line argument). + */ +static int +setdebug() +{ + debug++; + return (1); +} + +/* + * setkdebug - Set kernel debugging level. + */ +static int +setkdebug(argv) + char **argv; +{ + return int_option(*argv, &kdebugflag); +} + +/* + * 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); +} + + +/* + * setmru - Set the largest MTU we'll use. + */ +static int +setmtu(argv) + char **argv; +{ + long mtu; + + if (!number_option(*argv, &mtu, 0)) + return 0; + if (mtu < MINMRU || mtu > MAXMRU) { + fprintf(stderr, "mtu option value of %d is too %s\n", mtu, + (mtu < MINMRU? "small": "large")); + return 0; + } + lcp_allowoptions[0].mru = mtu; + 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); + return 0; + } + if (!readable(fileno(ufile))) { + fprintf(stderr, "%s: access denied\n", *argv); + return 0; + } + 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); + return 0; + } + 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 - disable vj compression + */ +static int +setnovj() +{ + ipcp_wantoptions[0].neg_vj = 0; + ipcp_allowoptions[0].neg_vj = 0; + return (1); +} + + +/* + * setnovjccomp - disable VJ connection-ID compression + */ +static int +setnovjccomp() +{ + ipcp_wantoptions[0].cflag = 0; + ipcp_allowoptions[0].cflag = 0; +} + + +/* + * setvjslots - set maximum number of connection slots for VJ compression + */ +static int +setvjslots(argv) + char **argv; +{ + int value; + + if (!int_option(*argv, &value)) + return 0; + if (value < 2 || value > 16) { + fprintf(stderr, "pppd: vj-max-slots value must be between 2 and 16\n"); + return 0; + } + ipcp_wantoptions [0].maxslotindex = + ipcp_allowoptions[0].maxslotindex = value - 1; + 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); +} + +/* + * setdisconnector - Set a program to disconnect from the serial line + */ +static int +setdisconnector(argv) + char **argv; +{ + disconnector = strdup(*argv); + if (disconnector == NULL) + novm("disconnector 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); +} + + +/* + * setasyncmap - add bits to asyncmap (what we request peer to escape). + */ +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); +} + + +/* + * setescape - add chars to the set we escape on transmission. + */ +static int +setescape(argv) + char **argv; +{ + int n, ret; + char *p, *endp; + + p = *argv; + ret = 1; + while (*p) { + n = strtol(p, &endp, 16); + if (p == endp) { + fprintf(stderr, "%s: invalid hex number: %s\n", progname, p); + return 0; + } + p = endp; + if (n < 0 || 0x20 <= n && n <= 0x3F || n == 0x5E || n > 0xFF) { + fprintf(stderr, "%s: can't escape character 0x%x\n", n); + ret = 0; + } else + xmit_accm[0][n >> 5] |= 1 << (n & 0x1F); + while (*p == ',' || *p == ' ') + ++p; + } + return ret; +} + + +/* + * 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); + return -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\n", arg); + return -1; + } else { + local = *(long *)hp->h_addr; + if (our_name[0] == 0) { + strncpy(our_name, arg, MAXNAMELEN); + our_name[MAXNAMELEN-1] = 0; + } + } + } + if (bad_ip_adrs(local)) { + fprintf(stderr, "bad local IP address %s\n", ip_ntoa(local)); + return -1; + } + 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\n", colon); + return -1; + } else { + remote = *(long *)hp->h_addr; + if (remote_name[0] == 0) { + strncpy(remote_name, colon, MAXNAMELEN); + remote_name[MAXNAMELEN-1] = 0; + } + } + } + if (bad_ip_adrs(remote)) { + fprintf(stderr, "bad remote IP address %s\n", ip_ntoa(remote)); + return -1; + } + if (remote != 0) + wo->hisaddr = remote; + } + + return 1; +} + + +/* + * setnoipdflt - disable setipdefault() + */ +static int +setnoipdflt() +{ + disable_defaultip = 1; + return 1; +} + + +/* + * setipcpaccl - accept peer's idea of our address + */ +static int +setipcpaccl() +{ + ipcp_wantoptions[0].accept_local = 1; + return 1; +} + + +/* + * setipcpaccr - accept peer's idea of its address + */ +static int +setipcpaccr() +{ + ipcp_wantoptions[0].accept_remote = 1; + 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 || disable_defaultip) + 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. + */ + wo->accept_local = 1; /* don't insist on this default value */ + if ((hp = gethostbyname(hostname)) == NULL) + return; + local = *(long *)hp->h_addr; + if (local != 0 && !bad_ip_adrs(local)) + 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 || (netmask & ~mask) != 0) { + fprintf(stderr, "Invalid netmask %s\n", *argv); + return 0; + } + + netmask = mask; + return (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); +} + + +static int +setcrtscts() +{ + crtscts = 1; + return (1); +} + +static int +setxonxoff() +{ + crtscts = 2; + 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 +setlock() +{ + lockflag = 1; + 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 the echo interval for modem-less monitors + */ + +static int +setlcpechointv(argv) + char **argv; +{ + return int_option(*argv, &lcp_echo_interval); +} + +static int +setlcpechofails(argv) + char **argv; +{ + return int_option(*argv, &lcp_echo_fails); +} + +/* + * Functions to set timeouts, max transmits, etc. + */ +static int +setlcptimeout(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].timeouttime); +} + +static int setlcpterm(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].maxtermtransmits); +} + +static int setlcpconf(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].maxconfreqtransmits); +} + +static int setlcpfails(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].maxnakloops); +} + +static int setipcptimeout(argv) + char **argv; +{ + return int_option(*argv, &ipcp_fsm[0].timeouttime); +} + +static int setipcpterm(argv) + char **argv; +{ + return int_option(*argv, &ipcp_fsm[0].maxtermtransmits); +} + +static int setipcpconf(argv) + char **argv; +{ + return int_option(*argv, &ipcp_fsm[0].maxconfreqtransmits); +} + +static int setipcpfails(argv) + char **argv; +{ + return int_option(*argv, &lcp_fsm[0].maxnakloops); +} + +static int setpaptimeout(argv) + char **argv; +{ + return int_option(*argv, &upap[0].us_timeouttime); +} + +static int setpapreqs(argv) + char **argv; +{ + return int_option(*argv, &upap[0].us_maxtransmits); +} + +static int setchaptimeout(argv) + char **argv; +{ + return int_option(*argv, &chap[0].timeouttime); +} + +static int setchapchal(argv) + char **argv; +{ + return int_option(*argv, &chap[0].max_transmits); +} + +static int setchapintv(argv) + char **argv; +{ + return int_option(*argv, &chap[0].chal_interval); +} diff --git a/usr.sbin/pppd/patchlevel.h b/usr.sbin/pppd/patchlevel.h new file mode 100644 index 000000000000..ad290b08f5fe --- /dev/null +++ b/usr.sbin/pppd/patchlevel.h @@ -0,0 +1,5 @@ +/* $Id: patchlevel.h,v 1.10 1994/06/09 01:51:10 paulus Exp $ */ +#define PATCHLEVEL 2 + +#define VERSION "2.1" +#define DATE "9 June 94" diff --git a/usr.sbin/pppd/pathnames.h b/usr.sbin/pppd/pathnames.h new file mode 100644 index 000000000000..d8e91dcad676 --- /dev/null +++ b/usr.sbin/pppd/pathnames.h @@ -0,0 +1,19 @@ +/* + * define path names + * + * $Id: pathnames.h,v 1.4 1994/05/18 06:34:46 paulus Exp $ + */ + +#if defined(STREAMS) || defined(ultrix) +#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" +#define _PATH_IPUP "/etc/ppp/ip-up" +#define _PATH_IPDOWN "/etc/ppp/ip-down" +#define _PATH_TTYOPT "/etc/ppp/options." +#define _PATH_USEROPT ".ppprc" diff --git a/usr.sbin/pppd/ppp.h b/usr.sbin/pppd/ppp.h new file mode 100644 index 000000000000..3d8f870bf2f3 --- /dev/null +++ b/usr.sbin/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/usr.sbin/pppd/pppd.8 b/usr.sbin/pppd/pppd.8 new file mode 100644 index 000000000000..3395631d8f64 --- /dev/null +++ b/usr.sbin/pppd/pppd.8 @@ -0,0 +1,701 @@ +.\" manual page [] for pppd 2.0 +.\" $Id: pppd.8,v 1.7 1994/06/09 01:50:48 paulus Exp $ +.\" SH section heading +.\" SS subsection heading +.\" LP paragraph +.\" IP indented paragraph +.\" TP hanging label +.TH PPPD 8 +.SH NAME +pppd \- Point to Point Protocol daemon +.SH SYNOPSIS +.B pppd +[ +.I options +] [ +.I tty_name +] [ +.I speed +] +.SH DESCRIPTION +.LP +The Point-to-Point Protocol (PPP) provides a method for transmitting +datagrams over serial point-to-point links. PPP +is composed of three parts: a method for encapsulating datagrams over +serial links, an extensible Link Control Protocol (LCP), and +a family of Network Control Protocols (NCP) for establishing +and configuring different network-layer protocols. +.LP +The encapsulation scheme is provided by driver code in the kernel. +.B pppd +provides the basic LCP, authentication support, and an +NCP for establishing and configuring the Internet Protocol (IP) +(called the IP Control Protocol, IPCP). +.SH FREQUENTLY USED OPTIONS +.TP +.I +Communicate over the named device. The string "/dev/" +is prepended if necessary. If no device name is given, +.I pppd +will use the controlling terminal, and will not fork to put itself in +the background. +.TP +.I +Set the baud rate to . On systems such as 4.4BSD and NetBSD, +any speed can be specified. Other systems (e.g. SunOS) allow only a +limited set of speeds. +.TP +.B asyncmap \fI +Set the async character map to . +This map describes which control characters cannot be successfully +received over the serial line. +.I pppd +will ask the peer to send these characters as a 2-byte escape sequence. +The argument is a 32 bit hex number +with each bit representing a character to escape. +Bit 0 (00000001) represents the character 0x00; +bit 31 (80000000) represents the character 0x1f or ^_. +If multiple \fBasyncmap\fR options are +given, the values are ORed together. +If no \fBasyncmap\fR option is given, no async character map will be +negotiated for the receive direction; the peer will then escape +\fIall\fR control characters. +.TP +.B auth +Require the peer to authenticate itself before allowing network +packets to be sent or received. +.TP +.B connect \fI

+Use the executable or shell command specified by \fI

\fR to set up the +serial line. This script would typically use the "chat" program to +dial the modem and start the remote ppp session. +.TP +.B crtscts +Use hardware flow control (i.e. RTS/CTS) to control the flow of data on +the serial port. +.TP +.B xonxoff +Use software flow control (i.e. XON/XOFF) to control the flow of data on +the serial port. This option is not implemented on BSD or Ultrix systems +at present. +.TP +.B -crtscts +A synonym for \fBxonxoff\fR. +.TP +.B defaultroute +Add a default route to the system routing tables, using the peer as +the gateway, when IPCP negotiation is successfully completed. +This entry is removed when the PPP connection is broken. +.TP +.B disconnect \fI

+Run the executable or shell command specified by \fI

\fR after +\fIpppd\fR has terminated the link. This script could, for example, +issue commands to the modem to cause it to hang up if hardware modem +control signals were not available. +.TP +.B escape \fIxx,yy,... +Specifies that certain characters should be escaped on transmission +(regardless of whether the peer requests them to be escaped with its +async control character map). The characters to be escaped are +specified as a list of hex numbers separated by commas. Note that +almost any character can be specified for the \fBescape\fR option, +unlike the \fBasyncmap\fR option which only allows control characters +to be specified. The characters which may not be escaped are those +with hex values 0x20 - 0x3f or 0x5e. +.TP +.B file \fI +Read options from file (the format is described below). +.TP +.B lock +Specifies that \fIpppd\fR should use a UUCP-style lock on the serial +device to ensure exclusive access to the device. +.TP +.B mru \fI +Set the MRU [Maximum Receive Unit] value to for negotiation. +.I pppd +will ask the peer to send packets of no more than bytes. +The minimum MRU value is 128. +The default MRU value is 1500. A value of 296 is recommended for slow +links (40 bytes for TCP/IP header + 256 bytes of data). +.TP +.B netmask \fI +Set the interface netmask to , a 32 bit netmask in "decimal dot" notation +(e.g. 255.255.255.0). +.TP +.B passive +Enables the "passive" option in the LCP. With this option, +.I pppd +will attempt to initiate a connection; if no reply is received from +the peer, +.I pppd +will then just wait passively for a valid LCP packet from the peer +(instead of exiting, as it does without this option). +.TP +.B silent +With this option, +.I pppd +will not transmit LCP packets to initiate a connection until a valid +LCP packet is received from the peer (as for the "passive" option with +old versions of \fIpppd\fR). +.SH OPTIONS +.TP +.I \fB:\fI +Set the local and/or remote interface IP addresses. Either one may be +omitted. The IP addresses can be specified with a host name or in +decimal dot notation (e.g. 150.234.56.78). The default local +address is the (first) IP address of the system (unless the +.B noipdefault +option is given). The remote address will be obtained from the peer +if not specified in any option. Thus, in simple cases, this option is +not required. +If a local and/or remote IP address is specified with this option, +.I pppd +will not accept a different value from the peer in the IPCP +negotiation, unless the +.B ipcp-accept-local +and/or +.B ipcp-accept-remote +options are given, respectively. +.TP +.B -all +Don't request or allow negotiation of any options for LCP and IPCP (use +default values). +.TP +.B -ac +Disable Address/Control compression negotiation (use default, i.e. +address/control field disabled). +.TP +.B -am +Disable asyncmap negotiation (use the default asyncmap, i.e. escape +all control characters). +.TP +.B -as \fI +Same as +.B asyncmap \fI +.TP +.B -d +Increase debugging level (same as the \fBdebug\fR option). +.TP +.B -detach +Don't fork to become a background process (otherwise +.I pppd +will do so if a serial device is specified). +.TP +.B -ip +Disable IP address negotiation (with this option, the remote IP +address must be specified with an option on the command line or in an +options file). +.TP +.B -mn +Disable magic number negotiation. With this option, +.I pppd +cannot detect a looped-back line. +.TP +.B -mru +Disable MRU [Maximum Receive Unit] negotiation (use default, i.e. 1500). +.TP +.B -p +Same as the +.B passive +option. +.TP +.B -pc +Disable protocol field compression negotiation (use default, i.e. +protocol field compression disabled). +.TP +.B +ua \fI

+Agree to authenticate using PAP [Password Authentication Protocol] if +requested by the peer, and +use the data in file

for the user and password to send to the +peer. The file contains the remote user name, followed by a newline, +followed by the remote password, followed by a newline. This option +is obsolescent. +.TP +.B +pap +Require the peer to authenticate itself using PAP. +.TP +.B -pap +Don't agree to authenticate using PAP. +.TP +.B +chap +Require the peer to authenticate itself using CHAP [Cryptographic +Handshake Authentication Protocol] authentication. +.TP +.B -chap +Don't agree to authenticate using CHAP. +.TP +.B -vj +Disable negotiation of Van Jacobson style IP header compression (use +default, i.e. no compression). +.TP +.B debug +Increase debugging level (same as \fB\-d\fR). +If this +option is given, \fIpppd\fR will log the contents of all control +packets sent or received in a readable form. The packets are logged +through syslog with facility \fIdaemon\fR and level \fIdebug\fR. This +information can be directed to a file by setting up /etc/syslog.conf +appropriately (see syslog.conf(5)). (If \fIpppd\fR is compiled with +extra debugging enabled, it will log messages using facility +\fIlocal2\fR instead of \fIdaemon\fR). +.TP +.B domain \fI +Append the domain name to the local host name for authentication +purposes. For example, if gethostname() returns the name porsche, but the +fully qualified domain name is porsche.Quotron.COM, you would use the +domain option to set the domain name to Quotron.COM. +.TP +.B modem +Use the modem control lines. On Ultrix, this option implies hardware +flow control, as for the \fBcrtscts\fR option. (This option is not fully +implemented.) +.TP +.B kdebug \fIn +Enable debugging code in the kernel-level PPP driver. The argument +\fIn\fR is a number which is the sum of the following values: 1 to +enable general debug messages, 2 to request that the contents of +received packets be printed, and 4 to request that the contents of +transmitted packets be printed. +.TP +.B local +Don't use the modem control lines. +.TP +.B mtu \fI +Set the MTU [Maximum Transmit Unit] value to \fI\fR. Unless the +peer requests a smaller value via MRU negotiation, \fIpppd\fR will +request that the kernel networking code send data packets of no more +than \fIn\fR bytes through the PPP network interface. +.TP +.B name \fI +Set the name of the local system for authentication purposes to . +.TP +.B user \fI +Set the user name to use for authenticating this machine with the peer +using PAP to . +.TP +.B usehostname +Enforce the use of the hostname as the name of the local system for +authentication purposes (overrides the +.B name +option). +.TP +.B remotename \fI +Set the assumed name of the remote system for authentication purposes +to . +.TP +.B proxyarp +Add an entry to this system's ARP [Address Resolution Protocol] table +with the IP address of the peer and the Ethernet address of this +system. +.TP +.B login +Use the system password database for authenticating the peer using +PAP. +.TP +.B noipdefault +Disables the default behaviour when no local IP address is specified, +which is to determine (if possible) the local IP address from the +hostname. With this option, the peer will have to supply the local IP +address during IPCP negotiation (unless it specified explicitly on the +command line or in an options file). +.TP +.B lcp-echo-interval \fI +If this option is given, \fIpppd\fR will send an LCP echo-request +frame to the peer every \fIn\fR seconds. Under Linux, the +echo-request is sent when no packets have been received from the peer +for \fIn\fR seconds. Normally the peer should respond to the +echo-request by sending an echo-reply. This option can be used with +the \fIlcp-echo-failure\fR option to detect that the peer is no longer +connected. +.TP +.B lcp-echo-failure \fI +If this option is given, \fIpppd\fR will presume the peer to be dead +if \fIn\fR LCP echo-requests are sent without receiving a valid LCP +echo-reply. If this happens, \fIpppd\fR will terminate the +connection. Use of this option requires a non-zero value for the +\fIlcp-echo-interval\fR parameter. This option can be used to enable +\fIpppd\fR to terminate after the physical connection has been broken +(e.g., the modem has hung up) in situations where no hardware modem +control lines are available. +.TP +.B lcp-restart \fI +Set the LCP restart interval (retransmission timeout) to seconds +(default 3). +.TP +.B lcp-max-terminate \fI +Set the maximum number of LCP terminate-request transmissions to +(default 3). +.TP +.B lcp-max-configure \fI +Set the maximum number of LCP configure-request transmissions to +(default 10). +.TP +.B lcp-max-failure \fI +Set the maximum number of LCP configure-NAKs returned before starting +to send configure-Rejects instead to (default 10). +.TP +.B ipcp-restart \fI +Set the IPCP restart interval (retransmission timeout) to seconds +(default 3). +.TP +.B ipcp-max-terminate \fI +Set the maximum number of IPCP terminate-request transmissions to +(default 3). +.TP +.B ipcp-max-configure \fI +Set the maximum number of IPCP configure-request transmissions to +(default 10). +.TP +.B ipcp-max-failure \fI +Set the maximum number of IPCP configure-NAKs returned before starting +to send configure-Rejects instead to (default 10). +.TP +.B pap-restart \fI +Set the PAP restart interval (retransmission timeout) to seconds +(default 3). +.TP +.B pap-max-authreq \fI +Set the maximum number of PAP authenticate-request transmissions to + (default 10). +.TP +.B chap-restart \fI +Set the CHAP restart interval (retransmission timeout for challenges) +to seconds (default 3). +.TP +.B chap-max-challenge \fI +Set the maximum number of CHAP challenge transmissions to (default +10). +.TP +.B chap-interval \fI +If this option is given, +.I pppd +will rechallenge the peer every seconds. +.TP +.B ipcp-accept-local +With this option, +.I pppd +will accept the peer's idea of our local IP address, even if the +local IP address was specified in an option. +.TP +.B ipcp-accept-remote +With this option, +.I pppd +will accept the peer's idea of its (remote) IP address, even if the +remote IP address was specified in an option. +.SH OPTIONS FILES +Options can be taken from files as well as the command line. +.I pppd +reads options from the files /etc/ppp/options and ~/.ppprc before +looking at the command line. An options file is parsed into a series +of words, delimited by whitespace. Whitespace can be included in a +word by enclosing the word in quotes ("). A backslash (\\) quotes the +following character. A hash (#) starts a comment, which continues +until the end of the line. +.SH AUTHENTICATION +.I pppd +provides system administrators with sufficient access control that PPP +access to a server machine can be provided to legitimate users without +fear of compromising the security of the server or the network it's +on. In part this is provided by the /etc/ppp/options file, where the +administrator can place options to require authentication whenever +.I pppd +is run, and in part by the PAP and CHAP secrets files, where the +administrator can restrict the set of IP addresses which individual +users may use. +.LP +The default behaviour of +.I pppd +is to agree to authenticate if requested, and to not +require authentication from the peer. However, +.I pppd +will not agree to +authenticate itself with a particular protocol if it has no secrets +which could be used to do so. +.LP +Authentication is based on secrets, which are selected from secrets +files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP). +Both secrets files have the same format, and both can store secrets +for several combinations of server (authenticating peer) and client +(peer being authenticated). Note that +.I pppd +can be both a server +and client, and that different protocols can be used in the two +directions if desired. +.LP +A secrets file is parsed into words as for a options file. A secret +is specified by a line containing at least 3 words, in the order +client, server, secret. Any following words on the same line are +taken to be a list of acceptable IP addresses for that client. If +there are only 3 words on the line, it is assumed that any IP address +is OK; to disallow all IP addresses, use "-". If the secret starts +with an `@', what follows is assumed to be the name of a file from +which to read the secret. A "*" as the client or server name matches +any name. When selecting a secret, \fIpppd\fR takes the best match, i.e. +the match with the fewest wildcards. +.LP +Thus a secrets file contains both secrets for use in authenticating +other hosts, plus secrets which we use for authenticating ourselves to +others. Which secret to use is chosen based on the names of the host +(the `local name') and its peer (the `remote name'). The local name +is set as follows: +.TP 3 +if the \fBusehostname\fR option is given, +then the local name is the hostname of this machine +(with the domain appended, if given) +.TP 3 +else if the \fBname\fR option is given, +then use the argument of the first \fBname\fR option seen +.TP 3 +else if the local IP address is specified with a hostname, +then use that name +.TP 3 +else use the hostname of this machine (with the domain appended, if given) +.LP +When authenticating ourselves using PAP, there is also a `username' +which is the local name by default, but can be set with the \fBuser\fR +option or the \fB+ua\fR option. +.LP +The remote name is set as follows: +.TP 3 +if the \fBremotename\fR option is given, +then use the argument of the last \fBremotename\fR option seen +.TP 3 +else if the remote IP address is specified with a hostname, +then use that host name +.TP 3 +else the remote name is the null string "". +.LP +Secrets are selected from the PAP secrets file as follows: +.TP 2 +* +For authenticating the peer, look for a secret with client == +username specified in the PAP authenticate-request, and server == +local name. +.TP 2 +* +For authenticating ourselves to the peer, look for a secret with +client == our username, server == remote name. +.LP +When authenticating the peer with PAP, a secret of "" matches any +password supplied by the peer. If the password doesn't match the +secret, the password is encrypted using crypt() and checked against +the secret again; thus secrets for authenticating the peer can be +stored in encrypted form. If the \fBlogin\fR option was specified, the +username and password are also checked against the system password +database. Thus, the system administrator can set up the pap-secrets +file to allow PPP access only to certain users, and to restrict the +set of IP addresses that each user can use. +.LP +Secrets are selected from the CHAP secrets file as follows: +.TP 2 +* +For authenticating the peer, look for a secret with client == name +specified in the CHAP-Response message, and server == local name. +.TP 2 +* +For authenticating ourselves to the peer, look for a secret with +client == local name, and server == name specified in the +CHAP-Challenge message. +.LP +Authentication must be satisfactorily completed before IPCP (or any +other Network Control Protocol) can be started. If authentication +fails, \fIpppd\fR will terminated the link (by closing LCP). If IPCP +negotiates an unacceptable IP address for the remote host, IPCP will +be closed. IP packets can only be sent or received when IPCP is open. +.LP +In some cases it is desirable to allow some hosts which can't +authenticate themselves to connect and use one of a restricted set of +IP addresses, even when the local host generally requires +authentication. If the peer refuses to authenticate itself when +requested, \fIpppd\fR takes that as equivalent to authenticating with +PAP using the empty string for the username and password. Thus, by +adding a line to the pap-secrets file which specifies the empty string +for the client and password, it is possible to allow restricted access +to hosts which refuse to authenticate themselves. +.SH ROUTING +.LP +When IPCP negotiation is completed successfully, +.I pppd +will inform the kernel of the local and remote IP addresses for the +ppp interface. This is sufficient to create a +host route to the remote end of the link, which will enable the peers +to exchange IP packets. Communication with other machines generally +requires further modification to routing tables and/or ARP (Address +Resolution Protocol) tables. In some cases this will be done +automatically through the actions of the \fIrouted\fR or \fIgated\fR +daemons, but in most cases some further intervention is required. +.LP +Sometimes it is desirable +to add a default route through the remote host, as in the case of a +machine whose only connection to the Internet is through the ppp +interface. The \fBdefaultroute\fR option causes \fIpppd\fR to create such a +default route when IPCP comes up, and delete it when the link is +terminated. +.LP +In some cases it is desirable to use proxy ARP, for example on a +server machine connected to a LAN, in order to allow other hosts to +communicate with the remote host. The \fBproxyarp\fR option causes \fIpppd\fR +to look for a network interface on the same subnet as the remote host +(an interface supporting broadcast and ARP, which is up and not a +point-to-point or loopback interface). If found, \fIpppd\fR creates a +permanent, published ARP entry with the IP address of the remote host +and the hardware address of the network interface found. +.SH EXAMPLES +.LP +In the simplest case, you can connect the serial ports of two machines +and issue a command like +.IP +pppd /dev/ttya 9600 passive +.LP +to each machine, assuming there is no \fIgetty\fR running on the +serial ports. If one machine has a \fIgetty\fR running, you can use +\fIkermit\fR or \fItip\fR on the other machine to log in to the first +machine and issue a command like +.IP +pppd passive +.LP +Then exit from the communications program (making sure the connection +isn't dropped), and issue a command like +.IP +pppd /dev/ttya 9600 +.LP +The process of logging in to the other machine and starting \fIpppd\fR +can be automated by using the \fBconnect\fR option to run \fIchat\fR, +for example: +.IP +pppd /dev/ttya 38400 connect 'chat "" "" "login:" "username" +"Password:" "password" "% " "exec pppd passive"' +.LP +(Note however that running chat like this will leave the password +visible in the parameter list of pppd and chat.) +.LP +If your serial connection is any more complicated than a piece of +wire, you may need to arrange for some control characters to be +escaped. In particular, it is often useful to escape XON (^Q) and +XOFF (^S), using \fBasyncmap a0000\fR. If the path includes a telnet, +you probably should escape ^] as well (\fBasyncmap 200a0000\fR). +If the path includes an rlogin, you will need to use the \fBescape +ff\fR option on the end which is running the rlogin client, since many +rlogin implementations are not +transparent; they will remove the sequence [0xff, 0xff, 0x73, 0x73, +followed by any 8 bytes] from the stream. +.SH DIAGNOSTICS +.LP +Messages are sent to the syslog daemon using facility LOG_DAEMON. +(This can be overriden by recompiling \fIpppd\fR with the macro +LOG_PPP defined as the desired facility.) In order to see the error +and debug messages, you will need to edit your /etc/syslog.conf file +to direct the messages to the desired output device or file. +.LP +The \fBdebug\fR option causes the contents of all control packets sent +or received to be logged, that is, all LCP, PAP, CHAP or IPCP packets. +This can be useful if the PPP negotiation does not succeed. +If debugging is enabled at compile time, \fIpppd\fR uses facility +LOG_LOCAL2 instead of LOG_DAEMON, and the \fBdebug\fR option +causes additional debugging messages to be logged. +.LP +Debugging can also be enabled by sending a +SIGUSR1 to the +.I pppd +process. +Debugging may be disabled by sending a SIGUSR2 to the +.I pppd +process. +.SH FILES +.TP +.B /var/run/ppp\fIn\fB.pid \fR(BSD), \fB/etc/ppp/ppp\fIn\fB.pid \fR(SunOS) +Process-ID for \fIpppd\fR process on ppp interface unit \fIn\fR. +.TP +.B /etc/ppp/ip-up +A program or script which is executed when the link is available for +sending and receiving IP packets (that is, IPCP has come up). It is +executed with the parameters \fIinterface-name tty-device speed +local-IP-address remote-IP-address\fR. +.IP +This program or script is executed with the same real and effective +user-ID as \fIpppd\fR, that is, at least the effective user-ID and +possibly the real user-ID will be \fBroot\fR. This is so that it can +be used to manipulate routes, run privileged daemons (e.g. +\fBsendmail\fR), etc. Be careful that the contents of the +/etc/ppp/ip-up and /etc/ppp/ip-down scripts do not compromise your +system's security. +.TP +.B /etc/ppp/ip-down +A program or script which is executed when the link is no longer +available for sending and receiving IP packets. This script can be +used for undoing the effects of the /etc/ppp/ip-up script. It is +invoked with the same parameters as the ip-up script, and the same +security considerations apply, since it is executed with the same +effective and real user-IDs as \fIpppd\fR. +.TP +.B /etc/ppp/pap-secrets +Usernames, passwords and IP addresses for PAP authentication. +.TP +.B /etc/ppp/chap-secrets +Names, secrets and IP addresses for CHAP authentication. +.TP +.B /etc/ppp/options +System default options for +.I pppd, +read before user default options or command-line options. +.TP +.B ~/.ppprc +User default options, read before command-line options. +.TP +.B /etc/ppp/options.\fIttyname +System default options for the serial port being used, read after +command-line options. +.SH SEE ALSO +.TP +.B RFC1144 +Jacobson, V. +.I Compressing TCP/IP headers for low-speed serial links. +1990 February. +.TP +.B RFC1321 +Rivest, R. +.I The MD5 Message-Digest Algorithm. +1992 April. +.TP +.B RFC1332 +McGregor, G. +.I PPP Internet Protocol Control Protocol (IPCP). +1992 May. +.TP +.B RFC1334 +Lloyd, B.; Simpson, W.A. +.I PPP authentication protocols. +1992 October. +.TP +.B RFC1548 +Simpson, W.A. +.I The Point\-to\-Point Protocol (PPP). +1993 December. +.TP +.B RFC1549 +Simpson, W.A. +.I PPP in HDLC Framing. +1993 December +.SH NOTES +The following signals have the specified effect when sent to the +.I pppd +process. +.TP +.B SIGINT, SIGTERM +These signals cause \fIpppd\fR to terminate the link (by closing LCP), +restore the serial device settings, and exit. +.TP +.B SIGHUP +Indicates that the physical layer has been disconnected. \fIpppd\fR +will attempt to restore the serial device settings (this may produce +error messages on Suns), and then exit. +.SH BUGS +The use of the modem control lines and the effects of the \fBmodem\fR +and \fBlocal\fR options are not well defined. +.SH AUTHORS +Drew Perkins, +Brad Clements, +Karl Fox, +Greg Christy, +Brad Parker (brad@fcr.com), +Paul Mackerras (paulus@cs.anu.edu.au) diff --git a/usr.sbin/pppd/pppd.h b/usr.sbin/pppd/pppd.h new file mode 100644 index 000000000000..e38687d7ac17 --- /dev/null +++ b/usr.sbin/pppd/pppd.h @@ -0,0 +1,215 @@ +/* + * 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.3 1994/05/26 06:43:42 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 */ + +/* + * Global variables. + */ +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 */ +extern int phase; /* See values below */ + +/* + * Values for phase. + */ +#define PHASE_DEAD 0 +#define PHASE_ESTABLISH 1 +#define PHASE_AUTHENTICATE 2 +#define PHASE_NETWORK 3 +#define PHASE_TERMINATE 4 + +/* + * Prototypes. + */ +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)++ = (u_char) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp)++ << 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (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/usr.sbin/pppd/sys-bsd.c b/usr.sbin/pppd/sys-bsd.c new file mode 100644 index 000000000000..65c0b7716faf --- /dev/null +++ b/usr.sbin/pppd/sys-bsd.c @@ -0,0 +1,743 @@ +/* + * 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.7 1994/05/30 06:10:07 paulus Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if RTM_VERSION >= 3 +#include +#endif + +#include "pppd.h" +#include "ppp.h" + +static int initdisc = -1; /* Initial TTY discipline */ +extern int kdebugflag; +static int rtm_seq; + +/* + * establish_ppp - Turn the serial port into a ppp interface. + */ +void +establish_ppp() +{ + int pppdisc = PPPDISC; + int x; + + 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); + } + + /* + * Enable debug in the driver if requested. + */ + if (kdebugflag) { + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_WARNING, "ioctl (PPPIOCGFLAGS): %m"); + } else { + x |= (kdebugflag & 0xFF) * SC_DEBUG; + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) + syslog(LOG_WARNING, "ioctl(PPPIOCSFLAGS): %m"); + } + } +} + + +/* + * disestablish_ppp - Restore the serial port to normal operation. + * This shouldn't call die() because it's called from die(). + */ +void +disestablish_ppp() +{ + int x; + char *s; + + if (initdisc >= 0) { + /* + * Check whether the link seems not to be 8-bit clean. + */ + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) { + s = NULL; + switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) { + case SC_RCV_B7_0: + s = "bit 7 set to 1"; + break; + case SC_RCV_B7_1: + s = "bit 7 set to 0"; + break; + case SC_RCV_EVNP: + s = "odd parity"; + break; + case SC_RCV_ODDP: + s = "even parity"; + break; + } + if (s != NULL) { + syslog(LOG_WARNING, "Serial link is not 8-bit clean:"); + syslog(LOG_WARNING, "All received characters had %s", s); + } + } + 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 (debug) + log_packet(p, len, "sent "); + + 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_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(unit, accm) + int unit; + ext_accm accm; +{ + if (ioctl(fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) + syslog(LOG_WARNING, "ioctl(set extended ACCM): %m"); +} + + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void +ppp_recv_config(unit, mru, asyncmap, pcomp, accomp) + int unit, mru; + u_long asyncmap; + int pcomp, accomp; +{ + int x; + + if (ioctl(fd, PPPIOCSMRU, (caddr_t) &mru) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSMRU): %m"); + quit(); + } + 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(); + } +} + +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp(u, vjcomp, cidcomp, maxcid) + int u, vjcomp, cidcomp, maxcid; +{ + 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; + } + if (ioctl(fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + return 0; + } + return 1; +} + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int +sifup(u) + int u; +{ + struct ifreq ifr; + u_int x; + + 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; + } + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); + return 0; + } + x |= SC_ENABLE_IP; + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + return 0; + } + return 1; +} + +/* + * sifdown - Config the interface down and disable IP. + */ +int +sifdown(u) + int u; +{ + struct ifreq ifr; + u_int x; + int rv; + + rv = 1; + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); + rv = 0; + } else { + x &= ~SC_ENABLE_IP; + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + rv = 0; + } + } + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m"); + rv = 0; + } else { + ifr.ifr_flags &= ~IFF_UP; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); + rv = 0; + } + } + return rv; +} + +/* + * 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) + int u; + u_long 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) + int u; + u_long 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) + int u; + u_long g; +{ + return dodefaultroute(g, 's'); +} + +/* + * cifdefaultroute - delete a default route through the address given. + */ +int +cifdefaultroute(u, g) + int u; + u_long g; +{ + return dodefaultroute(g, 'c'); +} + +/* + * dodefaultroute - talk to a routing socket to add/delete a default route. + */ +int +dodefaultroute(g, cmd) + u_long g; + int cmd; +{ + int routes; + struct { + struct rt_msghdr hdr; + struct sockaddr_in dst; + struct sockaddr_in gway; + struct sockaddr_in mask; + } rtmsg; + + if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { + syslog(LOG_ERR, "%cifdefaultroute: opening routing socket: %m", cmd); + return 0; + } + + memset(&rtmsg, 0, sizeof(rtmsg)); + rtmsg.hdr.rtm_type = cmd == 's'? RTM_ADD: RTM_DELETE; + rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY; + rtmsg.hdr.rtm_version = RTM_VERSION; + rtmsg.hdr.rtm_seq = ++rtm_seq; + rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + rtmsg.dst.sin_len = sizeof(rtmsg.dst); + rtmsg.dst.sin_family = AF_INET; + rtmsg.gway.sin_len = sizeof(rtmsg.gway); + rtmsg.gway.sin_family = AF_INET; + rtmsg.gway.sin_addr.s_addr = g; + rtmsg.mask.sin_len = sizeof(rtmsg.dst); + rtmsg.mask.sin_family = AF_INET; + + rtmsg.hdr.rtm_msglen = sizeof(rtmsg); + if (write(routes, &rtmsg, sizeof(rtmsg)) < 0) { + syslog(LOG_ERR, "%s default route: %m", cmd=='s'? "add": "delete"); + close(routes); + return 0; + } + + close(routes); + return 1; +} + +#if RTM_VERSION >= 3 + +/* + * sifproxyarp - Make a proxy ARP entry for the peer. + */ +static struct { + struct rt_msghdr hdr; + struct sockaddr_inarp dst; + struct sockaddr_dl hwa; + char extra[128]; +} arpmsg; + +static int arpmsg_valid; + +int +sifproxyarp(unit, hisaddr) + int unit; + u_long hisaddr; +{ + int routes; + int l; + + /* + * Get the hardware address of an interface on the same subnet + * as our local address. + */ + memset(&arpmsg, 0, sizeof(arpmsg)); + if (!get_ether_addr(hisaddr, &arpmsg.hwa)) { + syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP"); + return 0; + } + + if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { + syslog(LOG_ERR, "sifproxyarp: opening routing socket: %m"); + return 0; + } + + arpmsg.hdr.rtm_type = RTM_ADD; + arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC; + arpmsg.hdr.rtm_version = RTM_VERSION; + arpmsg.hdr.rtm_seq = ++rtm_seq; + arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; + arpmsg.hdr.rtm_inits = RTV_EXPIRE; + arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp); + arpmsg.dst.sin_family = AF_INET; + arpmsg.dst.sin_addr.s_addr = hisaddr; + arpmsg.dst.sin_other = SIN_PROXY; + + arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg + + arpmsg.hwa.sdl_len; + if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { + syslog(LOG_ERR, "add proxy arp entry: %m"); + close(routes); + return 0; + } + + close(routes); + arpmsg_valid = 1; + return 1; +} + +/* + * cifproxyarp - Delete the proxy ARP entry for the peer. + */ +int +cifproxyarp(unit, hisaddr) + int unit; + u_long hisaddr; +{ + int routes; + + if (!arpmsg_valid) + return 0; + arpmsg_valid = 0; + + arpmsg.hdr.rtm_type = RTM_DELETE; + arpmsg.hdr.rtm_seq = ++rtm_seq; + + if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { + syslog(LOG_ERR, "sifproxyarp: opening routing socket: %m"); + return 0; + } + + if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { + syslog(LOG_ERR, "delete proxy arp entry: %m"); + close(routes); + return 0; + } + + close(routes); + return 1; +} + +#else /* RTM_VERSION */ + +/* + * sifproxyarp - Make a proxy ARP entry for the peer. + */ +int +sifproxyarp(unit, hisaddr) + int unit; + u_long hisaddr; +{ + struct arpreq arpreq; + struct { + struct sockaddr_dl sdl; + char space[128]; + } dls; + + BZERO(&arpreq, sizeof(arpreq)); + + /* + * Get the hardware address of an interface on the same subnet + * as our local address. + */ + if (!get_ether_addr(hisaddr, &dls.sdl)) { + syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP"); + return 0; + } + + arpreq.arp_ha.sa_len = sizeof(struct sockaddr); + arpreq.arp_ha.sa_family = AF_UNSPEC; + BCOPY(LLADDR(&dls.sdl), arpreq.arp_ha.sa_data, dls.sdl.sdl_alen); + 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; +} +#endif /* RTM_VERSION */ + + +/* + * 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_dl *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; + BCOPY(dla, hwaddr, dla->sdl_len); + return 1; + } + ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len); + } + + return 0; +} + + +/* + * ppp_available - check whether the system has any ppp interfaces + * (in fact we check whether we can do an ioctl on ppp0). + */ +int +ppp_available() +{ + int s, ok; + struct ifreq ifr; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return 1; /* can't tell - maybe we're not root */ + + strncpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name)); + ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0; + close(s); + + return ok; +} diff --git a/usr.sbin/pppd/sys-linux.c b/usr.sbin/pppd/sys-linux.c new file mode 100644 index 000000000000..306ae4a8bca1 --- /dev/null +++ b/usr.sbin/pppd/sys-linux.c @@ -0,0 +1,830 @@ +/* + * sys-linux.c - System-dependent procedures for setting up + * PPP interfaces on Linux systems + * + * 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 +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "ppp.h" +#include "fsm.h" +#include "ipcp.h" + +static int initdisc = -1; /* Initial TTY discipline */ +static int prev_kdebugflag = 0; +extern int kdebugflag; +extern u_long netmask; + +#define MAX_IFS 32 + +/* prototypes */ +void die __ARGS((int)); + +/* + * SET_SA_FAMILY - set the sa_family field of a struct sockaddr, + * if it exists. + */ + +#define SET_SA_FAMILY(addr, family) \ + memset ((char *) &(addr), '\0', sizeof(addr)); \ + addr.sa_family = (family); + +/* + * set_kdebugflag - Define the debugging level for the kernel + */ + +int set_kdebugflag (int requested_level) +{ + if (ioctl(fd, PPPIOCGDEBUG, &prev_kdebugflag) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCGDEBUG): %m"); + return (0); + } + + if (prev_kdebugflag != requested_level) { + if (ioctl(fd, PPPIOCSDEBUG, &requested_level) < 0) { + syslog (LOG_ERR, "ioctl(PPPIOCSDEBUG): %m"); + return (0); + } + syslog(LOG_INFO, "set kernel debugging level to %d", requested_level); + } + return (1); +} + +/* + * establish_ppp - Turn the serial port into a ppp interface. + */ + +void establish_ppp (void) +{ + int pppdisc = N_PPP; + int sig = SIGIO; + + if (ioctl(fd, PPPIOCSINPSIG, &sig) == -1) { + syslog(LOG_ERR, "ioctl(PPPIOCSINPSIG): %m"); + die(1); + } + + if (ioctl(fd, TIOCEXCL, 0) < 0) { + syslog (LOG_WARNING, "ioctl(TIOCEXCL): %m"); + } + + 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); + } + + if (ioctl(fd, PPPIOCGUNIT, &ifunit) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m"); + die (1); + } + + set_kdebugflag (kdebugflag); +} + +/* + * disestablish_ppp - Restore the serial port to normal operation. + * This shouldn't call die() because it's called from die(). + */ + +void disestablish_ppp(void) +{ + int x; + char *s; + + if (initdisc >= 0) { + set_kdebugflag (prev_kdebugflag); + /* + * Check whether the link seems not to be 8-bit clean. + */ + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) { + s = NULL; + switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) { + case SC_RCV_B7_0: + s = "bit 7 set to 1"; + break; + case SC_RCV_B7_1: + s = "bit 7 set to 0"; + break; + case SC_RCV_EVNP: + s = "odd parity"; + break; + case SC_RCV_ODDP: + s = "even parity"; + break; + } + if (s != NULL) { + syslog(LOG_WARNING, "Serial link is not 8-bit clean:"); + syslog(LOG_WARNING, "All received characters had %s", s); + } + } + + if (ioctl(fd, TIOCSETD, &initdisc) < 0) + syslog(LOG_ERR, "ioctl(TIOCSETD): %m"); + + if (ioctl(fd, TIOCNXCL, 0) < 0) + syslog (LOG_WARNING, "ioctl(TIOCNXCL): %m"); + + initdisc = -1; + } +} + +/* + * output - Output PPP packet. + */ + +void output (int unit, unsigned char *p, int len) +{ + if (unit != 0) + MAINDEBUG((LOG_WARNING, "output: unit != 0!")); + + if (debug) + log_packet(p, len, "sent "); + + 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 (unsigned char *buf) +{ + int len; + + len = read(fd, buf, MTU + DLLHEADERLEN); + if (len < 0) { + if (errno == EWOULDBLOCK) { +#if 0 + MAINDEBUG((LOG_DEBUG, "read(fd): EWOULDBLOCK")); +#endif + 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 (int unit,int mtu,u_long asyncmap,int pcomp,int accomp) +{ + u_int x; + struct ifreq ifr; + + MAINDEBUG ((LOG_DEBUG, "send_config: mtu = %d\n", mtu)); + 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(); + } + + MAINDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap)); + 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; + + MAINDEBUG ((LOG_DEBUG, "send_config: flags = %x\n", x)); + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + quit(); + } +} + +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(unit, accm) + int unit; + ext_accm accm; +{ + MAINDEBUG ((LOG_DEBUG, "set_xaccm: %08lx %08lx %08lx %08lx\n", + accm[0], accm[1], accm[2], accm[3])); + if (ioctl(fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) + syslog(LOG_WARNING, "ioctl(set extended ACCM): %m"); +} + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void ppp_recv_config (int unit,int mru,u_long asyncmap,int pcomp,int accomp) +{ + u_int x; + + MAINDEBUG ((LOG_DEBUG, "recv_config: mru = %d\n", mru)); + if (ioctl(fd, PPPIOCSMRU, (caddr_t) &mru) < 0) + syslog(LOG_ERR, "ioctl(PPPIOCSMRU): %m"); + + MAINDEBUG ((LOG_DEBUG, "recv_config: asyncmap = %lx\n", asyncmap)); + if (ioctl(fd, PPPIOCRASYNCMAP, (caddr_t) &asyncmap) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCRASYNCMAP): %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; + MAINDEBUG ((LOG_DEBUG, "recv_config: flags = %x\n", x)); + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + quit(); + } +} + +/* + * sifvjcomp - config tcp header compression + */ + +int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid) +{ + 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; + } + + if (vjcomp) { + if (ioctl (fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) { + syslog (LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + return 0; + } + } + + return 1; +} + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ + +int sifup (int 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 | IFF_POINTOPOINT); + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); + return 0; + } + return 1; +} + +/* + * sifdown - Config the interface down and disable IP. + */ + +int sifdown (int 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; + ifr.ifr_flags |= IFF_POINTOPOINT; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); + return 0; + } + return 1; +} + +/* + * sifaddr - Config the interface IP addresses and netmask. + */ + +int sifaddr (int unit, int our_adr, int his_adr, int net_mask) +{ + struct ifreq ifr; + struct rtentry rt; + + SET_SA_FAMILY (ifr.ifr_addr, AF_INET); + SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET); + SET_SA_FAMILY (ifr.ifr_netmask, AF_INET); + + strncpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); +/* + * Set our IP address + */ + ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = our_adr; + if (ioctl(s, SIOCSIFADDR, (caddr_t) &ifr) < 0) { + if (errno != EEXIST) + syslog (LOG_ERR, "ioctl(SIOCAIFADDR): %m"); + else + syslog (LOG_WARNING, "ioctl(SIOCAIFADDR): Address already exists"); + return (0); + } +/* + * Set the gateway address + */ + ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = his_adr; + if (ioctl(s, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) { + syslog (LOG_ERR, "ioctl(SIOCSIFDSTADDR): %m"); + return (0); + } +/* + * Set the netmask + */ + if (net_mask != 0) { + ((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr.s_addr = net_mask; + if (ioctl(s, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) { + syslog (LOG_ERR, "ioctl(SIOCSIFNETMASK): %m"); + return (0); + } + } +/* + * Add the device route + */ + memset (&rt, '\0', sizeof (rt)); + + SET_SA_FAMILY (rt.rt_dst, AF_INET); + SET_SA_FAMILY (rt.rt_gateway, AF_INET); + rt.rt_dev = ifname; /* MJC */ + + ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = 0; + ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = his_adr; + rt.rt_flags = RTF_UP | RTF_HOST; + + if (ioctl(s, SIOCADDRT, &rt) < 0) { + syslog (LOG_ERR, "ioctl(SIOCADDRT) device route: %m"); + return (0); + } + return 1; +} + +/* + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + */ + +int cifaddr (int unit, int our_adr, int his_adr) +{ + struct rtentry rt; +/* + * Delete the route through the device + */ + memset (&rt, '\0', sizeof (rt)); + + SET_SA_FAMILY (rt.rt_dst, AF_INET); + SET_SA_FAMILY (rt.rt_gateway, AF_INET); + rt.rt_dev = ifname; /* MJC */ + + ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = 0; + ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = his_adr; + rt.rt_flags = RTF_UP | RTF_HOST; + + if (ioctl(s, SIOCDELRT, &rt) < 0) { + syslog (LOG_ERR, "ioctl(SIOCDELRT) device route: %m"); + return (0); + } + return 1; +} + +/* + * path_to_route - determine the path to the proc file system data + */ + +FILE *route_fd = (FILE *) 0; +static char route_buffer [100]; + +static char *path_to_route (void); +static int open_route_table (void); +static void close_route_table (void); +static int read_route_table (struct rtentry *rt); +static int defaultroute_exists (void); + +/* + * path_to_route - find the path to the route tables in the proc file system + */ + +static char *path_to_route (void) +{ + struct mntent *mntent; + FILE *fp; + + fp = fopen (MOUNTED, "r"); + if (fp != 0) { + while ((mntent = getmntent (fp)) != 0) { + if (strcmp (mntent->mnt_type, MNTTYPE_IGNORE) == 0) + continue; + + if (strcmp (mntent->mnt_type, "proc") == 0) { + strncpy (route_buffer, mntent->mnt_dir, + sizeof (route_buffer)-10); + route_buffer [sizeof (route_buffer)-10] = '\0'; + strcat (route_buffer, "/net/route"); + + fclose (fp); + return (route_buffer); + } + } + fclose (fp); + } + syslog (LOG_ERR, "proc file system not mounted"); + return 0; +} + +/* + * open_route_table - open the interface to the route table + */ + +static int open_route_table (void) +{ + char *path; + + if (route_fd != (FILE *) 0) + close_route_table(); + + path = path_to_route(); + if (path == NULL) + return 0; + + route_fd = fopen (path, "r"); + if (route_fd == (FILE *) 0) { + syslog (LOG_ERR, "can not open %s: %m", path); + return 0; + } + + /* read and discard the header line. */ + if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0) { + close_route_table(); + return 0; + } + return 1; +} + +/* + * close_route_table - close the interface to the route table + */ + +static void close_route_table (void) +{ + if (route_fd != (FILE *) 0) { + fclose (route_fd); + route_fd = (FILE *) 0; + } +} + +/* + * read_route_table - read the next entry from the route table + */ + +static int read_route_table (struct rtentry *rt) +{ + static char delims[] = " \t\n"; + char *dev_ptr, *ptr, *dst_ptr, *gw_ptr, *flag_ptr; + + if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0) + return 0; + + memset (rt, '\0', sizeof (struct rtentry)); + + dev_ptr = strtok (route_buffer, delims); /* interface name */ + dst_ptr = strtok (NULL, delims); /* destination address */ + gw_ptr = strtok (NULL, delims); /* gateway */ + flag_ptr = strtok (NULL, delims); /* flags */ +#if 0 + ptr = strtok (NULL, delims); /* reference count */ + ptr = strtok (NULL, delims); /* useage count */ + ptr = strtok (NULL, delims); /* metric */ + ptr = strtok (NULL, delims); /* mask */ +#endif + + ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr = + strtoul (dst_ptr, NULL, 16); + + ((struct sockaddr_in *) &rt->rt_gateway)->sin_addr.s_addr = + strtoul (gw_ptr, NULL, 16); + + rt->rt_flags = (short) strtoul (flag_ptr, NULL, 16); + rt->rt_dev = dev_ptr; + + return 1; +} + +/* + * defaultroute_exists - determine if there is a default route + */ + +static int defaultroute_exists (void) +{ + struct rtentry rt; + int result = 0; + + if (!open_route_table()) + return 0; + + while (read_route_table(&rt) != 0) { + if (rt.rt_flags & RTF_UP == 0) + continue; + + if (((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr == 0L) { + syslog (LOG_ERR, + "ppp not replacing existing default route to %s[%s]", + rt.rt_dev, + inet_ntoa (((struct sockaddr_in *) &rt.rt_gateway)-> + sin_addr.s_addr)); + result = 1; + break; + } + } + close_route_table(); + return result; +} + +/* + * sifdefaultroute - assign a default route through the address given. + */ + +int sifdefaultroute (int unit, int gateway) +{ + struct rtentry rt; + + if (defaultroute_exists()) + return 0; + + memset (&rt, '\0', sizeof (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 = gateway; + + rt.rt_flags = RTF_UP | 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 (int unit, int gateway) +{ + 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 = gateway; + + rt.rt_flags = RTF_UP | 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 (int unit, u_long his_adr) +{ + struct arpreq arpreq; + + memset (&arpreq, '\0', sizeof(arpreq)); +/* + * Get the hardware address of an interface on the same subnet + * as our local address. + */ + if (!get_ether_addr(his_adr, &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 = his_adr; + 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 (int unit, u_long his_adr) +{ + struct arpreq arpreq; + + memset (&arpreq, '\0', sizeof(arpreq)); + SET_SA_FAMILY(arpreq.arp_pa, AF_INET); + + ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = his_adr; + 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. + */ + +int get_ether_addr (u_long ipaddr, struct sockaddr *hwaddr) +{ + struct ifreq *ifr, *ifend, *ifp; + int i; + 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; + } + MAINDEBUG ((LOG_DEBUG, "proxy arp: scanning %d interfaces for IP %s", + ifc.ifc_len / sizeof(struct ifreq), ip_ntoa(ipaddr))); +/* + * Scan through looking for an interface with an Internet + * address on the same subnet as `ipaddr'. + */ + ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq)); + for (ifr = ifc.ifc_req; ifr < ifend; ifr++) { + 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)); + MAINDEBUG ((LOG_DEBUG, "proxy arp: examining interface %s", + 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 *) &ifreq.ifr_addr)->sin_addr.s_addr; + MAINDEBUG ((LOG_DEBUG, "proxy arp: interface addr %s mask %lx", + ip_ntoa(ina), ntohl(mask))); + if (((ipaddr ^ ina) & mask) != 0) + continue; + break; + } + } + + if (ifr >= ifend) + return 0; + + syslog(LOG_INFO, "found interface %s for proxy arp", ifreq.ifr_name); +/* + * Now get the hardware address. + */ + if (ioctl (s, SIOCGIFHWADDR, &ifreq) < 0) { + syslog(LOG_ERR, "SIOCGIFHWADDR(%s): %m", ifreq.ifr_name); + return 0; + } + + hwaddr->sa_family = ARPHRD_ETHER; +#ifndef old_ifr_hwaddr + memcpy (&hwaddr->sa_data, &ifreq.ifr_hwaddr, ETH_ALEN); +#else + memcpy (&hwaddr->sa_data, &ifreq.ifr_hwaddr.sa_data, ETH_ALEN); +#endif + + MAINDEBUG ((LOG_DEBUG, + "proxy arp: found hwaddr %02x:%02x:%02x:%02x:%02x:%02x", + (int) ((unsigned char *) &hwaddr->sa_data)[0], + (int) ((unsigned char *) &hwaddr->sa_data)[1], + (int) ((unsigned char *) &hwaddr->sa_data)[2], + (int) ((unsigned char *) &hwaddr->sa_data)[3], + (int) ((unsigned char *) &hwaddr->sa_data)[4], + (int) ((unsigned char *) &hwaddr->sa_data)[5])); + return 1; +} + +/* + * ppp_available - check whether the system has any ppp interfaces + * (in fact we check whether we can do an ioctl on ppp0). + */ + +int ppp_available(void) +{ + int s, ok; + struct ifreq ifr; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return 1; /* can't tell - maybe we're not root */ + + strncpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name)); + ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0; + close(s); + + return ok; +} + +int +logwtmp(line, name, host) + char *line, *name, *host; +{ + struct utmp ut; + + memset (&ut, 0, sizeof (ut)); + (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + (void)time(&ut.ut_time); + + pututline (&ut); /* Write the line to the proper place */ + endutent(); /* Indicate operation is complete */ +} diff --git a/usr.sbin/pppd/sys-str.c b/usr.sbin/pppd/sys-str.c new file mode 100644 index 000000000000..c197d4bf3cd2 --- /dev/null +++ b/usr.sbin/pppd/sys-str.c @@ -0,0 +1,730 @@ +/* + * 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 + +#include +#include +#include +#include + +#include "pppd.h" +#include "ppp.h" +#include + +#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; +static int pushed_ppp; + +extern int hungup; /* has the physical layer been disconnected? */ +extern int kdebugflag; + +#define PAI_FLAGS_B7_0 0x100 +#define PAI_FLAGS_B7_1 0x200 +#define PAI_FLAGS_PAR_EVEN 0x400 +#define PAI_FLAGS_PAR_ODD 0x800 +#define PAI_FLAGS_HIBITS 0xF00 + +/* + * ppp_available - check if this kernel supports PPP. + */ +int +ppp_available() +{ + int fd, ret; + + fd = open("/dev/tty", O_RDONLY, 0); + if (fd < 0) + return 1; /* can't find out - assume we have ppp */ + ret = ioctl(fd, I_FIND, "pppasync") >= 0; + close(fd); + return ret; +} + + +/* + * 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++; + } + + /* 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); + } + pushed_ppp = 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); + } + + /* Set debug flags in driver */ + if (ioctl(fd, SIOCSIFDEBUG, &kdebugflag) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFDEBUG): %m"); + } +} + +/* + * 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() +{ + int flags; + char *s; + + if (hungup) { + /* we can't push or pop modules after the stream has hung up */ + str_module_count = 0; + return; + } + + if (pushed_ppp) { + /* + * Check whether the link seems not to be 8-bit clean. + */ + if (ioctl(fd, SIOCGIFDEBUG, (caddr_t) &flags) == 0) { + s = NULL; + switch (~flags & PAI_FLAGS_HIBITS) { + case PAI_FLAGS_B7_0: + s = "bit 7 set to 1"; + break; + case PAI_FLAGS_B7_1: + s = "bit 7 set to 0"; + break; + case PAI_FLAGS_PAR_EVEN: + s = "odd parity"; + break; + case PAI_FLAGS_PAR_ODD: + s = "even parity"; + break; + } + if (s != NULL) { + syslog(LOG_WARNING, "Serial link is not 8-bit clean:"); + syslog(LOG_WARNING, "All received characters had %s", s); + } + } + } + + while (ioctl(fd, I_POP, 0) == 0) /* pop any we pushed */ + ; + pushed_ppp = 0; + + for (; str_module_count > 0; str_module_count--) { + if (ioctl(fd, I_PUSH, str_modules[str_module_count-1].modname)) { + syslog(LOG_WARNING, "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!")); + if (debug) + log_packet(p, len, "sent "); + + 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? 1: 0); + if(ioctl(fd, SIOCSIFCOMPPROT, &c) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFCOMPPROT): %m"); + quit(); + } + + c = (accomp? 1: 0); + if(ioctl(fd, SIOCSIFCOMPAC, &c) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFCOMPAC): %m"); + quit(); + } +} + + +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(unit, accm) + int unit; + ext_accm accm; +{ + if (ioctl(fd, SIOCSIFXASYNCMAP, accm) < 0 && errno != ENOTTY) + syslog(LOG_WARNING, "ioctl(set extended ACCM): %m"); +} + + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +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"); + } + + if (ioctl(fd, SIOCSIFRASYNCMAP, (caddr_t) &asyncmap) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFRASYNCMAP): %m"); + } + + c = 2 + (pcomp? 1: 0); + if(ioctl(fd, SIOCSIFCOMPPROT, &c) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFCOMPPROT): %m"); + } + + c = 2 + (accomp? 1: 0); + if (ioctl(fd, SIOCSIFCOMPAC, &c) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFCOMPAC): %m"); + } +} + +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp(u, vjcomp, cidcomp, maxcid) + int u, vjcomp, cidcomp, maxcid; +{ + char x; + + x = (vjcomp? 1: 0) + (cidcomp? 0: 2) + (maxcid << 4); + 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) + int 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) + int 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 u; + u_long 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) + int u; + u_long 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) + int u; + u_long 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) + int u; + u_long 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_WARNING, "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 (which also applies to logwtmp below): + * + * 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; +} + +#define WTMPFILE "/usr/adm/wtmp" + +int +logwtmp(line, name, host) + char *line, *name, *host; +{ + int fd; + struct stat buf; + struct utmp ut; + + if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0) + return; + if (!fstat(fd, &buf)) { + (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + (void)time(&ut.ut_time); + if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp)) + (void)ftruncate(fd, buf.st_size); + } + close(fd); +} diff --git a/usr.sbin/pppd/sys-ultrix.c b/usr.sbin/pppd/sys-ultrix.c new file mode 100644 index 000000000000..ca8f2a4a1875 --- /dev/null +++ b/usr.sbin/pppd/sys-ultrix.c @@ -0,0 +1,663 @@ +/* + * sys-ultrix.c - System-dependent procedures for setting up + * PPP interfaces on Ultrix systems. + * + * 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-ultrix.c,v 1.4 1994/05/25 06:30:49 paulus Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "pppd.h" +#include "ppp.h" + +static int initdisc = -1; /* Initial TTY discipline */ +extern int kdebugflag; + +/* + * establish_ppp - Turn the serial port into a ppp interface. + */ +void +establish_ppp() +{ + int pppdisc = PPPDISC; + int x; + + 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); + } + + /* + * Enable debug in the driver if requested. + */ + if (kdebugflag) { + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_WARNING, "ioctl (PPPIOCGFLAGS): %m"); + } else { + x |= (kdebugflag & 0xFF) * SC_DEBUG; + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) + syslog(LOG_WARNING, "ioctl(PPPIOCSFLAGS): %m"); + } + } +} + + +/* + * disestablish_ppp - Restore the serial port to normal operation. + * This shouldn't call die() because it's called from die(). + */ +void +disestablish_ppp() +{ + int x; + char *s; + + if (initdisc >= 0) { + /* + * Check whether the link seems not to be 8-bit clean. + */ + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) { + s = NULL; + switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) { + case SC_RCV_B7_0: + s = "bit 7 set to 1"; + break; + case SC_RCV_B7_1: + s = "bit 7 set to 0"; + break; + case SC_RCV_EVNP: + s = "odd parity"; + break; + case SC_RCV_ODDP: + s = "even parity"; + break; + } + if (s != NULL) { + syslog(LOG_WARNING, "Serial link is not 8-bit clean:"); + syslog(LOG_WARNING, "All received characters had %s", s); + } + } + 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 (debug) + log_packet(p, len, "sent "); + + 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_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(unit, accm) + int unit; + ext_accm accm; +{ + if (ioctl(fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) + syslog(LOG_WARNING, "ioctl(set extended ACCM): %m"); +} + + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void +ppp_recv_config(unit, mru, asyncmap, pcomp, accomp) + int unit, mru; + u_long asyncmap; + int pcomp, accomp; +{ + int x; + + if (ioctl(fd, PPPIOCSMRU, (caddr_t) &mru) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSMRU): %m"); + quit(); + } + 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(); + } +} + +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp(u, vjcomp, cidcomp, maxcid) + int u, vjcomp, cidcomp, maxcid; +{ + 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; + } + if (ioctl(fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + return 0; + } + return 1; +} + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int +sifup(u) +{ + struct ifreq ifr; + u_int x; + + 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; + } + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); + return 0; + } + x |= SC_ENABLE_IP; + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + return 0; + } + return 1; +} + +/* + * sifdown - Config the interface down and disable IP. + */ +int +sifdown(u) +{ + struct ifreq ifr; + u_int x; + int rv; + + rv = 1; + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); + rv = 0; + } else { + x &= ~SC_ENABLE_IP; + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); + rv = 0; + } + } + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m"); + rv = 0; + } else { + ifr.ifr_flags &= ~IFF_UP; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); + rv = 0; + } + } + return rv; +} + +/* + * 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); + +/* + * 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_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 + sizeof(struct sockaddr) +); + } + + if (ifr >= ifend) + return 0; + syslog(LOG_DEBUG, "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_DLI) { +/* && ifr->ifr_addr.sa_family == AF_LINK) { Per! Kolla !!! ROHACK */ + /* + * Found the link-level address - copy it out + */ + dla = (struct sockaddr_dl *)&ifr->ifr_addr; + hwaddr->sa_family = AF_UNSPEC; + BCOPY(dla, hwaddr->sa_data, sizeof(hwaddr->sa_data)); + return 1; + } + ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + sizeof(struct sockaddr) +); + } + + return 0; +} + + +/* + * ppp_available - check whether the system has any ppp interfaces + * (in fact we check whether we can do an ioctl on ppp0). + */ + +int +ppp_available() +{ + int s, ok; + struct ifreq ifr; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return 1; /* can't tell - maybe we're not root */ + + strncpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name)); + ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0; + close(s); + + return ok; +} + + +/* + Seems like strdup() is not part of string package in Ultrix. + If I understood the man-page on the sun this should work. + + Robert Olsson +*/ + +char *strdup( in ) char *in; +{ + char* dup; + if(! (dup = (char *) malloc( strlen( in ) +1 ))) return NULL; + (void) strcpy( dup, in ); + return dup; +} + +/* + * This logwtmp() implementation is subject to the following copyright: + * + * Copyright (c) 1988 The 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. + */ + +#define WTMPFILE "/usr/adm/wtmp" + +int +logwtmp(line, name, host) + char *line, *name, *host; +{ + int fd; + struct stat buf; + struct utmp ut; + + if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0) + return; + if (!fstat(fd, &buf)) { + (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + (void)time(&ut.ut_time); + if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp)) + (void)ftruncate(fd, buf.st_size); + } + close(fd); +} diff --git a/usr.sbin/pppd/upap.c b/usr.sbin/pppd/upap.c new file mode 100644 index 000000000000..61d22acbf286 --- /dev/null +++ b/usr.sbin/pppd/upap.c @@ -0,0 +1,561 @@ +/* + * 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.2 1994/04/11 07:13:44 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)); +} + +/* + * upap_printpkt - print the contents of a PAP packet. + */ +char *upap_codenames[] = { + "AuthReq", "AuthAck", "AuthNak" +}; + +int +upap_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __ARGS((void *, char *, ...)); + void *arg; +{ + int code, id, len; + int mlen, ulen, wlen; + char *user, *pwd, *msg; + u_char *pstart; + + if (plen < UPAP_HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < UPAP_HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(upap_codenames) / sizeof(char *)) + printer(arg, " %s", upap_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= UPAP_HEADERLEN; + switch (code) { + case UPAP_AUTHREQ: + if (len < 1) + break; + ulen = p[0]; + if (len < ulen + 2) + break; + wlen = p[ulen + 1]; + if (len < ulen + wlen + 2) + break; + user = (char *) (p + 1); + pwd = (char *) (p + ulen + 2); + p += ulen + wlen + 2; + len -= ulen + wlen + 2; + printer(arg, " user="); + print_string(user, ulen, printer, arg); + printer(arg, " password="); + print_string(pwd, wlen, printer, arg); + break; + case UPAP_AUTHACK: + case UPAP_AUTHNAK: + if (len < 1) + break; + mlen = p[0]; + if (len < mlen + 1) + break; + msg = (char *) (p + 1); + p += mlen + 1; + len -= mlen + 1; + printer(arg, "msg="); + print_string(msg, mlen, printer, arg); + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} diff --git a/usr.sbin/pppd/upap.h b/usr.sbin/pppd/upap.h new file mode 100644 index 000000000000..10c3414a0afa --- /dev/null +++ b/usr.sbin/pppd/upap.h @@ -0,0 +1,91 @@ +/* + * 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.2 1994/04/11 07:13:44 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)); +int upap_printpkt __ARGS((u_char *, int, + void (*) __ARGS((void *, char *, ...)), void *));