pax_global_header00006660000000000000000000000064113003505770014513gustar00rootroot0000000000000052 comment=da6956a56ea8550f33287fc86a960df8ce1b9af0 ppp-2.4.5/000077500000000000000000000000001130035057700123225ustar00rootroot00000000000000ppp-2.4.5/.gitignore000066400000000000000000000000471130035057700143130ustar00rootroot00000000000000*.orig *~ *.o *.so *.a *.cat8 Makefile ppp-2.4.5/Changes-2.3000066400000000000000000000433621130035057700141250ustar00rootroot00000000000000What was new in ppp-2.3.11. *************************** * Support for Solaris 8 has been added, including support for replumbing and IPV6. * The Solaris `snoop' utility should now work on ppp interfaces. * New hooks have been added - pap_logout_hook, ip_up_hook, and ip_down_hook. * A new `passprompt' plugin is included, thanks to Alan Curry, which makes it possible for pppd to call an external program to get the PAP password to send to the peer. * The error messages for the situation where authentication is required because the system has a default route have been improved. * There is a new connect_delay option which specifies how long pppd should pause after the connect script finishes. Previously this delay was fixed at 1 second. (This delay terminates as soon as pppd sees a valid PPP frame from the peer.) * The `hide-password' option is now the default, and there is a new `show-password' option to enable the printing of password strings in the debug output. * A fairly complete list of the names of PPP protocols has been added so that when pppd rejects a frame because its protocol is not supported, it can print the name of the unsupported protocol. * Synchronous serial lines are supported under Linux 2.3.x. * The bug where pppd would not recognize a modem hangup under Linux 2.3.x kernels has been fixed. What was new in ppp-2.3.10. *************************** * Pppd now supports `plugins', which are pieces of code (packaged as shared libraries) which can be loaded into pppd at runtime and which can affect its behaviour. The intention is that plugins provide a way for people to customize the behaviour of pppd for their own needs without needing to change the base pppd source. I have added some hooks into pppd (places where pppd will call a function pointer, if non-zero, to replace some of pppd's code) and I will be receptive to suggestions about places to add more hooks. Plugins are supported under Linux and Solaris at present. * We have a new maintainer for the Solaris port, Adi Masputra of Sun Microsystems, and he has updated the Solaris port so that it should work on 64-bit machines under Solaris 7 and later. * Pppd now has an `allow-ip' option, which takes an argument which is an IP address (or subnet) which peers are permitted to use without authenticating themselves. The argument takes the same form as each element of the allowed IP address list in the secrets files. The allow-ip option is privileged and may be specified multiple times. Using the allow-ip option should be cleaner than putting a line like `"" * "" address' in /etc/ppp/pap-secrets. * Chat can now substitute environment variables into the script. This is enabled by the -E flag. (Thanks to Andreas Arens for the patch.) * If the PAP username and password from the peer contains unprintable characters, they will be translated to a printable form before looking in the pap-secrets file. Characters >= 0x80 are translated to a M- form, and characters from 0 to 0x1f (and 0x7f as well) are translated to a ^X form. If this change causes you grief, let me know what would be a better translation. It appears that some peers send nulls or other control characters in their usernames and passwords. * Pppd has new `ktune' and `noktune' options, which enable/disable it to change kernel settings as appropriate. This is only implemented under Linux, and requires the /proc filesystem to be mounted. Under Linux, with the ktune option, pppd will enable IP forwarding in the kernel if the proxyarp option is used, and will enable the dynamic IP address kernel option in demand mode if the local IP address changes. * Pppd no longer requires a remote address to be specified for demand dialling. If none is specified, it will use a default value of 10.112.112.112+unit_number. (It will not propose this default to the peer.) * The default holdoff is now 0 if no connect script is given. * The IPV6 code from Tommi Komulainen, which I unfortunately only partially merged in to ppp-2.3.9, has been fixed and updated. * The linux compilation glitches should be fixed now. What was new in ppp-2.3.9. ************************** * Support for the new generic PPP layer under development for the Linux kernel. * You can now place extra options to apply to specific users at the end of the line with their password in the pap-secrets or chap-secrets file, separated from the IP address(es) with a "--" separator. These options are parsed after the peer is authenticated but before network protocol (IPCP, IPXCP) or CCP negotiation commences. * Pppd will apply the holdoff period if the link was terminated by the peer. It doesn't apply it if the link was terminated because the local pppd thought it was idle. * Synchronous support for Solaris has been added, thanks to John Morrison, and for FreeBSD, thanks to Paul Fulghum. * IPV6 support has been merged in, from Tommi Komulainen. At the moment it only supports Linux and it is not tested by me. * The `nodefaultip' option can be used in demand mode to say that pppd should not suggest its local IP address to the peer. * The `init' option has been added; this causes pppd to run a script to initialize the serial device (e.g. by sending an init string to the modem). Unlike the connect option, this can be used in a dial-in situation. (Thanks to Tobias Ringstrom.) * There is a new `logfile' option to send log messages to a file as well as syslog. * There is a new, privileged `linkname' option which sets a logical name for the link. Pppd will create a /var/run/ppp-.pid file containing its process ID. * There is a new `maxfail' option which specifies how many consecutive failed connection attempts are permitted before pppd will exit. The default value is 10, and 0 means infinity. :-) * Sundry bugs fixed. What was new in ppp-2.3.8. ************************** * The exit status of pppd will now indicate whether the link was successfully established, or if not, what error was encountered. * Pppd has two new options: fdlog will send log messages to file descriptor instead of standard output, and nofdlog will stop log messages from being sent to any file descriptor (they will still be sent to syslog). Pppd now will not send log messages to a file descriptor if the serial port is open on that file descriptor. * Pppd sets an environment variable called PPPLOGNAME for scripts that it runs, indicating the login name of the user who invoked pppd. * Pppd sets environment variables CONNECT_TIME, BYTES_SENT and BYTES_RCVD for the ip-down and auth-down scripts indicating the statistics for the connection just terminated. (CONNECT_TIME is in seconds.) * If the user has the serial device open on standard input and specifies a symbolic link to the serial device on the command line, pppd will detect this and behave correctly (i.e. not detach from its controlling terminal). Furthermore, if the serial port is open for reading and writing on standard input, pppd will assume that it is locked by its invoker and not lock it itself. * Chat now has a feature where if a string to be sent begins with an at sign (@), the rest of the string is taken as the name of a file (regular file or named pipe), and the actual string to send is taken from that file. * Support for FreeBSD-2.2.8 and 3.0 has been added, thanks to Paul Fulghum. * The Tru64 (aka Digital Unix aka OSF/1) port has been updated. * The system panics on Solaris SMP systems related to PPP connections being established and terminated should no longer occur. * Fixed quite a few bugs. What was new in ppp-2.3.7. ************************** * Pppd can now automatically allocate itself a pseudo-tty to use as the serial device. This has made three new options possible: - `pty script' will run `script' with its standard input and output connected to the master side of the pty. For example: pppd pty 'ssh -t server.my.net pppd' is a basic command for setting up a PPP link (tunnel) over ssh. (In practice you may need to specify other options such as IP addresses, etc.) - `notty' tells pppd to communicate over its standard input and output, which do not have to be a terminal device. - `record filename' tells pppd to record all of the characters sent and received over the serial device to a file called `filename'. The data is recorded in a tagged format with timestamps, which can be printed in a readable form with the pppdump program, which is included in this distribution. * Pppd now logs the connect time and number of bytes sent and received (at the level of the serial device) when the connection is terminated. * If you use the updetach or nodetach option, pppd will print its messages to standard output as well as logging them with syslog (provided of course pppd isn't using its standard input or output as its serial device). * There is a new `privgroup groupname' option (a privileged option). If the user running pppd is in group `groupname', s/he can use privileged options without restriction. * There is a new `receive-all' option, which causes pppd to accept all control characters, even the ones that the peer should be escaping (i.e. the receive asyncmap is 0). This is useful with some buggy peers. * The default asyncmap is now 0. * There is a new `sync' option, currently only implemented under Linux, which allows pppd to run on synchronous HDLC devices. * If a value for the device name or for the connect, disconnect, welcome or pty option is given in a privileged option file (i.e. /etc/ppp/options or a file loaded with the `call' option), it cannot be overridden by a non-privileged user. * Many bugs have been fixed, notably: - signals are not blocked unnecessarily, as they were in 2.3.6. - the usepeerdns option should work now. - the SPEED environment variable for scripts is set correctly. - the /etc/ppp/auth-down script is not run until auth-up completes. - the device is opened as root if it is the device on standard input. - pppd doesn't die with the ioctl(PPPIOCSASYNCMAP) error under linux if a hangup occurs at the wrong time. * Some error messages have been changed to be clearer (I hope :-) What was new in ppp-2.3.6. ************************** * Pppd now opens the tty device as the user (rather than as root) if the device name was given by the user, i.e. on the command line or in the ~/.ppprc file. If the device name was given in /etc/ppp/options or in a file loaded with the `call' option, the device is opened as root. * The default behaviour of pppd is now to let a peer which has not authenticated itself (e.g. your ISP) use any IP address to which the system does not already have a route. (This is currently only supported under Linux, Solaris and Digital Unix; on the other systems, the peer must now authenticate itself unless the noauth option is used.) * Added new option `usepeerdns', thanks to Nick Walker . If the peer supplies DNS addresses, these will be written to /etc/ppp/resolv.conf. The ip-up script can then be used to add these addresses to /etc/resolv.conf if desired (see the ip-up.local.add and ip-down.local.add files in the scripts directory). * The Solaris ppp driver should now work correctly on SMP systems. * Minor corrections so that the code can compile under Solaris 7, and under Linux with glibc-2.1. * The Linux kernel driver has been restructured for improved performance. * Pppd now won't start the ip-down script until the ip-up script has finished. What was new in ppp-2.3.5. ************************** * Minor corrections to the Digital UNIX and NetBSD ports. * A workaround to avoid tickling a bug in the `se' serial port driver on Sun PCI Ultra machines running Solaris. * Fixed a bug in the negotiation of the Microsoft WINS server address option. * Fixed a bug in the Linux port where it would fail for kernel versions above 2.1.99. What was new in ppp-2.3.4. ************************** * The NeXT port has been updated, thanks to Steve Perkins. * ppp-2.3.4 compiles and works under Solaris 2.6, using either gcc or cc. * With the Solaris, SVR4 and SunOS ports, you can control the choice of C compiler, C compiler options, and installation directories by editing the svr4/Makedefs or sunos4/Makedefs file. * Until now, we have been using the number 24 to identify Deflate compression in the CCP negotiations, which was the number in the draft RFC describing Deflate. The number actually assigned to Deflate is 26. The code has been changed to use 26, but to allow the use of 24 for now for backwards compatibility. (This can be disabled with the `nodeflatedraft' option to pppd.) * Fixed some bugs in the linux driver and deflate compressor which were causing compression problems, including corrupting long incompressible packets sometimes. * Fixes to the PAM and shadow password support in pppd, from Al Longyear and others. * Pppd now sets some environment variables for scripts it invokes (ip-up/down, auth-ip/down), giving information about the connection. The variables it sets are PEERNAME, IPLOCAL, IPREMOTE, UID, DEVICE, SPEED, and IFNAME. * Pppd now has an `updetach' option, which will cause it to detach from its controlling terminal once the link has come up (i.e. once it is available for IP traffic). What was new in ppp-2.3.3. ************************** * Fixed compilation problems under SunOS. * Fixed a bug introduced into chat in 2.3.2, and compilation problems introduced into the MS-CHAP implementation in 2.3.2. * The linux kernel driver has been updated for recent 2.1-series kernel changes, and it now will ask kerneld to load compression modules when required, if the kernel is configured to support kerneld. * Pppd should now compile correctly under linux on systems with glibc. What was new in ppp-2.3.2. ************************** * In 2.3.1, I made a change which was intended to make pppd able to detect loss of CD during or immediately after the connection script runs. Unfortunately, this had the side-effect that the connection script wouldn't work at all on some systems. This change has been reversed. * Fix compilation problems in the Linux kernel driver. What was new in ppp-2.3.1. ************************** * Enhancements to chat, thanks to Francis Demierre. Chat can now accept comments in the chat script file, and has new SAY, HANGUP, CLR_ABORT and CLR_REPORT keywords. * Fixed a bug which causes 2.3.0 to crash Solaris systems. * Bug-fixes and restructuring of the Linux kernel driver. * The holdoff behaviour of pppd has been changed slightly: now, if the link comes up for IP (or other network protocol) traffic, we consider that the link has been successfully established, and don't enforce the holdoff period after the link goes down. * Pppd should now correctly wait for CD (carrier detect) from the modem, even when the serial port initially had CLOCAL set, and it should also detect loss of CD during or immediately after the connection script runs. * Under linux, pppd will work with older 2.2.0* version kernel drivers, although demand-dialling is not supported with them. * Minor bugfixes for pppd. What was new in ppp-2.3. ************************ * Demand-dialling. Pppd now has a mode where it will establish the network interface immediately when it starts, but not actually bring the link up until it sees some data to be sent. Look for the demand option description in the pppd man page. Demand-dialling is not supported under Ultrix or NeXTStep. * Idle timeout. Pppd will optionally terminate the link if no data packets are sent or received within a certain time interval. * Pppd now runs the /etc/ppp/auth-up script, if it exists, when the peer successfully authenticates itself, and /etc/ppp/auth-down when the connection is subsequently terminated. This can be useful for accounting purposes. * A new packet compression scheme, Deflate, has been implemented. This uses the same compression method as `gzip'. This method is free of patent or copyright restrictions, and it achieves better compression than BSD-Compress. It does consume more CPU cycles for compression than BSD-Compress, but this shouldn't be a problem for links running at 100kbit/s or less. * There is no code in this distribution which is covered by Brad Clements' restrictive copyright notice. The STREAMS modules for SunOS and OSF/1 have been rewritten, based on the Solaris 2 modules, which were written from scratch without any Clements code. * Pppstats has been reworked to clean up the output format somewhat. It also has a new -d option which displays data rate in kbyte/s for those columns which would normally display bytes. * Pppd options beginning with - or + have been renamed, e.g. -ip became noip, +chap became require-chap, etc. The old options are still accepted for compatibility but may be removed in future. * Pppd now has some options (such as the new `noauth' option) which can only be specified if it is being run by root, or in an "privileged" options file: /etc/ppp/options or an options file in the /etc/ppp/peers directory. There is a new "call" option to read options from a file in /etc/ppp/peers, making it possible for non-root users to make unauthenticated connections, but only to certain trusted peers. My intention is to make the `auth' option the default in a future release. * Several minor new features have been added to pppd, including the maxconnect and welcome options. Pppd will now terminate the connection when there are no network control protocols running. The allowed IP address(es) field in the secrets files can now specify subnets (with a notation like 123.45.67.89/24) and addresses which are not acceptable (put a ! on the front). * Numerous bugs have been fixed (no doubt some have been introduced :-) Thanks to those who reported bugs in ppp-2.2. ppp-2.4.5/FAQ000066400000000000000000000624621130035057700126660ustar00rootroot00000000000000This is a list of Frequently Asked Questions about using ppp-2.x and their answers. ------------------------------------------------------------------------ Q: Can you give me an example of how I might set up my machine to dial out to an ISP? A: Here's an example for dialling out to an ISP via a modem on /dev/tty02. The modem uses hardware (CTS/RTS) flow control, and the serial port is run at 38400 baud. The ISP assigns our IP address. To configure pppd for this connection, create a file under /etc/ppp/peers called (say) my-isp containing the following: tty02 crtscts 38400 connect 'chat -v -f /etc/ppp/chat/my-isp' defaultroute The ppp connection is then initiated using the following command: pppd call my-isp Of course, if the directory containing pppd is not in your path, you will need to give the full pathname for pppd, for example, /usr/sbin/pppd. When you run this, pppd will use the chat program to dial the ISP and invoke its ppp service. Chat will read the file specified with -f, namely /etc/ppp/chat/my-isp, to find a list of strings to expect to receive, and strings to send. This file would contain something like this: ABORT "NO CARRIER" ABORT "NO DIALTONE" ABORT "ERROR" ABORT "NO ANSWER" ABORT "BUSY" ABORT "Username/Password Incorrect" "" "at" OK "at&d2&c1" OK "atdt2479381" "name:" "^Uusername" "word:" "\qpassword" "annex" "\q^Uppp" "Switching to PPP-ppp-Switching to PPP" You will need to change the details here. The first string on each line is a string to expect to receive; the second is the string to send. You can add or delete lines according to the dialog required to access your ISP's system. This example is for a modem with a standard AT command set, dialling out to an Annex terminal server. The \q toggles "quiet" mode; when quiet mode is on, the strings to be sent are replaced by ?????? in the log. You may need to go through the dialog manually using kermit or tip first to determine what should go in the script. To terminate the link, run the following script, called (say) kill-ppp: #!/bin/sh unit=ppp${1-0} piddir=/var/run if [ -f $piddir/$unit.pid ]; then kill -1 `cat $piddir/$unit.pid` fi On some systems (SunOS, Solaris, Ultrix), you will need to change /var/run to /etc/ppp. ------------------------------------------------------------------------ Q: Can you give me an example of how I could set up my office machine so I can dial in to it from home? A: Let's assume that the office machine is called "office" and is on a local ethernet subnet. Call the home machine "home" and give it an IP address on the same subnet as "office". We'll require both machines to authenticate themselves to each other. Set up the files on "office" as follows: /etc/ppp/options contains: auth # require the peer to authenticate itself lock # other options can go here if desired /etc/ppp/chap-secrets contains: home office "beware the frub-jub" home office home "bird, my son!%&*" - Set up a modem on a serial port so that users can dial in to the modem and get a login prompt. On "home", set up the files as follows: /etc/ppp/options contains the same as on "office". /etc/ppp/chap-secrets contains: home office "beware the frub-jub" - office home "bird, my son!%&*" office Create a file called /etc/ppp/peers/office containing the following: tty02 crtscts 38400 connect 'chat -v -f /etc/ppp/chat/office' defaultroute (You may need to change some of the details here.) Create the /etc/ppp/chat/office file containing the following: ABORT "NO CARRIER" ABORT "NO DIALTONE" ABORT "ERROR" ABORT "NO ANSWER" ABORT "BUSY" ABORT "ogin incorrect" "" "at" OK "at&d2&c1" OK "atdt2479381" "name:" "^Uusername" "word:" "\qpassword" "$" "\q^U/usr/sbin/pppd proxyarp" "~" You will need to change the details. Note that the "$" in the second-last line is expecting the shell prompt after a successful login - you may need to change it to "%" or something else. You then initiate the connection (from home) with the command: pppd call office ------------------------------------------------------------------------ Q: When I try to establish a connection, the modem successfully dials the remote system, but then hangs up a few seconds later. How do I find out what's going wrong? A: There are a number of possible problems here. The first thing to do is to ensure that pppd's messages are visible. Pppd uses the syslog facility to log messages which help to identify specific problems. Messages from pppd have facility "daemon" and levels ranging from "debug" to "error". Usually it is useful to see messages of level "notice" or higher on the console. To see these, find the line in /etc/syslog.conf which has /dev/console on the right-hand side, and add "daemon.notice" in the list on the left. The line will end up looking something like this: *.err;kern.debug;auth.notice;mail.crit;daemon.notice /dev/console Note that the whitespace is tabs, *not* spaces. If you are having problems, it may be useful to see messages of level "info" as well, in which case you would change "daemon.notice" to "daemon.info". In addition, it is useful to collect pppd's debugging output in a file - the debug option to pppd causes it to log the contents of all control packets sent and received in human-readable form. To do this, add a line like this to /etc/syslog.conf: daemon,local2.debug /etc/ppp/log and create an empty /etc/ppp/log file. When you change syslog.conf, you will need to send a HUP signal to syslogd to causes it to re-read syslog.conf. You can do this with a command like this (as root): kill -HUP `cat /etc/syslogd.pid` (On some systems, you need to use /var/run/syslog.pid instead of /etc/syslogd.pid.) After setting up syslog like this, you can use the -v flag to chat and the `debug' option to pppd to get more information. Try initiating the connection again; when it fails, inspect /etc/ppp/log to see what happened and where the connection failed. ------------------------------------------------------------------------ Q: When I try to establish a connection, I get an error message saying "Serial link is not 8-bit clean". Why? A: The most common cause is that your connection script hasn't successfully dialled out to the remote system and invoked ppp service there. Instead, pppd is talking to something (a shell or login process on the remote machine, or maybe just the modem) which is only outputting 7-bit characters. This can also arise with a modem which uses an AT command set if the dial command is issued before pppd is invoked, rather than within a connect script started by pppd. If the serial port is set to 7 bits/character plus parity when the last AT command is issued, the modem serial port will be set to the same setting. Note that pppd *always* sets the local serial port to 8 bits per character, with no parity and 1 stop bit. So you shouldn't need to issue an stty command before invoking pppd. ------------------------------------------------------------------------ Q: When I try to establish a connection, I get an error message saying "Serial line is looped back". Why? A: Probably your connection script hasn't successfully dialled out to the remote system and invoked ppp service there. Instead, pppd is talking to something which is just echoing back the characters it receives. The -v option to chat can help you find out what's going on. It can be useful to include "~" as the last expect string to chat, so chat won't return until it's seen the start of the first PPP frame from the remote system. Another possibility is that your phone connection has dropped for some obscure reason and the modem is echoing the characters it receives from your system. ------------------------------------------------------------------------ Q: I installed pppd successfully, but when I try to run it, I get a message saying something like "peer authentication required but no authentication files accessible". A: When pppd is used on a machine which already has a connection to the Internet (or to be more precise, one which has a default route in its routing table), it will require all peers to authenticate themselves. The reason for this is that if you don't require authentication, you have a security hole, because the peer can basically choose any IP address it wants, even the IP address of some trusted host (for example, a host mentioned in some .rhosts file). On machines which don't have a default route, pppd does not require the peer to authenticate itself. The reason is that such machines would mostly be using pppd to dial out to an ISP which will refuse to authenticate itself. In that case the peer can use any IP address as long as the system does not already have a route to that address. For example, if you have a local ethernet network, the peer can't use an address on that network. (In fact it could if it authenticated itself and it was permitted to use that address by the pap-secrets or chap-secrets file.) There are 3 ways around the problem: 1. If possible, arrange for the peer to authenticate itself, and create the necessary secrets files (/etc/ppp/pap-secrets and/or /etc/ppp/chap-secrets). 2. If the peer refuses to authenticate itself, and will always be using the same IP address, or one of a small set of IP addresses, you can create an entry in the /etc/ppp/pap-secrets file like this: "" * "" his-ip.his-domain his-other-ip.other-domain (that is, using the empty string for the client name and password fields). Of couse, you replace the 4th and following fields in the example above with the IP address(es) that the peer may use. You can use either hostnames or numeric IP addresses. 3. You can add the `noauth' option to the /etc/ppp/options file. Pppd will then not ask the peer to authenticate itself. If you do this, I *strongly* recommend that you remove the set-uid bit from the permissions on the pppd executable, with a command like this: chmod u-s /usr/sbin/pppd Then, an intruder could only use pppd maliciously if they had already become root, in which case they couldn't do any more damage using pppd than they could anyway. ------------------------------------------------------------------------ Q: What do I need to put in the secrets files? A: Three things: - secrets (i.e. passwords) to use for authenticating this host to other hosts (i.e., for proving our identity to others); - secrets which other hosts can use for authenticating themselves to us (i.e., so that they can prove their identity to us); and - information about which IP addresses other hosts may use, once they have authenticated themselves. There are two authentication files: /etc/ppp/pap-secrets, which contains secrets for use with PAP (the Password Authentication Protocol), and /etc/ppp/chap-secrets, which contains secrets for use with CHAP (the Challenge Handshake Authentication Protocol). Both files have the same simple format, which is as follows: - The file contains a series of entries, each of which contains a secret for authenticating one machine to another. - Each entry is contained on a single logical line. A logical line may be continued across several lines by placing a backslash (\) at the end of each line except the last. - Each entry has 3 or more fields, separated by whitespace (spaces and/or tabs). These fields are, in order: * The name of the machine that is authenticating itself (the "client"). * The name of the machine that is authenticating the client (the "server"). * The secret to be used for authenticating that client to that server. If this field begins with the at-sign `@', the rest of the field is taken as the name of a file containing the actual secret. * The 4th and any following fields list the IP address(es) that the client may use. - The file may contain comments, which begin with a `#' and continue to the end of the line. - Double quotes `"' should be used around a field if it contains characters with special significance, such as space, tab, `#', etc. - The backslash `\' may be used before characters with special significance (space, tab, `#', `\', etc.) to remove that significance. Some important points to note: * A machine can be *both* a "client" and a "server" for the purposes of authentication - this happens when both peers require the other to authenticate itself. So A would authenticate itself to B, and B would also authenticate itself to A (possibly using a different authentication protocol). * If both the "client" and the "server" are running ppp-2.x, they need to have a similar entry in the appropriate secrets file; the first two fields are *not* swapped on the client, compared to the server. So the client might have an entry like this: ay bee "our little secret" - and the corresponding entry on the server could look like this: ay bee "our little secret" 123.45.67.89 ------------------------------------------------------------------------ Q: Explain about PAP and CHAP? PAP stands for the Password Authentication Protocol. With this protocol, the "client" (the machine that needs to authenticate itself) sends its name and a password, in clear text, to the "server". The server returns a message indicating whether the name and password are valid. CHAP stands for the Challenge Handshake Authentication Protocol. It is designed to address some of the deficiencies and vulnerabilities of PAP. Like PAP, it is based on the client and server having a shared secret, but the secret is never passed in clear text over the link. Instead, the server sends a "challenge" - an arbitrary string of bytes, and the client must prove it knows the shared secret by generating a hash value from the challenge combined with the shared secret, and sending the hash value back to the server. The server also generates the hash value and compares it with the value received from the client. At a practical level, CHAP can be slightly easier to configure than PAP because the server sends its name with the challenge. Thus, when finding the appropriate secret in the secrets file, the client knows the server's name. In contrast, with PAP, the client has to find its password (i.e. the shared secret) before it has received anything from the server. Thus, it may be necessary to use the `remotename' option to pppd when using PAP authentication so that it can select the appropriate secret from /etc/ppp/pap-secrets. Microsoft also has a variant of CHAP which uses a different hashing arrangement from normal CHAP. There is a client-side (authenticatee) implementation of Microsoft's CHAP in ppp-2.3; see README.MSCHAP80. In ppp-2.4.2, server-side (authenticator) support was added as well as support for Microsoft CHAP v2; see README.MSCHAP81. ------------------------------------------------------------------------ Q: When the modem hangs up, without the remote system having terminated the connection properly, pppd does not notice the hangup, but just keeps running. How do I get pppd to notice the hangup and exit? A: Pppd detects modem hangup by looking for an end-of-file indication from the serial driver, which should be generated when the CD (carrier detect) signal on the serial port is deasserted. For this to work: - The modem has to be set to assert CD when the connection is made and deassert it when the phone line hangs up. Usually the AT&C1 modem command sets this mode. - The cable from the modem to the serial port must connect the CD signal (on pin 8). - Some serial drivers have a "software carrier detect" mode, which must be *disabled*. The method of doing this varies between systems. Under SunOS, use the ttysoftcar command. Under NetBSD, edit /etc/ttys to remove the "softcar" flag from the line for the serial port, and run ttyflags. ------------------------------------------------------------------------ Q: Why should I use PPP compression (BSD-Compress or Deflate) when my modem already does V.42 compression? Won't it slow the CPU down a lot? A: Using PPP compression is preferable, especially when using modems over phone lines, for the following reasons: - The V.42 compression in the modem isn't very strong - it's an LZW technique (same as BSD-Compress) with a 10, 11 or 12 bit code size. With BSD-Compress you can use a code size of up to 15 bits and get much better compression, or you can use Deflate and get even better compression ratios. - I have found that enabling V.42 compression in my 14.4k modem increases the round-trip time for a character to be sent, echoed and returned by around 40ms, from 160ms to 200ms (with error correction enabled). This is enough to make it feel less responsive on rlogin or telnet sessions. Using PPP compression adds less than 5ms (small enough that I couldn't measure it reliably). I admit my modem is a cheapie and other modems may well perform better. - While compression and decompression do require some CPU time, they reduce the amount of time spent in the serial driver to transmit a given amount of data. Many machines require an interrupt for each character sent or received, and the interrupt handler can take a significant amount of CPU time. So the increase in CPU load isn't as great as you might think. My measurements indicate that a system with a 33MHz 486 CPU should be able to do Deflate compression for serial link speeds of up to 100kb/s or more. It depends somewhat on the type of data, of course; for example, when compressing a string of nulls with Deflate, it's hard to get a high output data rate from the compressor, simply because it compresses strings of nulls so well that it has to eat a very large amount of input data to get each byte of output. ------------------------------------------------------------------------ Q: I get messages saying "Unsupported protocol (...) received". What do these mean? A: If you only get one or two when pppd starts negotiating with the peer, they mean that the peer wanted to negotiate some PPP protocol that pppd doesn't understand. This doesn't represent a problem, it simply means that there is some functionality that the peer supports that pppd doesn't, so that functionality can't be used. If you get them sporadically while the link is operating, or if the protocol numbers (in parentheses) don't correspond to any valid PPP protocol that the peer might be using, then the problem is probably that characters are getting corrupted on the receive side, or that extra characters are being inserted into the receive stream somehow. If this is happening, most packets that get corrupted should get discarded by the FCS (Frame Check Sequence, a 16-bit CRC) check, but a small number may get through. One possibility may be that you are receiving broadcast messages on the remote system which are being sent over your serial link. Another possibility is that your modem is set for XON/XOFF (software) flow control and is inserting ^Q and ^S characters into the receive data stream. ------------------------------------------------------------------------ Q: I get messages saying "Protocol-Reject for unsupported protocol ...". What do these mean? A: This is the other side of the previous question. If characters are getting corrupted on the way to the peer, or if your system is inserting extra bogus characters into the transmit data stream, the peer may send protocol-reject messages to you, resulting in the above message (since your pppd doesn't recognize the protocol number either.) ------------------------------------------------------------------------ Q: I get a message saying something like "ioctl(TIOCSETD): Operation not permitted". How do I fix this? A: This is because pppd is not running as root. If you have not installed pppd setuid-root, you will have to be root to run it. If you have installed pppd setuid-root and you still get this message, it is probably because your shell is using some other copy of pppd than the installed one - for example, if you are in the pppd directory where you've just built pppd and your $PATH has . before /usr/sbin (or wherever pppd gets installed). ------------------------------------------------------------------------ Q: Has your package been ported to HP/UX or IRIX or AIX? A: No. I don't have access to systems running HP/UX or AIX. No-one has volunteered to port it to HP/UX. I had someone who did a port for AIX 4.x, but who is no longer able to maintain it. And apparently AIX 3.x is quite different, so it would need a separate port. IRIX includes a good PPP implementation in the standard distribution, as far as I know. ------------------------------------------------------------------------ Q: Under SunOS 4, when I try to modload the ppp modules, I get the message "can't open /dev/vd: No such device". A: First check in /dev that there is an entry like this: crw-r--r-- 1 root 57, 0 Oct 2 1991 vd If not, make one (mknod /dev/vd c 57 0). If the problem still exists, probably your kernel has been configured without the vd driver included. The vd driver is needed for loadable module support. First, identify the config file that was used. When you boot your machine, or if you run /etc/dmesg, you'll see a line that looks something like this: SunOS Release 4.1.3_U1 (CAP_XBOX) #7: Thu Mar 21 15:31:56 EST 1996 ^^^^^^^^ this is the config file name The config file will be in the /sys/`arch -k`/conf directory (arch -k should return sun4m for a SparcStation 10, sun3x for a Sun 3/80, etc.). Look in there for a line saying "options VDDRV". If that line isn't present (or is commented out), add it (or uncomment it). You then need to rebuild the kernel as described in the SunOS manuals. Basically you need to run config and make like this: /usr/etc/config CAP_XBOX cd ../CAP_XBOX make (replacing the string CAP_XBOX by the name of the config file for your kernel, of course). Then copy the new kernel to /: mv /vmunix /vmunix.working cp vmunix / and reboot. Modload should then work. ------------------------------------------------------------------------ Q: I'm running Linux (or NetBSD or FreeBSD), and my system comes with PPP already. Should I consider installing this package? Why? A: The PPP that is already installed in your system is (or is derived from) some version of this PPP package. You can find out what version of this package is already installed with the command "pppd --help". If this is older than the latest version, you may wish to install the latest version so that you can take advantage of the new features or bug fixes. ------------------------------------------------------------------------ Q: I'm running pppd in demand mode, and I find that pppd often dials out unnecessarily when I try to make a connection within my local machine or with a machine on my local LAN. What can I do about this? A: Very often the cause of this is that a program is trying to contact a nameserver to resolve a hostname, and the nameserver (specified in /etc/resolv.conf, usually) is on the far side of the ppp link. You can try executing a command such as `ping myhost' (where myhost is the name of the local machine, or some other machine on a local LAN), to see whether that starts the ppp link. If it does, check the setup of your /etc/hosts file to make sure you have the local machine and any hosts on your local LAN listed, and /etc/resolv.conf and/or /etc/nsswitch.conf files to make sure you resolve hostnames from /etc/hosts if possible before trying to contact a nameserver. ------------------------------------------------------------------------ Q: Since I installed ppp-2.3.6, dialin users to my server have been getting this message when they run pppd: peer authentication required but no suitable secret(s) found for authenticating any peer to us (ispserver) A: In 2.3.6, the default is to let an unauthenticated peer only use IP addresses to which the machine doesn't already have a route. So on a machine with a default route, everyone has to authenticate. If you really don't want that, you can put `noauth' in the /etc/ppp/options file. Note that there is then no check on who is using which IP address. IMHO, this is undesirably insecure, but I guess it may be tolerable as long as you don't use any .rhosts files or anything like that. I recommend that you require dialin users to authenticate, even if just with PAP using their login password (using the `login' option to pppd). If you do use `noauth', you should at least have a pppusers group and set the permissions on pppd to allow only user and group to execute it. ------------------------------------------------------------------------ Q: When running pppd as a dial-in server, I often get the message "LCP: timeout sending Config-Requests" from pppd. It seems to be random, but dial-out always works fine. What is wrong? A: Most modern modems auto-detects the speed of the serial line between the modem and the computer. This auto-detection occurs when the computer sends characters to the modem, when the modem is in command mode. It does not occur when the modem is in data mode. Thus, if you send commands to the modem at 2400 bps, and then change the serial port speed to 115200 bps, the modem will not detect this change until something is transmitted from the computer to the modem. When running pppd in dial-in mode (i.e. without a connect script), pppd sets the speed of the serial port, but does not transmit anything. If the modem was already running at the specified speed, everything is fine, but if not, you will just receive garbage from the modem. To cure this, use an init script such as the following: pppd ttyS0 115200 modem crtscts init "chat '' AT OK" To reset the modem and enable auto-answer, use: pppd ttyS0 115200 modem crtscts init "chat '' ATZ OK ATS0=1 OK" ppp-2.4.5/PLUGINS000066400000000000000000000304261130035057700133730ustar00rootroot00000000000000Starting with version 2.3.10, pppd includes support for `plugins' - pieces of code which can be loaded into pppd at runtime and which can affect its behaviour in various ways. The idea of plugins is to provide a way for people to customize the behaviour of pppd without having to either apply local patches to each version or get their patches accepted into the standard distribution. A plugin is a standard shared library object, typically with a name ending in .so. They are loaded using the standard dlopen() library call, so plugins are only supported on systems which support shared libraries and the dlopen call. At present pppd is compiled with plugin support only under Linux and Solaris. Plugins are loaded into pppd using the `plugin' option, which takes one argument, the name of a shared object file. The plugin option is a privileged option. If the name given does not contain a slash, pppd will look in the /usr/lib/pppd/ directory for the file, where is the version number of pppd, for example, 2.4.2. I suggest that you either give the full path name of the shared object file or just the base name; if you don't, it may be possible for unscrupulous users to substitute another shared object file for the one you mean to load, e.g. by setting the LD_LIBRARY_PATH variable. Plugins are usually written in C and compiled and linked to a shared object file in the appropriate manner for your platform. Using gcc under Linux, a plugin called `xyz' could be compiled and linked with the following commands: gcc -c -O xyz.c gcc -shared -o xyz.so xyz.o There are some example plugins in the pppd/plugins directory in the ppp distribution. Currently there is one example, minconn.c, which implements a `minconnect' option, which specifies a minimum connect time before the idle timeout applies. Plugins can access global variables within pppd, so it is useful for them to #include "pppd.h" from the pppd source directory. Every plugin must contain a global procedure called `plugin_init'. This procedure will get called (with no arguments) immediately after the plugin is loaded. Every plugin should also contain a variable called pppd_version declared as follows: char pppd_version[] = VERSION; If this declaration is included, pppd will not load the module if its version number differs from that compiled into the plugin binary. Plugins can affect the behaviour of pppd in at least four ways: 1. They can add extra options which pppd will then recognize. This is done by calling the add_options() procedure with a pointer to an array of option_t structures. The last entry in the array must have its name field set to NULL. 2. Pppd contains `hook' variables which are procedure pointers. If a given hook is not NULL, pppd will call the procedure it points to at the appropriate point in its processing. The plugin can set any of these hooks to point to its own procedures. See below for a description of the hooks which are currently implemented. 3. Plugin code can call any global procedures and access any global variables in pppd. 4. Plugins can register procedures to be called when particular events occur, using the `notifier' mechanism in pppd. The differences between hooks and notifiers are that a hook will only call one function, whereas a notifier can call an arbitrary number, and that a hook usually returns some value to pppd, whereas a notifier function returns nothing. Here is a list of the currently implemented hooks in pppd. int (*idle_time_hook)(struct ppp_idle *idlep); The idle_time_hook is called when the link first comes up (i.e. when the first network protocol comes up) and at intervals thereafter. On the first call, the idlep parameter is NULL, and the return value is the number of seconds before pppd should check the link activity, or 0 if there is to be no idle timeout. On subsequent calls, idlep points to a structure giving the number of seconds since the last packets were sent and received. If the return value is > 0, pppd will wait that many seconds before checking again. If it is <= 0, that indicates that the link should be terminated due to lack of activity. int (*holdoff_hook)(void); The holdoff_hook is called when an attempt to bring up the link fails, or the link is terminated, and the persist or demand option was used. It returns the number of seconds that pppd should wait before trying to reestablish the link (0 means immediately). int (*pap_check_hook)(void); int (*pap_passwd_hook)(char *user, char *passwd); int (*pap_auth_hook)(char *user, char *passwd, char **msgp, struct wordlist **paddrs, struct wordlist **popts); void (*pap_logout_hook)(void); These hooks are designed to allow a plugin to replace the normal PAP password processing in pppd with something different (e.g. contacting an external server). The pap_check_hook is called to check whether there is any possibility that the peer could authenticate itself to us. If it returns 1, pppd will ask the peer to authenticate itself. If it returns 0, pppd will not ask the peer to authenticate itself (but if authentication is required, pppd may exit, or terminate the link before network protocol negotiation). If it returns -1, pppd will look in the pap-secrets file as it would normally. The pap_passwd_hook is called to determine what username and password pppd should use in authenticating itself to the peer with PAP. The user string will already be initialized, by the `user' option, the `name' option, or from the hostname, but can be changed if necessary. MAXNAMELEN bytes of space are available at *user, and MAXSECRETLEN bytes of space at *passwd. If this hook returns 0, pppd will use the values at *user and *passwd; if it returns -1, pppd will look in the pap-secrets file, or use the value from the +ua or password option, as it would normally. The pap_auth_hook is called to determine whether the username and password supplied by the peer are valid. user and passwd point to null-terminated strings containing the username and password supplied by the peer, with non-printable characters converted to a printable form. The pap_auth_hook function should set msg to a string to be returned to the peer and return 1 if the username/password was valid and 0 if not. If the hook returns -1, pppd will look in the pap-secrets file as usual. If the username/password was valid, the hook can set *paddrs to point to a wordlist containing the IP address(es) which the peer is permitted to use, formatted as in the pap-secrets file. It can also set *popts to a wordlist containing any extra options for this user which pppd should apply at this point. The pap_logout_hook is called when the link is terminated, instead of pppd's internal `plogout' function. It can be used for accounting purposes. This hook is deprecated and will be replaced by a notifier. int (*chap_check_hook)(void); int (*chap_passwd_hook)(char *user, char *passwd); int (*chap_verify_hook)(char *name, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space) These hooks are designed to allow a plugin to replace the normal CHAP password processing in pppd with something different (e.g. contacting an external server). The chap_check_hook is called to check whether there is any possibility that the peer could authenticate itself to us. If it returns 1, pppd will ask the peer to authenticate itself. If it returns 0, pppd will not ask the peer to authenticate itself (but if authentication is required, pppd may exit, or terminate the link before network protocol negotiation). If it returns -1, pppd will look in the chap-secrets file as it would normally. The chap_passwd_hook is called to determine what password pppd should use in authenticating itself to the peer with CHAP. The user string will already be initialized, by the `user' option, the `name' option, or from the hostname, but can be changed if necessary. This hook is called only if pppd is a client, not if it is a server. MAXSECRETLEN bytes of space are available at *passwd. If this hook returns 0, pppd will use the value *passwd; if it returns -1, pppd will fail to authenticate. The chap_verify_hook is called to determine whether the peer's response to our CHAP challenge is valid -- it should return 1 if valid or 0 if not. The parameters are: * name points to a null-terminated string containing the username supplied by the peer, or the remote name specified with the "remotename" option. * ourname points to a null-terminated string containing the name of the local machine (the hostname, or the name specified with the "name" option). * id is the value of the id field from the challenge. * digest points to a chap_digest_type struct, which contains an identifier for the type of digest in use plus function pointers for functions for dealing with digests of that type. * challenge points to the challenge as a counted string (length byte followed by the actual challenge bytes). * response points to the response as a counted string. * message points to an area of message_space bytes in which to store any message that should be returned to the peer. int (*null_auth_hook)(struct wordlist **paddrs, struct wordlist **popts); This hook allows a plugin to determine what the policy should be if the peer refuses to authenticate when it is requested to. If the return value is 0, the link will be terminated; if it is 1, the connection is allowed to proceed, and in this case *paddrs and *popts can be set as for pap_auth_hook, to specify what IP addresses are permitted and any extra options to be applied. If the return value is -1, pppd will look in the pap-secrets file as usual. void (*ip_choose_hook)(u_int32_t *addrp); This hook is called at the beginning of IPCP negotiation. It gives a plugin the opportunity to set the IP address for the peer; the address should be stored in *addrp. If nothing is stored in *addrp, pppd will determine the peer's address in the usual manner. int (*allowed_address_hook)(u_int32_t addr) This hook is called to see if a peer is allowed to use the specified address. If the hook returns 1, the address is accepted. If it returns 0, the address is rejected. If it returns -1, the address is verified in the normal away against the appropriate options and secrets files. void (*snoop_recv_hook)(unsigned char *p, int len) void (*snoop_send_hook)(unsigned char *p, int len) These hooks are called whenever pppd receives or sends a packet. The packet is in p; its length is len. This allows plugins to "snoop in" on the pppd conversation. The hooks may prove useful in implmenting L2TP. void (*multilink_join_hook)(); This is called whenever a new link completes LCP negotiation and joins the bundle, if we are doing multilink. A plugin registers itself with a notifier by declaring a procedure of the form: void my_notify_proc(void *opaque, int arg); and then registering the procedure with the appropriate notifier with a call of the form add_notifier(&interesting_notifier, my_notify_proc, opaque); The `opaque' parameter in the add_notifier call will be passed to my_notify_proc every time it is called. The `arg' parameter to my_notify_proc depends on the notifier. A notify procedure can be removed from the list for a notifier with a call of the form remove_notifier(&interesting_notifier, my_notify_proc, opaque); Here is a list of the currently-implemented notifiers in pppd. * pidchange. This notifier is called in the parent when pppd has forked and the child is continuing pppd's processing, i.e. when pppd detaches from its controlling terminal. The argument is the pid of the child. * phasechange. This is called when pppd moves from one phase of operation to another. The argument is the new phase number. * exitnotify. This is called just before pppd exits. The argument is the status with which pppd will exit (i.e. the argument to exit()). * sigreceived. This is called when a signal is received, from within the signal handler. The argument is the signal number. * ip_up_notifier. This is called when IPCP has come up. * ip_down_notifier. This is called when IPCP goes down. * auth_up_notifier. This is called when the peer has successfully authenticated itself. * link_down_notifier. This is called when the link goes down. ## $Id: PLUGINS,v 1.8 2008/06/15 07:02:18 paulus Exp $ ## ppp-2.4.5/README000066400000000000000000000260121130035057700132030ustar00rootroot00000000000000This is the README file for ppp-2.4, a package which implements the Point-to-Point Protocol (PPP) to provide Internet connections over serial lines. Introduction. ************* The Point-to-Point Protocol (PPP) provides a standard way to establish a network connection over a serial link. At present, this package supports IP and IPV6 and the protocols layered above them, such as TCP and UDP. The Linux port of this package also has support for IPX. This PPP implementation consists of two parts: - Kernel code, which establishes a network interface and passes packets between the serial port, the kernel networking code and the PPP daemon (pppd). This code is implemented using STREAMS modules on Solaris, and as a line discipline under Linux. - The PPP daemon (pppd), which negotiates with the peer to establish the link and sets up the ppp network interface. Pppd includes support for authentication, so you can control which other systems may make a PPP connection and what IP addresses they may use. The platforms supported by this package are Linux and Solaris. I have code for NeXTStep, FreeBSD, SunOS 4.x, SVR4, Tru64 (Digital Unix), AIX and Ultrix but no active maintainers for these platforms. Code for all of these except AIX is included in the ppp-2.3.11 release. The kernel code for Linux is no longer distributed with this package, since the relevant kernel code is in the official Linux kernel source (and has been for many years) and is included in all reasonably modern Linux distributions. The Linux kernel code supports using PPP over things other than serial ports, such as PPP over Ethernet and PPP over ATM. Installation. ************* The file SETUP contains general information about setting up your system for using PPP. There is also a README file for each supported system, which contains more specific details for installing PPP on that system. The supported systems, and the corresponding README files, are: Linux README.linux Solaris README.sol2 In each case you start by running the ./configure script. This works out which operating system you are using and creates the appropriate makefiles. You then run `make' to compile the user-level code, and (as root) `make install' to install the user-level programs pppd, chat and pppstats. N.B. Since 2.3.0, leaving the permitted IP addresses column of the pap-secrets or chap-secrets file empty means that no addresses are permitted. You need to put a "*" in that column to allow the peer to use any IP address. (This only applies where the peer is authenticating itself to you, of course.) What's new in ppp-2.4.5. ************************ * Under Linux, pppd can now operate in a mode where it doesn't request the peer's IP address, as some peers refuse to supply an IP address. Since Linux supports device routes as well as gateway routes, it's possible to have no remote IP address assigned to the ppp interface and still route traffic over it. * Pppd now works better with 3G modems that do strange things such as sending IPCP Configure-Naks with the same values over and over again. * The PPP over L2TP plugin is included, which works with the pppol2tp PPP channel code in the Linux kernel. This allows pppd to be used to set up tunnels using the Layer 2 Tunneling Protocol. * A new 'enable-session' option has been added, which enables session accounting via PAM or wtwp/wtmpx, as appropriate. See the pppd man page for details. * Several bugs have been fixed. What was new in ppp-2.4.4. ************************** * Pppd will now run /etc/ppp/ip-pre-up, if it exists, after creating the ppp interface and configuring its IP addresses but before bringing it up. This can be used, for example, for adding firewall rules for the interface. * Lots of bugs fixed, particularly in the area of demand-dialled and persistent connections. * The rp-pppoe plugin now accepts any interface name (that isn't an existing pppd option name) without putting "nic-" on the front of it, not just eth*, nas*, tap* and br*. What was new in ppp-2.4.3. ************************** * The configure script now accepts --prefix and --sysconfdir options. These default to /usr/local and /etc. If you want pppd put in /usr/sbin as before, use ./configure --prefix=/usr. * Doing `make install' no longer puts example configuration files in /etc/ppp. Use `make install-etcppp' if you want that. * The code has been updated to work with version 0.8.3 of libpcap. Unfortunately the libpcap maintainers removed support for the "inbound" and "outbound" keywords on PPP links, meaning that if you link pppd with libpcap-0.8.3, you can't use those keywords in the active-filter and pass-filter expressions. The support has been reinstated in the CVS version and should be in future libpcap releases. If you need the in/outbound keywords, use a later release than 0.8.3, or get the CVS version from http://www.tcpdump.org. * There is a new option, child-timeout, which sets the length of time that pppd will wait for child processes (such as the command specified with the pty option) to exit before exiting itself. It defaults to 5 seconds. After the timeout, pppd will send a SIGTERM to any remaining child processes and exit. A value of 0 means no timeout. * Various bugs have been fixed, including some CBCP packet parsing bugs that could lead to the peer being able to crash pppd if CBCP support is enabled. * Various fixes and enhancements to the radius and rp-pppoe plugins have been added. * There is a new winbind plugin, from Andrew Bartlet of the Samba team, which provides the ability to authenticate the peer against an NT domain controller using MS-CHAP or MS-CHAPV2. * There is a new pppoatm plugin, by various authors, sent in by David Woodhouse. * The multilink code has been substantially reworked. The first pppd for a bundle still controls the ppp interface, but it doesn't exit until all the links in the bundle have terminated. If the first pppd is signalled to exit, it signals all the other pppds controlling links in the bundle. * The TDB code has been updated to the latest version. This should eliminate the problem that some people have seen where the database file (/var/run/pppd.tdb) keeps on growing. Unfortunately, however, the new code uses an incompatible database format. For this reason, pppd now uses /var/run/pppd2.tdb as the database filename. What was new in ppp-2.4.2. ************************** * The CHAP code has been rewritten. Pppd now has support for MS-CHAP V1 and V2 authentication, both as server and client. The new CHAP code is cleaner than the old code and avoids some copyright problems that existed in the old code. * MPPE (Microsoft Point-to-Point Encryption) support has been added, although the current implementation shouldn't be considered completely secure. (There is no assurance that the current code won't ever transmit an unencrypted packet.) * James Carlson's implementation of the Extensible Authentication Protocol (EAP) has been added. * Support for the Encryption Control Protocol (ECP) has been added. * Some new plug-ins have been included: - A plug-in for kernel-mode PPPoE (PPP over Ethernet) - A plug-in for supplying the PAP password over a pipe from another process - A plug-in for authenticating using a Radius server. * Updates and bug-fixes for the Solaris port. * The CBCP (Call Back Control Protocol) code has been updated. There are new options `remotenumber' and `allow-number'. * Extra hooks for plugins to use have been added. * There is now a `maxoctets' option, which causes pppd to terminate the link once the number of bytes passed on the link exceeds a given value. * There are now options to control whether pppd can use the IPCP IP-Address and IP-Addresses options: `ipcp-no-address' and `ipcp-no-addresses'. * Fixed several bugs, including potential buffer overflows in chat. What was new in ppp-2.4.1. ************************** * Pppd can now print out the set of options that are in effect. The new `dump' option causes pppd to print out the option values after option parsing is complete. The `dryrun' option causes pppd to print the options and then exit. * The option parsing code has been fixed so that options in the per-tty options file are parsed correctly, and don't override values from the command line in most cases. * The plugin option now looks in /usr/lib/pppd/ (for example, /usr/lib/pppd/2.4.1b1) for shared objects for plugins if there is no slash in the plugin name. * When loading a plugin, pppd will now check the version of pppd for which the plugin was compiled, and refuse to load it if it is different to pppd's version string. To enable this, the plugin source needs to #include "pppd.h" and have a line saying: char pppd_version[] = VERSION; * There is a bug in zlib, discovered by James Carlson, which can cause kernel memory corruption if Deflate is used with the lowest setting, 8. As a workaround pppd will now insist on using at least 9. * Pppd should compile on Solaris and SunOS again. * Pppd should now set the MTU correctly on demand-dialled interfaces. What was new in ppp-2.4.0. ************************** * Multilink: this package now allows you to combine multiple serial links into one logical link or `bundle', for increased bandwidth and reduced latency. This is currently only supported under the 2.4.x and later Linux kernels. * All the pppd processes running on a system now write information into a common database. I used the `tdb' code from samba for this. * New hooks have been added. For a list of the changes made during the 2.3 series releases of this package, see the Changes-2.3 file. Compression methods. ******************** This package supports two packet compression methods: Deflate and BSD-Compress. Other compression methods which are in common use include Predictor, LZS, and MPPC. These methods are not supported for two reasons - they are patent-encumbered, and they cause some packets to expand slightly, which pppd doesn't currently allow for. BSD-Compress and Deflate (which uses the same algorithm as gzip) don't ever expand packets. Contacts. ********* The comp.protocols.ppp newsgroup is a useful place to get help if you have trouble getting your ppp connections to work. Please do not send me questions of the form "please help me get connected to my ISP" - I'm sorry, but I simply do not have the time to answer all the questions like this that I get. If you find bugs in this package, please report them to the maintainer for the port for the operating system you are using: Linux Paul Mackerras Solaris James Carlson Copyrights: *********** All of the code can be freely used and redistributed. The individual source files each have their own copyright and permission notice. Pppd, pppstats and pppdump are under BSD-style notices. Some of the pppd plugins are GPL'd. Chat is public domain. Distribution: ************* The primary site for releases of this software is: ftp://ftp.samba.org/pub/ppp/ ppp-2.4.5/README.MPPE000066400000000000000000000054541130035057700137520ustar00rootroot00000000000000PPP Support for MPPE (Microsoft Point to Point Encryption) ========================================================== Frank Cusack frank@google.com Mar 19, 2002 Updated by Paul Mackerras, Sep 2008 DISCUSSION MPPE is Microsoft's encryption scheme for PPP links. It is pretty much solely intended for use with PPP over Internet links -- if you have a true point to point link you have little need for encryption. It is generally used with PPTP. MPPE is negotiated within CCP (Compression Control Protocol) as option 18. In order for MPPE to work, both peers must agree to do it. This complicates things enough that I chose to implement it as strictly a binary option, off by default. If you turn it on, all other compression options are disabled and MPPE *must* be negotiated successfully in both directions (CCP is unidirectional) or the link will be disconnected. I think this is reasonable since, if you want encryption, you want encryption. That is, I am not convinced that optional encryption is useful. While PPP regards MPPE as a "compressor", it actually expands every frame by 4 bytes, the MPPE overhead (encapsulation). Because of the data expansion, you'll see that ppp interfaces get their mtu reduced by 4 bytes whenever MPPE is negotiated. This is because when MPPE is active, it is *required* that *every* packet be encrypted. PPPD sets the mtu = MIN(peer mru, configured mtu). To ensure that MPPE frames are not larger than the peer's mru, we reduce the mtu by 4 bytes so that the network layer never sends ppp a packet that's too large. There is an option to compress the data before encrypting (MPPC), however the algorithm is patented and requires execution of a license with Hifn. MPPC as an RFC is a complete farce. I have no further details on MPPC. Some recommendations: - Use stateless mode. Stateful mode is disabled by default. Unfortunately, stateless mode is very expensive as the peers must rekey for every packet. - Use 128-bit encryption. - Use MS-CHAPv2 only. Reference documents: MPPE MPPE Key Derivation MPPC PPTP MS RADIUS Attributes You might be interested in PoPToP, a Linux PPTP server. You can find it at RADIUS support for MPPE is from Ralf Hofmann, . BUILDING THE PPPD The userland component of PPPD has no additional requirements above those for MS-CHAP and MS-CHAPv2. MPPE support is now included in the mainline Linux kernel releases. CONFIGURATION See pppd(8) for the MPPE options. Under Linux, if your modutils is earlier than 2.4.15, you will need to add alias ppp-compress-18 ppp_mppe to /etc/modules.conf. ppp-2.4.5/README.MSCHAP80000066400000000000000000000137701130035057700143340ustar00rootroot00000000000000PPP Support for Microsoft's CHAP-80 =================================== Eric Rosenquist rosenqui@strataware.com (updated by Paul Mackerras) (updated by Al Longyear) (updated by Farrell Woods) (updated by Frank Cusack) INTRODUCTION Microsoft has introduced an extension to the Challenge/Handshake Authentication Protocol (CHAP) which avoids storing cleartext passwords on a server. (Unfortunately, this is not as secure as it sounds, because the encrypted password stored on a server can be used by a bogus client to gain access to the server just as easily as if the password were stored in cleartext.) The details of the Microsoft extensions can be found in the document: In short, MS-CHAP is identified as since the hex value of 80 is used to designate Microsoft's scheme. Standard PPP CHAP uses a value of 5. If you enable PPP debugging with the "debug" option and see something like the following in your logs, the remote server is requesting MS-CHAP: rcvd [LCP ConfReq id=0x2 ] ^^^^^^^ MS-CHAP is enabled by default under Linux in pppd/Makefile.linux by the line "CHAPMS=y". CONFIGURATION If you've never used PPPD with CHAP before, read the man page (type "man pppd") and read the description in there. Basically, you need to edit the "chap-secrets" file typically named /etc/ppp/chap-secrets. This should contain the following two lines for each system with which you use CHAP (with no leading blanks): RemoteHost Account Secret Account RemoteHost Secret Note that you need both lines and that item 1 and 2 are swapped in the second line. I'm not sure why you need it twice, but it works and I didn't have time to look into it further. The "RemoteHost" is a somewhat arbitrary name for the remote Windows NT system you're dialing. It doesn't have to match the NT system's name, but it *does* have to match what you use with the "remotename" parameter. The "Account" is the Windows NT account name you have been told to use when dialing, and the "Secret" is the password for that account. For example, if your service provider calls their machine "DialupNT" and tells you your account and password are "customer47" and "foobar", add the following to your chap-secrets file: DialupNT customer47 foobar customer47 DialupNT foobar The only other thing you need to do for MS-CHAP (compared to normal CHAP) is to always use the "remotename" option, either on the command line or in your "options" file (see the pppd man page for details). In the case of the above example, you would need to use the following command line: pppd name customer47 remotename DialupNT or add: name customer47 remotename DialupNT to your PPPD "options" file. The "remotename" option is required for MS-CHAP since Microsoft PPP servers don't send their system name in the CHAP challenge packet. E=691 (AUTHENTICATION_FAILURE) ERRORS WHEN YOU HAVE THE VALID SECRET (PASSWORD) If your RAS server is not the domain controller and is not a 'stand-alone' server then it must make a query to the domain controller for your domain. You need to specify the domain name with the user name when you attempt to use this type of a configuration. The domain name is specified with the local name in the chap-secrets file and with the option for the 'name' parameter. For example, the previous example would become: DialupNT domain\\customer47 foobar domain\\customer47 DialupNT foobar and pppd name 'domain\\customer47' remotename DialupNT or add: name domain\\customer47 remotename DialupNT when the Windows NT domain name is simply called 'domain'. TROUBLESHOOTING Assuming that everything else has been configured correctly for PPP and CHAP, the MS-CHAP-specific problems you're likely to encounter are mostly related to your Windows NT account and its settings. A Microsoft server returns error codes in its CHAP response. The following are extracted from RFC 2433: 646 ERROR_RESTRICTED_LOGON_HOURS 647 ERROR_ACCT_DISABLED 648 ERROR_PASSWD_EXPIRED 649 ERROR_NO_DIALIN_PERMISSION 691 ERROR_AUTHENTICATION_FAILURE 709 ERROR_CHANGING_PASSWORD You'll see these in your pppd log as a line similar to: Remote message: E=649 R=0 The "E=" is the error number from the table above, and the "R=" flag indicates whether the error is transient and the client should retry. If you consistently get error 691, then either you're using the wrong account name/password, or the DES library or MD4 hashing (in md4.c) aren't working properly. Verify your account name and password (use a Windows NT or Windows 95 system to dial-in if you have one available). If that checks out, test the DES library with the "destest" program included with the DES library. If DES checks out, the md4.c routines are probably failing (system byte ordering may be a problem) or my code is screwing up. I've only got access to a Linux system, so you're on your own for anything else. Another thing that might cause problems is that some RAS servers won't respond at all to LCP config requests without seeing the word "CLIENT" from the other end. If you see pppd sending out LCP config requests without getting any reply, try putting something in your chat script to send the word CLIENT after the modem has connected. STILL TO DO A site using only MS-CHAP to authenticate has no need to store cleartext passwords in the "chap-secrets" file. A utility that spits out the ASCII hex MD4 hash of a given password would be nice, and would allow that hash to be used in chap-secrets in place of the password. The code to do this could quite easily be lifted from chap_ms.c (you have to convert the password to Unicode before hashing it). The chap_ms.c file would also have to be changed to recognize a password hash (16 binary bytes == 32 ASCII hex characters) and skip the hashing stage. This would have no real security value as the hash is plaintext-equivalent. ppp-2.4.5/README.MSCHAP81000066400000000000000000000045651130035057700143370ustar00rootroot00000000000000PPP Support for Microsoft's CHAP-81 =================================== Frank Cusack frank@google.com Some text verbatim from README.MSCHAP80, by Eric Rosenquist, rosenqui@strataware.com INTRODUCTION First, please read README.MSCHAP80; almost everything there applies here. MS-CHAP was basically devised by Microsoft because rather than store plaintext passwords, they (Microsoft) store the md4 hash of passwords. It provides no advantage over standard CHAP, since the hash is used as plaintext-equivalent. (Well, the Change-Password packet is arguably an advantage.) It does introduce a significant weakness if the LM hash is used. Additionally, the format of the failure packet potentially gives information to an attacker. The weakness of the LM hash is partly addressed in RFC 2433, which deprecates its use. MS-CHAPv2 adds 2 benefits to MS-CHAP. (1) The LM hash is no longer used. (2) Mutual authentication is required. Note that the mutual authentication in MS-CHAPv2 is different than the case where both PPP peers require authentication from the other; the former proves that the server has access to the client's password, the latter proves that the server has access to a secret which the client also has -- which may or may not be the same as the client's password (but should not be the same, per RFC 1994). Whether this provides any actual benefit is outside the scope of this document. The details of MS-CHAPv2 can be found in the document: BUILDING THE PPPD In addition to the requirements for MS-CHAP, MS-CHAPv2 uses the SHA-1 hash algorithm. A public domain implementation is provided with pppd. TROUBLESHOOTING Assuming that everything else has been configured correctly for PPP and CHAP, the MS-CHAPv2-specific problems you're likely to encounter are mostly related to your Windows NT account and its settings. A Microsoft server returns error codes in its CHAP response. The following are extracted from RFC 2759: 646 ERROR_RESTRICTED_LOGON_HOURS 647 ERROR_ACCT_DISABLED 648 ERROR_PASSWD_EXPIRED 649 ERROR_NO_DIALIN_PERMISSION 691 ERROR_AUTHENTICATION_FAILURE 709 ERROR_CHANGING_PASSWORD You'll see these in your pppd log as a line similar to: Remote message: E=649 No dialin permission Previously, pppd would log this as: Remote message: E=649 R=0 Now, the text message is logged (both for MS-CHAP and MS-CHAPv2). ppp-2.4.5/README.cbcp000066400000000000000000000037351130035057700141200ustar00rootroot00000000000000 Microsoft Call Back Configuration Protocol. by Pedro Roque Marques (updated by Paul Mackerras) The CBCP is a method by which the Microsoft Windows NT Server may implement additional security. It is possible to configure the server in such a manner so as to require that the client systems which connect with it are required that following a valid authentication to leave a method by which the number may be returned call. It is a requirement of servers to be so configured that the protocol be exchanged. So, this set of patches may be applied to the pppd process to enable the cbcp client *only* portion of the specification. It is primarily meant to permit connection with Windows NT Servers. The ietf-working specification may be obtained from ftp.microsoft.com in the developr/rfc directory. The ietf task group has decided to recommend that the LCP sequence be extended to permit the callback operation. For this reason, these patches are not 'part' of pppd but are an adjunct to the code. To enable CBCP support, all that is required is to uncomment the line in Makefile.linux that sets CBCP=y and recompile pppd. I use such script to make a callback: pppd debug nodetach /dev/modem 115200 crtscts modem \ callback 222222 name NAME remotename SERVER \ connect 'chat -v "" atz OK atdt111111 CONNECT ""' sleep 1 pppd debug /dev/modem 115200 crtscts modem \ name NAME remotename SERVER defaultroute \ connect 'chat -v RING ATA CONNECT "\c"' First we invoke pppd with 'nodetach' option in order to not detach from the controlling terminal and 'callback NUMBER' option, then wait for 1 second and invoke pppd again which waits for a callback (RING) and then answers (ATA). Number 222222 is a callback number, i.e. server will call us back at this number, while number 111111 is the number we are calling to. You have to put in /etc/ppp/chap-secrets the following two lines: NAME SERVER PASSWORD SERVER NAME PASSWORD You have to use your real login name, remote server name and password. ppp-2.4.5/README.eap-srp000066400000000000000000000142641130035057700145570ustar00rootroot00000000000000EAP with MD5-Challenge and SRP-SHA1 support by James Carlson, Sun Microsystems Version 2, September 22nd, 2002 1. What it does The Extensible Authentication Protocol (EAP; RFC 2284) is a security protocol that can be used with PPP. It provides a means to plug in multiple optional authentication methods. This implementation includes the required default MD5-Challenge method, which is similar to CHAP (RFC 1994), as well as the new SRP-SHA1 method. This latter method relies on an exchange that is not vulnerable to dictionary attacks (as is CHAP), does not require the server to keep a cleartext copy of the secret (as in CHAP), supports identity privacy, and produces a temporary shared key that could be used for data encryption. The SRP-SHA1 method is based on draft-ietf-pppext-eap-srp-03.txt, a work in progress. 2. Required libraries Two other packages are required first. Download and install OpenSSL and Thomas Wu's SRP implementation. http://www.openssl.org/ (or ftp://ftp.openssl.org/source/) http://srp.stanford.edu/ Follow the directions in each package to install the SSL and SRP libraries. Once SRP is installed, you may run tconf as root to create known fields, if desired. (This step is not required.) 3. Installing the patch The EAP-SRP patch described here is integrated into this version of pppd. The following patch may be used with older pppd sources: ftp://playground.sun.com/carlsonj/eap/ppp-2.4.1-eap-1.tar.gz Configure, compile, and install as root. You may want to edit pppd/Makefile after configuring to enable or disable optional features. % ./configure % make % su # make install If you use csh or tcsh, run "rehash" to pick up the new commands. If you're using Solaris, and you run into trouble with the pseudonym feature on the server side ("no DES here" shows in the log file), make sure that you have the "domestic" versions of the DES libraries linked. You should see "crypt_d" in "ldd /usr/local/bin/pppd". If you see "crypt_i" instead, then make sure that /usr/lib/libcrypt.* links to /usr/lib/libcrypt_d.*. (If you have the international version of Solaris, then you won't have crypt_d. You might want to find an alternative DES library.) 4. Adding the secrets On the EAP SRP-SHA1 client side, access to the cleartext secret is required. This can be done in two ways: - Enter the client name, server name, and password in the /etc/ppp/srp-secrets file. This file has the same format as the existing chap-secrets and pap-secrets files. clientname servername "secret here" - Use the "password" option in any of the standard configuration files (or the command line) to specify the secret. password "secret here" On the EAP SRP-SHA1 server side, a secret verifier is required. This is a one-way hash of the client's name and password. To generate this value, run the srp-entry program (see srp-entry(8)). This program prompts for the client name and the passphrase (the secret). The output will be an entry, such as the following, suitable for use in the server's srp-secrets file. Note that if this is transferred by cut-and-paste, the entry must be a single line of text in the file. pppuser srpserver 0:LFDpwg4HBLi4/kWByzbZpW6pE95/iIWBSt7L.DAkHsvwQphtiq0f6reoUy/1LC1qYqjcrV97lCDmQHQd4KIACGgtkhttLdP3KMowvS0wLXLo25FPJeG2sMAUEWu/HlJPn2/gHyh9aT.ZxUs5MsoQ1E61sJkVBc.2qze1CdZiQGTK3qtWRP6DOpM1bfhKtPoVm.g.MiCcTMWzc54xJUIA0mgKtpthE3JrqCc81cXUt4DYi5yBzeeGTqrI0z2/Gj8Jp7pS4Fkq3GmnYjMxnKfQorFXNwl3m7JSaPa8Gj9/BqnorJOsnSMlIhBe6dy4CYytuTbNb4Wv/nFkmSThK782V:2cIyMp1yKslQgE * The "secret" field consists of three entries separated by colons. The first entry is the index of the modulus and generator from SRP's /etc/tpasswd.conf. If the special value 0 is used, then the well-known modulus/generator value is used (this is recommended, because it is much faster). The second value is the verifier value. The third is the password "salt." These latter two values are encoded in base64 notation. For EAP MD5-Challenge, both client and server use the existing /etc/ppp/chap-secrets file. 5. Configuration options There are two main options relating to EAP available for the client. These are: refuse-eap - refuse to authenticate with EAP srp-use-pseudonym - use the identity privacy if offered by server The second option stores a pseudonym, if offered by the EAP SRP-SHA1 server, in the $HOME/.ppp_pseudonym file. The pseudonym is typically an encrypted version of the client identity. During EAP start-up, the pseudonym stored in this file is offered to the peer as the identity. If this is accepted by the peer, then eavesdroppers will be unable to determine the identity of the client. Each time the client is authenticated, the server will offer a new pseudoname to the client using an obscured (reversibly encrypted) message. Thus, access across successive sessions cannot be tracked. There are two main options for EAP on the server: require-eap - require client to use EAP srp-pn-secret "string" - set server's pseudoname secret The second option sets the long-term secret used on the server to encrypt the user's identity to produce pseudonames. The pseudoname is constructed by hashing this string with the current date (to the nearest day) with SHA1, then using this hash as the key for a DES encryption of the client's name. The date is added to the hash for two reasons. First, this allows the pseudonym to change daily. Second, it allows the server to decode any previous pseudonym by trying previous dates. See the pppd(8) man page for additional options. 6. Comments welcome! This is still an experimental implementation. It has been tested and reviewed carefully for correctness, but may still be incomplete or have other flaws. All comments are welcome. Please address them to the author: james.d.carlson@sun.com or, for EAP itself or the SRP extensions to EAP, to the IETF PPP Extensions working group: ietf-ppp@merit.edu ppp-2.4.5/README.linux000066400000000000000000000261521130035057700143460ustar00rootroot00000000000000 PPP for Linux ------------- Paul Mackerras 8 March 2001 for ppp-2.4.2 Updated for ppp-2.4.5, Sep 08 1. Introduction --------------- The Linux PPP implementation includes both kernel and user-level parts. This package contains the user-level part, which consists of the PPP daemon (pppd) and associated utilities. In the past this package has contained updated kernel drivers. This is no longer necessary, as the current kernel sources contain up-to-date drivers (and have done since the 2.4.x kernel series). The Linux PPP implementation is capable of being used both for initiating PPP connections (as a `client') or for handling incoming PPP connections (as a `server'). Note that this is an operational distinction, based on how the connection is created, rather than a distinction that is made in the PPP protocols themselves. Mostly this package is used for PPP connections over modems connected via asynchronous serial ports, so this guide concentrates on this situation. The PPP protocol consists of two parts. One is a scheme for framing and encoding packets, the other is a series of protocols called LCP, IPCP, PAP and CHAP, for negotiating link options and for authentication. This package similarly consists of two parts: a kernel module which handles PPP's low-level framing protocol, and a user-level program called pppd which implements PPP's negotiation protocols. The kernel module assembles/disassembles PPP frames, handles error detection, and forwards packets between the serial port and either the kernel network code or the user-level program pppd. IP packets go directly to the kernel network code. So once pppd has negotiated the link, it in practice lies completely dormant until you want to take the link down, when it negotiates a graceful disconnect. 2. Installation --------------- 2.1 Kernel driver Assuming you are running a recent 2.4 or 2.6 (or later) series kernel, the kernel source code will contain an up-to-date kernel PPP driver. If the PPP driver was included in your kernel configuration when your kernel was built, then you only need to install the user-level programs. Otherwise you will need to get the source tree for your kernel version, configure it with PPP included, and recompile. Most Linux distribution vendors ship kernels with PPP included in the configuration. The PPP driver can be either compiled into the kernel or compiled as a kernel module. If it is compiled into the kernel, the PPP driver is included in the kernel image which is loaded at boot time. If it is compiled as a module, the PPP driver is present in one or more files under /lib/modules and is loaded into the kernel when needed. The 2.2 series kernels contain an older version of the kernel PPP driver, one which doesn't support multilink. If you want multilink, you need to run a 2.4 or 2.6 series kernel. The kernel PPP driver was completely rewritten for the 2.4 series kernels to support multilink and to allow it to operate over diverse kinds of communication medium (the 2.2 driver only operates over serial ports and devices which look like serial ports, such as pseudo-ttys). Under the 2.2 kernels, if PPP is compiled as a module, the PPP driver modules should be present in the /lib/modules/`uname -r`/net directory (where `uname -r` represents the kernel version number). The PPP driver module itself is called ppp.o, and there will usually be compression modules there, ppp_deflate.o and bsd_comp.o, as well as slhc.o, which handles TCP/IP header compression. If the PPP driver is compiled into the kernel, the compression code will still be compiled as modules, for kernels before 2.2.17pre12. For 2.2.17pre12 and later, if the PPP driver is compiled in, the compression code will also. Under the 2.4 kernels, there are two PPP modules, ppp_generic.o and ppp_async.o, plus the compression modules (ppp_deflate.o, bsd_comp.o and slhc.o). If the PPP generic driver is compiled into the kernel, the other four can then be present either as modules or compiled into the kernel. There is a sixth module, ppp_synctty.o, which is used for synchronous tty devices such as high-speed WAN adaptors. 2.2 User-level programs If you obtained this package in .rpm or .deb format, you simply follow the usual procedure for installing the package. If you are using the .tar.gz form of this package, then cd into the ppp-2.4.5 directory you obtained by unpacking the archive and issue the following commands: $ ./configure $ make # make install The `make install' has to be done as root. This makes and installs four programs and their man pages: pppd, chat, pppstats and pppdump. If the /etc/ppp configuration directory doesn't exist, the `make install' step will create it and install some default configuration files. 2.3 System setup for 2.4 kernels Under the 2.4 series kernels, pppd needs to be able to open /dev/ppp, character device (108,0). If you are using udev (as most distributions do), the /dev/ppp node should be created by udevd. Otherwise you may need to create a /dev/ppp device node with the commands: # mknod /dev/ppp c 108 0 # chmod 600 /dev/ppp 2.4 System setup under 2.2 series kernels Under the 2.2 series kernels, you should add the following to your /etc/modules.conf or /etc/conf.modules: alias tty-ldisc-3 ppp alias ppp-compress-21 bsd_comp alias ppp-compress-24 ppp_deflate alias ppp-compress-26 ppp_deflate 3. Getting help with problems ----------------------------- If you have problems with your PPP setup, or you just want to ask some questions, or better yet if you can help others with their PPP questions, then you should join the linux-ppp mailing list. Send an email to majordomo@vger.kernel.org with a line in the body saying subscribe linux-ppp To leave the mailing list, send an email to majordomo@vger.kernel.org with a line in the body saying unsubscribe linux-ppp To send a message to the list, email it to linux-ppp@vger.kernel.org. You don't have to be subscribed to send messages to the list. You can also email me (paulus@samba.org) but I am overloaded with email and I can't respond to most messages I get in a timely fashion. There are also several relevant news groups, such as comp.protocols.ppp, comp.os.linux.networking, or comp.os.linux.setup. 4. Configuring your dial-out PPP connections -------------------------------------------- Some Linux distribution makers include tools in their distributions for setting up PPP connections. For example, for Red Hat Linux and derivatives, you should probably use linuxconf or netcfg to set up your PPP connections. The two main windowing environments for Linux, KDE and Gnome, both come with GUI utilities for configuring and controlling PPP dial-out connections. They are convenient and relatively easy to configure. A third alternative is to use a PPP front-end package such as wvdial or ezppp. These also will handle most of the details of talking to the modem and setting up the PPP connection for you. Assuming that you don't want to use any of these tools, you want to set up the configuration manually yourself, then read on. This document gives a brief description and example. More details can be found by reading the pppd and chat man pages and the PPP-HOWTO. We assume that you have a modem that uses the Hayes-compatible AT command set connected to an async serial port (e.g. /dev/ttyS0) and that you are dialling out to an ISP. The trickiest and most variable part of setting up a dial-out PPP connection is the part which involves getting the modem to dial and then invoking PPP service at the far end. Generally, once both ends are talking PPP the rest is relatively straightforward. Now in fact pppd doesn't know anything about how to get modems to dial or what you have to say to the system at the far end to get it to talk PPP. That's handled by an external program such as chat, specified with the connect option to pppd. Chat takes a series of strings to expect from the modem interleaved with a series of strings to send to the modem. See the chat man page for more information. Here is a simple example for connecting to an ISP, assuming that the ISP's system starts talking PPP as soon as it answers the phone: pppd connect 'chat -v "" AT OK ATDT5551212 ~' \ /dev/ttyS0 57600 crtscts debug defaultroute Going through pppd's options in order: connect 'chat ...' This gives a command to run to contact the PPP server. Here the supplied 'chat' program is used to dial a remote computer. The whole command is enclosed in single quotes because pppd expects a one-word argument for the 'connect' option. The options to 'chat' itself are: -v verbose mode; log what we do to syslog "" don't wait for any prompt, but instead... AT send the string "AT" OK expect the response "OK", then ATDT5551212 dial the modem, then ~ wait for a ~ character, indicating the start of a PPP frame from the server /dev/ttyS0 specifies which serial port the modem is connected to 57600 specifies the baud rate to use crtscts use hardware flow control using the RTS & CTS signals debug log the PPP negotiation with syslog defaultroute add default network route via the PPP link Pppd will write error messages and debugging logs to the syslogd daemon using the facility name "daemon". These messages may already be logged to the console or to a file like /var/log/messages; consult your /etc/syslog.conf file to see. If you want to make all pppd messages go to a file such as /var/log/ppp-debug, add the line daemon.* /var/log/ppp-debug ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is one or more tabs. Do not use spaces. to syslog.conf; make sure to put one or more TAB characters (not spaces!) between the two fields. Then you need to create an empty /var/log/ppp-debug file with a command such as touch /var/log/ppp-debug and then restart syslogd, usually by sending it a SIGHUP signal with a command like this: killall -HUP syslogd 4.1 Is the link up? The main way to tell if your PPP link is up and operational is the ifconfig ("interface configuration") command. Type /sbin/ifconfig at a shell prompt. It should print a list of interfaces including one like this example: ppp0 Link encap Point-to-Point Protocol inet addr 192.76.32.3 P-t-P 129.67.1.165 Mask 255.255.255.0 UP POINTOPOINT RUNNING MTU 1500 Metric 1 RX packets 33 errors 0 dropped 0 overrun 0 TX packets 42 errors 0 dropped 0 overrun 0 Assuming that ifconfig shows the ppp network interface, you can test the link using the ping command like this: /sbin/ping -c 3 129.67.1.165 where the address you give is the address shown as the P-t-P address in the ifconfig output. If the link is operating correctly, you should see output like this: PING 129.67.1.165 (129.67.1.165): 56 data bytes 64 bytes from 129.67.1.165: icmp_seq=0 ttl=255 time=268 ms 64 bytes from 129.67.1.165: icmp_seq=1 ttl=255 time=247 ms 64 bytes from 129.67.1.165: icmp_seq=2 ttl=255 time=266 ms --- 129.67.1.165 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 247/260/268 ms ppp-2.4.5/README.pppoe000066400000000000000000000066521130035057700143350ustar00rootroot00000000000000 PPPoE Support ------------- Michal Ostrowski 8 August 2001 for ppp-2.4.2 Updated for ppp-2.4.5 by Paul Mackerras, Sep 08 1. Introduction --------------- This document describes the support for PPP over Ethernet (PPPoE) included with this package. It is assumed that the reader is familiar with Linux PPP (as it pertains to tty/modem-based connections). In particular, users of PPP in the Linux 2.2 series kernels should ensure they are familiar with the changes to the PPP implementation in the 2.4 series kernels before attempting to use PPPoE features. If you are not familiar with PPP, I recommend looking at other packages which include end-user configuration tools, such as Roaring Penguin (http://www.roaringpenguin.com/pppoe). PPPoE is a protocol typically used by *DSL providers to manage IP addresses and authenticate users. Essentially, PPPoE provides for a PPP connection to be established not over a physical serial-line or modem, but over a logical connection between two unique MAC-addresses on an ethernet network. Once the PPPoE layer discovers the end-points to be used in the link and negotiates it, frames may be sent to and received from the PPPoE layer just as if the link was a serial line (or that is how it's supposed to be). With this in mind, the goal of the implementation of PPPoE support in Linux is to allow users to simply specify that the device they intend to use for the PPP connection is an ethernet device (i.e. "eth0") and the rest of the system should function as usual. 2. Using PPPoE -------------- This section is a quick guide for getting PPPoE working, to allow one to connect to their ISP who is providing PPPoE based services. 1. Enable "Prompt for development and/or incomplete code/drivers" and "PPP over Ethernet" in your kernel configuration. Most distributions will include the kernel PPPoE module by default. 2. Compile and install your kernel. 3. Install the ppp package. 4. Add the following line to /etc/ppp/options: plugin rp-pppoe.so The effect of this line is simply to make "eth0", "eth1", ....,"ethx" all valid device names for pppd (just like ttyS0, ttyS1). 5. Add the necessary authentication options to your pppd configuration (i.e. PAP/CHAP information). If you wish to maintain seperate configurations for different devices you may place configuration options in device-specific configuration files: /etc/ppp/options.devname (devname=ttyS0, ttyS1, eth0, eth1 or any other valid device name). 6. Invoke pppd with the appropriate device name: e.g. "pppd eth0" Do not include any compression or flow control options in your PPPoE configuration. They will be ignored. Again, here it is assumed that the reader is familiar with the general process of configuring PPP. The steps outlined here refer only to the steps and configuration options which are PPPoE specific, and it is assumed that the reader will also configure other aspects of the system (e.g. PAP authentication parameters). 3. Advanced Functionality -------------------------- For more advanced functionality (such as providing PPPoE services) and user configuration tools, look to the Roaring Penguin PPPoE software package (http://www.roaringpenguin.com/pppoe). 4. Credits ----------- The PPPoE plugin included in this package is a component of the Roaring Penguin PPPoE package, included in this package courtesy of Roaring Penguin Software. (http://www.roaringpenguin.com). ppp-2.4.5/README.pppol2tp000066400000000000000000000047601130035057700147700ustar00rootroot00000000000000PPPoL2TP plugin =============== The pppol2tp plugin lets pppd use the Linux kernel driver pppol2tp.ko to pass PPP frames in L2TP tunnels. The driver was integrated into the kernel in the 2.6.23 release. For kernels before 2.6.23, an out-of-tree kernel module is available from the pppol2tp-kmod package in the OpenL2TP project. Note that pppd receives only PPP control frames over the PPPoL2TP socket; data frames are handled entirely by the kernel. The pppol2tp plugin adds extra arguments to pppd and uses the Linux kernel PPP-over-L2TP driver to set up each session's data path. Arguments are:- pppol2tp - FD for PPPoL2TP socket pppol2tp_lns_mode - PPPoL2TP LNS behavior. Default off. pppol2tp_send_seq - PPPoL2TP enable sequence numbers in transmitted data packets. Default off. pppol2tp_recv_seq - PPPoL2TP enforce sequence numbers in received data packets. Default off. pppol2tp_reorderto - PPPoL2TP data packet reorder timeout. Default 0 (no reordering). pppol2tp_debug_mask - PPPoL2TP debug mask. Bitwise OR of 1 - verbose debug 2 - control 4 - kernel transport 8 - ppp packet data Default: 0 (no debug). pppol2tp_ifname - Name of PPP network interface visible to "ifconfig" and "ip link". Default: "pppN" pppol2tp_tunnel_id - L2TP tunnel_id tunneling this PPP session. pppol2tp_session_id - L2TP session_id of this PPP session. The tunnel_id/session_id pair is used when sending event messages to openl2tpd. pppd will typically be started by an L2TP daemon for each L2TP sesion, supplying one or more of the above arguments as required. The pppd user will usually have no visibility of these arguments. Two hooks are exported by this plugin. void (*pppol2tp_send_accm_hook)(int tunnel_id, int session_id, uint32_t send_accm, uint32_t recv_accm); void (*pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up); Credits ======= This plugin was developed by Katalix Systems as part of the OpenL2TP project, http://openl2tp.sourceforge.net. OpenL2TP is a full-featured L2TP client-server, suitable for use as an enterprise L2TP VPN server or a VPN client. Please copy problems to the OpenL2TP mailing list: openl2tp-users@lists.sourceforge.net. Maintained by: James Chapman jchapman@katalix.com Katalix Systems Ltd http://www.katalix.com ppp-2.4.5/README.pwfd000066400000000000000000000067441130035057700141540ustar00rootroot00000000000000 Support to pass the password via a pipe to the pppd --------------------------------------------------- Arvin Schnell 2002-02-08 1. Introduction --------------- Normally programs like wvdial or kppp read the online password from their config file and store them in the pap- and chap-secrets before they start the pppd and remove them afterwards. Sure they need special privileges to do so. The passwordfd feature offers a simpler and more secure solution. The program that starts the pppd opens a pipe and writes the password into it. The pppd simply reads the password from that pipe. This methods is used for quite a while on SuSE Linux by the programs wvdial, kppp and smpppd. 2. Example ---------- Here is a short C program that uses the passwordfd feature. It starts the pppd to buildup a pppoe connection. --snip-- #include #include #include #include #include #include #ifndef _PATH_PPPD #define _PATH_PPPD "/usr/sbin/pppd" #endif // Of course these values can be read from a configuration file or // entered in a graphical dialog. char *device = "eth0"; char *username = "1122334455661122334455660001@t-online.de"; char *password = "hello"; pid_t pid = 0; void sigproc (int src) { fprintf (stderr, "Sending signal %d to pid %d\n", src, pid); kill (pid, src); exit (EXIT_SUCCESS); } void sigchild (int src) { fprintf (stderr, "Daemon died\n"); exit (EXIT_SUCCESS); } int start_pppd () { signal (SIGINT, &sigproc); signal (SIGTERM, &sigproc); signal (SIGCHLD, &sigchild); pid = fork (); if (pid < 0) { fprintf (stderr, "unable to fork() for pppd: %m\n"); return 0; } if (pid == 0) { int i, pppd_argc = 0; char *pppd_argv[20]; char buffer[32] = ""; int pppd_passwdfd[2]; for (i = 0; i < 20; i++) pppd_argv[i] = NULL; pppd_argv[pppd_argc++] = "pppd"; pppd_argv[pppd_argc++] = "call"; pppd_argv[pppd_argc++] = "pwfd-test"; // The device must be after the call, since the call loads the plugin. pppd_argv[pppd_argc++] = device; pppd_argv[pppd_argc++] = "user"; pppd_argv[pppd_argc++] = username; // Open a pipe to pass the password to pppd. if (pipe (pppd_passwdfd) == -1) { fprintf (stderr, "pipe failed: %m\n"); exit (EXIT_FAILURE); } // Of course this only works it the password is shorter // than the pipe buffer. Otherwise you have to fork to // prevent that your main program blocks. write (pppd_passwdfd[1], password, strlen (password)); close (pppd_passwdfd[1]); // Tell the pppd to read the password from the fd. pppd_argv[pppd_argc++] = "passwordfd"; snprintf (buffer, 32, "%d", pppd_passwdfd[0]); pppd_argv[pppd_argc++] = buffer; if (execv (_PATH_PPPD, (char **) pppd_argv) < 0) { fprintf (stderr, "cannot execl %s: %m\n", _PATH_PPPD); exit (EXIT_FAILURE); } } pause (); return 1; } int main (int argc, char **argv) { if (start_pppd ()) exit (EXIT_SUCCESS); exit (EXIT_FAILURE); } ---snip--- Copy this file to /etc/ppp/peers/pwfd-test. The plugins can't be loaded on the command line (unless you are root) since the plugin option is privileged. ---snip--- # # PPPoE plugin for kernel 2.4 # plugin pppoe.so # # This plugin enables us to pipe the password to pppd, thus we don't have # to fiddle with pap-secrets and chap-secrets. The user is also passed # on the command line. # plugin passwordfd.so noauth usepeerdns defaultroute hide-password nodetach nopcomp novjccomp noccp ---snip--- ppp-2.4.5/README.sol2000066400000000000000000000240251130035057700140630ustar00rootroot00000000000000This file describes the installation process for ppp-2.4 on systems running Solaris. The Solaris and SVR4 ports share a lot of code but are not identical. The STREAMS kernel modules and driver for Solaris are in the solaris directory (and use some code from the modules directory). NOTE: Although the kernel driver and modules have been designed to operate correctly on SMP systems, they have not been extensively tested on SMP machines. Some users of SMP Solaris x86 systems have reported system problems apparently linked to the use of previous versions of this software. I believe these problems have been fixed. Installation. ************* 1. Run the configure script and make the user-level programs and the kernel modules. ./configure make The configure script will automatically find Sun's cc if it's in the standard location (/opt/SUNWspro/bin/cc). If you do not have Sun's WorkShop compiler, configure will attempt to use 'gcc'. If this is found and you have a 64 bit kernel, it will check that gcc accepts the "-m64" option, which is required to build kernel modules. You should not have to edit the Makefiles for most ordinary cases. 2. Install the programs and kernel modules: as root, do make install This installs pppd, chat and pppstats in /usr/local/bin and the kernel modules in /kernel/drv and /kernel/strmod, and creates the /etc/ppp directory and populates it with default configuration files. You can change the installation directories by editing solaris/Makedefs. If you have a 64 bit kernel, the 64-bit drivers are installed in /kernel/drv/sparcv9 and /kernel/strmod/sparcv9. If your system normally has only one network interface at boot time, the default Solaris system startup scripts will disable IP forwarding in the IP kernel module. This will prevent the remote machine from using the local machine as a gateway to access other hosts. The solution is to create an /etc/ppp/ip-up script containing something like this: #!/bin/sh /usr/sbin/ndd -set /dev/ip ip_forwarding 1 See the man page for ip(7p) for details. Integrated pppd *************** Solaris 8 07/01 (Update 5) and later have an integrated version of pppd, known as "Solaris PPP 4.0," and is based on ppp-2.4.0. This version comes with the standard Solaris software distribution and is supported by Sun. It is fully tested in 64-bit and SMP modes, and with bundled and unbundled synchronous drivers. Solaris 8 10/01 (Update 6) and later includes integrated PPPoE client and server support, with kernel-resident data handling. See pppd(1M). The feature is part of the regular full installation, and is provided by these packages: SUNWpppd - 32-bit mode kernel drivers SUNWpppdr - root-resident /etc/ppp config samples SUNWpppdu - /usr/bin/pppd itself, plus chat SUNWpppdx - 64-bit mode kernel drivers SUNWpppdt - PPPoE support SUNWpppg - GPL'd optional 'pppdump' and plugins SUNWpppgS - Source for GPL'd optional features Use the open source version of pppd if you wish to recompile to add new features or to experiment with the code. Production systems, however, should run the Sun-supplied version, if at all possible. You can run both versions on a single system if you wish. The Solaris PPP 4.0 interfaces are named "spppN," while this open source version names its interfaces as "pppN". The STREAMS modules are similarly separated. The Sun-supplied pppd lives in /usr/bin/pppd, while the open source version installs (by default) in /usr/local/bin/pppd. Dynamic STREAMS Re-Plumbing Support. ************************************ Solaris 8 (and later) includes dynamic re-plumbing support. With this feature, modules below ip can be inserted, or removed, without having the ip stream be unplumbed, and re-plumbed again. All state in ip for the interface will be preserved as modules are added or removed. Users can install (or upgrade) modules such as firewall, bandwidth manager, cache manager, tunneling, etc., without shutting the interface down. To support this, ppp driver now uses /dev/udp instead of /dev/ip for the ip stream. The interface stream (where ip module pushed on top of ppp) is then I_PLINK'ed below the ip stream. /dev/udp is used because STREAMS will not let a driver be PLINK'ed under itself, and /dev/ip is typically the driver at the bottom of the tunneling interfaces stream. The mux ids of the ip streams are then added using SIOCSxIFMUXID ioctl. Users will be able to see the modules on the interface stream by, for example: pikapon# ifconfig ppp modlist 0 ip 1 ppp Or arbitrarily if bandwidth manager and firewall modules are installed: pikapon# ifconfig hme0 modlist 0 arp 1 ip 2 ipqos 3 firewall 4 hme Snoop Support. ************** This version includes support for /usr/sbin/snoop. Tests have been done on Solaris 7 through 9. Only IPv4 and IPv6 packets will be sent up to stream(s) marked as promiscuous (i.e., those used by snoop). Users will be able to see the packets on the ppp interface by, for example: snoop -d ppp0 See the man page for snoop(1M) for details. IPv6 Support. ************* This is for Solaris 8 and later. This version has been tested under Solaris 8 and 9 running IPv6. Interoperability testing has only been done between Solaris machines in terms of the IPV6 NCP. An additional command line option for the pppd daemon has been added: ipv6cp-use-persistent. By default, compilation for IPv6 support is not enabled. Uncomment the necessary lines in pppd/Makefile.sol2 to enable it. Once done, the quickest way to get IPv6 running is to add the following somewhere in the command line option: +ipv6 ipv6cp-use-persistent The persistent id for the link-local address was added to conform to RFC 2472; such that if there's an EUI-48 available, use that to make up the EUI-64. As of now, the Solaris implementation extracts the EUI-48 id from the Ethernet's MAC address (the ethernet interface needs to be up). Future work might support other ways of obtaining a unique yet persistent id, such as EEPROM serial numbers, etc. There need not be any up/down scripts for ipv6, e.g. /etc/ppp/ipv6-up or /etc/ppp/ipv6-down, to trigger IPv6 neighbor discovery for auto configuration and routing. The in.ndpd daemon will perform all of the necessary jobs in the background. /etc/inet/ndpd.conf can be further customized to enable the machine as an IPv6 router. See the man page for in.ndpd(1M) and ndpd.conf(4) for details. Below is a sample output of "ifconfig -a" with persistent link-local address. Note the UNNUMBERED flag is set because hme0 and ppp0 both have identical link-local IPv6 addresses: lo0: flags=1000849 mtu 8232 index 1 inet 127.0.0.1 netmask ff000000 hme0: flags=1000843 mtu 1500 index 2 inet 129.146.86.248 netmask ffffff00 broadcast 129.146.86.255 ether 8:0:20:8d:38:c1 lo0: flags=2000849 mtu 8252 index 1 inet6 ::1/128 hme0: flags=2000841 mtu 1500 index 2 ether 8:0:20:8d:38:c1 inet6 fe80::a00:20ff:fe8d:38c1/10 hme0:1: flags=2080841 mtu 1500 index 2 inet6 fec0::56:a00:20ff:fe8d:38c1/64 hme0:2: flags=2080841 mtu 1500 index 2 inet6 2000::56:a00:20ff:fe8d:38c1/64 hme0:3: flags=2080841 mtu 1500 index 2 inet6 2::56:a00:20ff:fe8d:38c1/64 ppp0: flags=10008d1 mtu 1500 index 12 inet 172.16.1.1 --> 172.16.1.2 netmask ffffff00 ppp0: flags=2202851 mtu 1500 index 12 inet6 fe80::a00:20ff:fe8d:38c1/10 --> fe80::a00:20ff:fe7a:24fb Note also that a plumbed ipv6 interface stream will exist throughout the entire PPP session in the case where the peer rejects IPV6CP, which further causes the interface state to stay down. Unplumbing will happen when the daemon exits. This is done by design and is not a bug. 64-bit Support. *************** This version has been tested under Solaris 7 through 9 in both 32- and 64-bit environments (Ultra class machines). Installing the package by executing "make install" will result in additional files residing in /kernel/drv/sparcv9 and /kernel/strmod/sparcv9 subdirectories. 64-bit modules and driver have been compiled and tested using Sun's cc and gcc. Synchronous Serial Support. *************************** This version has working but limited support for the on-board synchronous HDLC interfaces. It has been tested with the /dev/se_hdlc, /dev/zsh, HSI/S, and HSI/P drivers. Synchronous mode was tested with a Cisco router. The ppp daemon does not directly support controlling the serial interface. It relies on the /usr/sbin/syncinit command to initialize HDLC mode and clocking. There is a confirmed bug with NRZ/NRZI mode in the /dev/se_hdlc driver, and Solaris patch 104596-11 is needed to correct it. (However this patch seems to introduce other serial problems. If you don't apply the patch, the workaround is to change the nrzi mode to yes or no, whichever works.) How to start pppd with synchronous support: #!/bin/sh local=1.1.1.1 # your ip address here baud=38400 # needed, but ignored by serial driver # Change to the correct serial driver/port #dev=/dev/zsh0 dev=/dev/se_hdlc0 # Change the driver, nrzi mode, speed and clocking to match # your setup. # This configuration is for external clocking from the DCE connect="syncinit se_hdlc0 nrzi=no speed=64000 txc=rxc rxc=rxc" /usr/sbin/pppd $dev sync $baud novj noauth $local: connect "$connect" Sample Cisco router config excerpt: ! ! Cisco router setup as DCE with RS-232 DCE cable ! ! interface Serial0 ip address 1.1.1.2 255.255.255.0 encapsulation ppp clockrate 64000 no nrzi-encoding no shutdown ! ppp-2.4.5/SETUP000066400000000000000000000101601130035057700131430ustar00rootroot00000000000000 Configuring a PPP link. After you have compiled and installed this package, there are some configuration files which will generally need to be set up. The pppd(8) man page is the best reference for the full details; this file outlines the configuration process for the most common case, where this package is being used to enable a machine to dial an ISP and connect to the internet. The FAQ and README.linux files also provide useful information about setting up PPP. Dialling an ISP. **************** Usually, an ISP will assign an IP address to your machine, and will refuse to authenticate itself to you. Some ISPs require a username and password to be entered before PPP service commences, while others use PPP authentication (using either the PAP or CHAP protocols). The recommended way to set up to dial an ISP is for the system administrator to create a file under /etc/ppp/peers, named for the ISP that you will be dialling. For example, suppose the file is called /etc/ppp/peers/isp. This file would contain something like this: ttyS0 # modem is connected to /dev/ttyS0 38400 # run the serial port at 38400 baud crtscts # use hardware flow control noauth # don't require the ISP to authenticate itself defaultroute # use the ISP as our default route connect '/usr/sbin/chat -v -f /etc/ppp/chat-isp' If there are any other pppd options that should apply when calling this ISP, they can also be placed in this file. The /etc/ppp/chat-isp file named in the last line contains the script for chat(8) to use to dial the ISP and go through any username/ password authentication required before PPP service starts. Here is an example (for dialling an Annex terminal server): ABORT "NO CARRIER" ABORT "NO DIALTONE" ABORT "ERROR" ABORT "NO ANSWER" ABORT "BUSY" ABORT "Username/Password Incorrect" "" "at" OK "at&d2&c1" OK "atdt2479381" "name:" "^Uusername" "word:" "\qpassword" "annex" "ppp" "Switching to PPP-ppp-Switching to PPP" See the chat(8) man page for details of the script. If you are not sure how the initial dialog with your ISP will go, you could use a terminal emulator such as kermit or minicom to go through the process manually. If your ISP requires PAP or CHAP authentication, you will have to create a line in /etc/ppp/pap-secrets or /etc/ppp/chap-secrets like this: myhostname * "password" (Replace myhostname with the hostname of your machine.) At this point, you can initiate the link with the command: /usr/sbin/pppd call isp (N.B.: pppd might be installed in a different directory on some systems). This will return to the shell prompt immediately, as pppd will detach itself from its controlling terminal. (If you don't want it to do this, use the "nodetach" option.) Pppd will log messages describing the progress of the connection and any errors using the syslog facility (see the syslogd(8) and syslog.conf(5) man pages). Pppd issues messages using syslog facility daemon (or local2 if it has been compiled with debugging enabled); chat uses facility local2. It is often useful to see messages of priority notice or higher on the console. To see these, find the line in /etc/syslog.conf which has /dev/console on the right-hand side, and add `daemon.notice' on the left. This line should end up something like this: *.err;kern.debug;daemon,local2,auth.notice;mail.crit /dev/console If you want to see more messages from pppd, request messages of priority info or higher for facility daemon, like this: *.err;kern.debug;daemon.info;local2,auth.notice;mail.crit /dev/console It is also useful to add a line like this: daemon,local2.debug /etc/ppp/ppp-log If you do this, you will need to create an empty /etc/ppp/ppp-log file. After modifying syslog.conf, you will then need to send a HUP signal to syslogd (or reboot). When you wish terminate the PPP link, you should send a TERM or INTR signal to pppd. Pppd writes its process ID to a file called ppp.pid in /var/run (or /etc/ppp on older systems such as SunOS or Ultrix). Here is the PPP interface unit number, which will be 0 unless you have more than one PPP link running simultaneously. Thus you can terminate the link with a command like kill `cat /var/run/ppp0.pid` ppp-2.4.5/chat/000077500000000000000000000000001130035057700132415ustar00rootroot00000000000000ppp-2.4.5/chat/.gitignore000066400000000000000000000000051130035057700152240ustar00rootroot00000000000000chat ppp-2.4.5/chat/Makefile.linux000066400000000000000000000012651130035057700160430ustar00rootroot00000000000000# $Id: Makefile.linux,v 1.15 2006/06/04 05:07:46 paulus Exp $ DESTDIR = $(INSTROOT)@DESTDIR@ BINDIR = $(DESTDIR)/sbin MANDIR = $(DESTDIR)/share/man/man8 CDEF1= -DTERMIOS # Use the termios structure CDEF2= -DSIGTYPE=void # Standard definition CDEF3= -UNO_SLEEP # Use the usleep function CDEF4= -DFNDELAY=O_NDELAY # Old name value CDEFS= $(CDEF1) $(CDEF2) $(CDEF3) $(CDEF4) COPTS= -O2 -g -pipe CFLAGS= $(COPTS) $(CDEFS) INSTALL= install all: chat chat: chat.o $(CC) -o chat chat.o chat.o: chat.c $(CC) -c $(CFLAGS) -o chat.o chat.c install: chat mkdir -p $(BINDIR) $(MANDIR) $(INSTALL) -s -c chat $(BINDIR) $(INSTALL) -c -m 644 chat.8 $(MANDIR) clean: rm -f chat.o chat *~ ppp-2.4.5/chat/Makefile.sol2000066400000000000000000000004071130035057700155600ustar00rootroot00000000000000# # Makefile for chat on Solaris 2 # include ../Makedefs.com CFLAGS = $(COPTS) -DNO_USLEEP -DSOL2 all: chat chat: chat.o $(CC) -o chat chat.o install: chat $(INSTALL) -f $(BINDIR) chat $(INSTALL) -m 444 -f $(MANDIR)/man8 chat.8 clean: rm -f *~ *.o chat ppp-2.4.5/chat/chat.8000066400000000000000000000445771130035057700142720ustar00rootroot00000000000000.\" -*- nroff -*- .\" manual page [] for chat 1.8 .\" $Id: chat.8,v 1.11 2004/11/13 12:22:49 paulus Exp $ .\" SH section heading .\" SS subsection heading .\" LP paragraph .\" IP indented paragraph .\" TP hanging label .TH CHAT 8 "22 May 1999" "Chat Version 1.22" .SH NAME chat \- Automated conversational script with a modem .SH SYNOPSIS .B chat [ .I options ] .I script .SH DESCRIPTION .LP The \fIchat\fR program defines a conversational exchange between the computer and the modem. Its primary purpose is to establish the connection between the Point-to-Point Protocol Daemon (\fIpppd\fR) and the remote's \fIpppd\fR process. .SH OPTIONS .TP .B \-f \fI Read the chat script from the chat \fIfile\fR. The use of this option is mutually exclusive with the chat script parameters. The user must have read access to the file. Multiple lines are permitted in the file. Space or horizontal tab characters should be used to separate the strings. .TP .B \-t \fI Set the timeout for the expected string to be received. If the string is not received within the time limit then the reply string is not sent. An alternate reply may be sent or the script will fail if there is no alternate reply string. A failed script will cause the \fIchat\fR program to terminate with a non-zero error code. .TP .B \-r \fI Set the file for output of the report strings. If you use the keyword \fIREPORT\fR, the resulting strings are written to this file. If this option is not used and you still use \fIREPORT\fR keywords, the \fIstderr\fR file is used for the report strings. .TP .B \-e Start with the echo option turned on. Echoing may also be turned on or off at specific points in the chat script by using the \fIECHO\fR keyword. When echoing is enabled, all output from the modem is echoed to \fIstderr\fR. .TP .B \-E Enables environment variable substituion within chat scripts using the standard \fI$xxx\fR syntax. .TP .B \-v Request that the \fIchat\fR script be executed in a verbose mode. The \fIchat\fR program will then log the execution state of the chat script as well as all text received from the modem and the output strings sent to the modem. The default is to log through the SYSLOG; the logging method may be altered with the \-S and \-s flags. .TP .B \-V Request that the \fIchat\fR script be executed in a stderr verbose mode. The \fIchat\fR program will then log all text received from the modem and the output strings sent to the modem to the stderr device. This device is usually the local console at the station running the chat or pppd program. .TP .B \-s Use stderr. All log messages from '\-v' and all error messages will be sent to stderr. .TP .B \-S Do not use the SYSLOG. By default, error messages are sent to the SYSLOG. The use of \-S will prevent both log messages from '\-v' and error messages from being sent to the SYSLOG. .TP .B \-T \fI Pass in an arbitary string, usually a phone number, that will be substituted for the \\T substitution metacharacter in a send string. .TP .B \-U \fI Pass in a second string, usually a phone number, that will be substituted for the \\U substitution metacharacter in a send string. This is useful when dialing an ISDN terminal adapter that requires two numbers. .TP .B script If the script is not specified in a file with the \fI\-f\fR option then the script is included as parameters to the \fIchat\fR program. .SH CHAT SCRIPT .LP The \fIchat\fR script defines the communications. .LP A script consists of one or more "expect\-send" pairs of strings, separated by spaces, with an optional "subexpect\-subsend" string pair, separated by a dash as in the following example: .IP ogin:\-BREAK\-ogin: ppp ssword: hello2u2 .LP This line indicates that the \fIchat\fR program should expect the string "ogin:". If it fails to receive a login prompt within the time interval allotted, it is to send a break sequence to the remote and then expect the string "ogin:". If the first "ogin:" is received then the break sequence is not generated. .LP Once it received the login prompt the \fIchat\fR program will send the string ppp and then expect the prompt "ssword:". When it receives the prompt for the password, it will send the password hello2u2. .LP A carriage return is normally sent following the reply string. It is not expected in the "expect" string unless it is specifically requested by using the \\r character sequence. .LP The expect sequence should contain only what is needed to identify the string. Since it is normally stored on a disk file, it should not contain variable information. It is generally not acceptable to look for time strings, network identification strings, or other variable pieces of data as an expect string. .LP To help correct for characters which may be corrupted during the initial sequence, look for the string "ogin:" rather than "login:". It is possible that the leading "l" character may be received in error and you may never find the string even though it was sent by the system. For this reason, scripts look for "ogin:" rather than "login:" and "ssword:" rather than "password:". .LP A very simple script might look like this: .IP ogin: ppp ssword: hello2u2 .LP In other words, expect ....ogin:, send ppp, expect ...ssword:, send hello2u2. .LP In actual practice, simple scripts are rare. At the vary least, you should include sub-expect sequences should the original string not be received. For example, consider the following script: .IP ogin:\-\-ogin: ppp ssword: hello2u2 .LP This would be a better script than the simple one used earlier. This would look for the same login: prompt, however, if one was not received, a single return sequence is sent and then it will look for login: again. Should line noise obscure the first login prompt then sending the empty line will usually generate a login prompt again. .SH COMMENTS Comments can be embedded in the chat script. A comment is a line which starts with the \fB#\fR (hash) character in column 1. Such comment lines are just ignored by the chat program. If a '#' character is to be expected as the first character of the expect sequence, you should quote the expect string. If you want to wait for a prompt that starts with a # (hash) character, you would have to write something like this: .IP # Now wait for the prompt and send logout string .br \&'# ' logout .LP .SH SENDING DATA FROM A FILE If the string to send starts with an at sign (@), the rest of the string is taken to be the name of a file to read to get the string to send. If the last character of the data read is a newline, it is removed. The file can be a named pipe (or fifo) instead of a regular file. This provides a way for \fBchat\fR to communicate with another program, for example, a program to prompt the user and receive a password typed in. .LP .SH ABORT STRINGS Many modems will report the status of the call as a string. These strings may be \fBCONNECTED\fR or \fBNO CARRIER\fR or \fBBUSY\fR. It is often desirable to terminate the script should the modem fail to connect to the remote. The difficulty is that a script would not know exactly which modem string it may receive. On one attempt, it may receive \fBBUSY\fR while the next time it may receive \fBNO CARRIER\fR. .LP These "abort" strings may be specified in the script using the \fIABORT\fR sequence. It is written in the script as in the following example: .IP ABORT BUSY ABORT 'NO CARRIER' '' ATZ OK ATDT5551212 CONNECT .LP This sequence will expect nothing; and then send the string ATZ. The expected response to this is the string \fIOK\fR. When it receives \fIOK\fR, the string ATDT5551212 to dial the telephone. The expected string is \fICONNECT\fR. If the string \fICONNECT\fR is received the remainder of the script is executed. However, should the modem find a busy telephone, it will send the string \fIBUSY\fR. This will cause the string to match the abort character sequence. The script will then fail because it found a match to the abort string. If it received the string \fINO CARRIER\fR, it will abort for the same reason. Either string may be received. Either string will terminate the \fIchat\fR script. .SH CLR_ABORT STRINGS This sequence allows for clearing previously set \fBABORT\fR strings. \fBABORT\fR strings are kept in an array of a pre-determined size (at compilation time); \fBCLR_ABORT\fR will reclaim the space for cleared entries so that new strings can use that space. .SH SAY STRINGS The \fBSAY\fR directive allows the script to send strings to the user at the terminal via standard error. If \fBchat\fR is being run by pppd, and pppd is running as a daemon (detached from its controlling terminal), standard error will normally be redirected to the file /etc/ppp/connect\-errors. .LP \fBSAY\fR strings must be enclosed in single or double quotes. If carriage return and line feed are needed in the string to be output, you must explicitely add them to your string. .LP The SAY strings could be used to give progress messages in sections of the script where you want to have 'ECHO OFF' but still let the user know what is happening. An example is: .IP ABORT BUSY .br ECHO OFF .br SAY "Dialling your ISP...\\n" .br \&'' ATDT5551212 .br TIMEOUT 120 .br SAY "Waiting up to 2 minutes for connection ... " .br CONNECT '' .br SAY "Connected, now logging in ...\n" .br ogin: account .br ssword: pass .br $ \c SAY "Logged in OK ...\n" \fIetc ...\fR .LP This sequence will only present the SAY strings to the user and all the details of the script will remain hidden. For example, if the above script works, the user will see: .IP Dialling your ISP... .br Waiting up to 2 minutes for connection ... Connected, now logging in ... .br Logged in OK ... .LP .SH REPORT STRINGS A \fBreport\fR string is similar to the ABORT string. The difference is that the strings, and all characters to the next control character such as a carriage return, are written to the report file. .LP The report strings may be used to isolate the transmission rate of the modem's connect string and return the value to the chat user. The analysis of the report string logic occurs in conjunction with the other string processing such as looking for the expect string. The use of the same string for a report and abort sequence is probably not very useful, however, it is possible. .LP The report strings to no change the completion code of the program. .LP These "report" strings may be specified in the script using the \fIREPORT\fR sequence. It is written in the script as in the following example: .IP REPORT CONNECT ABORT BUSY '' ATDT5551212 CONNECT '' ogin: account .LP This sequence will expect nothing; and then send the string ATDT5551212 to dial the telephone. The expected string is \fICONNECT\fR. If the string \fICONNECT\fR is received the remainder of the script is executed. In addition the program will write to the expect\-file the string "CONNECT" plus any characters which follow it such as the connection rate. .SH CLR_REPORT STRINGS This sequence allows for clearing previously set \fBREPORT\fR strings. \fBREPORT\fR strings are kept in an array of a pre-determined size (at compilation time); \fBCLR_REPORT\fR will reclaim the space for cleared entries so that new strings can use that space. .SH ECHO The echo options controls whether the output from the modem is echoed to \fIstderr\fR. This option may be set with the \fI\-e\fR option, but it can also be controlled by the \fIECHO\fR keyword. The "expect\-send" pair \fIECHO\fR \fION\fR enables echoing, and \fIECHO\fR \fIOFF\fR disables it. With this keyword you can select which parts of the conversation should be visible. For instance, with the following script: .IP ABORT 'BUSY' .br ABORT 'NO CARRIER' .br '' ATZ .br OK\\r\\n ATD1234567 .br \\r\\n \\c .br ECHO ON .br CONNECT \\c .br ogin: account .LP all output resulting from modem configuration and dialing is not visible, but starting with the \fICONNECT\fR (or \fIBUSY\fR) message, everything will be echoed. .SH HANGUP The HANGUP options control whether a modem hangup should be considered as an error or not. This option is useful in scripts for dialling systems which will hang up and call your system back. The HANGUP options can be \fBON\fR or \fBOFF\fR. .br When HANGUP is set OFF and the modem hangs up (e.g., after the first stage of logging in to a callback system), \fBchat\fR will continue running the script (e.g., waiting for the incoming call and second stage login prompt). As soon as the incoming call is connected, you should use the \fBHANGUP ON\fR directive to reinstall normal hang up signal behavior. Here is an (simple) example script: .IP ABORT 'BUSY' .br '' ATZ .br OK\\r\\n ATD1234567 .br \\r\\n \\c .br CONNECT \\c .br \&'Callback login:' call_back_ID .br HANGUP OFF .br ABORT "Bad Login" .br \&'Callback Password:' Call_back_password .br TIMEOUT 120 .br CONNECT \\c .br HANGUP ON .br ABORT "NO CARRIER" .br ogin:\-\-BREAK\-\-ogin: real_account .br \fIetc ...\fR .LP .SH TIMEOUT The initial timeout value is 45 seconds. This may be changed using the \fB\-t\fR parameter. .LP To change the timeout value for the next expect string, the following example may be used: .IP ATZ OK ATDT5551212 CONNECT TIMEOUT 10 ogin:\-\-ogin: TIMEOUT 5 assword: hello2u2 .LP This will change the timeout to 10 seconds when it expects the login: prompt. The timeout is then changed to 5 seconds when it looks for the password prompt. .LP The timeout, once changed, remains in effect until it is changed again. .SH SENDING EOT The special reply string of \fIEOT\fR indicates that the chat program should send an EOT character to the remote. This is normally the End-of-file character sequence. A return character is not sent following the EOT. .PR The EOT sequence may be embedded into the send string using the sequence \fI^D\fR. .SH GENERATING BREAK The special reply string of \fIBREAK\fR will cause a break condition to be sent. The break is a special signal on the transmitter. The normal processing on the receiver is to change the transmission rate. It may be used to cycle through the available transmission rates on the remote until you are able to receive a valid login prompt. .PR The break sequence may be embedded into the send string using the \fI\\K\fR sequence. .SH ESCAPE SEQUENCES The expect and reply strings may contain escape sequences. All of the sequences are legal in the reply string. Many are legal in the expect. Those which are not valid in the expect sequence are so indicated. .TP .B '' Expects or sends a null string. If you send a null string then it will still send the return character. This sequence may either be a pair of apostrophe or quote characters. .TP .B \\\\b represents a backspace character. .TP .B \\\\c Suppresses the newline at the end of the reply string. This is the only method to send a string without a trailing return character. It must be at the end of the send string. For example, the sequence hello\\c will simply send the characters h, e, l, l, o. .I (not valid in expect.) .TP .B \\\\d Delay for one second. The program uses sleep(1) which will delay to a maximum of one second. .I (not valid in expect.) .TP .B \\\\K Insert a BREAK .I (not valid in expect.) .TP .B \\\\n Send a newline or linefeed character. .TP .B \\\\N Send a null character. The same sequence may be represented by \\0. .I (not valid in expect.) .TP .B \\\\p Pause for a fraction of a second. The delay is 1/10th of a second. .I (not valid in expect.) .TP .B \\\\q Suppress writing the string to the SYSLOG file. The string ?????? is written to the log in its place. .I (not valid in expect.) .TP .B \\\\r Send or expect a carriage return. .TP .B \\\\s Represents a space character in the string. This may be used when it is not desirable to quote the strings which contains spaces. The sequence 'HI TIM' and HI\\sTIM are the same. .TP .B \\\\t Send or expect a tab character. .TP .B \\\\T Send the phone number string as specified with the \fI\-T\fR option .I (not valid in expect.) .TP .B \\\\U Send the phone number 2 string as specified with the \fI\-U\fR option .I (not valid in expect.) .TP .B \\\\\\\\ Send or expect a backslash character. .TP .B \\\\ddd Collapse the octal digits (ddd) into a single ASCII character and send that character. .I (some characters are not valid in expect.) .TP .B \^^C Substitute the sequence with the control character represented by C. For example, the character DC1 (17) is shown as \^^Q. .I (some characters are not valid in expect.) .SH ENVIRONMENT VARIABLES Environment variables are available within chat scripts, if the \fI\-E\fR option was specified in the command line. The metacharacter \fI$\fR is used to introduce the name of the environment variable to substitute. If the substition fails, because the requested environment variable is not set, \fInothing\fR is replaced for the variable. .SH TERMINATION CODES The \fIchat\fR program will terminate with the following completion codes. .TP .B 0 The normal termination of the program. This indicates that the script was executed without error to the normal conclusion. .TP .B 1 One or more of the parameters are invalid or an expect string was too large for the internal buffers. This indicates that the program as not properly executed. .TP .B 2 An error occurred during the execution of the program. This may be due to a read or write operation failing for some reason or chat receiving a signal such as SIGINT. .TP .B 3 A timeout event occurred when there was an \fIexpect\fR string without having a "\-subsend" string. This may mean that you did not program the script correctly for the condition or that some unexpected event has occurred and the expected string could not be found. .TP .B 4 The first string marked as an \fIABORT\fR condition occurred. .TP .B 5 The second string marked as an \fIABORT\fR condition occurred. .TP .B 6 The third string marked as an \fIABORT\fR condition occurred. .TP .B 7 The fourth string marked as an \fIABORT\fR condition occurred. .TP .B ... The other termination codes are also strings marked as an \fIABORT\fR condition. .LP Using the termination code, it is possible to determine which event terminated the script. It is possible to decide if the string "BUSY" was received from the modem as opposed to "NO DIAL TONE". While the first event may be retried, the second will probably have little chance of succeeding during a retry. .SH SEE ALSO Additional information about \fIchat\fR scripts may be found with UUCP documentation. The \fIchat\fR script was taken from the ideas proposed by the scripts used by the \fIuucico\fR program. .LP uucico(1), uucp(1) .SH COPYRIGHT The \fIchat\fR program is in public domain. This is not the GNU public license. If it breaks then you get to keep both pieces. ppp-2.4.5/chat/chat.c000066400000000000000000001035541130035057700143340ustar00rootroot00000000000000/* * Chat -- a program for automatic session establishment (i.e. dial * the phone and log in). * * Standard termination codes: * 0 - successful completion of the script * 1 - invalid argument, expect string too large, etc. * 2 - error on an I/O operation or fatal error condition. * 3 - timeout waiting for a simple string. * 4 - the first string declared as "ABORT" * 5 - the second string declared as "ABORT" * 6 - ... and so on for successive ABORT strings. * * This software is in the public domain. * * ----------------- * 22-May-99 added environment substitutuion, enabled with -E switch. * Andreas Arens . * * 12-May-99 added a feature to read data to be sent from a file, * if the send string starts with @. Idea from gpk . * * added -T and -U option and \T and \U substitution to pass a phone * number into chat script. Two are needed for some ISDN TA applications. * Keith Dart * * * Added SAY keyword to send output to stderr. * This allows to turn ECHO OFF and to output specific, user selected, * text to give progress messages. This best works when stderr * exists (i.e.: pppd in nodetach mode). * * Added HANGUP directives to allow for us to be called * back. When HANGUP is set to NO, chat will not hangup at HUP signal. * We rely on timeouts in that case. * * Added CLR_ABORT to clear previously set ABORT string. This has been * dictated by the HANGUP above as "NO CARRIER" (for example) must be * an ABORT condition until we know the other host is going to close * the connection for call back. As soon as we have completed the * first stage of the call back sequence, "NO CARRIER" is a valid, non * fatal string. As soon as we got called back (probably get "CONNECT"), * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command. * Note that CLR_ABORT packs the abort_strings[] array so that we do not * have unused entries not being reclaimed. * * In the same vein as above, added CLR_REPORT keyword. * * Allow for comments. Line starting with '#' are comments and are * ignored. If a '#' is to be expected as the first character, the * expect string must be quoted. * * * Francis Demierre * Thu May 15 17:15:40 MET DST 1997 * * * Added -r "report file" switch & REPORT keyword. * Robert Geer * * Added -s "use stderr" and -S "don't use syslog" switches. * June 18, 1997 * Karl O. Pinc * * * Added -e "echo" switch & ECHO keyword * Dick Streefland * * * Considerable updates and modifications by * Al Longyear * Paul Mackerras * * * The original author is: * * Karl Fox * Morning Star Technologies, Inc. * 1760 Zollinger Road * Columbus, OH 43221 * (614)451-1883 * */ #ifndef __STDC__ #define const #endif #ifndef lint static const char rcsid[] = "$Id: chat.c,v 1.30 2004/01/17 05:47:55 carlsonj Exp $"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifndef TERMIO #undef TERMIOS #define TERMIOS #endif #ifdef TERMIO #include #endif #ifdef TERMIOS #include #endif #define STR_LEN 1024 #ifndef SIGTYPE #define SIGTYPE void #endif #undef __P #undef __V #ifdef __STDC__ #include #define __V(x) x #define __P(x) x #else #include #define __V(x) (va_alist) va_dcl #define __P(x) () #define const #endif #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif #ifdef SUNOS extern int sys_nerr; extern char *sys_errlist[]; #define memmove(to, from, n) bcopy(from, to, n) #define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\ "unknown error") #endif /*************** Micro getopt() *********************************************/ #define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \ (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\ &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0)) #define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \ (_O=4,(char*)0):(char*)0) #define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0) #define ARG(c,v) (c?(--c,*v++):(char*)0) static int _O = 0; /* Internal state */ /*************** Micro getopt() *********************************************/ char *program_name; #define MAX_ABORTS 50 #define MAX_REPORTS 50 #define DEFAULT_CHAT_TIMEOUT 45 int echo = 0; int verbose = 0; int to_log = 1; int to_stderr = 0; int Verbose = 0; int quiet = 0; int report = 0; int use_env = 0; int exit_code = 0; FILE* report_fp = (FILE *) 0; char *report_file = (char *) 0; char *chat_file = (char *) 0; char *phone_num = (char *) 0; char *phone_num2 = (char *) 0; int timeout = DEFAULT_CHAT_TIMEOUT; int have_tty_parameters = 0; #ifdef TERMIO #define term_parms struct termio #define get_term_param(param) ioctl(0, TCGETA, param) #define set_term_param(param) ioctl(0, TCSETA, param) struct termio saved_tty_parameters; #endif #ifdef TERMIOS #define term_parms struct termios #define get_term_param(param) tcgetattr(0, param) #define set_term_param(param) tcsetattr(0, TCSANOW, param) struct termios saved_tty_parameters; #endif char *abort_string[MAX_ABORTS], *fail_reason = (char *)0, fail_buffer[50]; int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0; int clear_abort_next = 0; char *report_string[MAX_REPORTS] ; char report_buffer[256] ; int n_reports = 0, report_next = 0, report_gathering = 0 ; int clear_report_next = 0; int say_next = 0, hup_next = 0; void *dup_mem __P((void *b, size_t c)); void *copy_of __P((char *s)); char *grow __P((char *s, char **p, size_t len)); void usage __P((void)); void msgf __P((const char *fmt, ...)); void fatal __P((int code, const char *fmt, ...)); SIGTYPE sigalrm __P((int signo)); SIGTYPE sigint __P((int signo)); SIGTYPE sigterm __P((int signo)); SIGTYPE sighup __P((int signo)); void unalarm __P((void)); void init __P((void)); void set_tty_parameters __P((void)); void echo_stderr __P((int)); void break_sequence __P((void)); void terminate __P((int status)); void do_file __P((char *chat_file)); int get_string __P((register char *string)); int put_string __P((register char *s)); int write_char __P((int c)); int put_char __P((int c)); int get_char __P((void)); void chat_send __P((register char *s)); char *character __P((int c)); void chat_expect __P((register char *s)); char *clean __P((register char *s, int sending)); void break_sequence __P((void)); void terminate __P((int status)); void pack_array __P((char **array, int end)); char *expect_strtok __P((char *, char *)); int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */ int main __P((int, char *[])); void *dup_mem(b, c) void *b; size_t c; { void *ans = malloc (c); if (!ans) fatal(2, "memory error!"); memcpy (ans, b, c); return ans; } void *copy_of (s) char *s; { return dup_mem (s, strlen (s) + 1); } /* grow a char buffer and keep a pointer offset */ char *grow(s, p, len) char *s; char **p; size_t len; { size_t l = *p - s; /* save p as distance into s */ s = realloc(s, len); if (!s) fatal(2, "memory error!"); *p = s + l; /* restore p */ return s; } /* * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \ * [ -r report-file ] \ * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]] * * Perform a UUCP-dialer-like chat script on stdin and stdout. */ int main(argc, argv) int argc; char **argv; { int option; char *arg; program_name = *argv; tzset(); while ((option = OPTION(argc, argv)) != 0) { switch (option) { case 'e': ++echo; break; case 'E': ++use_env; break; case 'v': ++verbose; break; case 'V': ++Verbose; break; case 's': ++to_stderr; break; case 'S': to_log = 0; break; case 'f': if ((arg = OPTARG(argc, argv)) != NULL) chat_file = copy_of(arg); else usage(); break; case 't': if ((arg = OPTARG(argc, argv)) != NULL) timeout = atoi(arg); else usage(); break; case 'r': arg = OPTARG (argc, argv); if (arg) { if (report_fp != NULL) fclose (report_fp); report_file = copy_of (arg); report_fp = fopen (report_file, "a"); if (report_fp != NULL) { if (verbose) fprintf (report_fp, "Opening \"%s\"...\n", report_file); report = 1; } } break; case 'T': if ((arg = OPTARG(argc, argv)) != NULL) phone_num = copy_of(arg); else usage(); break; case 'U': if ((arg = OPTARG(argc, argv)) != NULL) phone_num2 = copy_of(arg); else usage(); break; default: usage(); break; } } /* * Default the report file to the stderr location */ if (report_fp == NULL) report_fp = stderr; if (to_log) { #ifdef ultrix openlog("chat", LOG_PID); #else openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); if (verbose) setlogmask(LOG_UPTO(LOG_INFO)); else setlogmask(LOG_UPTO(LOG_WARNING)); #endif } init(); if (chat_file != NULL) { arg = ARG(argc, argv); if (arg != NULL) usage(); else do_file (chat_file); } else { while ((arg = ARG(argc, argv)) != NULL) { chat_expect(arg); if ((arg = ARG(argc, argv)) != NULL) chat_send(arg); } } terminate(0); return 0; } /* * Process a chat script when read from a file. */ void do_file (chat_file) char *chat_file; { int linect, sendflg; char *sp, *arg, quote; char buf [STR_LEN]; FILE *cfp; cfp = fopen (chat_file, "r"); if (cfp == NULL) fatal(1, "%s -- open failed: %m", chat_file); linect = 0; sendflg = 0; while (fgets(buf, STR_LEN, cfp) != NULL) { sp = strchr (buf, '\n'); if (sp) *sp = '\0'; linect++; sp = buf; /* lines starting with '#' are comments. If a real '#' is to be expected, it should be quoted .... */ if ( *sp == '#' ) continue; while (*sp != '\0') { if (*sp == ' ' || *sp == '\t') { ++sp; continue; } if (*sp == '"' || *sp == '\'') { quote = *sp++; arg = sp; while (*sp != quote) { if (*sp == '\0') fatal(1, "unterminated quote (line %d)", linect); if (*sp++ == '\\') { if (*sp != '\0') ++sp; } } } else { arg = sp; while (*sp != '\0' && *sp != ' ' && *sp != '\t') ++sp; } if (*sp != '\0') *sp++ = '\0'; if (sendflg) chat_send (arg); else chat_expect (arg); sendflg = !sendflg; } } fclose (cfp); } /* * We got an error parsing the command line. */ void usage() { fprintf(stderr, "\ Usage: %s [-e] [-E] [-v] [-V] [-t timeout] [-r report-file]\n\ [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name); exit(1); } char line[1024]; /* * Send a message to syslog and/or stderr. */ void msgf __V((const char *fmt, ...)) { va_list args; #ifdef __STDC__ va_start(args, fmt); #else char *fmt; va_start(args); fmt = va_arg(args, char *); #endif vfmtmsg(line, sizeof(line), fmt, args); if (to_log) syslog(LOG_INFO, "%s", line); if (to_stderr) fprintf(stderr, "%s\n", line); } /* * Print an error message and terminate. */ void fatal __V((int code, const char *fmt, ...)) { va_list args; #ifdef __STDC__ va_start(args, fmt); #else int code; char *fmt; va_start(args); code = va_arg(args, int); fmt = va_arg(args, char *); #endif vfmtmsg(line, sizeof(line), fmt, args); if (to_log) syslog(LOG_ERR, "%s", line); if (to_stderr) fprintf(stderr, "%s\n", line); terminate(code); } int alarmed = 0; SIGTYPE sigalrm(signo) int signo; { int flags; alarm(1); alarmed = 1; /* Reset alarm to avoid race window */ signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ if ((flags = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin: %m"); if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); if (verbose) msgf("alarm"); } void unalarm() { int flags; if ((flags = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin: %m"); if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); } SIGTYPE sigint(signo) int signo; { fatal(2, "SIGINT"); } SIGTYPE sigterm(signo) int signo; { fatal(2, "SIGTERM"); } SIGTYPE sighup(signo) int signo; { fatal(2, "SIGHUP"); } void init() { signal(SIGINT, sigint); signal(SIGTERM, sigterm); signal(SIGHUP, sighup); set_tty_parameters(); signal(SIGALRM, sigalrm); alarm(0); alarmed = 0; } void set_tty_parameters() { #if defined(get_term_param) term_parms t; if (get_term_param (&t) < 0) fatal(2, "Can't get terminal parameters: %m"); saved_tty_parameters = t; have_tty_parameters = 1; t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; t.c_oflag = 0; t.c_lflag = 0; t.c_cc[VERASE] = t.c_cc[VKILL] = 0; t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 0; if (set_term_param (&t) < 0) fatal(2, "Can't set terminal parameters: %m"); #endif } void break_sequence() { #ifdef TERMIOS tcsendbreak (0, 0); #endif } void terminate(status) int status; { static int terminating = 0; if (terminating) exit(status); terminating = 1; echo_stderr(-1); /* * Allow the last of the report string to be gathered before we terminate. */ if (report_gathering) { int c, rep_len; rep_len = strlen(report_buffer); while (rep_len + 1 <= sizeof(report_buffer)) { alarm(1); c = get_char(); alarm(0); if (c < 0 || iscntrl(c)) break; report_buffer[rep_len] = c; ++rep_len; } report_buffer[rep_len] = 0; fprintf (report_fp, "chat: %s\n", report_buffer); } if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { if (verbose) fprintf (report_fp, "Closing \"%s\".\n", report_file); fclose (report_fp); report_fp = (FILE *) NULL; } #if defined(get_term_param) if (have_tty_parameters) { if (set_term_param (&saved_tty_parameters) < 0) fatal(2, "Can't restore terminal parameters: %m"); } #endif exit(status); } /* * 'Clean up' this string. */ char *clean(s, sending) register char *s; int sending; /* set to 1 when sending (putting) this string. */ { char cur_chr; char *s1, *p, *phchar; int add_return = sending; size_t len = strlen(s) + 3; /* see len comments below */ #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) #define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \ || (((chr) >= 'a') && ((chr) <= 'z')) \ || (((chr) >= 'A') && ((chr) <= 'Z')) \ || (chr) == '_') p = s1 = malloc(len); if (!p) fatal(2, "memory error!"); while (*s) { cur_chr = *s++; if (cur_chr == '^') { cur_chr = *s++; if (cur_chr == '\0') { *p++ = '^'; break; } cur_chr &= 0x1F; if (cur_chr != 0) { *p++ = cur_chr; } continue; } if (use_env && cur_chr == '$') { /* ARI */ char c; phchar = s; while (isalnumx(*s)) s++; c = *s; /* save */ *s = '\0'; phchar = getenv(phchar); *s = c; /* restore */ if (phchar) { len += strlen(phchar); s1 = grow(s1, &p, len); while (*phchar) *p++ = *phchar++; } continue; } if (cur_chr != '\\') { *p++ = cur_chr; continue; } cur_chr = *s++; if (cur_chr == '\0') { if (sending) { *p++ = '\\'; *p++ = '\\'; /* +1 for len */ } break; } switch (cur_chr) { case 'b': *p++ = '\b'; break; case 'c': if (sending && *s == '\0') add_return = 0; else *p++ = cur_chr; break; case '\\': case 'K': case 'p': case 'd': if (sending) *p++ = '\\'; *p++ = cur_chr; break; case 'T': if (sending && phone_num) { len += strlen(phone_num); s1 = grow(s1, &p, len); for (phchar = phone_num; *phchar != '\0'; phchar++) *p++ = *phchar; } else { *p++ = '\\'; *p++ = 'T'; } break; case 'U': if (sending && phone_num2) { len += strlen(phone_num2); s1 = grow(s1, &p, len); for (phchar = phone_num2; *phchar != '\0'; phchar++) *p++ = *phchar; } else { *p++ = '\\'; *p++ = 'U'; } break; case 'q': quiet = 1; break; case 'r': *p++ = '\r'; break; case 'n': *p++ = '\n'; break; case 's': *p++ = ' '; break; case 't': *p++ = '\t'; break; case 'N': if (sending) { *p++ = '\\'; *p++ = '\0'; } else *p++ = 'N'; break; case '$': /* ARI */ if (use_env) { *p++ = cur_chr; break; } /* FALL THROUGH */ default: if (isoctal (cur_chr)) { cur_chr &= 0x07; if (isoctal (*s)) { cur_chr <<= 3; cur_chr |= *s++ - '0'; if (isoctal (*s)) { cur_chr <<= 3; cur_chr |= *s++ - '0'; } } if (cur_chr != 0 || sending) { if (sending && (cur_chr == '\\' || cur_chr == 0)) *p++ = '\\'; *p++ = cur_chr; } break; } if (sending) *p++ = '\\'; *p++ = cur_chr; break; } } if (add_return) *p++ = '\r'; /* +2 for len */ *p = '\0'; /* +3 for len */ return s1; } /* * A modified version of 'strtok'. This version skips \ sequences. */ char *expect_strtok (s, term) char *s, *term; { static char *str = ""; int escape_flag = 0; char *result; /* * If a string was specified then do initial processing. */ if (s) str = s; /* * If this is the escape flag then reset it and ignore the character. */ if (*str) result = str; else result = (char *) 0; while (*str) { if (escape_flag) { escape_flag = 0; ++str; continue; } if (*str == '\\') { ++str; escape_flag = 1; continue; } /* * If this is not in the termination string, continue. */ if (strchr (term, *str) == (char *) 0) { ++str; continue; } /* * This is the terminator. Mark the end of the string and stop. */ *str++ = '\0'; break; } return (result); } /* * Process the expect string */ void chat_expect (s) char *s; { char *expect; char *reply; if (strcmp(s, "HANGUP") == 0) { ++hup_next; return; } if (strcmp(s, "ABORT") == 0) { ++abort_next; return; } if (strcmp(s, "CLR_ABORT") == 0) { ++clear_abort_next; return; } if (strcmp(s, "REPORT") == 0) { ++report_next; return; } if (strcmp(s, "CLR_REPORT") == 0) { ++clear_report_next; return; } if (strcmp(s, "TIMEOUT") == 0) { ++timeout_next; return; } if (strcmp(s, "ECHO") == 0) { ++echo_next; return; } if (strcmp(s, "SAY") == 0) { ++say_next; return; } /* * Fetch the expect and reply string. */ for (;;) { expect = expect_strtok (s, "-"); s = (char *) 0; if (expect == (char *) 0) return; reply = expect_strtok (s, "-"); /* * Handle the expect string. If successful then exit. */ if (get_string (expect)) return; /* * If there is a sub-reply string then send it. Otherwise any condition * is terminal. */ if (reply == (char *) 0 || exit_code != 3) break; chat_send (reply); } /* * The expectation did not occur. This is terminal. */ if (fail_reason) msgf("Failed (%s)", fail_reason); else msgf("Failed"); terminate(exit_code); } /* * Translate the input character to the appropriate string for printing * the data. */ char *character(c) int c; { static char string[10]; char *meta; meta = (c & 0x80) ? "M-" : ""; c &= 0x7F; if (c < 32) sprintf(string, "%s^%c", meta, (int)c + '@'); else if (c == 127) sprintf(string, "%s^?", meta); else sprintf(string, "%s%c", meta, c); return (string); } /* * process the reply string */ void chat_send (s) register char *s; { char file_data[STR_LEN]; if (say_next) { say_next = 0; s = clean(s, 1); write(2, s, strlen(s)); free(s); return; } if (hup_next) { hup_next = 0; if (strcmp(s, "OFF") == 0) signal(SIGHUP, SIG_IGN); else signal(SIGHUP, sighup); return; } if (echo_next) { echo_next = 0; echo = (strcmp(s, "ON") == 0); return; } if (abort_next) { char *s1; abort_next = 0; if (n_aborts >= MAX_ABORTS) fatal(2, "Too many ABORT strings"); s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long ABORT string ('%v')", s); abort_string[n_aborts++] = s1; if (verbose) msgf("abort on (%v)", s); return; } if (clear_abort_next) { char *s1; int i; int old_max; int pack = 0; clear_abort_next = 0; s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); old_max = n_aborts; for (i=0; i < n_aborts; i++) { if ( strcmp(s1,abort_string[i]) == 0 ) { free(abort_string[i]); abort_string[i] = NULL; pack++; n_aborts--; if (verbose) msgf("clear abort on (%v)", s); } } free(s1); if (pack) pack_array(abort_string,old_max); return; } if (report_next) { char *s1; report_next = 0; if (n_reports >= MAX_REPORTS) fatal(2, "Too many REPORT strings"); s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long REPORT string ('%v')", s); report_string[n_reports++] = s1; if (verbose) msgf("report (%v)", s); return; } if (clear_report_next) { char *s1; int i; int old_max; int pack = 0; clear_report_next = 0; s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long REPORT string ('%v')", s); old_max = n_reports; for (i=0; i < n_reports; i++) { if ( strcmp(s1,report_string[i]) == 0 ) { free(report_string[i]); report_string[i] = NULL; pack++; n_reports--; if (verbose) msgf("clear report (%v)", s); } } free(s1); if (pack) pack_array(report_string,old_max); return; } if (timeout_next) { timeout_next = 0; timeout = atoi(s); if (timeout <= 0) timeout = DEFAULT_CHAT_TIMEOUT; if (verbose) msgf("timeout set to %d seconds", timeout); return; } /* * The syntax @filename means read the string to send from the * file `filename'. */ if (s[0] == '@') { /* skip the @ and any following white-space */ char *fn = s; while (*++fn == ' ' || *fn == '\t') ; if (*fn != 0) { FILE *f; int n = 0; /* open the file and read until STR_LEN-1 bytes or end-of-file */ f = fopen(fn, "r"); if (f == NULL) fatal(1, "%s -- open failed: %m", fn); while (n < STR_LEN - 1) { int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f); if (nr < 0) fatal(1, "%s -- read error", fn); if (nr == 0) break; n += nr; } fclose(f); /* use the string we got as the string to send, but trim off the final newline if any. */ if (n > 0 && file_data[n-1] == '\n') --n; file_data[n] = 0; s = file_data; } } if (strcmp(s, "EOT") == 0) s = "^D\\c"; else if (strcmp(s, "BREAK") == 0) s = "\\K\\c"; if (!put_string(s)) fatal(1, "Failed"); } int get_char() { int status; char c; status = read(0, &c, 1); switch (status) { case 1: return ((int)c & 0x7F); default: msgf("warning: read() on stdin returned %d", status); case -1: if ((status = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin: %m"); if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); return (-1); } } int put_char(c) int c; { int status; char ch = c; usleep(10000); /* inter-character typing delay (?) */ status = write(1, &ch, 1); switch (status) { case 1: return (0); default: msgf("warning: write() on stdout returned %d", status); case -1: if ((status = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin, %m"); if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); return (-1); } } int write_char (c) int c; { if (alarmed || put_char(c) < 0) { alarm(0); alarmed = 0; if (verbose) { if (errno == EINTR || errno == EWOULDBLOCK) msgf(" -- write timed out"); else msgf(" -- write failed: %m"); } return (0); } return (1); } int put_string (s) register char *s; { quiet = 0; s = clean(s, 1); if (verbose) { if (quiet) msgf("send (?????\?)"); else msgf("send (%v)", s); } alarm(timeout); alarmed = 0; while (*s) { register char c = *s++; if (c != '\\') { if (!write_char (c)) return 0; continue; } c = *s++; switch (c) { case 'd': sleep(1); break; case 'K': break_sequence(); break; case 'p': usleep(10000); /* 1/100th of a second (arg is microseconds) */ break; default: if (!write_char (c)) return 0; break; } } alarm(0); alarmed = 0; return (1); } /* * Echo a character to stderr. * When called with -1, a '\n' character is generated when * the cursor is not at the beginning of a line. */ void echo_stderr(n) int n; { static int need_lf; char *s; switch (n) { case '\r': /* ignore '\r' */ break; case -1: if (need_lf == 0) break; /* fall through */ case '\n': write(2, "\n", 1); need_lf = 0; break; default: s = character(n); write(2, s, strlen(s)); need_lf = 1; break; } } /* * 'Wait for' this string to appear on this file descriptor. */ int get_string(string) register char *string; { char temp[STR_LEN]; int c, printed = 0, len, minlen; register char *s = temp, *end = s + STR_LEN; char *logged = temp; fail_reason = (char *)0; string = clean(string, 0); len = strlen(string); minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; if (verbose) msgf("expect (%v)", string); if (len > STR_LEN) { msgf("expect string is too long"); exit_code = 1; return 0; } if (len == 0) { if (verbose) msgf("got it"); return (1); } alarm(timeout); alarmed = 0; while ( ! alarmed && (c = get_char()) >= 0) { int n, abort_len, report_len; if (echo) echo_stderr(c); if (verbose && c == '\n') { if (s == logged) msgf(""); /* blank line */ else msgf("%0.*v", s - logged, logged); logged = s + 1; } *s++ = c; if (verbose && s >= logged + 80) { msgf("%0.*v", s - logged, logged); logged = s; } if (Verbose) { if (c == '\n') fputc( '\n', stderr ); else if (c != '\r') fprintf( stderr, "%s", character(c) ); } if (!report_gathering) { for (n = 0; n < n_reports; ++n) { if ((report_string[n] != (char*) NULL) && s - temp >= (report_len = strlen(report_string[n])) && strncmp(s - report_len, report_string[n], report_len) == 0) { time_t time_now = time ((time_t*) NULL); struct tm* tm_now = localtime (&time_now); strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); strcat (report_buffer, report_string[n]); report_string[n] = (char *) NULL; report_gathering = 1; break; } } } else { if (!iscntrl (c)) { int rep_len = strlen (report_buffer); report_buffer[rep_len] = c; report_buffer[rep_len + 1] = '\0'; } else { report_gathering = 0; fprintf (report_fp, "chat: %s\n", report_buffer); } } if (s - temp >= len && c == string[len - 1] && strncmp(s - len, string, len) == 0) { if (verbose) { if (s > logged) msgf("%0.*v", s - logged, logged); msgf(" -- got it\n"); } alarm(0); alarmed = 0; return (1); } for (n = 0; n < n_aborts; ++n) { if (s - temp >= (abort_len = strlen(abort_string[n])) && strncmp(s - abort_len, abort_string[n], abort_len) == 0) { if (verbose) { if (s > logged) msgf("%0.*v", s - logged, logged); msgf(" -- failed"); } alarm(0); alarmed = 0; exit_code = n + 4; strcpy(fail_reason = fail_buffer, abort_string[n]); return (0); } } if (s >= end) { if (logged < s - minlen) { if (verbose) msgf("%0.*v", s - logged, logged); logged = s; } s -= minlen; memmove(temp, s, minlen); logged = temp + (logged - s); s = temp + minlen; } if (alarmed && verbose) msgf("warning: alarm synchronization problem"); } alarm(0); if (verbose && printed) { if (alarmed) msgf(" -- read timed out"); else msgf(" -- read failed: %m"); } exit_code = 3; alarmed = 0; return (0); } /* * Gross kludge to handle Solaris versions >= 2.6 having usleep. */ #ifdef SOL2 #include #if MAXUID > 65536 /* then this is Solaris 2.6 or later */ #undef NO_USLEEP #endif #endif /* SOL2 */ #ifdef NO_USLEEP #include #include /* usleep -- support routine for 4.2BSD system call emulations last edit: 29-Oct-1984 D A Gwyn */ extern int select(); int usleep( usec ) /* returns 0 if ok, else -1 */ long usec; /* delay in microseconds */ { static struct { /* `timeval' */ long tv_sec; /* seconds */ long tv_usec; /* microsecs */ } delay; /* _select() timeout */ delay.tv_sec = usec / 1000000L; delay.tv_usec = usec % 1000000L; return select(0, (long *)0, (long *)0, (long *)0, &delay); } #endif void pack_array (array, end) char **array; /* The address of the array of string pointers */ int end; /* The index of the next free entry before CLR_ */ { int i, j; for (i = 0; i < end; i++) { if (array[i] == NULL) { for (j = i+1; j < end; ++j) if (array[j] != NULL) array[i++] = array[j]; for (; i < end; ++i) array[i] = NULL; break; } } } /* * vfmtmsg - format a message into a buffer. Like vsprintf except we * also specify the length of the output buffer, and we handle the * %m (error message) format. * Doesn't do floating-point formats. * Returns the number of chars put into buf. */ #define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) int vfmtmsg(buf, buflen, fmt, args) char *buf; int buflen; const char *fmt; va_list args; { int c, i, n; int width, prec, fillch; int base, len, neg, quoted; unsigned long val = 0; char *str, *buf0; const char *f; unsigned char *p; char num[32]; static char hexchars[] = "0123456789abcdef"; buf0 = buf; --buflen; while (buflen > 0) { for (f = fmt; *f != '%' && *f != 0; ++f) ; if (f > fmt) { len = f - fmt; if (len > buflen) len = buflen; memcpy(buf, fmt, len); buf += len; buflen -= len; fmt = f; } if (*fmt == 0) break; c = *++fmt; width = prec = 0; fillch = ' '; if (c == '0') { fillch = '0'; c = *++fmt; } if (c == '*') { width = va_arg(args, int); c = *++fmt; } else { while (isdigit(c)) { width = width * 10 + c - '0'; c = *++fmt; } } if (c == '.') { c = *++fmt; if (c == '*') { prec = va_arg(args, int); c = *++fmt; } else { while (isdigit(c)) { prec = prec * 10 + c - '0'; c = *++fmt; } } } str = 0; base = 0; neg = 0; ++fmt; switch (c) { case 'd': i = va_arg(args, int); if (i < 0) { neg = 1; val = -i; } else val = i; base = 10; break; case 'o': val = va_arg(args, unsigned int); base = 8; break; case 'x': val = va_arg(args, unsigned int); base = 16; break; case 'p': val = (unsigned long) va_arg(args, void *); base = 16; neg = 2; break; case 's': str = va_arg(args, char *); break; case 'c': num[0] = va_arg(args, int); num[1] = 0; str = num; break; case 'm': str = strerror(errno); break; case 'v': /* "visible" string */ case 'q': /* quoted string */ quoted = c == 'q'; p = va_arg(args, unsigned char *); if (fillch == '0' && prec > 0) { n = prec; } else { n = strlen((char *)p); if (prec > 0 && prec < n) n = prec; } while (n > 0 && buflen > 0) { c = *p++; --n; if (!quoted && c >= 0x80) { OUTCHAR('M'); OUTCHAR('-'); c -= 0x80; } if (quoted && (c == '"' || c == '\\')) OUTCHAR('\\'); if (c < 0x20 || (0x7f <= c && c < 0xa0)) { if (quoted) { OUTCHAR('\\'); switch (c) { case '\t': OUTCHAR('t'); break; case '\n': OUTCHAR('n'); break; case '\b': OUTCHAR('b'); break; case '\f': OUTCHAR('f'); break; default: OUTCHAR('x'); OUTCHAR(hexchars[c >> 4]); OUTCHAR(hexchars[c & 0xf]); } } else { if (c == '\t') OUTCHAR(c); else { OUTCHAR('^'); OUTCHAR(c ^ 0x40); } } } else OUTCHAR(c); } continue; default: *buf++ = '%'; if (c != '%') --fmt; /* so %z outputs %z etc. */ --buflen; continue; } if (base != 0) { str = num + sizeof(num); *--str = 0; while (str > num + neg) { *--str = hexchars[val % base]; val = val / base; if (--prec <= 0 && val == 0) break; } switch (neg) { case 1: *--str = '-'; break; case 2: *--str = 'x'; *--str = '0'; break; } len = num + sizeof(num) - 1 - str; } else { len = strlen(str); if (prec > 0 && len > prec) len = prec; } if (width > 0) { if (width > buflen) width = buflen; if ((n = width - len) > 0) { buflen -= n; for (; n > 0; --n) *buf++ = fillch; } } if (len > buflen) len = buflen; memcpy(buf, str, len); buf += len; buflen -= len; } *buf = 0; return buf - buf0; } ppp-2.4.5/common/000077500000000000000000000000001130035057700136125ustar00rootroot00000000000000ppp-2.4.5/common/zlib.c000066400000000000000000005343051130035057700147300ustar00rootroot00000000000000/* * This file is derived from various .h and .c files from the zlib-1.0.4 * distribution by Jean-loup Gailly and Mark Adler, with some additions * by Paul Mackerras to aid in implementing Deflate compression and * decompression for PPP packets. See zlib.h for conditions of * distribution and use. * * Changes that have been made include: * - added Z_PACKET_FLUSH (see zlib.h for details) * - added inflateIncomp and deflateOutputPending * - allow strm->next_out to be NULL, meaning discard the output * * $Id: zlib.c,v 1.12 2002/04/02 13:34:03 dfs Exp $ */ /* * ==FILEVERSION 971210== * * This marker is used by the Linux installation script to determine * whether an up-to-date version of this file is already installed. */ #define NO_DUMMY_DECL #define NO_ZCFUNCS #define MY_ZCALLOC #if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL)) #define inflate inflate_ppp /* FreeBSD already has an inflate :-( */ #endif /* +++ zutil.h */ /* zutil.h -- internal interface and configuration of the compression library * Copyright (C) 1995-1996 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* From: zutil.h,v 1.16 1996/07/24 13:41:13 me Exp $ */ #ifndef _Z_UTIL_H #define _Z_UTIL_H #include "zlib.h" #if defined(KERNEL) || defined(_KERNEL) /* Assume this is a *BSD or SVR4 kernel */ #include #include #include #undef u # define HAVE_MEMCPY # define memcpy(d, s, n) bcopy((s), (d), (n)) # define memset(d, v, n) bzero((d), (n)) # define memcmp bcmp #else #if defined(__KERNEL__) /* Assume this is a Linux kernel */ #include #define HAVE_MEMCPY #else /* not kernel */ #if defined(MSDOS)||defined(VMS)||defined(CRAY)||defined(WIN32)||defined(RISCOS) # include # include #else extern int errno; #endif #ifdef STDC # include # include #endif #endif /* __KERNEL__ */ #endif /* _KERNEL || KERNEL */ #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ typedef unsigned char uch; typedef uch FAR uchf; typedef unsigned short ush; typedef ush FAR ushf; typedef unsigned long ulg; extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ #define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] #define ERR_RETURN(strm,err) \ return (strm->msg = (char*)ERR_MSG(err), (err)) /* To be used only when the state is known to be valid */ /* common constants */ #ifndef DEF_WBITS # define DEF_WBITS MAX_WBITS #endif /* default windowBits for decompression. MAX_WBITS is for compression only */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default memLevel */ #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 /* The three kinds of block type */ #define MIN_MATCH 3 #define MAX_MATCH 258 /* The minimum and maximum match lengths */ #define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ /* target dependencies */ #ifdef MSDOS # define OS_CODE 0x00 # ifdef __TURBOC__ # include # else /* MSC or DJGPP */ # include # endif #endif #ifdef OS2 # define OS_CODE 0x06 #endif #ifdef WIN32 /* Window 95 & Windows NT */ # define OS_CODE 0x0b #endif #if defined(VAXC) || defined(VMS) # define OS_CODE 0x02 # define FOPEN(name, mode) \ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") #endif #ifdef AMIGA # define OS_CODE 0x01 #endif #if defined(ATARI) || defined(atarist) # define OS_CODE 0x05 #endif #ifdef MACOS # define OS_CODE 0x07 #endif #ifdef __50SERIES /* Prime/PRIMOS */ # define OS_CODE 0x0F #endif #ifdef TOPS20 # define OS_CODE 0x0a #endif #if defined(_BEOS_) || defined(RISCOS) # define fdopen(fd,mode) NULL /* No fdopen() */ #endif /* Common defaults */ #ifndef OS_CODE # define OS_CODE 0x03 /* assume Unix */ #endif #ifndef FOPEN # define FOPEN(name, mode) fopen((name), (mode)) #endif /* functions */ #ifdef HAVE_STRERROR extern char *strerror OF((int)); # define zstrerror(errnum) strerror(errnum) #else # define zstrerror(errnum) "" #endif #if defined(pyr) # define NO_MEMCPY #endif #if (defined(M_I86SM) || defined(M_I86MM)) && !defined(_MSC_VER) /* Use our own functions for small and medium model with MSC <= 5.0. * You may have to use the same strategy for Borland C (untested). */ # define NO_MEMCPY #endif #if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) # define HAVE_MEMCPY #endif #ifdef HAVE_MEMCPY # ifdef SMALL_MEDIUM /* MSDOS small or medium model */ # define zmemcpy _fmemcpy # define zmemcmp _fmemcmp # define zmemzero(dest, len) _fmemset(dest, 0, len) # else # define zmemcpy memcpy # define zmemcmp memcmp # define zmemzero(dest, len) memset(dest, 0, len) # endif #else extern void zmemcpy OF((Bytef* dest, Bytef* source, uInt len)); extern int zmemcmp OF((Bytef* s1, Bytef* s2, uInt len)); extern void zmemzero OF((Bytef* dest, uInt len)); #endif /* Diagnostic functions */ #ifdef DEBUG_ZLIB # include # ifndef verbose # define verbose 0 # endif extern void z_error OF((char *m)); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) fprintf x # define Tracev(x) {if (verbose) fprintf x ;} # define Tracevv(x) {if (verbose>1) fprintf x ;} # define Tracec(c,x) {if (verbose && (c)) fprintf x ;} # define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} #else # define Assert(cond,msg) # define Trace(x) # define Tracev(x) # define Tracevv(x) # define Tracec(c,x) # define Tracecv(c,x) #endif typedef uLong (*check_func) OF((uLong check, const Bytef *buf, uInt len)); voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); void zcfree OF((voidpf opaque, voidpf ptr)); #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} #endif /* _Z_UTIL_H */ /* --- zutil.h */ /* +++ deflate.h */ /* deflate.h -- internal compression state * Copyright (C) 1995-1996 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* From: deflate.h,v 1.10 1996/07/02 12:41:00 me Exp $ */ #ifndef _DEFLATE_H #define _DEFLATE_H /* #include "zutil.h" */ /* =========================================================================== * Internal compression state. */ #define LENGTH_CODES 29 /* number of length codes, not counting the special END_BLOCK code */ #define LITERALS 256 /* number of literal bytes 0..255 */ #define L_CODES (LITERALS+1+LENGTH_CODES) /* number of Literal or Length codes, including the END_BLOCK code */ #define D_CODES 30 /* number of distance codes */ #define BL_CODES 19 /* number of codes used to transfer the bit lengths */ #define HEAP_SIZE (2*L_CODES+1) /* maximum heap size */ #define MAX_BITS 15 /* All codes must not exceed MAX_BITS bits */ #define INIT_STATE 42 #define BUSY_STATE 113 #define FINISH_STATE 666 /* Stream status */ /* Data structure describing a single value and its code string. */ typedef struct ct_data_s { union { ush freq; /* frequency count */ ush code; /* bit string */ } fc; union { ush dad; /* father node in Huffman tree */ ush len; /* length of bit string */ } dl; } FAR ct_data; #define Freq fc.freq #define Code fc.code #define Dad dl.dad #define Len dl.len typedef struct static_tree_desc_s static_tree_desc; typedef struct tree_desc_s { ct_data *dyn_tree; /* the dynamic tree */ int max_code; /* largest code with non zero frequency */ static_tree_desc *stat_desc; /* the corresponding static tree */ } FAR tree_desc; typedef ush Pos; typedef Pos FAR Posf; typedef unsigned IPos; /* A Pos is an index in the character window. We use short instead of int to * save space in the various tables. IPos is used only for parameter passing. */ typedef struct deflate_state { z_streamp strm; /* pointer back to this zlib stream */ int status; /* as the name implies */ Bytef *pending_buf; /* output still pending */ ulg pending_buf_size; /* size of pending_buf */ Bytef *pending_out; /* next pending byte to output to the stream */ int pending; /* nb of bytes in the pending buffer */ int noheader; /* suppress zlib header and adler32 */ Byte data_type; /* UNKNOWN, BINARY or ASCII */ Byte method; /* STORED (for zip only) or DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ /* used by deflate.c: */ uInt w_size; /* LZ77 window size (32K by default) */ uInt w_bits; /* log2(w_size) (8..16) */ uInt w_mask; /* w_size - 1 */ Bytef *window; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. Also, it limits * the window size to 64K, which is quite useful on MSDOS. * To do: use the user input buffer as sliding window. */ ulg window_size; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ Posf *prev; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ Posf *head; /* Heads of the hash chains or NIL. */ uInt ins_h; /* hash index of string to be inserted */ uInt hash_size; /* number of elements in hash table */ uInt hash_bits; /* log2(hash_size) */ uInt hash_mask; /* hash_size-1 */ uInt hash_shift; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ long block_start; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ uInt match_length; /* length of best match */ IPos prev_match; /* previous match */ int match_available; /* set if previous match exists */ uInt strstart; /* start of string to insert */ uInt match_start; /* start of matching string */ uInt lookahead; /* number of valid bytes ahead in window */ uInt prev_length; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ uInt max_chain_length; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ uInt max_lazy_match; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ # define max_insert_length max_lazy_match /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ int level; /* compression level (1..9) */ int strategy; /* favor or force Huffman coding*/ uInt good_match; /* Use a faster search when the previous match is longer than this */ int nice_match; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn't use ct_data typedef below to supress compiler warning */ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ struct tree_desc_s l_desc; /* desc. for literal tree */ struct tree_desc_s d_desc; /* desc. for distance tree */ struct tree_desc_s bl_desc; /* desc. for bit length tree */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ int heap_len; /* number of elements in the heap */ int heap_max; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ uch depth[2*L_CODES+1]; /* Depth of each subtree used as tie breaker for trees of equal frequency */ uchf *l_buf; /* buffer for literals or lengths */ uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can't count above 4 */ uInt last_lit; /* running index in l_buf */ ushf *d_buf; /* Buffer for distances. To simplify the code, d_buf and l_buf have * the same number of elements. To use different lengths, an extra flag * array would be necessary. */ ulg opt_len; /* bit length of current block with optimal trees */ ulg static_len; /* bit length of current block with static trees */ ulg compressed_len; /* total bit length of compressed file */ uInt matches; /* number of string matches in current block */ int last_eob_len; /* bit length of EOB code for last block */ #ifdef DEBUG_ZLIB ulg bits_sent; /* bit length of the compressed data */ #endif ush bi_buf; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ int bi_valid; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ } FAR deflate_state; /* Output a byte on the stream. * IN assertion: there is enough room in pending_buf. */ #define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* Minimum amount of lookahead, except at the end of the input file. * See deflate.c for comments about the MIN_MATCH+1. */ #define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) /* In order to simplify the code, particularly on 16 bit machines, match * distances are limited to MAX_DIST instead of WSIZE. */ /* in trees.c */ void _tr_init OF((deflate_state *s)); int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); ulg _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, int eof)); void _tr_align OF((deflate_state *s)); void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, int eof)); void _tr_stored_type_only OF((deflate_state *)); #endif /* --- deflate.h */ /* +++ deflate.c */ /* deflate.c -- compress data using the deflation algorithm * Copyright (C) 1995-1996 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process depends on being able to identify portions * of the input text which are identical to earlier input (within a * sliding window trailing behind the input currently being processed). * * The most straightforward technique turns out to be the fastest for * most input files: try all possible matches and select the longest. * The key feature of this algorithm is that insertions into the string * dictionary are very simple and thus fast, and deletions are avoided * completely. Insertions are performed at each input character, whereas * string matches are performed only when the previous match ends. So it * is preferable to spend more time in matches to allow very fast string * insertions and avoid deletions. The matching algorithm for small * strings is inspired from that of Rabin & Karp. A brute force approach * is used to find longer strings when a small match has been found. * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze * (by Leonid Broukhis). * A previous version of this file used a more sophisticated algorithm * (by Fiala and Greene) which is guaranteed to run in linear amortized * time, but has a larger average cost, uses more memory and is patented. * However the F&G algorithm may be faster for some highly redundant * files if the parameter max_chain_length (described below) is too large. * * ACKNOWLEDGEMENTS * * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and * I found it in 'freeze' written by Leonid Broukhis. * Thanks to many people for bug reports and testing. * * REFERENCES * * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". * Available in ftp://ds.internic.net/rfc/rfc1951.txt * * A description of the Rabin and Karp algorithm is given in the book * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. * * Fiala,E.R., and Greene,D.H. * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 * */ /* From: deflate.c,v 1.15 1996/07/24 13:40:58 me Exp $ */ /* #include "deflate.h" */ char deflate_copyright[] = " deflate 1.0.4 Copyright 1995-1996 Jean-loup Gailly "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* =========================================================================== * Function prototypes. */ typedef enum { need_more, /* block not completed, need more input or more output */ block_done, /* block flush performed */ finish_started, /* finish started, need only more output at next deflate */ finish_done /* finish done, accept no more input or output */ } block_state; typedef block_state (*compress_func) OF((deflate_state *s, int flush)); /* Compression function. Returns the block state after the call. */ local void fill_window OF((deflate_state *s)); local block_state deflate_stored OF((deflate_state *s, int flush)); local block_state deflate_fast OF((deflate_state *s, int flush)); local block_state deflate_slow OF((deflate_state *s, int flush)); local void lm_init OF((deflate_state *s)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_streamp strm)); local int read_buf OF((z_streamp strm, charf *buf, unsigned size)); #ifdef ASMV void match_init OF((void)); /* asm code initialization */ uInt longest_match OF((deflate_state *s, IPos cur_match)); #else local uInt longest_match OF((deflate_state *s, IPos cur_match)); #endif #ifdef DEBUG_ZLIB local void check_match OF((deflate_state *s, IPos start, IPos match, int length)); #endif /* =========================================================================== * Local data */ #define NIL 0 /* Tail of hash chains */ #ifndef TOO_FAR # define TOO_FAR 4096 #endif /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* Minimum amount of lookahead, except at the end of the input file. * See deflate.c for comments about the MIN_MATCH+1. */ /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ typedef struct config_s { ush good_length; /* reduce lazy search above this match length */ ush max_lazy; /* do not perform lazy search above this match length */ ush nice_length; /* quit search above this match length */ ush max_chain; compress_func func; } config; local config configuration_table[10] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */ /* 2 */ {4, 5, 16, 8, deflate_fast}, /* 3 */ {4, 6, 32, 32, deflate_fast}, /* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ /* 5 */ {8, 16, 32, 32, deflate_slow}, /* 6 */ {8, 16, 128, 128, deflate_slow}, /* 7 */ {8, 32, 128, 256, deflate_slow}, /* 8 */ {32, 128, 258, 1024, deflate_slow}, /* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */ /* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different * meaning. */ #define EQUAL 0 /* result of memcmp for equal strings */ #ifndef NO_DUMMY_DECL struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ #endif /* =========================================================================== * Update a hash value with the given input byte * IN assertion: all calls to to UPDATE_HASH are made with consecutive * input characters, so that a running hash key can be computed from the * previous key instead of complete recalculation each time. */ #define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) /* =========================================================================== * Insert string str in the dictionary and set match_head to the previous head * of the hash chain (the most recent string with same hash key). Return * the previous length of the hash chain. * IN assertion: all calls to to INSERT_STRING are made with consecutive * input characters and the first MIN_MATCH bytes of str are valid * (except for the last MIN_MATCH-1 bytes of the input file). */ #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) /* =========================================================================== * Initialize the hash table (avoiding 64K overflow for 16 bit systems). * prev[] will be initialized on the fly. */ #define CLEAR_HASH(s) \ s->head[s->hash_size-1] = NIL; \ zmemzero((charf *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); /* ========================================================================= */ int deflateInit_(strm, level, version, stream_size) z_streamp strm; int level; const char *version; int stream_size; { return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ int deflateInit2_(strm, level, method, windowBits, memLevel, strategy, version, stream_size) z_streamp strm; int level; int method; int windowBits; int memLevel; int strategy; const char *version; int stream_size; { deflate_state *s; int noheader = 0; static char* my_version = ZLIB_VERSION; ushf *overlay; /* We overlay pending_buf and d_buf+l_buf. This works since the average * output size for (length,distance) codes is <= 24 bits. */ if (version == Z_NULL || version[0] != my_version[0] || stream_size != sizeof(z_stream)) { return Z_VERSION_ERROR; } if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; #ifndef NO_ZCFUNCS if (strm->zalloc == Z_NULL) { strm->zalloc = zcalloc; strm->opaque = (voidpf)0; } if (strm->zfree == Z_NULL) strm->zfree = zcfree; #endif if (level == Z_DEFAULT_COMPRESSION) level = 6; if (windowBits < 0) { /* undocumented feature: suppress zlib header */ noheader = 1; windowBits = -windowBits; } if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) { return Z_STREAM_ERROR; } s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; s->strm = strm; s->noheader = noheader; s->w_bits = windowBits; s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; s->hash_bits = memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); s->pending_buf = (uchf *) overlay; s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); deflateEnd (strm); return Z_MEM_ERROR; } s->d_buf = overlay + s->lit_bufsize/sizeof(ush); s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; s->level = level; s->strategy = strategy; s->method = (Byte)method; return deflateReset(strm); } /* ========================================================================= */ int deflateSetDictionary (strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { deflate_state *s; uInt length = dictLength; uInt n; IPos hash_head = 0; if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) return Z_STREAM_ERROR; s = (deflate_state *) strm->state; if (s->status != INIT_STATE) return Z_STREAM_ERROR; strm->adler = adler32(strm->adler, dictionary, dictLength); if (length < MIN_MATCH) return Z_OK; if (length > MAX_DIST(s)) { length = MAX_DIST(s); #ifndef USE_DICT_HEAD dictionary += dictLength - length; /* use the tail of the dictionary */ #endif } zmemcpy((charf *)s->window, dictionary, length); s->strstart = length; s->block_start = (long)length; /* Insert all strings in the hash table (except for the last two bytes). * s->lookahead stays null, so s->ins_h will be recomputed at the next * call of fill_window. */ s->ins_h = s->window[0]; UPDATE_HASH(s, s->ins_h, s->window[1]); for (n = 0; n <= length - MIN_MATCH; n++) { INSERT_STRING(s, n, hash_head); } if (hash_head) hash_head = 0; /* to make compiler happy */ return Z_OK; } /* ========================================================================= */ int deflateReset (strm) z_streamp strm; { deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL || strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR; strm->total_in = strm->total_out = 0; strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ strm->data_type = Z_UNKNOWN; s = (deflate_state *)strm->state; s->pending = 0; s->pending_out = s->pending_buf; if (s->noheader < 0) { s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */ } s->status = s->noheader ? BUSY_STATE : INIT_STATE; strm->adler = 1; s->last_flush = Z_NO_FLUSH; _tr_init(s); lm_init(s); return Z_OK; } /* ========================================================================= */ int deflateParams(strm, level, strategy) z_streamp strm; int level; int strategy; { deflate_state *s; compress_func func; int err = Z_OK; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; s = (deflate_state *) strm->state; if (level == Z_DEFAULT_COMPRESSION) { level = 6; } if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) { return Z_STREAM_ERROR; } func = configuration_table[s->level].func; if (func != configuration_table[level].func && strm->total_in != 0) { /* Flush the last buffer: */ err = deflate(strm, Z_PARTIAL_FLUSH); } if (s->level != level) { s->level = level; s->max_lazy_match = configuration_table[level].max_lazy; s->good_match = configuration_table[level].good_length; s->nice_match = configuration_table[level].nice_length; s->max_chain_length = configuration_table[level].max_chain; } s->strategy = strategy; return err; } /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ local void putShortMSB (s, b) deflate_state *s; uInt b; { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } /* ========================================================================= * Flush as much pending output as possible. All deflate() output goes * through this function so some applications may wish to modify it * to avoid allocating a large strm->next_out buffer and copying into it. * (See also read_buf()). */ local void flush_pending(strm) z_streamp strm; { deflate_state *s = (deflate_state *) strm->state; unsigned len = s->pending; if (len > strm->avail_out) len = strm->avail_out; if (len == 0) return; if (strm->next_out != Z_NULL) { zmemcpy(strm->next_out, s->pending_out, len); strm->next_out += len; } s->pending_out += len; strm->total_out += len; strm->avail_out -= len; s->pending -= len; if (s->pending == 0) { s->pending_out = s->pending_buf; } } /* ========================================================================= */ int deflate (strm, flush) z_streamp strm; int flush; { int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL || flush > Z_FINISH || flush < 0) { return Z_STREAM_ERROR; } s = (deflate_state *) strm->state; if ((strm->next_in == Z_NULL && strm->avail_in != 0) || (s->status == FINISH_STATE && flush != Z_FINISH)) { ERR_RETURN(strm, Z_STREAM_ERROR); } if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); s->strm = strm; /* just in case */ old_flush = s->last_flush; s->last_flush = flush; /* Write the zlib header */ if (s->status == INIT_STATE) { uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; uInt level_flags = (s->level-1) >> 1; if (level_flags > 3) level_flags = 3; header |= (level_flags << 6); if (s->strstart != 0) header |= PRESET_DICT; header += 31 - (header % 31); s->status = BUSY_STATE; putShortMSB(s, header); /* Save the adler32 of the preset dictionary: */ if (s->strstart != 0) { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } strm->adler = 1L; } /* Flush as much pending output as possible */ if (s->pending != 0) { flush_pending(strm); if (strm->avail_out == 0) { /* Since avail_out is 0, deflate will be called again with * more output space, but possibly with both pending and * avail_in equal to zero. There won't be anything to do, * but this is not an error situation so make sure we * return OK instead of BUF_ERROR at next call of deflate: */ s->last_flush = -1; return Z_OK; } /* Make sure there is something to do and avoid duplicate consecutive * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUFF_ERROR. */ } else if (strm->avail_in == 0 && flush <= old_flush && flush != Z_FINISH) { ERR_RETURN(strm, Z_BUF_ERROR); } /* User must not provide more input after the first FINISH: */ if (s->status == FINISH_STATE && strm->avail_in != 0) { ERR_RETURN(strm, Z_BUF_ERROR); } /* Start a new block or continue the current one. */ if (strm->avail_in != 0 || s->lookahead != 0 || (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; bstate = (*(configuration_table[s->level].func))(s, flush); if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; } if (bstate == need_more || bstate == finish_started) { if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ } return Z_OK; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don't have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } if (bstate == block_done) { if (flush == Z_PARTIAL_FLUSH) { _tr_align(s); } else if (flush == Z_PACKET_FLUSH) { /* Output just the 3-bit `stored' block type value, but not a zero length. */ _tr_stored_type_only(s); } else { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, (char*)0, 0L, 0); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush == Z_FULL_FLUSH) { CLEAR_HASH(s); /* forget history */ } } flush_pending(strm); if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return Z_OK; } } } Assert(strm->avail_out > 0, "bug2"); if (flush != Z_FINISH) return Z_OK; if (s->noheader) return Z_STREAM_END; /* Write the zlib trailer (adler32) */ putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ s->noheader = -1; /* write the trailer only once! */ return s->pending != 0 ? Z_OK : Z_STREAM_END; } /* ========================================================================= */ int deflateEnd (strm) z_streamp strm; { int status; deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; s = (deflate_state *) strm->state; status = s->status; if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) { return Z_STREAM_ERROR; } /* Deallocate in reverse order of allocations: */ TRY_FREE(strm, s->pending_buf); TRY_FREE(strm, s->head); TRY_FREE(strm, s->prev); TRY_FREE(strm, s->window); ZFREE(strm, s); strm->state = Z_NULL; return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; } /* ========================================================================= * Copy the source state to the destination state. */ int deflateCopy (dest, source) z_streamp dest; z_streamp source; { deflate_state *ds; deflate_state *ss; ushf *overlay; if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) return Z_STREAM_ERROR; ss = (deflate_state *) source->state; zmemcpy(dest, source, sizeof(*dest)); ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); if (ds == Z_NULL) return Z_MEM_ERROR; dest->state = (struct internal_state FAR *) ds; zmemcpy(ds, ss, sizeof(*ds)); ds->strm = dest; ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); ds->pending_buf = (uchf *) overlay; if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ds->pending_buf == Z_NULL) { deflateEnd (dest); return Z_MEM_ERROR; } /* ??? following zmemcpy doesn't work for 16-bit MSDOS */ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; ds->l_desc.dyn_tree = ds->dyn_ltree; ds->d_desc.dyn_tree = ds->dyn_dtree; ds->bl_desc.dyn_tree = ds->bl_tree; return Z_OK; } /* =========================================================================== * Return the number of bytes of output which are immediately available * for output from the decompressor. */ int deflateOutputPending (strm) z_streamp strm; { if (strm == Z_NULL || strm->state == Z_NULL) return 0; return ((deflate_state *)(strm->state))->pending; } /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. All deflate() input goes through * this function so some applications may wish to modify it to avoid * allocating a large strm->next_in buffer and copying from it. * (See also flush_pending()). */ local int read_buf(strm, buf, size) z_streamp strm; charf *buf; unsigned size; { unsigned len = strm->avail_in; if (len > size) len = size; if (len == 0) return 0; strm->avail_in -= len; if (!((deflate_state *)(strm->state))->noheader) { strm->adler = adler32(strm->adler, strm->next_in, len); } zmemcpy(buf, strm->next_in, len); strm->next_in += len; strm->total_in += len; return (int)len; } /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ local void lm_init (s) deflate_state *s; { s->window_size = (ulg)2L*s->w_size; CLEAR_HASH(s); /* Set the default configuration parameters: */ s->max_lazy_match = configuration_table[s->level].max_lazy; s->good_match = configuration_table[s->level].good_length; s->nice_match = configuration_table[s->level].nice_length; s->max_chain_length = configuration_table[s->level].max_chain; s->strstart = 0; s->block_start = 0L; s->lookahead = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; #ifdef ASMV match_init(); /* initialize the asm code */ #endif } /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ #ifndef ASMV /* For 80x86 and 680x0, an optimized version will be provided in match.asm or * match.S. The code will be functionally equivalent. */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ int best_len = s->prev_length; /* best match length so far */ int nice_match = s->nice_match; /* stop if match long enough */ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? s->strstart - (IPos)MAX_DIST(s) : NIL; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ Posf *prev = s->prev; uInt wmask = s->w_mask; #ifdef UNALIGNED_OK /* Compare two bytes at a time. Note: this is not always beneficial. * Try with and without -DUNALIGNED_OK to check. */ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; register ush scan_start = *(ushf*)scan; register ush scan_end = *(ushf*)(scan+best_len-1); #else register Bytef *strend = s->window + s->strstart + MAX_MATCH; register Byte scan_end1 = scan[best_len-1]; register Byte scan_end = scan[best_len]; #endif /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s->prev_length >= s->good_match) { chain_length >>= 2; } /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); do { Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2: */ #if (defined(UNALIGNED_OK) && MAX_MATCH == 258) /* This code assumes sizeof(unsigned short) == 2. Do not use * UNALIGNED_OK if your compiler uses a different size. */ if (*(ushf*)(match+best_len-1) != scan_end || *(ushf*)match != scan_start) continue; /* It is not necessary to compare scan[2] and match[2] since they are * always equal when the other bytes match, given that the hash keys * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at * strstart+3, +5, ... up to strstart+257. We check for insufficient * lookahead only every 4th comparison; the 128th check will be made * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ Assert(scan[2] == match[2], "scan[2]?"); scan++, match++; do { } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && scan < strend); /* The funny "do {}" generates better code on most compilers */ /* Here, scan <= window+strstart+257 */ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); if (*scan == *match) scan++; len = (MAX_MATCH - 1) - (int)(strend-scan); scan = strend - (MAX_MATCH-1); #else /* UNALIGNED_OK */ if (match[best_len] != scan_end || match[best_len-1] != scan_end1 || *match != *scan || *++match != scan[1]) continue; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match++; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); scan = strend - MAX_MATCH; #endif /* UNALIGNED_OK */ if (len > best_len) { s->match_start = cur_match; best_len = len; if (len >= nice_match) break; #ifdef UNALIGNED_OK scan_end = *(ushf*)(scan+best_len-1); #else scan_end1 = scan[best_len-1]; scan_end = scan[best_len]; #endif } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length != 0); if ((uInt)best_len <= s->lookahead) return best_len; return s->lookahead; } #endif /* ASMV */ #ifdef DEBUG_ZLIB /* =========================================================================== * Check that the match at match_start is indeed a match. */ local void check_match(s, start, match, length) deflate_state *s; IPos start, match; int length; { /* check that the match is indeed a match */ if (zmemcmp((charf *)s->window + match, (charf *)s->window + start, length) != EQUAL) { fprintf(stderr, " start %u, match %u, length %d\n", start, match, length); do { fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); } while (--length != 0); z_error("invalid match"); } if (z_verbose > 1) { fprintf(stderr,"\\[%d,%d]", start-match, length); do { putc(s->window[start++], stderr); } while (--length != 0); } } #else # define check_match(s, start, match, length) #endif /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ local void fill_window(s) deflate_state *s; { register unsigned n, m; register Posf *p; unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; do { more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); /* Deal with !@#$% 64K limit: */ if (more == 0 && s->strstart == 0 && s->lookahead == 0) { more = wsize; } else if (more == (unsigned)(-1)) { /* Very unlikely, but possible on 16 bit machine if strstart == 0 * and lookahead == 1 (input done one byte at time) */ more--; /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ } else if (s->strstart >= wsize+MAX_DIST(s)) { zmemcpy((charf *)s->window, (charf *)s->window+wsize, (unsigned)wsize); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; /* Slide the hash table (could be avoided with 32 bit values at the expense of memory usage). We slide even when level == 0 to keep the hash table consistent if we switch back to level > 0 later. (Using level 0 permanently is not an optimal usage of zlib, so we don't care about this pathological case.) */ n = s->hash_size; p = &s->head[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m-wsize : NIL); } while (--n); n = wsize; p = &s->prev[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m-wsize : NIL); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); more += wsize; } if (s->strm->avail_in == 0) return; /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ Assert(more >= 2, "more < 2"); n = read_buf(s->strm, (charf *)s->window + s->strstart + s->lookahead, more); s->lookahead += n; /* Initialize the hash value now that we have some input: */ if (s->lookahead >= MIN_MATCH) { s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); } /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. */ #define FLUSH_BLOCK_ONLY(s, eof) { \ _tr_flush_block(s, (s->block_start >= 0L ? \ (charf *)&s->window[(unsigned)s->block_start] : \ (charf *)Z_NULL), \ (ulg)((long)s->strstart - s->block_start), \ (eof)); \ s->block_start = s->strstart; \ flush_pending(s->strm); \ Tracev((stderr,"[FLUSH]")); \ } /* Same but force premature exit if necessary. */ #define FLUSH_BLOCK(s, eof) { \ FLUSH_BLOCK_ONLY(s, eof); \ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ } /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. * This function does not insert new strings in the dictionary since * uncompressible data is probably not useful. This function is used * only for the level=0 compression option. * NOTE: this function should be optimized to avoid extra copying from * window to pending_buf. */ local block_state deflate_stored(s, flush) deflate_state *s; int flush; { /* Stored blocks are limited to 0xffff bytes, pending_buf is limited * to pending_buf_size, and each stored block has a 5 byte header: */ ulg max_block_size = 0xffff; ulg max_start; if (max_block_size > s->pending_buf_size - 5) { max_block_size = s->pending_buf_size - 5; } /* Copy as much as possible from input to output: */ for (;;) { /* Fill the window as much as possible: */ if (s->lookahead <= 1) { Assert(s->strstart < s->w_size+MAX_DIST(s) || s->block_start >= (long)s->w_size, "slide too late"); fill_window(s); if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; if (s->lookahead == 0) break; /* flush the current block */ } Assert(s->block_start >= 0L, "block gone"); s->strstart += s->lookahead; s->lookahead = 0; /* Emit a stored block if pending_buf will be full: */ max_start = s->block_start + max_block_size; if (s->strstart == 0 || (ulg)s->strstart >= max_start) { /* strstart == 0 is possible when wraparound on 16-bit machine */ s->lookahead = (uInt)(s->strstart - max_start); s->strstart = (uInt)max_start; FLUSH_BLOCK(s, 0); } /* Flush if we may have to slide, otherwise block_start may become * negative and the data will be gone: */ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { FLUSH_BLOCK(s, 0); } } FLUSH_BLOCK(s, flush == Z_FINISH); return flush == Z_FINISH ? finish_done : block_done; } /* =========================================================================== * Compress as much as possible from the input stream, return the current * block state. * This function does not perform lazy evaluation of matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ local block_state deflate_fast(s, flush) deflate_state *s; int flush; { IPos hash_head = NIL; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ if (s->strategy != Z_HUFFMAN_ONLY) { s->match_length = longest_match (s, hash_head); } /* longest_match() sets match_start */ } if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->match_start, s->match_length); bflush = _tr_tally(s, s->strstart - s->match_start, s->match_length - MIN_MATCH); s->lookahead -= s->match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ if (s->match_length <= s->max_insert_length && s->lookahead >= MIN_MATCH) { s->match_length--; /* string at strstart already in hash table */ do { s->strstart++; INSERT_STRING(s, s->strstart, hash_head); /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s->match_length != 0); s->strstart++; } else { s->strstart += s->match_length; s->match_length = 0; s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); bflush = _tr_tally (s, 0, s->window[s->strstart]); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } FLUSH_BLOCK(s, flush == Z_FINISH); return flush == Z_FINISH ? finish_done : block_done; } /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ local block_state deflate_slow(s, flush) deflate_state *s; int flush; { IPos hash_head = NIL; /* head of hash chain */ int bflush; /* set if current block must be flushed */ /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. */ s->prev_length = s->match_length, s->prev_match = s->match_start; s->match_length = MIN_MATCH-1; if (hash_head != NIL && s->prev_length < s->max_lazy_match && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ if (s->strategy != Z_HUFFMAN_ONLY) { s->match_length = longest_match (s, hash_head); } /* longest_match() sets match_start */ if (s->match_length <= 5 && (s->strategy == Z_FILTERED || (s->match_length == MIN_MATCH && s->strstart - s->match_start > TOO_FAR))) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s->match_length = MIN_MATCH-1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ check_match(s, s->strstart-1, s->prev_match, s->prev_length); bflush = _tr_tally(s, s->strstart -1 - s->prev_match, s->prev_length - MIN_MATCH); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s->lookahead -= s->prev_length-1; s->prev_length -= 2; do { if (++s->strstart <= max_insert) { INSERT_STRING(s, s->strstart, hash_head); } } while (--s->prev_length != 0); s->match_available = 0; s->match_length = MIN_MATCH-1; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } else if (s->match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ Tracevv((stderr,"%c", s->window[s->strstart-1])); if (_tr_tally (s, 0, s->window[s->strstart-1])) { FLUSH_BLOCK_ONLY(s, 0); } s->strstart++; s->lookahead--; if (s->strm->avail_out == 0) return need_more; } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s->match_available = 1; s->strstart++; s->lookahead--; } } Assert (flush != Z_NO_FLUSH, "no flush?"); if (s->match_available) { Tracevv((stderr,"%c", s->window[s->strstart-1])); _tr_tally (s, 0, s->window[s->strstart-1]); s->match_available = 0; } FLUSH_BLOCK(s, flush == Z_FINISH); return flush == Z_FINISH ? finish_done : block_done; } /* --- deflate.c */ /* +++ trees.c */ /* trees.c -- output deflated data using Huffman coding * Copyright (C) 1995-1996 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process uses several Huffman trees. The more * common source values are represented by shorter bit sequences. * * Each code tree is stored in a compressed form which is itself * a Huffman encoding of the lengths of all the code strings (in * ascending order by source values). The actual code strings are * reconstructed from the lengths in the inflate process, as described * in the deflate specification. * * REFERENCES * * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc * * Storer, James A. * Data Compression: Methods and Theory, pp. 49-50. * Computer Science Press, 1988. ISBN 0-7167-8156-5. * * Sedgewick, R. * Algorithms, p290. * Addison-Wesley, 1983. ISBN 0-201-06672-6. */ /* From: trees.c,v 1.11 1996/07/24 13:41:06 me Exp $ */ /* #include "deflate.h" */ #ifdef DEBUG_ZLIB # include #endif /* =========================================================================== * Constants */ #define MAX_BL_BITS 7 /* Bit length codes must not exceed MAX_BL_BITS bits */ #define END_BLOCK 256 /* end of block literal code */ #define REP_3_6 16 /* repeat previous bit length 3-6 times (2 bits of repeat count) */ #define REPZ_3_10 17 /* repeat a zero length 3-10 times (3 bits of repeat count) */ #define REPZ_11_138 18 /* repeat a zero length 11-138 times (7 bits of repeat count) */ local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; local int extra_dbits[D_CODES] /* extra bits for each distance code */ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; local int extra_blbits[BL_CODES]/* extra bits for each bit length code */ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; local uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ #define Buf_size (8 * 2*sizeof(char)) /* Number of bits used within bi_buf. (bi_buf might be implemented on * more than 16 bits on some systems.) */ /* =========================================================================== * Local data. These are initialized only once. */ local ct_data static_ltree[L_CODES+2]; /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ local ct_data static_dtree[D_CODES]; /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ local uch dist_code[512]; /* distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ local uch length_code[MAX_MATCH-MIN_MATCH+1]; /* length code for each normalized match length (0 == MIN_MATCH) */ local int base_length[LENGTH_CODES]; /* First normalized length for each code (0 = MIN_MATCH) */ local int base_dist[D_CODES]; /* First normalized distance for each code (0 = distance of 1) */ struct static_tree_desc_s { ct_data *static_tree; /* static tree or NULL */ intf *extra_bits; /* extra bits for each code or NULL */ int extra_base; /* base index for extra_bits */ int elems; /* max number of elements in the tree */ int max_length; /* max bit length for the codes */ }; local static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; local static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; local static_tree_desc static_bl_desc = {(ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== * Local (static) routines in this file. */ local void tr_static_init OF((void)); local void init_block OF((deflate_state *s)); local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); local void build_tree OF((deflate_state *s, tree_desc *desc)); local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); local int build_bl_tree OF((deflate_state *s)); local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, int blcodes)); local void compress_block OF((deflate_state *s, ct_data *ltree, ct_data *dtree)); local void set_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); local void copy_block OF((deflate_state *s, charf *buf, unsigned len, int header)); #ifndef DEBUG_ZLIB # define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) /* Send a code of the given tree. c and tree must not have side effects */ #else /* DEBUG_ZLIB */ # define send_code(s, c, tree) \ { if (verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ send_bits(s, tree[c].Code, tree[c].Len); } #endif #define d_code(dist) \ ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) /* Mapping from a distance to a distance code. dist is the distance - 1 and * must not have side effects. dist_code[256] and dist_code[257] are never * used. */ /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ #define put_short(s, w) { \ put_byte(s, (uch)((w) & 0xff)); \ put_byte(s, (uch)((ush)(w) >> 8)); \ } /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef DEBUG_ZLIB local void send_bits OF((deflate_state *s, int value, int length)); local void send_bits(s, value, length) deflate_state *s; int value; /* value to send */ int length; /* number of bits */ { Tracevv((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; /* If not enough room in bi_buf, use (valid) bits from bi_buf and * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { s->bi_buf |= (value << s->bi_valid); put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); s->bi_valid += length - Buf_size; } else { s->bi_buf |= value << s->bi_valid; s->bi_valid += length; } } #else /* !DEBUG_ZLIB */ #define send_bits(s, value, length) \ { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ int val = value;\ s->bi_buf |= (val << s->bi_valid);\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ s->bi_valid += len - Buf_size;\ } else {\ s->bi_buf |= (value) << s->bi_valid;\ s->bi_valid += len;\ }\ } #endif /* DEBUG_ZLIB */ #define MAX(a,b) (a >= b ? a : b) /* the arguments must not have side effects */ /* =========================================================================== * Initialize the various 'constant' tables. In a multi-threaded environment, * this function may be called by two threads concurrently, but this is * harmless since both invocations do exactly the same thing. */ local void tr_static_init() { static int static_init_done = 0; int n; /* iterates over tree elements */ int bits; /* bit counter */ int length; /* length value */ int code; /* code value */ int dist; /* distance index */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ if (static_init_done) return; /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES-1; code++) { base_length[code] = length; for (n = 0; n < (1< dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ for ( ; code < D_CODES; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { dist_code[256 + dist++] = (uch)code; } } Assert (dist == 256, "tr_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; n = 0; while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES; n++) { static_dtree[n].Len = 5; static_dtree[n].Code = bi_reverse((unsigned)n, 5); } static_init_done = 1; } /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ void _tr_init(s) deflate_state *s; { tr_static_init(); s->compressed_len = 0L; s->l_desc.dyn_tree = s->dyn_ltree; s->l_desc.stat_desc = &static_l_desc; s->d_desc.dyn_tree = s->dyn_dtree; s->d_desc.stat_desc = &static_d_desc; s->bl_desc.dyn_tree = s->bl_tree; s->bl_desc.stat_desc = &static_bl_desc; s->bi_buf = 0; s->bi_valid = 0; s->last_eob_len = 8; /* enough lookahead for inflate */ #ifdef DEBUG_ZLIB s->bits_sent = 0L; #endif /* Initialize the first block of the first file: */ init_block(s); } /* =========================================================================== * Initialize a new block. */ local void init_block(s) deflate_state *s; { int n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; s->dyn_ltree[END_BLOCK].Freq = 1; s->opt_len = s->static_len = 0L; s->last_lit = s->matches = 0; } #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ /* =========================================================================== * Remove the smallest element from the heap and recreate the heap with * one less element. Updates heap and heap_len. */ #define pqremove(s, tree, top) \ {\ top = s->heap[SMALLEST]; \ s->heap[SMALLEST] = s->heap[s->heap_len--]; \ pqdownheap(s, tree, SMALLEST); \ } /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ #define smaller(tree, n, m, depth) \ (tree[n].Freq < tree[m].Freq || \ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ local void pqdownheap(s, tree, k) deflate_state *s; ct_data *tree; /* the tree to restore */ int k; /* node to move down */ { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { /* Set j to the smallest of the two sons: */ if (j < s->heap_len && smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s->heap[j], s->depth)) break; /* Exchange v with the smallest son */ s->heap[k] = s->heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s->heap[k] = v; } /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ local void gen_bitlen(s, desc) deflate_state *s; tree_desc *desc; /* the tree descriptor */ { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; ct_data *stree = desc->stat_desc->static_tree; intf *extra = desc->stat_desc->extra_bits; int base = desc->stat_desc->extra_base; int max_length = desc->stat_desc->max_length; int h; /* heap index */ int n, m; /* iterate over the tree elements */ int bits; /* bit length */ int xbits; /* extra bits */ ush f; /* frequency */ int overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ for (h = s->heap_max+1; h < HEAP_SIZE; h++) { n = s->heap[h]; bits = tree[tree[n].Dad].Len + 1; if (bits > max_length) bits = max_length, overflow++; tree[n].Len = (ush)bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) continue; /* not a leaf node */ s->bl_count[bits]++; xbits = 0; if (n >= base) xbits = extra[n-base]; f = tree[n].Freq; s->opt_len += (ulg)f * (bits + xbits); if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); } if (overflow == 0) return; Trace((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length-1; while (s->bl_count[bits] == 0) bits--; s->bl_count[bits]--; /* move one leaf down the tree */ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ s->bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from 'ar' written by Haruhiko Okumura.) */ for (bits = max_length; bits != 0; bits--) { n = s->bl_count[bits]; while (n != 0) { m = s->heap[--h]; if (m > max_code) continue; if (tree[m].Len != (unsigned) bits) { Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); s->opt_len += ((long)bits - (long)tree[m].Len) *(long)tree[m].Freq; tree[m].Len = (ush)bits; } n--; } } } /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ local void gen_codes (tree, max_code, bl_count) ct_data *tree; /* the tree to decorate */ int max_code; /* largest code with non zero frequency */ ushf *bl_count; /* number of codes at each bit length */ { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ ush code = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { next_code[bits] = code = (code + bl_count[bits-1]) << 1; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; int n, m; /* iterate over heap elements */ int max_code = -1; /* largest code with non zero frequency */ int node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. * heap[0] is not used. */ s->heap_len = 0, s->heap_max = HEAP_SIZE; for (n = 0; n < elems; n++) { if (tree[n].Freq != 0) { s->heap[++(s->heap_len)] = max_code = n; s->depth[n] = 0; } else { tree[n].Len = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s->heap_len < 2) { node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); tree[node].Freq = 1; s->depth[node] = 0; s->opt_len--; if (stree) s->static_len -= stree[node].Len; /* node is 0 or 1 so it does not have extra bits */ } desc->max_code = max_code; /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { pqremove(s, tree, n); /* n = node of least frequency */ m = s->heap[SMALLEST]; /* m = node of next least frequency */ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ s->heap[--(s->heap_max)] = m; /* Create a new node father of n and m */ tree[node].Freq = tree[n].Freq + tree[m].Freq; s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1); tree[n].Dad = tree[m].Dad = (ush)node; #ifdef DUMP_BL_TREE if (tree == s->bl_tree) { fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); } #endif /* and insert the new node in the heap */ s->heap[SMALLEST] = node++; pqdownheap(s, tree, SMALLEST); } while (s->heap_len >= 2); s->heap[--(s->heap_max)] = s->heap[SMALLEST]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, (tree_desc *)desc); /* The field len is now set, we can generate the bit codes */ gen_codes ((ct_data *)tree, max_code, s->bl_count); } /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ local void scan_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ if (nextlen == 0) max_count = 138, min_count = 3; tree[max_code+1].Len = (ush)0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { s->bl_tree[curlen].Freq += count; } else if (curlen != 0) { if (curlen != prevlen) s->bl_tree[curlen].Freq++; s->bl_tree[REP_3_6].Freq++; } else if (count <= 10) { s->bl_tree[REPZ_3_10].Freq++; } else { s->bl_tree[REPZ_11_138].Freq++; } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ local void send_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ /* tree[max_code+1].Len = -1; */ /* guard already set */ if (nextlen == 0) max_count = 138, min_count = 3; for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s->bl_tree); } while (--count != 0); } else if (curlen != 0) { if (curlen != prevlen) { send_code(s, curlen, s->bl_tree); count--; } Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); } else { send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ local int build_bl_tree(s) deflate_state *s; { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); /* Build the bit length tree: */ build_tree(s, (tree_desc *)(&(s->bl_desc))); /* opt_len now includes the length of the tree representations, except * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ s->opt_len += 3*(max_blindex+1) + 5+5+4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); return max_blindex; } /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ local void send_all_trees(s, lcodes, dcodes, blcodes) deflate_state *s; int lcodes, dcodes, blcodes; /* number of codes for each tree */ { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, "too many codes"); Tracev((stderr, "\nbl counts: ")); send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes-1, 5); send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { Tracev((stderr, "\nbl code %2d ", bl_order[rank])); send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); } Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); } /* =========================================================================== * Send a stored block */ void _tr_stored_block(s, buf, stored_len, eof) deflate_state *s; charf *buf; /* input block */ ulg stored_len; /* length of input block */ int eof; /* true if this is the last block for a file */ { send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ } /* Send just the `stored block' type code without any length bytes or data. */ void _tr_stored_type_only(s) deflate_state *s; { send_bits(s, (STORED_BLOCK << 1), 3); bi_windup(s); s->compressed_len = (s->compressed_len + 3) & ~7L; } /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. * The current inflate code requires 9 bits of lookahead. If the * last two codes for the previous block (real code plus EOB) were coded * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode * the last real code. In this case we send two empty static blocks instead * of one. (There are no problems if the previous block is stored or fixed.) * To simplify the code, we assume the worst case of last real code encoded * on one bit only. */ void _tr_align(s) deflate_state *s; { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ bi_flush(s); /* Of the 10 bits for the empty block, we have already sent * (10 - bi_valid) bits. The lookahead for the last real code (before * the EOB of the previous block) was thus at least one plus the length * of the EOB plus what we have just sent of the empty static block. */ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); s->compressed_len += 10L; bi_flush(s); } s->last_eob_len = 7; } /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and output the encoded block to the zip file. This function * returns the total compressed length for the file so far. */ ulg _tr_flush_block(s, buf, stored_len, eof) deflate_state *s; charf *buf; /* input block, or NULL if too old */ ulg stored_len; /* length of input block */ int eof; /* true if this is the last block for a file */ { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ /* Build the Huffman trees unless a stored block is forced */ if (s->level > 0) { /* Check if the file is ascii or binary */ if (s->data_type == Z_UNKNOWN) set_data_type(s); /* Construct the literal and distance trees */ build_tree(s, (tree_desc *)(&(s->l_desc))); Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, s->static_len)); build_tree(s, (tree_desc *)(&(s->d_desc))); Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute first the block length in bytes*/ opt_lenb = (s->opt_len+3+7)>>3; static_lenb = (s->static_len+3+7)>>3; Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, s->last_lit)); if (static_lenb <= opt_lenb) opt_lenb = static_lenb; } else { Assert(buf != (char*)0, "lost buf"); opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ } /* If compression failed and this is the first and last block, * and if the .zip file can be seeked (to rewrite the local header), * the whole file is transformed into a stored file: */ #ifdef STORED_FILE_OK # ifdef FORCE_STORED_FILE if (eof && s->compressed_len == 0L) { /* force stored file */ # else if (stored_len <= opt_lenb && eof && s->compressed_len==0L && seekable()) { # endif /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ if (buf == (charf*)0) error ("block vanished"); copy_block(s, buf, (unsigned)stored_len, 0); /* without header */ s->compressed_len = stored_len << 3; s->method = STORED; } else #endif /* STORED_FILE_OK */ #ifdef FORCE_STORED if (buf != (char*)0) { /* force stored block */ #else if (stored_len+4 <= opt_lenb && buf != (char*)0) { /* 4: two words for the lengths */ #endif /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can't have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ _tr_stored_block(s, buf, stored_len, eof); #ifdef FORCE_STATIC } else if (static_lenb >= 0) { /* force static trees */ #else } else if (static_lenb == opt_lenb) { #endif send_bits(s, (STATIC_TREES<<1)+eof, 3); compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); s->compressed_len += 3 + s->static_len; } else { send_bits(s, (DYN_TREES<<1)+eof, 3); send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, max_blindex+1); compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); s->compressed_len += 3 + s->opt_len; } Assert (s->compressed_len == s->bits_sent, "bad compressed size"); init_block(s); if (eof) { bi_windup(s); s->compressed_len += 7; /* align on byte boundary */ } Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, s->compressed_len-7*eof)); return s->compressed_len >> 3; } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ int _tr_tally (s, dist, lc) deflate_state *s; unsigned dist; /* distance of matched string */ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ { s->d_buf[s->last_lit] = (ush)dist; s->l_buf[s->last_lit++] = (uch)lc; if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; } else { s->matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ Assert((ush)dist < (ush)MAX_DIST(s) && (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++; s->dyn_dtree[d_code(dist)].Freq++; } /* Try to guess if it is profitable to stop the current block here */ if (s->level > 2 && (s->last_lit & 0xfff) == 0) { /* Compute an upper bound for the compressed length */ ulg out_length = (ulg)s->last_lit*8L; ulg in_length = (ulg)((long)s->strstart - s->block_start); int dcode; for (dcode = 0; dcode < D_CODES; dcode++) { out_length += (ulg)s->dyn_dtree[dcode].Freq * (5L+extra_dbits[dcode]); } out_length >>= 3; Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", s->last_lit, in_length, out_length, 100L - out_length*100L/in_length)); if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; } return (s->last_lit == s->lit_bufsize-1); /* We avoid equality with lit_bufsize because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ } /* =========================================================================== * Send the block data compressed using the given Huffman trees */ local void compress_block(s, ltree, dtree) deflate_state *s; ct_data *ltree; /* literal tree */ ct_data *dtree; /* distance tree */ { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ unsigned lx = 0; /* running index in l_buf */ unsigned code; /* the code to send */ int extra; /* number of extra bits to send */ if (s->last_lit != 0) do { dist = s->d_buf[lx]; lc = s->l_buf[lx++]; if (dist == 0) { send_code(s, lc, ltree); /* send a literal byte */ Tracecv(isgraph(lc), (stderr," '%c' ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = length_code[lc]; send_code(s, code+LITERALS+1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra != 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra != 0) { dist -= base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow"); } while (lx < s->last_lit); send_code(s, END_BLOCK, ltree); s->last_eob_len = ltree[END_BLOCK].Len; } /* =========================================================================== * Set the data type to ASCII or BINARY, using a crude approximation: * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. * IN assertion: the fields freq of dyn_ltree are set and the total of all * frequencies does not exceed 64K (to fit in an int on 16 bit machines). */ local void set_data_type(s) deflate_state *s; { int n = 0; unsigned ascii_freq = 0; unsigned bin_freq = 0; while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); } /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ local unsigned bi_reverse(code, len) unsigned code; /* the value to invert */ int len; /* its bit length */ { register unsigned res = 0; do { res |= code & 1; code >>= 1, res <<= 1; } while (--len > 0); return res >> 1; } /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ local void bi_flush(s) deflate_state *s; { if (s->bi_valid == 16) { put_short(s, s->bi_buf); s->bi_buf = 0; s->bi_valid = 0; } else if (s->bi_valid >= 8) { put_byte(s, (Byte)s->bi_buf); s->bi_buf >>= 8; s->bi_valid -= 8; } } /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ local void bi_windup(s) deflate_state *s; { if (s->bi_valid > 8) { put_short(s, s->bi_buf); } else if (s->bi_valid > 0) { put_byte(s, (Byte)s->bi_buf); } s->bi_buf = 0; s->bi_valid = 0; #ifdef DEBUG_ZLIB s->bits_sent = (s->bits_sent+7) & ~7; #endif } /* =========================================================================== * Copy a stored block, storing first the length and its * one's complement if requested. */ local void copy_block(s, buf, len, header) deflate_state *s; charf *buf; /* the input data */ unsigned len; /* its length */ int header; /* true if block header must be written */ { bi_windup(s); /* align on byte boundary */ s->last_eob_len = 8; /* enough lookahead for inflate */ if (header) { put_short(s, (ush)len); put_short(s, (ush)~len); #ifdef DEBUG_ZLIB s->bits_sent += 2*16; #endif } #ifdef DEBUG_ZLIB s->bits_sent += (ulg)len<<3; #endif /* bundle up the put_byte(s, *buf++) calls */ zmemcpy(&s->pending_buf[s->pending], buf, len); s->pending += len; } /* --- trees.c */ /* +++ inflate.c */ /* inflate.c -- zlib interface to inflate modules * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* #include "zutil.h" */ /* +++ infblock.h */ /* infblock.h -- header to use infblock.c * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ struct inflate_blocks_state; typedef struct inflate_blocks_state FAR inflate_blocks_statef; extern inflate_blocks_statef * inflate_blocks_new OF(( z_streamp z, check_func c, /* check function */ uInt w)); /* window size */ extern int inflate_blocks OF(( inflate_blocks_statef *, z_streamp , int)); /* initial return code */ extern void inflate_blocks_reset OF(( inflate_blocks_statef *, z_streamp , uLongf *)); /* check value on output */ extern int inflate_blocks_free OF(( inflate_blocks_statef *, z_streamp , uLongf *)); /* check value on output */ extern void inflate_set_dictionary OF(( inflate_blocks_statef *s, const Bytef *d, /* dictionary */ uInt n)); /* dictionary length */ extern int inflate_addhistory OF(( inflate_blocks_statef *, z_streamp)); extern int inflate_packet_flush OF(( inflate_blocks_statef *)); /* --- infblock.h */ #ifndef NO_DUMMY_DECL struct inflate_blocks_state {int dummy;}; /* for buggy compilers */ #endif /* inflate private state */ struct internal_state { /* mode */ enum { METHOD, /* waiting for method byte */ FLAG, /* waiting for flag byte */ DICT4, /* four dictionary check bytes to go */ DICT3, /* three dictionary check bytes to go */ DICT2, /* two dictionary check bytes to go */ DICT1, /* one dictionary check byte to go */ DICT0, /* waiting for inflateSetDictionary */ BLOCKS, /* decompressing blocks */ CHECK4, /* four check bytes to go */ CHECK3, /* three check bytes to go */ CHECK2, /* two check bytes to go */ CHECK1, /* one check byte to go */ DONE, /* finished check, done */ BAD} /* got an error--stay here */ mode; /* current inflate mode */ /* mode dependent information */ union { uInt method; /* if FLAGS, method byte */ struct { uLong was; /* computed check value */ uLong need; /* stream check value */ } check; /* if CHECK, check values to compare */ uInt marker; /* if BAD, inflateSync's marker bytes count */ } sub; /* submode */ /* mode independent information */ int nowrap; /* flag for no wrapper */ uInt wbits; /* log2(window size) (8..15, defaults to 15) */ inflate_blocks_statef *blocks; /* current inflate_blocks state */ }; int inflateReset(z) z_streamp z; { uLong c; if (z == Z_NULL || z->state == Z_NULL) return Z_STREAM_ERROR; z->total_in = z->total_out = 0; z->msg = Z_NULL; z->state->mode = z->state->nowrap ? BLOCKS : METHOD; inflate_blocks_reset(z->state->blocks, z, &c); Trace((stderr, "inflate: reset\n")); return Z_OK; } int inflateEnd(z) z_streamp z; { uLong c; if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) return Z_STREAM_ERROR; if (z->state->blocks != Z_NULL) inflate_blocks_free(z->state->blocks, z, &c); ZFREE(z, z->state); z->state = Z_NULL; Trace((stderr, "inflate: end\n")); return Z_OK; } int inflateInit2_(z, w, version, stream_size) z_streamp z; int w; const char *version; int stream_size; { if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; /* initialize state */ if (z == Z_NULL) return Z_STREAM_ERROR; z->msg = Z_NULL; #ifndef NO_ZCFUNCS if (z->zalloc == Z_NULL) { z->zalloc = zcalloc; z->opaque = (voidpf)0; } if (z->zfree == Z_NULL) z->zfree = zcfree; #endif if ((z->state = (struct internal_state FAR *) ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) return Z_MEM_ERROR; z->state->blocks = Z_NULL; /* handle undocumented nowrap option (no zlib header or check) */ z->state->nowrap = 0; if (w < 0) { w = - w; z->state->nowrap = 1; } /* set window size */ if (w < 8 || w > 15) { inflateEnd(z); return Z_STREAM_ERROR; } z->state->wbits = (uInt)w; /* create inflate_blocks state */ if ((z->state->blocks = inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) == Z_NULL) { inflateEnd(z); return Z_MEM_ERROR; } Trace((stderr, "inflate: allocated\n")); /* reset state */ inflateReset(z); return Z_OK; } int inflateInit_(z, version, stream_size) z_streamp z; const char *version; int stream_size; { return inflateInit2_(z, DEF_WBITS, version, stream_size); } #define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} #define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) int inflate(z, f) z_streamp z; int f; { int r; uInt b; if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL || f < 0) return Z_STREAM_ERROR; r = Z_BUF_ERROR; while (1) switch (z->state->mode) { case METHOD: NEEDBYTE if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED) { z->state->mode = BAD; z->msg = (char*)"unknown compression method"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } if ((z->state->sub.method >> 4) + 8 > z->state->wbits) { z->state->mode = BAD; z->msg = (char*)"invalid window size"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } z->state->mode = FLAG; case FLAG: NEEDBYTE b = NEXTBYTE; if (((z->state->sub.method << 8) + b) % 31) { z->state->mode = BAD; z->msg = (char*)"incorrect header check"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } Trace((stderr, "inflate: zlib header ok\n")); if (!(b & PRESET_DICT)) { z->state->mode = BLOCKS; break; } z->state->mode = DICT4; case DICT4: NEEDBYTE z->state->sub.check.need = (uLong)NEXTBYTE << 24; z->state->mode = DICT3; case DICT3: NEEDBYTE z->state->sub.check.need += (uLong)NEXTBYTE << 16; z->state->mode = DICT2; case DICT2: NEEDBYTE z->state->sub.check.need += (uLong)NEXTBYTE << 8; z->state->mode = DICT1; case DICT1: NEEDBYTE z->state->sub.check.need += (uLong)NEXTBYTE; z->adler = z->state->sub.check.need; z->state->mode = DICT0; return Z_NEED_DICT; case DICT0: z->state->mode = BAD; z->msg = (char*)"need dictionary"; z->state->sub.marker = 0; /* can try inflateSync */ return Z_STREAM_ERROR; case BLOCKS: r = inflate_blocks(z->state->blocks, z, r); if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) r = inflate_packet_flush(z->state->blocks); if (r == Z_DATA_ERROR) { z->state->mode = BAD; z->state->sub.marker = 0; /* can try inflateSync */ break; } if (r != Z_STREAM_END) return r; r = Z_OK; inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); if (z->state->nowrap) { z->state->mode = DONE; break; } z->state->mode = CHECK4; case CHECK4: NEEDBYTE z->state->sub.check.need = (uLong)NEXTBYTE << 24; z->state->mode = CHECK3; case CHECK3: NEEDBYTE z->state->sub.check.need += (uLong)NEXTBYTE << 16; z->state->mode = CHECK2; case CHECK2: NEEDBYTE z->state->sub.check.need += (uLong)NEXTBYTE << 8; z->state->mode = CHECK1; case CHECK1: NEEDBYTE z->state->sub.check.need += (uLong)NEXTBYTE; if (z->state->sub.check.was != z->state->sub.check.need) { z->state->mode = BAD; z->msg = (char*)"incorrect data check"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } Trace((stderr, "inflate: zlib check ok\n")); z->state->mode = DONE; case DONE: return Z_STREAM_END; case BAD: return Z_DATA_ERROR; default: return Z_STREAM_ERROR; } empty: if (f != Z_PACKET_FLUSH) return r; z->state->mode = BAD; z->msg = (char *)"need more for packet flush"; z->state->sub.marker = 0; /* can try inflateSync */ return Z_DATA_ERROR; } int inflateSetDictionary(z, dictionary, dictLength) z_streamp z; const Bytef *dictionary; uInt dictLength; { uInt length = dictLength; if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0) return Z_STREAM_ERROR; if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR; z->adler = 1L; if (length >= ((uInt)1<state->wbits)) { length = (1<state->wbits)-1; dictionary += dictLength - length; } inflate_set_dictionary(z->state->blocks, dictionary, length); z->state->mode = BLOCKS; return Z_OK; } /* * This subroutine adds the data at next_in/avail_in to the output history * without performing any output. The output buffer must be "caught up"; * i.e. no pending output (hence s->read equals s->write), and the state must * be BLOCKS (i.e. we should be willing to see the start of a series of * BLOCKS). On exit, the output will also be caught up, and the checksum * will have been updated if need be. */ int inflateIncomp(z) z_stream *z; { if (z->state->mode != BLOCKS) return Z_DATA_ERROR; return inflate_addhistory(z->state->blocks, z); } int inflateSync(z) z_streamp z; { uInt n; /* number of bytes to look at */ Bytef *p; /* pointer to bytes */ uInt m; /* number of marker bytes found in a row */ uLong r, w; /* temporaries to save total_in and total_out */ /* set up */ if (z == Z_NULL || z->state == Z_NULL) return Z_STREAM_ERROR; if (z->state->mode != BAD) { z->state->mode = BAD; z->state->sub.marker = 0; } if ((n = z->avail_in) == 0) return Z_BUF_ERROR; p = z->next_in; m = z->state->sub.marker; /* search */ while (n && m < 4) { if (*p == (Byte)(m < 2 ? 0 : 0xff)) m++; else if (*p) m = 0; else m = 4 - m; p++, n--; } /* restore */ z->total_in += p - z->next_in; z->next_in = p; z->avail_in = n; z->state->sub.marker = m; /* return no joy or set up to restart on a new block */ if (m != 4) return Z_DATA_ERROR; r = z->total_in; w = z->total_out; inflateReset(z); z->total_in = r; z->total_out = w; z->state->mode = BLOCKS; return Z_OK; } #undef NEEDBYTE #undef NEXTBYTE /* --- inflate.c */ /* +++ infblock.c */ /* infblock.c -- interpret and process block types to last block * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* #include "zutil.h" */ /* #include "infblock.h" */ /* +++ inftrees.h */ /* inftrees.h -- header to use inftrees.c * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Huffman code lookup table entry--this entry is four bytes for machines that have 16-bit pointers (e.g. PC's in the small or medium model). */ typedef struct inflate_huft_s FAR inflate_huft; struct inflate_huft_s { union { struct { Byte Exop; /* number of extra bits or operation */ Byte Bits; /* number of bits in this code or subcode */ } what; Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ } word; /* 16-bit, 8 bytes for 32-bit machines) */ union { uInt Base; /* literal, length base, or distance base */ inflate_huft *Next; /* pointer to next level of table */ } more; }; #ifdef DEBUG_ZLIB extern uInt inflate_hufts; #endif extern int inflate_trees_bits OF(( uIntf *, /* 19 code lengths */ uIntf *, /* bits tree desired/actual depth */ inflate_huft * FAR *, /* bits tree result */ z_streamp )); /* for zalloc, zfree functions */ extern int inflate_trees_dynamic OF(( uInt, /* number of literal/length codes */ uInt, /* number of distance codes */ uIntf *, /* that many (total) code lengths */ uIntf *, /* literal desired/actual bit depth */ uIntf *, /* distance desired/actual bit depth */ inflate_huft * FAR *, /* literal/length tree result */ inflate_huft * FAR *, /* distance tree result */ z_streamp )); /* for zalloc, zfree functions */ extern int inflate_trees_fixed OF(( uIntf *, /* literal desired/actual bit depth */ uIntf *, /* distance desired/actual bit depth */ inflate_huft * FAR *, /* literal/length tree result */ inflate_huft * FAR *)); /* distance tree result */ extern int inflate_trees_free OF(( inflate_huft *, /* tables to free */ z_streamp )); /* for zfree function */ /* --- inftrees.h */ /* +++ infcodes.h */ /* infcodes.h -- header to use infcodes.c * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ struct inflate_codes_state; typedef struct inflate_codes_state FAR inflate_codes_statef; extern inflate_codes_statef *inflate_codes_new OF(( uInt, uInt, inflate_huft *, inflate_huft *, z_streamp )); extern int inflate_codes OF(( inflate_blocks_statef *, z_streamp , int)); extern void inflate_codes_free OF(( inflate_codes_statef *, z_streamp )); /* --- infcodes.h */ /* +++ infutil.h */ /* infutil.h -- types and macros common to blocks and codes * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ #ifndef _INFUTIL_H #define _INFUTIL_H typedef enum { TYPE, /* get type bits (3, including end bit) */ LENS, /* get lengths for stored */ STORED, /* processing stored block */ TABLE, /* get table lengths */ BTREE, /* get bit lengths tree for a dynamic block */ DTREE, /* get length, distance trees for a dynamic block */ CODES, /* processing fixed or dynamic block */ DRY, /* output remaining window bytes */ DONEB, /* finished last block, done */ BADB} /* got a data error--stuck here */ inflate_block_mode; /* inflate blocks semi-private state */ struct inflate_blocks_state { /* mode */ inflate_block_mode mode; /* current inflate_block mode */ /* mode dependent information */ union { uInt left; /* if STORED, bytes left to copy */ struct { uInt table; /* table lengths (14 bits) */ uInt index; /* index into blens (or border) */ uIntf *blens; /* bit lengths of codes */ uInt bb; /* bit length tree depth */ inflate_huft *tb; /* bit length decoding tree */ } trees; /* if DTREE, decoding info for trees */ struct { inflate_huft *tl; inflate_huft *td; /* trees to free */ inflate_codes_statef *codes; } decode; /* if CODES, current state */ } sub; /* submode */ uInt last; /* true if this block is the last block */ /* mode independent information */ uInt bitk; /* bits in bit buffer */ uLong bitb; /* bit buffer */ Bytef *window; /* sliding window */ Bytef *end; /* one byte after sliding window */ Bytef *read; /* window read pointer */ Bytef *write; /* window write pointer */ check_func checkfn; /* check function */ uLong check; /* check on output */ }; /* defines for inflate input/output */ /* update pointers and return */ #define UPDBITS {s->bitb=b;s->bitk=k;} #define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} #define UPDOUT {s->write=q;} #define UPDATE {UPDBITS UPDIN UPDOUT} #define LEAVE {UPDATE return inflate_flush(s,z,r);} /* get bytes and bits */ #define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} #define NEEDBYTE {if(n)r=Z_OK;else LEAVE} #define NEXTBYTE (n--,*p++) #define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} /* output bytes */ #define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) #define LOADOUT {q=s->write;m=(uInt)WAVAIL;} #define WWRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} #define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} #define NEEDOUT {if(m==0){WWRAP if(m==0){FLUSH WWRAP if(m==0) LEAVE}}r=Z_OK;} #define OUTBYTE(a) {*q++=(Byte)(a);m--;} /* load local pointers */ #define LOAD {LOADIN LOADOUT} /* masks for lower bits (size given to avoid silly warnings with Visual C++) */ extern uInt inflate_mask[17]; /* copy as much as possible from the sliding window to the output area */ extern int inflate_flush OF(( inflate_blocks_statef *, z_streamp , int)); #ifndef NO_DUMMY_DECL struct internal_state {int dummy;}; /* for buggy compilers */ #endif #endif /* --- infutil.h */ #ifndef NO_DUMMY_DECL struct inflate_codes_state {int dummy;}; /* for buggy compilers */ #endif /* Table for deflate from PKZIP's appnote.txt. */ local const uInt border[] = { /* Order of the bit length code lengths */ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* Notes beyond the 1.93a appnote.txt: 1. Distance pointers never point before the beginning of the output stream. 2. Distance pointers can point back across blocks, up to 32k away. 3. There is an implied maximum of 7 bits for the bit length table and 15 bits for the actual data. 4. If only one code exists, then it is encoded using one bit. (Zero would be more efficient, but perhaps a little confusing.) If two codes exist, they are coded using one bit each (0 and 1). 5. There is no way of sending zero distance codes--a dummy must be sent if there are none. (History: a pre 2.0 version of PKZIP would store blocks with no distance codes, but this was discovered to be too harsh a criterion.) Valid only for 1.93a. 2.04c does allow zero distance codes, which is sent as one code of zero bits in length. 6. There are up to 286 literal/length codes. Code 256 represents the end-of-block. Note however that the static length tree defines 288 codes just to fill out the Huffman codes. Codes 286 and 287 cannot be used though, since there is no length base or extra bits defined for them. Similarily, there are up to 30 distance codes. However, static trees define 32 codes (all 5 bits) to fill out the Huffman codes, but the last two had better not show up in the data. 7. Unzip can check dynamic Huffman blocks for complete code sets. The exception is that a single code would not be complete (see #4). 8. The five bits following the block type is really the number of literal codes sent minus 257. 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits (1+6+6). Therefore, to output three times the length, you output three codes (1+1+1), whereas to output four times the same length, you only need two codes (1+3). Hmm. 10. In the tree reconstruction algorithm, Code = Code + Increment only if BitLength(i) is not zero. (Pretty obvious.) 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) 12. Note: length code 284 can represent 227-258, but length code 285 really is 258. The last length deserves its own, short code since it gets used a lot in very redundant files. The length 258 is special since 258 - 3 (the min match length) is 255. 13. The literal/length and distance code bit lengths are read as a single stream of lengths. It is possible (and advantageous) for a repeat code (16, 17, or 18) to go across the boundary between the two sets of lengths. */ void inflate_blocks_reset(s, z, c) inflate_blocks_statef *s; z_streamp z; uLongf *c; { if (s->checkfn != Z_NULL) *c = s->check; if (s->mode == BTREE || s->mode == DTREE) ZFREE(z, s->sub.trees.blens); if (s->mode == CODES) { inflate_codes_free(s->sub.decode.codes, z); inflate_trees_free(s->sub.decode.td, z); inflate_trees_free(s->sub.decode.tl, z); } s->mode = TYPE; s->bitk = 0; s->bitb = 0; s->read = s->write = s->window; if (s->checkfn != Z_NULL) z->adler = s->check = (*s->checkfn)(0L, Z_NULL, 0); Trace((stderr, "inflate: blocks reset\n")); } inflate_blocks_statef *inflate_blocks_new(z, c, w) z_streamp z; check_func c; uInt w; { inflate_blocks_statef *s; if ((s = (inflate_blocks_statef *)ZALLOC (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) return s; if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) { ZFREE(z, s); return Z_NULL; } s->end = s->window + w; s->checkfn = c; s->mode = TYPE; Trace((stderr, "inflate: blocks allocated\n")); inflate_blocks_reset(s, z, &s->check); return s; } #ifdef DEBUG_ZLIB extern uInt inflate_hufts; #endif int inflate_blocks(s, z, r) inflate_blocks_statef *s; z_streamp z; int r; { uInt t; /* temporary storage */ uLong b; /* bit buffer */ uInt k; /* bits in bit buffer */ Bytef *p; /* input data pointer */ uInt n; /* bytes available there */ Bytef *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ /* copy input/output information to locals (UPDATE macro restores) */ LOAD /* process input based on current state */ while (1) switch (s->mode) { case TYPE: NEEDBITS(3) t = (uInt)b & 7; s->last = t & 1; switch (t >> 1) { case 0: /* stored */ Trace((stderr, "inflate: stored block%s\n", s->last ? " (last)" : "")); DUMPBITS(3) t = k & 7; /* go to byte boundary */ DUMPBITS(t) s->mode = LENS; /* get length of stored block */ break; case 1: /* fixed */ Trace((stderr, "inflate: fixed codes block%s\n", s->last ? " (last)" : "")); { uInt bl, bd; inflate_huft *tl, *td; inflate_trees_fixed(&bl, &bd, &tl, &td); s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); if (s->sub.decode.codes == Z_NULL) { r = Z_MEM_ERROR; LEAVE } s->sub.decode.tl = Z_NULL; /* don't try to free these */ s->sub.decode.td = Z_NULL; } DUMPBITS(3) s->mode = CODES; break; case 2: /* dynamic */ Trace((stderr, "inflate: dynamic codes block%s\n", s->last ? " (last)" : "")); DUMPBITS(3) s->mode = TABLE; break; case 3: /* illegal */ DUMPBITS(3) s->mode = BADB; z->msg = (char*)"invalid block type"; r = Z_DATA_ERROR; LEAVE } break; case LENS: NEEDBITS(32) if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) { s->mode = BADB; z->msg = (char*)"invalid stored block lengths"; r = Z_DATA_ERROR; LEAVE } s->sub.left = (uInt)b & 0xffff; b = k = 0; /* dump bits */ Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); break; case STORED: if (n == 0) LEAVE NEEDOUT t = s->sub.left; if (t > n) t = n; if (t > m) t = m; zmemcpy(q, p, t); p += t; n -= t; q += t; m -= t; if ((s->sub.left -= t) != 0) break; Tracev((stderr, "inflate: stored end, %lu total out\n", z->total_out + (q >= s->read ? q - s->read : (s->end - s->read) + (q - s->window)))); s->mode = s->last ? DRY : TYPE; break; case TABLE: NEEDBITS(14) s->sub.trees.table = t = (uInt)b & 0x3fff; #ifndef PKZIP_BUG_WORKAROUND if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) { s->mode = BADB; z->msg = (char*)"too many length or distance symbols"; r = Z_DATA_ERROR; LEAVE } #endif t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); if (t < 19) t = 19; if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) { r = Z_MEM_ERROR; LEAVE } DUMPBITS(14) s->sub.trees.index = 0; Tracev((stderr, "inflate: table sizes ok\n")); s->mode = BTREE; case BTREE: while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) { NEEDBITS(3) s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; DUMPBITS(3) } while (s->sub.trees.index < 19) s->sub.trees.blens[border[s->sub.trees.index++]] = 0; s->sub.trees.bb = 7; t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, &s->sub.trees.tb, z); if (t != Z_OK) { r = t; if (r == Z_DATA_ERROR) { s->mode = BADB; ZFREE(z, s->sub.trees.blens); } LEAVE } s->sub.trees.index = 0; Tracev((stderr, "inflate: bits tree ok\n")); s->mode = DTREE; case DTREE: while (t = s->sub.trees.table, s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) { inflate_huft *h; uInt i, j, c; t = s->sub.trees.bb; NEEDBITS(t) h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); t = h->word.what.Bits; c = h->more.Base; if (c < 16) { DUMPBITS(t) s->sub.trees.blens[s->sub.trees.index++] = c; } else /* c == 16..18 */ { i = c == 18 ? 7 : c - 14; j = c == 18 ? 11 : 3; NEEDBITS(t + i) DUMPBITS(t) j += (uInt)b & inflate_mask[i]; DUMPBITS(i) i = s->sub.trees.index; t = s->sub.trees.table; if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) { inflate_trees_free(s->sub.trees.tb, z); ZFREE(z, s->sub.trees.blens); s->mode = BADB; z->msg = (char*)"invalid bit length repeat"; r = Z_DATA_ERROR; LEAVE } c = c == 16 ? s->sub.trees.blens[i - 1] : 0; do { s->sub.trees.blens[i++] = c; } while (--j); s->sub.trees.index = i; } } inflate_trees_free(s->sub.trees.tb, z); s->sub.trees.tb = Z_NULL; { uInt bl, bd; inflate_huft *tl, *td; inflate_codes_statef *c; bl = 9; /* must be <= 9 for lookahead assumptions */ bd = 6; /* must be <= 9 for lookahead assumptions */ t = s->sub.trees.table; #ifdef DEBUG_ZLIB inflate_hufts = 0; #endif t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), s->sub.trees.blens, &bl, &bd, &tl, &td, z); if (t != Z_OK) { if (t == (uInt)Z_DATA_ERROR) { s->mode = BADB; ZFREE(z, s->sub.trees.blens); } r = t; LEAVE } ZFREE(z, s->sub.trees.blens); Tracev((stderr, "inflate: trees ok, %d * %d bytes used\n", inflate_hufts, sizeof(inflate_huft))); if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) { inflate_trees_free(td, z); inflate_trees_free(tl, z); r = Z_MEM_ERROR; LEAVE } s->sub.decode.codes = c; s->sub.decode.tl = tl; s->sub.decode.td = td; } s->mode = CODES; case CODES: UPDATE if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) return inflate_flush(s, z, r); r = Z_OK; inflate_codes_free(s->sub.decode.codes, z); inflate_trees_free(s->sub.decode.td, z); inflate_trees_free(s->sub.decode.tl, z); LOAD Tracev((stderr, "inflate: codes end, %lu total out\n", z->total_out + (q >= s->read ? q - s->read : (s->end - s->read) + (q - s->window)))); if (!s->last) { s->mode = TYPE; break; } if (k > 7) /* return unused byte, if any */ { Assert(k < 16, "inflate_codes grabbed too many bytes") k -= 8; n++; p--; /* can always return one */ } s->mode = DRY; case DRY: FLUSH if (s->read != s->write) LEAVE s->mode = DONEB; case DONEB: r = Z_STREAM_END; LEAVE case BADB: r = Z_DATA_ERROR; LEAVE default: r = Z_STREAM_ERROR; LEAVE } } int inflate_blocks_free(s, z, c) inflate_blocks_statef *s; z_streamp z; uLongf *c; { inflate_blocks_reset(s, z, c); ZFREE(z, s->window); ZFREE(z, s); Trace((stderr, "inflate: blocks freed\n")); return Z_OK; } void inflate_set_dictionary(s, d, n) inflate_blocks_statef *s; const Bytef *d; uInt n; { zmemcpy((charf *)s->window, d, n); s->read = s->write = s->window + n; } /* * This subroutine adds the data at next_in/avail_in to the output history * without performing any output. The output buffer must be "caught up"; * i.e. no pending output (hence s->read equals s->write), and the state must * be BLOCKS (i.e. we should be willing to see the start of a series of * BLOCKS). On exit, the output will also be caught up, and the checksum * will have been updated if need be. */ int inflate_addhistory(s, z) inflate_blocks_statef *s; z_stream *z; { uLong b; /* bit buffer */ /* NOT USED HERE */ uInt k; /* bits in bit buffer */ /* NOT USED HERE */ uInt t; /* temporary storage */ Bytef *p; /* input data pointer */ uInt n; /* bytes available there */ Bytef *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ if (s->read != s->write) return Z_STREAM_ERROR; if (s->mode != TYPE) return Z_DATA_ERROR; /* we're ready to rock */ LOAD /* while there is input ready, copy to output buffer, moving * pointers as needed. */ while (n) { t = n; /* how many to do */ /* is there room until end of buffer? */ if (t > m) t = m; /* update check information */ if (s->checkfn != Z_NULL) s->check = (*s->checkfn)(s->check, q, t); zmemcpy(q, p, t); q += t; p += t; n -= t; z->total_out += t; s->read = q; /* drag read pointer forward */ /* WWRAP */ /* expand WWRAP macro by hand to handle s->read */ if (q == s->end) { s->read = q = s->window; m = WAVAIL; } } UPDATE return Z_OK; } /* * At the end of a Deflate-compressed PPP packet, we expect to have seen * a `stored' block type value but not the (zero) length bytes. */ int inflate_packet_flush(s) inflate_blocks_statef *s; { if (s->mode != LENS) return Z_DATA_ERROR; s->mode = TYPE; return Z_OK; } /* --- infblock.c */ /* +++ inftrees.c */ /* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* #include "zutil.h" */ /* #include "inftrees.h" */ char inflate_copyright[] = " inflate 1.0.4 Copyright 1995-1996 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ #ifndef NO_DUMMY_DECL struct internal_state {int dummy;}; /* for buggy compilers */ #endif /* simplify the use of the inflate_huft type with some defines */ #define base more.Base #define next more.Next #define exop word.what.Exop #define bits word.what.Bits local int huft_build OF(( uIntf *, /* code lengths in bits */ uInt, /* number of codes */ uInt, /* number of "simple" codes */ const uIntf *, /* list of base values for non-simple codes */ const uIntf *, /* list of extra bits for non-simple codes */ inflate_huft * FAR*,/* result: starting table */ uIntf *, /* maximum lookup bits (returns actual) */ z_streamp )); /* for zalloc function */ local voidpf falloc OF(( voidpf, /* opaque pointer (not used) */ uInt, /* number of items */ uInt)); /* size of item */ /* Tables for deflate from PKZIP's appnote.txt. */ local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; /* see note #13 above about 258 */ local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; local const uInt cpdext[30] = { /* Extra bits for distance codes */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /* Huffman code decoding is performed using a multi-level table lookup. The fastest way to decode is to simply build a lookup table whose size is determined by the longest code. However, the time it takes to build this table can also be a factor if the data being decoded is not very long. The most common codes are necessarily the shortest codes, so those codes dominate the decoding time, and hence the speed. The idea is you can have a shorter table that decodes the shorter, more probable codes, and then point to subsidiary tables for the longer codes. The time it costs to decode the longer codes is then traded against the time it takes to make longer tables. This results of this trade are in the variables lbits and dbits below. lbits is the number of bits the first level table for literal/ length codes can decode in one step, and dbits is the same thing for the distance codes. Subsequent tables are also less than or equal to those sizes. These values may be adjusted either when all of the codes are shorter than that, in which case the longest code length in bits is used, or when the shortest code is *longer* than the requested table size, in which case the length of the shortest code in bits is used. There are two different values for the two tables, since they code a different number of possibilities each. The literal/length table codes 286 possible values, or in a flat code, a little over eight bits. The distance table codes 30 possible values, or a little less than five bits, flat. The optimum values for speed end up being about one bit more than those, so lbits is 8+1 and dbits is 5+1. The optimum values may differ though from machine to machine, and possibly even between compilers. Your mileage may vary. */ /* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ #define BMAX 15 /* maximum bit length of any code */ #define N_MAX 288 /* maximum number of codes in any set */ #ifdef DEBUG_ZLIB uInt inflate_hufts; #endif local int huft_build(b, n, s, d, e, t, m, zs) uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ uInt n; /* number of codes (assumed <= N_MAX) */ uInt s; /* number of simple-valued codes (0..s-1) */ const uIntf *d; /* list of base values for non-simple codes */ const uIntf *e; /* list of extra bits for non-simple codes */ inflate_huft * FAR *t; /* result: starting table */ uIntf *m; /* maximum lookup bits, returns actual */ z_streamp zs; /* for zalloc function */ /* Given a list of code lengths and a maximum table size, make a set of tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR if the given code set is incomplete (the tables are still built in this case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */ { uInt a; /* counter for codes of length k */ uInt c[BMAX+1]; /* bit length count table */ uInt f; /* i repeats in table every f entries */ int g; /* maximum code length */ int h; /* table level */ register uInt i; /* counter, current code */ register uInt j; /* counter */ register int k; /* number of bits in current code */ int l; /* bits per table (returned in m) */ register uIntf *p; /* pointer into c[], b[], or v[] */ inflate_huft *q; /* points to current table */ struct inflate_huft_s r; /* table entry for structure assignment */ inflate_huft *u[BMAX]; /* table stack */ uInt v[N_MAX]; /* values in order of bit length */ register int w; /* bits before this table == (l * h) */ uInt x[BMAX+1]; /* bit offsets, then code stack */ uIntf *xp; /* pointer into x */ int y; /* number of dummy codes added */ uInt z; /* number of entries in current table */ /* Generate counts for each bit length */ p = c; #define C0 *p++ = 0; #define C2 C0 C0 C0 C0 #define C4 C2 C2 C2 C2 C4 /* clear c[]--assume BMAX+1 is 16 */ p = b; i = n; do { c[*p++]++; /* assume all entries <= BMAX */ } while (--i); if (c[0] == n) /* null input--all zero length codes */ { *t = (inflate_huft *)Z_NULL; *m = 0; return Z_OK; } /* Find minimum and maximum length, bound *m by those */ l = *m; for (j = 1; j <= BMAX; j++) if (c[j]) break; k = j; /* minimum code length */ if ((uInt)l < j) l = j; for (i = BMAX; i; i--) if (c[i]) break; g = i; /* maximum code length */ if ((uInt)l > i) l = i; *m = l; /* Adjust last length count to fill out codes, if needed */ for (y = 1 << j; j < i; j++, y <<= 1) if ((y -= c[j]) < 0) return Z_DATA_ERROR; if ((y -= c[i]) < 0) return Z_DATA_ERROR; c[i] += y; /* Generate starting offsets into the value table for each length */ x[1] = j = 0; p = c + 1; xp = x + 2; while (--i) { /* note that i == g from above */ *xp++ = (j += *p++); } /* Make a table of values in order of bit lengths */ p = b; i = 0; do { if ((j = *p++) != 0) v[x[j]++] = i; } while (++i < n); n = x[g]; /* set n to length of v */ /* Generate the Huffman codes and for each, make the table entries */ x[0] = i = 0; /* first Huffman code is zero */ p = v; /* grab values in bit order */ h = -1; /* no tables yet--level -1 */ w = -l; /* bits decoded == (l * h) */ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ q = (inflate_huft *)Z_NULL; /* ditto */ z = 0; /* ditto */ /* go through the bit lengths (k already is bits in shortest code) */ for (; k <= g; k++) { a = c[k]; while (a--) { /* here i is the Huffman code of length k bits for value *p */ /* make tables up to required level */ while (k > w + l) { h++; w += l; /* previous table always l bits */ /* compute minimum size table less than or equal to l bits */ z = g - w; z = z > (uInt)l ? l : z; /* table size upper limit */ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ { /* too few codes for k-w bit table */ f -= a + 1; /* deduct codes from patterns left */ xp = c + k; if (j < z) while (++j < z) /* try smaller tables up to z bits */ { if ((f <<= 1) <= *++xp) break; /* enough codes to use up j bits */ f -= *xp; /* else deduct codes from patterns */ } } z = 1 << j; /* table entries for j-bit table */ /* allocate and link in new table */ if ((q = (inflate_huft *)ZALLOC (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) { if (h) inflate_trees_free(u[0], zs); return Z_MEM_ERROR; /* not enough memory */ } #ifdef DEBUG_ZLIB inflate_hufts += z + 1; #endif *t = q + 1; /* link to list for huft_free() */ *(t = &(q->next)) = Z_NULL; u[h] = ++q; /* table starts after link */ /* connect to last table, if there is one */ if (h) { x[h] = i; /* save pattern for backing up */ r.bits = (Byte)l; /* bits to dump before this table */ r.exop = (Byte)j; /* bits in this table */ r.next = q; /* pointer to this table */ j = i >> (w - l); /* (get around Turbo C bug) */ u[h-1][j] = r; /* connect to last table */ } } /* set up table entry in r */ r.bits = (Byte)(k - w); if (p >= v + n) r.exop = 128 + 64; /* out of values--invalid code */ else if (*p < s) { r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ r.base = *p++; /* simple code is just the value */ } else { r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ r.base = d[*p++ - s]; } /* fill code-like entries with r */ f = 1 << (k - w); for (j = i >> w; j < z; j += f) q[j] = r; /* backwards increment the k-bit code i */ for (j = 1 << (k - 1); i & j; j >>= 1) i ^= j; i ^= j; /* backup over finished tables */ while ((i & ((1 << w) - 1)) != x[h]) { h--; /* don't need to update q */ w -= l; } } } /* Return Z_BUF_ERROR if we were given an incomplete table */ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; } int inflate_trees_bits(c, bb, tb, z) uIntf *c; /* 19 code lengths */ uIntf *bb; /* bits tree desired/actual depth */ inflate_huft * FAR *tb; /* bits tree result */ z_streamp z; /* for zfree function */ { int r; r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed dynamic bit lengths tree"; else if (r == Z_BUF_ERROR || *bb == 0) { inflate_trees_free(*tb, z); z->msg = (char*)"incomplete dynamic bit lengths tree"; r = Z_DATA_ERROR; } return r; } int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) uInt nl; /* number of literal/length codes */ uInt nd; /* number of distance codes */ uIntf *c; /* that many (total) code lengths */ uIntf *bl; /* literal desired/actual bit depth */ uIntf *bd; /* distance desired/actual bit depth */ inflate_huft * FAR *tl; /* literal/length tree result */ inflate_huft * FAR *td; /* distance tree result */ z_streamp z; /* for zfree function */ { int r; /* build literal/length tree */ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z); if (r != Z_OK || *bl == 0) { if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed literal/length tree"; else if (r != Z_MEM_ERROR) { inflate_trees_free(*tl, z); z->msg = (char*)"incomplete literal/length tree"; r = Z_DATA_ERROR; } return r; } /* build distance tree */ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z); if (r != Z_OK || (*bd == 0 && nl > 257)) { if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed distance tree"; else if (r == Z_BUF_ERROR) { #ifdef PKZIP_BUG_WORKAROUND r = Z_OK; } #else inflate_trees_free(*td, z); z->msg = (char*)"incomplete distance tree"; r = Z_DATA_ERROR; } else if (r != Z_MEM_ERROR) { z->msg = (char*)"empty distance tree with lengths"; r = Z_DATA_ERROR; } inflate_trees_free(*tl, z); return r; #endif } /* done */ return Z_OK; } /* build fixed tables only once--keep them here */ local int fixed_built = 0; #define FIXEDH 530 /* number of hufts used by fixed tables */ local inflate_huft fixed_mem[FIXEDH]; local uInt fixed_bl; local uInt fixed_bd; local inflate_huft *fixed_tl; local inflate_huft *fixed_td; local voidpf falloc(q, n, s) voidpf q; /* opaque pointer */ uInt n; /* number of items */ uInt s; /* size of item */ { Assert(s == sizeof(inflate_huft) && n <= *(intf *)q, "inflate_trees falloc overflow"); *(intf *)q -= n+s-s; /* s-s to avoid warning */ return (voidpf)(fixed_mem + *(intf *)q); } int inflate_trees_fixed(bl, bd, tl, td) uIntf *bl; /* literal desired/actual bit depth */ uIntf *bd; /* distance desired/actual bit depth */ inflate_huft * FAR *tl; /* literal/length tree result */ inflate_huft * FAR *td; /* distance tree result */ { /* build fixed tables if not already (multiple overlapped executions ok) */ if (!fixed_built) { int k; /* temporary variable */ unsigned c[288]; /* length list for huft_build */ z_stream z; /* for falloc function */ int f = FIXEDH; /* number of hufts left in fixed_mem */ /* set up fake z_stream for memory routines */ z.zalloc = falloc; z.zfree = Z_NULL; z.opaque = (voidpf)&f; /* literal table */ for (k = 0; k < 144; k++) c[k] = 8; for (; k < 256; k++) c[k] = 9; for (; k < 280; k++) c[k] = 7; for (; k < 288; k++) c[k] = 8; fixed_bl = 7; huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); /* distance table */ for (k = 0; k < 30; k++) c[k] = 5; fixed_bd = 5; huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); /* done */ Assert(f == 0, "invalid build of fixed tables"); fixed_built = 1; } *bl = fixed_bl; *bd = fixed_bd; *tl = fixed_tl; *td = fixed_td; return Z_OK; } int inflate_trees_free(t, z) inflate_huft *t; /* table to free */ z_streamp z; /* for zfree function */ /* Free the malloc'ed tables built by huft_build(), which makes a linked list of the tables it made, with the links in a dummy first entry of each table. */ { register inflate_huft *p, *q, *r; /* Reverse linked list */ p = Z_NULL; q = t; while (q != Z_NULL) { r = (q - 1)->next; (q - 1)->next = p; p = q; q = r; } /* Go through linked list, freeing from the malloced (t[-1]) address. */ while (p != Z_NULL) { q = (--p)->next; ZFREE(z,p); p = q; } return Z_OK; } /* --- inftrees.c */ /* +++ infcodes.c */ /* infcodes.c -- process literals and length/distance pairs * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* #include "zutil.h" */ /* #include "inftrees.h" */ /* #include "infblock.h" */ /* #include "infcodes.h" */ /* #include "infutil.h" */ /* +++ inffast.h */ /* inffast.h -- header to use inffast.c * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ extern int inflate_fast OF(( uInt, uInt, inflate_huft *, inflate_huft *, inflate_blocks_statef *, z_streamp )); /* --- inffast.h */ /* simplify the use of the inflate_huft type with some defines */ #define base more.Base #define next more.Next #define exop word.what.Exop #define bits word.what.Bits /* inflate codes private state */ struct inflate_codes_state { /* mode */ enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ START, /* x: set up for LEN */ LEN, /* i: get length/literal/eob next */ LENEXT, /* i: getting length extra (have base) */ DIST, /* i: get distance next */ DISTEXT, /* i: getting distance extra */ COPY, /* o: copying bytes in window, waiting for space */ LIT, /* o: got literal, waiting for output space */ WASH, /* o: got eob, possibly still output waiting */ END, /* x: got eob and all data flushed */ BADCODE} /* x: got error */ mode; /* current inflate_codes mode */ /* mode dependent information */ uInt len; union { struct { inflate_huft *tree; /* pointer into tree */ uInt need; /* bits needed */ } code; /* if LEN or DIST, where in tree */ uInt lit; /* if LIT, literal */ struct { uInt get; /* bits to get for extra */ uInt dist; /* distance back to copy from */ } copy; /* if EXT or COPY, where and how much */ } sub; /* submode */ /* mode independent information */ Byte lbits; /* ltree bits decoded per branch */ Byte dbits; /* dtree bits decoder per branch */ inflate_huft *ltree; /* literal/length/eob tree */ inflate_huft *dtree; /* distance tree */ }; inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) uInt bl, bd; inflate_huft *tl; inflate_huft *td; /* need separate declaration for Borland C++ */ z_streamp z; { inflate_codes_statef *c; if ((c = (inflate_codes_statef *) ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) { c->mode = START; c->lbits = (Byte)bl; c->dbits = (Byte)bd; c->ltree = tl; c->dtree = td; Tracev((stderr, "inflate: codes new\n")); } return c; } int inflate_codes(s, z, r) inflate_blocks_statef *s; z_streamp z; int r; { uInt j; /* temporary storage */ inflate_huft *t; /* temporary pointer */ uInt e; /* extra bits or operation */ uLong b; /* bit buffer */ uInt k; /* bits in bit buffer */ Bytef *p; /* input data pointer */ uInt n; /* bytes available there */ Bytef *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ Bytef *f; /* pointer to copy strings from */ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ /* copy input/output information to locals (UPDATE macro restores) */ LOAD /* process input and output based on current state */ while (1) switch (c->mode) { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ case START: /* x: set up for LEN */ #ifndef SLOW if (m >= 258 && n >= 10) { UPDATE r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); LOAD if (r != Z_OK) { c->mode = r == Z_STREAM_END ? WASH : BADCODE; break; } } #endif /* !SLOW */ c->sub.code.need = c->lbits; c->sub.code.tree = c->ltree; c->mode = LEN; case LEN: /* i: get length/literal/eob next */ j = c->sub.code.need; NEEDBITS(j) t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); DUMPBITS(t->bits) e = (uInt)(t->exop); if (e == 0) /* literal */ { c->sub.lit = t->base; Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", t->base)); c->mode = LIT; break; } if (e & 16) /* length */ { c->sub.copy.get = e & 15; c->len = t->base; c->mode = LENEXT; break; } if ((e & 64) == 0) /* next table */ { c->sub.code.need = e; c->sub.code.tree = t->next; break; } if (e & 32) /* end of block */ { Tracevv((stderr, "inflate: end of block\n")); c->mode = WASH; break; } c->mode = BADCODE; /* invalid code */ z->msg = (char*)"invalid literal/length code"; r = Z_DATA_ERROR; LEAVE case LENEXT: /* i: getting length extra (have base) */ j = c->sub.copy.get; NEEDBITS(j) c->len += (uInt)b & inflate_mask[j]; DUMPBITS(j) c->sub.code.need = c->dbits; c->sub.code.tree = c->dtree; Tracevv((stderr, "inflate: length %u\n", c->len)); c->mode = DIST; case DIST: /* i: get distance next */ j = c->sub.code.need; NEEDBITS(j) t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); DUMPBITS(t->bits) e = (uInt)(t->exop); if (e & 16) /* distance */ { c->sub.copy.get = e & 15; c->sub.copy.dist = t->base; c->mode = DISTEXT; break; } if ((e & 64) == 0) /* next table */ { c->sub.code.need = e; c->sub.code.tree = t->next; break; } c->mode = BADCODE; /* invalid code */ z->msg = (char*)"invalid distance code"; r = Z_DATA_ERROR; LEAVE case DISTEXT: /* i: getting distance extra */ j = c->sub.copy.get; NEEDBITS(j) c->sub.copy.dist += (uInt)b & inflate_mask[j]; DUMPBITS(j) Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); c->mode = COPY; case COPY: /* o: copying bytes in window, waiting for space */ #ifndef __TURBOC__ /* Turbo C bug for following expression */ f = (uInt)(q - s->window) < c->sub.copy.dist ? s->end - (c->sub.copy.dist - (q - s->window)) : q - c->sub.copy.dist; #else f = q - c->sub.copy.dist; if ((uInt)(q - s->window) < c->sub.copy.dist) f = s->end - (c->sub.copy.dist - (uInt)(q - s->window)); #endif while (c->len) { NEEDOUT OUTBYTE(*f++) if (f == s->end) f = s->window; c->len--; } c->mode = START; break; case LIT: /* o: got literal, waiting for output space */ NEEDOUT OUTBYTE(c->sub.lit) c->mode = START; break; case WASH: /* o: got eob, possibly more output */ FLUSH if (s->read != s->write) LEAVE c->mode = END; case END: r = Z_STREAM_END; LEAVE case BADCODE: /* x: got error */ r = Z_DATA_ERROR; LEAVE default: r = Z_STREAM_ERROR; LEAVE } } void inflate_codes_free(c, z) inflate_codes_statef *c; z_streamp z; { ZFREE(z, c); Tracev((stderr, "inflate: codes free\n")); } /* --- infcodes.c */ /* +++ infutil.c */ /* inflate_util.c -- data and routines common to blocks and codes * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* #include "zutil.h" */ /* #include "infblock.h" */ /* #include "inftrees.h" */ /* #include "infcodes.h" */ /* #include "infutil.h" */ #ifndef NO_DUMMY_DECL struct inflate_codes_state {int dummy;}; /* for buggy compilers */ #endif /* And'ing with mask[n] masks the lower n bits */ uInt inflate_mask[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; /* copy as much as possible from the sliding window to the output area */ int inflate_flush(s, z, r) inflate_blocks_statef *s; z_streamp z; int r; { uInt n; Bytef *p; Bytef *q; /* local copies of source and destination pointers */ p = z->next_out; q = s->read; /* compute number of bytes to copy as far as end of window */ n = (uInt)((q <= s->write ? s->write : s->end) - q); if (n > z->avail_out) n = z->avail_out; if (n && r == Z_BUF_ERROR) r = Z_OK; /* update counters */ z->avail_out -= n; z->total_out += n; /* update check information */ if (s->checkfn != Z_NULL) z->adler = s->check = (*s->checkfn)(s->check, q, n); /* copy as far as end of window */ if (p != Z_NULL) { zmemcpy(p, q, n); p += n; } q += n; /* see if more to copy at beginning of window */ if (q == s->end) { /* wrap pointers */ q = s->window; if (s->write == s->end) s->write = s->window; /* compute bytes to copy */ n = (uInt)(s->write - q); if (n > z->avail_out) n = z->avail_out; if (n && r == Z_BUF_ERROR) r = Z_OK; /* update counters */ z->avail_out -= n; z->total_out += n; /* update check information */ if (s->checkfn != Z_NULL) z->adler = s->check = (*s->checkfn)(s->check, q, n); /* copy */ if (p != Z_NULL) { zmemcpy(p, q, n); p += n; } q += n; } /* update pointers */ z->next_out = p; s->read = q; /* done */ return r; } /* --- infutil.c */ /* +++ inffast.c */ /* inffast.c -- process literals and length/distance pairs fast * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* #include "zutil.h" */ /* #include "inftrees.h" */ /* #include "infblock.h" */ /* #include "infcodes.h" */ /* #include "infutil.h" */ /* #include "inffast.h" */ #ifndef NO_DUMMY_DECL struct inflate_codes_state {int dummy;}; /* for buggy compilers */ #endif /* simplify the use of the inflate_huft type with some defines */ #define base more.Base #define next more.Next #define exop word.what.Exop #define bits word.what.Bits /* macros for bit input with no checking and for returning unused bytes */ #define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<>3);p-=c;k&=7;} /* Called with number of bytes left to write in window at least 258 (the maximum string length) and number of input bytes available at least ten. The ten bytes are six bytes for the longest length/ distance pair plus four bytes for overloading the bit buffer. */ int inflate_fast(bl, bd, tl, td, s, z) uInt bl, bd; inflate_huft *tl; inflate_huft *td; /* need separate declaration for Borland C++ */ inflate_blocks_statef *s; z_streamp z; { inflate_huft *t; /* temporary pointer */ uInt e; /* extra bits or operation */ uLong b; /* bit buffer */ uInt k; /* bits in bit buffer */ Bytef *p; /* input data pointer */ uInt n; /* bytes available there */ Bytef *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ uInt ml; /* mask for literal/length tree */ uInt md; /* mask for distance tree */ uInt c; /* bytes to copy */ uInt d; /* distance back to copy from */ Bytef *r; /* copy source pointer */ /* load input, output, bit values */ LOAD /* initialize masks */ ml = inflate_mask[bl]; md = inflate_mask[bd]; /* do until not enough input or output space for fast loop */ do { /* assume called with m >= 258 && n >= 10 */ /* get literal/length code */ GRABBITS(20) /* max bits for literal/length code */ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) { DUMPBITS(t->bits) Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? "inflate: * literal '%c'\n" : "inflate: * literal 0x%02x\n", t->base)); *q++ = (Byte)t->base; m--; continue; } do { DUMPBITS(t->bits) if (e & 16) { /* get extra bits for length */ e &= 15; c = t->base + ((uInt)b & inflate_mask[e]); DUMPBITS(e) Tracevv((stderr, "inflate: * length %u\n", c)); /* decode distance base of block to copy */ GRABBITS(15); /* max bits for distance code */ e = (t = td + ((uInt)b & md))->exop; do { DUMPBITS(t->bits) if (e & 16) { /* get extra bits to add to distance base */ e &= 15; GRABBITS(e) /* get extra bits (up to 13) */ d = t->base + ((uInt)b & inflate_mask[e]); DUMPBITS(e) Tracevv((stderr, "inflate: * distance %u\n", d)); /* do the copy */ m -= c; if ((uInt)(q - s->window) >= d) /* offset before dest */ { /* just copy */ r = q - d; *q++ = *r++; c--; /* minimum count is three, */ *q++ = *r++; c--; /* so unroll loop a little */ } else /* else offset after destination */ { e = d - (uInt)(q - s->window); /* bytes from offset to end */ r = s->end - e; /* pointer to offset */ if (c > e) /* if source crosses, */ { c -= e; /* copy to end of window */ do { *q++ = *r++; } while (--e); r = s->window; /* copy rest from start of window */ } } do { /* copy all or what's left */ *q++ = *r++; } while (--c); break; } else if ((e & 64) == 0) e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; else { z->msg = (char*)"invalid distance code"; UNGRAB UPDATE return Z_DATA_ERROR; } } while (1); break; } if ((e & 64) == 0) { if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) { DUMPBITS(t->bits) Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? "inflate: * literal '%c'\n" : "inflate: * literal 0x%02x\n", t->base)); *q++ = (Byte)t->base; m--; break; } } else if (e & 32) { Tracevv((stderr, "inflate: * end of block\n")); UNGRAB UPDATE return Z_STREAM_END; } else { z->msg = (char*)"invalid literal/length code"; UNGRAB UPDATE return Z_DATA_ERROR; } } while (1); } while (m >= 258 && n >= 10); /* not enough input or output--restore pointers and return */ UNGRAB UPDATE return Z_OK; } /* --- inffast.c */ /* +++ zutil.c */ /* zutil.c -- target dependent utility functions for the compression library * Copyright (C) 1995-1996 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* From: zutil.c,v 1.17 1996/07/24 13:41:12 me Exp $ */ #ifdef DEBUG_ZLIB #include #endif /* #include "zutil.h" */ #ifndef NO_DUMMY_DECL struct internal_state {int dummy;}; /* for buggy compilers */ #endif #ifndef STDC extern void exit OF((int)); #endif const char *z_errmsg[10] = { "need dictionary", /* Z_NEED_DICT 2 */ "stream end", /* Z_STREAM_END 1 */ "", /* Z_OK 0 */ "file error", /* Z_ERRNO (-1) */ "stream error", /* Z_STREAM_ERROR (-2) */ "data error", /* Z_DATA_ERROR (-3) */ "insufficient memory", /* Z_MEM_ERROR (-4) */ "buffer error", /* Z_BUF_ERROR (-5) */ "incompatible version",/* Z_VERSION_ERROR (-6) */ ""}; const char *zlibVersion() { return ZLIB_VERSION; } #ifdef DEBUG_ZLIB void z_error (m) char *m; { fprintf(stderr, "%s\n", m); exit(1); } #endif #ifndef HAVE_MEMCPY void zmemcpy(dest, source, len) Bytef* dest; Bytef* source; uInt len; { if (len == 0) return; do { *dest++ = *source++; /* ??? to be unrolled */ } while (--len != 0); } int zmemcmp(s1, s2, len) Bytef* s1; Bytef* s2; uInt len; { uInt j; for (j = 0; j < len; j++) { if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; } return 0; } void zmemzero(dest, len) Bytef* dest; uInt len; { if (len == 0) return; do { *dest++ = 0; /* ??? to be unrolled */ } while (--len != 0); } #endif #ifdef __TURBOC__ #if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__) /* Small and medium model in Turbo C are for now limited to near allocation * with reduced MAX_WBITS and MAX_MEM_LEVEL */ # define MY_ZCALLOC /* Turbo C malloc() does not allow dynamic allocation of 64K bytes * and farmalloc(64K) returns a pointer with an offset of 8, so we * must fix the pointer. Warning: the pointer must be put back to its * original form in order to free it, use zcfree(). */ #define MAX_PTR 10 /* 10*64K = 640K */ local int next_ptr = 0; typedef struct ptr_table_s { voidpf org_ptr; voidpf new_ptr; } ptr_table; local ptr_table table[MAX_PTR]; /* This table is used to remember the original form of pointers * to large buffers (64K). Such pointers are normalized with a zero offset. * Since MSDOS is not a preemptive multitasking OS, this table is not * protected from concurrent access. This hack doesn't work anyway on * a protected system like OS/2. Use Microsoft C instead. */ voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) { voidpf buf = opaque; /* just to make some compilers happy */ ulg bsize = (ulg)items*size; /* If we allocate less than 65520 bytes, we assume that farmalloc * will return a usable pointer which doesn't have to be normalized. */ if (bsize < 65520L) { buf = farmalloc(bsize); if (*(ush*)&buf != 0) return buf; } else { buf = farmalloc(bsize + 16L); } if (buf == NULL || next_ptr >= MAX_PTR) return NULL; table[next_ptr].org_ptr = buf; /* Normalize the pointer to seg:0 */ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; *(ush*)&buf = 0; table[next_ptr++].new_ptr = buf; return buf; } void zcfree (voidpf opaque, voidpf ptr) { int n; if (*(ush*)&ptr != 0) { /* object < 64K */ farfree(ptr); return; } /* Find the original pointer */ for (n = 0; n < next_ptr; n++) { if (ptr != table[n].new_ptr) continue; farfree(table[n].org_ptr); while (++n < next_ptr) { table[n-1] = table[n]; } next_ptr--; return; } ptr = opaque; /* just to make some compilers happy */ Assert(0, "zcfree: ptr not found"); } #endif #endif /* __TURBOC__ */ #if defined(M_I86) && !defined(__32BIT__) /* Microsoft C in 16-bit mode */ # define MY_ZCALLOC #if (!defined(_MSC_VER) || (_MSC_VER < 600)) # define _halloc halloc # define _hfree hfree #endif voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) { if (opaque) opaque = 0; /* to make compiler happy */ return _halloc((long)items, size); } void zcfree (voidpf opaque, voidpf ptr) { if (opaque) opaque = 0; /* to make compiler happy */ _hfree(ptr); } #endif /* MSC */ #ifndef MY_ZCALLOC /* Any system without a special alloc function */ #ifndef STDC extern voidp calloc OF((uInt items, uInt size)); extern void free OF((voidpf ptr)); #endif voidpf zcalloc (opaque, items, size) voidpf opaque; unsigned items; unsigned size; { if (opaque) items += size - size; /* make compiler happy */ return (voidpf)calloc(items, size); } void zcfree (opaque, ptr) voidpf opaque; voidpf ptr; { free(ptr); if (opaque) return; /* make compiler happy */ } #endif /* MY_ZCALLOC */ /* --- zutil.c */ /* +++ adler32.c */ /* adler32.c -- compute the Adler-32 checksum of a data stream * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* From: adler32.c,v 1.10 1996/05/22 11:52:18 me Exp $ */ /* #include "zlib.h" */ #define BASE 65521L /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #define DO1(buf,i) {s1 += buf[i]; s2 += s1;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); /* ========================================================================= */ uLong adler32(adler, buf, len) uLong adler; const Bytef *buf; uInt len; { unsigned long s1 = adler & 0xffff; unsigned long s2 = (adler >> 16) & 0xffff; int k; if (buf == Z_NULL) return 1L; while (len > 0) { k = len < NMAX ? len : NMAX; len -= k; while (k >= 16) { DO16(buf); buf += 16; k -= 16; } if (k != 0) do { s1 += *buf++; s2 += s1; } while (--k); s1 %= BASE; s2 %= BASE; } return (s2 << 16) | s1; } /* --- adler32.c */ ppp-2.4.5/common/zlib.h000066400000000000000000001242321130035057700147270ustar00rootroot00000000000000/* $Id: zlib.h,v 1.7 1997/11/27 06:03:33 paulus Exp $ */ /* * This file is derived from zlib.h and zconf.h from the zlib-1.0.4 * distribution by Jean-loup Gailly and Mark Adler, with some additions * by Paul Mackerras to aid in implementing Deflate compression and * decompression for PPP packets. */ /* * ==FILEVERSION 971127== * * This marker is used by the Linux installation script to determine * whether an up-to-date version of this file is already installed. */ /* +++ zlib.h */ /* zlib.h -- interface of the 'zlib' general purpose compression library version 1.0.4, Jul 24th, 1996. Copyright (C) 1995-1996 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler gzip@prep.ai.mit.edu madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). */ #ifndef _ZLIB_H #define _ZLIB_H #ifdef __cplusplus extern "C" { #endif /* +++ zconf.h */ /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-1996 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* From: zconf.h,v 1.20 1996/07/02 15:09:28 me Exp $ */ #ifndef _ZCONF_H #define _ZCONF_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. */ #ifdef Z_PREFIX # define deflateInit_ z_deflateInit_ # define deflate z_deflate # define deflateEnd z_deflateEnd # define inflateInit_ z_inflateInit_ # define inflate z_inflate # define inflateEnd z_inflateEnd # define deflateInit2_ z_deflateInit2_ # define deflateSetDictionary z_deflateSetDictionary # define deflateCopy z_deflateCopy # define deflateReset z_deflateReset # define deflateParams z_deflateParams # define inflateInit2_ z_inflateInit2_ # define inflateSetDictionary z_inflateSetDictionary # define inflateSync z_inflateSync # define inflateReset z_inflateReset # define compress z_compress # define uncompress z_uncompress # define adler32 z_adler32 # define crc32 z_crc32 # define get_crc_table z_get_crc_table # define Byte z_Byte # define uInt z_uInt # define uLong z_uLong # define Bytef z_Bytef # define charf z_charf # define intf z_intf # define uIntf z_uIntf # define uLongf z_uLongf # define voidpf z_voidpf # define voidp z_voidp #endif #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) # define WIN32 #endif #if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) # ifndef __32BIT__ # define __32BIT__ # endif #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #if defined(MSDOS) && !defined(__32BIT__) # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC) # define STDC #endif #if (defined(__STDC__) || defined(__cplusplus)) && !defined(STDC) # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const # endif #endif /* Some Mac compilers merge all .h files incorrectly: */ #if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__) # define NO_DUMMY_DECL #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2 */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): 1 << (windowBits+2) + 1 << (memLevel+9) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus a few kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) /* MSC small or medium model */ # define SMALL_MEDIUM # ifdef _MSC_VER # define FAR __far # else # define FAR far # endif #endif #if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) # ifndef __32BIT__ # define SMALL_MEDIUM # define FAR __far # endif #endif #ifndef FAR # define FAR #endif typedef unsigned char Byte; /* 8 bits */ typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #if defined(__BORLANDC__) && defined(SMALL_MEDIUM) /* Borland C/C++ ignores FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte FAR *voidpf; typedef Byte *voidp; #endif /* Compile with -DZLIB_DLL for Windows DLL support */ #if (defined(_WINDOWS) || defined(WINDOWS)) && defined(ZLIB_DLL) # include # define EXPORT WINAPI #else # define EXPORT #endif #endif /* _ZCONF_H */ /* --- zconf.h */ #define ZLIB_VERSION "1.0.4P" /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms may be added later and will have the same stream interface. For compression the application must provide the output buffer and may optionally provide the input buffer for optimization. For decompression, the application must provide the input buffer and may optionally provide the output buffer for optimization. Compression can be done in a single step if the buffers are large enough (for example if an input file is mmap'ed), or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. The library does not install any signal handler. It is recommended to add at least a handler for SIGSEGV when decompressing; the library checks the consistency of the input data whenever possible but may go nuts for some forms of corrupted input. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); typedef void (*free_func) OF((voidpf opaque, voidpf address)); struct internal_state; typedef struct z_stream_s { Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total nb of input bytes read so far */ Bytef *next_out; /* next output byte should be put there */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total nb of bytes output so far */ char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: ascii or binary */ uLong adler; /* adler32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; typedef z_stream FAR *z_streamp; /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return Z_NULL if there is not enough memory for the object. On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use in the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 #define Z_PACKET_FLUSH 2 #define Z_SYNC_FLUSH 3 #define Z_FULL_FLUSH 4 #define Z_FINISH 5 /* Allowed flush values; see deflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) /* Return codes for the compression/decompression functions. Negative * values are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_DEFAULT_STRATEGY 0 /* compression strategy; see deflateInit2() below for details */ #define Z_BINARY 0 #define Z_ASCII 1 #define Z_UNKNOWN 2 /* Possible values of the data_type field */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ /* basic functions */ extern const char * EXPORT zlibVersion OF((void)); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check is automatically made by deflateInit and inflateInit. */ /* extern int EXPORT deflateInit OF((z_streamp strm, int level)); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level, Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ extern int EXPORT deflate OF((z_streamp strm, int flush)); /* Performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary (in interactive applications). Some output may be provided even if flush is not set. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. If the parameter flush is set to Z_PARTIAL_FLUSH, the current compression block is terminated and flushed to the output buffer so that the decompressor can get all input data available so far. For method 9, a future variant on method 8, the current block will be flushed but not terminated. Z_SYNC_FLUSH has the same effect as partial flush except that the compressed output is byte aligned (the compressor can clear its internal bit buffer) and the current block is always terminated; this can be useful if the compressor has to be restarted from scratch after an interruption (in which case the internal state of the compressor may be lost). If flush is set to Z_FULL_FLUSH, the compression block is terminated, a special marker is output and the compression dictionary is discarded; this is useful to allow the decompressor to synchronize if one compressed block has been damaged (see inflateSync below). Flushing degrades compression and so should be used only when necessary. Using Z_FULL_FLUSH too often can seriously degrade the compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). If the parameter flush is set to Z_PACKET_FLUSH, the compression block is terminated, and a zero-length stored block is output, omitting the length bytes (the effect of this is that the 3-bit type code 000 for a stored block is output, and the output is then byte-aligned). This is designed for use at the end of a PPP packet. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space; if deflate returns with Z_OK, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used immediately after deflateInit if all the compression is to be done in a single step. In this case, avail_out must be at least 0.1% larger than avail_in plus 12 bytes. If deflate does not return Z_STREAM_END, then it must be called again as described above. deflate() may update data_type if it can make a good guess about the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible. */ extern int EXPORT deflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed prematurely (some input or output was discarded). In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* extern int EXPORT inflateInit OF((z_streamp strm)); Initializes the internal stream state for decompression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, inflateInit updates them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller. msg is set to null if there is no error message. inflateInit does not perform any decompression: this will be done by inflate(). */ extern int EXPORT inflate OF((z_streamp strm, int flush)); /* Performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in is updated and processing will resume at this point for the next call of inflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH, inflate flushes as much output as possible to the output buffer. The flushing behavior of inflate is not specified for values of the flush parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the current implementation actually flushes as much output as possible anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data has been consumed, it is expecting to see the length field of a stored block; if not, it returns Z_DATA_ERROR. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all the uncompressed data. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The next operation on this stream must be inflateEnd to deallocate the decompression state. The use of Z_FINISH is never required, but can be used to inform inflate that a faster routine may be used for the single inflate() call. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point (see inflateSetDictionary below), Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if the stream structure was inconsistent (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress is possible or if there was not enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then call inflateSync to look for a good compression block. In the Z_NEED_DICT case, strm->adler is set to the Adler32 value of the dictionary chosen by the compressor. */ extern int EXPORT inflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent. In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* Advanced functions */ /* The following functions are needed only in some special applications. */ /* extern int EXPORT deflateInit2 OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)); This is another version of deflateInit with more compression options. The fields next_in, zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. (Method 9 will allow a 64K history buffer and partial block flushes.) The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library (the value 16 will be allowed for method 9). Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no string match). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. If next_in is not null, the library will use this buffer to hold also some history information; the buffer must either hold the entire input data, or have at least 1<<(windowBits+1) bytes and be writable. If next_in is null, the library will allocate its own history buffer (and leave next_in null). next_out need not be provided here but must be provided by the application for the next call of deflate(). If the history buffer is provided by the application, next_in must must never be changed by the application since the compressor maintains information inside this buffer from call to call; the application must provide more input only by increasing avail_in. next_in is always reset by the library in this case. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid method). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ extern int EXPORT deflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the compression dictionary (history buffer) from the given byte sequence without producing any compressed output. This function must be called immediately after deflateInit or deflateInit2, before any call of deflate. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. In this version of the library, only the last 32K bytes of the dictionary are used. Upon return of this function, strm->adler is set to the Adler32 value of the dictionary; the decompressor may later use this value to determine which dictionary has been used by the compressor. (The Adler32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (such as NULL dictionary) or the stream state is inconsistent (for example if deflate has already been called for this stream). deflateSetDictionary does not perform any compression: this will be done by deflate(). */ extern int EXPORT deflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. If the source stream is using an application-supplied history buffer, a new buffer is allocated for the destination stream. The compressed output buffer is always application-supplied. It's the responsibility of the application to provide the correct values of next_out and avail_out for the next call of deflate. This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal compression state which can be quite large, so this strategy is slow and can consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being NULL). msg is left unchanged in both source and destination. */ extern int EXPORT deflateReset OF((z_streamp strm)); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate all the internal compression state. The stream will keep the same compression level and any other attributes that may have been set by deflateInit2. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being NULL). */ extern int EXPORT deflateParams OF((z_streamp strm, int level, int strategy)); /* Dynamically update the compression level and compression strategy. This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression level is changed, the input available so far is compressed with the old level (and may be flushed); the new level will take effect only at the next call of deflate(). Before the call of deflateParams, the stream state must be set as for a call of deflate(), since the currently available input may have to be compressed and flushed. In particular, strm->avail_out must be non-zero. deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if strm->avail_out was zero. */ extern int EXPORT deflateOutputPending OF((z_streamp strm)); /* Returns the number of bytes of output which are immediately available from the compressor (i.e. without any further input or flush). */ /* extern int EXPORT inflateInit2 OF((z_streamp strm, int windowBits)); This is another version of inflateInit with more compression options. The fields next_out, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library (the value 16 will be allowed soon). The default value is 15 if inflateInit is used instead. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. If next_out is not null, the library will use this buffer for the history buffer; the buffer must either be large enough to hold the entire output data, or have at least 1</dev/null 2>&1; then if [ "$archvariant" = "-64x" ]; then ( cd /tmp; echo "int x;" > ppp$$.c /opt/SUNWspro/bin/cc -c -errwarn -xchip=opteron -xarch=amd64 ppp$$.c >/dev/null 2>&1 || ( echo "WorkShop C is unable to make 64 bit modules, and your $karch system needs" echo "them. Consider upgrading cc on this machine." rm -f ppp$$.c exit 1 ) || exit 1 rm -f ppp$$.c ppp$$.o ) || exit 1 fi elif gcc --version >/dev/null 2>&1; then archvariant=gcc$archvariant compiletype=.gcc if [ "$archvariant" = "gcc-64" -o"$archvariant" = "gcc-64x" ]; then ( cd /tmp; touch ppp$$.c gcc -c -m64 ppp$$.c >/dev/null 2>&1 || ( echo "gcc is unable to make 64 bit modules, and your $karch system needs them." echo "Consider upgrading gcc on this machine, or switching to Sun WorkShop." rm -f ppp$$.c exit 1 ) || exit 1 rm -f ppp$$.c ppp$$.o ) || exit 1 fi else echo "C compiler not found; hoping for the best." fi;; NetBSD|FreeBSD|ULTRIX|OSF1|NeXTStep|SINIX-?|UNIX_SV|UNIX_System_V) state="notincluded";; # NetBSD) # makext="bsd"; # case $release in # 0.*) state="ancient";; # 1.0*) state="ancient";; # 1.1*) state="known"; ksrc="netbsd-1.1";; # 1.2*) state="known"; ksrc="netbsd-1.2"; makext="netbsd-1.2";; # 1.[3-9]*|[2-9]*) # state="late"; ksrc="netbsd-1.2";; # esac;; # ULTRIX) # makext="ultrix"; # case $release in # [0-3]*) state="ancient";; # 4.[01]*) state="early"; ksrc="ultrix";; # 4.[234]) state="known"; ksrc="ultrix";; # esac;; # OSF1) # makext="osf"; # case $release in # V1.*) state="neolithic"; ksrc="osf1";; # V[23].*) state="neolithic"; ksrc="osf1";; # V4.*) state="known"; ksrc="osf1";; # V[5-9]*) state="late"; ksrc="osf1";; # esac;; # FreeBSD) # makext="bsd"; # case $release in # 1.*) state="known"; ksrc="freebsd-old";; # 2.[01]*) state="known"; ksrc="freebsd-2.0";; # 2.2.[2-7]*) state="late"; ksrc="freebsd-2.0";; # 2.2.8*) state="known"; ksrc="freebsd-2.2.8";; # 3.[0-1]*) state="known"; ksrc="freebsd-3.0";; # esac;; # NeXTStep) # makext="NeXT"; # ksrc="NeXT"; # state="known";; # SINIX-?) # case $release in # 5.4[01]) state=known; ksrc=svr4; makext=svr4;; # 5.4[2-9]) state=late; ksrc=svr4; makext=svr4;; # esac;; # # Intel SVR4 systems come with a bug in the uname program. Unless # # your provider fixed the bug, or you get a fix for it, uname -S will # # overwrite the system name with the node name! # UNIX_SV|UNIX_System_V|`uname -n`) # case $release in # 4.0) state=known; ksrc=svr4; makext=svr4;; # 4.2) state=late; ksrc=svr4; makext=svr4;; # esac;; esac if [ -d "$ksrc" ]; then :; else state="notincluded" unset ksrc fi case $state in neolithic) echo "This is a newer release on an outdated OS ($system)." echo " This software may or may not work on this OS." echo " You may want to download an older version of PPP for this OS.";; ancient) echo "This is an old release of a supported OS ($system)." echo "This software cannot be used as-is on this system," echo "but you may be able to port it. Good luck!" exit;; early) echo "This is an old release of a supported OS ($system)." echo "This software should install and run on this system," echo "but it hasn't been tested.";; late) echo "This is a newer release of $system than is supported by" echo "this software. It may or may not work.";; unknown) echo "This software has not been ported to $system. Sorry.";; notincluded) echo "Support for $system has not been included" echo "in this distribution. Sorry.";; known) echo "Configuring for $system";; esac # Parse arguments while [ $# -gt 0 ]; do arg=$1 val= shift case $arg in *=*) val=`expr "x$arg" : 'x[^=]*=\(.*\)'` arg=`expr "x$arg" : 'x\([^=]*\)=.*'` ;; --prefix|--sysconf) if [ $# -eq 0 ]; then echo "error: the $arg argument requires a value" 1>&2 exit 1 fi val=$1 shift ;; esac case $arg in --prefix) DESTDIR=$val ;; --sysconfdir) SYSCONF=$val ;; esac done mkmkf() { rm -f $2 if [ -f $1 ]; then echo " $2 <= $1" sed -e "s,@DESTDIR@,$DESTDIR,g" -e "s,@SYSCONF@,$SYSCONF,g" $1 >$2 fi } if [ -d "$ksrc" ]; then echo "Creating Makefiles." mkmkf $ksrc/Makefile.top Makefile mkmkf $ksrc/Makedefs$compiletype Makedefs.com for dir in pppd pppstats chat pppdump pppd/plugins pppd/plugins/rp-pppoe \ pppd/plugins/radius pppd/plugins/pppoatm \ pppd/plugins/pppol2tp; do mkmkf $dir/Makefile.$makext $dir/Makefile done if [ -f $ksrc/Makefile.$makext$archvariant ]; then mkmkf $ksrc/Makefile.$makext$archvariant $ksrc/Makefile fi else echo "Unable to locate kernel source $ksrc" exit 1 fi ppp-2.4.5/contrib/000077500000000000000000000000001130035057700137625ustar00rootroot00000000000000ppp-2.4.5/contrib/pppgetpass/000077500000000000000000000000001130035057700161505ustar00rootroot00000000000000ppp-2.4.5/contrib/pppgetpass/Makefile.linux000066400000000000000000000007661130035057700207570ustar00rootroot00000000000000all: pppgetpass.vt pppgetpass.gtk pppgetpass.vt: pppgetpass.vt.o pppgetpass.gtk: pppgetpass.gtk.o $(CC) $(LDFLAGS) pppgetpass.gtk.o `gtk-config --libs` -o pppgetpass.gtk pppgetpass.gtk.o: pppgetpass.gtk.c $(CC) $(CFLAGS) -c pppgetpass.gtk.c `gtk-config --cflags` install: all install -m 755 pppgetpass.sh /usr/bin/pppgetpass install -m 4755 -o root -g root pppgetpass.vt /usr/bin/ install -m 755 -o root -g root pppgetpass.gtk /usr/X11/bin/ clean: rm -f *.o pppgetpass.gtk pppgetpass.vt core ppp-2.4.5/contrib/pppgetpass/pppgetpass.8000066400000000000000000000007021130035057700204260ustar00rootroot00000000000000.TH PPPGETPASS 8 "26 Sep 1999" .SH NAME pppgetpass \- prompt for PAP password .SH SYNOPSIS .B pppgetpass .I client server fd .SH DESCRIPTION .B pppgetpass the outer half of a plugin for PAP password prompting in pppd. If the peer requires PAP, and the .B passprompt.so plugin is loaded into pppd, it will run .B /usr/sbin/pppgetpass (or another program specified by the .B promptprog option) to prompt the user for the password. .SH SEE ALSO pppd(8) ppp-2.4.5/contrib/pppgetpass/pppgetpass.gtk.c000066400000000000000000000045471130035057700213000ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include int outfd; int err; static void okpressed(void *widget, void *clientdata) { GtkWidget *answer=clientdata; gchar *pass; int passlen; ssize_t wrote; (void)widget; pass=gtk_entry_get_text(GTK_ENTRY(answer)); passlen=strlen(pass); if(!passlen) return; if((wrote=write(outfd, pass, passlen))!=passlen) { if(wrote<0) syslog(LOG_ERR, "write error on outpipe: %m"); else syslog(LOG_ERR, "short write on outpipe"); err=1; } gtk_main_quit(); } int main(int argc, char **argv) { GtkWidget *mainwindow, *vbox, *question, *answer, *ok; char buf[1024]; gtk_init(&argc, &argv); openlog(argv[0], LOG_PID, LOG_DAEMON); if(argc!=4) { syslog(LOG_WARNING, "Usage error"); return 1; } outfd=atoi(argv[3]); mainwindow=gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(mainwindow), "pppgetpass"); gtk_signal_connect(GTK_OBJECT(mainwindow), "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), 0); vbox=gtk_vbox_new(FALSE, 5); gtk_container_add(GTK_CONTAINER(mainwindow), vbox); gtk_widget_show(vbox); if(argv[1][0] && argv[2][0]) snprintf(buf, sizeof buf, "Password for PPP client %s on server %s: ", argv[1], argv[2]); else if(argv[1][0] && !argv[2][0]) snprintf(buf, sizeof buf, "Password for PPP client %s: ", argv[1]); else if(!argv[1][0] && argv[2][0]) snprintf(buf, sizeof buf, "Password for PPP on server %s: ", argv[2]); else snprintf(buf, sizeof buf, "Enter PPP password: "); question=gtk_label_new(buf); gtk_box_pack_start(GTK_BOX(vbox), question, FALSE, TRUE, 0); gtk_widget_show(question); answer=gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(answer), 0); gtk_box_pack_start(GTK_BOX(vbox), answer, FALSE, TRUE, 0); gtk_widget_show(answer); ok=gtk_button_new_with_label("OK"); gtk_box_pack_start(GTK_BOX(vbox), ok, FALSE, TRUE, 0); gtk_signal_connect(GTK_OBJECT(ok), "clicked", GTK_SIGNAL_FUNC(okpressed), answer); gtk_widget_show(ok); gtk_widget_show(mainwindow); gtk_main(); return err; } ppp-2.4.5/contrib/pppgetpass/pppgetpass.sh000066400000000000000000000001431130035057700206700ustar00rootroot00000000000000#!/bin/sh if [ -z "$DISPLAY" ]; then exec pppgetpass.vt "$@" else exec pppgetpass.gtk "$@" fi ppp-2.4.5/contrib/pppgetpass/pppgetpass.vt.c000066400000000000000000000117341130035057700211400ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include static int console_owner(uid_t, int); int main(int argc, char **argv) { int console; uid_t uid; struct vt_stat origstate; int openvtnum; char openvtname[256]; int openvt; gid_t gid; int chowned; FILE *fp; struct termios t; char pass[256], *nl; int outfd, passlen; ssize_t wrote; console=open("/dev/console", O_RDWR); uid=getuid(); gid=getgid(); seteuid(uid); openlog(argv[0], LOG_PID, LOG_DAEMON); if(argc!=4) { syslog(LOG_WARNING, "Usage error"); return 1; } if(console<0) { syslog(LOG_ERR, "open(/dev/console): %m"); return 1; } if(ioctl(console, VT_GETSTATE, &origstate)<0) { syslog(LOG_ERR, "VT_GETSTATE: %m"); return 1; } if(uid) { if(!console_owner(uid, origstate.v_active)) { int i; for(i=0;i<64;++i) { if(i!=origstate.v_active && console_owner(uid, i)) break; } if(i==64) { syslog(LOG_WARNING, "run by uid %lu not at console", (unsigned long)uid); return 1; } } } if(ioctl(console, VT_OPENQRY, &openvtnum)<0) { syslog(LOG_ERR, "VT_OPENQRY: %m"); return 1; } if(openvtnum==-1) { syslog(LOG_ERR, "No free VTs"); return 1; } snprintf(openvtname, sizeof openvtname, "/dev/tty%d", openvtnum); seteuid(0); openvt=open(openvtname, O_RDWR); if(openvt<0) { seteuid(uid); syslog(LOG_ERR, "open(%s): %m", openvtname); return 1; } chowned=fchown(openvt, uid, gid); if(chowned<0) { seteuid(uid); syslog(LOG_ERR, "fchown(%s): %m", openvtname); return 1; } close(console); if(ioctl(openvt, VT_ACTIVATE, openvtnum)<0) { seteuid(uid); syslog(LOG_ERR, "VT_ACTIVATE(%d): %m", openvtnum); return 1; } while(ioctl(openvt, VT_WAITACTIVE, openvtnum)<0) { if(errno!=EINTR) { ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "VT_WAITACTIVE(%d): %m", openvtnum); return 1; } } seteuid(uid); fp=fdopen(openvt, "r+"); if(!fp) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "fdopen(%s): %m", openvtname); return 1; } if(tcgetattr(openvt, &t)<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "tcgetattr(%s): %m", openvtname); return 1; } t.c_lflag &= ~ECHO; if(tcsetattr(openvt, TCSANOW, &t)<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "tcsetattr(%s): %m", openvtname); return 1; } if(fprintf(fp, "\033[2J\033[H")<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "write error on %s: %m", openvtname); return 1; } if(argv[1][0] && argv[2][0]) { if(fprintf(fp, "Password for PPP client %s on server %s: ", argv[1], argv[2])<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "write error on %s: %m", openvtname); return 1; } } else if(argv[1][0] && !argv[2][0]) { if(fprintf(fp, "Password for PPP client %s: ", argv[1])<0) { syslog(LOG_ERR, "write error on %s: %m", openvtname); seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); return 1; } } else if(!argv[1][0] && argv[2][0]) { if(fprintf(fp, "Password for PPP on server %s: ", argv[2])<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "write error on %s: %m", openvtname); return 1; } } else { if(fprintf(fp, "Enter PPP password: ")<0) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); syslog(LOG_ERR, "write error on %s: %m", openvtname); return 1; } } if(!fgets(pass, sizeof pass, fp)) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); if(ferror(fp)) { syslog(LOG_ERR, "read error on %s: %m", openvtname); } return 1; } if((nl=strchr(pass, '\n'))) *nl=0; passlen=strlen(pass); outfd=atoi(argv[3]); if((wrote=write(outfd, pass, passlen))!=passlen) { seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); if(wrote<0) syslog(LOG_ERR, "write error on outpipe: %m"); else syslog(LOG_ERR, "short write on outpipe"); return 1; } seteuid(0); ioctl(openvt, VT_ACTIVATE, origstate.v_active); seteuid(uid); return 0; } static int console_owner(uid_t uid, int cons) { char name[256]; struct stat st; snprintf(name, sizeof name, "/dev/tty%d", cons); if(stat(name, &st)<0) { if(errno!=ENOENT) syslog(LOG_ERR, "stat(%s): %m", name); return 0; } return uid==st.st_uid; } ppp-2.4.5/etc.ppp/000077500000000000000000000000001130035057700136735ustar00rootroot00000000000000ppp-2.4.5/etc.ppp/chap-secrets000066400000000000000000000001161130035057700161750ustar00rootroot00000000000000# Secrets for authentication using CHAP # client server secret IP addresses ppp-2.4.5/etc.ppp/options000066400000000000000000000000051130035057700153040ustar00rootroot00000000000000lock ppp-2.4.5/etc.ppp/pap-secrets000066400000000000000000000001151130035057700160410ustar00rootroot00000000000000# Secrets for authentication using PAP # client server secret IP addresses ppp-2.4.5/include/000077500000000000000000000000001130035057700137455ustar00rootroot00000000000000ppp-2.4.5/include/linux/000077500000000000000000000000001130035057700151045ustar00rootroot00000000000000ppp-2.4.5/include/linux/if_ppp.h000066400000000000000000000163441130035057700165420ustar00rootroot00000000000000/* $Id: if_ppp.h,v 1.23 2002/12/06 09:49:15 paulus Exp $ */ /* * if_ppp.h - Point-to-Point Protocol definitions. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* * ==FILEVERSION 20000724== * * NOTE TO MAINTAINERS: * If you modify this file at all, please set the above date. * if_ppp.h is shipped with a PPP distribution as well as with the kernel; * if everyone increases the FILEVERSION number above, then scripts * can do the right thing when deciding whether to install a new if_ppp.h * file. Don't change the format of that line otherwise, so the * installation script can recognize it. */ #ifndef _IF_PPP_H_ #define _IF_PPP_H_ /* * Packet sizes */ #define PPP_MTU 1500 /* Default MTU (size of Info field) */ #define PPP_MAXMRU 65000 /* Largest MRU we allow */ #define PROTO_IPX 0x002b /* protocol numbers */ #define PROTO_DNA_RT 0x0027 /* DNA Routing */ /* * Bit definitions for flags. */ #define SC_COMP_PROT 0x00000001 /* protocol compression (output) */ #define SC_COMP_AC 0x00000002 /* header compression (output) */ #define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */ #define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */ #define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */ #define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */ #define SC_CCP_OPEN 0x00000040 /* Look at CCP packets */ #define SC_CCP_UP 0x00000080 /* May send/recv compressed packets */ #define SC_ENABLE_IP 0x00000100 /* IP packets may be exchanged */ #define SC_LOOP_TRAFFIC 0x00000200 /* send traffic to pppd */ #define SC_MULTILINK 0x00000400 /* do multilink encapsulation */ #define SC_MP_SHORTSEQ 0x00000800 /* use short MP sequence numbers */ #define SC_COMP_RUN 0x00001000 /* compressor has been inited */ #define SC_DECOMP_RUN 0x00002000 /* decompressor has been inited */ #define SC_MP_XSHORTSEQ 0x00004000 /* transmit short MP seq numbers */ #define SC_DEBUG 0x00010000 /* enable debug messages */ #define SC_LOG_INPKT 0x00020000 /* log contents of good pkts recvd */ #define SC_LOG_OUTPKT 0x00040000 /* log contents of pkts sent */ #define SC_LOG_RAWIN 0x00080000 /* log all chars received */ #define SC_LOG_FLUSH 0x00100000 /* log all chars flushed */ #define SC_SYNC 0x00200000 /* synchronous serial mode */ #define SC_MASK 0x0f200fff /* bits that user can change */ /* state bits */ #define SC_XMIT_BUSY 0x10000000 /* (used by isdn_ppp?) */ #define SC_RCV_ODDP 0x08000000 /* have rcvd char with odd parity */ #define SC_RCV_EVNP 0x04000000 /* have rcvd char with even parity */ #define SC_RCV_B7_1 0x02000000 /* have rcvd char with bit 7 = 1 */ #define SC_RCV_B7_0 0x01000000 /* have rcvd char with bit 7 = 0 */ #define SC_DC_FERROR 0x00800000 /* fatal decomp error detected */ #define SC_DC_ERROR 0x00400000 /* non-fatal decomp error detected */ /* * Ioctl definitions. */ struct npioctl { int protocol; /* PPP protocol, e.g. PPP_IP */ enum NPmode mode; }; /* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */ struct ppp_option_data { __u8 *ptr; __u32 length; int transmit; }; struct ifpppstatsreq { struct ifreq b; struct ppp_stats stats; /* statistic information */ }; struct ifpppcstatsreq { struct ifreq b; struct ppp_comp_stats stats; }; #define ifr__name b.ifr_ifrn.ifrn_name #define stats_ptr b.ifr_ifru.ifru_data /* * Ioctl definitions. */ #define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */ #define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */ #define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */ #define PPPIOCSASYNCMAP _IOW('t', 87, int) /* set async map */ #define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */ #define PPPIOCGRASYNCMAP _IOR('t', 85, int) /* get receive async map */ #define PPPIOCSRASYNCMAP _IOW('t', 84, int) /* set receive async map */ #define PPPIOCGMRU _IOR('t', 83, int) /* get max receive unit */ #define PPPIOCSMRU _IOW('t', 82, int) /* set max receive unit */ #define PPPIOCSMAXCID _IOW('t', 81, int) /* set VJ max slot ID */ #define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */ #define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */ #define PPPIOCXFERUNIT _IO('t', 78) /* transfer PPP unit */ #define PPPIOCSCOMPRESS _IOW('t', 77, struct ppp_option_data) #define PPPIOCGNPMODE _IOWR('t', 76, struct npioctl) /* get NP mode */ #define PPPIOCSNPMODE _IOW('t', 75, struct npioctl) /* set NP mode */ #define PPPIOCSPASS _IOW('t', 71, struct sock_fprog) /* set pass filter */ #define PPPIOCSACTIVE _IOW('t', 70, struct sock_fprog) /* set active filt */ #define PPPIOCGDEBUG _IOR('t', 65, int) /* Read debug level */ #define PPPIOCSDEBUG _IOW('t', 64, int) /* Set debug level */ #define PPPIOCGIDLE _IOR('t', 63, struct ppp_idle) /* get idle time */ #define PPPIOCNEWUNIT _IOWR('t', 62, int) /* create new ppp unit */ #define PPPIOCATTACH _IOW('t', 61, int) /* attach to ppp unit */ #define PPPIOCDETACH _IOW('t', 60, int) /* detach from ppp unit/chan */ #define PPPIOCSMRRU _IOW('t', 59, int) /* set multilink MRU */ #define PPPIOCCONNECT _IOW('t', 58, int) /* connect channel to unit */ #define PPPIOCDISCONN _IO('t', 57) /* disconnect channel */ #define PPPIOCATTCHAN _IOW('t', 56, int) /* attach to ppp channel */ #define PPPIOCGCHAN _IOR('t', 55, int) /* get ppp channel number */ #define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0) #define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */ #define SIOCGPPPCSTATS (SIOCDEVPRIVATE + 2) #if !defined(ifr_mtu) #define ifr_mtu ifr_ifru.ifru_metric #endif #endif /* _IF_PPP_H_ */ ppp-2.4.5/include/linux/if_pppol2tp.h000066400000000000000000000036641130035057700175240ustar00rootroot00000000000000/*************************************************************************** * Linux PPP over L2TP (PPPoL2TP) Socket Implementation (RFC 2661) * * This file supplies definitions required by the PPP over L2TP driver * (pppol2tp.c). All version information wrt this file is located in pppol2tp.c * * License: * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ #ifndef __LINUX_IF_PPPOL2TP_H #define __LINUX_IF_PPPOL2TP_H #include /* Structure used to connect() the socket to a particular tunnel UDP * socket. */ struct pppol2tp_addr { pid_t pid; /* pid that owns the fd. * 0 => current */ int fd; /* FD of UDP socket to use */ struct sockaddr_in addr; /* IP address and port to send to */ __u16 s_tunnel, s_session; /* For matching incoming packets */ __u16 d_tunnel, d_session; /* For sending outgoing packets */ }; /* Socket options: * DEBUG - bitmask of debug message categories * SENDSEQ - 0 => don't send packets with sequence numbers * 1 => send packets with sequence numbers * RECVSEQ - 0 => receive packet sequence numbers are optional * 1 => drop receive packets without sequence numbers * LNSMODE - 0 => act as LAC. * 1 => act as LNS. * REORDERTO - reorder timeout (in millisecs). If 0, don't try to reorder. */ enum { PPPOL2TP_SO_DEBUG = 1, PPPOL2TP_SO_RECVSEQ = 2, PPPOL2TP_SO_SENDSEQ = 3, PPPOL2TP_SO_LNSMODE = 4, PPPOL2TP_SO_REORDERTO = 5, }; /* Debug message categories for the DEBUG socket option */ enum { PPPOL2TP_MSG_DEBUG = (1 << 0), /* verbose debug (if * compiled in) */ PPPOL2TP_MSG_CONTROL = (1 << 1), /* userspace - kernel * interface */ PPPOL2TP_MSG_SEQ = (1 << 2), /* sequence numbers */ PPPOL2TP_MSG_DATA = (1 << 3), /* data packets */ }; #endif ppp-2.4.5/include/linux/ppp-comp.h000066400000000000000000000150241130035057700170120ustar00rootroot00000000000000/* * ppp-comp.h - Definitions for doing PPP packet compression. * * Copyright (c) 1984 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp-comp.h,v 1.10 2002/12/06 09:49:15 paulus Exp $ */ /* * ==FILEVERSION 20020319== * * NOTE TO MAINTAINERS: * If you modify this file at all, please set the above date. * ppp-comp.h is shipped with a PPP distribution as well as with the kernel; * if everyone increases the FILEVERSION number above, then scripts * can do the right thing when deciding whether to install a new ppp-comp.h * file. Don't change the format of that line otherwise, so the * installation script can recognize it. */ #ifndef _NET_PPP_COMP_H #define _NET_PPP_COMP_H /* * The following symbols control whether we include code for * various compression methods. */ #ifndef DO_BSD_COMPRESS #define DO_BSD_COMPRESS 1 /* by default, include BSD-Compress */ #endif #ifndef DO_DEFLATE #define DO_DEFLATE 1 /* by default, include Deflate */ #endif #define DO_PREDICTOR_1 0 #define DO_PREDICTOR_2 0 /* * Structure giving methods for compression/decompression. */ struct compressor { int compress_proto; /* CCP compression protocol number */ /* Allocate space for a compressor (transmit side) */ void *(*comp_alloc) (unsigned char *options, int opt_len); /* Free space used by a compressor */ void (*comp_free) (void *state); /* Initialize a compressor */ int (*comp_init) (void *state, unsigned char *options, int opt_len, int unit, int opthdr, int debug); /* Reset a compressor */ void (*comp_reset) (void *state); /* Compress a packet */ int (*compress) (void *state, unsigned char *rptr, unsigned char *obuf, int isize, int osize); /* Return compression statistics */ void (*comp_stat) (void *state, struct compstat *stats); /* Allocate space for a decompressor (receive side) */ void *(*decomp_alloc) (unsigned char *options, int opt_len); /* Free space used by a decompressor */ void (*decomp_free) (void *state); /* Initialize a decompressor */ int (*decomp_init) (void *state, unsigned char *options, int opt_len, int unit, int opthdr, int mru, int debug); /* Reset a decompressor */ void (*decomp_reset) (void *state); /* Decompress a packet. */ int (*decompress) (void *state, unsigned char *ibuf, int isize, unsigned char *obuf, int osize); /* Update state for an incompressible packet received */ void (*incomp) (void *state, unsigned char *ibuf, int icnt); /* Return decompression statistics */ void (*decomp_stat) (void *state, struct compstat *stats); }; /* * The return value from decompress routine is the length of the * decompressed packet if successful, otherwise DECOMP_ERROR * or DECOMP_FATALERROR if an error occurred. * * We need to make this distinction so that we can disable certain * useful functionality, namely sending a CCP reset-request as a result * of an error detected after decompression. This is to avoid infringing * a patent held by Motorola. * Don't you just lurve software patents. */ #define DECOMP_ERROR -1 /* error detected before decomp. */ #define DECOMP_FATALERROR -2 /* error detected after decomp. */ /* * CCP codes. */ #define CCP_CONFREQ 1 #define CCP_CONFACK 2 #define CCP_TERMREQ 5 #define CCP_TERMACK 6 #define CCP_RESETREQ 14 #define CCP_RESETACK 15 /* * Max # bytes for a CCP option */ #define CCP_MAX_OPTION_LENGTH 32 /* * Parts of a CCP packet. */ #define CCP_CODE(dp) ((dp)[0]) #define CCP_ID(dp) ((dp)[1]) #define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3]) #define CCP_HDRLEN 4 #define CCP_OPT_CODE(dp) ((dp)[0]) #define CCP_OPT_LENGTH(dp) ((dp)[1]) #define CCP_OPT_MINLEN 2 /* * Definitions for BSD-Compress. */ #define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */ #define CILEN_BSD_COMPRESS 3 /* length of config. option */ /* Macros for handling the 3rd byte of the BSD-Compress config option. */ #define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */ #define BSD_VERSION(x) ((x) >> 5) /* version of option format */ #define BSD_CURRENT_VERSION 1 /* current version number */ #define BSD_MAKE_OPT(v, n) (((v) << 5) | (n)) #define BSD_MIN_BITS 9 /* smallest code size supported */ #define BSD_MAX_BITS 15 /* largest code size supported */ /* * Definitions for Deflate. */ #define CI_DEFLATE 26 /* config option for Deflate */ #define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */ #define CILEN_DEFLATE 4 /* length of its config option */ #define DEFLATE_MIN_SIZE 8 #define DEFLATE_MAX_SIZE 15 #define DEFLATE_METHOD_VAL 8 #define DEFLATE_SIZE(x) (((x) >> 4) + DEFLATE_MIN_SIZE) #define DEFLATE_METHOD(x) ((x) & 0x0F) #define DEFLATE_MAKE_OPT(w) ((((w) - DEFLATE_MIN_SIZE) << 4) \ + DEFLATE_METHOD_VAL) #define DEFLATE_CHK_SEQUENCE 0 /* * Definitions for MPPE. */ #define CI_MPPE 18 /* config option for MPPE */ #define CILEN_MPPE 6 /* length of config option */ /* * Definitions for other, as yet unsupported, compression methods. */ #define CI_PREDICTOR_1 1 /* config option for Predictor-1 */ #define CILEN_PREDICTOR_1 2 /* length of its config option */ #define CI_PREDICTOR_2 2 /* config option for Predictor-2 */ #define CILEN_PREDICTOR_2 2 /* length of its config option */ #endif /* _NET_PPP_COMP_H */ ppp-2.4.5/include/linux/ppp_defs.h000066400000000000000000000152501130035057700170600ustar00rootroot00000000000000/* $Id: ppp_defs.h,v 1.11 2002/12/06 09:49:15 paulus Exp $ */ /* * ppp_defs.h - PPP definitions. * * Copyright (c) 1989-2002 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * ==FILEVERSION 20020521== * * NOTE TO MAINTAINERS: * If you modify this file at all, please set the above date. * ppp_defs.h is shipped with a PPP distribution as well as with the kernel; * if everyone increases the FILEVERSION number above, then scripts * can do the right thing when deciding whether to install a new ppp_defs.h * file. Don't change the format of that line otherwise, so the * installation script can recognize it. */ #ifndef _PPP_DEFS_H_ #define _PPP_DEFS_H_ /* * The basic PPP frame. */ #define PPP_HDRLEN 4 /* octets for standard ppp header */ #define PPP_FCSLEN 2 /* octets for FCS */ #define PPP_MRU 1500 /* default MRU = max length of info field */ #define PPP_ADDRESS(p) (((__u8 *)(p))[0]) #define PPP_CONTROL(p) (((__u8 *)(p))[1]) #define PPP_PROTOCOL(p) ((((__u8 *)(p))[2] << 8) + ((__u8 *)(p))[3]) /* * Significant octet values. */ #define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ #define PPP_UI 0x03 /* Unnumbered Information */ #define PPP_FLAG 0x7e /* Flag Sequence */ #define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ #define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ /* * Protocol field values. */ #define PPP_IP 0x21 /* Internet Protocol */ #define PPP_AT 0x29 /* AppleTalk Protocol */ #define PPP_IPX 0x2b /* IPX protocol */ #define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ #define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ #define PPP_MP 0x3d /* Multilink protocol */ #define PPP_IPV6 0x57 /* Internet Protocol Version 6 */ #define PPP_COMPFRAG 0xfb /* fragment compressed below bundle */ #define PPP_COMP 0xfd /* compressed packet */ #define PPP_IPCP 0x8021 /* IP Control Protocol */ #define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ #define PPP_IPXCP 0x802b /* IPX Control Protocol */ #define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */ #define PPP_CCPFRAG 0x80fb /* CCP at link level (below MP bundle) */ #define PPP_CCP 0x80fd /* Compression Control Protocol */ #define PPP_ECPFRAG 0x8055 /* ECP at link level (below MP bundle) */ #define PPP_ECP 0x8053 /* Encryption Control Protocol */ #define PPP_LCP 0xc021 /* Link Control Protocol */ #define PPP_PAP 0xc023 /* Password Authentication Protocol */ #define PPP_LQR 0xc025 /* Link Quality Report protocol */ #define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ #define PPP_CBCP 0xc029 /* Callback Control Protocol */ /* * Values for FCS calculations. */ #define PPP_INITFCS 0xffff /* Initial FCS value */ #define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ #define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) /* * Extended asyncmap - allows any character to be escaped. */ typedef __u32 ext_accm[8]; /* * What to do with network protocol (NP) packets. */ enum NPmode { NPMODE_PASS, /* pass the packet through */ NPMODE_DROP, /* silently drop the packet */ NPMODE_ERROR, /* return an error */ NPMODE_QUEUE /* save it up for later. */ }; /* * Statistics for LQRP and pppstats */ struct pppstat { __u32 ppp_discards; /* # frames discarded */ __u32 ppp_ibytes; /* bytes received */ __u32 ppp_ioctects; /* bytes received not in error */ __u32 ppp_ipackets; /* packets received */ __u32 ppp_ierrors; /* receive errors */ __u32 ppp_ilqrs; /* # LQR frames received */ __u32 ppp_obytes; /* raw bytes sent */ __u32 ppp_ooctects; /* frame bytes sent */ __u32 ppp_opackets; /* packets sent */ __u32 ppp_oerrors; /* transmit errors */ __u32 ppp_olqrs; /* # LQR frames sent */ }; struct vjstat { __u32 vjs_packets; /* outbound packets */ __u32 vjs_compressed; /* outbound compressed packets */ __u32 vjs_searches; /* searches for connection state */ __u32 vjs_misses; /* times couldn't find conn. state */ __u32 vjs_uncompressedin; /* inbound uncompressed packets */ __u32 vjs_compressedin; /* inbound compressed packets */ __u32 vjs_errorin; /* inbound unknown type packets */ __u32 vjs_tossed; /* inbound packets tossed because of error */ }; struct compstat { __u32 unc_bytes; /* total uncompressed bytes */ __u32 unc_packets; /* total uncompressed packets */ __u32 comp_bytes; /* compressed bytes */ __u32 comp_packets; /* compressed packets */ __u32 inc_bytes; /* incompressible bytes */ __u32 inc_packets; /* incompressible packets */ /* the compression ratio is defined as in_count / bytes_out */ __u32 in_count; /* Bytes received */ __u32 bytes_out; /* Bytes transmitted */ double ratio; /* not computed in kernel. */ }; struct ppp_stats { struct pppstat p; /* basic PPP statistics */ struct vjstat vj; /* VJ header compression statistics */ }; struct ppp_comp_stats { struct compstat c; /* packet compression statistics */ struct compstat d; /* packet decompression statistics */ }; /* * The following structure records the time in seconds since * the last NP packet was sent or received. */ struct ppp_idle { time_t xmit_idle; /* time since last NP packet sent */ time_t recv_idle; /* time since last NP packet received */ }; #ifndef __P #ifdef __STDC__ #define __P(x) x #else #define __P(x) () #endif #endif #endif /* _PPP_DEFS_H_ */ ppp-2.4.5/include/net/000077500000000000000000000000001130035057700145335ustar00rootroot00000000000000ppp-2.4.5/include/net/if_ppp.h000066400000000000000000000144151130035057700161660ustar00rootroot00000000000000/* $Id: if_ppp.h,v 1.19 2002/12/06 09:49:15 paulus Exp $ */ /* * if_ppp.h - Point-to-Point Protocol definitions. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _IF_PPP_H_ #define _IF_PPP_H_ /* * Bit definitions for flags. */ #define SC_COMP_PROT 0x00000001 /* protocol compression (output) */ #define SC_COMP_AC 0x00000002 /* header compression (output) */ #define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */ #define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */ #define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */ #define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */ #define SC_CCP_OPEN 0x00000040 /* Look at CCP packets */ #define SC_CCP_UP 0x00000080 /* May send/recv compressed packets */ #define SC_DEBUG 0x00010000 /* enable debug messages */ #define SC_LOG_INPKT 0x00020000 /* log contents of good pkts recvd */ #define SC_LOG_OUTPKT 0x00040000 /* log contents of pkts sent */ #define SC_LOG_RAWIN 0x00080000 /* log all chars received */ #define SC_LOG_FLUSH 0x00100000 /* log all chars flushed */ #define SC_RCV_B7_0 0x01000000 /* have rcvd char with bit 7 = 0 */ #define SC_RCV_B7_1 0x02000000 /* have rcvd char with bit 7 = 1 */ #define SC_RCV_EVNP 0x04000000 /* have rcvd char with even parity */ #define SC_RCV_ODDP 0x08000000 /* have rcvd char with odd parity */ #define SC_SYNC 0x00200000 /* use synchronous HDLC framing */ #define SC_MASK 0x0fff00ff /* bits that user can change */ /* * State bits in sc_flags, not changeable by user. */ #define SC_TIMEOUT 0x00000400 /* timeout is currently pending */ #define SC_VJ_RESET 0x00000800 /* need to reset VJ decomp */ #define SC_COMP_RUN 0x00001000 /* compressor has been inited */ #define SC_DECOMP_RUN 0x00002000 /* decompressor has been inited */ #define SC_DC_ERROR 0x00004000 /* non-fatal decomp error detected */ #define SC_DC_FERROR 0x00008000 /* fatal decomp error detected */ #define SC_TBUSY 0x10000000 /* xmitter doesn't need a packet yet */ #define SC_PKTLOST 0x20000000 /* have lost or dropped a packet */ #define SC_FLUSH 0x40000000 /* flush input until next PPP_FLAG */ #define SC_ESCAPED 0x80000000 /* saw a PPP_ESCAPE */ /* * Ioctl definitions. */ struct npioctl { int protocol; /* PPP procotol, e.g. PPP_IP */ enum NPmode mode; }; /* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */ struct ppp_option_data { u_char *ptr; u_int length; int transmit; }; struct ifpppstatsreq { char ifr_name[IFNAMSIZ]; struct ppp_stats stats; }; struct ifpppcstatsreq { char ifr_name[IFNAMSIZ]; struct ppp_comp_stats stats; }; /* * Ioctl definitions. */ #define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */ #define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */ #define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */ #define PPPIOCSASYNCMAP _IOW('t', 87, int) /* set async map */ #define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */ #define PPPIOCGRASYNCMAP _IOR('t', 85, int) /* get receive async map */ #define PPPIOCSRASYNCMAP _IOW('t', 84, int) /* set receive async map */ #define PPPIOCGMRU _IOR('t', 83, int) /* get max receive unit */ #define PPPIOCSMRU _IOW('t', 82, int) /* set max receive unit */ #define PPPIOCSMAXCID _IOW('t', 81, int) /* set VJ max slot ID */ #define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */ #define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */ #define PPPIOCXFERUNIT _IO('t', 78) /* transfer PPP unit */ #define PPPIOCSCOMPRESS _IOW('t', 77, struct ppp_option_data) #define PPPIOCGNPMODE _IOWR('t', 76, struct npioctl) /* get NP mode */ #define PPPIOCSNPMODE _IOW('t', 75, struct npioctl) /* set NP mode */ #define PPPIOCGIDLE _IOR('t', 74, struct ppp_idle) /* get idle time */ #ifdef PPP_FILTER #define PPPIOCSPASS _IOW('t', 71, struct bpf_program) /* set pass filter */ #define PPPIOCSACTIVE _IOW('t', 70, struct bpf_program) /* set active filt */ #endif /* PPP_FILTER */ /* PPPIOC[GS]MTU are alternatives to SIOC[GS]IFMTU, used under Ultrix */ #define PPPIOCGMTU _IOR('t', 73, int) /* get interface MTU */ #define PPPIOCSMTU _IOW('t', 72, int) /* set interface MTU */ /* * These two are interface ioctls so that pppstats can do them on * a socket without having to open the serial device. */ #define SIOCGPPPSTATS _IOWR('i', 123, struct ifpppstatsreq) #define SIOCGPPPCSTATS _IOWR('i', 122, struct ifpppcstatsreq) #if !defined(ifr_mtu) #define ifr_mtu ifr_ifru.ifru_metric #endif #if (defined(_KERNEL) || defined(KERNEL)) && !defined(NeXT) void pppattach __P((void)); void pppintr __P((void)); #endif #endif /* _IF_PPP_H_ */ ppp-2.4.5/include/net/ppp-comp.h000066400000000000000000000137251130035057700164470ustar00rootroot00000000000000/* * ppp-comp.h - Definitions for doing PPP packet compression. * * Copyright (c) 1984 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp-comp.h,v 1.13 2002/12/06 09:49:15 paulus Exp $ */ #ifndef _NET_PPP_COMP_H #define _NET_PPP_COMP_H /* * The following symbols control whether we include code for * various compression methods. */ #ifndef DO_BSD_COMPRESS #define DO_BSD_COMPRESS 1 /* by default, include BSD-Compress */ #endif #ifndef DO_DEFLATE #define DO_DEFLATE 1 /* by default, include Deflate */ #endif #define DO_PREDICTOR_1 0 #define DO_PREDICTOR_2 0 /* * Structure giving methods for compression/decompression. */ #ifdef PACKETPTR struct compressor { int compress_proto; /* CCP compression protocol number */ /* Allocate space for a compressor (transmit side) */ void *(*comp_alloc) __P((u_char *options, int opt_len)); /* Free space used by a compressor */ void (*comp_free) __P((void *state)); /* Initialize a compressor */ int (*comp_init) __P((void *state, u_char *options, int opt_len, int unit, int hdrlen, int debug)); /* Reset a compressor */ void (*comp_reset) __P((void *state)); /* Compress a packet */ int (*compress) __P((void *state, PACKETPTR *mret, PACKETPTR mp, int orig_len, int max_len)); /* Return compression statistics */ void (*comp_stat) __P((void *state, struct compstat *stats)); /* Allocate space for a decompressor (receive side) */ void *(*decomp_alloc) __P((u_char *options, int opt_len)); /* Free space used by a decompressor */ void (*decomp_free) __P((void *state)); /* Initialize a decompressor */ int (*decomp_init) __P((void *state, u_char *options, int opt_len, int unit, int hdrlen, int mru, int debug)); /* Reset a decompressor */ void (*decomp_reset) __P((void *state)); /* Decompress a packet. */ int (*decompress) __P((void *state, PACKETPTR mp, PACKETPTR *dmpp)); /* Update state for an incompressible packet received */ void (*incomp) __P((void *state, PACKETPTR mp)); /* Return decompression statistics */ void (*decomp_stat) __P((void *state, struct compstat *stats)); }; #endif /* PACKETPTR */ /* * Return values for decompress routine. * We need to make these distinctions so that we can disable certain * useful functionality, namely sending a CCP reset-request as a result * of an error detected after decompression. This is to avoid infringing * a patent held by Motorola. * Don't you just lurve software patents. */ #define DECOMP_OK 0 /* everything went OK */ #define DECOMP_ERROR 1 /* error detected before decomp. */ #define DECOMP_FATALERROR 2 /* error detected after decomp. */ /* * CCP codes. */ #define CCP_CONFREQ 1 #define CCP_CONFACK 2 #define CCP_TERMREQ 5 #define CCP_TERMACK 6 #define CCP_RESETREQ 14 #define CCP_RESETACK 15 /* * Max # bytes for a CCP option */ #define CCP_MAX_OPTION_LENGTH 32 /* * Parts of a CCP packet. */ #define CCP_CODE(dp) ((dp)[0]) #define CCP_ID(dp) ((dp)[1]) #define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3]) #define CCP_HDRLEN 4 #define CCP_OPT_CODE(dp) ((dp)[0]) #define CCP_OPT_LENGTH(dp) ((dp)[1]) #define CCP_OPT_MINLEN 2 /* * Definitions for BSD-Compress. */ #define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */ #define CILEN_BSD_COMPRESS 3 /* length of config. option */ /* Macros for handling the 3rd byte of the BSD-Compress config option. */ #define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */ #define BSD_VERSION(x) ((x) >> 5) /* version of option format */ #define BSD_CURRENT_VERSION 1 /* current version number */ #define BSD_MAKE_OPT(v, n) (((v) << 5) | (n)) #define BSD_MIN_BITS 9 /* smallest code size supported */ #define BSD_MAX_BITS 15 /* largest code size supported */ /* * Definitions for Deflate. */ #define CI_DEFLATE 26 /* config option for Deflate */ #define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */ #define CILEN_DEFLATE 4 /* length of its config option */ #define DEFLATE_MIN_SIZE 8 #define DEFLATE_MAX_SIZE 15 #define DEFLATE_METHOD_VAL 8 #define DEFLATE_SIZE(x) (((x) >> 4) + DEFLATE_MIN_SIZE) #define DEFLATE_METHOD(x) ((x) & 0x0F) #define DEFLATE_MAKE_OPT(w) ((((w) - DEFLATE_MIN_SIZE) << 4) \ + DEFLATE_METHOD_VAL) #define DEFLATE_CHK_SEQUENCE 0 /* * Definitions for MPPE. */ #define CI_MPPE 18 /* config option for MPPE */ #define CILEN_MPPE 6 /* length of config option */ /* * Definitions for other, as yet unsupported, compression methods. */ #define CI_PREDICTOR_1 1 /* config option for Predictor-1 */ #define CILEN_PREDICTOR_1 2 /* length of its config option */ #define CI_PREDICTOR_2 2 /* config option for Predictor-2 */ #define CILEN_PREDICTOR_2 2 /* length of its config option */ #endif /* _NET_PPP_COMP_H */ ppp-2.4.5/include/net/ppp_defs.h000066400000000000000000000147141130035057700165130ustar00rootroot00000000000000/* $Id: ppp_defs.h,v 1.17 2002/12/06 09:49:15 paulus Exp $ */ /* * ppp_defs.h - PPP definitions. * * Copyright (c) 1984 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _PPP_DEFS_H_ #define _PPP_DEFS_H_ /* * The basic PPP frame. */ #define PPP_HDRLEN 4 /* octets for standard ppp header */ #define PPP_FCSLEN 2 /* octets for FCS */ /* * Packet sizes * * Note - lcp shouldn't be allowed to negotiate stuff outside these * limits. See lcp.h in the pppd directory. * (XXX - these constants should simply be shared by lcp.c instead * of living in lcp.h) */ #define PPP_MTU 1500 /* Default MTU (size of Info field) */ #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) #define PPP_MINMTU 64 #define PPP_MRU 1500 /* default MRU = max length of info field */ #define PPP_MAXMRU 65000 /* Largest MRU we allow */ #define PPP_MINMRU 128 #define PPP_ADDRESS(p) (((u_char *)(p))[0]) #define PPP_CONTROL(p) (((u_char *)(p))[1]) #define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) /* * Significant octet values. */ #define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ #define PPP_UI 0x03 /* Unnumbered Information */ #define PPP_FLAG 0x7e /* Flag Sequence */ #define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ #define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ /* * Protocol field values. */ #define PPP_IP 0x21 /* Internet Protocol */ #define PPP_AT 0x29 /* AppleTalk Protocol */ #define PPP_IPX 0x2b /* IPX protocol */ #define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ #define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ #define PPP_IPV6 0x57 /* Internet Protocol Version 6 */ #define PPP_COMP 0xfd /* compressed packet */ #define PPP_IPCP 0x8021 /* IP Control Protocol */ #define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ #define PPP_IPXCP 0x802b /* IPX Control Protocol */ #define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */ #define PPP_CCP 0x80fd /* Compression Control Protocol */ #define PPP_ECP 0x8053 /* Encryption Control Protocol */ #define PPP_LCP 0xc021 /* Link Control Protocol */ #define PPP_PAP 0xc023 /* Password Authentication Protocol */ #define PPP_LQR 0xc025 /* Link Quality Report protocol */ #define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ #define PPP_CBCP 0xc029 /* Callback Control Protocol */ #define PPP_EAP 0xc227 /* Extensible Authentication Protocol */ /* * Values for FCS calculations. */ #define PPP_INITFCS 0xffff /* Initial FCS value */ #define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ #define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) /* * A 32-bit unsigned integral type. */ #if !defined(__BIT_TYPES_DEFINED__) && !defined(_BITYPES) \ && !defined(__FreeBSD__) && (NS_TARGET < 40) #ifdef UINT32_T typedef UINT32_T u_int32_t; #else typedef unsigned int u_int32_t; typedef unsigned short u_int16_t; #endif #endif /* * Extended asyncmap - allows any character to be escaped. */ typedef u_int32_t ext_accm[8]; /* * What to do with network protocol (NP) packets. */ enum NPmode { NPMODE_PASS, /* pass the packet through */ NPMODE_DROP, /* silently drop the packet */ NPMODE_ERROR, /* return an error */ NPMODE_QUEUE /* save it up for later. */ }; /* * Statistics. */ struct pppstat { unsigned int ppp_ibytes; /* bytes received */ unsigned int ppp_ipackets; /* packets received */ unsigned int ppp_ierrors; /* receive errors */ unsigned int ppp_obytes; /* bytes sent */ unsigned int ppp_opackets; /* packets sent */ unsigned int ppp_oerrors; /* transmit errors */ }; struct vjstat { unsigned int vjs_packets; /* outbound packets */ unsigned int vjs_compressed; /* outbound compressed packets */ unsigned int vjs_searches; /* searches for connection state */ unsigned int vjs_misses; /* times couldn't find conn. state */ unsigned int vjs_uncompressedin; /* inbound uncompressed packets */ unsigned int vjs_compressedin; /* inbound compressed packets */ unsigned int vjs_errorin; /* inbound unknown type packets */ unsigned int vjs_tossed; /* inbound packets tossed because of error */ }; struct ppp_stats { struct pppstat p; /* basic PPP statistics */ struct vjstat vj; /* VJ header compression statistics */ }; struct compstat { unsigned int unc_bytes; /* total uncompressed bytes */ unsigned int unc_packets; /* total uncompressed packets */ unsigned int comp_bytes; /* compressed bytes */ unsigned int comp_packets; /* compressed packets */ unsigned int inc_bytes; /* incompressible bytes */ unsigned int inc_packets; /* incompressible packets */ unsigned int ratio; /* recent compression ratio << 8 */ }; struct ppp_comp_stats { struct compstat c; /* packet compression statistics */ struct compstat d; /* packet decompression statistics */ }; /* * The following structure records the time in seconds since * the last NP packet was sent or received. */ struct ppp_idle { time_t xmit_idle; /* time since last NP packet sent */ time_t recv_idle; /* time since last NP packet received */ }; #ifndef __P #ifdef __STDC__ #define __P(x) x #else #define __P(x) () #endif #endif #endif /* _PPP_DEFS_H_ */ ppp-2.4.5/include/net/pppio.h000066400000000000000000000112531130035057700160350ustar00rootroot00000000000000/* * pppio.h - ioctl and other misc. definitions for STREAMS modules. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: pppio.h,v 1.9 2002/12/06 09:49:15 paulus Exp $ */ #define _PPPIO(n) (('p' << 8) + (n)) #define PPPIO_NEWPPA _PPPIO(130) /* allocate a new PPP unit */ #define PPPIO_GETSTAT _PPPIO(131) /* get PPP statistics */ #define PPPIO_GETCSTAT _PPPIO(132) /* get PPP compression stats */ #define PPPIO_MTU _PPPIO(133) /* set max transmission unit */ #define PPPIO_MRU _PPPIO(134) /* set max receive unit */ #define PPPIO_CFLAGS _PPPIO(135) /* set/clear/get compression flags */ #define PPPIO_XCOMP _PPPIO(136) /* alloc transmit compressor */ #define PPPIO_RCOMP _PPPIO(137) /* alloc receive decompressor */ #define PPPIO_XACCM _PPPIO(138) /* set transmit asyncmap */ #define PPPIO_RACCM _PPPIO(139) /* set receive asyncmap */ #define PPPIO_VJINIT _PPPIO(140) /* initialize VJ comp/decomp */ #define PPPIO_ATTACH _PPPIO(141) /* attach to a ppa (without putmsg) */ #define PPPIO_LASTMOD _PPPIO(142) /* mark last ppp module */ #define PPPIO_GCLEAN _PPPIO(143) /* get 8-bit-clean flags */ #define PPPIO_DEBUG _PPPIO(144) /* request debug information */ #define PPPIO_BIND _PPPIO(145) /* bind to SAP */ #define PPPIO_NPMODE _PPPIO(146) /* set mode for handling data pkts */ #define PPPIO_GIDLE _PPPIO(147) /* get time since last data pkt */ #define PPPIO_PASSFILT _PPPIO(148) /* set filter for packets to pass */ #define PPPIO_ACTIVEFILT _PPPIO(149) /* set filter for "link active" pkts */ /* * Values for PPPIO_CFLAGS */ #define COMP_AC 0x1 /* compress address/control */ #define DECOMP_AC 0x2 /* decompress address/control */ #define COMP_PROT 0x4 /* compress PPP protocol */ #define DECOMP_PROT 0x8 /* decompress PPP protocol */ #define COMP_VJC 0x10 /* compress TCP/IP headers */ #define COMP_VJCCID 0x20 /* compress connection ID as well */ #define DECOMP_VJC 0x40 /* decompress TCP/IP headers */ #define DECOMP_VJCCID 0x80 /* accept compressed connection ID */ #define CCP_ISOPEN 0x100 /* look at CCP packets */ #define CCP_ISUP 0x200 /* do packet comp/decomp */ #define CCP_ERROR 0x400 /* (status) error in packet decomp */ #define CCP_FATALERROR 0x800 /* (status) fatal error ditto */ #define CCP_COMP_RUN 0x1000 /* (status) seen CCP ack sent */ #define CCP_DECOMP_RUN 0x2000 /* (status) seen CCP ack rcvd */ /* * Values for 8-bit-clean flags. */ #define RCV_B7_0 1 /* have rcvd char with bit 7 = 0 */ #define RCV_B7_1 2 /* have rcvd char with bit 7 = 1 */ #define RCV_EVNP 4 /* have rcvd char with even parity */ #define RCV_ODDP 8 /* have rcvd char with odd parity */ /* * Values for the first byte of M_CTL messages passed between * PPP modules. */ #define PPPCTL_OERROR 0xe0 /* output error [up] */ #define PPPCTL_IERROR 0xe1 /* input error (e.g. FCS) [up] */ #define PPPCTL_MTU 0xe2 /* set MTU [down] */ #define PPPCTL_MRU 0xe3 /* set MRU [down] */ #define PPPCTL_UNIT 0xe4 /* note PPP unit number [down] */ /* * Values for the integer argument to PPPIO_DEBUG. */ #define PPPDBG_DUMP 0x10000 /* print out debug info now */ #define PPPDBG_LOG 0x100 /* log various things */ #define PPPDBG_DRIVER 0 /* identifies ppp driver as target */ #define PPPDBG_IF 1 /* identifies ppp network i/f target */ #define PPPDBG_COMP 2 /* identifies ppp compression target */ #define PPPDBG_AHDLC 3 /* identifies ppp async hdlc target */ ppp-2.4.5/include/net/slcompress.h000066400000000000000000000133731130035057700171050ustar00rootroot00000000000000/* * Definitions for tcp compression routines. * * $Id: slcompress.h,v 1.4 1994/09/21 06:50:08 paulus Exp $ * * Copyright (c) 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: * - Initial distribution. */ #ifndef _SLCOMPRESS_H_ #define _SLCOMPRESS_H_ #define MAX_STATES 16 /* must be > 2 and < 256 */ #define MAX_HDR MLEN /* XXX 4bsd-ism: should really be 128 */ /* * Compressed packet format: * * The first octet contains the packet type (top 3 bits), TCP * 'push' bit, and flags that indicate which of the 4 TCP sequence * numbers have changed (bottom 5 bits). The next octet is a * conversation number that associates a saved IP/TCP header with * the compressed packet. The next two octets are the TCP checksum * from the original datagram. The next 0 to 15 octets are * sequence number changes, one change per bit set in the header * (there may be no changes and there are two special cases where * the receiver implicitly knows what changed -- see below). * * There are 5 numbers which can change (they are always inserted * in the following order): TCP urgent pointer, window, * acknowlegement, sequence number and IP ID. (The urgent pointer * is different from the others in that its value is sent, not the * change in value.) Since typical use of SLIP links is biased * toward small packets (see comments on MTU/MSS below), changes * use a variable length coding with one octet for numbers in the * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the * range 256 - 65535 or 0. (If the change in sequence number or * ack is more than 65535, an uncompressed packet is sent.) */ /* * Packet types (must not conflict with IP protocol version) * * The top nibble of the first octet is the packet type. There are * three possible types: IP (not proto TCP or tcp with one of the * control flags set); uncompressed TCP (a normal IP/TCP packet but * with the 8-bit protocol field replaced by an 8-bit connection id -- * this type of packet syncs the sender & receiver); and compressed * TCP (described above). * * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and * is logically part of the 4-bit "changes" field that follows. Top * three bits are actual packet type. For backward compatibility * and in the interest of conserving bits, numbers are chosen so the * IP protocol version number (4) which normally appears in this nibble * means "IP packet". */ /* packet types */ #define TYPE_IP 0x40 #define TYPE_UNCOMPRESSED_TCP 0x70 #define TYPE_COMPRESSED_TCP 0x80 #define TYPE_ERROR 0x00 /* Bits in first octet of compressed packet */ #define NEW_C 0x40 /* flag bits for what changed in a packet */ #define NEW_I 0x20 #define NEW_S 0x08 #define NEW_A 0x04 #define NEW_W 0x02 #define NEW_U 0x01 /* reserved, special-case values of above */ #define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ #define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ #define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) #define TCP_PUSH_BIT 0x10 /* * "state" data for each active tcp conversation on the wire. This is * basically a copy of the entire IP/TCP header from the last packet * we saw from the conversation together with a small identifier * the transmit & receive ends of the line use to locate saved header. */ struct cstate { struct cstate *cs_next; /* next most recently used cstate (xmit only) */ u_short cs_hlen; /* size of hdr (receive only) */ u_char cs_id; /* connection # associated with this state */ u_char cs_filler; union { char csu_hdr[MAX_HDR]; struct ip csu_ip; /* ip/tcp hdr from most recent packet */ } slcs_u; }; #define cs_ip slcs_u.csu_ip #define cs_hdr slcs_u.csu_hdr /* * all the state data for one serial line (we need one of these * per line). */ struct slcompress { struct cstate *last_cs; /* most recently used tstate */ u_char last_recv; /* last rcvd conn. id */ u_char last_xmit; /* last sent conn. id */ u_short flags; #ifndef SL_NO_STATS int sls_packets; /* outbound packets */ int sls_compressed; /* outbound compressed packets */ int sls_searches; /* searches for connection state */ int sls_misses; /* times couldn't find conn. state */ int sls_uncompressedin; /* inbound uncompressed packets */ int sls_compressedin; /* inbound compressed packets */ int sls_errorin; /* inbound unknown type packets */ int sls_tossed; /* inbound packets tossed because of error */ #endif struct cstate tstate[MAX_STATES]; /* xmit connection states */ struct cstate rstate[MAX_STATES]; /* receive connection states */ }; /* flag values */ #define SLF_TOSS 1 /* tossing rcvd frames because of input err */ void sl_compress_init __P((struct slcompress *)); void sl_compress_setup __P((struct slcompress *, int)); u_int sl_compress_tcp __P((struct mbuf *, struct ip *, struct slcompress *, int)); int sl_uncompress_tcp __P((u_char **, int, u_int, struct slcompress *)); int sl_uncompress_tcp_core __P((u_char *, int, int, u_int, struct slcompress *, u_char **, u_int *)); #endif /* _SLCOMPRESS_H_ */ ppp-2.4.5/include/net/vjcompress.h000066400000000000000000000127371130035057700171110ustar00rootroot00000000000000/* * Definitions for tcp compression routines. * * $Id: vjcompress.h,v 1.3 1996/05/28 00:55:33 paulus Exp $ * * Copyright (c) 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: * - Initial distribution. */ #ifndef _VJCOMPRESS_H_ #define _VJCOMPRESS_H_ #define MAX_STATES 16 /* must be > 2 and < 256 */ #define MAX_HDR 128 /* * Compressed packet format: * * The first octet contains the packet type (top 3 bits), TCP * 'push' bit, and flags that indicate which of the 4 TCP sequence * numbers have changed (bottom 5 bits). The next octet is a * conversation number that associates a saved IP/TCP header with * the compressed packet. The next two octets are the TCP checksum * from the original datagram. The next 0 to 15 octets are * sequence number changes, one change per bit set in the header * (there may be no changes and there are two special cases where * the receiver implicitly knows what changed -- see below). * * There are 5 numbers which can change (they are always inserted * in the following order): TCP urgent pointer, window, * acknowlegement, sequence number and IP ID. (The urgent pointer * is different from the others in that its value is sent, not the * change in value.) Since typical use of SLIP links is biased * toward small packets (see comments on MTU/MSS below), changes * use a variable length coding with one octet for numbers in the * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the * range 256 - 65535 or 0. (If the change in sequence number or * ack is more than 65535, an uncompressed packet is sent.) */ /* * Packet types (must not conflict with IP protocol version) * * The top nibble of the first octet is the packet type. There are * three possible types: IP (not proto TCP or tcp with one of the * control flags set); uncompressed TCP (a normal IP/TCP packet but * with the 8-bit protocol field replaced by an 8-bit connection id -- * this type of packet syncs the sender & receiver); and compressed * TCP (described above). * * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and * is logically part of the 4-bit "changes" field that follows. Top * three bits are actual packet type. For backward compatibility * and in the interest of conserving bits, numbers are chosen so the * IP protocol version number (4) which normally appears in this nibble * means "IP packet". */ /* packet types */ #define TYPE_IP 0x40 #define TYPE_UNCOMPRESSED_TCP 0x70 #define TYPE_COMPRESSED_TCP 0x80 #define TYPE_ERROR 0x00 /* Bits in first octet of compressed packet */ #define NEW_C 0x40 /* flag bits for what changed in a packet */ #define NEW_I 0x20 #define NEW_S 0x08 #define NEW_A 0x04 #define NEW_W 0x02 #define NEW_U 0x01 /* reserved, special-case values of above */ #define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ #define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ #define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) #define TCP_PUSH_BIT 0x10 /* * "state" data for each active tcp conversation on the wire. This is * basically a copy of the entire IP/TCP header from the last packet * we saw from the conversation together with a small identifier * the transmit & receive ends of the line use to locate saved header. */ struct cstate { struct cstate *cs_next; /* next most recently used state (xmit only) */ u_short cs_hlen; /* size of hdr (receive only) */ u_char cs_id; /* connection # associated with this state */ u_char cs_filler; union { char csu_hdr[MAX_HDR]; struct ip csu_ip; /* ip/tcp hdr from most recent packet */ } vjcs_u; }; #define cs_ip vjcs_u.csu_ip #define cs_hdr vjcs_u.csu_hdr /* * all the state data for one serial line (we need one of these per line). */ struct vjcompress { struct cstate *last_cs; /* most recently used tstate */ u_char last_recv; /* last rcvd conn. id */ u_char last_xmit; /* last sent conn. id */ u_short flags; #ifndef VJ_NO_STATS struct vjstat stats; #endif struct cstate tstate[MAX_STATES]; /* xmit connection states */ struct cstate rstate[MAX_STATES]; /* receive connection states */ }; /* flag values */ #define VJF_TOSS 1 /* tossing rcvd frames because of input err */ extern void vj_compress_init __P((struct vjcompress *comp, int max_state)); extern u_int vj_compress_tcp __P((struct ip *ip, u_int mlen, struct vjcompress *comp, int compress_cid_flag, u_char **vjhdrp)); extern void vj_uncompress_err __P((struct vjcompress *comp)); extern int vj_uncompress_uncomp __P((u_char *buf, int buflen, struct vjcompress *comp)); extern int vj_uncompress_tcp __P((u_char *buf, int buflen, int total_len, struct vjcompress *comp, u_char **hdrp, u_int *hlenp)); #endif /* _VJCOMPRESS_H_ */ ppp-2.4.5/linux/000077500000000000000000000000001130035057700134615ustar00rootroot00000000000000ppp-2.4.5/linux/Makefile.top000066400000000000000000000030031130035057700157160ustar00rootroot00000000000000# PPP top-level Makefile for Linux. DESTDIR = $(INSTROOT)@DESTDIR@ BINDIR = $(DESTDIR)/sbin INCDIR = $(DESTDIR)/include MANDIR = $(DESTDIR)/share/man ETCDIR = $(INSTROOT)@SYSCONF@/ppp # uid 0 = root INSTALL= install all: cd chat; $(MAKE) $(MFLAGS) all cd pppd/plugins; $(MAKE) $(MFLAGS) all cd pppd; $(MAKE) $(MFLAGS) all cd pppstats; $(MAKE) $(MFLAGS) all cd pppdump; $(MAKE) $(MFLAGS) all install: $(BINDIR) $(MANDIR)/man8 install-progs install-devel install-progs: cd chat; $(MAKE) $(MFLAGS) install cd pppd/plugins; $(MAKE) $(MFLAGS) install cd pppd; $(MAKE) $(MFLAGS) install cd pppstats; $(MAKE) $(MFLAGS) install cd pppdump; $(MAKE) $(MFLAGS) install install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \ $(ETCDIR)/chap-secrets install-devel: cd pppd; $(MAKE) $(MFLAGS) install-devel $(ETCDIR)/options: $(INSTALL) -c -m 644 etc.ppp/options $@ $(ETCDIR)/pap-secrets: $(INSTALL) -c -m 600 etc.ppp/pap-secrets $@ $(ETCDIR)/chap-secrets: $(INSTALL) -c -m 600 etc.ppp/chap-secrets $@ $(BINDIR): $(INSTALL) -d -m 755 $@ $(MANDIR)/man8: $(INSTALL) -d -m 755 $@ $(ETCDIR): $(INSTALL) -d -m 755 $@ clean: rm -f `find . -name '*.[oas]' -print` rm -f `find . -name 'core' -print` rm -f `find . -name '*~' -print` cd chat; $(MAKE) clean cd pppd/plugins; $(MAKE) clean cd pppd; $(MAKE) clean cd pppstats; $(MAKE) clean cd pppdump; $(MAKE) clean dist-clean: clean rm -f Makefile `find . -name Makefile -print` #kernel: # cd linux; ./kinstall.sh # no tests yet, one day... installcheck: true ppp-2.4.5/modules/000077500000000000000000000000001130035057700137725ustar00rootroot00000000000000ppp-2.4.5/modules/bsd-comp.c000066400000000000000000000717311130035057700156530ustar00rootroot00000000000000/* Because this code is derived from the 4.3BSD compress source: * * * Copyright (c) 1985, 1986 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * James A. Woods, derived from original work by Spencer Thomas * and Joseph Orost. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This version is for use with STREAMS under SunOS 4.x, * Digital UNIX, AIX 4.x, and SVR4 systems including Solaris 2. * * $Id: bsd-comp.c,v 1.21 2004/01/17 05:47:55 carlsonj Exp $ */ #ifdef AIX4 #include #endif #include #include #include #include #include "ppp_mod.h" #ifdef SVR4 #include #ifndef _BIG_ENDIAN #define BSD_LITTLE_ENDIAN #endif #endif #ifdef __osf__ #undef FIRST #undef LAST #define BSD_LITTLE_ENDIAN #endif #ifdef SOL2 #include #endif #define PACKETPTR mblk_t * #include #if DO_BSD_COMPRESS /* * PPP "BSD compress" compression * The differences between this compression and the classic BSD LZW * source are obvious from the requirement that the classic code worked * with files while this handles arbitrarily long streams that * are broken into packets. They are: * * When the code size expands, a block of junk is not emitted by * the compressor and not expected by the decompressor. * * New codes are not necessarily assigned every time an old * code is output by the compressor. This is because a packet * end forces a code to be emitted, but does not imply that a * new sequence has been seen. * * The compression ratio is checked at the first end of a packet * after the appropriate gap. Besides simplifying and speeding * things up, this makes it more likely that the transmitter * and receiver will agree when the dictionary is cleared when * compression is not going well. */ /* * A dictionary for doing BSD compress. */ struct bsd_db { int totlen; /* length of this structure */ u_int hsize; /* size of the hash table */ u_char hshift; /* used in hash function */ u_char n_bits; /* current bits/code */ u_char maxbits; u_char debug; u_char unit; u_short seqno; /* sequence number of next packet */ u_int hdrlen; /* header length to preallocate */ u_int mru; u_int maxmaxcode; /* largest valid code */ u_int max_ent; /* largest code in use */ u_int in_count; /* uncompressed bytes, aged */ u_int bytes_out; /* compressed bytes, aged */ u_int ratio; /* recent compression ratio */ u_int checkpoint; /* when to next check the ratio */ u_int clear_count; /* times dictionary cleared */ u_int incomp_count; /* incompressible packets */ u_int incomp_bytes; /* incompressible bytes */ u_int uncomp_count; /* uncompressed packets */ u_int uncomp_bytes; /* uncompressed bytes */ u_int comp_count; /* compressed packets */ u_int comp_bytes; /* compressed bytes */ u_short *lens; /* array of lengths of codes */ struct bsd_dict { union { /* hash value */ u_int32_t fcode; struct { #ifdef BSD_LITTLE_ENDIAN u_short prefix; /* preceding code */ u_char suffix; /* last character of new code */ u_char pad; #else u_char pad; u_char suffix; /* last character of new code */ u_short prefix; /* preceding code */ #endif } hs; } f; u_short codem1; /* output of hash table -1 */ u_short cptr; /* map code to hash table entry */ } dict[1]; }; #define BSD_OVHD 2 /* BSD compress overhead/packet */ #define BSD_INIT_BITS BSD_MIN_BITS static void *bsd_comp_alloc __P((u_char *options, int opt_len)); static void *bsd_decomp_alloc __P((u_char *options, int opt_len)); static void bsd_free __P((void *state)); static int bsd_comp_init __P((void *state, u_char *options, int opt_len, int unit, int hdrlen, int debug)); static int bsd_decomp_init __P((void *state, u_char *options, int opt_len, int unit, int hdrlen, int mru, int debug)); static int bsd_compress __P((void *state, mblk_t **mret, mblk_t *mp, int slen, int maxolen)); static void bsd_incomp __P((void *state, mblk_t *dmsg)); static int bsd_decompress __P((void *state, mblk_t *cmp, mblk_t **dmpp)); static void bsd_reset __P((void *state)); static void bsd_comp_stats __P((void *state, struct compstat *stats)); /* * Procedures exported to ppp_comp.c. */ struct compressor ppp_bsd_compress = { CI_BSD_COMPRESS, /* compress_proto */ bsd_comp_alloc, /* comp_alloc */ bsd_free, /* comp_free */ bsd_comp_init, /* comp_init */ bsd_reset, /* comp_reset */ bsd_compress, /* compress */ bsd_comp_stats, /* comp_stat */ bsd_decomp_alloc, /* decomp_alloc */ bsd_free, /* decomp_free */ bsd_decomp_init, /* decomp_init */ bsd_reset, /* decomp_reset */ bsd_decompress, /* decompress */ bsd_incomp, /* incomp */ bsd_comp_stats, /* decomp_stat */ }; /* * the next two codes should not be changed lightly, as they must not * lie within the contiguous general code space. */ #define CLEAR 256 /* table clear output code */ #define FIRST 257 /* first free entry */ #define LAST 255 #define MAXCODE(b) ((1 << (b)) - 1) #define BADCODEM1 MAXCODE(BSD_MAX_BITS) #define BSD_HASH(prefix,suffix,hshift) ((((u_int32_t)(suffix)) << (hshift)) \ ^ (u_int32_t)(prefix)) #define BSD_KEY(prefix,suffix) ((((u_int32_t)(suffix)) << 16) \ + (u_int32_t)(prefix)) #define CHECK_GAP 10000 /* Ratio check interval */ #define RATIO_SCALE_LOG 8 #define RATIO_SCALE (1<>RATIO_SCALE_LOG) #define DECOMP_CHUNK 256 /* * clear the dictionary */ static void bsd_clear(db) struct bsd_db *db; { db->clear_count++; db->max_ent = FIRST-1; db->n_bits = BSD_INIT_BITS; db->ratio = 0; db->bytes_out = 0; db->in_count = 0; db->checkpoint = CHECK_GAP; } /* * If the dictionary is full, then see if it is time to reset it. * * Compute the compression ratio using fixed-point arithmetic * with 8 fractional bits. * * Since we have an infinite stream instead of a single file, * watch only the local compression ratio. * * Since both peers must reset the dictionary at the same time even in * the absence of CLEAR codes (while packets are incompressible), they * must compute the same ratio. */ static int /* 1=output CLEAR */ bsd_check(db) struct bsd_db *db; { u_int new_ratio; if (db->in_count >= db->checkpoint) { /* age the ratio by limiting the size of the counts */ if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX) { db->in_count -= db->in_count/4; db->bytes_out -= db->bytes_out/4; } db->checkpoint = db->in_count + CHECK_GAP; if (db->max_ent >= db->maxmaxcode) { /* Reset the dictionary only if the ratio is worse, * or if it looks as if it has been poisoned * by incompressible data. * * This does not overflow, because * db->in_count <= RATIO_MAX. */ new_ratio = db->in_count << RATIO_SCALE_LOG; if (db->bytes_out != 0) new_ratio /= db->bytes_out; if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE) { bsd_clear(db); return 1; } db->ratio = new_ratio; } } return 0; } /* * Return statistics. */ static void bsd_comp_stats(state, stats) void *state; struct compstat *stats; { struct bsd_db *db = (struct bsd_db *) state; u_int out; stats->unc_bytes = db->uncomp_bytes; stats->unc_packets = db->uncomp_count; stats->comp_bytes = db->comp_bytes; stats->comp_packets = db->comp_count; stats->inc_bytes = db->incomp_bytes; stats->inc_packets = db->incomp_count; stats->ratio = db->in_count; out = db->bytes_out; if (stats->ratio <= 0x7fffff) stats->ratio <<= 8; else out >>= 8; if (out != 0) stats->ratio /= out; } /* * Reset state, as on a CCP ResetReq. */ static void bsd_reset(state) void *state; { struct bsd_db *db = (struct bsd_db *) state; db->seqno = 0; bsd_clear(db); db->clear_count = 0; } /* * Allocate space for a (de) compressor. */ static void * bsd_alloc(options, opt_len, decomp) u_char *options; int opt_len, decomp; { int bits; u_int newlen, hsize, hshift, maxmaxcode; struct bsd_db *db; if (opt_len != 3 || options[0] != CI_BSD_COMPRESS || options[1] != 3 || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION) return NULL; bits = BSD_NBITS(options[2]); switch (bits) { case 9: /* needs 82152 for both directions */ case 10: /* needs 84144 */ case 11: /* needs 88240 */ case 12: /* needs 96432 */ hsize = 5003; hshift = 4; break; case 13: /* needs 176784 */ hsize = 9001; hshift = 5; break; case 14: /* needs 353744 */ hsize = 18013; hshift = 6; break; case 15: /* needs 691440 */ hsize = 35023; hshift = 7; break; case 16: /* needs 1366160--far too much, */ /* hsize = 69001; */ /* and 69001 is too big for cptr */ /* hshift = 8; */ /* in struct bsd_db */ /* break; */ default: return NULL; } maxmaxcode = MAXCODE(bits); newlen = sizeof(*db) + (hsize-1) * (sizeof(db->dict[0])); #ifdef __osf__ db = (struct bsd_db *) ALLOC_SLEEP(newlen); #else db = (struct bsd_db *) ALLOC_NOSLEEP(newlen); #endif if (!db) return NULL; bzero(db, sizeof(*db) - sizeof(db->dict)); if (!decomp) { db->lens = NULL; } else { #ifdef __osf__ db->lens = (u_short *) ALLOC_SLEEP((maxmaxcode+1) * sizeof(db->lens[0])); #else db->lens = (u_short *) ALLOC_NOSLEEP((maxmaxcode+1) * sizeof(db->lens[0])); #endif if (!db->lens) { FREE(db, newlen); return NULL; } } db->totlen = newlen; db->hsize = hsize; db->hshift = hshift; db->maxmaxcode = maxmaxcode; db->maxbits = bits; return (void *) db; } static void bsd_free(state) void *state; { struct bsd_db *db = (struct bsd_db *) state; if (db->lens) FREE(db->lens, (db->maxmaxcode+1) * sizeof(db->lens[0])); FREE(db, db->totlen); } static void * bsd_comp_alloc(options, opt_len) u_char *options; int opt_len; { return bsd_alloc(options, opt_len, 0); } static void * bsd_decomp_alloc(options, opt_len) u_char *options; int opt_len; { return bsd_alloc(options, opt_len, 1); } /* * Initialize the database. */ static int bsd_init(db, options, opt_len, unit, hdrlen, mru, debug, decomp) struct bsd_db *db; u_char *options; int opt_len, unit, hdrlen, mru, debug, decomp; { int i; if (opt_len < CILEN_BSD_COMPRESS || options[0] != CI_BSD_COMPRESS || options[1] != CILEN_BSD_COMPRESS || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION || BSD_NBITS(options[2]) != db->maxbits || decomp && db->lens == NULL) return 0; if (decomp) { i = LAST+1; while (i != 0) db->lens[--i] = 1; } i = db->hsize; while (i != 0) { db->dict[--i].codem1 = BADCODEM1; db->dict[i].cptr = 0; } db->unit = unit; db->hdrlen = hdrlen; db->mru = mru; if (debug) db->debug = 1; bsd_reset(db); return 1; } static int bsd_comp_init(state, options, opt_len, unit, hdrlen, debug) void *state; u_char *options; int opt_len, unit, hdrlen, debug; { return bsd_init((struct bsd_db *) state, options, opt_len, unit, hdrlen, 0, debug, 0); } static int bsd_decomp_init(state, options, opt_len, unit, hdrlen, mru, debug) void *state; u_char *options; int opt_len, unit, hdrlen, mru, debug; { return bsd_init((struct bsd_db *) state, options, opt_len, unit, hdrlen, mru, debug, 1); } /* * compress a packet * One change from the BSD compress command is that when the * code size expands, we do not output a bunch of padding. * * N.B. at present, we ignore the hdrlen specified in the comp_init call. */ static int /* new slen */ bsd_compress(state, mretp, mp, slen, maxolen) void *state; mblk_t **mretp; /* return compressed mbuf chain here */ mblk_t *mp; /* from here */ int slen; /* uncompressed length */ int maxolen; /* max compressed length */ { struct bsd_db *db = (struct bsd_db *) state; int hshift = db->hshift; u_int max_ent = db->max_ent; u_int n_bits = db->n_bits; u_int bitno = 32; u_int32_t accm = 0, fcode; struct bsd_dict *dictp; u_char c; int hval, disp, ent, ilen; mblk_t *np, *mret; u_char *rptr, *wptr; u_char *cp_end; int olen; mblk_t *m, **mnp; #define PUTBYTE(v) { \ if (wptr) { \ *wptr++ = (v); \ if (wptr >= cp_end) { \ m->b_wptr = wptr; \ m = m->b_cont; \ if (m) { \ wptr = m->b_wptr; \ cp_end = m->b_datap->db_lim; \ } else \ wptr = NULL; \ } \ } \ ++olen; \ } #define OUTPUT(ent) { \ bitno -= n_bits; \ accm |= ((ent) << bitno); \ do { \ PUTBYTE(accm >> 24); \ accm <<= 8; \ bitno += 8; \ } while (bitno <= 24); \ } /* * First get the protocol and check that we're * interested in this packet. */ *mretp = NULL; rptr = mp->b_rptr; if (rptr + PPP_HDRLEN > mp->b_wptr) { if (!pullupmsg(mp, PPP_HDRLEN)) return 0; rptr = mp->b_rptr; } ent = PPP_PROTOCOL(rptr); /* get the protocol */ if (ent < 0x21 || ent > 0xf9) return 0; /* Don't generate compressed packets which are larger than the uncompressed packet. */ if (maxolen > slen) maxolen = slen; /* Allocate enough message blocks to give maxolen total space. */ mnp = &mret; for (olen = maxolen; olen > 0; ) { m = allocb((olen < 4096? olen: 4096), BPRI_MED); *mnp = m; if (m == NULL) { if (mret != NULL) { freemsg(mret); mnp = &mret; } break; } mnp = &m->b_cont; olen -= m->b_datap->db_lim - m->b_wptr; } *mnp = NULL; if ((m = mret) != NULL) { wptr = m->b_wptr; cp_end = m->b_datap->db_lim; } else wptr = cp_end = NULL; olen = 0; /* * Copy the PPP header over, changing the protocol, * and install the 2-byte sequence number. */ if (wptr) { wptr[0] = PPP_ADDRESS(rptr); wptr[1] = PPP_CONTROL(rptr); wptr[2] = 0; /* change the protocol */ wptr[3] = PPP_COMP; wptr[4] = db->seqno >> 8; wptr[5] = db->seqno; wptr += PPP_HDRLEN + BSD_OVHD; } ++db->seqno; rptr += PPP_HDRLEN; slen = mp->b_wptr - rptr; ilen = slen + 1; np = mp->b_cont; for (;;) { if (slen <= 0) { if (!np) break; rptr = np->b_rptr; slen = np->b_wptr - rptr; np = np->b_cont; if (!slen) continue; /* handle 0-length buffers */ ilen += slen; } slen--; c = *rptr++; fcode = BSD_KEY(ent, c); hval = BSD_HASH(ent, c, hshift); dictp = &db->dict[hval]; /* Validate and then check the entry. */ if (dictp->codem1 >= max_ent) goto nomatch; if (dictp->f.fcode == fcode) { ent = dictp->codem1+1; continue; /* found (prefix,suffix) */ } /* continue probing until a match or invalid entry */ disp = (hval == 0) ? 1 : hval; do { hval += disp; if (hval >= db->hsize) hval -= db->hsize; dictp = &db->dict[hval]; if (dictp->codem1 >= max_ent) goto nomatch; } while (dictp->f.fcode != fcode); ent = dictp->codem1 + 1; /* finally found (prefix,suffix) */ continue; nomatch: OUTPUT(ent); /* output the prefix */ /* code -> hashtable */ if (max_ent < db->maxmaxcode) { struct bsd_dict *dictp2; /* expand code size if needed */ if (max_ent >= MAXCODE(n_bits)) db->n_bits = ++n_bits; /* Invalidate old hash table entry using * this code, and then take it over. */ dictp2 = &db->dict[max_ent+1]; if (db->dict[dictp2->cptr].codem1 == max_ent) db->dict[dictp2->cptr].codem1 = BADCODEM1; dictp2->cptr = hval; dictp->codem1 = max_ent; dictp->f.fcode = fcode; db->max_ent = ++max_ent; } ent = c; } OUTPUT(ent); /* output the last code */ db->bytes_out += olen; db->in_count += ilen; if (bitno < 32) ++db->bytes_out; /* count complete bytes */ if (bsd_check(db)) OUTPUT(CLEAR); /* do not count the CLEAR */ /* * Pad dribble bits of last code with ones. * Do not emit a completely useless byte of ones. */ if (bitno != 32) PUTBYTE((accm | (0xff << (bitno-8))) >> 24); /* * Increase code size if we would have without the packet * boundary and as the decompressor will. */ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) db->n_bits++; db->uncomp_bytes += ilen; ++db->uncomp_count; if (olen + PPP_HDRLEN + BSD_OVHD > maxolen && mret != NULL) { /* throw away the compressed stuff if it is longer than uncompressed */ freemsg(mret); mret = NULL; ++db->incomp_count; db->incomp_bytes += ilen; } else if (wptr != NULL) { m->b_wptr = wptr; if (m->b_cont) { freemsg(m->b_cont); m->b_cont = NULL; } ++db->comp_count; db->comp_bytes += olen + BSD_OVHD; } *mretp = mret; return olen + PPP_HDRLEN + BSD_OVHD; #undef OUTPUT #undef PUTBYTE } /* * Update the "BSD Compress" dictionary on the receiver for * incompressible data by pretending to compress the incoming data. */ static void bsd_incomp(state, dmsg) void *state; mblk_t *dmsg; { struct bsd_db *db = (struct bsd_db *) state; u_int hshift = db->hshift; u_int max_ent = db->max_ent; u_int n_bits = db->n_bits; struct bsd_dict *dictp; u_int32_t fcode; u_char c; long hval, disp; int slen, ilen; u_int bitno = 7; u_char *rptr; u_int ent; rptr = dmsg->b_rptr; if (rptr + PPP_HDRLEN > dmsg->b_wptr) { if (!pullupmsg(dmsg, PPP_HDRLEN)) return; rptr = dmsg->b_rptr; } ent = PPP_PROTOCOL(rptr); /* get the protocol */ if (ent < 0x21 || ent > 0xf9) return; db->seqno++; ilen = 1; /* count the protocol as 1 byte */ rptr += PPP_HDRLEN; for (;;) { slen = dmsg->b_wptr - rptr; if (slen <= 0) { dmsg = dmsg->b_cont; if (!dmsg) break; rptr = dmsg->b_rptr; continue; /* skip zero-length buffers */ } ilen += slen; do { c = *rptr++; fcode = BSD_KEY(ent, c); hval = BSD_HASH(ent, c, hshift); dictp = &db->dict[hval]; /* validate and then check the entry */ if (dictp->codem1 >= max_ent) goto nomatch; if (dictp->f.fcode == fcode) { ent = dictp->codem1+1; continue; /* found (prefix,suffix) */ } /* continue probing until a match or invalid entry */ disp = (hval == 0) ? 1 : hval; do { hval += disp; if (hval >= db->hsize) hval -= db->hsize; dictp = &db->dict[hval]; if (dictp->codem1 >= max_ent) goto nomatch; } while (dictp->f.fcode != fcode); ent = dictp->codem1+1; continue; /* finally found (prefix,suffix) */ nomatch: /* output (count) the prefix */ bitno += n_bits; /* code -> hashtable */ if (max_ent < db->maxmaxcode) { struct bsd_dict *dictp2; /* expand code size if needed */ if (max_ent >= MAXCODE(n_bits)) db->n_bits = ++n_bits; /* Invalidate previous hash table entry * assigned this code, and then take it over. */ dictp2 = &db->dict[max_ent+1]; if (db->dict[dictp2->cptr].codem1 == max_ent) db->dict[dictp2->cptr].codem1 = BADCODEM1; dictp2->cptr = hval; dictp->codem1 = max_ent; dictp->f.fcode = fcode; db->max_ent = ++max_ent; db->lens[max_ent] = db->lens[ent]+1; } ent = c; } while (--slen != 0); } bitno += n_bits; /* output (count) the last code */ db->bytes_out += bitno/8; db->in_count += ilen; (void)bsd_check(db); ++db->incomp_count; db->incomp_bytes += ilen; ++db->uncomp_count; db->uncomp_bytes += ilen; /* Increase code size if we would have without the packet * boundary and as the decompressor will. */ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) db->n_bits++; } /* * Decompress "BSD Compress" * * Because of patent problems, we return DECOMP_ERROR for errors * found by inspecting the input data and for system problems, but * DECOMP_FATALERROR for any errors which could possibly be said to * be being detected "after" decompression. For DECOMP_ERROR, * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be * infringing a patent of Motorola's if we do, so we take CCP down * instead. * * Given that the frame has the correct sequence number and a good FCS, * errors such as invalid codes in the input most likely indicate a * bug, so we return DECOMP_FATALERROR for them in order to turn off * compression, even though they are detected by inspecting the input. */ static int bsd_decompress(state, cmsg, dmpp) void *state; mblk_t *cmsg, **dmpp; { struct bsd_db *db = (struct bsd_db *) state; u_int max_ent = db->max_ent; u_int32_t accm = 0; u_int bitno = 32; /* 1st valid bit in accm */ u_int n_bits = db->n_bits; u_int tgtbitno = 32-n_bits; /* bitno when we have a code */ struct bsd_dict *dictp; int explen, i, seq, len; u_int incode, oldcode, finchar; u_char *p, *rptr, *wptr; mblk_t *dmsg, *mret; int adrs, ctrl, ilen; int dlen, space, codelen, extra; /* * Get at least the BSD Compress header in the first buffer */ rptr = cmsg->b_rptr; if (rptr + PPP_HDRLEN + BSD_OVHD >= cmsg->b_wptr) { if (!pullupmsg(cmsg, PPP_HDRLEN + BSD_OVHD + 1)) { if (db->debug) printf("bsd_decomp%d: failed to pullup\n", db->unit); return DECOMP_ERROR; } rptr = cmsg->b_rptr; } /* * Save the address/control from the PPP header * and then get the sequence number. */ adrs = PPP_ADDRESS(rptr); ctrl = PPP_CONTROL(rptr); rptr += PPP_HDRLEN; seq = (rptr[0] << 8) + rptr[1]; rptr += BSD_OVHD; ilen = len = cmsg->b_wptr - rptr; /* * Check the sequence number and give up if it is not what we expect. */ if (seq != db->seqno++) { if (db->debug) printf("bsd_decomp%d: bad sequence # %d, expected %d\n", db->unit, seq, db->seqno - 1); return DECOMP_ERROR; } /* * Allocate one message block to start with. */ if ((dmsg = allocb(DECOMP_CHUNK + db->hdrlen, BPRI_MED)) == NULL) return DECOMP_ERROR; mret = dmsg; dmsg->b_wptr += db->hdrlen; dmsg->b_rptr = wptr = dmsg->b_wptr; /* Fill in the ppp header, but not the last byte of the protocol (that comes from the decompressed data). */ wptr[0] = adrs; wptr[1] = ctrl; wptr[2] = 0; wptr += PPP_HDRLEN - 1; space = dmsg->b_datap->db_lim - wptr; oldcode = CLEAR; explen = 0; for (;;) { if (len == 0) { cmsg = cmsg->b_cont; if (!cmsg) /* quit at end of message */ break; rptr = cmsg->b_rptr; len = cmsg->b_wptr - rptr; ilen += len; continue; /* handle 0-length buffers */ } /* * Accumulate bytes until we have a complete code. * Then get the next code, relying on the 32-bit, * unsigned accm to mask the result. */ bitno -= 8; accm |= *rptr++ << bitno; --len; if (tgtbitno < bitno) continue; incode = accm >> tgtbitno; accm <<= n_bits; bitno += n_bits; if (incode == CLEAR) { /* * The dictionary must only be cleared at * the end of a packet. But there could be an * empty message block at the end. */ if (len > 0 || cmsg->b_cont != 0) { if (cmsg->b_cont) len += msgdsize(cmsg->b_cont); if (len > 0) { freemsg(dmsg); if (db->debug) printf("bsd_decomp%d: bad CLEAR\n", db->unit); return DECOMP_FATALERROR; } } bsd_clear(db); explen = ilen = 0; break; } if (incode > max_ent + 2 || incode > db->maxmaxcode || incode > max_ent && oldcode == CLEAR) { freemsg(dmsg); if (db->debug) { printf("bsd_decomp%d: bad code 0x%x oldcode=0x%x ", db->unit, incode, oldcode); printf("max_ent=0x%x dlen=%d seqno=%d\n", max_ent, dlen, db->seqno); } return DECOMP_FATALERROR; /* probably a bug */ } /* Special case for KwKwK string. */ if (incode > max_ent) { finchar = oldcode; extra = 1; } else { finchar = incode; extra = 0; } codelen = db->lens[finchar]; explen += codelen + extra; if (explen > db->mru + 1) { freemsg(dmsg); if (db->debug) printf("bsd_decomp%d: ran out of mru\n", db->unit); return DECOMP_FATALERROR; } /* * Decode this code and install it in the decompressed buffer. */ space -= codelen + extra; if (space < 0) { /* Allocate another message block. */ dmsg->b_wptr = wptr; dlen = codelen + extra; if (dlen < DECOMP_CHUNK) dlen = DECOMP_CHUNK; if ((dmsg->b_cont = allocb(dlen, BPRI_MED)) == NULL) { freemsg(dmsg); return DECOMP_ERROR; } dmsg = dmsg->b_cont; wptr = dmsg->b_wptr; space = dmsg->b_datap->db_lim - wptr - codelen - extra; } p = (wptr += codelen); while (finchar > LAST) { dictp = &db->dict[db->dict[finchar].cptr]; #ifdef DEBUG --codelen; if (codelen <= 0) { freemsg(dmsg); printf("bsd_decomp%d: fell off end of chain ", db->unit); printf("0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, db->dict[finchar].cptr, max_ent); return DECOMP_FATALERROR; } if (dictp->codem1 != finchar-1) { freemsg(dmsg); printf("bsd_decomp%d: bad code chain 0x%x finchar=0x%x ", db->unit, incode, finchar); printf("oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, db->dict[finchar].cptr, dictp->codem1); return DECOMP_FATALERROR; } #endif *--p = dictp->f.hs.suffix; finchar = dictp->f.hs.prefix; } *--p = finchar; #ifdef DEBUG if (--codelen != 0) printf("bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent); #endif if (extra) /* the KwKwK case again */ *wptr++ = finchar; /* * If not first code in a packet, and * if not out of code space, then allocate a new code. * * Keep the hash table correct so it can be used * with uncompressed packets. */ if (oldcode != CLEAR && max_ent < db->maxmaxcode) { struct bsd_dict *dictp2; u_int32_t fcode; int hval, disp; fcode = BSD_KEY(oldcode,finchar); hval = BSD_HASH(oldcode,finchar,db->hshift); dictp = &db->dict[hval]; /* look for a free hash table entry */ if (dictp->codem1 < max_ent) { disp = (hval == 0) ? 1 : hval; do { hval += disp; if (hval >= db->hsize) hval -= db->hsize; dictp = &db->dict[hval]; } while (dictp->codem1 < max_ent); } /* * Invalidate previous hash table entry * assigned this code, and then take it over */ dictp2 = &db->dict[max_ent+1]; if (db->dict[dictp2->cptr].codem1 == max_ent) { db->dict[dictp2->cptr].codem1 = BADCODEM1; } dictp2->cptr = hval; dictp->codem1 = max_ent; dictp->f.fcode = fcode; db->max_ent = ++max_ent; db->lens[max_ent] = db->lens[oldcode]+1; /* Expand code size if needed. */ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) { db->n_bits = ++n_bits; tgtbitno = 32-n_bits; } } oldcode = incode; } dmsg->b_wptr = wptr; /* * Keep the checkpoint right so that incompressible packets * clear the dictionary at the right times. */ db->bytes_out += ilen; db->in_count += explen; if (bsd_check(db) && db->debug) { printf("bsd_decomp%d: peer should have cleared dictionary\n", db->unit); } ++db->comp_count; db->comp_bytes += ilen + BSD_OVHD; ++db->uncomp_count; db->uncomp_bytes += explen; *dmpp = mret; return DECOMP_OK; } #endif /* DO_BSD_COMPRESS */ ppp-2.4.5/modules/deflate.c000066400000000000000000000473141130035057700155530ustar00rootroot00000000000000/* * ppp_deflate.c - interface the zlib procedures for Deflate compression * and decompression (as used by gzip) to the PPP code. * This version is for use with STREAMS under SunOS 4.x, Solaris 2, * SVR4, OSF/1 and AIX 4.x. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: deflate.c,v 1.12 2004/01/17 05:47:55 carlsonj Exp $ */ #ifdef AIX4 #include #endif #include #include #include #include #include "ppp_mod.h" #define PACKETPTR mblk_t * #include #ifdef __osf__ #include "zlib.h" #else #include "../common/zlib.h" #endif #ifdef SOL2 #include #endif #if DO_DEFLATE #define DEFLATE_DEBUG 1 /* * State for a Deflate (de)compressor. */ struct deflate_state { int seqno; int w_size; int unit; int hdrlen; int mru; int debug; z_stream strm; struct compstat stats; }; #define DEFLATE_OVHD 2 /* Deflate overhead/packet */ static void *z_alloc __P((void *, u_int items, u_int size)); static void *z_alloc_init __P((void *, u_int items, u_int size)); static void z_free __P((void *, void *ptr)); static void *z_comp_alloc __P((u_char *options, int opt_len)); static void *z_decomp_alloc __P((u_char *options, int opt_len)); static void z_comp_free __P((void *state)); static void z_decomp_free __P((void *state)); static int z_comp_init __P((void *state, u_char *options, int opt_len, int unit, int hdrlen, int debug)); static int z_decomp_init __P((void *state, u_char *options, int opt_len, int unit, int hdrlen, int mru, int debug)); static int z_compress __P((void *state, mblk_t **mret, mblk_t *mp, int slen, int maxolen)); static void z_incomp __P((void *state, mblk_t *dmsg)); static int z_decompress __P((void *state, mblk_t *cmp, mblk_t **dmpp)); static void z_comp_reset __P((void *state)); static void z_decomp_reset __P((void *state)); static void z_comp_stats __P((void *state, struct compstat *stats)); /* * Procedures exported to ppp_comp.c. */ struct compressor ppp_deflate = { CI_DEFLATE, /* compress_proto */ z_comp_alloc, /* comp_alloc */ z_comp_free, /* comp_free */ z_comp_init, /* comp_init */ z_comp_reset, /* comp_reset */ z_compress, /* compress */ z_comp_stats, /* comp_stat */ z_decomp_alloc, /* decomp_alloc */ z_decomp_free, /* decomp_free */ z_decomp_init, /* decomp_init */ z_decomp_reset, /* decomp_reset */ z_decompress, /* decompress */ z_incomp, /* incomp */ z_comp_stats, /* decomp_stat */ }; struct compressor ppp_deflate_draft = { CI_DEFLATE_DRAFT, /* compress_proto */ z_comp_alloc, /* comp_alloc */ z_comp_free, /* comp_free */ z_comp_init, /* comp_init */ z_comp_reset, /* comp_reset */ z_compress, /* compress */ z_comp_stats, /* comp_stat */ z_decomp_alloc, /* decomp_alloc */ z_decomp_free, /* decomp_free */ z_decomp_init, /* decomp_init */ z_decomp_reset, /* decomp_reset */ z_decompress, /* decompress */ z_incomp, /* incomp */ z_comp_stats, /* decomp_stat */ }; #define DECOMP_CHUNK 512 /* * Space allocation and freeing routines for use by zlib routines. */ struct zchunk { u_int size; u_int guard; }; #define GUARD_MAGIC 0x77a6011a static void * z_alloc_init(notused, items, size) void *notused; u_int items, size; { struct zchunk *z; size = items * size + sizeof(struct zchunk); #ifdef __osf__ z = (struct zchunk *) ALLOC_SLEEP(size); #else z = (struct zchunk *) ALLOC_NOSLEEP(size); #endif z->size = size; z->guard = GUARD_MAGIC; return (void *) (z + 1); } static void * z_alloc(notused, items, size) void *notused; u_int items, size; { struct zchunk *z; size = items * size + sizeof(struct zchunk); z = (struct zchunk *) ALLOC_NOSLEEP(size); z->size = size; z->guard = GUARD_MAGIC; return (void *) (z + 1); } static void z_free(notused, ptr) void *notused; void *ptr; { struct zchunk *z = ((struct zchunk *) ptr) - 1; if (z->guard != GUARD_MAGIC) { printf("ppp: z_free of corrupted chunk at %x (%x, %x)\n", z, z->size, z->guard); return; } FREE(z, z->size); } /* * Allocate space for a compressor. */ static void * z_comp_alloc(options, opt_len) u_char *options; int opt_len; { struct deflate_state *state; int w_size; if (opt_len != CILEN_DEFLATE || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || options[1] != CILEN_DEFLATE || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || options[3] != DEFLATE_CHK_SEQUENCE) return NULL; w_size = DEFLATE_SIZE(options[2]); /* * N.B. the 9 below should be DEFLATE_MIN_SIZE (8), but using * 8 will cause kernel crashes because of a bug in zlib. */ if (w_size < 9 || w_size > DEFLATE_MAX_SIZE) return NULL; #ifdef __osf__ state = (struct deflate_state *) ALLOC_SLEEP(sizeof(*state)); #else state = (struct deflate_state *) ALLOC_NOSLEEP(sizeof(*state)); #endif if (state == NULL) return NULL; state->strm.next_in = NULL; state->strm.zalloc = (alloc_func) z_alloc_init; state->strm.zfree = (free_func) z_free; if (deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION, DEFLATE_METHOD_VAL, -w_size, 8, Z_DEFAULT_STRATEGY) != Z_OK) { FREE(state, sizeof(*state)); return NULL; } state->strm.zalloc = (alloc_func) z_alloc; state->w_size = w_size; bzero(&state->stats, sizeof(state->stats)); return (void *) state; } static void z_comp_free(arg) void *arg; { struct deflate_state *state = (struct deflate_state *) arg; deflateEnd(&state->strm); FREE(state, sizeof(*state)); } static int z_comp_init(arg, options, opt_len, unit, hdrlen, debug) void *arg; u_char *options; int opt_len, unit, hdrlen, debug; { struct deflate_state *state = (struct deflate_state *) arg; if (opt_len < CILEN_DEFLATE || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || options[1] != CILEN_DEFLATE || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || DEFLATE_SIZE(options[2]) != state->w_size || options[3] != DEFLATE_CHK_SEQUENCE) return 0; state->seqno = 0; state->unit = unit; state->hdrlen = hdrlen; state->debug = debug; deflateReset(&state->strm); return 1; } static void z_comp_reset(arg) void *arg; { struct deflate_state *state = (struct deflate_state *) arg; state->seqno = 0; deflateReset(&state->strm); } static int z_compress(arg, mret, mp, orig_len, maxolen) void *arg; mblk_t **mret; /* compressed packet (out) */ mblk_t *mp; /* uncompressed packet (in) */ int orig_len, maxolen; { struct deflate_state *state = (struct deflate_state *) arg; u_char *rptr, *wptr; int proto, olen, wspace, r, flush; mblk_t *m; /* * Check that the protocol is in the range we handle. */ *mret = NULL; rptr = mp->b_rptr; if (rptr + PPP_HDRLEN > mp->b_wptr) { if (!pullupmsg(mp, PPP_HDRLEN)) return 0; rptr = mp->b_rptr; } proto = PPP_PROTOCOL(rptr); if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) return orig_len; /* Allocate one mblk initially. */ if (maxolen > orig_len) maxolen = orig_len; if (maxolen <= PPP_HDRLEN + 2) { wspace = 0; m = NULL; } else { wspace = maxolen + state->hdrlen; if (wspace > 4096) wspace = 4096; m = allocb(wspace, BPRI_MED); } if (m != NULL) { *mret = m; if (state->hdrlen + PPP_HDRLEN + 2 < wspace) { m->b_rptr += state->hdrlen; m->b_wptr = m->b_rptr; wspace -= state->hdrlen; } wptr = m->b_wptr; /* * Copy over the PPP header and store the 2-byte sequence number. */ wptr[0] = PPP_ADDRESS(rptr); wptr[1] = PPP_CONTROL(rptr); wptr[2] = PPP_COMP >> 8; wptr[3] = PPP_COMP; wptr += PPP_HDRLEN; wptr[0] = state->seqno >> 8; wptr[1] = state->seqno; wptr += 2; state->strm.next_out = wptr; state->strm.avail_out = wspace - (PPP_HDRLEN + 2); } else { state->strm.next_out = NULL; state->strm.avail_out = 1000000; } ++state->seqno; rptr += (proto > 0xff)? 2: 3; /* skip 1st proto byte if 0 */ state->strm.next_in = rptr; state->strm.avail_in = mp->b_wptr - rptr; mp = mp->b_cont; flush = (mp == NULL)? Z_PACKET_FLUSH: Z_NO_FLUSH; olen = 0; for (;;) { r = deflate(&state->strm, flush); if (r != Z_OK) { printf("z_compress: deflate returned %d (%s)\n", r, (state->strm.msg? state->strm.msg: "")); break; } if (flush != Z_NO_FLUSH && state->strm.avail_out != 0) break; /* all done */ if (state->strm.avail_in == 0 && mp != NULL) { state->strm.next_in = mp->b_rptr; state->strm.avail_in = mp->b_wptr - mp->b_rptr; mp = mp->b_cont; if (mp == NULL) flush = Z_PACKET_FLUSH; } if (state->strm.avail_out == 0) { if (m != NULL) { m->b_wptr += wspace; olen += wspace; wspace = maxolen - olen; if (wspace <= 0) { wspace = 0; m->b_cont = NULL; } else { if (wspace < 32) wspace = 32; else if (wspace > 4096) wspace = 4096; m->b_cont = allocb(wspace, BPRI_MED); } m = m->b_cont; if (m != NULL) { state->strm.next_out = m->b_wptr; state->strm.avail_out = wspace; } } if (m == NULL) { state->strm.next_out = NULL; state->strm.avail_out = 1000000; } } } if (m != NULL) { m->b_wptr += wspace - state->strm.avail_out; olen += wspace - state->strm.avail_out; } /* * See if we managed to reduce the size of the packet. */ if (olen < orig_len && m != NULL) { state->stats.comp_bytes += olen; state->stats.comp_packets++; } else { if (*mret != NULL) { freemsg(*mret); *mret = NULL; } state->stats.inc_bytes += orig_len; state->stats.inc_packets++; olen = orig_len; } state->stats.unc_bytes += orig_len; state->stats.unc_packets++; return olen; } static void z_comp_stats(arg, stats) void *arg; struct compstat *stats; { struct deflate_state *state = (struct deflate_state *) arg; u_int out; *stats = state->stats; stats->ratio = stats->unc_bytes; out = stats->comp_bytes + stats->unc_bytes; if (stats->ratio <= 0x7ffffff) stats->ratio <<= 8; else out >>= 8; if (out != 0) stats->ratio /= out; } /* * Allocate space for a decompressor. */ static void * z_decomp_alloc(options, opt_len) u_char *options; int opt_len; { struct deflate_state *state; int w_size; if (opt_len != CILEN_DEFLATE || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || options[1] != CILEN_DEFLATE || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || options[3] != DEFLATE_CHK_SEQUENCE) return NULL; w_size = DEFLATE_SIZE(options[2]); /* * N.B. the 9 below should be DEFLATE_MIN_SIZE (8), but using * 8 will cause kernel crashes because of a bug in zlib. */ if (w_size < 9 || w_size > DEFLATE_MAX_SIZE) return NULL; #ifdef __osf__ state = (struct deflate_state *) ALLOC_SLEEP(sizeof(*state)); #else state = (struct deflate_state *) ALLOC_NOSLEEP(sizeof(*state)); #endif if (state == NULL) return NULL; state->strm.next_out = NULL; state->strm.zalloc = (alloc_func) z_alloc_init; state->strm.zfree = (free_func) z_free; if (inflateInit2(&state->strm, -w_size) != Z_OK) { FREE(state, sizeof(*state)); return NULL; } state->strm.zalloc = (alloc_func) z_alloc; state->w_size = w_size; bzero(&state->stats, sizeof(state->stats)); return (void *) state; } static void z_decomp_free(arg) void *arg; { struct deflate_state *state = (struct deflate_state *) arg; inflateEnd(&state->strm); FREE(state, sizeof(*state)); } static int z_decomp_init(arg, options, opt_len, unit, hdrlen, mru, debug) void *arg; u_char *options; int opt_len, unit, hdrlen, mru, debug; { struct deflate_state *state = (struct deflate_state *) arg; if (opt_len < CILEN_DEFLATE || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || options[1] != CILEN_DEFLATE || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || DEFLATE_SIZE(options[2]) != state->w_size || options[3] != DEFLATE_CHK_SEQUENCE) return 0; state->seqno = 0; state->unit = unit; state->hdrlen = hdrlen; state->debug = debug; state->mru = mru; inflateReset(&state->strm); return 1; } static void z_decomp_reset(arg) void *arg; { struct deflate_state *state = (struct deflate_state *) arg; state->seqno = 0; inflateReset(&state->strm); } /* * Decompress a Deflate-compressed packet. * * Because of patent problems, we return DECOMP_ERROR for errors * found by inspecting the input data and for system problems, but * DECOMP_FATALERROR for any errors which could possibly be said to * be being detected "after" decompression. For DECOMP_ERROR, * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be * infringing a patent of Motorola's if we do, so we take CCP down * instead. * * Given that the frame has the correct sequence number and a good FCS, * errors such as invalid codes in the input most likely indicate a * bug, so we return DECOMP_FATALERROR for them in order to turn off * compression, even though they are detected by inspecting the input. */ static int z_decompress(arg, mi, mop) void *arg; mblk_t *mi, **mop; { struct deflate_state *state = (struct deflate_state *) arg; mblk_t *mo, *mo_head; u_char *rptr, *wptr; int rlen, olen, ospace; int seq, i, flush, r, decode_proto; u_char hdr[PPP_HDRLEN + DEFLATE_OVHD]; *mop = NULL; rptr = mi->b_rptr; for (i = 0; i < PPP_HDRLEN + DEFLATE_OVHD; ++i) { while (rptr >= mi->b_wptr) { mi = mi->b_cont; if (mi == NULL) return DECOMP_ERROR; rptr = mi->b_rptr; } hdr[i] = *rptr++; } /* Check the sequence number. */ seq = (hdr[PPP_HDRLEN] << 8) + hdr[PPP_HDRLEN+1]; if (seq != state->seqno) { #if !DEFLATE_DEBUG if (state->debug) #endif printf("z_decompress%d: bad seq # %d, expected %d\n", state->unit, seq, state->seqno); return DECOMP_ERROR; } ++state->seqno; /* Allocate an output message block. */ mo = allocb(DECOMP_CHUNK + state->hdrlen, BPRI_MED); if (mo == NULL) return DECOMP_ERROR; mo_head = mo; mo->b_cont = NULL; mo->b_rptr += state->hdrlen; mo->b_wptr = wptr = mo->b_rptr; ospace = DECOMP_CHUNK; olen = 0; /* * Fill in the first part of the PPP header. The protocol field * comes from the decompressed data. */ wptr[0] = PPP_ADDRESS(hdr); wptr[1] = PPP_CONTROL(hdr); wptr[2] = 0; /* * Set up to call inflate. We set avail_out to 1 initially so we can * look at the first byte of the output and decide whether we have * a 1-byte or 2-byte protocol field. */ state->strm.next_in = rptr; state->strm.avail_in = mi->b_wptr - rptr; mi = mi->b_cont; flush = (mi == NULL)? Z_PACKET_FLUSH: Z_NO_FLUSH; rlen = state->strm.avail_in + PPP_HDRLEN + DEFLATE_OVHD; state->strm.next_out = wptr + 3; state->strm.avail_out = 1; decode_proto = 1; /* * Call inflate, supplying more input or output as needed. */ for (;;) { r = inflate(&state->strm, flush); if (r != Z_OK) { #if !DEFLATE_DEBUG if (state->debug) #endif printf("z_decompress%d: inflate returned %d (%s)\n", state->unit, r, (state->strm.msg? state->strm.msg: "")); freemsg(mo_head); return DECOMP_FATALERROR; } if (flush != Z_NO_FLUSH && state->strm.avail_out != 0) break; /* all done */ if (state->strm.avail_in == 0 && mi != NULL) { state->strm.next_in = mi->b_rptr; state->strm.avail_in = mi->b_wptr - mi->b_rptr; rlen += state->strm.avail_in; mi = mi->b_cont; if (mi == NULL) flush = Z_PACKET_FLUSH; } if (state->strm.avail_out == 0) { if (decode_proto) { state->strm.avail_out = ospace - PPP_HDRLEN; if ((wptr[3] & 1) == 0) { /* 2-byte protocol field */ wptr[2] = wptr[3]; --state->strm.next_out; ++state->strm.avail_out; } decode_proto = 0; } else { mo->b_wptr += ospace; olen += ospace; mo->b_cont = allocb(DECOMP_CHUNK, BPRI_MED); mo = mo->b_cont; if (mo == NULL) { freemsg(mo_head); return DECOMP_ERROR; } state->strm.next_out = mo->b_rptr; state->strm.avail_out = ospace = DECOMP_CHUNK; } } } if (decode_proto) { freemsg(mo_head); return DECOMP_ERROR; } mo->b_wptr += ospace - state->strm.avail_out; olen += ospace - state->strm.avail_out; #if DEFLATE_DEBUG if (olen > state->mru + PPP_HDRLEN) printf("ppp_deflate%d: exceeded mru (%d > %d)\n", state->unit, olen, state->mru + PPP_HDRLEN); #endif state->stats.unc_bytes += olen; state->stats.unc_packets++; state->stats.comp_bytes += rlen; state->stats.comp_packets++; *mop = mo_head; return DECOMP_OK; } /* * Incompressible data has arrived - add it to the history. */ static void z_incomp(arg, mi) void *arg; mblk_t *mi; { struct deflate_state *state = (struct deflate_state *) arg; u_char *rptr; int rlen, proto, r; /* * Check that the protocol is one we handle. */ rptr = mi->b_rptr; if (rptr + PPP_HDRLEN > mi->b_wptr) { if (!pullupmsg(mi, PPP_HDRLEN)) return; rptr = mi->b_rptr; } proto = PPP_PROTOCOL(rptr); if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) return; ++state->seqno; /* * Iterate through the message blocks, adding the characters in them * to the decompressor's history. For the first block, we start * at the either the 1st or 2nd byte of the protocol field, * depending on whether the protocol value is compressible. */ rlen = mi->b_wptr - mi->b_rptr; state->strm.next_in = rptr + 3; state->strm.avail_in = rlen - 3; if (proto > 0xff) { --state->strm.next_in; ++state->strm.avail_in; } for (;;) { r = inflateIncomp(&state->strm); if (r != Z_OK) { /* gak! */ #if !DEFLATE_DEBUG if (state->debug) #endif printf("z_incomp%d: inflateIncomp returned %d (%s)\n", state->unit, r, (state->strm.msg? state->strm.msg: "")); return; } mi = mi->b_cont; if (mi == NULL) break; state->strm.next_in = mi->b_rptr; state->strm.avail_in = mi->b_wptr - mi->b_rptr; rlen += state->strm.avail_in; } /* * Update stats. */ state->stats.inc_bytes += rlen; state->stats.inc_packets++; state->stats.unc_bytes += rlen; state->stats.unc_packets++; } #endif /* DO_DEFLATE */ ppp-2.4.5/modules/if_ppp.c000066400000000000000000000460371130035057700154250ustar00rootroot00000000000000/* * if_ppp.c - a network interface connected to a STREAMS module. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: if_ppp.c,v 1.18 2002/12/06 09:49:15 paulus Exp $ */ /* * This file is used under SunOS 4 and Digital UNIX. * * This file provides the glue between PPP and IP. */ #define INET 1 #include #include #include #include #include #include #include #include #include #include #include #ifdef __osf__ #include #include #else #include #endif #include "ppp_mod.h" #include #ifdef SNIT_SUPPORT #include #include #include #endif #ifdef __osf__ #define SIOCSIFMTU SIOCSIPMTU #define SIOCGIFMTU SIOCRIPMTU #define IFA_ADDR(ifa) (*(ifa)->ifa_addr) #else #define IFA_ADDR(ifa) ((ifa)->ifa_addr) #endif #define ifr_mtu ifr_metric static int if_ppp_open __P((queue_t *, int, int, int)); static int if_ppp_close __P((queue_t *, int)); static int if_ppp_wput __P((queue_t *, mblk_t *)); static int if_ppp_rput __P((queue_t *, mblk_t *)); #define PPP_IF_ID 0x8021 static struct module_info minfo = { PPP_IF_ID, "if_ppp", 0, INFPSZ, 4096, 128 }; static struct qinit rinit = { if_ppp_rput, NULL, if_ppp_open, if_ppp_close, NULL, &minfo, NULL }; static struct qinit winit = { if_ppp_wput, NULL, NULL, NULL, NULL, &minfo, NULL }; struct streamtab if_pppinfo = { &rinit, &winit, NULL, NULL }; typedef struct if_ppp_state { int unit; queue_t *q; int flags; } if_ppp_t; /* Values for flags */ #define DBGLOG 1 static int if_ppp_count; /* Number of currently-active streams */ static int ppp_nalloc; /* Number of elements of ifs and states */ static struct ifnet **ifs; /* Array of pointers to interface structs */ static if_ppp_t **states; /* Array of pointers to state structs */ static int if_ppp_output __P((struct ifnet *, struct mbuf *, struct sockaddr *)); static int if_ppp_ioctl __P((struct ifnet *, u_int, caddr_t)); static struct mbuf *make_mbufs __P((mblk_t *, int)); static mblk_t *make_message __P((struct mbuf *, int)); #ifdef SNIT_SUPPORT /* Fake ether header for SNIT */ static struct ether_header snit_ehdr = {{0}, {0}, ETHERTYPE_IP}; #endif #ifndef __osf__ static void ppp_if_detach __P((struct ifnet *)); /* * Detach all the interfaces before unloading. * Not sure this works. */ int if_ppp_unload() { int i; if (if_ppp_count > 0) return EBUSY; for (i = 0; i < ppp_nalloc; ++i) if (ifs[i] != 0) ppp_if_detach(ifs[i]); if (ifs) { FREE(ifs, ppp_nalloc * sizeof (struct ifnet *)); FREE(states, ppp_nalloc * sizeof (struct if_ppp_t *)); } ppp_nalloc = 0; return 0; } #endif /* __osf__ */ /* * STREAMS module entry points. */ static int if_ppp_open(q, dev, flag, sflag) queue_t *q; int dev; int flag, sflag; { if_ppp_t *sp; if (q->q_ptr == 0) { sp = (if_ppp_t *) ALLOC_SLEEP(sizeof (if_ppp_t)); if (sp == 0) return OPENFAIL; bzero(sp, sizeof (if_ppp_t)); q->q_ptr = (caddr_t) sp; WR(q)->q_ptr = (caddr_t) sp; sp->unit = -1; /* no interface unit attached at present */ sp->q = WR(q); sp->flags = 0; ++if_ppp_count; } return 0; } static int if_ppp_close(q, flag) queue_t *q; int flag; { if_ppp_t *sp; struct ifnet *ifp; sp = (if_ppp_t *) q->q_ptr; if (sp != 0) { if (sp->flags & DBGLOG) printf("if_ppp closed, q=%x sp=%x\n", q, sp); if (sp->unit >= 0) { if (sp->unit < ppp_nalloc) { states[sp->unit] = 0; ifp = ifs[sp->unit]; if (ifp != 0) ifp->if_flags &= ~(IFF_UP | IFF_RUNNING); #ifdef DEBUG } else { printf("if_ppp: unit %d nonexistent!\n", sp->unit); #endif } } FREE(sp, sizeof (if_ppp_t)); --if_ppp_count; } return 0; } static int if_ppp_wput(q, mp) queue_t *q; mblk_t *mp; { if_ppp_t *sp; struct iocblk *iop; int error, unit; struct ifnet *ifp; sp = (if_ppp_t *) q->q_ptr; switch (mp->b_datap->db_type) { case M_DATA: /* * Now why would we be getting data coming in here?? */ if (sp->flags & DBGLOG) printf("if_ppp: got M_DATA len=%d\n", msgdsize(mp)); freemsg(mp); break; case M_IOCTL: iop = (struct iocblk *) mp->b_rptr; error = EINVAL; if (sp->flags & DBGLOG) printf("if_ppp: got ioctl cmd=%x count=%d\n", iop->ioc_cmd, iop->ioc_count); switch (iop->ioc_cmd) { case PPPIO_NEWPPA: /* well almost */ if (iop->ioc_count != sizeof(int) || sp->unit >= 0) break; if ((error = NOTSUSER()) != 0) break; unit = *(int *)mp->b_cont->b_rptr; /* Check that this unit isn't already in use */ if (unit < ppp_nalloc && states[unit] != 0) { error = EADDRINUSE; break; } /* Extend ifs and states arrays if necessary. */ error = ENOSR; if (unit >= ppp_nalloc) { int newn; struct ifnet **newifs; if_ppp_t **newstates; newn = unit + 4; if (sp->flags & DBGLOG) printf("if_ppp: extending ifs to %d\n", newn); newifs = (struct ifnet **) ALLOC_NOSLEEP(newn * sizeof (struct ifnet *)); if (newifs == 0) break; bzero(newifs, newn * sizeof (struct ifnet *)); newstates = (if_ppp_t **) ALLOC_NOSLEEP(newn * sizeof (struct if_ppp_t *)); if (newstates == 0) { FREE(newifs, newn * sizeof (struct ifnet *)); break; } bzero(newstates, newn * sizeof (struct if_ppp_t *)); bcopy(ifs, newifs, ppp_nalloc * sizeof(struct ifnet *)); bcopy(states, newstates, ppp_nalloc * sizeof(if_ppp_t *)); if (ifs) { FREE(ifs, ppp_nalloc * sizeof(struct ifnet *)); FREE(states, ppp_nalloc * sizeof(if_ppp_t *)); } ifs = newifs; states = newstates; ppp_nalloc = newn; } /* Allocate a new ifnet struct if necessary. */ ifp = ifs[unit]; if (ifp == 0) { ifp = (struct ifnet *) ALLOC_NOSLEEP(sizeof (struct ifnet)); if (ifp == 0) break; bzero(ifp, sizeof (struct ifnet)); ifs[unit] = ifp; ifp->if_name = "ppp"; ifp->if_unit = unit; ifp->if_mtu = PPP_MTU; ifp->if_flags = IFF_POINTOPOINT | IFF_RUNNING; #ifndef __osf__ #ifdef IFF_MULTICAST ifp->if_flags |= IFF_MULTICAST; #endif #endif /* __osf__ */ ifp->if_output = if_ppp_output; #ifdef __osf__ ifp->if_version = "Point-to-Point Protocol, version 2.3.11"; ifp->if_mediamtu = PPP_MTU; ifp->if_type = IFT_PPP; ifp->if_hdrlen = PPP_HDRLEN; ifp->if_addrlen = 0; ifp->if_flags |= IFF_NOARP | IFF_SIMPLEX | IFF_NOTRAILERS; #ifdef IFF_VAR_MTU ifp->if_flags |= IFF_VAR_MTU; #endif #ifdef NETMASTERCPU ifp->if_affinity = NETMASTERCPU; #endif #endif ifp->if_ioctl = if_ppp_ioctl; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; if_attach(ifp); if (sp->flags & DBGLOG) printf("if_ppp: created unit %d\n", unit); } else { ifp->if_mtu = PPP_MTU; ifp->if_flags |= IFF_RUNNING; } states[unit] = sp; sp->unit = unit; error = 0; iop->ioc_count = 0; if (sp->flags & DBGLOG) printf("if_ppp: attached unit %d, sp=%x q=%x\n", unit, sp, sp->q); break; case PPPIO_DEBUG: error = -1; if (iop->ioc_count == sizeof(int)) { if (*(int *)mp->b_cont->b_rptr == PPPDBG_LOG + PPPDBG_IF) { printf("if_ppp: debug log enabled, q=%x sp=%x\n", q, sp); sp->flags |= DBGLOG; error = 0; iop->ioc_count = 0; } } break; default: error = -1; break; } if (sp->flags & DBGLOG) printf("if_ppp: ioctl result %d\n", error); if (error < 0) putnext(q, mp); else if (error == 0) { mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } else { mp->b_datap->db_type = M_IOCNAK; iop->ioc_count = 0; iop->ioc_error = error; qreply(q, mp); } break; default: putnext(q, mp); } return 0; } static int if_ppp_rput(q, mp) queue_t *q; mblk_t *mp; { if_ppp_t *sp; int proto, s; struct mbuf *mb; struct ifqueue *inq; struct ifnet *ifp; int len; sp = (if_ppp_t *) q->q_ptr; switch (mp->b_datap->db_type) { case M_DATA: /* * Convert the message into an mbuf chain * and inject it into the network code. */ if (sp->flags & DBGLOG) printf("if_ppp: rput pkt len %d data %x %x %x %x %x %x %x %x\n", msgdsize(mp), mp->b_rptr[0], mp->b_rptr[1], mp->b_rptr[2], mp->b_rptr[3], mp->b_rptr[4], mp->b_rptr[5], mp->b_rptr[6], mp->b_rptr[7]); if (sp->unit < 0) { freemsg(mp); break; } if (sp->unit >= ppp_nalloc || (ifp = ifs[sp->unit]) == 0) { #ifdef DEBUG printf("if_ppp: no unit %d!\n", sp->unit); #endif freemsg(mp); break; } if ((ifp->if_flags & IFF_UP) == 0) { freemsg(mp); break; } ++ifp->if_ipackets; proto = PPP_PROTOCOL(mp->b_rptr); adjmsg(mp, PPP_HDRLEN); len = msgdsize(mp); mb = make_mbufs(mp, sizeof(struct ifnet *)); freemsg(mp); if (mb == NULL) { if (sp->flags & DBGLOG) printf("if_ppp%d: make_mbufs failed\n", ifp->if_unit); ++ifp->if_ierrors; break; } #ifdef SNIT_SUPPORT if (proto == PPP_IP && (ifp->if_flags & IFF_PROMISC)) { struct nit_if nif; nif.nif_header = (caddr_t) &snit_ehdr; nif.nif_hdrlen = sizeof(snit_ehdr); nif.nif_bodylen = len; nif.nif_promisc = 0; snit_intr(ifp, mb, &nif); } #endif /* * For Digital UNIX, there's space set aside in the header mbuf * for the interface info. * * For Sun it's smuggled around via a pointer at the front of the mbuf. */ #ifdef __osf__ mb->m_pkthdr.rcvif = ifp; mb->m_pkthdr.len = len; #else mb->m_off -= sizeof(struct ifnet *); mb->m_len += sizeof(struct ifnet *); *mtod(mb, struct ifnet **) = ifp; #endif inq = 0; switch (proto) { case PPP_IP: inq = &ipintrq; schednetisr(NETISR_IP); } if (inq != 0) { s = splhigh(); if (IF_QFULL(inq)) { IF_DROP(inq); ++ifp->if_ierrors; if (sp->flags & DBGLOG) printf("if_ppp: inq full, proto=%x\n", proto); m_freem(mb); } else { IF_ENQUEUE(inq, mb); } splx(s); } else { if (sp->flags & DBGLOG) printf("if_ppp%d: proto=%x?\n", ifp->if_unit, proto); ++ifp->if_ierrors; m_freem(mb); } break; default: putnext(q, mp); } return 0; } /* * Network code wants to output a packet. * Turn it into a STREAMS message and send it down. */ static int if_ppp_output(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; { mblk_t *mp; int proto, s; if_ppp_t *sp; u_char *p; if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m0); return ENETDOWN; } if ((unsigned)ifp->if_unit >= ppp_nalloc) { #ifdef DEBUG printf("if_ppp_output: unit %d?\n", ifp->if_unit); #endif m_freem(m0); return EINVAL; } sp = states[ifp->if_unit]; if (sp == 0) { #ifdef DEBUG printf("if_ppp_output: no queue?\n"); #endif m_freem(m0); return ENETDOWN; } if (sp->flags & DBGLOG) { p = mtod(m0, u_char *); printf("if_ppp_output%d: af=%d data=%x %x %x %x %x %x %x %x q=%x\n", ifp->if_unit, dst->sa_family, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], sp->q); } switch (dst->sa_family) { case AF_INET: proto = PPP_IP; #ifdef SNIT_SUPPORT if (ifp->if_flags & IFF_PROMISC) { struct nit_if nif; struct mbuf *m; int len; for (len = 0, m = m0; m != NULL; m = m->m_next) len += m->m_len; nif.nif_header = (caddr_t) &snit_ehdr; nif.nif_hdrlen = sizeof(snit_ehdr); nif.nif_bodylen = len; nif.nif_promisc = 0; snit_intr(ifp, m0, &nif); } #endif break; default: m_freem(m0); return EAFNOSUPPORT; } ++ifp->if_opackets; mp = make_message(m0, PPP_HDRLEN); m_freem(m0); if (mp == 0) { ++ifp->if_oerrors; return ENOBUFS; } mp->b_rptr -= PPP_HDRLEN; mp->b_rptr[0] = PPP_ALLSTATIONS; mp->b_rptr[1] = PPP_UI; mp->b_rptr[2] = proto >> 8; mp->b_rptr[3] = proto; s = splstr(); if (sp->flags & DBGLOG) printf("if_ppp: putnext(%x, %x), r=%x w=%x p=%x\n", sp->q, mp, mp->b_rptr, mp->b_wptr, proto); putnext(sp->q, mp); splx(s); return 0; } /* * Socket ioctl routine for ppp interfaces. */ static int if_ppp_ioctl(ifp, cmd, data) struct ifnet *ifp; u_int cmd; caddr_t data; { int s, error; struct ifreq *ifr = (struct ifreq *) data; struct ifaddr *ifa = (struct ifaddr *) data; u_short mtu; error = 0; s = splimp(); switch (cmd) { case SIOCSIFFLAGS: if ((ifp->if_flags & IFF_RUNNING) == 0) ifp->if_flags &= ~IFF_UP; break; case SIOCSIFADDR: if (IFA_ADDR(ifa).sa_family != AF_INET) error = EAFNOSUPPORT; break; case SIOCSIFDSTADDR: if (IFA_ADDR(ifa).sa_family != AF_INET) error = EAFNOSUPPORT; break; case SIOCSIFMTU: if ((error = NOTSUSER()) != 0) break; #ifdef __osf__ /* this hack is necessary because ifioctl checks ifr_data * in 4.0 and 5.0, but ifr_data and ifr_metric overlay each * other in the definition of struct ifreq so pppd can't set both. */ bcopy(ifr->ifr_data, &mtu, sizeof (u_short)); ifr->ifr_mtu = mtu; #endif if (ifr->ifr_mtu < PPP_MINMTU || ifr->ifr_mtu > PPP_MAXMTU) { error = EINVAL; break; } ifp->if_mtu = ifr->ifr_mtu; break; case SIOCGIFMTU: ifr->ifr_mtu = ifp->if_mtu; break; case SIOCADDMULTI: case SIOCDELMULTI: switch(ifr->ifr_addr.sa_family) { case AF_INET: break; default: error = EAFNOSUPPORT; break; } break; default: error = EINVAL; } splx(s); return (error); } /* * Turn a STREAMS message into an mbuf chain. */ static struct mbuf * make_mbufs(mp, off) mblk_t *mp; int off; { struct mbuf *head, **prevp, *m; int len, space, n; unsigned char *cp, *dp; len = msgdsize(mp); if (len == 0) return 0; prevp = &head; space = 0; cp = mp->b_rptr; #ifdef __osf__ MGETHDR(m, M_DONTWAIT, MT_DATA); m->m_len = 0; space = MHLEN; *prevp = m; prevp = &m->m_next; dp = mtod(m, unsigned char *); len -= space; off = 0; #endif for (;;) { while (cp >= mp->b_wptr) { mp = mp->b_cont; if (mp == 0) { *prevp = 0; return head; } cp = mp->b_rptr; } n = mp->b_wptr - cp; if (space == 0) { MGET(m, M_DONTWAIT, MT_DATA); *prevp = m; if (m == 0) { if (head != 0) m_freem(head); return 0; } if (len + off > 2 * MLEN) { #ifdef __osf__ MCLGET(m, M_DONTWAIT); #else MCLGET(m); #endif } #ifdef __osf__ space = ((m->m_flags & M_EXT) ? MCLBYTES : MLEN); #else space = (m->m_off > MMAXOFF? MCLBYTES: MLEN) - off; m->m_off += off; #endif m->m_len = 0; len -= space; dp = mtod(m, unsigned char *); off = 0; prevp = &m->m_next; } if (n > space) n = space; bcopy(cp, dp, n); cp += n; dp += n; space -= n; m->m_len += n; } } /* * Turn an mbuf chain into a STREAMS message. */ #define ALLOCB_MAX 4096 static mblk_t * make_message(m, off) struct mbuf *m; int off; { mblk_t *head, **prevp, *mp; int len, space, n, nb; unsigned char *cp, *dp; struct mbuf *nm; len = 0; for (nm = m; nm != 0; nm = nm->m_next) len += nm->m_len; prevp = &head; space = 0; cp = mtod(m, unsigned char *); nb = m->m_len; for (;;) { while (nb <= 0) { m = m->m_next; if (m == 0) { *prevp = 0; return head; } cp = mtod(m, unsigned char *); nb = m->m_len; } if (space == 0) { space = len + off; if (space > ALLOCB_MAX) space = ALLOCB_MAX; mp = allocb(space, BPRI_LO); *prevp = mp; if (mp == 0) { if (head != 0) freemsg(head); return 0; } dp = mp->b_rptr += off; space -= off; len -= space; off = 0; prevp = &mp->b_cont; } n = nb < space? nb: space; bcopy(cp, dp, n); cp += n; dp += n; nb -= n; space -= n; mp->b_wptr = dp; } } /* * Digital UNIX doesn't allow for removing ifnet structures * from the list. But then we're not using this as a loadable * module anyway, so that's OK. * * Under SunOS, this should allow the module to be unloaded. * Unfortunately, it doesn't seem to detach all the references, * so your system may well crash after you unload this module :-( */ #ifndef __osf__ /* * Remove an interface from the system. * This routine contains magic. */ #include #include #include #include #include #include #include #include static void ppp_if_detach(ifp) struct ifnet *ifp; { int s; struct inpcb *pcb; struct ifaddr *ifa; struct in_ifaddr **inap; struct ifnet **ifpp; s = splhigh(); /* * Clear the interface from any routes currently cached in * TCP or UDP protocol control blocks. */ for (pcb = tcb.inp_next; pcb != &tcb; pcb = pcb->inp_next) if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp) in_losing(pcb); for (pcb = udb.inp_next; pcb != &udb; pcb = pcb->inp_next) if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp) in_losing(pcb); /* * Delete routes through all addresses of the interface. */ for (ifa = ifp->if_addrlist; ifa != 0; ifa = ifa->ifa_next) { rtinit(ifa, ifa, SIOCDELRT, RTF_HOST); rtinit(ifa, ifa, SIOCDELRT, 0); } /* * Unlink the interface's address(es) from the in_ifaddr list. */ for (inap = &in_ifaddr; *inap != 0; ) { if ((*inap)->ia_ifa.ifa_ifp == ifp) *inap = (*inap)->ia_next; else inap = &(*inap)->ia_next; } /* * Delete the interface from the ifnet list. */ for (ifpp = &ifnet; (*ifpp) != 0; ) { if (*ifpp == ifp) break; ifpp = &(*ifpp)->if_next; } if (*ifpp == 0) printf("couldn't find interface ppp%d in ifnet list\n", ifp->if_unit); else *ifpp = ifp->if_next; splx(s); } #endif /* __osf__ */ ppp-2.4.5/modules/ppp.c000066400000000000000000001715011130035057700147420ustar00rootroot00000000000000/* * ppp.c - STREAMS multiplexing pseudo-device driver for PPP. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp.c,v 1.26 2002/12/06 09:49:15 paulus Exp $ */ /* * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX. */ #include #include #include #include #include #include #ifdef __osf__ #include #include #define queclass(mp) ((mp)->b_band & QPCTL) #else #include #endif #include #ifdef SVR4 #include #include #include #include #ifdef SOL2 #include #include #include #include #else #include #include #include #include #endif /* SOL2 */ #else /* not SVR4 */ #include #endif /* SVR4 */ #include #include #include "ppp_mod.h" /* * Modifications marked with #ifdef PRIOQ are for priority queueing of * interactive traffic, and are due to Marko Zec . */ #ifdef PRIOQ #endif /* PRIOQ */ #include /* leave this outside of PRIOQ for htons */ #ifdef __STDC__ #define __P(x) x #else #define __P(x) () #endif /* * The IP module may use this SAP value for IP packets. */ #ifndef ETHERTYPE_IP #define ETHERTYPE_IP 0x800 #endif #if !defined(ETHERTYPE_IPV6) #define ETHERTYPE_IPV6 0x86dd #endif /* !defined(ETHERTYPE_IPV6) */ #if !defined(ETHERTYPE_ALLSAP) && defined(SOL2) #define ETHERTYPE_ALLSAP 0 #endif /* !defined(ETHERTYPE_ALLSAP) && defined(SOL2) */ #if !defined(PPP_ALLSAP) && defined(SOL2) #define PPP_ALLSAP PPP_ALLSTATIONS #endif /* !defined(PPP_ALLSAP) && defined(SOL2) */ extern time_t time; #ifdef SOL2 /* * We use this reader-writer lock to ensure that the lower streams * stay connected to the upper streams while the lower-side put and * service procedures are running. Essentially it is an existence * lock for the upper stream associated with each lower stream. */ krwlock_t ppp_lower_lock; #define LOCK_LOWER_W rw_enter(&ppp_lower_lock, RW_WRITER) #define LOCK_LOWER_R rw_enter(&ppp_lower_lock, RW_READER) #define TRYLOCK_LOWER_R rw_tryenter(&ppp_lower_lock, RW_READER) #define UNLOCK_LOWER rw_exit(&ppp_lower_lock) #define MT_ENTER(x) mutex_enter(x) #define MT_EXIT(x) mutex_exit(x) /* * Notes on multithreaded implementation for Solaris 2: * * We use an inner perimeter around each queue pair and an outer * perimeter around the whole driver. The inner perimeter is * entered exclusively for all entry points (open, close, put, * service). The outer perimeter is entered exclusively for open * and close and shared for put and service. This is all done for * us by the streams framework. * * I used to think that the perimeters were entered for the lower * streams' put and service routines as well as for the upper streams'. * Because of problems experienced by people, and after reading the * documentation more closely, I now don't think that is true. So we * now use ppp_lower_lock to give us an existence guarantee on the * upper stream controlling each lower stream. * * Shared entry to the outer perimeter protects the existence of all * the upper streams and their upperstr_t structures, and guarantees * that the following fields of any upperstr_t won't change: * nextmn, next, nextppa. It guarantees that the lowerq field of an * upperstr_t won't go from non-zero to zero, that the global `ppas' * won't change and that the no lower stream will get unlinked. * * Shared (reader) access to ppa_lower_lock guarantees that no lower * stream will be unlinked and that the lowerq field of all upperstr_t * structures won't change. */ #else /* SOL2 */ #define LOCK_LOWER_W 0 #define LOCK_LOWER_R 0 #define TRYLOCK_LOWER_R 1 #define UNLOCK_LOWER 0 #define MT_ENTER(x) 0 #define MT_EXIT(x) 0 #endif /* SOL2 */ /* * Private information; one per upper stream. */ typedef struct upperstr { minor_t mn; /* minor device number */ struct upperstr *nextmn; /* next minor device */ queue_t *q; /* read q associated with this upper stream */ int flags; /* flag bits, see below */ int state; /* current DLPI state */ int sap; /* service access point */ int req_sap; /* which SAP the DLPI client requested */ struct upperstr *ppa; /* control stream for our ppa */ struct upperstr *next; /* next stream for this ppa */ uint ioc_id; /* last ioctl ID for this stream */ enum NPmode npmode; /* what to do with packets on this SAP */ unsigned char rblocked; /* flow control has blocked upper read strm */ /* N.B. rblocked is only changed by control stream's put/srv procs */ /* * There is exactly one control stream for each PPA. * The following fields are only used for control streams. */ int ppa_id; queue_t *lowerq; /* write queue attached below this PPA */ struct upperstr *nextppa; /* next control stream */ int mru; int mtu; struct pppstat stats; /* statistics */ time_t last_sent; /* time last NP packet sent */ time_t last_recv; /* time last NP packet rcvd */ #ifdef SOL2 kmutex_t stats_lock; /* lock for stats updates */ kstat_t *kstats; /* stats for netstat */ #endif /* SOL2 */ #ifdef LACHTCP int ifflags; char ifname[IFNAMSIZ]; struct ifstats ifstats; #endif /* LACHTCP */ } upperstr_t; /* Values for flags */ #define US_PRIV 1 /* stream was opened by superuser */ #define US_CONTROL 2 /* stream is a control stream */ #define US_BLOCKED 4 /* flow ctrl has blocked lower write stream */ #define US_LASTMOD 8 /* no PPP modules below us */ #define US_DBGLOG 0x10 /* log various occurrences */ #define US_RBLOCKED 0x20 /* flow ctrl has blocked upper read stream */ #if defined(SOL2) #if DL_CURRENT_VERSION >= 2 #define US_PROMISC 0x40 /* stream is promiscuous */ #endif /* DL_CURRENT_VERSION >= 2 */ #define US_RAWDATA 0x80 /* raw M_DATA, no DLPI header */ #endif /* defined(SOL2) */ #ifdef PRIOQ static u_char max_band=0; static u_char def_band=0; #define IPPORT_DEFAULT 65535 /* * Port priority table * Highest priority ports are listed first, lowest are listed last. * ICMP & packets using unlisted ports will be treated as "default". * If IPPORT_DEFAULT is not listed here, "default" packets will be * assigned lowest priority. * Each line should be terminated with "0". * Line containing only "0" marks the end of the list. */ static u_short prioq_table[]= { 113, 53, 0, 22, 23, 513, 517, 518, 0, 514, 21, 79, 111, 0, 25, 109, 110, 0, IPPORT_DEFAULT, 0, 20, 70, 80, 8001, 8008, 8080, 0, /* 8001,8008,8080 - common proxy ports */ 0 }; #endif /* PRIOQ */ static upperstr_t *minor_devs = NULL; static upperstr_t *ppas = NULL; #ifdef SVR4 static int pppopen __P((queue_t *, dev_t *, int, int, cred_t *)); static int pppclose __P((queue_t *, int, cred_t *)); #else static int pppopen __P((queue_t *, int, int, int)); static int pppclose __P((queue_t *, int)); #endif /* SVR4 */ static int pppurput __P((queue_t *, mblk_t *)); static int pppuwput __P((queue_t *, mblk_t *)); static int pppursrv __P((queue_t *)); static int pppuwsrv __P((queue_t *)); static int ppplrput __P((queue_t *, mblk_t *)); static int ppplwput __P((queue_t *, mblk_t *)); static int ppplrsrv __P((queue_t *)); static int ppplwsrv __P((queue_t *)); #ifndef NO_DLPI static void dlpi_request __P((queue_t *, mblk_t *, upperstr_t *)); static void dlpi_error __P((queue_t *, upperstr_t *, int, int, int)); static void dlpi_ok __P((queue_t *, int)); #endif static int send_data __P((mblk_t *, upperstr_t *)); static void new_ppa __P((queue_t *, mblk_t *)); static void attach_ppa __P((queue_t *, mblk_t *)); static void detach_ppa __P((queue_t *, mblk_t *)); static void detach_lower __P((queue_t *, mblk_t *)); static void debug_dump __P((queue_t *, mblk_t *)); static upperstr_t *find_dest __P((upperstr_t *, int)); #if defined(SOL2) static upperstr_t *find_promisc __P((upperstr_t *, int)); static mblk_t *prepend_ether __P((upperstr_t *, mblk_t *, int)); static mblk_t *prepend_udind __P((upperstr_t *, mblk_t *, int)); static void promisc_sendup __P((upperstr_t *, mblk_t *, int, int)); #endif /* defined(SOL2) */ static int putctl2 __P((queue_t *, int, int, int)); static int putctl4 __P((queue_t *, int, int, int)); static int pass_packet __P((upperstr_t *ppa, mblk_t *mp, int outbound)); #ifdef FILTER_PACKETS static int ip_hard_filter __P((upperstr_t *ppa, mblk_t *mp, int outbound)); #endif /* FILTER_PACKETS */ #define PPP_ID 0xb1a6 static struct module_info ppp_info = { #ifdef PRIOQ PPP_ID, "ppp", 0, 512, 512, 384 #else PPP_ID, "ppp", 0, 512, 512, 128 #endif /* PRIOQ */ }; static struct qinit pppurint = { pppurput, pppursrv, pppopen, pppclose, NULL, &ppp_info, NULL }; static struct qinit pppuwint = { pppuwput, pppuwsrv, NULL, NULL, NULL, &ppp_info, NULL }; static struct qinit ppplrint = { ppplrput, ppplrsrv, NULL, NULL, NULL, &ppp_info, NULL }; static struct qinit ppplwint = { ppplwput, ppplwsrv, NULL, NULL, NULL, &ppp_info, NULL }; #ifdef LACHTCP extern struct ifstats *ifstats; int pppdevflag = 0; #endif struct streamtab pppinfo = { &pppurint, &pppuwint, &ppplrint, &ppplwint }; int ppp_count; /* * How we maintain statistics. */ #ifdef SOL2 #define INCR_IPACKETS(ppa) \ if (ppa->kstats != 0) { \ KSTAT_NAMED_PTR(ppa->kstats)[0].value.ul++; \ } #define INCR_IERRORS(ppa) \ if (ppa->kstats != 0) { \ KSTAT_NAMED_PTR(ppa->kstats)[1].value.ul++; \ } #define INCR_OPACKETS(ppa) \ if (ppa->kstats != 0) { \ KSTAT_NAMED_PTR(ppa->kstats)[2].value.ul++; \ } #define INCR_OERRORS(ppa) \ if (ppa->kstats != 0) { \ KSTAT_NAMED_PTR(ppa->kstats)[3].value.ul++; \ } #endif #ifdef LACHTCP #define INCR_IPACKETS(ppa) ppa->ifstats.ifs_ipackets++; #define INCR_IERRORS(ppa) ppa->ifstats.ifs_ierrors++; #define INCR_OPACKETS(ppa) ppa->ifstats.ifs_opackets++; #define INCR_OERRORS(ppa) ppa->ifstats.ifs_oerrors++; #endif /* * STREAMS driver entry points. */ static int #ifdef SVR4 pppopen(q, devp, oflag, sflag, credp) queue_t *q; dev_t *devp; int oflag, sflag; cred_t *credp; #else pppopen(q, dev, oflag, sflag) queue_t *q; int dev; /* really dev_t */ int oflag, sflag; #endif { upperstr_t *up; upperstr_t **prevp; minor_t mn; #ifdef PRIOQ u_short *ptr; u_char new_band; #endif /* PRIOQ */ if (q->q_ptr) DRV_OPEN_OK(dev); /* device is already open */ #ifdef PRIOQ /* Calculate max_bband & def_band from definitions in prioq.h This colud be done at some more approtiate time (less often) but this way it works well so I'll just leave it here */ max_band = 1; def_band = 0; ptr = prioq_table; while (*ptr) { new_band = 1; while (*ptr) if (*ptr++ == IPPORT_DEFAULT) { new_band = 0; def_band = max_band; } max_band += new_band; ptr++; } if (def_band) def_band = max_band - def_band; --max_band; #endif /* PRIOQ */ if (sflag == CLONEOPEN) { mn = 0; for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) { if (up->mn != mn) break; ++mn; } } else { #ifdef SVR4 mn = getminor(*devp); #else mn = minor(dev); #endif for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) { if (up->mn >= mn) break; } if (up->mn == mn) { /* this can't happen */ q->q_ptr = WR(q)->q_ptr = (caddr_t) up; DRV_OPEN_OK(dev); } } /* * Construct a new minor node. */ up = (upperstr_t *) ALLOC_SLEEP(sizeof(upperstr_t)); bzero((caddr_t) up, sizeof(upperstr_t)); if (up == 0) { DPRINT("pppopen: out of kernel memory\n"); OPEN_ERROR(ENXIO); } up->nextmn = *prevp; *prevp = up; up->mn = mn; #ifdef SVR4 *devp = makedevice(getmajor(*devp), mn); #endif up->q = q; if (NOTSUSER() == 0) up->flags |= US_PRIV; #ifndef NO_DLPI up->state = DL_UNATTACHED; #endif #ifdef LACHTCP up->ifflags = IFF_UP | IFF_POINTOPOINT; #endif up->sap = -1; up->last_sent = up->last_recv = time; up->npmode = NPMODE_DROP; q->q_ptr = (caddr_t) up; WR(q)->q_ptr = (caddr_t) up; noenable(WR(q)); #ifdef SOL2 mutex_init(&up->stats_lock, NULL, MUTEX_DRIVER, NULL); #endif ++ppp_count; qprocson(q); DRV_OPEN_OK(makedev(major(dev), mn)); } static int #ifdef SVR4 pppclose(q, flag, credp) queue_t *q; int flag; cred_t *credp; #else pppclose(q, flag) queue_t *q; int flag; #endif { upperstr_t *up, **upp; upperstr_t *as, *asnext; upperstr_t **prevp; qprocsoff(q); up = (upperstr_t *) q->q_ptr; if (up == 0) { DPRINT("pppclose: q_ptr = 0\n"); return 0; } if (up->flags & US_DBGLOG) DPRINT2("ppp/%d: close, flags=%x\n", up->mn, up->flags); if (up->flags & US_CONTROL) { #ifdef LACHTCP struct ifstats *ifp, *pifp; #endif if (up->lowerq != 0) { /* Gack! the lower stream should have be unlinked earlier! */ DPRINT1("ppp%d: lower stream still connected on close?\n", up->mn); LOCK_LOWER_W; up->lowerq->q_ptr = 0; RD(up->lowerq)->q_ptr = 0; up->lowerq = 0; UNLOCK_LOWER; } /* * This stream represents a PPA: * For all streams attached to the PPA, clear their * references to this PPA. * Then remove this PPA from the list of PPAs. */ for (as = up->next; as != 0; as = asnext) { asnext = as->next; as->next = 0; as->ppa = 0; if (as->flags & US_BLOCKED) { as->flags &= ~US_BLOCKED; flushq(WR(as->q), FLUSHDATA); } } for (upp = &ppas; *upp != 0; upp = &(*upp)->nextppa) if (*upp == up) { *upp = up->nextppa; break; } #ifdef LACHTCP /* Remove the statistics from the active list. */ for (ifp = ifstats, pifp = 0; ifp; ifp = ifp->ifs_next) { if (ifp == &up->ifstats) { if (pifp) pifp->ifs_next = ifp->ifs_next; else ifstats = ifp->ifs_next; break; } pifp = ifp; } #endif } else { /* * If this stream is attached to a PPA, * remove it from the PPA's list. */ if ((as = up->ppa) != 0) { for (; as->next != 0; as = as->next) if (as->next == up) { as->next = up->next; break; } } } #ifdef SOL2 if (up->kstats) kstat_delete(up->kstats); mutex_destroy(&up->stats_lock); #endif q->q_ptr = NULL; WR(q)->q_ptr = NULL; for (prevp = &minor_devs; *prevp != 0; prevp = &(*prevp)->nextmn) { if (*prevp == up) { *prevp = up->nextmn; break; } } FREE(up, sizeof(upperstr_t)); --ppp_count; return 0; } /* * A message from on high. We do one of three things: * - qreply() * - put the message on the lower write stream * - queue it for our service routine */ static int pppuwput(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us, *ppa, *nps; struct iocblk *iop; struct linkblk *lb; #ifdef LACHTCP struct ifreq *ifr; int i; #endif queue_t *lq; int error, n, sap; mblk_t *mq; struct ppp_idle *pip; #ifdef PRIOQ queue_t *tlq; #endif /* PRIOQ */ #ifdef NO_DLPI upperstr_t *os; #endif us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("pppuwput: q_ptr = 0!\n"); return 0; } if (mp == 0) { DPRINT1("pppuwput/%d: mp = 0!\n", us->mn); return 0; } if (mp->b_datap == 0) { DPRINT1("pppuwput/%d: mp->b_datap = 0!\n", us->mn); return 0; } switch (mp->b_datap->db_type) { #ifndef NO_DLPI case M_PCPROTO: case M_PROTO: dlpi_request(q, mp, us); break; #endif /* NO_DLPI */ case M_DATA: if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: uwput M_DATA len=%d flags=%x\n", us->mn, msgdsize(mp), us->flags); if (us->ppa == 0 || msgdsize(mp) > us->ppa->mtu + PPP_HDRLEN #ifndef NO_DLPI || (us->flags & US_CONTROL) == 0 #endif /* NO_DLPI */ ) { DPRINT1("pppuwput: junk data len=%d\n", msgdsize(mp)); freemsg(mp); break; } #ifdef NO_DLPI if ((us->flags & US_CONTROL) == 0 && !pass_packet(us, mp, 1)) break; #endif if (!send_data(mp, us)) putq(q, mp); break; case M_IOCTL: iop = (struct iocblk *) mp->b_rptr; error = EINVAL; if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: ioctl %x count=%d\n", us->mn, iop->ioc_cmd, iop->ioc_count); switch (iop->ioc_cmd) { #if defined(SOL2) case DLIOCRAW: /* raw M_DATA mode */ us->flags |= US_RAWDATA; error = 0; break; #endif /* defined(SOL2) */ case I_LINK: if ((us->flags & US_CONTROL) == 0 || us->lowerq != 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl I_LINK b_cont = 0!\n", us->mn); break; } lb = (struct linkblk *) mp->b_cont->b_rptr; lq = lb->l_qbot; if (lq == 0) { DPRINT1("pppuwput/%d: ioctl I_LINK l_qbot = 0!\n", us->mn); break; } LOCK_LOWER_W; us->lowerq = lq; lq->q_ptr = (caddr_t) q; RD(lq)->q_ptr = (caddr_t) us->q; UNLOCK_LOWER; iop->ioc_count = 0; error = 0; us->flags &= ~US_LASTMOD; /* Unblock upper streams which now feed this lower stream. */ qenable(q); /* Send useful information down to the modules which are now linked below us. */ putctl2(lq, M_CTL, PPPCTL_UNIT, us->ppa_id); putctl4(lq, M_CTL, PPPCTL_MRU, us->mru); putctl4(lq, M_CTL, PPPCTL_MTU, us->mtu); #ifdef PRIOQ /* Lower tty driver's queue hiwat/lowat from default 4096/128 to 256/128 since we don't want queueing of data on output to physical device */ freezestr(lq); for (tlq = lq; tlq->q_next != NULL; tlq = tlq->q_next) ; strqset(tlq, QHIWAT, 0, 256); strqset(tlq, QLOWAT, 0, 128); unfreezestr(lq); #endif /* PRIOQ */ break; case I_UNLINK: if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl I_UNLINK b_cont = 0!\n", us->mn); break; } lb = (struct linkblk *) mp->b_cont->b_rptr; #if DEBUG if (us->lowerq != lb->l_qbot) { DPRINT2("ppp unlink: lowerq=%x qbot=%x\n", us->lowerq, lb->l_qbot); break; } #endif iop->ioc_count = 0; qwriter(q, mp, detach_lower, PERIM_OUTER); error = -1; break; case PPPIO_NEWPPA: if (us->flags & US_CONTROL) break; if ((us->flags & US_PRIV) == 0) { error = EPERM; break; } /* Arrange to return an int */ if ((mq = mp->b_cont) == 0 || mq->b_datap->db_lim - mq->b_rptr < sizeof(int)) { mq = allocb(sizeof(int), BPRI_HI); if (mq == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = mq; mq->b_cont = 0; } iop->ioc_count = sizeof(int); mq->b_wptr = mq->b_rptr + sizeof(int); qwriter(q, mp, new_ppa, PERIM_OUTER); error = -1; break; case PPPIO_ATTACH: /* like dlpi_attach, for programs which can't write to the stream (like pppstats) */ if (iop->ioc_count != sizeof(int) || us->ppa != 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_ATTACH b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; for (ppa = ppas; ppa != 0; ppa = ppa->nextppa) if (ppa->ppa_id == n) break; if (ppa == 0) break; us->ppa = ppa; iop->ioc_count = 0; qwriter(q, mp, attach_ppa, PERIM_OUTER); error = -1; break; #ifdef NO_DLPI case PPPIO_BIND: /* Attach to a given SAP. */ if (iop->ioc_count != sizeof(int) || us->ppa == 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_BIND b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; /* n must be a valid PPP network protocol number. */ if (n < 0x21 || n > 0x3fff || (n & 0x101) != 1) break; /* check that no other stream is bound to this sap already. */ for (os = us->ppa; os != 0; os = os->next) if (os->sap == n) break; if (os != 0) break; us->sap = n; iop->ioc_count = 0; error = 0; break; #endif /* NO_DLPI */ case PPPIO_MRU: if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_MRU b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; if (n <= 0 || n > PPP_MAXMRU) break; if (n < PPP_MRU) n = PPP_MRU; us->mru = n; if (us->lowerq) putctl4(us->lowerq, M_CTL, PPPCTL_MRU, n); error = 0; iop->ioc_count = 0; break; case PPPIO_MTU: if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_MTU b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; if (n <= 0 || n > PPP_MAXMTU) break; us->mtu = n; #ifdef LACHTCP /* The MTU reported in netstat, not used as IP max packet size! */ us->ifstats.ifs_mtu = n; #endif if (us->lowerq) putctl4(us->lowerq, M_CTL, PPPCTL_MTU, n); error = 0; iop->ioc_count = 0; break; case PPPIO_LASTMOD: us->flags |= US_LASTMOD; error = 0; break; case PPPIO_DEBUG: if (iop->ioc_count != sizeof(int)) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_DEBUG b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; if (n == PPPDBG_DUMP + PPPDBG_DRIVER) { qwriter(q, NULL, debug_dump, PERIM_OUTER); iop->ioc_count = 0; error = -1; } else if (n == PPPDBG_LOG + PPPDBG_DRIVER) { DPRINT1("ppp/%d: debug log enabled\n", us->mn); us->flags |= US_DBGLOG; iop->ioc_count = 0; error = 0; } else { if (us->ppa == 0 || us->ppa->lowerq == 0) break; putnext(us->ppa->lowerq, mp); error = -1; } break; case PPPIO_NPMODE: if (iop->ioc_count != 2 * sizeof(int)) break; if ((us->flags & US_CONTROL) == 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_NPMODE b_cont = 0!\n", us->mn); break; } sap = ((int *)mp->b_cont->b_rptr)[0]; for (nps = us->next; nps != 0; nps = nps->next) { if (us->flags & US_DBGLOG) DPRINT2("us = 0x%x, us->next->sap = 0x%x\n", nps, nps->sap); if (nps->sap == sap) break; } if (nps == 0) { if (us->flags & US_DBGLOG) DPRINT2("ppp/%d: no stream for sap %x\n", us->mn, sap); break; } /* XXX possibly should use qwriter here */ nps->npmode = (enum NPmode) ((int *)mp->b_cont->b_rptr)[1]; if (nps->npmode != NPMODE_QUEUE && (nps->flags & US_BLOCKED) != 0) qenable(WR(nps->q)); iop->ioc_count = 0; error = 0; break; case PPPIO_GIDLE: if ((ppa = us->ppa) == 0) break; mq = allocb(sizeof(struct ppp_idle), BPRI_HI); if (mq == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = mq; mq->b_cont = 0; pip = (struct ppp_idle *) mq->b_wptr; pip->xmit_idle = time - ppa->last_sent; pip->recv_idle = time - ppa->last_recv; mq->b_wptr += sizeof(struct ppp_idle); iop->ioc_count = sizeof(struct ppp_idle); error = 0; break; #ifdef LACHTCP case SIOCSIFNAME: /* Sent from IP down to us. Attach the ifstats structure. */ if (iop->ioc_count != sizeof(struct ifreq) || us->ppa == 0) break; ifr = (struct ifreq *)mp->b_cont->b_rptr; /* Find the unit number in the interface name. */ for (i = 0; i < IFNAMSIZ; i++) { if (ifr->ifr_name[i] == 0 || (ifr->ifr_name[i] >= '0' && ifr->ifr_name[i] <= '9')) break; else us->ifname[i] = ifr->ifr_name[i]; } us->ifname[i] = 0; /* Convert the unit number to binary. */ for (n = 0; i < IFNAMSIZ; i++) { if (ifr->ifr_name[i] == 0) { break; } else { n = n * 10 + ifr->ifr_name[i] - '0'; } } /* Verify the ppa. */ if (us->ppa->ppa_id != n) break; ppa = us->ppa; /* Set up the netstat block. */ strncpy (ppa->ifname, us->ifname, IFNAMSIZ); ppa->ifstats.ifs_name = ppa->ifname; ppa->ifstats.ifs_unit = n; ppa->ifstats.ifs_active = us->state != DL_UNBOUND; ppa->ifstats.ifs_mtu = ppa->mtu; /* Link in statistics used by netstat. */ ppa->ifstats.ifs_next = ifstats; ifstats = &ppa->ifstats; iop->ioc_count = 0; error = 0; break; case SIOCGIFFLAGS: if (!(us->flags & US_CONTROL)) { if (us->ppa) us = us->ppa; else break; } ((struct iocblk_in *)iop)->ioc_ifflags = us->ifflags; error = 0; break; case SIOCSIFFLAGS: if (!(us->flags & US_CONTROL)) { if (us->ppa) us = us->ppa; else break; } us->ifflags = ((struct iocblk_in *)iop)->ioc_ifflags; error = 0; break; case SIOCSIFADDR: if (!(us->flags & US_CONTROL)) { if (us->ppa) us = us->ppa; else break; } us->ifflags |= IFF_RUNNING; ((struct iocblk_in *)iop)->ioc_ifflags |= IFF_RUNNING; error = 0; break; case SIOCSIFMTU: /* * Vanilla SVR4 systems don't handle SIOCSIFMTU, rather * they take the MTU from the DL_INFO_ACK we sent in response * to their DL_INFO_REQ. Fortunately, they will update the * MTU if we send an unsolicited DL_INFO_ACK up. */ if ((mq = allocb(sizeof(dl_info_req_t), BPRI_HI)) == 0) break; /* should do bufcall */ ((union DL_primitives *)mq->b_rptr)->dl_primitive = DL_INFO_REQ; mq->b_wptr = mq->b_rptr + sizeof(dl_info_req_t); dlpi_request(q, mq, us); error = 0; break; case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFADDR: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCGIFMETRIC: error = 0; break; #endif /* LACHTCP */ default: if (us->ppa == 0 || us->ppa->lowerq == 0) break; us->ioc_id = iop->ioc_id; error = -1; switch (iop->ioc_cmd) { case PPPIO_GETSTAT: case PPPIO_GETCSTAT: if (us->flags & US_LASTMOD) { error = EINVAL; break; } putnext(us->ppa->lowerq, mp); break; default: if (us->flags & US_PRIV) putnext(us->ppa->lowerq, mp); else { DPRINT1("ppp ioctl %x rejected\n", iop->ioc_cmd); error = EPERM; } break; } break; } if (error > 0) { iop->ioc_error = error; mp->b_datap->db_type = M_IOCNAK; qreply(q, mp); } else if (error == 0) { mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } break; case M_FLUSH: if (us->flags & US_DBGLOG) DPRINT2("ppp/%d: flush %x\n", us->mn, *mp->b_rptr); if (*mp->b_rptr & FLUSHW) flushq(q, FLUSHDATA); if (*mp->b_rptr & FLUSHR) { *mp->b_rptr &= ~FLUSHW; qreply(q, mp); } else freemsg(mp); break; default: freemsg(mp); break; } return 0; } #ifndef NO_DLPI static void dlpi_request(q, mp, us) queue_t *q; mblk_t *mp; upperstr_t *us; { union DL_primitives *d = (union DL_primitives *) mp->b_rptr; int size = mp->b_wptr - mp->b_rptr; mblk_t *reply, *np; upperstr_t *ppa, *os; int sap, len; dl_info_ack_t *info; dl_bind_ack_t *ackp; #if DL_CURRENT_VERSION >= 2 dl_phys_addr_ack_t *paddrack; static struct ether_addr eaddr = {0}; #endif if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: dlpi prim %x len=%d\n", us->mn, d->dl_primitive, size); switch (d->dl_primitive) { case DL_INFO_REQ: if (size < sizeof(dl_info_req_t)) goto badprim; if ((reply = allocb(sizeof(dl_info_ack_t), BPRI_HI)) == 0) break; /* should do bufcall */ reply->b_datap->db_type = M_PCPROTO; info = (dl_info_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_info_ack_t); bzero((caddr_t) info, sizeof(dl_info_ack_t)); info->dl_primitive = DL_INFO_ACK; info->dl_max_sdu = us->ppa? us->ppa->mtu: PPP_MAXMTU; info->dl_min_sdu = 1; info->dl_addr_length = sizeof(uint); info->dl_mac_type = DL_ETHER; /* a bigger lie */ info->dl_current_state = us->state; info->dl_service_mode = DL_CLDLS; info->dl_provider_style = DL_STYLE2; #if DL_CURRENT_VERSION >= 2 info->dl_sap_length = sizeof(uint); info->dl_version = DL_CURRENT_VERSION; #endif qreply(q, reply); break; case DL_ATTACH_REQ: if (size < sizeof(dl_attach_req_t)) goto badprim; if (us->state != DL_UNATTACHED || us->ppa != 0) { dlpi_error(q, us, DL_ATTACH_REQ, DL_OUTSTATE, 0); break; } for (ppa = ppas; ppa != 0; ppa = ppa->nextppa) if (ppa->ppa_id == d->attach_req.dl_ppa) break; if (ppa == 0) { dlpi_error(q, us, DL_ATTACH_REQ, DL_BADPPA, 0); break; } us->ppa = ppa; qwriter(q, mp, attach_ppa, PERIM_OUTER); return; case DL_DETACH_REQ: if (size < sizeof(dl_detach_req_t)) goto badprim; if (us->state != DL_UNBOUND || us->ppa == 0) { dlpi_error(q, us, DL_DETACH_REQ, DL_OUTSTATE, 0); break; } qwriter(q, mp, detach_ppa, PERIM_OUTER); return; case DL_BIND_REQ: if (size < sizeof(dl_bind_req_t)) goto badprim; if (us->state != DL_UNBOUND || us->ppa == 0) { dlpi_error(q, us, DL_BIND_REQ, DL_OUTSTATE, 0); break; } #if 0 /* apparently this test fails (unnecessarily?) on some systems */ if (d->bind_req.dl_service_mode != DL_CLDLS) { dlpi_error(q, us, DL_BIND_REQ, DL_UNSUPPORTED, 0); break; } #endif /* saps must be valid PPP network protocol numbers, except that we accept ETHERTYPE_IP in place of PPP_IP. */ sap = d->bind_req.dl_sap; us->req_sap = sap; #if defined(SOL2) if (us->flags & US_DBGLOG) DPRINT2("DL_BIND_REQ: ip gives sap = 0x%x, us = 0x%x", sap, us); if (sap == ETHERTYPE_IP) /* normal IFF_IPV4 */ sap = PPP_IP; else if (sap == ETHERTYPE_IPV6) /* when IFF_IPV6 is set */ sap = PPP_IPV6; else if (sap == ETHERTYPE_ALLSAP) /* snoop gives sap of 0 */ sap = PPP_ALLSAP; else { DPRINT2("DL_BIND_REQ: unrecognized sap = 0x%x, us = 0x%x", sap, us); dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0); break; } #else if (sap == ETHERTYPE_IP) sap = PPP_IP; if (sap < 0x21 || sap > 0x3fff || (sap & 0x101) != 1) { dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0); break; } #endif /* defined(SOL2) */ /* check that no other stream is bound to this sap already. */ for (os = us->ppa; os != 0; os = os->next) if (os->sap == sap) break; if (os != 0) { dlpi_error(q, us, DL_BIND_REQ, DL_NOADDR, 0); break; } us->sap = sap; us->state = DL_IDLE; if ((reply = allocb(sizeof(dl_bind_ack_t) + sizeof(uint), BPRI_HI)) == 0) break; /* should do bufcall */ ackp = (dl_bind_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_bind_ack_t) + sizeof(uint); reply->b_datap->db_type = M_PCPROTO; bzero((caddr_t) ackp, sizeof(dl_bind_ack_t)); ackp->dl_primitive = DL_BIND_ACK; ackp->dl_sap = sap; ackp->dl_addr_length = sizeof(uint); ackp->dl_addr_offset = sizeof(dl_bind_ack_t); *(uint *)(ackp+1) = sap; qreply(q, reply); break; case DL_UNBIND_REQ: if (size < sizeof(dl_unbind_req_t)) goto badprim; if (us->state != DL_IDLE) { dlpi_error(q, us, DL_UNBIND_REQ, DL_OUTSTATE, 0); break; } us->sap = -1; us->state = DL_UNBOUND; #ifdef LACHTCP us->ppa->ifstats.ifs_active = 0; #endif dlpi_ok(q, DL_UNBIND_REQ); break; case DL_UNITDATA_REQ: if (size < sizeof(dl_unitdata_req_t)) goto badprim; if (us->state != DL_IDLE) { dlpi_error(q, us, DL_UNITDATA_REQ, DL_OUTSTATE, 0); break; } if ((ppa = us->ppa) == 0) { cmn_err(CE_CONT, "ppp: in state dl_idle but ppa == 0?\n"); break; } len = mp->b_cont == 0? 0: msgdsize(mp->b_cont); if (len > ppa->mtu) { DPRINT2("dlpi data too large (%d > %d)\n", len, ppa->mtu); break; } #if defined(SOL2) /* * Should there be any promiscuous stream(s), send the data * up for each promiscuous stream that we recognize. */ if (mp->b_cont) promisc_sendup(ppa, mp->b_cont, us->sap, 0); #endif /* defined(SOL2) */ mp->b_band = 0; #ifdef PRIOQ /* Extract s_port & d_port from IP-packet, the code is a bit dirty here, but so am I, too... */ if (mp->b_datap->db_type == M_PROTO && us->sap == PPP_IP && mp->b_cont != 0) { u_char *bb, *tlh; int iphlen, len; u_short *ptr; u_char band_unset, cur_band, syn; u_short s_port, d_port; bb = mp->b_cont->b_rptr; /* bb points to IP-header*/ len = mp->b_cont->b_wptr - mp->b_cont->b_rptr; syn = 0; s_port = IPPORT_DEFAULT; d_port = IPPORT_DEFAULT; if (len >= 20) { /* 20 = minimum length of IP header */ iphlen = (bb[0] & 0x0f) * 4; tlh = bb + iphlen; len -= iphlen; switch (bb[9]) { case IPPROTO_TCP: if (len >= 20) { /* min length of TCP header */ s_port = (tlh[0] << 8) + tlh[1]; d_port = (tlh[2] << 8) + tlh[3]; syn = tlh[13] & 0x02; } break; case IPPROTO_UDP: if (len >= 8) { /* min length of UDP header */ s_port = (tlh[0] << 8) + tlh[1]; d_port = (tlh[2] << 8) + tlh[3]; } break; } } /* * Now calculate b_band for this packet from the * port-priority table. */ ptr = prioq_table; cur_band = max_band; band_unset = 1; while (*ptr) { while (*ptr && band_unset) if (s_port == *ptr || d_port == *ptr++) { mp->b_band = cur_band; band_unset = 0; break; } ptr++; cur_band--; } if (band_unset) mp->b_band = def_band; /* It may be usable to urge SYN packets a bit */ if (syn) mp->b_band++; } #endif /* PRIOQ */ /* this assumes PPP_HDRLEN <= sizeof(dl_unitdata_req_t) */ if (mp->b_datap->db_ref > 1) { np = allocb(PPP_HDRLEN, BPRI_HI); if (np == 0) break; /* gak! */ np->b_cont = mp->b_cont; mp->b_cont = 0; freeb(mp); mp = np; } else mp->b_datap->db_type = M_DATA; /* XXX should use dl_dest_addr_offset/length here, but we would have to translate ETHERTYPE_IP -> PPP_IP */ mp->b_wptr = mp->b_rptr + PPP_HDRLEN; mp->b_rptr[0] = PPP_ALLSTATIONS; mp->b_rptr[1] = PPP_UI; mp->b_rptr[2] = us->sap >> 8; mp->b_rptr[3] = us->sap; if (pass_packet(us, mp, 1)) { if (!send_data(mp, us)) putq(q, mp); } return; #if DL_CURRENT_VERSION >= 2 case DL_PHYS_ADDR_REQ: if (size < sizeof(dl_phys_addr_req_t)) goto badprim; /* * Don't check state because ifconfig sends this one down too */ if ((reply = allocb(sizeof(dl_phys_addr_ack_t)+ETHERADDRL, BPRI_HI)) == 0) break; /* should do bufcall */ reply->b_datap->db_type = M_PCPROTO; paddrack = (dl_phys_addr_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_phys_addr_ack_t); bzero((caddr_t) paddrack, sizeof(dl_phys_addr_ack_t)+ETHERADDRL); paddrack->dl_primitive = DL_PHYS_ADDR_ACK; paddrack->dl_addr_length = ETHERADDRL; paddrack->dl_addr_offset = sizeof(dl_phys_addr_ack_t); bcopy(&eaddr, reply->b_wptr, ETHERADDRL); reply->b_wptr += ETHERADDRL; qreply(q, reply); break; #if defined(SOL2) case DL_PROMISCON_REQ: if (size < sizeof(dl_promiscon_req_t)) goto badprim; us->flags |= US_PROMISC; dlpi_ok(q, DL_PROMISCON_REQ); break; case DL_PROMISCOFF_REQ: if (size < sizeof(dl_promiscoff_req_t)) goto badprim; us->flags &= ~US_PROMISC; dlpi_ok(q, DL_PROMISCOFF_REQ); break; #else case DL_PROMISCON_REQ: /* fall thru */ case DL_PROMISCOFF_REQ: /* fall thru */ #endif /* defined(SOL2) */ #endif /* DL_CURRENT_VERSION >= 2 */ #if DL_CURRENT_VERSION >= 2 case DL_SET_PHYS_ADDR_REQ: case DL_SUBS_BIND_REQ: case DL_SUBS_UNBIND_REQ: case DL_ENABMULTI_REQ: case DL_DISABMULTI_REQ: case DL_XID_REQ: case DL_TEST_REQ: case DL_REPLY_UPDATE_REQ: case DL_REPLY_REQ: case DL_DATA_ACK_REQ: #endif case DL_CONNECT_REQ: case DL_TOKEN_REQ: dlpi_error(q, us, d->dl_primitive, DL_NOTSUPPORTED, 0); break; case DL_CONNECT_RES: case DL_DISCONNECT_REQ: case DL_RESET_REQ: case DL_RESET_RES: dlpi_error(q, us, d->dl_primitive, DL_OUTSTATE, 0); break; case DL_UDQOS_REQ: dlpi_error(q, us, d->dl_primitive, DL_BADQOSTYPE, 0); break; #if DL_CURRENT_VERSION >= 2 case DL_TEST_RES: case DL_XID_RES: break; #endif default: cmn_err(CE_CONT, "ppp: unknown dlpi prim 0x%x\n", d->dl_primitive); /* fall through */ badprim: dlpi_error(q, us, d->dl_primitive, DL_BADPRIM, 0); break; } freemsg(mp); } static void dlpi_error(q, us, prim, err, uerr) queue_t *q; upperstr_t *us; int prim, err, uerr; { mblk_t *reply; dl_error_ack_t *errp; if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: dlpi error, prim=%x, err=%x\n", us->mn, prim, err); reply = allocb(sizeof(dl_error_ack_t), BPRI_HI); if (reply == 0) return; /* XXX should do bufcall */ reply->b_datap->db_type = M_PCPROTO; errp = (dl_error_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_error_ack_t); errp->dl_primitive = DL_ERROR_ACK; errp->dl_error_primitive = prim; errp->dl_errno = err; errp->dl_unix_errno = uerr; qreply(q, reply); } static void dlpi_ok(q, prim) queue_t *q; int prim; { mblk_t *reply; dl_ok_ack_t *okp; reply = allocb(sizeof(dl_ok_ack_t), BPRI_HI); if (reply == 0) return; /* XXX should do bufcall */ reply->b_datap->db_type = M_PCPROTO; okp = (dl_ok_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_ok_ack_t); okp->dl_primitive = DL_OK_ACK; okp->dl_correct_primitive = prim; qreply(q, reply); } #endif /* NO_DLPI */ static int pass_packet(us, mp, outbound) upperstr_t *us; mblk_t *mp; int outbound; { int pass; upperstr_t *ppa; if ((ppa = us->ppa) == 0) { freemsg(mp); return 0; } #ifdef FILTER_PACKETS pass = ip_hard_filter(us, mp, outbound); #else /* * Here is where we might, in future, decide whether to pass * or drop the packet, and whether it counts as link activity. */ pass = 1; #endif /* FILTER_PACKETS */ if (pass < 0) { /* pass only if link already up, and don't update time */ if (ppa->lowerq == 0) { freemsg(mp); return 0; } pass = 1; } else if (pass) { if (outbound) ppa->last_sent = time; else ppa->last_recv = time; } return pass; } /* * We have some data to send down to the lower stream (or up the * control stream, if we don't have a lower stream attached). * Returns 1 if the message was dealt with, 0 if it wasn't able * to be sent on and should therefore be queued up. */ static int send_data(mp, us) mblk_t *mp; upperstr_t *us; { upperstr_t *ppa; if ((us->flags & US_BLOCKED) || us->npmode == NPMODE_QUEUE) return 0; ppa = us->ppa; if (ppa == 0 || us->npmode == NPMODE_DROP || us->npmode == NPMODE_ERROR) { if (us->flags & US_DBGLOG) DPRINT2("ppp/%d: dropping pkt (npmode=%d)\n", us->mn, us->npmode); freemsg(mp); return 1; } if (ppa->lowerq == 0) { /* try to send it up the control stream */ if (bcanputnext(ppa->q, mp->b_band)) { /* * The message seems to get corrupted for some reason if * we just send the message up as it is, so we send a copy. */ mblk_t *np = copymsg(mp); freemsg(mp); if (np != 0) putnext(ppa->q, np); return 1; } } else { if (bcanputnext(ppa->lowerq, mp->b_band)) { MT_ENTER(&ppa->stats_lock); ppa->stats.ppp_opackets++; ppa->stats.ppp_obytes += msgdsize(mp); #ifdef INCR_OPACKETS INCR_OPACKETS(ppa); #endif MT_EXIT(&ppa->stats_lock); /* * The lower queue is only ever detached while holding an * exclusive lock on the whole driver. So we can be confident * that the lower queue is still there. */ putnext(ppa->lowerq, mp); return 1; } } us->flags |= US_BLOCKED; return 0; } /* * Allocate a new PPA id and link this stream into the list of PPAs. * This procedure is called with an exclusive lock on all queues in * this driver. */ static void new_ppa(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us, *up, **usp; int ppa_id; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("new_ppa: q_ptr = 0!\n"); return; } usp = &ppas; ppa_id = 0; while ((up = *usp) != 0 && ppa_id == up->ppa_id) { ++ppa_id; usp = &up->nextppa; } us->ppa_id = ppa_id; us->ppa = us; us->next = 0; us->nextppa = *usp; *usp = us; us->flags |= US_CONTROL; us->npmode = NPMODE_PASS; us->mtu = PPP_MTU; us->mru = PPP_MRU; #ifdef SOL2 /* * Create a kstats record for our statistics, so netstat -i works. */ if (us->kstats == 0) { char unit[32]; sprintf(unit, "ppp%d", us->ppa->ppa_id); us->kstats = kstat_create("ppp", us->ppa->ppa_id, unit, "net", KSTAT_TYPE_NAMED, 4, 0); if (us->kstats != 0) { kstat_named_t *kn = KSTAT_NAMED_PTR(us->kstats); strcpy(kn[0].name, "ipackets"); kn[0].data_type = KSTAT_DATA_ULONG; strcpy(kn[1].name, "ierrors"); kn[1].data_type = KSTAT_DATA_ULONG; strcpy(kn[2].name, "opackets"); kn[2].data_type = KSTAT_DATA_ULONG; strcpy(kn[3].name, "oerrors"); kn[3].data_type = KSTAT_DATA_ULONG; kstat_install(us->kstats); } } #endif /* SOL2 */ *(int *)mp->b_cont->b_rptr = ppa_id; mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } static void attach_ppa(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us, *t; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("attach_ppa: q_ptr = 0!\n"); return; } #ifndef NO_DLPI us->state = DL_UNBOUND; #endif for (t = us->ppa; t->next != 0; t = t->next) ; t->next = us; us->next = 0; if (mp->b_datap->db_type == M_IOCTL) { mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } else { #ifndef NO_DLPI dlpi_ok(q, DL_ATTACH_REQ); #endif } } static void detach_ppa(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us, *t; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("detach_ppa: q_ptr = 0!\n"); return; } for (t = us->ppa; t->next != 0; t = t->next) if (t->next == us) { t->next = us->next; break; } us->next = 0; us->ppa = 0; #ifndef NO_DLPI us->state = DL_UNATTACHED; dlpi_ok(q, DL_DETACH_REQ); #endif } /* * We call this with qwriter in order to give the upper queue procedures * the guarantee that the lower queue is not going to go away while * they are executing. */ static void detach_lower(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("detach_lower: q_ptr = 0!\n"); return; } LOCK_LOWER_W; us->lowerq->q_ptr = 0; RD(us->lowerq)->q_ptr = 0; us->lowerq = 0; UNLOCK_LOWER; /* Unblock streams which now feed back up the control stream. */ qenable(us->q); mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } static int pppuwsrv(q) queue_t *q; { upperstr_t *us, *as; mblk_t *mp; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("pppuwsrv: q_ptr = 0!\n"); return 0; } /* * If this is a control stream, then this service procedure * probably got enabled because of flow control in the lower * stream being enabled (or because of the lower stream going * away). Therefore we enable the service procedure of all * attached upper streams. */ if (us->flags & US_CONTROL) { for (as = us->next; as != 0; as = as->next) qenable(WR(as->q)); } /* Try to send on any data queued here. */ us->flags &= ~US_BLOCKED; while ((mp = getq(q)) != 0) { if (!send_data(mp, us)) { putbq(q, mp); break; } } return 0; } /* should never get called... */ static int ppplwput(q, mp) queue_t *q; mblk_t *mp; { putnext(q, mp); return 0; } static int ppplwsrv(q) queue_t *q; { queue_t *uq; /* * Flow control has back-enabled this stream: * enable the upper write service procedure for * the upper control stream for this lower stream. */ LOCK_LOWER_R; uq = (queue_t *) q->q_ptr; if (uq != 0) qenable(uq); UNLOCK_LOWER; return 0; } /* * This should only get called for control streams. */ static int pppurput(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *ppa, *us; int proto, len; struct iocblk *iop; ppa = (upperstr_t *) q->q_ptr; if (ppa == 0) { DPRINT("pppurput: q_ptr = 0!\n"); return 0; } switch (mp->b_datap->db_type) { case M_CTL: MT_ENTER(&ppa->stats_lock); switch (*mp->b_rptr) { case PPPCTL_IERROR: #ifdef INCR_IERRORS INCR_IERRORS(ppa); #endif ppa->stats.ppp_ierrors++; break; case PPPCTL_OERROR: #ifdef INCR_OERRORS INCR_OERRORS(ppa); #endif ppa->stats.ppp_oerrors++; break; } MT_EXIT(&ppa->stats_lock); freemsg(mp); break; case M_IOCACK: case M_IOCNAK: /* * Attempt to match up the response with the stream * that the request came from. */ iop = (struct iocblk *) mp->b_rptr; for (us = ppa; us != 0; us = us->next) if (us->ioc_id == iop->ioc_id) break; if (us == 0) freemsg(mp); else putnext(us->q, mp); break; case M_HANGUP: /* * The serial device has hung up. We don't want to send * the M_HANGUP message up to pppd because that will stop * us from using the control stream any more. Instead we * send a zero-length message as an end-of-file indication. */ freemsg(mp); mp = allocb(1, BPRI_HI); if (mp == 0) { DPRINT1("ppp/%d: couldn't allocate eof message!\n", ppa->mn); break; } putnext(ppa->q, mp); break; default: if (mp->b_datap->db_type == M_DATA) { len = msgdsize(mp); if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN) { PULLUP(mp, PPP_HDRLEN); if (mp == 0) { DPRINT1("ppp_urput: msgpullup failed (len=%d)\n", len); break; } } MT_ENTER(&ppa->stats_lock); ppa->stats.ppp_ipackets++; ppa->stats.ppp_ibytes += len; #ifdef INCR_IPACKETS INCR_IPACKETS(ppa); #endif MT_EXIT(&ppa->stats_lock); proto = PPP_PROTOCOL(mp->b_rptr); #if defined(SOL2) /* * Should there be any promiscuous stream(s), send the data * up for each promiscuous stream that we recognize. */ promisc_sendup(ppa, mp, proto, 1); #endif /* defined(SOL2) */ if (proto < 0x8000 && (us = find_dest(ppa, proto)) != 0) { /* * A data packet for some network protocol. * Queue it on the upper stream for that protocol. * XXX could we just putnext it? (would require thought) * The rblocked flag is there to ensure that we keep * messages in order for each network protocol. */ if (!pass_packet(us, mp, 0)) break; if (!us->rblocked && !canput(us->q)) us->rblocked = 1; if (!us->rblocked) putq(us->q, mp); else putq(q, mp); break; } } /* * A control frame, a frame for an unknown protocol, * or some other message type. * Send it up to pppd via the control stream. */ if (queclass(mp) == QPCTL || canputnext(ppa->q)) putnext(ppa->q, mp); else putq(q, mp); break; } return 0; } static int pppursrv(q) queue_t *q; { upperstr_t *us, *as; mblk_t *mp, *hdr; #ifndef NO_DLPI dl_unitdata_ind_t *ud; #endif int proto; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("pppursrv: q_ptr = 0!\n"); return 0; } if (us->flags & US_CONTROL) { /* * A control stream. * If there is no lower queue attached, run the write service * routines of other upper streams attached to this PPA. */ if (us->lowerq == 0) { as = us; do { if (as->flags & US_BLOCKED) qenable(WR(as->q)); as = as->next; } while (as != 0); } /* * Messages get queued on this stream's read queue if they * can't be queued on the read queue of the attached stream * that they are destined for. This is for flow control - * when this queue fills up, the lower read put procedure will * queue messages there and the flow control will propagate * down from there. */ while ((mp = getq(q)) != 0) { proto = PPP_PROTOCOL(mp->b_rptr); if (proto < 0x8000 && (as = find_dest(us, proto)) != 0) { if (!canput(as->q)) break; putq(as->q, mp); } else { if (!canputnext(q)) break; putnext(q, mp); } } if (mp) { putbq(q, mp); } else { /* can now put stuff directly on network protocol streams again */ for (as = us->next; as != 0; as = as->next) as->rblocked = 0; } /* * If this stream has a lower stream attached, * enable the read queue's service routine. * XXX we should really only do this if the queue length * has dropped below the low-water mark. */ if (us->lowerq != 0) qenable(RD(us->lowerq)); } else { /* * A network protocol stream. Put a DLPI header on each * packet and send it on. * (Actually, it seems that the IP module will happily * accept M_DATA messages without the DL_UNITDATA_IND header.) */ while ((mp = getq(q)) != 0) { if (!canputnext(q)) { putbq(q, mp); break; } #ifndef NO_DLPI proto = PPP_PROTOCOL(mp->b_rptr); mp->b_rptr += PPP_HDRLEN; hdr = allocb(sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint), BPRI_MED); if (hdr == 0) { /* XXX should put it back and use bufcall */ freemsg(mp); continue; } hdr->b_datap->db_type = M_PROTO; ud = (dl_unitdata_ind_t *) hdr->b_wptr; hdr->b_wptr += sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint); hdr->b_cont = mp; ud->dl_primitive = DL_UNITDATA_IND; ud->dl_dest_addr_length = sizeof(uint); ud->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t); ud->dl_src_addr_length = sizeof(uint); ud->dl_src_addr_offset = ud->dl_dest_addr_offset + sizeof(uint); #if DL_CURRENT_VERSION >= 2 ud->dl_group_address = 0; #endif /* Send the DLPI client the data with the SAP they requested, (e.g. ETHERTYPE_IP) rather than the PPP protocol number (e.g. PPP_IP) */ ((uint *)(ud + 1))[0] = us->req_sap; /* dest SAP */ ((uint *)(ud + 1))[1] = us->req_sap; /* src SAP */ putnext(q, hdr); #else /* NO_DLPI */ putnext(q, mp); #endif /* NO_DLPI */ } /* * Now that we have consumed some packets from this queue, * enable the control stream's read service routine so that we * can process any packets for us that might have got queued * there for flow control reasons. */ if (us->ppa) qenable(us->ppa->q); } return 0; } static upperstr_t * find_dest(ppa, proto) upperstr_t *ppa; int proto; { upperstr_t *us; for (us = ppa->next; us != 0; us = us->next) if (proto == us->sap) break; return us; } #if defined (SOL2) /* * Test upstream promiscuous conditions. As of now, only pass IPv4 and * Ipv6 packets upstream (let PPP packets be decoded elsewhere). */ static upperstr_t * find_promisc(us, proto) upperstr_t *us; int proto; { if ((proto != PPP_IP) && (proto != PPP_IPV6)) return (upperstr_t *)0; for ( ; us; us = us->next) { if ((us->flags & US_PROMISC) && (us->state == DL_IDLE)) return us; } return (upperstr_t *)0; } /* * Prepend an empty Ethernet header to msg for snoop, et al. */ static mblk_t * prepend_ether(us, mp, proto) upperstr_t *us; mblk_t *mp; int proto; { mblk_t *eh; int type; if ((eh = allocb(sizeof(struct ether_header), BPRI_HI)) == 0) { freemsg(mp); return (mblk_t *)0; } if (proto == PPP_IP) type = ETHERTYPE_IP; else if (proto == PPP_IPV6) type = ETHERTYPE_IPV6; else type = proto; /* What else? Let decoder decide */ eh->b_wptr += sizeof(struct ether_header); bzero((caddr_t)eh->b_rptr, sizeof(struct ether_header)); ((struct ether_header *)eh->b_rptr)->ether_type = htons((short)type); eh->b_cont = mp; return (eh); } /* * Prepend DL_UNITDATA_IND mblk to msg */ static mblk_t * prepend_udind(us, mp, proto) upperstr_t *us; mblk_t *mp; int proto; { dl_unitdata_ind_t *dlu; mblk_t *dh; size_t size; size = sizeof(dl_unitdata_ind_t); if ((dh = allocb(size, BPRI_MED)) == 0) { freemsg(mp); return (mblk_t *)0; } dh->b_datap->db_type = M_PROTO; dh->b_wptr = dh->b_datap->db_lim; dh->b_rptr = dh->b_wptr - size; dlu = (dl_unitdata_ind_t *)dh->b_rptr; dlu->dl_primitive = DL_UNITDATA_IND; dlu->dl_dest_addr_length = 0; dlu->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t); dlu->dl_src_addr_length = 0; dlu->dl_src_addr_offset = sizeof(dl_unitdata_ind_t); dlu->dl_group_address = 0; dh->b_cont = mp; return (dh); } /* * For any recognized promiscuous streams, send data upstream */ static void promisc_sendup(ppa, mp, proto, skip) upperstr_t *ppa; mblk_t *mp; int proto, skip; { mblk_t *dup_mp, *dup_dup_mp; upperstr_t *prus, *nprus; if ((prus = find_promisc(ppa, proto)) != 0) { if (dup_mp = dupmsg(mp)) { if (skip) dup_mp->b_rptr += PPP_HDRLEN; for ( ; nprus = find_promisc(prus->next, proto); prus = nprus) { if (dup_dup_mp = dupmsg(dup_mp)) { if (canputnext(prus->q)) { if (prus->flags & US_RAWDATA) { dup_dup_mp = prepend_ether(prus, dup_dup_mp, proto); putnext(prus->q, dup_dup_mp); } else { dup_dup_mp = prepend_udind(prus, dup_dup_mp, proto); putnext(prus->q, dup_dup_mp); } } else { DPRINT("ppp_urput: data to promisc q dropped\n"); freemsg(dup_dup_mp); } } } if (canputnext(prus->q)) { if (prus->flags & US_RAWDATA) { dup_mp = prepend_ether(prus, dup_mp, proto); putnext(prus->q, dup_mp); } else { dup_mp = prepend_udind(prus, dup_mp, proto); putnext(prus->q, dup_mp); } } else { DPRINT("ppp_urput: data to promisc q dropped\n"); freemsg(dup_mp); } } } } #endif /* defined(SOL2) */ /* * We simply put the message on to the associated upper control stream * (either here or in ppplrsrv). That way we enter the perimeters * before looking through the list of attached streams to decide which * stream it should go up. */ static int ppplrput(q, mp) queue_t *q; mblk_t *mp; { queue_t *uq; struct iocblk *iop; switch (mp->b_datap->db_type) { case M_IOCTL: iop = (struct iocblk *) mp->b_rptr; iop->ioc_error = EINVAL; mp->b_datap->db_type = M_IOCNAK; qreply(q, mp); return 0; case M_FLUSH: if (*mp->b_rptr & FLUSHR) flushq(q, FLUSHDATA); if (*mp->b_rptr & FLUSHW) { *mp->b_rptr &= ~FLUSHR; qreply(q, mp); } else freemsg(mp); return 0; } /* * If we can't get the lower lock straight away, queue this one * rather than blocking, to avoid the possibility of deadlock. */ if (!TRYLOCK_LOWER_R) { putq(q, mp); return 0; } /* * Check that we're still connected to the driver. */ uq = (queue_t *) q->q_ptr; if (uq == 0) { UNLOCK_LOWER; DPRINT1("ppplrput: q = %x, uq = 0??\n", q); freemsg(mp); return 0; } /* * Try to forward the message to the put routine for the upper * control stream for this lower stream. * If there are already messages queued here, queue this one so * they don't get out of order. */ if (queclass(mp) == QPCTL || (qsize(q) == 0 && canput(uq))) put(uq, mp); else putq(q, mp); UNLOCK_LOWER; return 0; } static int ppplrsrv(q) queue_t *q; { mblk_t *mp; queue_t *uq; /* * Packets get queued here for flow control reasons * or if the lrput routine couldn't get the lower lock * without blocking. */ LOCK_LOWER_R; uq = (queue_t *) q->q_ptr; if (uq == 0) { UNLOCK_LOWER; flushq(q, FLUSHALL); DPRINT1("ppplrsrv: q = %x, uq = 0??\n", q); return 0; } while ((mp = getq(q)) != 0) { if (queclass(mp) == QPCTL || canput(uq)) put(uq, mp); else { putbq(q, mp); break; } } UNLOCK_LOWER; return 0; } static int putctl2(q, type, code, val) queue_t *q; int type, code, val; { mblk_t *mp; mp = allocb(2, BPRI_HI); if (mp == 0) return 0; mp->b_datap->db_type = type; mp->b_wptr[0] = code; mp->b_wptr[1] = val; mp->b_wptr += 2; putnext(q, mp); return 1; } static int putctl4(q, type, code, val) queue_t *q; int type, code, val; { mblk_t *mp; mp = allocb(4, BPRI_HI); if (mp == 0) return 0; mp->b_datap->db_type = type; mp->b_wptr[0] = code; ((short *)mp->b_wptr)[1] = val; mp->b_wptr += 4; putnext(q, mp); return 1; } static void debug_dump(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us; queue_t *uq, *lq; DPRINT("ppp upper streams:\n"); for (us = minor_devs; us != 0; us = us->nextmn) { uq = us->q; DPRINT3(" %d: q=%x rlev=%d", us->mn, uq, (uq? qsize(uq): 0)); DPRINT3(" wlev=%d flags=0x%b", (uq? qsize(WR(uq)): 0), us->flags, "\020\1priv\2control\3blocked\4last"); DPRINT3(" state=%x sap=%x req_sap=%x", us->state, us->sap, us->req_sap); if (us->ppa == 0) DPRINT(" ppa=?\n"); else DPRINT1(" ppa=%d\n", us->ppa->ppa_id); if (us->flags & US_CONTROL) { lq = us->lowerq; DPRINT3(" control for %d lq=%x rlev=%d", us->ppa_id, lq, (lq? qsize(RD(lq)): 0)); DPRINT3(" wlev=%d mru=%d mtu=%d\n", (lq? qsize(lq): 0), us->mru, us->mtu); } } mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } #ifdef FILTER_PACKETS #include #include #include #include #define MAX_IPHDR 128 /* max TCP/IP header size */ /* The following table contains a hard-coded list of protocol/port pairs. * Any matching packets are either discarded unconditionally, or, * if ok_if_link_up is non-zero when a connection does not currently exist * (i.e., they go through if the connection is present, but never initiate * a dial-out). * This idea came from a post by dm@garage.uun.org (David Mazieres) */ static struct pktfilt_tab { int proto; u_short port; u_short ok_if_link_up; } pktfilt_tab[] = { { IPPROTO_UDP, 520, 1 }, /* RIP, ok to pass if link is up */ { IPPROTO_UDP, 123, 1 }, /* NTP, don't keep up the link for it */ { -1, 0, 0 } /* terminator entry has port == -1 */ }; static int ip_hard_filter(us, mp, outbound) upperstr_t *us; mblk_t *mp; int outbound; { struct ip *ip; struct pktfilt_tab *pft; mblk_t *temp_mp; int proto; int len, hlen; /* Note, the PPP header has already been pulled up in all cases */ proto = PPP_PROTOCOL(mp->b_rptr); if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: filter, proto=0x%x, out=%d\n", us->mn, proto, outbound); switch (proto) { case PPP_IP: if ((mp->b_wptr - mp->b_rptr) == PPP_HDRLEN && mp->b_cont != 0) { temp_mp = mp->b_cont; len = msgdsize(temp_mp); hlen = (len < MAX_IPHDR) ? len : MAX_IPHDR; PULLUP(temp_mp, hlen); if (temp_mp == 0) { DPRINT2("ppp/%d: filter, pullup next failed, len=%d\n", us->mn, hlen); mp->b_cont = 0; /* PULLUP() freed the rest */ freemsg(mp); return 0; } ip = (struct ip *)mp->b_cont->b_rptr; } else { len = msgdsize(mp); hlen = (len < (PPP_HDRLEN+MAX_IPHDR)) ? len : (PPP_HDRLEN+MAX_IPHDR); PULLUP(mp, hlen); if (mp == 0) { DPRINT2("ppp/%d: filter, pullup failed, len=%d\n", us->mn, hlen); return 0; } ip = (struct ip *)(mp->b_rptr + PPP_HDRLEN); } /* For IP traffic, certain packets (e.g., RIP) may be either * 1. ignored - dropped completely * 2. will not initiate a connection, but * will be passed if a connection is currently up. */ for (pft=pktfilt_tab; pft->proto != -1; pft++) { if (ip->ip_p == pft->proto) { switch(pft->proto) { case IPPROTO_UDP: if (((struct udphdr *) &((int *)ip)[ip->ip_hl])->uh_dport == htons(pft->port)) goto endfor; break; case IPPROTO_TCP: if (((struct tcphdr *) &((int *)ip)[ip->ip_hl])->th_dport == htons(pft->port)) goto endfor; break; } } } endfor: if (pft->proto != -1) { if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: found IP pkt, proto=0x%x (%d)\n", us->mn, pft->proto, pft->port); /* Discard if not connected, or if not pass_with_link_up */ /* else, if link is up let go by, but don't update time */ return pft->ok_if_link_up? -1: 0; } break; } /* end switch (proto) */ return 1; } #endif /* FILTER_PACKETS */ ppp-2.4.5/modules/ppp_ahdlc.c000066400000000000000000000546111130035057700160770ustar00rootroot00000000000000/* * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC. * * Re-written by Adi Masputra , based on * the original ppp_ahdlc.c * * Copyright (c) 2000 by Sun Microsystems, Inc. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation is hereby granted, provided that the above copyright * notice appears in all copies. * * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, * OR MODIFICATIONS. * * $Id: ppp_ahdlc.c,v 1.18 2002/12/06 09:49:15 paulus Exp $ */ /* * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX. */ #include #include #include #include #ifdef SVR4 #include #include #include #include #else #include #ifdef __osf__ #include #endif #endif /* SVR4 */ #include #include #include "ppp_mod.h" /* * Right now, mutex is only enabled for Solaris 2.x */ #if defined(SOL2) #define USE_MUTEX #endif /* SOL2 */ /* * intpointer_t and uintpointer_t are signed and unsigned integer types * large enough to hold any data pointer; that is, data pointers can be * assigned into or from these integer types without losing precision. * On recent Solaris releases, these types are defined in sys/int_types.h, * but not on SunOS 4.x or the earlier Solaris versions. */ #if defined(_LP64) || defined(_I32LPx) typedef long intpointer_t; typedef unsigned long uintpointer_t; #else typedef int intpointer_t; typedef unsigned int uintpointer_t; #endif MOD_OPEN_DECL(ahdlc_open); MOD_CLOSE_DECL(ahdlc_close); static int ahdlc_wput __P((queue_t *, mblk_t *)); static int ahdlc_rput __P((queue_t *, mblk_t *)); static void ahdlc_encode __P((queue_t *, mblk_t *)); static void ahdlc_decode __P((queue_t *, mblk_t *)); static int msg_byte __P((mblk_t *, unsigned int)); #if defined(SOL2) /* * Don't send HDLC start flag is last transmit is within 1.5 seconds - * FLAG_TIME is defined is microseconds */ #define FLAG_TIME 1500 #define ABS(x) (x >= 0 ? x : (-x)) #endif /* SOL2 */ /* * Extract byte i of message mp */ #define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \ msg_byte((mp), (i))) /* * Is this LCP packet one we have to transmit using LCP defaults? */ #define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7) /* * Standard STREAMS declarations */ static struct module_info minfo = { 0x7d23, "ppp_ahdl", 0, INFPSZ, 32768, 512 }; static struct qinit rinit = { ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL }; static struct qinit winit = { ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL }; #if defined(SVR4) && !defined(SOL2) int phdldevflag = 0; #define ppp_ahdlcinfo phdlinfo #endif /* defined(SVR4) && !defined(SOL2) */ struct streamtab ppp_ahdlcinfo = { &rinit, /* ptr to st_rdinit */ &winit, /* ptr to st_wrinit */ NULL, /* ptr to st_muxrinit */ NULL, /* ptr to st_muxwinit */ #if defined(SUNOS4) NULL /* ptr to ptr to st_modlist */ #endif /* SUNOS4 */ }; #if defined(SUNOS4) int ppp_ahdlc_count = 0; /* open counter */ #endif /* SUNOS4 */ /* * Per-stream state structure */ typedef struct ahdlc_state { #if defined(USE_MUTEX) kmutex_t lock; /* lock for this structure */ #endif /* USE_MUTEX */ int flags; /* link flags */ mblk_t *rx_buf; /* ptr to receive buffer */ int rx_buf_size; /* receive buffer size */ ushort_t infcs; /* calculated rx HDLC FCS */ u_int32_t xaccm[8]; /* 256-bit xmit ACCM */ u_int32_t raccm; /* 32-bit rcv ACCM */ int mtu; /* interface MTU */ int mru; /* link MRU */ int unit; /* current PPP unit number */ struct pppstat stats; /* statistic structure */ #if defined(SOL2) clock_t flag_time; /* time in usec between flags */ clock_t lbolt; /* last updated lbolt */ #endif /* SOL2 */ } ahdlc_state_t; /* * Values for flags */ #define ESCAPED 0x100 /* last saw escape char on input */ #define IFLUSH 0x200 /* flushing input due to error */ /* * RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. */ #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP) /* * FCS lookup table as calculated by genfcstab. */ static u_short fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; static u_int32_t paritytab[8] = { 0x96696996, 0x69969669, 0x69969669, 0x96696996, 0x69969669, 0x96696996, 0x96696996, 0x69969669 }; /* * STREAMS module open (entry) point */ MOD_OPEN(ahdlc_open) { ahdlc_state_t *state; /* * Return if it's already opened */ if (q->q_ptr) { return 0; } /* * This can only be opened as a module */ if (sflag != MODOPEN) { return 0; } state = (ahdlc_state_t *) ALLOC_NOSLEEP(sizeof(ahdlc_state_t)); if (state == 0) OPEN_ERROR(ENOSR); bzero((caddr_t) state, sizeof(ahdlc_state_t)); q->q_ptr = (caddr_t) state; WR(q)->q_ptr = (caddr_t) state; #if defined(USE_MUTEX) mutex_init(&state->lock, NULL, MUTEX_DEFAULT, NULL); mutex_enter(&state->lock); #endif /* USE_MUTEX */ state->xaccm[0] = ~0; /* escape 0x00 through 0x1f */ state->xaccm[3] = 0x60000000; /* escape 0x7d and 0x7e */ state->mru = PPP_MRU; /* default of 1500 bytes */ #if defined(SOL2) state->flag_time = drv_usectohz(FLAG_TIME); #endif /* SOL2 */ #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ #if defined(SUNOS4) ppp_ahdlc_count++; #endif /* SUNOS4 */ qprocson(q); return 0; } /* * STREAMS module close (exit) point */ MOD_CLOSE(ahdlc_close) { ahdlc_state_t *state; qprocsoff(q); state = (ahdlc_state_t *) q->q_ptr; if (state == 0) { DPRINT("state == 0 in ahdlc_close\n"); return 0; } #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ if (state->rx_buf != 0) { freemsg(state->rx_buf); state->rx_buf = 0; } #if defined(USE_MUTEX) mutex_exit(&state->lock); mutex_destroy(&state->lock); #endif /* USE_MUTEX */ FREE(q->q_ptr, sizeof(ahdlc_state_t)); q->q_ptr = NULL; OTHERQ(q)->q_ptr = NULL; #if defined(SUNOS4) if (ppp_ahdlc_count) ppp_ahdlc_count--; #endif /* SUNOS4 */ return 0; } /* * Write side put routine */ static int ahdlc_wput(q, mp) queue_t *q; mblk_t *mp; { ahdlc_state_t *state; struct iocblk *iop; int error; mblk_t *np; struct ppp_stats *psp; state = (ahdlc_state_t *) q->q_ptr; if (state == 0) { DPRINT("state == 0 in ahdlc_wput\n"); freemsg(mp); return 0; } switch (mp->b_datap->db_type) { case M_DATA: /* * A data packet - do character-stuffing and FCS, and * send it onwards. */ ahdlc_encode(q, mp); freemsg(mp); break; case M_IOCTL: iop = (struct iocblk *) mp->b_rptr; error = EINVAL; switch (iop->ioc_cmd) { case PPPIO_XACCM: if ((iop->ioc_count < sizeof(u_int32_t)) || (iop->ioc_count > sizeof(ext_accm))) { break; } if (mp->b_cont == 0) { DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit); break; } #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm, iop->ioc_count); state->xaccm[2] &= ~0x40000000; /* don't escape 0x5e */ state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */ #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ iop->ioc_count = 0; error = 0; break; case PPPIO_RACCM: if (iop->ioc_count != sizeof(u_int32_t)) break; if (mp->b_cont == 0) { DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit); break; } #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm, sizeof(u_int32_t)); #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ iop->ioc_count = 0; error = 0; break; case PPPIO_GCLEAN: np = allocb(sizeof(int), BPRI_HI); if (np == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = np; #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ *(int *)np->b_wptr = state->flags & RCV_FLAGS; #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ np->b_wptr += sizeof(int); iop->ioc_count = sizeof(int); error = 0; break; case PPPIO_GETSTAT: np = allocb(sizeof(struct ppp_stats), BPRI_HI); if (np == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = np; psp = (struct ppp_stats *) np->b_wptr; np->b_wptr += sizeof(struct ppp_stats); bzero((caddr_t)psp, sizeof(struct ppp_stats)); psp->p = state->stats; iop->ioc_count = sizeof(struct ppp_stats); error = 0; break; case PPPIO_LASTMOD: /* we knew this anyway */ error = 0; break; default: error = -1; break; } if (error < 0) putnext(q, mp); else if (error == 0) { mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } else { mp->b_datap->db_type = M_IOCNAK; iop->ioc_count = 0; iop->ioc_error = error; qreply(q, mp); } break; case M_CTL: switch (*mp->b_rptr) { case PPPCTL_MTU: #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ state->mtu = ((unsigned short *)mp->b_rptr)[1]; #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ freemsg(mp); break; case PPPCTL_MRU: #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ state->mru = ((unsigned short *)mp->b_rptr)[1]; #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ freemsg(mp); break; case PPPCTL_UNIT: #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ state->unit = mp->b_rptr[1]; #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ break; default: putnext(q, mp); } break; default: putnext(q, mp); } return 0; } /* * Read side put routine */ static int ahdlc_rput(q, mp) queue_t *q; mblk_t *mp; { ahdlc_state_t *state; state = (ahdlc_state_t *) q->q_ptr; if (state == 0) { DPRINT("state == 0 in ahdlc_rput\n"); freemsg(mp); return 0; } switch (mp->b_datap->db_type) { case M_DATA: ahdlc_decode(q, mp); break; case M_HANGUP: #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ if (state->rx_buf != 0) { /* XXX would like to send this up for debugging */ freemsg(state->rx_buf); state->rx_buf = 0; } state->flags = IFLUSH; #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ putnext(q, mp); break; default: putnext(q, mp); } return 0; } /* * Extract bit c from map m, to determine if c needs to be escaped */ #define IN_TX_MAP(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f))) static void ahdlc_encode(q, mp) queue_t *q; mblk_t *mp; { ahdlc_state_t *state; u_int32_t *xaccm, loc_xaccm[8]; ushort_t fcs; size_t outmp_len; mblk_t *outmp, *tmp; uchar_t *dp, fcs_val; int is_lcp, code; #if defined(SOL2) clock_t lbolt; #endif /* SOL2 */ if (msgdsize(mp) < 4) { return; } state = (ahdlc_state_t *)q->q_ptr; #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ /* * Allocate an output buffer large enough to handle a case where all * characters need to be escaped */ outmp_len = (msgdsize(mp) << 1) + /* input block x 2 */ (sizeof(fcs) << 2) + /* HDLC FCS x 4 */ (sizeof(uchar_t) << 1); /* HDLC flags x 2 */ outmp = allocb(outmp_len, BPRI_MED); if (outmp == NULL) { state->stats.ppp_oerrors++; #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR); return; } #if defined(SOL2) /* * Check if our last transmit happenned within flag_time, using * the system's LBOLT value in clock ticks */ if (drv_getparm(LBOLT, &lbolt) != -1) { if (ABS((clock_t)lbolt - state->lbolt) > state->flag_time) { *outmp->b_wptr++ = PPP_FLAG; } state->lbolt = lbolt; } else { *outmp->b_wptr++ = PPP_FLAG; } #else /* * If the driver below still has a message to process, skip the * HDLC flag, otherwise, put one in the beginning */ if (qsize(q->q_next) == 0) { *outmp->b_wptr++ = PPP_FLAG; } #endif /* * All control characters must be escaped for LCP packets with code * values between 1 (Conf-Req) and 7 (Code-Rej). */ is_lcp = ((MSG_BYTE(mp, 0) == PPP_ALLSTATIONS) && (MSG_BYTE(mp, 1) == PPP_UI) && (MSG_BYTE(mp, 2) == (PPP_LCP >> 8)) && (MSG_BYTE(mp, 3) == (PPP_LCP & 0xff)) && LCP_USE_DFLT(mp)); xaccm = state->xaccm; if (is_lcp) { bcopy((caddr_t)state->xaccm, (caddr_t)loc_xaccm, sizeof(loc_xaccm)); loc_xaccm[0] = ~0; /* force escape on 0x00 through 0x1f */ xaccm = loc_xaccm; } fcs = PPP_INITFCS; /* Initial FCS is 0xffff */ /* * Process this block and the rest (if any) attached to the this one */ for (tmp = mp; tmp; tmp = tmp->b_cont) { if (tmp->b_datap->db_type == M_DATA) { for (dp = tmp->b_rptr; dp < tmp->b_wptr; dp++) { fcs = PPP_FCS(fcs, *dp); if (IN_TX_MAP(*dp, xaccm)) { *outmp->b_wptr++ = PPP_ESCAPE; *outmp->b_wptr++ = *dp ^ PPP_TRANS; } else { *outmp->b_wptr++ = *dp; } } } else { continue; /* skip if db_type is something other than M_DATA */ } } /* * Append the HDLC FCS, making sure that escaping is done on any * necessary bytes */ fcs_val = (fcs ^ 0xffff) & 0xff; if (IN_TX_MAP(fcs_val, xaccm)) { *outmp->b_wptr++ = PPP_ESCAPE; *outmp->b_wptr++ = fcs_val ^ PPP_TRANS; } else { *outmp->b_wptr++ = fcs_val; } fcs_val = ((fcs ^ 0xffff) >> 8) & 0xff; if (IN_TX_MAP(fcs_val, xaccm)) { *outmp->b_wptr++ = PPP_ESCAPE; *outmp->b_wptr++ = fcs_val ^ PPP_TRANS; } else { *outmp->b_wptr++ = fcs_val; } /* * And finally, append the HDLC flag, and send it away */ *outmp->b_wptr++ = PPP_FLAG; state->stats.ppp_obytes += msgdsize(outmp); state->stats.ppp_opackets++; #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ putnext(q, outmp); return; } /* * Checks the 32-bit receive ACCM to see if the byte needs un-escaping */ #define IN_RX_MAP(c, m) ((((unsigned int) (uchar_t) (c)) < 0x20) && \ (m) & (1 << (c))) /* * Process received characters. */ static void ahdlc_decode(q, mp) queue_t *q; mblk_t *mp; { ahdlc_state_t *state; mblk_t *om; uchar_t *dp; state = (ahdlc_state_t *) q->q_ptr; #if defined(USE_MUTEX) mutex_enter(&state->lock); #endif /* USE_MUTEX */ state->stats.ppp_ibytes += msgdsize(mp); for (; mp != 0; om = mp->b_cont, freeb(mp), mp = om) for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) { /* * This should detect the lack of 8-bit communication channel * which is necessary for PPP to work. In addition, it also * checks on the parity. */ if (*dp & 0x80) state->flags |= RCV_B7_1; else state->flags |= RCV_B7_0; if (paritytab[*dp >> 5] & (1 << (*dp & 0x1f))) state->flags |= RCV_ODDP; else state->flags |= RCV_EVNP; /* * So we have a HDLC flag ... */ if (*dp == PPP_FLAG) { /* * If we think that it marks the beginning of the frame, * then continue to process the next octects */ if ((state->flags & IFLUSH) || (state->rx_buf == 0) || (msgdsize(state->rx_buf) == 0)) { state->flags &= ~IFLUSH; continue; } /* * We get here because the above condition isn't true, * in which case the HDLC flag was there to mark the end * of the frame (or so we think) */ om = state->rx_buf; if (state->infcs == PPP_GOODFCS) { state->stats.ppp_ipackets++; adjmsg(om, -PPP_FCSLEN); putnext(q, om); } else { DPRINT2("ppp%d: bad fcs (len=%d)\n", state->unit, msgdsize(state->rx_buf)); freemsg(state->rx_buf); state->flags &= ~(IFLUSH | ESCAPED); state->stats.ppp_ierrors++; putctl1(q->q_next, M_CTL, PPPCTL_IERROR); } state->rx_buf = 0; continue; } if (state->flags & IFLUSH) { continue; } /* * Allocate a receive buffer, large enough to store a frame (after * un-escaping) of at least 1500 octets. If MRU is negotiated to * be more than the default, then allocate that much. In addition, * we add an extra 32-bytes for a fudge factor */ if (state->rx_buf == 0) { state->rx_buf_size = (state->mru < PPP_MRU ? PPP_MRU : state->mru); state->rx_buf_size += (sizeof(u_int32_t) << 3); state->rx_buf = allocb(state->rx_buf_size, BPRI_MED); /* * If allocation fails, try again on the next frame */ if (state->rx_buf == 0) { state->flags |= IFLUSH; continue; } state->flags &= ~(IFLUSH | ESCAPED); state->infcs = PPP_INITFCS; } if (*dp == PPP_ESCAPE) { state->flags |= ESCAPED; continue; } /* * Make sure we un-escape the necessary characters, as well as the * ones in our receive async control character map */ if (state->flags & ESCAPED) { *dp ^= PPP_TRANS; state->flags &= ~ESCAPED; } else if (IN_RX_MAP(*dp, state->raccm)) continue; /* * Unless the peer lied to us about the negotiated MRU, we should * never get a frame which is too long. If it happens, toss it away * and grab the next incoming one */ if (msgdsize(state->rx_buf) < state->rx_buf_size) { state->infcs = PPP_FCS(state->infcs, *dp); *state->rx_buf->b_wptr++ = *dp; } else { DPRINT2("ppp%d: frame too long (%d)\n", state->unit, msgdsize(state->rx_buf)); freemsg(state->rx_buf); state->rx_buf = 0; state->flags |= IFLUSH; } } #if defined(USE_MUTEX) mutex_exit(&state->lock); #endif /* USE_MUTEX */ } static int msg_byte(mp, i) mblk_t *mp; unsigned int i; { while (mp != 0 && i >= mp->b_wptr - mp->b_rptr) mp = mp->b_cont; if (mp == 0) return -1; return mp->b_rptr[i]; } ppp-2.4.5/modules/ppp_comp.c000066400000000000000000000671151130035057700157650ustar00rootroot00000000000000/* * ppp_comp.c - STREAMS module for kernel-level compression and CCP support. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp_comp.c,v 1.14 2002/12/06 09:49:15 paulus Exp $ */ /* * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX. */ #include #include #include #include #ifdef SVR4 #include #include #include #else #include #ifdef __osf__ #include #endif #endif /* SVR4 */ #include #include #include "ppp_mod.h" #ifdef __osf__ #include #include #endif #include #include #include #include #define PACKETPTR mblk_t * #include MOD_OPEN_DECL(ppp_comp_open); MOD_CLOSE_DECL(ppp_comp_close); static int ppp_comp_rput __P((queue_t *, mblk_t *)); static int ppp_comp_rsrv __P((queue_t *)); static int ppp_comp_wput __P((queue_t *, mblk_t *)); static int ppp_comp_wsrv __P((queue_t *)); static void ppp_comp_ccp __P((queue_t *, mblk_t *, int)); static int msg_byte __P((mblk_t *, unsigned int)); /* Extract byte i of message mp. */ #define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \ msg_byte((mp), (i))) /* Is this LCP packet one we have to transmit using LCP defaults? */ #define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7) #define PPP_COMP_ID 0xbadf static struct module_info minfo = { #ifdef PRIOQ PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16512, 16384, #else PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16384, 4096, #endif }; static struct qinit r_init = { ppp_comp_rput, ppp_comp_rsrv, ppp_comp_open, ppp_comp_close, NULL, &minfo, NULL }; static struct qinit w_init = { ppp_comp_wput, ppp_comp_wsrv, NULL, NULL, NULL, &minfo, NULL }; #if defined(SVR4) && !defined(SOL2) int pcmpdevflag = 0; #define ppp_compinfo pcmpinfo #endif struct streamtab ppp_compinfo = { &r_init, &w_init, NULL, NULL }; int ppp_comp_count; /* number of module instances in use */ #ifdef __osf__ static void ppp_comp_alloc __P((comp_state_t *)); typedef struct memreq { unsigned char comp_opts[20]; int cmd; int thread_status; char *returned_mem; } memreq_t; #endif typedef struct comp_state { int flags; int mru; int mtu; int unit; struct compressor *xcomp; void *xstate; struct compressor *rcomp; void *rstate; struct vjcompress vj_comp; int vj_last_ierrors; struct pppstat stats; #ifdef __osf__ memreq_t memreq; thread_t thread; #endif } comp_state_t; #ifdef __osf__ extern task_t first_task; #endif /* Bits in flags are as defined in pppio.h. */ #define CCP_ERR (CCP_ERROR | CCP_FATALERROR) #define LAST_MOD 0x1000000 /* no ppp modules below us */ #define DBGLOG 0x2000000 /* log debugging stuff */ #define MAX_IPHDR 128 /* max TCP/IP header size */ #define MAX_VJHDR 20 /* max VJ compressed header size (?) */ #undef MIN /* just in case */ #define MIN(a, b) ((a) < (b)? (a): (b)) /* * List of compressors we know about. */ #if DO_BSD_COMPRESS extern struct compressor ppp_bsd_compress; #endif #if DO_DEFLATE extern struct compressor ppp_deflate, ppp_deflate_draft; #endif struct compressor *ppp_compressors[] = { #if DO_BSD_COMPRESS &ppp_bsd_compress, #endif #if DO_DEFLATE &ppp_deflate, &ppp_deflate_draft, #endif NULL }; /* * STREAMS module entry points. */ MOD_OPEN(ppp_comp_open) { comp_state_t *cp; #ifdef __osf__ thread_t thread; #endif if (q->q_ptr == NULL) { cp = (comp_state_t *) ALLOC_SLEEP(sizeof(comp_state_t)); if (cp == NULL) OPEN_ERROR(ENOSR); bzero((caddr_t)cp, sizeof(comp_state_t)); WR(q)->q_ptr = q->q_ptr = (caddr_t) cp; cp->mru = PPP_MRU; cp->mtu = PPP_MTU; cp->xstate = NULL; cp->rstate = NULL; vj_compress_init(&cp->vj_comp, -1); #ifdef __osf__ if (!(thread = kernel_thread_w_arg(first_task, ppp_comp_alloc, (void *)cp))) OPEN_ERROR(ENOSR); cp->thread = thread; #endif ++ppp_comp_count; qprocson(q); } return 0; } MOD_CLOSE(ppp_comp_close) { comp_state_t *cp; qprocsoff(q); cp = (comp_state_t *) q->q_ptr; if (cp != NULL) { if (cp->xstate != NULL) (*cp->xcomp->comp_free)(cp->xstate); if (cp->rstate != NULL) (*cp->rcomp->decomp_free)(cp->rstate); #ifdef __osf__ if (!cp->thread) printf("ppp_comp_close: NULL thread!\n"); else thread_terminate(cp->thread); #endif FREE(cp, sizeof(comp_state_t)); q->q_ptr = NULL; OTHERQ(q)->q_ptr = NULL; --ppp_comp_count; } return 0; } #ifdef __osf__ /* thread for calling back to a compressor's memory allocator * Needed for Digital UNIX since it's VM can't handle requests * for large amounts of memory without blocking. The thread * provides a context in which we can call a memory allocator * that may block. */ static void ppp_comp_alloc(comp_state_t *cp) { int len, cmd; unsigned char *compressor_options; thread_t thread; void *(*comp_allocator)(); #if defined(MAJOR_VERSION) && (MAJOR_VERSION <= 2) /* In 2.x and earlier the argument gets passed * in the thread structure itself. Yuck. */ thread = current_thread(); cp = thread->reply_port; thread->reply_port = PORT_NULL; #endif for (;;) { assert_wait((vm_offset_t)&cp->memreq.thread_status, TRUE); thread_block(); if (thread_should_halt(current_thread())) thread_halt_self(); cmd = cp->memreq.cmd; compressor_options = &cp->memreq.comp_opts[0]; len = compressor_options[1]; if (cmd == PPPIO_XCOMP) { cp->memreq.returned_mem = cp->xcomp->comp_alloc(compressor_options, len); if (!cp->memreq.returned_mem) { cp->memreq.thread_status = ENOSR; } else { cp->memreq.thread_status = 0; } } else { cp->memreq.returned_mem = cp->rcomp->decomp_alloc(compressor_options, len); if (!cp->memreq.returned_mem) { cp->memreq.thread_status = ENOSR; } else { cp->memreq.thread_status = 0; } } } } #endif /* __osf__ */ /* here's the deal with memory allocation under Digital UNIX. * Some other may also benefit from this... * We can't ask for huge chunks of memory in a context where * the caller can't be put to sleep (like, here.) The alloc * is likely to fail. Instead we do this: the first time we * get called, kick off a thread to do the allocation. Return * immediately to the caller with EAGAIN, as an indication that * they should send down the ioctl again. By the time the * second call comes in it's likely that the memory allocation * thread will have returned with the requested memory. We will * continue to return EAGAIN however until the thread has completed. * When it has, we return zero (and the memory) if the allocator * was successful and ENOSR otherwise. * * Callers of the RCOMP and XCOMP ioctls are encouraged (but not * required) to loop for some number of iterations with a small * delay in the loop body (for instance a 1/10-th second "sleep" * via select.) */ static int ppp_comp_wput(q, mp) queue_t *q; mblk_t *mp; { struct iocblk *iop; comp_state_t *cp; int error, len, n; int flags, mask; mblk_t *np; struct compressor **comp; struct ppp_stats *psp; struct ppp_comp_stats *csp; unsigned char *opt_data; int nxslots, nrslots; cp = (comp_state_t *) q->q_ptr; if (cp == 0) { DPRINT("cp == 0 in ppp_comp_wput\n"); freemsg(mp); return 0; } switch (mp->b_datap->db_type) { case M_DATA: putq(q, mp); break; case M_IOCTL: iop = (struct iocblk *) mp->b_rptr; error = EINVAL; switch (iop->ioc_cmd) { case PPPIO_CFLAGS: /* set/get CCP state */ if (iop->ioc_count != 2 * sizeof(int)) break; if (mp->b_cont == 0) { DPRINT1("ppp_comp_wput/%d: PPPIO_CFLAGS b_cont = 0!\n", cp->unit); break; } flags = ((int *) mp->b_cont->b_rptr)[0]; mask = ((int *) mp->b_cont->b_rptr)[1]; cp->flags = (cp->flags & ~mask) | (flags & mask); if ((mask & CCP_ISOPEN) && (flags & CCP_ISOPEN) == 0) { if (cp->xstate != NULL) { (*cp->xcomp->comp_free)(cp->xstate); cp->xstate = NULL; } if (cp->rstate != NULL) { (*cp->rcomp->decomp_free)(cp->rstate); cp->rstate = NULL; } cp->flags &= ~CCP_ISUP; } error = 0; iop->ioc_count = sizeof(int); ((int *) mp->b_cont->b_rptr)[0] = cp->flags; mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int); break; case PPPIO_VJINIT: /* * Initialize VJ compressor/decompressor */ if (iop->ioc_count != 2) break; if (mp->b_cont == 0) { DPRINT1("ppp_comp_wput/%d: PPPIO_VJINIT b_cont = 0!\n", cp->unit); break; } nxslots = mp->b_cont->b_rptr[0] + 1; nrslots = mp->b_cont->b_rptr[1] + 1; if (nxslots > MAX_STATES || nrslots > MAX_STATES) break; vj_compress_init(&cp->vj_comp, nxslots); cp->vj_last_ierrors = cp->stats.ppp_ierrors; error = 0; iop->ioc_count = 0; break; case PPPIO_XCOMP: case PPPIO_RCOMP: if (iop->ioc_count <= 0) break; if (mp->b_cont == 0) { DPRINT1("ppp_comp_wput/%d: PPPIO_[XR]COMP b_cont = 0!\n", cp->unit); break; } opt_data = mp->b_cont->b_rptr; len = mp->b_cont->b_wptr - opt_data; if (len > iop->ioc_count) len = iop->ioc_count; if (opt_data[1] < 2 || opt_data[1] > len) break; for (comp = ppp_compressors; *comp != NULL; ++comp) if ((*comp)->compress_proto == opt_data[0]) { /* here's the handler! */ error = 0; #ifndef __osf__ if (iop->ioc_cmd == PPPIO_XCOMP) { /* A previous call may have fetched memory for a compressor * that's now being retired or reset. Free it using it's * mechanism for freeing stuff. */ if (cp->xstate != NULL) { (*cp->xcomp->comp_free)(cp->xstate); cp->xstate = NULL; } cp->xcomp = *comp; cp->xstate = (*comp)->comp_alloc(opt_data, len); if (cp->xstate == NULL) error = ENOSR; } else { if (cp->rstate != NULL) { (*cp->rcomp->decomp_free)(cp->rstate); cp->rstate = NULL; } cp->rcomp = *comp; cp->rstate = (*comp)->decomp_alloc(opt_data, len); if (cp->rstate == NULL) error = ENOSR; } #else if ((error = cp->memreq.thread_status) != EAGAIN) if (iop->ioc_cmd == PPPIO_XCOMP) { if (cp->xstate) { (*cp->xcomp->comp_free)(cp->xstate); cp->xstate = 0; } /* sanity check for compressor options */ if (sizeof (cp->memreq.comp_opts) < len) { printf("can't handle options for compressor %d (%d)\n", opt_data[0], opt_data[1]); cp->memreq.thread_status = ENOSR; cp->memreq.returned_mem = 0; } /* fill in request for the thread and kick it off */ if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) { bcopy(opt_data, cp->memreq.comp_opts, len); cp->memreq.cmd = PPPIO_XCOMP; cp->xcomp = *comp; error = cp->memreq.thread_status = EAGAIN; thread_wakeup((vm_offset_t)&cp->memreq.thread_status); } else { cp->xstate = cp->memreq.returned_mem; cp->memreq.returned_mem = 0; cp->memreq.thread_status = 0; } } else { if (cp->rstate) { (*cp->rcomp->decomp_free)(cp->rstate); cp->rstate = NULL; } if (sizeof (cp->memreq.comp_opts) < len) { printf("can't handle options for compressor %d (%d)\n", opt_data[0], opt_data[1]); cp->memreq.thread_status = ENOSR; cp->memreq.returned_mem = 0; } if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) { bcopy(opt_data, cp->memreq.comp_opts, len); cp->memreq.cmd = PPPIO_RCOMP; cp->rcomp = *comp; error = cp->memreq.thread_status = EAGAIN; thread_wakeup((vm_offset_t)&cp->memreq.thread_status); } else { cp->rstate = cp->memreq.returned_mem; cp->memreq.returned_mem = 0; cp->memreq.thread_status = 0; } } #endif break; } iop->ioc_count = 0; break; case PPPIO_GETSTAT: if ((cp->flags & LAST_MOD) == 0) { error = -1; /* let the ppp_ahdl module handle it */ break; } np = allocb(sizeof(struct ppp_stats), BPRI_HI); if (np == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = np; psp = (struct ppp_stats *) np->b_wptr; np->b_wptr += sizeof(struct ppp_stats); iop->ioc_count = sizeof(struct ppp_stats); psp->p = cp->stats; psp->vj = cp->vj_comp.stats; error = 0; break; case PPPIO_GETCSTAT: np = allocb(sizeof(struct ppp_comp_stats), BPRI_HI); if (np == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = np; csp = (struct ppp_comp_stats *) np->b_wptr; np->b_wptr += sizeof(struct ppp_comp_stats); iop->ioc_count = sizeof(struct ppp_comp_stats); bzero((caddr_t)csp, sizeof(struct ppp_comp_stats)); if (cp->xstate != 0) (*cp->xcomp->comp_stat)(cp->xstate, &csp->c); if (cp->rstate != 0) (*cp->rcomp->decomp_stat)(cp->rstate, &csp->d); error = 0; break; case PPPIO_DEBUG: if (iop->ioc_count != sizeof(int)) break; if (mp->b_cont == 0) { DPRINT1("ppp_comp_wput/%d: PPPIO_DEBUG b_cont = 0!\n", cp->unit); break; } n = *(int *)mp->b_cont->b_rptr; if (n == PPPDBG_LOG + PPPDBG_COMP) { DPRINT1("ppp_comp%d: debug log enabled\n", cp->unit); cp->flags |= DBGLOG; error = 0; iop->ioc_count = 0; } else { error = -1; } break; case PPPIO_LASTMOD: cp->flags |= LAST_MOD; error = 0; break; default: error = -1; break; } if (error < 0) putnext(q, mp); else if (error == 0) { mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } else { mp->b_datap->db_type = M_IOCNAK; iop->ioc_error = error; iop->ioc_count = 0; qreply(q, mp); } break; case M_CTL: switch (*mp->b_rptr) { case PPPCTL_MTU: cp->mtu = ((unsigned short *)mp->b_rptr)[1]; break; case PPPCTL_MRU: cp->mru = ((unsigned short *)mp->b_rptr)[1]; break; case PPPCTL_UNIT: cp->unit = mp->b_rptr[1]; break; } putnext(q, mp); break; default: putnext(q, mp); } return 0; } static int ppp_comp_wsrv(q) queue_t *q; { mblk_t *mp, *cmp = NULL; comp_state_t *cp; int len, proto, type, hlen, code; struct ip *ip; unsigned char *vjhdr, *dp; cp = (comp_state_t *) q->q_ptr; if (cp == 0) { DPRINT("cp == 0 in ppp_comp_wsrv\n"); return 0; } while ((mp = getq(q)) != 0) { /* assert(mp->b_datap->db_type == M_DATA) */ #ifdef PRIOQ if (!bcanputnext(q,mp->b_band)) #else if (!canputnext(q)) #endif /* PRIOQ */ { putbq(q, mp); break; } /* * First check the packet length and work out what the protocol is. */ len = msgdsize(mp); if (len < PPP_HDRLEN) { DPRINT1("ppp_comp_wsrv: bogus short packet (%d)\n", len); freemsg(mp); cp->stats.ppp_oerrors++; putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR); continue; } proto = (MSG_BYTE(mp, 2) << 8) + MSG_BYTE(mp, 3); /* * Make sure we've got enough data in the first mblk * and that we are its only user. */ if (proto == PPP_CCP) hlen = len; else if (proto == PPP_IP) hlen = PPP_HDRLEN + MAX_IPHDR; else hlen = PPP_HDRLEN; if (hlen > len) hlen = len; if (mp->b_wptr < mp->b_rptr + hlen || mp->b_datap->db_ref > 1) { PULLUP(mp, hlen); if (mp == 0) { DPRINT1("ppp_comp_wsrv: pullup failed (%d)\n", hlen); cp->stats.ppp_oerrors++; putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR); continue; } } /* * Do VJ compression if requested. */ if (proto == PPP_IP && (cp->flags & COMP_VJC)) { ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN); if (ip->ip_p == IPPROTO_TCP) { type = vj_compress_tcp(ip, len - PPP_HDRLEN, &cp->vj_comp, (cp->flags & COMP_VJCCID), &vjhdr); switch (type) { case TYPE_UNCOMPRESSED_TCP: mp->b_rptr[3] = proto = PPP_VJC_UNCOMP; break; case TYPE_COMPRESSED_TCP: dp = vjhdr - PPP_HDRLEN; dp[1] = mp->b_rptr[1]; /* copy control field */ dp[0] = mp->b_rptr[0]; /* copy address field */ dp[2] = 0; /* set protocol field */ dp[3] = proto = PPP_VJC_COMP; mp->b_rptr = dp; break; } } } /* * Do packet compression if enabled. */ if (proto == PPP_CCP) ppp_comp_ccp(q, mp, 0); else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN) && cp->xstate != NULL) { len = msgdsize(mp); (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len, (cp->flags & CCP_ISUP? cp->mtu + PPP_HDRLEN: 0)); if (cmp != NULL) { #ifdef PRIOQ cmp->b_band=mp->b_band; #endif /* PRIOQ */ freemsg(mp); mp = cmp; } } /* * Do address/control and protocol compression if enabled. */ if ((cp->flags & COMP_AC) && !(proto == PPP_LCP && LCP_USE_DFLT(mp))) { mp->b_rptr += 2; /* drop the address & ctrl fields */ if (proto < 0x100 && (cp->flags & COMP_PROT)) ++mp->b_rptr; /* drop the high protocol byte */ } else if (proto < 0x100 && (cp->flags & COMP_PROT)) { /* shuffle up the address & ctrl fields */ mp->b_rptr[2] = mp->b_rptr[1]; mp->b_rptr[1] = mp->b_rptr[0]; ++mp->b_rptr; } cp->stats.ppp_opackets++; cp->stats.ppp_obytes += msgdsize(mp); putnext(q, mp); } return 0; } static int ppp_comp_rput(q, mp) queue_t *q; mblk_t *mp; { comp_state_t *cp; struct iocblk *iop; struct ppp_stats *psp; cp = (comp_state_t *) q->q_ptr; if (cp == 0) { DPRINT("cp == 0 in ppp_comp_rput\n"); freemsg(mp); return 0; } switch (mp->b_datap->db_type) { case M_DATA: putq(q, mp); break; case M_IOCACK: iop = (struct iocblk *) mp->b_rptr; switch (iop->ioc_cmd) { case PPPIO_GETSTAT: /* * Catch this on the way back from the ppp_ahdl module * so we can fill in the VJ stats. */ if (mp->b_cont == 0 || iop->ioc_count != sizeof(struct ppp_stats)) break; psp = (struct ppp_stats *) mp->b_cont->b_rptr; psp->vj = cp->vj_comp.stats; break; } putnext(q, mp); break; case M_CTL: switch (mp->b_rptr[0]) { case PPPCTL_IERROR: ++cp->stats.ppp_ierrors; break; case PPPCTL_OERROR: ++cp->stats.ppp_oerrors; break; } putnext(q, mp); break; default: putnext(q, mp); } return 0; } static int ppp_comp_rsrv(q) queue_t *q; { int proto, rv, i; mblk_t *mp, *dmp = NULL, *np; uchar_t *dp, *iphdr; comp_state_t *cp; int len, hlen, vjlen; u_int iphlen; cp = (comp_state_t *) q->q_ptr; if (cp == 0) { DPRINT("cp == 0 in ppp_comp_rsrv\n"); return 0; } while ((mp = getq(q)) != 0) { /* assert(mp->b_datap->db_type == M_DATA) */ if (!canputnext(q)) { putbq(q, mp); break; } len = msgdsize(mp); cp->stats.ppp_ibytes += len; cp->stats.ppp_ipackets++; /* * First work out the protocol and where the PPP header ends. */ i = 0; proto = MSG_BYTE(mp, 0); if (proto == PPP_ALLSTATIONS) { i = 2; proto = MSG_BYTE(mp, 2); } if ((proto & 1) == 0) { ++i; proto = (proto << 8) + MSG_BYTE(mp, i); } hlen = i + 1; /* * Now reconstruct a complete, contiguous PPP header at the * start of the packet. */ if (hlen < ((cp->flags & DECOMP_AC)? 0: 2) + ((cp->flags & DECOMP_PROT)? 1: 2)) { /* count these? */ goto bad; } if (mp->b_rptr + hlen > mp->b_wptr) { adjmsg(mp, hlen); /* XXX check this call */ hlen = 0; } if (hlen != PPP_HDRLEN) { /* * We need to put some bytes on the front of the packet * to make a full-length PPP header. * If we can put them in *mp, we do, otherwise we * tack another mblk on the front. * XXX we really shouldn't need to carry around * the address and control at this stage. */ dp = mp->b_rptr + hlen - PPP_HDRLEN; if (dp < mp->b_datap->db_base || mp->b_datap->db_ref > 1) { np = allocb(PPP_HDRLEN, BPRI_MED); if (np == 0) goto bad; np->b_cont = mp; mp->b_rptr += hlen; mp = np; dp = mp->b_wptr; mp->b_wptr += PPP_HDRLEN; } else mp->b_rptr = dp; dp[0] = PPP_ALLSTATIONS; dp[1] = PPP_UI; dp[2] = proto >> 8; dp[3] = proto; } /* * Now see if we have a compressed packet to decompress, * or a CCP packet to take notice of. */ proto = PPP_PROTOCOL(mp->b_rptr); if (proto == PPP_CCP) { len = msgdsize(mp); if (mp->b_wptr < mp->b_rptr + len) { PULLUP(mp, len); if (mp == 0) goto bad; } ppp_comp_ccp(q, mp, 1); } else if (proto == PPP_COMP) { if ((cp->flags & CCP_ISUP) && (cp->flags & CCP_DECOMP_RUN) && cp->rstate && (cp->flags & CCP_ERR) == 0) { rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp); switch (rv) { case DECOMP_OK: freemsg(mp); mp = dmp; if (mp == NULL) { /* no error, but no packet returned either. */ continue; } break; case DECOMP_ERROR: cp->flags |= CCP_ERROR; ++cp->stats.ppp_ierrors; putctl1(q->q_next, M_CTL, PPPCTL_IERROR); break; case DECOMP_FATALERROR: cp->flags |= CCP_FATALERROR; ++cp->stats.ppp_ierrors; putctl1(q->q_next, M_CTL, PPPCTL_IERROR); break; } } } else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) { (*cp->rcomp->incomp)(cp->rstate, mp); } /* * Now do VJ decompression. */ proto = PPP_PROTOCOL(mp->b_rptr); if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) { len = msgdsize(mp) - PPP_HDRLEN; if ((cp->flags & DECOMP_VJC) == 0 || len <= 0) goto bad; /* * Advance past the ppp header. * Here we assume that the whole PPP header is in the first mblk. */ np = mp; dp = np->b_rptr + PPP_HDRLEN; if (dp >= mp->b_wptr) { np = np->b_cont; dp = np->b_rptr; } /* * Make sure we have sufficient contiguous data at this point. */ hlen = (proto == PPP_VJC_COMP)? MAX_VJHDR: MAX_IPHDR; if (hlen > len) hlen = len; if (np->b_wptr < dp + hlen || np->b_datap->db_ref > 1) { PULLUP(mp, hlen + PPP_HDRLEN); if (mp == 0) goto bad; np = mp; dp = np->b_rptr + PPP_HDRLEN; } if (proto == PPP_VJC_COMP) { /* * Decompress VJ-compressed packet. * First reset compressor if an input error has occurred. */ if (cp->stats.ppp_ierrors != cp->vj_last_ierrors) { if (cp->flags & DBGLOG) DPRINT1("ppp%d: resetting VJ\n", cp->unit); vj_uncompress_err(&cp->vj_comp); cp->vj_last_ierrors = cp->stats.ppp_ierrors; } vjlen = vj_uncompress_tcp(dp, np->b_wptr - dp, len, &cp->vj_comp, &iphdr, &iphlen); if (vjlen < 0) { if (cp->flags & DBGLOG) DPRINT2("ppp%d: vj_uncomp_tcp failed, pkt len %d\n", cp->unit, len); ++cp->vj_last_ierrors; /* so we don't reset next time */ goto bad; } /* drop ppp and vj headers off */ if (mp != np) { freeb(mp); mp = np; } mp->b_rptr = dp + vjlen; /* allocate a new mblk for the ppp and ip headers */ if ((np = allocb(iphlen + PPP_HDRLEN + 4, BPRI_MED)) == 0) goto bad; dp = np->b_rptr; /* prepend mblk with TCP/IP hdr */ dp[0] = PPP_ALLSTATIONS; /* reconstruct PPP header */ dp[1] = PPP_UI; dp[2] = PPP_IP >> 8; dp[3] = PPP_IP; bcopy((caddr_t)iphdr, (caddr_t)dp + PPP_HDRLEN, iphlen); np->b_wptr = dp + iphlen + PPP_HDRLEN; np->b_cont = mp; /* XXX there seems to be a bug which causes panics in strread if we make an mbuf with only the IP header in it :-( */ if (mp->b_wptr - mp->b_rptr > 4) { bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, 4); mp->b_rptr += 4; np->b_wptr += 4; } else { bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, mp->b_wptr - mp->b_rptr); np->b_wptr += mp->b_wptr - mp->b_rptr; np->b_cont = mp->b_cont; freeb(mp); } mp = np; } else { /* * "Decompress" a VJ-uncompressed packet. */ cp->vj_last_ierrors = cp->stats.ppp_ierrors; if (!vj_uncompress_uncomp(dp, hlen, &cp->vj_comp)) { if (cp->flags & DBGLOG) DPRINT2("ppp%d: vj_uncomp_uncomp failed, pkt len %d\n", cp->unit, len); ++cp->vj_last_ierrors; /* don't need to reset next time */ goto bad; } mp->b_rptr[3] = PPP_IP; /* fix up the PPP protocol field */ } } putnext(q, mp); continue; bad: if (mp != 0) freemsg(mp); cp->stats.ppp_ierrors++; putctl1(q->q_next, M_CTL, PPPCTL_IERROR); } return 0; } /* * Handle a CCP packet being sent or received. * Here all the data in the packet is in a single mbuf. */ static void ppp_comp_ccp(q, mp, rcvd) queue_t *q; mblk_t *mp; int rcvd; { int len, clen; comp_state_t *cp; unsigned char *dp; len = msgdsize(mp); if (len < PPP_HDRLEN + CCP_HDRLEN) return; cp = (comp_state_t *) q->q_ptr; dp = mp->b_rptr + PPP_HDRLEN; len -= PPP_HDRLEN; clen = CCP_LENGTH(dp); if (clen > len) return; switch (CCP_CODE(dp)) { case CCP_CONFREQ: case CCP_TERMREQ: case CCP_TERMACK: cp->flags &= ~CCP_ISUP; break; case CCP_CONFACK: if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN && clen >= CCP_HDRLEN + CCP_OPT_MINLEN && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) { if (!rcvd) { if (cp->xstate != NULL && (*cp->xcomp->comp_init) (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN, cp->unit, 0, ((cp->flags & DBGLOG) != 0))) cp->flags |= CCP_COMP_RUN; } else { if (cp->rstate != NULL && (*cp->rcomp->decomp_init) (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN, cp->unit, 0, cp->mru, ((cp->flags & DBGLOG) != 0))) cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN; } } break; case CCP_RESETACK: if (cp->flags & CCP_ISUP) { if (!rcvd) { if (cp->xstate && (cp->flags & CCP_COMP_RUN)) (*cp->xcomp->comp_reset)(cp->xstate); } else { if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) { (*cp->rcomp->decomp_reset)(cp->rstate); cp->flags &= ~CCP_ERROR; } } } break; } } #if 0 dump_msg(mp) mblk_t *mp; { dblk_t *db; while (mp != 0) { db = mp->b_datap; DPRINT2("mp=%x cont=%x ", mp, mp->b_cont); DPRINT3("rptr=%x wptr=%x datap=%x\n", mp->b_rptr, mp->b_wptr, db); DPRINT2(" base=%x lim=%x", db->db_base, db->db_lim); DPRINT2(" ref=%d type=%d\n", db->db_ref, db->db_type); mp = mp->b_cont; } } #endif static int msg_byte(mp, i) mblk_t *mp; unsigned int i; { while (mp != 0 && i >= mp->b_wptr - mp->b_rptr) mp = mp->b_cont; if (mp == 0) return -1; return mp->b_rptr[i]; } ppp-2.4.5/modules/ppp_mod.h000066400000000000000000000116331130035057700156050ustar00rootroot00000000000000/* * Miscellaneous definitions for PPP STREAMS modules. */ /* * Macros for allocating and freeing kernel memory. */ #ifdef SVR4 /* SVR4, including Solaris 2 */ #include #define ALLOC_SLEEP(n) kmem_alloc((n), KM_SLEEP) #define ALLOC_NOSLEEP(n) kmem_alloc((n), KM_NOSLEEP) #define FREE(p, n) kmem_free((p), (n)) #endif #ifdef SUNOS4 #include /* SunOS 4.x */ #define ALLOC_SLEEP(n) kmem_alloc((n), KMEM_SLEEP) #define ALLOC_NOSLEEP(n) kmem_alloc((n), KMEM_NOSLEEP) #define FREE(p, n) kmem_free((p), (n)) #define NOTSUSER() (suser()? 0: EPERM) #define bcanputnext(q, band) canputnext((q)) #endif /* SunOS 4 */ #ifdef __osf__ #include /* caution: this mirrors macros in sys/malloc.h, and uses interfaces * which are subject to change. * The problems are that: * - the official MALLOC macro wants the lhs of the assignment as an argument, * and it takes care of the assignment itself (yuck.) * - PPP insists on using "FREE" which conflicts with a macro of the same name. * */ #ifdef BUCKETINDX /* V2.0 */ #define ALLOC_SLEEP(n) (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_WAITOK) #define ALLOC_NOSLEEP(n) (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_NOWAIT) #else #define ALLOC_SLEEP(n) (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_WAITOK) #define ALLOC_NOSLEEP(n) (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_NOWAIT) #endif #define bcanputnext(q, band) canputnext((q)) #ifdef FREE #undef FREE #endif #define FREE(p, n) free((void *)(p), M_DEVBUF) #define NO_DLPI 1 #ifndef IFT_PPP #define IFT_PPP 0x17 #endif #include #define NOTSUSER() (suser(u.u_procp->p_rcred, &u.u_acflag) ? EPERM : 0) /* #include "ppp_osf.h" */ #endif /* __osf__ */ #ifdef AIX4 #define ALLOC_SLEEP(n) xmalloc((n), 0, pinned_heap) /* AIX V4.x */ #define ALLOC_NOSLEEP(n) xmalloc((n), 0, pinned_heap) /* AIX V4.x */ #define FREE(p, n) xmfree((p), pinned_heap) #define NOTSUSER() (suser()? 0: EPERM) #endif /* AIX */ /* * Macros for printing debugging stuff. */ #ifdef DEBUG #if defined(SVR4) || defined(__osf__) #if defined(SNI) #include #define STRLOG_ID 4712 #define DPRINT(f) strlog(STRLOG_ID, 0, 0, SL_TRACE, f) #define DPRINT1(f, a1) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1) #define DPRINT2(f, a1, a2) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2) #define DPRINT3(f, a1, a2, a3) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2, a3) #else #define DPRINT(f) cmn_err(CE_CONT, f) #define DPRINT1(f, a1) cmn_err(CE_CONT, f, a1) #define DPRINT2(f, a1, a2) cmn_err(CE_CONT, f, a1, a2) #define DPRINT3(f, a1, a2, a3) cmn_err(CE_CONT, f, a1, a2, a3) #endif /* SNI */ #else #define DPRINT(f) printf(f) #define DPRINT1(f, a1) printf(f, a1) #define DPRINT2(f, a1, a2) printf(f, a1, a2) #define DPRINT3(f, a1, a2, a3) printf(f, a1, a2, a3) #endif /* SVR4 or OSF */ #else #define DPRINT(f) 0 #define DPRINT1(f, a1) 0 #define DPRINT2(f, a1, a2) 0 #define DPRINT3(f, a1, a2, a3) 0 #endif /* DEBUG */ #ifndef SVR4 typedef unsigned char uchar_t; typedef unsigned short ushort_t; #ifndef __osf__ typedef int minor_t; #endif #endif /* * If we don't have multithreading support, define substitutes. */ #ifndef D_MP # define qprocson(q) # define qprocsoff(q) # define put(q, mp) ((*(q)->q_qinfo->qi_putp)((q), (mp))) # define canputnext(q) canput((q)->q_next) # define qwriter(q, mp, func, scope) (func)((q), (mp)) #endif #ifdef D_MP /* Use msgpullup if we have other multithreading support. */ #define PULLUP(mp, len) \ do { \ mblk_t *np = msgpullup((mp), (len)); \ freemsg((mp)); \ mp = np; \ } while (0) #else /* Use pullupmsg if we don't have any multithreading support. */ #define PULLUP(mp, len) \ do { \ if (!pullupmsg((mp), (len))) { \ freemsg((mp)); \ mp = 0; \ } \ } while (0) #endif /* * How to declare the open and close procedures for a module. */ #ifdef SVR4 #define MOD_OPEN_DECL(name) \ static int name __P((queue_t *, dev_t *, int, int, cred_t *)) #define MOD_CLOSE_DECL(name) \ static int name __P((queue_t *, int, cred_t *)) #define MOD_OPEN(name) \ static int name(q, devp, flag, sflag, credp) \ queue_t *q; \ dev_t *devp; \ int flag, sflag; \ cred_t *credp; #define MOD_CLOSE(name) \ static int name(q, flag, credp) \ queue_t *q; \ int flag; \ cred_t *credp; #define OPEN_ERROR(x) return (x) #define DRV_OPEN_OK(dev) return 0 #define NOTSUSER() (drv_priv(credp)) #else /* not SVR4 */ #define MOD_OPEN_DECL(name) \ static int name __P((queue_t *, int, int, int)) #define MOD_CLOSE_DECL(name) \ static int name __P((queue_t *, int)) #define MOD_OPEN(name) \ static int name(q, dev, flag, sflag) \ queue_t *q; \ int dev; \ int flag, sflag; #define MOD_CLOSE(name) \ static int name(q, flag) \ queue_t *q; \ int flag; #define OPEN_ERROR(x) { u.u_error = (x); return OPENFAIL; } #define DRV_OPEN_OK(dev) return (dev) #endif /* SVR4 */ ppp-2.4.5/modules/vjcompress.c000066400000000000000000000376301130035057700163420ustar00rootroot00000000000000/* * Routines to compress and uncompess tcp packets (for transmission * over low speed serial lines. * * Copyright (c) 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: * - Initial distribution. * * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, * so that the entire packet being decompressed doesn't have * to be in contiguous memory (just the compressed header). */ /* * This version is used under SunOS 4.x, Digital UNIX, AIX 4.x, * and SVR4 systems including Solaris 2. * * $Id: vjcompress.c,v 1.11 2004/01/17 05:47:55 carlsonj Exp $ */ #include #include #ifdef SVR4 #ifndef __GNUC__ #include /* for ntohl, etc. */ #else /* make sure we don't get the gnu "fixed" one! */ #include "/usr/include/sys/byteorder.h" #endif #endif #ifdef __osf__ #include #endif #include #ifdef AIX4 #define _NETINET_IN_SYSTM_H_ typedef u_long n_long; #else #include #endif #ifdef SOL2 #include #endif #include #include #include #include #ifndef VJ_NO_STATS #define INCR(counter) ++comp->stats.counter #else #define INCR(counter) #endif #define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n)) #undef BCOPY #define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n)) #ifndef KERNEL #define ovbcopy bcopy #endif #ifdef __osf__ #define getip_hl(base) (((base).ip_vhl)&0xf) #define getth_off(base) ((((base).th_xoff)&0xf0)>>4) #else #define getip_hl(base) ((base).ip_hl) #define getth_off(base) ((base).th_off) #endif void vj_compress_init(comp, max_state) struct vjcompress *comp; int max_state; { register u_int i; register struct cstate *tstate = comp->tstate; if (max_state == -1) max_state = MAX_STATES - 1; bzero((char *)comp, sizeof(*comp)); for (i = max_state; i > 0; --i) { tstate[i].cs_id = i; tstate[i].cs_next = &tstate[i - 1]; } tstate[0].cs_next = &tstate[max_state]; tstate[0].cs_id = 0; comp->last_cs = &tstate[0]; comp->last_recv = 255; comp->last_xmit = 255; comp->flags = VJF_TOSS; } /* ENCODE encodes a number that is known to be non-zero. ENCODEZ * checks for zero (since zero has to be encoded in the long, 3 byte * form). */ #define ENCODE(n) { \ if ((u_short)(n) >= 256) { \ *cp++ = 0; \ cp[1] = (n); \ cp[0] = (n) >> 8; \ cp += 2; \ } else { \ *cp++ = (n); \ } \ } #define ENCODEZ(n) { \ if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ *cp++ = 0; \ cp[1] = (n); \ cp[0] = (n) >> 8; \ cp += 2; \ } else { \ *cp++ = (n); \ } \ } #define DECODEL(f) { \ if (*cp == 0) {\ u_int32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \ (f) = htonl(tmp); \ cp += 3; \ } else { \ u_int32_t tmp = ntohl(f) + (u_int32_t)*cp++; \ (f) = htonl(tmp); \ } \ } #define DECODES(f) { \ if (*cp == 0) {\ u_short tmp = ntohs(f) + ((cp[1] << 8) | cp[2]); \ (f) = htons(tmp); \ cp += 3; \ } else { \ u_short tmp = ntohs(f) + (u_int32_t)*cp++; \ (f) = htons(tmp); \ } \ } #define DECODEU(f) { \ if (*cp == 0) {\ (f) = htons((cp[1] << 8) | cp[2]); \ cp += 3; \ } else { \ (f) = htons((u_int32_t)*cp++); \ } \ } u_int vj_compress_tcp(ip, mlen, comp, compress_cid, vjhdrp) register struct ip *ip; u_int mlen; struct vjcompress *comp; int compress_cid; u_char **vjhdrp; { register struct cstate *cs = comp->last_cs->cs_next; register u_int hlen = getip_hl(*ip); register struct tcphdr *oth; register struct tcphdr *th; register u_int deltaS, deltaA; register u_int changes = 0; u_char new_seq[16]; register u_char *cp = new_seq; /* * Bail if this is an IP fragment or if the TCP packet isn't * `compressible' (i.e., ACK isn't set or some other control bit is * set). (We assume that the caller has already made sure the * packet is IP proto TCP). */ if ((ip->ip_off & htons(0x3fff)) || mlen < 40) return (TYPE_IP); th = (struct tcphdr *)&((int *)ip)[hlen]; if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK) return (TYPE_IP); /* * Packet is compressible -- we're going to send either a * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need * to locate (or create) the connection state. Special case the * most recently used connection since it's most likely to be used * again & we don't have to do any reordering if it's used. */ INCR(vjs_packets); if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr || ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr || *(int *)th != ((int *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) { /* * Wasn't the first -- search for it. * * States are kept in a circularly linked list with * last_cs pointing to the end of the list. The * list is kept in lru order by moving a state to the * head of the list whenever it is referenced. Since * the list is short and, empirically, the connection * we want is almost always near the front, we locate * states via linear search. If we don't find a state * for the datagram, the oldest state is (re-)used. */ register struct cstate *lcs; register struct cstate *lastcs = comp->last_cs; do { lcs = cs; cs = cs->cs_next; INCR(vjs_searches); if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr && *(int *)th == ((int *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) goto found; } while (cs != lastcs); /* * Didn't find it -- re-use oldest cstate. Send an * uncompressed packet that tells the other side what * connection number we're using for this conversation. * Note that since the state list is circular, the oldest * state points to the newest and we only need to set * last_cs to update the lru linkage. */ INCR(vjs_misses); comp->last_cs = lcs; hlen += getth_off(*th); hlen <<= 2; if (hlen > mlen) return (TYPE_IP); goto uncompressed; found: /* * Found it -- move to the front on the connection list. */ if (cs == lastcs) comp->last_cs = lcs; else { lcs->cs_next = cs->cs_next; cs->cs_next = lastcs->cs_next; lastcs->cs_next = cs; } } /* * Make sure that only what we expect to change changed. The first * line of the `if' checks the IP protocol version, header length & * type of service. The 2nd line checks the "Don't fragment" bit. * The 3rd line checks the time-to-live and protocol (the protocol * check is unnecessary but costless). The 4th line checks the TCP * header length. The 5th line checks IP options, if any. The 6th * line checks TCP options, if any. If any of these things are * different between the previous & current datagram, we send the * current datagram `uncompressed'. */ oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen]; deltaS = hlen; hlen += getth_off(*th); hlen <<= 2; if (hlen > mlen) return (TYPE_IP); if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] || getth_off(*th) != getth_off(*oth) || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) || (getth_off(*th) > 5 && BCMP(th + 1, oth + 1, (getth_off(*th) - 5) << 2))) goto uncompressed; /* * Figure out which of the changing fields changed. The * receiver expects changes in the order: urgent, window, * ack, seq (the order minimizes the number of temporaries * needed in this section of code). */ if (th->th_flags & TH_URG) { deltaS = ntohs(th->th_urp); ENCODEZ(deltaS); changes |= NEW_U; } else if (th->th_urp != oth->th_urp) /* argh! URG not set but urp changed -- a sensible * implementation should never do this but RFC793 * doesn't prohibit the change so we have to deal * with it. */ goto uncompressed; if ((deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) > 0) { ENCODE(deltaS); changes |= NEW_W; } if ((deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) > 0) { if (deltaA > 0xffff) goto uncompressed; ENCODE(deltaA); changes |= NEW_A; } if ((deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) > 0) { if (deltaS > 0xffff) goto uncompressed; ENCODE(deltaS); changes |= NEW_S; } switch(changes) { case 0: /* * Nothing changed. If this packet contains data and the * last one didn't, this is probably a data packet following * an ack (normal on an interactive connection) and we send * it compressed. Otherwise it's probably a retransmit, * retransmitted ack or window probe. Send it uncompressed * in case the other side missed the compressed version. */ if (ip->ip_len != cs->cs_ip.ip_len && ntohs(cs->cs_ip.ip_len) == hlen) break; /* (fall through) */ case SPECIAL_I: case SPECIAL_D: /* * actual changes match one of our special case encodings -- * send packet uncompressed. */ goto uncompressed; case NEW_S|NEW_A: if (deltaS == deltaA && deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { /* special case for echoed terminal traffic */ changes = SPECIAL_I; cp = new_seq; } break; case NEW_S: if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { /* special case for data xfer */ changes = SPECIAL_D; cp = new_seq; } break; } deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id); if (deltaS != 1) { ENCODEZ(deltaS); changes |= NEW_I; } if (th->th_flags & TH_PUSH) changes |= TCP_PUSH_BIT; /* * Grab the cksum before we overwrite it below. Then update our * state with this packet's header. */ deltaA = ntohs(th->th_sum); BCOPY(ip, &cs->cs_ip, hlen); /* * We want to use the original packet as our compressed packet. * (cp - new_seq) is the number of bytes we need for compressed * sequence numbers. In addition we need one byte for the change * mask, one for the connection id and two for the tcp checksum. * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how * many bytes of the original packet to toss so subtract the two to * get the new packet size. */ deltaS = cp - new_seq; cp = (u_char *)ip; if (compress_cid == 0 || comp->last_xmit != cs->cs_id) { comp->last_xmit = cs->cs_id; hlen -= deltaS + 4; *vjhdrp = (cp += hlen); *cp++ = changes | NEW_C; *cp++ = cs->cs_id; } else { hlen -= deltaS + 3; *vjhdrp = (cp += hlen); *cp++ = changes; } *cp++ = deltaA >> 8; *cp++ = deltaA; BCOPY(new_seq, cp, deltaS); INCR(vjs_compressed); return (TYPE_COMPRESSED_TCP); /* * Update connection state cs & send uncompressed packet (that is, * a regular ip/tcp packet but with the 'conversation id' we hope * to use on future compressed packets in the protocol field). */ uncompressed: BCOPY(ip, &cs->cs_ip, hlen); ip->ip_p = cs->cs_id; comp->last_xmit = cs->cs_id; return (TYPE_UNCOMPRESSED_TCP); } /* * Called when we may have missed a packet. */ void vj_uncompress_err(comp) struct vjcompress *comp; { comp->flags |= VJF_TOSS; INCR(vjs_errorin); } /* * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. */ int vj_uncompress_uncomp(buf, buflen, comp) u_char *buf; int buflen; struct vjcompress *comp; { register u_int hlen; register struct cstate *cs; register struct ip *ip; ip = (struct ip *) buf; hlen = getip_hl(*ip) << 2; if (ip->ip_p >= MAX_STATES || hlen + sizeof(struct tcphdr) > buflen || (hlen += getth_off(*((struct tcphdr *)&((char *)ip)[hlen])) << 2) > buflen || hlen > MAX_HDR) { comp->flags |= VJF_TOSS; INCR(vjs_errorin); return (0); } cs = &comp->rstate[comp->last_recv = ip->ip_p]; comp->flags &=~ VJF_TOSS; ip->ip_p = IPPROTO_TCP; BCOPY(ip, &cs->cs_ip, hlen); cs->cs_hlen = hlen; INCR(vjs_uncompressedin); return (1); } /* * Uncompress a packet of type TYPE_COMPRESSED_TCP. * The packet starts at buf and is of total length total_len. * The first buflen bytes are at buf; this must include the entire * compressed TCP/IP header. This procedure returns the length * of the VJ header, with a pointer to the uncompressed IP header * in *hdrp and its length in *hlenp. */ int vj_uncompress_tcp(buf, buflen, total_len, comp, hdrp, hlenp) u_char *buf; int buflen, total_len; struct vjcompress *comp; u_char **hdrp; u_int *hlenp; { register u_char *cp; register u_int hlen, changes; register struct tcphdr *th; register struct cstate *cs; register u_short *bp; register u_int vjlen; register u_int32_t tmp; INCR(vjs_compressedin); cp = buf; changes = *cp++; if (changes & NEW_C) { /* Make sure the state index is in range, then grab the state. * If we have a good state index, clear the 'discard' flag. */ if (*cp >= MAX_STATES) goto bad; comp->flags &=~ VJF_TOSS; comp->last_recv = *cp++; } else { /* this packet has an implicit state index. If we've * had a line error since the last time we got an * explicit state index, we have to toss the packet. */ if (comp->flags & VJF_TOSS) { INCR(vjs_tossed); return (-1); } } cs = &comp->rstate[comp->last_recv]; hlen = getip_hl(cs->cs_ip) << 2; th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; th->th_sum = htons((*cp << 8) | cp[1]); cp += 2; if (changes & TCP_PUSH_BIT) th->th_flags |= TH_PUSH; else th->th_flags &=~ TH_PUSH; switch (changes & SPECIALS_MASK) { case SPECIAL_I: { register u_int32_t i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; /* some compilers can't nest inline assembler.. */ tmp = ntohl(th->th_ack) + i; th->th_ack = htonl(tmp); tmp = ntohl(th->th_seq) + i; th->th_seq = htonl(tmp); } break; case SPECIAL_D: /* some compilers can't nest inline assembler.. */ tmp = ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; th->th_seq = htonl(tmp); break; default: if (changes & NEW_U) { th->th_flags |= TH_URG; DECODEU(th->th_urp); } else th->th_flags &=~ TH_URG; if (changes & NEW_W) DECODES(th->th_win); if (changes & NEW_A) DECODEL(th->th_ack); if (changes & NEW_S) DECODEL(th->th_seq); break; } if (changes & NEW_I) { DECODES(cs->cs_ip.ip_id); } else { cs->cs_ip.ip_id = ntohs(cs->cs_ip.ip_id) + 1; cs->cs_ip.ip_id = htons(cs->cs_ip.ip_id); } /* * At this point, cp points to the first byte of data in the * packet. Fill in the IP total length and update the IP * header checksum. */ vjlen = cp - buf; buflen -= vjlen; if (buflen < 0) /* we must have dropped some characters (crc should detect * this but the old slip framing won't) */ goto bad; total_len += cs->cs_hlen - vjlen; cs->cs_ip.ip_len = htons(total_len); /* recompute the ip header checksum */ bp = (u_short *) &cs->cs_ip; cs->cs_ip.ip_sum = 0; for (changes = 0; hlen > 0; hlen -= 2) changes += *bp++; changes = (changes & 0xffff) + (changes >> 16); changes = (changes & 0xffff) + (changes >> 16); cs->cs_ip.ip_sum = ~ changes; *hdrp = (u_char *) &cs->cs_ip; *hlenp = cs->cs_hlen; return vjlen; bad: comp->flags |= VJF_TOSS; INCR(vjs_errorin); return (-1); } ppp-2.4.5/pppd/000077500000000000000000000000001130035057700132655ustar00rootroot00000000000000ppp-2.4.5/pppd/.gitignore000066400000000000000000000000051130035057700152500ustar00rootroot00000000000000pppd ppp-2.4.5/pppd/Makefile.linux000066400000000000000000000116331130035057700160670ustar00rootroot00000000000000# # pppd makefile for Linux # $Id: Makefile.linux,v 1.70 2007/06/19 02:08:34 carlsonj Exp $ # # Default installation locations DESTDIR = $(INSTROOT)@DESTDIR@ BINDIR = $(DESTDIR)/sbin MANDIR = $(DESTDIR)/share/man/man8 INCDIR = $(DESTDIR)/include TARGETS = pppd PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap-new.c md5.c ccp.c \ ecp.c ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c \ demand.c utils.c tty.c eap.c chap-md5.c session.c HEADERS = ccp.h session.h chap-new.h ecp.h fsm.h ipcp.h \ ipxcp.h lcp.h magic.h md5.h patchlevel.h pathnames.h pppd.h \ upap.h eap.h MANPAGES = pppd.8 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o md5.o ccp.o \ ecp.o auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o \ eap.o chap-md5.o session.o # # include dependencies if present ifeq (.depend,$(wildcard .depend)) include .depend endif # CC = gcc # COPTS = -O2 -pipe -Wall -g LIBS = # Uncomment the next 2 lines to include support for Microsoft's # MS-CHAP authentication protocol. Also, edit plugins/radius/Makefile.linux. CHAPMS=y USE_CRYPT=y # Don't use MSLANMAN unless you really know what you're doing. #MSLANMAN=y # Uncomment the next line to include support for MPPE. CHAPMS (above) must # also be enabled. Also, edit plugins/radius/Makefile.linux. MPPE=y # Uncomment the next line to include support for PPP packet filtering. # This requires that the libpcap library and headers be installed # and that the kernel driver support PPP packet filtering. FILTER=y # Uncomment the next line to enable multilink PPP (enabled by default) # Linux distributions: Please leave multilink ENABLED in your builds # of pppd! HAVE_MULTILINK=y # Uncomment the next line to enable the TDB database (enabled by default.) # If you enable multilink, then TDB is automatically enabled also. # Linux distributions: Please leave TDB ENABLED in your builds. USE_TDB=y HAS_SHADOW=y #USE_PAM=y #HAVE_INET6=y # Enable plugins PLUGIN=y # Enable Microsoft proprietary Callback Control Protocol #CBCP=y # Enable EAP SRP-SHA1 authentication (requires libsrp) #USE_SRP=y MAXOCTETS=y INCLUDE_DIRS= -I../include COMPILE_FLAGS= -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MMAP CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) '-DDESTDIR="@DESTDIR@"' ifdef CHAPMS CFLAGS += -DCHAPMS=1 NEEDDES=y PPPDOBJS += md4.o chap_ms.o HEADERS += md4.h chap_ms.h ifdef MSLANMAN CFLAGS += -DMSLANMAN=1 endif ifdef MPPE CFLAGS += -DMPPE=1 endif endif # EAP SRP-SHA1 ifdef USE_SRP CFLAGS += -DUSE_SRP -DOPENSSL -I/usr/local/ssl/include LIBS += -lsrp -L/usr/local/ssl/lib -lcrypto TARGETS += srp-entry EXTRAINSTALL = $(INSTALL) -s -c -m 555 srp-entry $(BINDIR)/srp-entry MANPAGES += srp-entry.8 EXTRACLEAN += srp-entry.o NEEDDES=y else # OpenSSL has an integrated version of SHA-1, and its implementation # is incompatible with this local SHA-1 implementation. We must use # one or the other, not both. PPPDSRCS += sha1.c HEADERS += sha1.h PPPDOBJS += sha1.o endif ifdef HAS_SHADOW CFLAGS += -DHAS_SHADOW #LIBS += -lshadow $(LIBS) endif ifneq ($(wildcard /usr/include/crypt.h),) CFLAGS += -DHAVE_CRYPT_H=1 LIBS += -lcrypt endif ifdef NEEDDES ifndef USE_CRYPT LIBS += -ldes $(LIBS) else CFLAGS += -DUSE_CRYPT=1 endif PPPDOBJS += pppcrypt.o HEADERS += pppcrypt.h endif # For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/. ifdef USE_PAM CFLAGS += -DUSE_PAM LIBS += -lpam -ldl endif # Multi-linnk ifdef HAVE_MULTILINK # Multilink implies the use of TDB USE_TDB=y CFLAGS += -DHAVE_MULTILINK PPPDSRCS += multilink.c PPPDOBJS += multilink.o endif # TDB ifdef USE_TDB CFLAGS += -DUSE_TDB=1 PPPDSRCS += tdb.c spinlock.c PPPDOBJS += tdb.o spinlock.o HEADERS += tdb.h spinlock.h endif # Lock library binary for Linux is included in 'linux' subdirectory. ifdef LOCKLIB LIBS += -llock CFLAGS += -DLOCKLIB=1 endif ifdef PLUGIN CFLAGS += -DPLUGIN LDFLAGS += -Wl,-E LIBS += -ldl endif ifdef FILTER ifneq ($(wildcard /usr/include/pcap-bpf.h),) LIBS += -lpcap CFLAGS += -DPPP_FILTER endif endif ifdef HAVE_INET6 PPPDSRCS += ipv6cp.c eui64.c HEADERS += ipv6cp.h eui64.h PPPDOBJS += ipv6cp.o eui64.o CFLAGS += -DINET6=1 endif ifdef CBCP PPPDSRCS += cbcp.c PPPDOBJS += cbcp.o CFLAGS += -DCBCP_SUPPORT HEADERS += cbcp.h endif ifdef MAXOCTETS CFLAGS += -DMAXOCTETS endif INSTALL= install all: $(TARGETS) install: pppd mkdir -p $(BINDIR) $(MANDIR) $(EXTRAINSTALL) $(INSTALL) -s -c -m 555 pppd $(BINDIR)/pppd if chgrp pppusers $(BINDIR)/pppd 2>/dev/null; then \ chmod o-rx,u+s $(BINDIR)/pppd; fi $(INSTALL) -c -m 444 pppd.8 $(MANDIR) pppd: $(PPPDOBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS) srp-entry: srp-entry.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ srp-entry.c $(LIBS) install-devel: mkdir -p $(INCDIR)/pppd $(INSTALL) -c -m 644 $(HEADERS) $(INCDIR)/pppd clean: rm -f $(PPPDOBJS) $(EXTRACLEAN) $(TARGETS) *~ #* core depend: $(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend ppp-2.4.5/pppd/Makefile.sol2000066400000000000000000000024341130035057700156060ustar00rootroot00000000000000# # Makefile for pppd under Solaris 2. # $Id: Makefile.sol2,v 1.30 2008/01/30 14:26:52 carlsonj Exp $ # include ../Makedefs.com CFLAGS = -I../include -DSVR4 -DSOL2 $(COPTS) '-DDESTDIR="@DESTDIR@"' LIBS = -lsocket -lnsl OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o eap.o md5.o \ tty.o ccp.o ecp.o auth.o options.o demand.o utils.o sys-solaris.o \ chap-md5.o session.o # Solaris uses shadow passwords CFLAGS += -DHAS_SHADOW # # Comment the following out to disable plugins # CFLAGS += -DPLUGIN LIBS += -ldl # # Solaris 8 and above accomodates /var/run, so uncomment the # following to place pppd process IDs on that location # #CFLAGS += -D_PATH_VARRUN='"/var/run/"' # # uncomment the following to enable IPv6 # # Solaris 8 and on includes support for IPv6 # #CFLAGS += -DINET6 #OBJS += ipv6cp.o eui64.o # Uncomment to enable MS-CHAP #CFLAGS += -DUSE_CRYPT -DCHAPMS -DMSLANMAN -DHAVE_CRYPT_H #OBJS += chap_ms.o pppcrypt.o md4.o sha1.o # Uncomment for CBCP #CFLAGS += -DCBCP_SUPPORT #OBJS += cbcp.o # Uncomment for PAM #CFLAGS += -DUSE_PAM #LIBS += -lpam # # Make targets # all: pppd pppd: $(OBJS) $(CC) -o pppd $(OBJS) $(LIBS) install: $(INSTALL) -f $(BINDIR) -m 4755 -u root pppd $(INSTALL) -f $(MANDIR)/man8 -m 444 pppd.8 clean: rm -f $(OBJS) pppd *~ core y.tab.c y.tab.h ppp-2.4.5/pppd/auth.c000066400000000000000000001655621130035057700144110ustar00rootroot00000000000000/* * auth.c - PPP authentication and phase control. * * Copyright (c) 1993-2002 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Derived from main.c, which is: * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: auth.c,v 1.117 2008/07/01 12:27:56 paulus Exp $" #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_PATH_LASTLOG) && defined(__linux__) #include #endif #include #include #include #ifdef HAS_SHADOW #include #ifndef PW_PPP #define PW_PPP PW_LOGIN #endif #endif #include #include "pppd.h" #include "fsm.h" #include "lcp.h" #include "ccp.h" #include "ecp.h" #include "ipcp.h" #include "upap.h" #include "chap-new.h" #include "eap.h" #ifdef CBCP_SUPPORT #include "cbcp.h" #endif #include "pathnames.h" #include "session.h" static const char rcsid[] = RCSID; /* Bits in scan_authfile return value */ #define NONWILD_SERVER 1 #define NONWILD_CLIENT 2 #define ISWILD(word) (word[0] == '*' && word[1] == 0) /* The name by which the peer authenticated itself to us. */ char peer_authname[MAXNAMELEN]; /* Records which authentication operations haven't completed yet. */ static int auth_pending[NUM_PPP]; /* Records which authentication operations have been completed. */ int auth_done[NUM_PPP]; /* List of addresses which the peer may use. */ static struct permitted_ip *addresses[NUM_PPP]; /* Wordlist giving addresses which the peer may use without authenticating itself. */ static struct wordlist *noauth_addrs; /* Remote telephone number, if available */ char remote_number[MAXNAMELEN]; /* Wordlist giving remote telephone numbers which may connect. */ static struct wordlist *permitted_numbers; /* Extra options to apply, from the secrets file entry for the peer. */ static struct wordlist *extra_options; /* Number of network protocols which we have opened. */ static int num_np_open; /* Number of network protocols which have come up. */ static int num_np_up; /* Set if we got the contents of passwd[] from the pap-secrets file. */ static int passwd_from_file; /* Set if we require authentication only because we have a default route. */ static bool default_auth; /* Hook to enable a plugin to control the idle time limit */ int (*idle_time_hook) __P((struct ppp_idle *)) = NULL; /* Hook for a plugin to say whether we can possibly authenticate any peer */ int (*pap_check_hook) __P((void)) = NULL; /* Hook for a plugin to check the PAP user and password */ int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp, struct wordlist **paddrs, struct wordlist **popts)) = NULL; /* Hook for a plugin to know about the PAP user logout */ void (*pap_logout_hook) __P((void)) = NULL; /* Hook for a plugin to get the PAP password for authenticating us */ int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL; /* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */ int (*chap_check_hook) __P((void)) = NULL; /* Hook for a plugin to get the CHAP password for authenticating us */ int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL; /* Hook for a plugin to say whether it is OK if the peer refuses to authenticate. */ int (*null_auth_hook) __P((struct wordlist **paddrs, struct wordlist **popts)) = NULL; int (*allowed_address_hook) __P((u_int32_t addr)) = NULL; #ifdef HAVE_MULTILINK /* Hook for plugin to hear when an interface joins a multilink bundle */ void (*multilink_join_hook) __P((void)) = NULL; #endif /* A notifier for when the peer has authenticated itself, and we are proceeding to the network phase. */ struct notifier *auth_up_notifier = NULL; /* A notifier for when the link goes down. */ struct notifier *link_down_notifier = NULL; /* * This is used to ensure that we don't start an auth-up/down * script while one is already running. */ enum script_state { s_down, s_up }; static enum script_state auth_state = s_down; static enum script_state auth_script_state = s_down; static pid_t auth_script_pid = 0; /* * Option variables. */ bool uselogin = 0; /* Use /etc/passwd for checking PAP */ bool session_mgmt = 0; /* Do session management (login records) */ bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */ bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ bool refuse_eap = 0; /* Don't wanna auth. ourselves with EAP */ #ifdef CHAPMS bool refuse_mschap = 0; /* Don't wanna auth. ourselves with MS-CHAP */ bool refuse_mschap_v2 = 0; /* Don't wanna auth. ourselves with MS-CHAPv2 */ #else bool refuse_mschap = 1; /* Don't wanna auth. ourselves with MS-CHAP */ bool refuse_mschap_v2 = 1; /* Don't wanna auth. ourselves with MS-CHAPv2 */ #endif bool usehostname = 0; /* Use hostname for our_name */ bool auth_required = 0; /* Always require authentication from peer */ bool allow_any_ip = 0; /* Allow peer to use any IP address */ bool explicit_remote = 0; /* User specified explicit remote name */ bool explicit_user = 0; /* Set if "user" option supplied */ bool explicit_passwd = 0; /* Set if "password" option supplied */ char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ static char *uafname; /* name of most recent +ua file */ extern char *crypt __P((const char *, const char *)); /* Prototypes for procedures local to this file. */ static void network_phase __P((int)); static void check_idle __P((void *)); static void connect_time_expired __P((void *)); static int null_login __P((int)); static int get_pap_passwd __P((char *)); static int have_pap_secret __P((int *)); static int have_chap_secret __P((char *, char *, int, int *)); static int have_srp_secret __P((char *client, char *server, int need_ip, int *lacks_ipp)); static int ip_addr_check __P((u_int32_t, struct permitted_ip *)); static int scan_authfile __P((FILE *, char *, char *, char *, struct wordlist **, struct wordlist **, char *, int)); static void free_wordlist __P((struct wordlist *)); static void auth_script __P((char *)); static void auth_script_done __P((void *)); static void set_allowed_addrs __P((int, struct wordlist *, struct wordlist *)); static int some_ip_ok __P((struct wordlist *)); static int setupapfile __P((char **)); static int privgroup __P((char **)); static int set_noauth_addr __P((char **)); static int set_permitted_number __P((char **)); static void check_access __P((FILE *, char *)); static int wordlist_count __P((struct wordlist *)); #ifdef MAXOCTETS static void check_maxoctets __P((void *)); #endif /* * Authentication-related options. */ option_t auth_options[] = { { "auth", o_bool, &auth_required, "Require authentication from peer", OPT_PRIO | 1 }, { "noauth", o_bool, &auth_required, "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV, &allow_any_ip }, { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap, "Require PAP authentication from peer", OPT_PRIOSUB | 1, &auth_required }, { "+pap", o_bool, &lcp_wantoptions[0].neg_upap, "Require PAP authentication from peer", OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required }, { "require-chap", o_bool, &auth_required, "Require CHAP authentication from peer", OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5, &lcp_wantoptions[0].chap_mdtype }, { "+chap", o_bool, &auth_required, "Require CHAP authentication from peer", OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5, &lcp_wantoptions[0].chap_mdtype }, #ifdef CHAPMS { "require-mschap", o_bool, &auth_required, "Require MS-CHAP authentication from peer", OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT, &lcp_wantoptions[0].chap_mdtype }, { "+mschap", o_bool, &auth_required, "Require MS-CHAP authentication from peer", OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT, &lcp_wantoptions[0].chap_mdtype }, { "require-mschap-v2", o_bool, &auth_required, "Require MS-CHAPv2 authentication from peer", OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2, &lcp_wantoptions[0].chap_mdtype }, { "+mschap-v2", o_bool, &auth_required, "Require MS-CHAPv2 authentication from peer", OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2, &lcp_wantoptions[0].chap_mdtype }, #endif { "refuse-pap", o_bool, &refuse_pap, "Don't agree to auth to peer with PAP", 1 }, { "-pap", o_bool, &refuse_pap, "Don't allow PAP authentication with peer", OPT_ALIAS | 1 }, { "refuse-chap", o_bool, &refuse_chap, "Don't agree to auth to peer with CHAP", OPT_A2CLRB | MDTYPE_MD5, &lcp_allowoptions[0].chap_mdtype }, { "-chap", o_bool, &refuse_chap, "Don't allow CHAP authentication with peer", OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5, &lcp_allowoptions[0].chap_mdtype }, #ifdef CHAPMS { "refuse-mschap", o_bool, &refuse_mschap, "Don't agree to auth to peer with MS-CHAP", OPT_A2CLRB | MDTYPE_MICROSOFT, &lcp_allowoptions[0].chap_mdtype }, { "-mschap", o_bool, &refuse_mschap, "Don't allow MS-CHAP authentication with peer", OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT, &lcp_allowoptions[0].chap_mdtype }, { "refuse-mschap-v2", o_bool, &refuse_mschap_v2, "Don't agree to auth to peer with MS-CHAPv2", OPT_A2CLRB | MDTYPE_MICROSOFT_V2, &lcp_allowoptions[0].chap_mdtype }, { "-mschap-v2", o_bool, &refuse_mschap_v2, "Don't allow MS-CHAPv2 authentication with peer", OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2, &lcp_allowoptions[0].chap_mdtype }, #endif { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap, "Require EAP authentication from peer", OPT_PRIOSUB | 1, &auth_required }, { "refuse-eap", o_bool, &refuse_eap, "Don't agree to authenticate to peer with EAP", 1 }, { "name", o_string, our_name, "Set local name for authentication", OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN }, { "+ua", o_special, (void *)setupapfile, "Get PAP user and password from file", OPT_PRIO | OPT_A2STRVAL, &uafname }, { "user", o_string, user, "Set name for auth with peer", OPT_PRIO | OPT_STATIC, &explicit_user, MAXNAMELEN }, { "password", o_string, passwd, "Password for authenticating us to the peer", OPT_PRIO | OPT_STATIC | OPT_HIDE, &explicit_passwd, MAXSECRETLEN }, { "usehostname", o_bool, &usehostname, "Must use hostname for authentication", 1 }, { "remotename", o_string, remote_name, "Set remote name for authentication", OPT_PRIO | OPT_STATIC, &explicit_remote, MAXNAMELEN }, { "login", o_bool, &uselogin, "Use system password database for PAP", OPT_A2COPY | 1 , &session_mgmt }, { "enable-session", o_bool, &session_mgmt, "Enable session accounting for remote peers", OPT_PRIV | 1 }, { "papcrypt", o_bool, &cryptpap, "PAP passwords are encrypted", 1 }, { "privgroup", o_special, (void *)privgroup, "Allow group members to use privileged options", OPT_PRIV | OPT_A2LIST }, { "allow-ip", o_special, (void *)set_noauth_addr, "Set IP address(es) which can be used without authentication", OPT_PRIV | OPT_A2LIST }, { "remotenumber", o_string, remote_number, "Set remote telephone number for authentication", OPT_PRIO | OPT_STATIC, NULL, MAXNAMELEN }, { "allow-number", o_special, (void *)set_permitted_number, "Set telephone number(s) which are allowed to connect", OPT_PRIV | OPT_A2LIST }, { NULL } }; /* * setupapfile - specifies UPAP info for authenticating with peer. */ static int setupapfile(argv) char **argv; { FILE *ufile; int l; uid_t euid; char u[MAXNAMELEN], p[MAXSECRETLEN]; char *fname; lcp_allowoptions[0].neg_upap = 1; /* open user info file */ fname = strdup(*argv); if (fname == NULL) novm("+ua file name"); euid = geteuid(); if (seteuid(getuid()) == -1) { option_error("unable to reset uid before opening %s: %m", fname); return 0; } ufile = fopen(fname, "r"); if (seteuid(euid) == -1) fatal("unable to regain privileges: %m"); if (ufile == NULL) { option_error("unable to open user login data file %s", fname); return 0; } check_access(ufile, fname); uafname = fname; /* get username */ if (fgets(u, MAXNAMELEN - 1, ufile) == NULL || fgets(p, MAXSECRETLEN - 1, ufile) == NULL) { fclose(ufile); option_error("unable to read user login data file %s", fname); return 0; } fclose(ufile); /* get rid of newlines */ l = strlen(u); if (l > 0 && u[l-1] == '\n') u[l-1] = 0; l = strlen(p); if (l > 0 && p[l-1] == '\n') p[l-1] = 0; if (override_value("user", option_priority, fname)) { strlcpy(user, u, sizeof(user)); explicit_user = 1; } if (override_value("passwd", option_priority, fname)) { strlcpy(passwd, p, sizeof(passwd)); explicit_passwd = 1; } return (1); } /* * privgroup - allow members of the group to have privileged access. */ static int privgroup(argv) char **argv; { struct group *g; int i; g = getgrnam(*argv); if (g == 0) { option_error("group %s is unknown", *argv); return 0; } for (i = 0; i < ngroups; ++i) { if (groups[i] == g->gr_gid) { privileged = 1; break; } } return 1; } /* * set_noauth_addr - set address(es) that can be used without authentication. * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets. */ static int set_noauth_addr(argv) char **argv; { char *addr = *argv; int l = strlen(addr) + 1; struct wordlist *wp; wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l); if (wp == NULL) novm("allow-ip argument"); wp->word = (char *) (wp + 1); wp->next = noauth_addrs; BCOPY(addr, wp->word, l); noauth_addrs = wp; return 1; } /* * set_permitted_number - set remote telephone number(s) that may connect. */ static int set_permitted_number(argv) char **argv; { char *number = *argv; int l = strlen(number) + 1; struct wordlist *wp; wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l); if (wp == NULL) novm("allow-number argument"); wp->word = (char *) (wp + 1); wp->next = permitted_numbers; BCOPY(number, wp->word, l); permitted_numbers = wp; return 1; } /* * An Open on LCP has requested a change from Dead to Establish phase. */ void link_required(unit) int unit; { } /* * Bring the link up to the point of being able to do ppp. */ void start_link(unit) int unit; { char *msg; new_phase(PHASE_SERIALCONN); hungup = 0; devfd = the_channel->connect(); msg = "Connect script failed"; if (devfd < 0) goto fail; /* set up the serial device as a ppp interface */ /* * N.B. we used to do tdb_writelock/tdb_writeunlock around this * (from establish_ppp to set_ifunit). However, we won't be * doing the set_ifunit in multilink mode, which is the only time * we need the atomicity that the tdb_writelock/tdb_writeunlock * gives us. Thus we don't need the tdb_writelock/tdb_writeunlock. */ fd_ppp = the_channel->establish_ppp(devfd); msg = "ppp establishment failed"; if (fd_ppp < 0) { status = EXIT_FATAL_ERROR; goto disconnect; } if (!demand && ifunit >= 0) set_ifunit(1); /* * Start opening the connection and wait for * incoming events (reply, timeout, etc.). */ if (ifunit >= 0) notice("Connect: %s <--> %s", ifname, ppp_devnam); else notice("Starting negotiation on %s", ppp_devnam); add_fd(fd_ppp); status = EXIT_NEGOTIATION_FAILED; new_phase(PHASE_ESTABLISH); lcp_lowerup(0); return; disconnect: new_phase(PHASE_DISCONNECT); if (the_channel->disconnect) the_channel->disconnect(); fail: new_phase(PHASE_DEAD); if (the_channel->cleanup) (*the_channel->cleanup)(); } /* * LCP has terminated the link; go to the Dead phase and take the * physical layer down. */ void link_terminated(unit) int unit; { if (phase == PHASE_DEAD || phase == PHASE_MASTER) return; new_phase(PHASE_DISCONNECT); if (pap_logout_hook) { pap_logout_hook(); } session_end(devnam); if (!doing_multilink) { notice("Connection terminated."); print_link_stats(); } else notice("Link terminated."); /* * Delete pid files before disestablishing ppp. Otherwise it * can happen that another pppd gets the same unit and then * we delete its pid file. */ if (!doing_multilink && !demand) remove_pidfiles(); /* * If we may want to bring the link up again, transfer * the ppp unit back to the loopback. Set the * real serial device back to its normal mode of operation. */ if (fd_ppp >= 0) { remove_fd(fd_ppp); clean_check(); the_channel->disestablish_ppp(devfd); if (doing_multilink) mp_exit_bundle(); fd_ppp = -1; } if (!hungup) lcp_lowerdown(0); if (!doing_multilink && !demand) script_unsetenv("IFNAME"); /* * Run disconnector script, if requested. * XXX we may not be able to do this if the line has hung up! */ if (devfd >= 0 && the_channel->disconnect) { the_channel->disconnect(); devfd = -1; } if (the_channel->cleanup) (*the_channel->cleanup)(); if (doing_multilink && multilink_master) { if (!bundle_terminating) new_phase(PHASE_MASTER); else mp_bundle_terminated(); } else new_phase(PHASE_DEAD); } /* * LCP has gone down; it will either die or try to re-establish. */ void link_down(unit) int unit; { if (auth_state != s_down) { notify(link_down_notifier, 0); auth_state = s_down; if (auth_script_state == s_up && auth_script_pid == 0) { update_link_stats(unit); auth_script_state = s_down; auth_script(_PATH_AUTHDOWN); } } if (!doing_multilink) { upper_layers_down(unit); if (phase != PHASE_DEAD && phase != PHASE_MASTER) new_phase(PHASE_ESTABLISH); } /* XXX if doing_multilink, should do something to stop network-layer traffic on the link */ } void upper_layers_down(int unit) { int i; struct protent *protp; for (i = 0; (protp = protocols[i]) != NULL; ++i) { if (!protp->enabled_flag) continue; if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) (*protp->lowerdown)(unit); if (protp->protocol < 0xC000 && protp->close != NULL) (*protp->close)(unit, "LCP down"); } num_np_open = 0; num_np_up = 0; } /* * 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]; int i; struct protent *protp; /* * Tell higher-level protocols that LCP is up. */ if (!doing_multilink) { for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) (*protp->lowerup)(unit); } if (!auth_required && noauth_addrs != NULL) set_allowed_addrs(unit, NULL, NULL); if (auth_required && !(go->neg_upap || go->neg_chap || go->neg_eap)) { /* * We wanted the peer to authenticate itself, and it refused: * if we have some address(es) it can use without auth, fine, * otherwise 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 (noauth_addrs != NULL) { set_allowed_addrs(unit, NULL, NULL); } else if (!wo->neg_upap || uselogin || !null_login(unit)) { warn("peer refused to authenticate: terminating link"); status = EXIT_PEER_AUTH_FAILED; lcp_close(unit, "peer refused to authenticate"); return; } } new_phase(PHASE_AUTHENTICATE); auth = 0; if (go->neg_eap) { eap_authpeer(unit, our_name); auth |= EAP_PEER; } else if (go->neg_chap) { chap_auth_peer(unit, our_name, CHAP_DIGEST(go->chap_mdtype)); auth |= CHAP_PEER; } else if (go->neg_upap) { upap_authpeer(unit); auth |= PAP_PEER; } if (ho->neg_eap) { eap_authwithpeer(unit, user); auth |= EAP_WITHPEER; } else if (ho->neg_chap) { chap_auth_with_peer(unit, user, CHAP_DIGEST(ho->chap_mdtype)); auth |= CHAP_WITHPEER; } else if (ho->neg_upap) { /* If a blank password was explicitly given as an option, trust the user and don't try to look up one. */ if (passwd[0] == 0 && !explicit_passwd) { passwd_from_file = 1; if (!get_pap_passwd(passwd)) error("No secret found for PAP login"); } upap_authwithpeer(unit, user, passwd); auth |= PAP_WITHPEER; } auth_pending[unit] = auth; auth_done[unit] = 0; if (!auth) network_phase(unit); } /* * Proceed to the network phase. */ static void network_phase(unit) int unit; { lcp_options *go = &lcp_gotoptions[unit]; /* Log calling number. */ if (*remote_number) notice("peer from calling number %q authorized", remote_number); /* * If the peer had to authenticate, run the auth-up script now. */ if (go->neg_chap || go->neg_upap || go->neg_eap) { notify(auth_up_notifier, 0); auth_state = s_up; if (auth_script_state == s_down && auth_script_pid == 0) { auth_script_state = s_up; auth_script(_PATH_AUTHUP); } } #ifdef CBCP_SUPPORT /* * If we negotiated callback, do it now. */ if (go->neg_cbcp) { new_phase(PHASE_CALLBACK); (*cbcp_protent.open)(unit); return; } #endif /* * Process extra options from the secrets file */ if (extra_options) { options_from_list(extra_options, 1); free_wordlist(extra_options); extra_options = 0; } start_networks(unit); } void start_networks(unit) int unit; { int i; struct protent *protp; int ecp_required, mppe_required; new_phase(PHASE_NETWORK); #ifdef HAVE_MULTILINK if (multilink) { if (mp_join_bundle()) { if (multilink_join_hook) (*multilink_join_hook)(); if (updetach && !nodetach) detach(); return; } } #endif /* HAVE_MULTILINK */ #ifdef PPP_FILTER if (!demand) set_filters(&pass_filter, &active_filter); #endif /* Start CCP and ECP */ for (i = 0; (protp = protocols[i]) != NULL; ++i) if ((protp->protocol == PPP_ECP || protp->protocol == PPP_CCP) && protp->enabled_flag && protp->open != NULL) (*protp->open)(0); /* * Bring up other network protocols iff encryption is not required. */ ecp_required = ecp_gotoptions[unit].required; mppe_required = ccp_gotoptions[unit].mppe; if (!ecp_required && !mppe_required) continue_networks(unit); } void continue_networks(unit) int unit; { int i; struct protent *protp; /* * Start the "real" network protocols. */ for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->protocol < 0xC000 && protp->protocol != PPP_CCP && protp->protocol != PPP_ECP && protp->enabled_flag && protp->open != NULL) { (*protp->open)(0); ++num_np_open; } if (num_np_open == 0) /* nothing to do */ lcp_close(0, "No network protocols running"); } /* * The peer has failed to authenticate himself using `protocol'. */ void auth_peer_fail(unit, protocol) int unit, protocol; { /* * Authentication failure: take the link down */ status = EXIT_PEER_AUTH_FAILED; lcp_close(unit, "Authentication failed"); } /* * The peer has been successfully authenticated using `protocol'. */ void auth_peer_success(unit, protocol, prot_flavor, name, namelen) int unit, protocol, prot_flavor; char *name; int namelen; { int bit; switch (protocol) { case PPP_CHAP: bit = CHAP_PEER; switch (prot_flavor) { case CHAP_MD5: bit |= CHAP_MD5_PEER; break; #ifdef CHAPMS case CHAP_MICROSOFT: bit |= CHAP_MS_PEER; break; case CHAP_MICROSOFT_V2: bit |= CHAP_MS2_PEER; break; #endif } break; case PPP_PAP: bit = PAP_PEER; break; case PPP_EAP: bit = EAP_PEER; break; default: warn("auth_peer_success: unknown protocol %x", protocol); return; } /* * Save the authenticated name of the peer for later. */ if (namelen > sizeof(peer_authname) - 1) namelen = sizeof(peer_authname) - 1; BCOPY(name, peer_authname, namelen); peer_authname[namelen] = 0; script_setenv("PEERNAME", peer_authname, 0); /* Save the authentication method for later. */ auth_done[unit] |= bit; /* * If there is no more authentication still to be done, * proceed to the network (or callback) phase. */ if ((auth_pending[unit] &= ~bit) == 0) network_phase(unit); } /* * We have failed to authenticate ourselves to the peer using `protocol'. */ void auth_withpeer_fail(unit, protocol) int unit, protocol; { if (passwd_from_file) BZERO(passwd, MAXSECRETLEN); /* * We've failed to authenticate ourselves to our peer. * Some servers keep sending CHAP challenges, but there * is no point in persisting without any way to get updated * authentication secrets. */ status = EXIT_AUTH_TOPEER_FAILED; lcp_close(unit, "Failed to authenticate ourselves to peer"); } /* * We have successfully authenticated ourselves with the peer using `protocol'. */ void auth_withpeer_success(unit, protocol, prot_flavor) int unit, protocol, prot_flavor; { int bit; const char *prot = ""; switch (protocol) { case PPP_CHAP: bit = CHAP_WITHPEER; prot = "CHAP"; switch (prot_flavor) { case CHAP_MD5: bit |= CHAP_MD5_WITHPEER; break; #ifdef CHAPMS case CHAP_MICROSOFT: bit |= CHAP_MS_WITHPEER; break; case CHAP_MICROSOFT_V2: bit |= CHAP_MS2_WITHPEER; break; #endif } break; case PPP_PAP: if (passwd_from_file) BZERO(passwd, MAXSECRETLEN); bit = PAP_WITHPEER; prot = "PAP"; break; case PPP_EAP: bit = EAP_WITHPEER; prot = "EAP"; break; default: warn("auth_withpeer_success: unknown protocol %x", protocol); bit = 0; } notice("%s authentication succeeded", prot); /* Save the authentication method for later. */ auth_done[unit] |= bit; /* * If there is no more authentication still being done, * proceed to the network (or callback) phase. */ if ((auth_pending[unit] &= ~bit) == 0) network_phase(unit); } /* * np_up - a network protocol has come up. */ void np_up(unit, proto) int unit, proto; { int tlim; if (num_np_up == 0) { /* * At this point we consider that the link has come up successfully. */ status = EXIT_OK; unsuccess = 0; new_phase(PHASE_RUNNING); if (idle_time_hook != 0) tlim = (*idle_time_hook)(NULL); else tlim = idle_time_limit; if (tlim > 0) TIMEOUT(check_idle, NULL, tlim); /* * Set a timeout to close the connection once the maximum * connect time has expired. */ if (maxconnect > 0) TIMEOUT(connect_time_expired, 0, maxconnect); #ifdef MAXOCTETS if (maxoctets > 0) TIMEOUT(check_maxoctets, NULL, maxoctets_timeout); #endif /* * Detach now, if the updetach option was given. */ if (updetach && !nodetach) detach(); } ++num_np_up; } /* * np_down - a network protocol has gone down. */ void np_down(unit, proto) int unit, proto; { if (--num_np_up == 0) { UNTIMEOUT(check_idle, NULL); UNTIMEOUT(connect_time_expired, NULL); #ifdef MAXOCTETS UNTIMEOUT(check_maxoctets, NULL); #endif new_phase(PHASE_NETWORK); } } /* * np_finished - a network protocol has finished using the link. */ void np_finished(unit, proto) int unit, proto; { if (--num_np_open <= 0) { /* no further use for the link: shut up shop. */ lcp_close(0, "No network protocols running"); } } #ifdef MAXOCTETS static void check_maxoctets(arg) void *arg; { unsigned int used; update_link_stats(ifunit); link_stats_valid=0; switch(maxoctets_dir) { case PPP_OCTETS_DIRECTION_IN: used = link_stats.bytes_in; break; case PPP_OCTETS_DIRECTION_OUT: used = link_stats.bytes_out; break; case PPP_OCTETS_DIRECTION_MAXOVERAL: case PPP_OCTETS_DIRECTION_MAXSESSION: used = (link_stats.bytes_in > link_stats.bytes_out) ? link_stats.bytes_in : link_stats.bytes_out; break; default: used = link_stats.bytes_in+link_stats.bytes_out; break; } if (used > maxoctets) { notice("Traffic limit reached. Limit: %u Used: %u", maxoctets, used); status = EXIT_TRAFFIC_LIMIT; lcp_close(0, "Traffic limit"); need_holdoff = 0; } else { TIMEOUT(check_maxoctets, NULL, maxoctets_timeout); } } #endif /* * check_idle - check whether the link has been idle for long * enough that we can shut it down. */ static void check_idle(arg) void *arg; { struct ppp_idle idle; time_t itime; int tlim; if (!get_idle_time(0, &idle)) return; if (idle_time_hook != 0) { tlim = idle_time_hook(&idle); } else { itime = MIN(idle.xmit_idle, idle.recv_idle); tlim = idle_time_limit - itime; } if (tlim <= 0) { /* link is idle: shut it down. */ notice("Terminating connection due to lack of activity."); status = EXIT_IDLE_TIMEOUT; lcp_close(0, "Link inactive"); need_holdoff = 0; } else { TIMEOUT(check_idle, NULL, tlim); } } /* * connect_time_expired - log a message and close the connection. */ static void connect_time_expired(arg) void *arg; { info("Connect time expired"); status = EXIT_CONNECT_TIME; lcp_close(0, "Connect time expired"); /* Close connection */ } /* * auth_check_options - called to check authentication options. */ void auth_check_options() { lcp_options *wo = &lcp_wantoptions[0]; int can_auth; int lacks_ip; /* Default our_name to hostname, and user to our_name */ if (our_name[0] == 0 || usehostname) strlcpy(our_name, hostname, sizeof(our_name)); /* If a blank username was explicitly given as an option, trust the user and don't use our_name */ if (user[0] == 0 && !explicit_user) strlcpy(user, our_name, sizeof(user)); /* * If we have a default route, require the peer to authenticate * unless the noauth option was given or the real user is root. */ if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) { auth_required = 1; default_auth = 1; } /* If we selected any CHAP flavors, we should probably negotiate it. :-) */ if (wo->chap_mdtype) wo->neg_chap = 1; /* If authentication is required, ask peer for CHAP, PAP, or EAP. */ if (auth_required) { allow_any_ip = 0; if (!wo->neg_chap && !wo->neg_upap && !wo->neg_eap) { wo->neg_chap = chap_mdtype_all != MDTYPE_NONE; wo->chap_mdtype = chap_mdtype_all; wo->neg_upap = 1; wo->neg_eap = 1; } } else { wo->neg_chap = 0; wo->chap_mdtype = MDTYPE_NONE; wo->neg_upap = 0; wo->neg_eap = 0; } /* * Check whether we have appropriate secrets to use * to authenticate the peer. Note that EAP can authenticate by way * of a CHAP-like exchanges as well as SRP. */ lacks_ip = 0; can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip)); if (!can_auth && (wo->neg_chap || wo->neg_eap)) { can_auth = have_chap_secret((explicit_remote? remote_name: NULL), our_name, 1, &lacks_ip); } if (!can_auth && wo->neg_eap) { can_auth = have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1, &lacks_ip); } if (auth_required && !can_auth && noauth_addrs == NULL) { if (default_auth) { option_error( "By default the remote system is required to authenticate itself"); option_error( "(because this system has a default route to the internet)"); } else if (explicit_remote) option_error( "The remote system (%s) is required to authenticate itself", remote_name); else option_error( "The remote system is required to authenticate itself"); option_error( "but I couldn't find any suitable secret (password) for it to use to do so."); if (lacks_ip) option_error( "(None of the available passwords would let it use an IP address.)"); exit(1); } /* * Early check for remote number authorization. */ if (!auth_number()) { warn("calling number %q is not authorized", remote_number); exit(EXIT_CNID_AUTH_FAILED); } } /* * auth_reset - called when LCP is starting negotiations to recheck * authentication options, i.e. whether we have appropriate secrets * to use for authenticating ourselves and/or the peer. */ void auth_reset(unit) int unit; { lcp_options *go = &lcp_gotoptions[unit]; lcp_options *ao = &lcp_allowoptions[unit]; int hadchap; hadchap = -1; ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL)); ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2) && (passwd[0] != 0 || (hadchap = have_chap_secret(user, (explicit_remote? remote_name: NULL), 0, NULL))); ao->neg_eap = !refuse_eap && ( passwd[0] != 0 || (hadchap == 1 || (hadchap == -1 && have_chap_secret(user, (explicit_remote? remote_name: NULL), 0, NULL))) || have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL)); hadchap = -1; if (go->neg_upap && !uselogin && !have_pap_secret(NULL)) go->neg_upap = 0; if (go->neg_chap) { if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL), our_name, 1, NULL))) go->neg_chap = 0; } if (go->neg_eap && (hadchap == 0 || (hadchap == -1 && !have_chap_secret((explicit_remote? remote_name: NULL), our_name, 1, NULL))) && !have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1, NULL)) go->neg_eap = 0; } /* * 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) int unit; char *auser; int userlen; char *apasswd; int passwdlen; char **msg; { int ret; char *filename; FILE *f; struct wordlist *addrs = NULL, *opts = NULL; char passwd[256], user[256]; char secret[MAXWORDLEN]; static int attempts = 0; /* * Make copies of apasswd and auser, then null-terminate them. * If there are unprintable characters in the password, make * them visible. */ slprintf(passwd, sizeof(passwd), "%.*v", passwdlen, apasswd); slprintf(user, sizeof(user), "%.*v", userlen, auser); *msg = ""; /* * Check if a plugin wants to handle this. */ if (pap_auth_hook) { ret = (*pap_auth_hook)(user, passwd, msg, &addrs, &opts); if (ret >= 0) { /* note: set_allowed_addrs() saves opts (but not addrs): don't free it! */ if (ret) set_allowed_addrs(unit, addrs, opts); else if (opts != 0) free_wordlist(opts); if (addrs != 0) free_wordlist(addrs); BZERO(passwd, sizeof(passwd)); return ret? UPAP_AUTHACK: UPAP_AUTHNAK; } } /* * Open the file of pap secrets and scan for a suitable secret * for authenticating this user. */ filename = _PATH_UPAPFILE; addrs = opts = NULL; ret = UPAP_AUTHNAK; f = fopen(filename, "r"); if (f == NULL) { error("Can't open PAP password file %s: %m", filename); } else { check_access(f, filename); if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename, 0) < 0) { warn("no PAP secret found for %s", user); } else { /* * If the secret is "@login", it means to check * the password against the login database. */ int login_secret = strcmp(secret, "@login") == 0; ret = UPAP_AUTHACK; if (uselogin || login_secret) { /* login option or secret is @login */ if (session_full(user, passwd, devnam, msg) == 0) { ret = UPAP_AUTHNAK; } } else if (session_mgmt) { if (session_check(user, NULL, devnam, NULL) == 0) { warn("Peer %q failed PAP Session verification", user); ret = UPAP_AUTHNAK; } } if (secret[0] != 0 && !login_secret) { /* password given in pap-secrets - must match */ if ((cryptpap || strcmp(passwd, secret) != 0) && strcmp(crypt(passwd, secret), secret) != 0) ret = UPAP_AUTHNAK; } } fclose(f); } if (ret == UPAP_AUTHNAK) { if (**msg == 0) *msg = "Login incorrect"; /* * XXX can we ever get here more than once?? * 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) { warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user); lcp_close(unit, "login failed"); } if (attempts > 3) sleep((u_int) (attempts - 3) * 5); if (opts != NULL) free_wordlist(opts); } else { attempts = 0; /* Reset count */ if (**msg == 0) *msg = "Login ok"; set_allowed_addrs(unit, addrs, opts); } if (addrs != NULL) free_wordlist(addrs); BZERO(passwd, sizeof(passwd)); BZERO(secret, sizeof(secret)); return ret; } /* * 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, *opts; char secret[MAXWORDLEN]; /* * Check if a plugin wants to handle this. */ ret = -1; if (null_auth_hook) ret = (*null_auth_hook)(&addrs, &opts); /* * Open the file of pap secrets and scan for a suitable secret. */ if (ret <= 0) { 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, &opts, filename, 0); ret = i >= 0 && secret[0] == 0; BZERO(secret, sizeof(secret)); fclose(f); } if (ret) set_allowed_addrs(unit, addrs, opts); else if (opts != 0) free_wordlist(opts); if (addrs != 0) free_wordlist(addrs); return ret; } /* * get_pap_passwd - get a password for authenticating ourselves with * our peer using PAP. Returns 1 on success, 0 if no suitable password * could be found. * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null). */ static int get_pap_passwd(passwd) char *passwd; { char *filename; FILE *f; int ret; char secret[MAXWORDLEN]; /* * Check whether a plugin wants to supply this. */ if (pap_passwd_hook) { ret = (*pap_passwd_hook)(user, passwd); if (ret >= 0) return ret; } filename = _PATH_UPAPFILE; f = fopen(filename, "r"); if (f == NULL) return 0; check_access(f, filename); ret = scan_authfile(f, user, (remote_name[0]? remote_name: NULL), secret, NULL, NULL, filename, 0); fclose(f); if (ret < 0) return 0; if (passwd != NULL) strlcpy(passwd, secret, MAXSECRETLEN); BZERO(secret, sizeof(secret)); return 1; } /* * have_pap_secret - check whether we have a PAP file with any * secrets that we could possibly use for authenticating the peer. */ static int have_pap_secret(lacks_ipp) int *lacks_ipp; { FILE *f; int ret; char *filename; struct wordlist *addrs; /* let the plugin decide, if there is one */ if (pap_check_hook) { ret = (*pap_check_hook)(); if (ret >= 0) return ret; } filename = _PATH_UPAPFILE; f = fopen(filename, "r"); if (f == NULL) return 0; ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name, NULL, &addrs, NULL, filename, 0); fclose(f); if (ret >= 0 && !some_ip_ok(addrs)) { if (lacks_ipp != 0) *lacks_ipp = 1; ret = -1; } if (addrs != 0) free_wordlist(addrs); return ret >= 0; } /* * 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, need_ip, lacks_ipp) char *client; char *server; int need_ip; int *lacks_ipp; { FILE *f; int ret; char *filename; struct wordlist *addrs; if (chap_check_hook) { ret = (*chap_check_hook)(); if (ret >= 0) { return ret; } } filename = _PATH_CHAPFILE; f = fopen(filename, "r"); if (f == NULL) return 0; if (client != NULL && client[0] == 0) client = NULL; else if (server != NULL && server[0] == 0) server = NULL; ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0); fclose(f); if (ret >= 0 && need_ip && !some_ip_ok(addrs)) { if (lacks_ipp != 0) *lacks_ipp = 1; ret = -1; } if (addrs != 0) free_wordlist(addrs); return ret >= 0; } /* * have_srp_secret - check whether we have a SRP 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_srp_secret(client, server, need_ip, lacks_ipp) char *client; char *server; int need_ip; int *lacks_ipp; { FILE *f; int ret; char *filename; struct wordlist *addrs; filename = _PATH_SRPFILE; f = fopen(filename, "r"); if (f == NULL) return 0; if (client != NULL && client[0] == 0) client = NULL; else if (server != NULL && server[0] == 0) server = NULL; ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0); fclose(f); if (ret >= 0 && need_ip && !some_ip_ok(addrs)) { if (lacks_ipp != 0) *lacks_ipp = 1; ret = -1; } if (addrs != 0) free_wordlist(addrs); return ret >= 0; } /* * 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, am_server) int unit; char *client; char *server; char *secret; int *secret_len; int am_server; { FILE *f; int ret, len; char *filename; struct wordlist *addrs, *opts; char secbuf[MAXWORDLEN]; if (!am_server && passwd[0] != 0) { strlcpy(secbuf, passwd, sizeof(secbuf)); } else if (!am_server && chap_passwd_hook) { if ( (*chap_passwd_hook)(client, secbuf) < 0) { error("Unable to obtain CHAP password for %s on %s from plugin", client, server); return 0; } } else { filename = _PATH_CHAPFILE; addrs = NULL; secbuf[0] = 0; f = fopen(filename, "r"); if (f == NULL) { error("Can't open chap secret file %s: %m", filename); return 0; } check_access(f, filename); ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename, 0); fclose(f); if (ret < 0) return 0; if (am_server) set_allowed_addrs(unit, addrs, opts); else if (opts != 0) free_wordlist(opts); if (addrs != 0) free_wordlist(addrs); } len = strlen(secbuf); if (len > MAXSECRETLEN) { error("Secret for %s on %s is too long", client, server); len = MAXSECRETLEN; } BCOPY(secbuf, secret, len); BZERO(secbuf, sizeof(secbuf)); *secret_len = len; return 1; } /* * get_srp_secret - open the SRP secret file and return the secret * for authenticating the given client on the given server. * (We could be either client or server). */ int get_srp_secret(unit, client, server, secret, am_server) int unit; char *client; char *server; char *secret; int am_server; { FILE *fp; int ret; char *filename; struct wordlist *addrs, *opts; if (!am_server && passwd[0] != '\0') { strlcpy(secret, passwd, MAXWORDLEN); } else { filename = _PATH_SRPFILE; addrs = NULL; fp = fopen(filename, "r"); if (fp == NULL) { error("Can't open srp secret file %s: %m", filename); return 0; } check_access(fp, filename); secret[0] = '\0'; ret = scan_authfile(fp, client, server, secret, &addrs, &opts, filename, am_server); fclose(fp); if (ret < 0) return 0; if (am_server) set_allowed_addrs(unit, addrs, opts); else if (opts != NULL) free_wordlist(opts); if (addrs != NULL) free_wordlist(addrs); } return 1; } /* * set_allowed_addrs() - set the list of allowed addresses. * Also looks for `--' indicating options to apply for this peer * and leaves the following words in extra_options. */ static void set_allowed_addrs(unit, addrs, opts) int unit; struct wordlist *addrs; struct wordlist *opts; { int n; struct wordlist *ap, **plink; struct permitted_ip *ip; char *ptr_word, *ptr_mask; struct hostent *hp; struct netent *np; u_int32_t a, mask, ah, offset; struct ipcp_options *wo = &ipcp_wantoptions[unit]; u_int32_t suggested_ip = 0; if (addresses[unit] != NULL) free(addresses[unit]); addresses[unit] = NULL; if (extra_options != NULL) free_wordlist(extra_options); extra_options = opts; /* * Count the number of IP addresses given. */ n = wordlist_count(addrs) + wordlist_count(noauth_addrs); if (n == 0) return; ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip)); if (ip == 0) return; /* temporarily append the noauth_addrs list to addrs */ for (plink = &addrs; *plink != NULL; plink = &(*plink)->next) ; *plink = noauth_addrs; n = 0; for (ap = addrs; ap != NULL; ap = ap->next) { /* "-" means no addresses authorized, "*" means any address allowed */ ptr_word = ap->word; if (strcmp(ptr_word, "-") == 0) break; if (strcmp(ptr_word, "*") == 0) { ip[n].permit = 1; ip[n].base = ip[n].mask = 0; ++n; break; } ip[n].permit = 1; if (*ptr_word == '!') { ip[n].permit = 0; ++ptr_word; } mask = ~ (u_int32_t) 0; offset = 0; ptr_mask = strchr (ptr_word, '/'); if (ptr_mask != NULL) { int bit_count; char *endp; bit_count = (int) strtol (ptr_mask+1, &endp, 10); if (bit_count <= 0 || bit_count > 32) { warn("invalid address length %v in auth. address list", ptr_mask+1); continue; } bit_count = 32 - bit_count; /* # bits in host part */ if (*endp == '+') { offset = ifunit + 1; ++endp; } if (*endp != 0) { warn("invalid address length syntax: %v", ptr_mask+1); continue; } *ptr_mask = '\0'; mask <<= bit_count; } hp = gethostbyname(ptr_word); if (hp != NULL && hp->h_addrtype == AF_INET) { a = *(u_int32_t *)hp->h_addr; } else { np = getnetbyname (ptr_word); if (np != NULL && np->n_addrtype == AF_INET) { a = htonl ((u_int32_t)np->n_net); if (ptr_mask == NULL) { /* calculate appropriate mask for net */ ah = ntohl(a); if (IN_CLASSA(ah)) mask = IN_CLASSA_NET; else if (IN_CLASSB(ah)) mask = IN_CLASSB_NET; else if (IN_CLASSC(ah)) mask = IN_CLASSC_NET; } } else { a = inet_addr (ptr_word); } } if (ptr_mask != NULL) *ptr_mask = '/'; if (a == (u_int32_t)-1L) { warn("unknown host %s in auth. address list", ap->word); continue; } if (offset != 0) { if (offset >= ~mask) { warn("interface unit %d too large for subnet %v", ifunit, ptr_word); continue; } a = htonl((ntohl(a) & mask) + offset); mask = ~(u_int32_t)0; } ip[n].mask = htonl(mask); ip[n].base = a & ip[n].mask; ++n; if (~mask == 0 && suggested_ip == 0) suggested_ip = a; } *plink = NULL; ip[n].permit = 0; /* make the last entry forbid all addresses */ ip[n].base = 0; /* to terminate the list */ ip[n].mask = 0; addresses[unit] = ip; /* * If the address given for the peer isn't authorized, or if * the user hasn't given one, AND there is an authorized address * which is a single host, then use that if we find one. */ if (suggested_ip != 0 && (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr))) { wo->hisaddr = suggested_ip; /* * Do we insist on this address? No, if there are other * addresses authorized than the suggested one. */ if (n > 1) wo->accept_remote = 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_int32_t addr; { int ok; /* don't allow loopback or multicast address */ if (bad_ip_adrs(addr)) return 0; if (allowed_address_hook) { ok = allowed_address_hook(addr); if (ok >= 0) return ok; } if (addresses[unit] != NULL) { ok = ip_addr_check(addr, addresses[unit]); if (ok >= 0) return ok; } if (auth_required) return 0; /* no addresses authorized */ return allow_any_ip || privileged || !have_route_to(addr); } static int ip_addr_check(addr, addrs) u_int32_t addr; struct permitted_ip *addrs; { for (; ; ++addrs) if ((addr & addrs->mask) == addrs->base) return addrs->permit; } /* * 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_int32_t addr; { addr = ntohl(addr); return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || IN_MULTICAST(addr) || IN_BADCLASS(addr); } /* * some_ip_ok - check a wordlist to see if it authorizes any * IP address(es). */ static int some_ip_ok(addrs) struct wordlist *addrs; { for (; addrs != 0; addrs = addrs->next) { if (addrs->word[0] == '-') break; if (addrs->word[0] != '!') return 1; /* some IP address is allowed */ } return 0; } /* * auth_number - check whether the remote number is allowed to connect. * Returns 1 if authorized, 0 otherwise. */ int auth_number() { struct wordlist *wp = permitted_numbers; int l; /* Allow all if no authorization list. */ if (!wp) return 1; /* Allow if we have a match in the authorization list. */ while (wp) { /* trailing '*' wildcard */ l = strlen(wp->word); if ((wp->word)[l - 1] == '*') l--; if (!strncasecmp(wp->word, remote_number, l)) return 1; wp = wp->next; } return 0; } /* * check_access - complain if a secret file has too-liberal permissions. */ static void check_access(f, filename) FILE *f; char *filename; { struct stat sbuf; if (fstat(fileno(f), &sbuf) < 0) { warn("cannot stat secret file %s: %m", filename); } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { warn("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 up to a "--" (i.e. address authorization * info) are placed in a wordlist and returned in *addrs. Any * following words (extra options) are placed in a wordlist and * returned in *opts. * We assume secret is NULL or points to MAXWORDLEN bytes of space. * Flags are non-zero if we need two colons in the secret in order to * match. */ static int scan_authfile(f, client, server, secret, addrs, opts, filename, flags) FILE *f; char *client; char *server; char *secret; struct wordlist **addrs; struct wordlist **opts; char *filename; int flags; { int newline, xxx; int got_flag, best_flag; FILE *sf; struct wordlist *ap, *addr_list, *alist, **app; char word[MAXWORDLEN]; char atfile[MAXWORDLEN]; char lsecret[MAXWORDLEN]; char *cp; if (addrs != NULL) *addrs = NULL; if (opts != NULL) *opts = 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 (!ISWILD(word)) { if (server != NULL && strcmp(word, server) != 0) continue; 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; /* * SRP-SHA1 authenticator should never be reading secrets from * a file. (Authenticatee may, though.) */ if (flags && ((cp = strchr(word, ':')) == NULL || strchr(cp + 1, ':') == NULL)) continue; if (secret != NULL) { /* * Special syntax: @/pathname means read secret from file. */ if (word[0] == '@' && word[1] == '/') { strlcpy(atfile, word+1, sizeof(atfile)); if ((sf = fopen(atfile, "r")) == NULL) { warn("can't open indirect secret file %s", atfile); continue; } check_access(sf, atfile); if (!getword(sf, word, &xxx, atfile)) { warn("no secret in indirect secret file %s", atfile); fclose(sf); continue; } fclose(sf); } strlcpy(lsecret, word, sizeof(lsecret)); } /* * Now read address authorization info and make a wordlist. */ app = &alist; for (;;) { if (!getword(f, word, &newline, filename) || newline) break; ap = (struct wordlist *) malloc(sizeof(struct wordlist) + strlen(word) + 1); if (ap == NULL) novm("authorized addresses"); ap->word = (char *) (ap + 1); strcpy(ap->word, word); *app = ap; app = &ap->next; } *app = NULL; /* * This is the best so far; remember it. */ best_flag = got_flag; if (addr_list) free_wordlist(addr_list); addr_list = alist; if (secret != NULL) strlcpy(secret, lsecret, MAXWORDLEN); if (!newline) break; } /* scan for a -- word indicating the start of options */ for (app = &addr_list; (ap = *app) != NULL; app = &ap->next) if (strcmp(ap->word, "--") == 0) break; /* ap = start of options */ if (ap != NULL) { ap = ap->next; /* first option */ free(*app); /* free the "--" word */ *app = NULL; /* terminate addr list */ } if (opts != NULL) *opts = ap; else if (ap != NULL) free_wordlist(ap); if (addrs != NULL) *addrs = addr_list; else if (addr_list != NULL) free_wordlist(addr_list); return best_flag; } /* * wordlist_count - return the number of items in a wordlist */ static int wordlist_count(wp) struct wordlist *wp; { int n; for (n = 0; wp != NULL; wp = wp->next) ++n; return n; } /* * 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; } } /* * auth_script_done - called when the auth-up or auth-down script * has finished. */ static void auth_script_done(arg) void *arg; { auth_script_pid = 0; switch (auth_script_state) { case s_up: if (auth_state == s_down) { auth_script_state = s_down; auth_script(_PATH_AUTHDOWN); } break; case s_down: if (auth_state == s_up) { auth_script_state = s_up; auth_script(_PATH_AUTHUP); } break; } } /* * auth_script - execute a script with arguments * interface-name peer-name real-user tty speed */ static void auth_script(script) char *script; { char strspeed[32]; struct passwd *pw; char struid[32]; char *user_name; char *argv[8]; if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL) user_name = pw->pw_name; else { slprintf(struid, sizeof(struid), "%d", getuid()); user_name = struid; } slprintf(strspeed, sizeof(strspeed), "%d", baud_rate); argv[0] = script; argv[1] = ifname; argv[2] = peer_authname; argv[3] = user_name; argv[4] = devnam; argv[5] = strspeed; argv[6] = NULL; auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL, 0); } ppp-2.4.5/pppd/cbcp.c000066400000000000000000000235451130035057700143510ustar00rootroot00000000000000/* * cbcp - Call Back Configuration Protocol. * * Copyright (c) 1995 Pedro Roque Marques. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The names of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Pedro Roque Marques * " * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: cbcp.c,v 1.17 2006/05/22 00:04:07 paulus Exp $" #include #include #include #include #include "pppd.h" #include "cbcp.h" #include "fsm.h" #include "lcp.h" static const char rcsid[] = RCSID; /* * Options. */ static int setcbcp __P((char **)); static option_t cbcp_option_list[] = { { "callback", o_special, (void *)setcbcp, "Ask for callback", OPT_PRIO | OPT_A2STRVAL, &cbcp[0].us_number }, { NULL } }; /* * Protocol entry points. */ static void cbcp_init __P((int unit)); static void cbcp_open __P((int unit)); static void cbcp_lowerup __P((int unit)); static void cbcp_input __P((int unit, u_char *pkt, int len)); static void cbcp_protrej __P((int unit)); static int cbcp_printpkt __P((u_char *pkt, int len, void (*printer) __P((void *, char *, ...)), void *arg)); struct protent cbcp_protent = { PPP_CBCP, cbcp_init, cbcp_input, cbcp_protrej, cbcp_lowerup, NULL, cbcp_open, NULL, cbcp_printpkt, NULL, 0, "CBCP", NULL, cbcp_option_list, NULL, NULL, NULL }; cbcp_state cbcp[NUM_PPP]; /* internal prototypes */ static void cbcp_recvreq __P((cbcp_state *us, u_char *pckt, int len)); static void cbcp_resp __P((cbcp_state *us)); static void cbcp_up __P((cbcp_state *us)); static void cbcp_recvack __P((cbcp_state *us, u_char *pckt, int len)); static void cbcp_send __P((cbcp_state *us, int code, u_char *buf, int len)); /* option processing */ static int setcbcp(argv) char **argv; { lcp_wantoptions[0].neg_cbcp = 1; cbcp_protent.enabled_flag = 1; cbcp[0].us_number = strdup(*argv); if (cbcp[0].us_number == 0) novm("callback number"); cbcp[0].us_type |= (1 << CB_CONF_USER); cbcp[0].us_type |= (1 << CB_CONF_ADMIN); return (1); } /* init state */ static void cbcp_init(iface) int iface; { cbcp_state *us; us = &cbcp[iface]; memset(us, 0, sizeof(cbcp_state)); us->us_unit = iface; us->us_type |= (1 << CB_CONF_NO); } /* lower layer is up */ static void cbcp_lowerup(iface) int iface; { cbcp_state *us = &cbcp[iface]; dbglog("cbcp_lowerup"); dbglog("want: %d", us->us_type); if (us->us_type == CB_CONF_USER) dbglog("phone no: %s", us->us_number); } static void cbcp_open(unit) int unit; { dbglog("cbcp_open"); } /* process an incomming packet */ static void cbcp_input(unit, inpacket, pktlen) int unit; u_char *inpacket; int pktlen; { u_char *inp; u_char code, id; u_short len; cbcp_state *us = &cbcp[unit]; inp = inpacket; if (pktlen < CBCP_MINLEN) { if (debug) dbglog("CBCP packet is too small"); return; } GETCHAR(code, inp); GETCHAR(id, inp); GETSHORT(len, inp); if (len > pktlen || len < CBCP_MINLEN) { if (debug) dbglog("CBCP packet: invalid length %d", len); return; } len -= CBCP_MINLEN; switch(code) { case CBCP_REQ: us->us_id = id; cbcp_recvreq(us, inp, len); break; case CBCP_RESP: if (debug) dbglog("CBCP_RESP received"); break; case CBCP_ACK: if (debug && id != us->us_id) dbglog("id doesn't match: expected %d recv %d", us->us_id, id); cbcp_recvack(us, inp, len); break; default: break; } } /* protocol was rejected by foe */ void cbcp_protrej(int iface) { } char *cbcp_codenames[] = { "Request", "Response", "Ack" }; char *cbcp_optionnames[] = { "NoCallback", "UserDefined", "AdminDefined", "List" }; /* pretty print a packet */ static int cbcp_printpkt(p, plen, printer, arg) u_char *p; int plen; void (*printer) __P((void *, char *, ...)); void *arg; { int code, opt, id, len, olen, delay; u_char *pstart; 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(cbcp_codenames) / sizeof(char *)) printer(arg, " %s", cbcp_codenames[code-1]); else printer(arg, " code=0x%x", code); printer(arg, " id=0x%x", id); len -= HEADERLEN; switch (code) { case CBCP_REQ: case CBCP_RESP: case CBCP_ACK: while(len >= 2) { GETCHAR(opt, p); GETCHAR(olen, p); if (olen < 2 || olen > len) { break; } printer(arg, " <"); len -= olen; if (opt >= 1 && opt <= sizeof(cbcp_optionnames) / sizeof(char *)) printer(arg, " %s", cbcp_optionnames[opt-1]); else printer(arg, " option=0x%x", opt); if (olen > 2) { GETCHAR(delay, p); printer(arg, " delay = %d", delay); } if (olen > 3) { int addrt; char str[256]; GETCHAR(addrt, p); memcpy(str, p, olen - 4); str[olen - 4] = 0; printer(arg, " number = %s", str); } printer(arg, ">"); } break; default: break; } for (; len > 0; --len) { GETCHAR(code, p); printer(arg, " %.2x", code); } return p - pstart; } /* received CBCP request */ static void cbcp_recvreq(us, pckt, pcktlen) cbcp_state *us; u_char *pckt; int pcktlen; { u_char type, opt_len, delay, addr_type; char address[256]; int len = pcktlen; address[0] = 0; while (len >= 2) { dbglog("length: %d", len); GETCHAR(type, pckt); GETCHAR(opt_len, pckt); if (opt_len < 2 || opt_len > len) break; if (opt_len > 2) GETCHAR(delay, pckt); us->us_allowed |= (1 << type); switch(type) { case CB_CONF_NO: dbglog("no callback allowed"); break; case CB_CONF_USER: dbglog("user callback allowed"); if (opt_len > 4) { GETCHAR(addr_type, pckt); memcpy(address, pckt, opt_len - 4); address[opt_len - 4] = 0; if (address[0]) dbglog("address: %s", address); } break; case CB_CONF_ADMIN: dbglog("user admin defined allowed"); break; case CB_CONF_LIST: break; } len -= opt_len; } if (len != 0) { if (debug) dbglog("cbcp_recvreq: malformed packet (%d bytes left)", len); return; } cbcp_resp(us); } static void cbcp_resp(us) cbcp_state *us; { u_char cb_type; u_char buf[256]; u_char *bufp = buf; int len = 0; int slen; cb_type = us->us_allowed & us->us_type; dbglog("cbcp_resp cb_type=%d", cb_type); #if 0 if (!cb_type) lcp_down(us->us_unit); #endif if (cb_type & ( 1 << CB_CONF_USER ) ) { dbglog("cbcp_resp CONF_USER"); slen = strlen(us->us_number); if (slen > 250) { warn("callback number truncated to 250 characters"); slen = 250; } PUTCHAR(CB_CONF_USER, bufp); len = 3 + 1 + slen + 1; PUTCHAR(len , bufp); PUTCHAR(5, bufp); /* delay */ PUTCHAR(1, bufp); BCOPY(us->us_number, bufp, slen + 1); cbcp_send(us, CBCP_RESP, buf, len); return; } if (cb_type & ( 1 << CB_CONF_ADMIN ) ) { dbglog("cbcp_resp CONF_ADMIN"); PUTCHAR(CB_CONF_ADMIN, bufp); len = 3; PUTCHAR(len, bufp); PUTCHAR(5, bufp); /* delay */ cbcp_send(us, CBCP_RESP, buf, len); return; } if (cb_type & ( 1 << CB_CONF_NO ) ) { dbglog("cbcp_resp CONF_NO"); PUTCHAR(CB_CONF_NO, bufp); len = 2; PUTCHAR(len , bufp); cbcp_send(us, CBCP_RESP, buf, len); start_networks(us->us_unit); return; } } static void cbcp_send(us, code, buf, len) cbcp_state *us; int code; u_char *buf; int len; { u_char *outp; int outlen; outp = outpacket_buf; outlen = 4 + len; MAKEHEADER(outp, PPP_CBCP); PUTCHAR(code, outp); PUTCHAR(us->us_id, outp); PUTSHORT(outlen, outp); if (len) BCOPY(buf, outp, len); output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN); } static void cbcp_recvack(us, pckt, len) cbcp_state *us; u_char *pckt; int len; { u_char type, delay, addr_type; int opt_len; char address[256]; if (len >= 2) { GETCHAR(type, pckt); GETCHAR(opt_len, pckt); if (opt_len >= 2 && opt_len <= len) { if (opt_len > 2) GETCHAR(delay, pckt); if (opt_len > 4) { GETCHAR(addr_type, pckt); memcpy(address, pckt, opt_len - 4); address[opt_len - 4] = 0; if (address[0]) dbglog("peer will call: %s", address); } if (type == CB_CONF_NO) return; cbcp_up(us); } else if (debug) dbglog("cbcp_recvack: malformed packet"); } } /* ok peer will do callback */ static void cbcp_up(us) cbcp_state *us; { persist = 0; status = EXIT_CALLBACK; lcp_close(0, "Call me back, please"); } ppp-2.4.5/pppd/cbcp.h000066400000000000000000000007761130035057700143570ustar00rootroot00000000000000#ifndef CBCP_H #define CBCP_H typedef struct cbcp_state { int us_unit; /* Interface unit number */ u_char us_id; /* Current id */ u_char us_allowed; int us_type; char *us_number; /* Telefone Number */ } cbcp_state; extern cbcp_state cbcp[]; extern struct protent cbcp_protent; #define CBCP_MINLEN 4 #define CBCP_REQ 1 #define CBCP_RESP 2 #define CBCP_ACK 3 #define CB_CONF_NO 1 #define CB_CONF_USER 2 #define CB_CONF_ADMIN 3 #define CB_CONF_LIST 4 #endif ppp-2.4.5/pppd/ccp.c000066400000000000000000001276511130035057700142120ustar00rootroot00000000000000/* * ccp.c - PPP Compression Control Protocol. * * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: ccp.c,v 1.50 2005/06/26 19:34:41 carlsonj Exp $" #include #include #include "pppd.h" #include "fsm.h" #include "ccp.h" #include #ifdef MPPE #include "chap_ms.h" /* mppe_xxxx_key, mppe_keys_set */ #include "lcp.h" /* lcp_close(), lcp_fsm */ #endif static const char rcsid[] = RCSID; /* * Unfortunately there is a bug in zlib which means that using a * size of 8 (window size = 256) for Deflate compression will cause * buffer overruns and kernel crashes in the deflate module. * Until this is fixed we only accept sizes in the range 9 .. 15. * Thanks to James Carlson for pointing this out. */ #define DEFLATE_MIN_WORKS 9 /* * Command-line options. */ static int setbsdcomp __P((char **)); static int setdeflate __P((char **)); static char bsd_value[8]; static char deflate_value[8]; /* * Option variables. */ #ifdef MPPE bool refuse_mppe_stateful = 1; /* Allow stateful mode? */ #endif static option_t ccp_option_list[] = { { "noccp", o_bool, &ccp_protent.enabled_flag, "Disable CCP negotiation" }, { "-ccp", o_bool, &ccp_protent.enabled_flag, "Disable CCP negotiation", OPT_ALIAS }, { "bsdcomp", o_special, (void *)setbsdcomp, "Request BSD-Compress packet compression", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, bsd_value }, { "nobsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress, "don't allow BSD-Compress", OPT_PRIOSUB | OPT_A2CLR, &ccp_allowoptions[0].bsd_compress }, { "-bsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress, "don't allow BSD-Compress", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &ccp_allowoptions[0].bsd_compress }, { "deflate", o_special, (void *)setdeflate, "request Deflate compression", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, deflate_value }, { "nodeflate", o_bool, &ccp_wantoptions[0].deflate, "don't allow Deflate compression", OPT_PRIOSUB | OPT_A2CLR, &ccp_allowoptions[0].deflate }, { "-deflate", o_bool, &ccp_wantoptions[0].deflate, "don't allow Deflate compression", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &ccp_allowoptions[0].deflate }, { "nodeflatedraft", o_bool, &ccp_wantoptions[0].deflate_draft, "don't use draft deflate #", OPT_A2COPY, &ccp_allowoptions[0].deflate_draft }, { "predictor1", o_bool, &ccp_wantoptions[0].predictor_1, "request Predictor-1", OPT_PRIO | 1 }, { "nopredictor1", o_bool, &ccp_wantoptions[0].predictor_1, "don't allow Predictor-1", OPT_PRIOSUB | OPT_A2CLR, &ccp_allowoptions[0].predictor_1 }, { "-predictor1", o_bool, &ccp_wantoptions[0].predictor_1, "don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &ccp_allowoptions[0].predictor_1 }, #ifdef MPPE /* MPPE options are symmetrical ... we only set wantoptions here */ { "require-mppe", o_bool, &ccp_wantoptions[0].mppe, "require MPPE encryption", OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 }, { "+mppe", o_bool, &ccp_wantoptions[0].mppe, "require MPPE encryption", OPT_ALIAS | OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 }, { "nomppe", o_bool, &ccp_wantoptions[0].mppe, "don't allow MPPE encryption", OPT_PRIO }, { "-mppe", o_bool, &ccp_wantoptions[0].mppe, "don't allow MPPE encryption", OPT_ALIAS | OPT_PRIO }, /* We use ccp_allowoptions[0].mppe as a junk var ... it is reset later */ { "require-mppe-40", o_bool, &ccp_allowoptions[0].mppe, "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40, &ccp_wantoptions[0].mppe }, { "+mppe-40", o_bool, &ccp_allowoptions[0].mppe, "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40, &ccp_wantoptions[0].mppe }, { "nomppe-40", o_bool, &ccp_allowoptions[0].mppe, "don't allow MPPE 40-bit encryption", OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, &ccp_wantoptions[0].mppe }, { "-mppe-40", o_bool, &ccp_allowoptions[0].mppe, "don't allow MPPE 40-bit encryption", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, &ccp_wantoptions[0].mppe }, { "require-mppe-128", o_bool, &ccp_allowoptions[0].mppe, "require MPPE 128-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_128, &ccp_wantoptions[0].mppe }, { "+mppe-128", o_bool, &ccp_allowoptions[0].mppe, "require MPPE 128-bit encryption", OPT_ALIAS | OPT_PRIO | OPT_A2OR | MPPE_OPT_128, &ccp_wantoptions[0].mppe }, { "nomppe-128", o_bool, &ccp_allowoptions[0].mppe, "don't allow MPPE 128-bit encryption", OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, &ccp_wantoptions[0].mppe }, { "-mppe-128", o_bool, &ccp_allowoptions[0].mppe, "don't allow MPPE 128-bit encryption", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, &ccp_wantoptions[0].mppe }, /* strange one; we always request stateless, but will we allow stateful? */ { "mppe-stateful", o_bool, &refuse_mppe_stateful, "allow MPPE stateful mode", OPT_PRIO }, { "nomppe-stateful", o_bool, &refuse_mppe_stateful, "disallow MPPE stateful mode", OPT_PRIO | 1 }, #endif /* MPPE */ { NULL } }; /* * Protocol entry points from main code. */ static void ccp_init __P((int unit)); static void ccp_open __P((int unit)); static void ccp_close __P((int unit, char *)); static void ccp_lowerup __P((int unit)); static void ccp_lowerdown __P((int)); static void ccp_input __P((int unit, u_char *pkt, int len)); static void ccp_protrej __P((int unit)); static int ccp_printpkt __P((u_char *pkt, int len, void (*printer) __P((void *, char *, ...)), void *arg)); static void ccp_datainput __P((int unit, u_char *pkt, int len)); struct protent ccp_protent = { PPP_CCP, ccp_init, ccp_input, ccp_protrej, ccp_lowerup, ccp_lowerdown, ccp_open, ccp_close, ccp_printpkt, ccp_datainput, 1, "CCP", "Compressed", ccp_option_list, NULL, NULL, NULL }; fsm ccp_fsm[NUM_PPP]; ccp_options ccp_wantoptions[NUM_PPP]; /* what to request the peer to use */ ccp_options ccp_gotoptions[NUM_PPP]; /* what the peer agreed to do */ ccp_options ccp_allowoptions[NUM_PPP]; /* what we'll agree to do */ ccp_options ccp_hisoptions[NUM_PPP]; /* what we agreed to do */ /* * Callbacks for fsm code. */ static void ccp_resetci __P((fsm *)); static int ccp_cilen __P((fsm *)); static void ccp_addci __P((fsm *, u_char *, int *)); static int ccp_ackci __P((fsm *, u_char *, int)); static int ccp_nakci __P((fsm *, u_char *, int, int)); static int ccp_rejci __P((fsm *, u_char *, int)); static int ccp_reqci __P((fsm *, u_char *, int *, int)); static void ccp_up __P((fsm *)); static void ccp_down __P((fsm *)); static int ccp_extcode __P((fsm *, int, int, u_char *, int)); static void ccp_rack_timeout __P((void *)); static char *method_name __P((ccp_options *, ccp_options *)); static fsm_callbacks ccp_callbacks = { ccp_resetci, ccp_cilen, ccp_addci, ccp_ackci, ccp_nakci, ccp_rejci, ccp_reqci, ccp_up, ccp_down, NULL, NULL, NULL, NULL, ccp_extcode, "CCP" }; /* * Do we want / did we get any compression? */ #define ANY_COMPRESS(opt) ((opt).deflate || (opt).bsd_compress \ || (opt).predictor_1 || (opt).predictor_2 \ || (opt).mppe) /* * Local state (mainly for handling reset-reqs and reset-acks). */ static int ccp_localstate[NUM_PPP]; #define RACK_PENDING 1 /* waiting for reset-ack */ #define RREQ_REPEAT 2 /* send another reset-req if no reset-ack */ #define RACKTIMEOUT 1 /* second */ static int all_rejected[NUM_PPP]; /* we rejected all peer's options */ /* * Option parsing. */ static int setbsdcomp(argv) char **argv; { int rbits, abits; char *str, *endp; str = *argv; abits = rbits = strtol(str, &endp, 0); if (endp != str && *endp == ',') { str = endp + 1; abits = strtol(str, &endp, 0); } if (*endp != 0 || endp == str) { option_error("invalid parameter '%s' for bsdcomp option", *argv); return 0; } if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS)) || (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) { option_error("bsdcomp option values must be 0 or %d .. %d", BSD_MIN_BITS, BSD_MAX_BITS); return 0; } if (rbits > 0) { ccp_wantoptions[0].bsd_compress = 1; ccp_wantoptions[0].bsd_bits = rbits; } else ccp_wantoptions[0].bsd_compress = 0; if (abits > 0) { ccp_allowoptions[0].bsd_compress = 1; ccp_allowoptions[0].bsd_bits = abits; } else ccp_allowoptions[0].bsd_compress = 0; slprintf(bsd_value, sizeof(bsd_value), rbits == abits? "%d": "%d,%d", rbits, abits); return 1; } static int setdeflate(argv) char **argv; { int rbits, abits; char *str, *endp; str = *argv; abits = rbits = strtol(str, &endp, 0); if (endp != str && *endp == ',') { str = endp + 1; abits = strtol(str, &endp, 0); } if (*endp != 0 || endp == str) { option_error("invalid parameter '%s' for deflate option", *argv); return 0; } if ((rbits != 0 && (rbits < DEFLATE_MIN_SIZE || rbits > DEFLATE_MAX_SIZE)) || (abits != 0 && (abits < DEFLATE_MIN_SIZE || abits > DEFLATE_MAX_SIZE))) { option_error("deflate option values must be 0 or %d .. %d", DEFLATE_MIN_SIZE, DEFLATE_MAX_SIZE); return 0; } if (rbits == DEFLATE_MIN_SIZE || abits == DEFLATE_MIN_SIZE) { if (rbits == DEFLATE_MIN_SIZE) rbits = DEFLATE_MIN_WORKS; if (abits == DEFLATE_MIN_SIZE) abits = DEFLATE_MIN_WORKS; warn("deflate option value of %d changed to %d to avoid zlib bug", DEFLATE_MIN_SIZE, DEFLATE_MIN_WORKS); } if (rbits > 0) { ccp_wantoptions[0].deflate = 1; ccp_wantoptions[0].deflate_size = rbits; } else ccp_wantoptions[0].deflate = 0; if (abits > 0) { ccp_allowoptions[0].deflate = 1; ccp_allowoptions[0].deflate_size = abits; } else ccp_allowoptions[0].deflate = 0; slprintf(deflate_value, sizeof(deflate_value), rbits == abits? "%d": "%d,%d", rbits, abits); return 1; } /* * ccp_init - initialize CCP. */ static void ccp_init(unit) int unit; { fsm *f = &ccp_fsm[unit]; f->unit = unit; f->protocol = PPP_CCP; f->callbacks = &ccp_callbacks; fsm_init(f); memset(&ccp_wantoptions[unit], 0, sizeof(ccp_options)); memset(&ccp_gotoptions[unit], 0, sizeof(ccp_options)); memset(&ccp_allowoptions[unit], 0, sizeof(ccp_options)); memset(&ccp_hisoptions[unit], 0, sizeof(ccp_options)); ccp_wantoptions[0].deflate = 1; ccp_wantoptions[0].deflate_size = DEFLATE_MAX_SIZE; ccp_wantoptions[0].deflate_correct = 1; ccp_wantoptions[0].deflate_draft = 1; ccp_allowoptions[0].deflate = 1; ccp_allowoptions[0].deflate_size = DEFLATE_MAX_SIZE; ccp_allowoptions[0].deflate_correct = 1; ccp_allowoptions[0].deflate_draft = 1; ccp_wantoptions[0].bsd_compress = 1; ccp_wantoptions[0].bsd_bits = BSD_MAX_BITS; ccp_allowoptions[0].bsd_compress = 1; ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS; ccp_allowoptions[0].predictor_1 = 1; } /* * ccp_open - CCP is allowed to come up. */ static void ccp_open(unit) int unit; { fsm *f = &ccp_fsm[unit]; if (f->state != OPENED) ccp_flags_set(unit, 1, 0); /* * Find out which compressors the kernel supports before * deciding whether to open in silent mode. */ ccp_resetci(f); if (!ANY_COMPRESS(ccp_gotoptions[unit])) f->flags |= OPT_SILENT; fsm_open(f); } /* * ccp_close - Terminate CCP. */ static void ccp_close(unit, reason) int unit; char *reason; { ccp_flags_set(unit, 0, 0); fsm_close(&ccp_fsm[unit], reason); } /* * ccp_lowerup - we may now transmit CCP packets. */ static void ccp_lowerup(unit) int unit; { fsm_lowerup(&ccp_fsm[unit]); } /* * ccp_lowerdown - we may not transmit CCP packets. */ static void ccp_lowerdown(unit) int unit; { fsm_lowerdown(&ccp_fsm[unit]); } /* * ccp_input - process a received CCP packet. */ static void ccp_input(unit, p, len) int unit; u_char *p; int len; { fsm *f = &ccp_fsm[unit]; int oldstate; /* * Check for a terminate-request so we can print a message. */ oldstate = f->state; fsm_input(f, p, len); if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED) { notice("Compression disabled by peer."); #ifdef MPPE if (ccp_gotoptions[unit].mppe) { error("MPPE disabled, closing LCP"); lcp_close(unit, "MPPE disabled by peer"); } #endif } /* * If we get a terminate-ack and we're not asking for compression, * close CCP. */ if (oldstate == REQSENT && p[0] == TERMACK && !ANY_COMPRESS(ccp_gotoptions[unit])) ccp_close(unit, "No compression negotiated"); } /* * Handle a CCP-specific code. */ static int ccp_extcode(f, code, id, p, len) fsm *f; int code, id; u_char *p; int len; { switch (code) { case CCP_RESETREQ: if (f->state != OPENED) break; /* send a reset-ack, which the transmitter will see and reset its compression state. */ fsm_sdata(f, CCP_RESETACK, id, NULL, 0); break; case CCP_RESETACK: if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) { ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT); UNTIMEOUT(ccp_rack_timeout, f); } break; default: return 0; } return 1; } /* * ccp_protrej - peer doesn't talk CCP. */ static void ccp_protrej(unit) int unit; { ccp_flags_set(unit, 0, 0); fsm_lowerdown(&ccp_fsm[unit]); #ifdef MPPE if (ccp_gotoptions[unit].mppe) { error("MPPE required but peer negotiation failed"); lcp_close(unit, "MPPE required but peer negotiation failed"); } #endif } /* * ccp_resetci - initialize at start of negotiation. */ static void ccp_resetci(f) fsm *f; { ccp_options *go = &ccp_gotoptions[f->unit]; u_char opt_buf[CCP_MAX_OPTION_LENGTH]; *go = ccp_wantoptions[f->unit]; all_rejected[f->unit] = 0; #ifdef MPPE if (go->mppe) { ccp_options *ao = &ccp_allowoptions[f->unit]; int auth_mschap_bits = auth_done[f->unit]; int numbits; /* * Start with a basic sanity check: mschap[v2] auth must be in * exactly one direction. RFC 3079 says that the keys are * 'derived from the credentials of the peer that initiated the call', * however the PPP protocol doesn't have such a concept, and pppd * cannot get this info externally. Instead we do the best we can. * NB: If MPPE is required, all other compression opts are invalid. * So, we return right away if we can't do it. */ /* Leave only the mschap auth bits set */ auth_mschap_bits &= (CHAP_MS_WITHPEER | CHAP_MS_PEER | CHAP_MS2_WITHPEER | CHAP_MS2_PEER); /* Count the mschap auths */ auth_mschap_bits >>= CHAP_MS_SHIFT; numbits = 0; do { numbits += auth_mschap_bits & 1; auth_mschap_bits >>= 1; } while (auth_mschap_bits); if (numbits > 1) { error("MPPE required, but auth done in both directions."); lcp_close(f->unit, "MPPE required but not available"); return; } if (!numbits) { error("MPPE required, but MS-CHAP[v2] auth not performed."); lcp_close(f->unit, "MPPE required but not available"); return; } /* A plugin (eg radius) may not have obtained key material. */ if (!mppe_keys_set) { error("MPPE required, but keys are not available. " "Possible plugin problem?"); lcp_close(f->unit, "MPPE required but not available"); return; } /* LM auth not supported for MPPE */ if (auth_done[f->unit] & (CHAP_MS_WITHPEER | CHAP_MS_PEER)) { /* This might be noise */ if (go->mppe & MPPE_OPT_40) { notice("Disabling 40-bit MPPE; MS-CHAP LM not supported"); go->mppe &= ~MPPE_OPT_40; ccp_wantoptions[f->unit].mppe &= ~MPPE_OPT_40; } } /* Last check: can we actually negotiate something? */ if (!(go->mppe & (MPPE_OPT_40 | MPPE_OPT_128))) { /* Could be misconfig, could be 40-bit disabled above. */ error("MPPE required, but both 40-bit and 128-bit disabled."); lcp_close(f->unit, "MPPE required but not available"); return; } /* sync options */ ao->mppe = go->mppe; /* MPPE is not compatible with other compression types */ ao->bsd_compress = go->bsd_compress = 0; ao->predictor_1 = go->predictor_1 = 0; ao->predictor_2 = go->predictor_2 = 0; ao->deflate = go->deflate = 0; } #endif /* MPPE */ /* * Check whether the kernel knows about the various * compression methods we might request. */ #ifdef MPPE if (go->mppe) { opt_buf[0] = CI_MPPE; opt_buf[1] = CILEN_MPPE; MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]); /* Key material unimportant here. */ if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0) { error("MPPE required, but kernel has no support."); lcp_close(f->unit, "MPPE required but not available"); } } #endif if (go->bsd_compress) { opt_buf[0] = CI_BSD_COMPRESS; opt_buf[1] = CILEN_BSD_COMPRESS; opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, BSD_MIN_BITS); if (ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0) <= 0) go->bsd_compress = 0; } if (go->deflate) { if (go->deflate_correct) { opt_buf[0] = CI_DEFLATE; opt_buf[1] = CILEN_DEFLATE; opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_WORKS); opt_buf[3] = DEFLATE_CHK_SEQUENCE; if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0) go->deflate_correct = 0; } if (go->deflate_draft) { opt_buf[0] = CI_DEFLATE_DRAFT; opt_buf[1] = CILEN_DEFLATE; opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_WORKS); opt_buf[3] = DEFLATE_CHK_SEQUENCE; if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0) go->deflate_draft = 0; } if (!go->deflate_correct && !go->deflate_draft) go->deflate = 0; } if (go->predictor_1) { opt_buf[0] = CI_PREDICTOR_1; opt_buf[1] = CILEN_PREDICTOR_1; if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_1, 0) <= 0) go->predictor_1 = 0; } if (go->predictor_2) { opt_buf[0] = CI_PREDICTOR_2; opt_buf[1] = CILEN_PREDICTOR_2; if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_2, 0) <= 0) go->predictor_2 = 0; } } /* * ccp_cilen - Return total length of our configuration info. */ static int ccp_cilen(f) fsm *f; { ccp_options *go = &ccp_gotoptions[f->unit]; return (go->bsd_compress? CILEN_BSD_COMPRESS: 0) + (go->deflate? CILEN_DEFLATE: 0) + (go->predictor_1? CILEN_PREDICTOR_1: 0) + (go->predictor_2? CILEN_PREDICTOR_2: 0) + (go->mppe? CILEN_MPPE: 0); } /* * ccp_addci - put our requests in a packet. */ static void ccp_addci(f, p, lenp) fsm *f; u_char *p; int *lenp; { int res; ccp_options *go = &ccp_gotoptions[f->unit]; u_char *p0 = p; /* * Add the compression types that we can receive, in decreasing * preference order. Get the kernel to allocate the first one * in case it gets Acked. */ #ifdef MPPE if (go->mppe) { u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN]; p[0] = opt_buf[0] = CI_MPPE; p[1] = opt_buf[1] = CILEN_MPPE; MPPE_OPTS_TO_CI(go->mppe, &p[2]); MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]); BCOPY(mppe_recv_key, &opt_buf[CILEN_MPPE], MPPE_MAX_KEY_LEN); res = ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0); if (res > 0) p += CILEN_MPPE; else /* This shouldn't happen, we've already tested it! */ lcp_close(f->unit, "MPPE required but not available in kernel"); } #endif if (go->deflate) { p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT; p[1] = CILEN_DEFLATE; p[2] = DEFLATE_MAKE_OPT(go->deflate_size); p[3] = DEFLATE_CHK_SEQUENCE; if (p != p0) { p += CILEN_DEFLATE; } else { for (;;) { if (go->deflate_size < DEFLATE_MIN_WORKS) { go->deflate = 0; break; } res = ccp_test(f->unit, p, CILEN_DEFLATE, 0); if (res > 0) { p += CILEN_DEFLATE; break; } else if (res < 0) { go->deflate = 0; break; } --go->deflate_size; p[2] = DEFLATE_MAKE_OPT(go->deflate_size); } } if (p != p0 && go->deflate_correct && go->deflate_draft) { p[0] = CI_DEFLATE_DRAFT; p[1] = CILEN_DEFLATE; p[2] = p[2 - CILEN_DEFLATE]; p[3] = DEFLATE_CHK_SEQUENCE; p += CILEN_DEFLATE; } } if (go->bsd_compress) { p[0] = CI_BSD_COMPRESS; p[1] = CILEN_BSD_COMPRESS; p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); if (p != p0) { p += CILEN_BSD_COMPRESS; /* not the first option */ } else { for (;;) { if (go->bsd_bits < BSD_MIN_BITS) { go->bsd_compress = 0; break; } res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0); if (res > 0) { p += CILEN_BSD_COMPRESS; break; } else if (res < 0) { go->bsd_compress = 0; break; } --go->bsd_bits; p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); } } } /* XXX Should Predictor 2 be preferable to Predictor 1? */ if (go->predictor_1) { p[0] = CI_PREDICTOR_1; p[1] = CILEN_PREDICTOR_1; if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 0) <= 0) { go->predictor_1 = 0; } else { p += CILEN_PREDICTOR_1; } } if (go->predictor_2) { p[0] = CI_PREDICTOR_2; p[1] = CILEN_PREDICTOR_2; if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 0) <= 0) { go->predictor_2 = 0; } else { p += CILEN_PREDICTOR_2; } } go->method = (p > p0)? p0[0]: -1; *lenp = p - p0; } /* * ccp_ackci - process a received configure-ack, and return * 1 iff the packet was OK. */ static int ccp_ackci(f, p, len) fsm *f; u_char *p; int len; { ccp_options *go = &ccp_gotoptions[f->unit]; u_char *p0 = p; #ifdef MPPE if (go->mppe) { u_char opt_buf[CILEN_MPPE]; opt_buf[0] = CI_MPPE; opt_buf[1] = CILEN_MPPE; MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]); if (len < CILEN_MPPE || memcmp(opt_buf, p, CILEN_MPPE)) return 0; p += CILEN_MPPE; len -= CILEN_MPPE; /* XXX Cope with first/fast ack */ if (len == 0) return 1; } #endif if (go->deflate) { if (len < CILEN_DEFLATE || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) || p[1] != CILEN_DEFLATE || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) || p[3] != DEFLATE_CHK_SEQUENCE) return 0; p += CILEN_DEFLATE; len -= CILEN_DEFLATE; /* XXX Cope with first/fast ack */ if (len == 0) return 1; if (go->deflate_correct && go->deflate_draft) { if (len < CILEN_DEFLATE || p[0] != CI_DEFLATE_DRAFT || p[1] != CILEN_DEFLATE || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) || p[3] != DEFLATE_CHK_SEQUENCE) return 0; p += CILEN_DEFLATE; len -= CILEN_DEFLATE; } } if (go->bsd_compress) { if (len < CILEN_BSD_COMPRESS || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) return 0; p += CILEN_BSD_COMPRESS; len -= CILEN_BSD_COMPRESS; /* XXX Cope with first/fast ack */ if (p == p0 && len == 0) return 1; } if (go->predictor_1) { if (len < CILEN_PREDICTOR_1 || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1) return 0; p += CILEN_PREDICTOR_1; len -= CILEN_PREDICTOR_1; /* XXX Cope with first/fast ack */ if (p == p0 && len == 0) return 1; } if (go->predictor_2) { if (len < CILEN_PREDICTOR_2 || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2) return 0; p += CILEN_PREDICTOR_2; len -= CILEN_PREDICTOR_2; /* XXX Cope with first/fast ack */ if (p == p0 && len == 0) return 1; } if (len != 0) return 0; return 1; } /* * ccp_nakci - process received configure-nak. * Returns 1 iff the nak was OK. */ static int ccp_nakci(f, p, len, treat_as_reject) fsm *f; u_char *p; int len; int treat_as_reject; { ccp_options *go = &ccp_gotoptions[f->unit]; ccp_options no; /* options we've seen already */ ccp_options try; /* options to ask for next time */ memset(&no, 0, sizeof(no)); try = *go; #ifdef MPPE if (go->mppe && len >= CILEN_MPPE && p[0] == CI_MPPE && p[1] == CILEN_MPPE) { no.mppe = 1; /* * Peer wants us to use a different strength or other setting. * Fail if we aren't willing to use his suggestion. */ MPPE_CI_TO_OPTS(&p[2], try.mppe); if ((try.mppe & MPPE_OPT_STATEFUL) && refuse_mppe_stateful) { error("Refusing MPPE stateful mode offered by peer"); try.mppe = 0; } else if (((go->mppe | MPPE_OPT_STATEFUL) & try.mppe) != try.mppe) { /* Peer must have set options we didn't request (suggest) */ try.mppe = 0; } if (!try.mppe) { error("MPPE required but peer negotiation failed"); lcp_close(f->unit, "MPPE required but peer negotiation failed"); } } #endif /* MPPE */ if (go->deflate && len >= CILEN_DEFLATE && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) && p[1] == CILEN_DEFLATE) { no.deflate = 1; /* * Peer wants us to use a different code size or something. * Stop asking for Deflate if we don't understand his suggestion. */ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_WORKS || p[3] != DEFLATE_CHK_SEQUENCE) try.deflate = 0; else if (DEFLATE_SIZE(p[2]) < go->deflate_size) try.deflate_size = DEFLATE_SIZE(p[2]); p += CILEN_DEFLATE; len -= CILEN_DEFLATE; if (go->deflate_correct && go->deflate_draft && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) { p += CILEN_DEFLATE; len -= CILEN_DEFLATE; } } if (go->bsd_compress && len >= CILEN_BSD_COMPRESS && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) { no.bsd_compress = 1; /* * Peer wants us to use a different number of bits * or a different version. */ if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION) try.bsd_compress = 0; else if (BSD_NBITS(p[2]) < go->bsd_bits) try.bsd_bits = BSD_NBITS(p[2]); p += CILEN_BSD_COMPRESS; len -= CILEN_BSD_COMPRESS; } /* * Predictor-1 and 2 have no options, so they can't be Naked. * * There may be remaining options but we ignore them. */ if (f->state != OPENED) *go = try; return 1; } /* * ccp_rejci - reject some of our suggested compression methods. */ static int ccp_rejci(f, p, len) fsm *f; u_char *p; int len; { ccp_options *go = &ccp_gotoptions[f->unit]; ccp_options try; /* options to request next time */ try = *go; /* * Cope with empty configure-rejects by ceasing to send * configure-requests. */ if (len == 0 && all_rejected[f->unit]) return -1; #ifdef MPPE if (go->mppe && len >= CILEN_MPPE && p[0] == CI_MPPE && p[1] == CILEN_MPPE) { error("MPPE required but peer refused"); lcp_close(f->unit, "MPPE required but peer refused"); p += CILEN_MPPE; len -= CILEN_MPPE; } #endif if (go->deflate_correct && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) { if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) || p[3] != DEFLATE_CHK_SEQUENCE) return 0; /* Rej is bad */ try.deflate_correct = 0; p += CILEN_DEFLATE; len -= CILEN_DEFLATE; } if (go->deflate_draft && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) { if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) || p[3] != DEFLATE_CHK_SEQUENCE) return 0; /* Rej is bad */ try.deflate_draft = 0; p += CILEN_DEFLATE; len -= CILEN_DEFLATE; } if (!try.deflate_correct && !try.deflate_draft) try.deflate = 0; if (go->bsd_compress && len >= CILEN_BSD_COMPRESS && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) { if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) return 0; try.bsd_compress = 0; p += CILEN_BSD_COMPRESS; len -= CILEN_BSD_COMPRESS; } if (go->predictor_1 && len >= CILEN_PREDICTOR_1 && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) { try.predictor_1 = 0; p += CILEN_PREDICTOR_1; len -= CILEN_PREDICTOR_1; } if (go->predictor_2 && len >= CILEN_PREDICTOR_2 && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) { try.predictor_2 = 0; p += CILEN_PREDICTOR_2; len -= CILEN_PREDICTOR_2; } if (len != 0) return 0; if (f->state != OPENED) *go = try; return 1; } /* * ccp_reqci - processed a received configure-request. * Returns CONFACK, CONFNAK or CONFREJ and the packet modified * appropriately. */ static int ccp_reqci(f, p, lenp, dont_nak) fsm *f; u_char *p; int *lenp; int dont_nak; { int ret, newret, res; u_char *p0, *retp; int len, clen, type, nb; ccp_options *ho = &ccp_hisoptions[f->unit]; ccp_options *ao = &ccp_allowoptions[f->unit]; #ifdef MPPE bool rej_for_ci_mppe = 1; /* Are we rejecting based on a bad/missing */ /* CI_MPPE, or due to other options? */ #endif ret = CONFACK; retp = p0 = p; len = *lenp; memset(ho, 0, sizeof(ccp_options)); ho->method = (len > 0)? p[0]: -1; while (len > 0) { newret = CONFACK; if (len < 2 || p[1] < 2 || p[1] > len) { /* length is bad */ clen = len; newret = CONFREJ; } else { type = p[0]; clen = p[1]; switch (type) { #ifdef MPPE case CI_MPPE: if (!ao->mppe || clen != CILEN_MPPE) { newret = CONFREJ; break; } MPPE_CI_TO_OPTS(&p[2], ho->mppe); /* Nak if anything unsupported or unknown are set. */ if (ho->mppe & MPPE_OPT_UNSUPPORTED) { newret = CONFNAK; ho->mppe &= ~MPPE_OPT_UNSUPPORTED; } if (ho->mppe & MPPE_OPT_UNKNOWN) { newret = CONFNAK; ho->mppe &= ~MPPE_OPT_UNKNOWN; } /* Check state opt */ if (ho->mppe & MPPE_OPT_STATEFUL) { /* * We can Nak and request stateless, but it's a * lot easier to just assume the peer will request * it if he can do it; stateful mode is bad over * the Internet -- which is where we expect MPPE. */ if (refuse_mppe_stateful) { error("Refusing MPPE stateful mode offered by peer"); newret = CONFREJ; break; } } /* Find out which of {S,L} are set. */ if ((ho->mppe & MPPE_OPT_128) && (ho->mppe & MPPE_OPT_40)) { /* Both are set, negotiate the strongest. */ newret = CONFNAK; if (ao->mppe & MPPE_OPT_128) ho->mppe &= ~MPPE_OPT_40; else if (ao->mppe & MPPE_OPT_40) ho->mppe &= ~MPPE_OPT_128; else { newret = CONFREJ; break; } } else if (ho->mppe & MPPE_OPT_128) { if (!(ao->mppe & MPPE_OPT_128)) { newret = CONFREJ; break; } } else if (ho->mppe & MPPE_OPT_40) { if (!(ao->mppe & MPPE_OPT_40)) { newret = CONFREJ; break; } } else { /* Neither are set. */ /* We cannot accept this. */ newret = CONFNAK; /* Give the peer our idea of what can be used, so it can choose and confirm */ ho->mppe = ao->mppe; } /* rebuild the opts */ MPPE_OPTS_TO_CI(ho->mppe, &p[2]); if (newret == CONFACK) { u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN]; int mtu; BCOPY(p, opt_buf, CILEN_MPPE); BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE], MPPE_MAX_KEY_LEN); if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 1) <= 0) { /* This shouldn't happen, we've already tested it! */ error("MPPE required, but kernel has no support."); lcp_close(f->unit, "MPPE required but not available"); newret = CONFREJ; break; } /* * We need to decrease the interface MTU by MPPE_PAD * because MPPE frames **grow**. The kernel [must] * allocate MPPE_PAD extra bytes in xmit buffers. */ mtu = netif_get_mtu(f->unit); if (mtu) netif_set_mtu(f->unit, mtu - MPPE_PAD); else newret = CONFREJ; } /* * We have accepted MPPE or are willing to negotiate * MPPE parameters. A CONFREJ is due to subsequent * (non-MPPE) processing. */ rej_for_ci_mppe = 0; break; #endif /* MPPE */ case CI_DEFLATE: case CI_DEFLATE_DRAFT: if (!ao->deflate || clen != CILEN_DEFLATE || (!ao->deflate_correct && type == CI_DEFLATE) || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) { newret = CONFREJ; break; } ho->deflate = 1; ho->deflate_size = nb = DEFLATE_SIZE(p[2]); if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL || p[3] != DEFLATE_CHK_SEQUENCE || nb > ao->deflate_size || nb < DEFLATE_MIN_WORKS) { newret = CONFNAK; if (!dont_nak) { p[2] = DEFLATE_MAKE_OPT(ao->deflate_size); p[3] = DEFLATE_CHK_SEQUENCE; /* fall through to test this #bits below */ } else break; } /* * Check whether we can do Deflate with the window * size they want. If the window is too big, reduce * it until the kernel can cope and nak with that. * We only check this for the first option. */ if (p == p0) { for (;;) { res = ccp_test(f->unit, p, CILEN_DEFLATE, 1); if (res > 0) break; /* it's OK now */ if (res < 0 || nb == DEFLATE_MIN_WORKS || dont_nak) { newret = CONFREJ; p[2] = DEFLATE_MAKE_OPT(ho->deflate_size); break; } newret = CONFNAK; --nb; p[2] = DEFLATE_MAKE_OPT(nb); } } break; case CI_BSD_COMPRESS: if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) { newret = CONFREJ; break; } ho->bsd_compress = 1; ho->bsd_bits = nb = BSD_NBITS(p[2]); if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION || nb > ao->bsd_bits || nb < BSD_MIN_BITS) { newret = CONFNAK; if (!dont_nak) { p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits); /* fall through to test this #bits below */ } else break; } /* * Check whether we can do BSD-Compress with the code * size they want. If the code size is too big, reduce * it until the kernel can cope and nak with that. * We only check this for the first option. */ if (p == p0) { for (;;) { res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 1); if (res > 0) break; if (res < 0 || nb == BSD_MIN_BITS || dont_nak) { newret = CONFREJ; p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ho->bsd_bits); break; } newret = CONFNAK; --nb; p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb); } } break; case CI_PREDICTOR_1: if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) { newret = CONFREJ; break; } ho->predictor_1 = 1; if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 1) <= 0) { newret = CONFREJ; } break; case CI_PREDICTOR_2: if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) { newret = CONFREJ; break; } ho->predictor_2 = 1; if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 1) <= 0) { newret = CONFREJ; } break; default: newret = CONFREJ; } } if (newret == CONFNAK && dont_nak) newret = CONFREJ; if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) { /* we're returning this option */ if (newret == CONFREJ && ret == CONFNAK) retp = p0; ret = newret; if (p != retp) BCOPY(p, retp, clen); retp += clen; } p += clen; len -= clen; } if (ret != CONFACK) { if (ret == CONFREJ && *lenp == retp - p0) all_rejected[f->unit] = 1; else *lenp = retp - p0; } #ifdef MPPE if (ret == CONFREJ && ao->mppe && rej_for_ci_mppe) { error("MPPE required but peer negotiation failed"); lcp_close(f->unit, "MPPE required but peer negotiation failed"); } #endif return ret; } /* * Make a string name for a compression method (or 2). */ static char * method_name(opt, opt2) ccp_options *opt, *opt2; { static char result[64]; if (!ANY_COMPRESS(*opt)) return "(none)"; switch (opt->method) { #ifdef MPPE case CI_MPPE: { char *p = result; char *q = result + sizeof(result); /* 1 past result */ slprintf(p, q - p, "MPPE "); p += 5; if (opt->mppe & MPPE_OPT_128) { slprintf(p, q - p, "128-bit "); p += 8; } if (opt->mppe & MPPE_OPT_40) { slprintf(p, q - p, "40-bit "); p += 7; } if (opt->mppe & MPPE_OPT_STATEFUL) slprintf(p, q - p, "stateful"); else slprintf(p, q - p, "stateless"); break; } #endif case CI_DEFLATE: case CI_DEFLATE_DRAFT: if (opt2 != NULL && opt2->deflate_size != opt->deflate_size) slprintf(result, sizeof(result), "Deflate%s (%d/%d)", (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""), opt->deflate_size, opt2->deflate_size); else slprintf(result, sizeof(result), "Deflate%s (%d)", (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""), opt->deflate_size); break; case CI_BSD_COMPRESS: if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits) slprintf(result, sizeof(result), "BSD-Compress (%d/%d)", opt->bsd_bits, opt2->bsd_bits); else slprintf(result, sizeof(result), "BSD-Compress (%d)", opt->bsd_bits); break; case CI_PREDICTOR_1: return "Predictor 1"; case CI_PREDICTOR_2: return "Predictor 2"; default: slprintf(result, sizeof(result), "Method %d", opt->method); } return result; } /* * CCP has come up - inform the kernel driver and log a message. */ static void ccp_up(f) fsm *f; { ccp_options *go = &ccp_gotoptions[f->unit]; ccp_options *ho = &ccp_hisoptions[f->unit]; char method1[64]; ccp_flags_set(f->unit, 1, 1); if (ANY_COMPRESS(*go)) { if (ANY_COMPRESS(*ho)) { if (go->method == ho->method) { notice("%s compression enabled", method_name(go, ho)); } else { strlcpy(method1, method_name(go, NULL), sizeof(method1)); notice("%s / %s compression enabled", method1, method_name(ho, NULL)); } } else notice("%s receive compression enabled", method_name(go, NULL)); } else if (ANY_COMPRESS(*ho)) notice("%s transmit compression enabled", method_name(ho, NULL)); #ifdef MPPE if (go->mppe) { BZERO(mppe_recv_key, MPPE_MAX_KEY_LEN); BZERO(mppe_send_key, MPPE_MAX_KEY_LEN); continue_networks(f->unit); /* Bring up IP et al */ } #endif } /* * CCP has gone down - inform the kernel driver. */ static void ccp_down(f) fsm *f; { if (ccp_localstate[f->unit] & RACK_PENDING) UNTIMEOUT(ccp_rack_timeout, f); ccp_localstate[f->unit] = 0; ccp_flags_set(f->unit, 1, 0); #ifdef MPPE if (ccp_gotoptions[f->unit].mppe) { ccp_gotoptions[f->unit].mppe = 0; if (lcp_fsm[f->unit].state == OPENED) { /* If LCP is not already going down, make sure it does. */ error("MPPE disabled"); lcp_close(f->unit, "MPPE disabled"); } } #endif } /* * Print the contents of a CCP packet. */ static char *ccp_codenames[] = { "ConfReq", "ConfAck", "ConfNak", "ConfRej", "TermReq", "TermAck", "CodeRej", NULL, NULL, NULL, NULL, NULL, NULL, "ResetReq", "ResetAck", }; static int ccp_printpkt(p, plen, printer, arg) u_char *p; int plen; void (*printer) __P((void *, char *, ...)); void *arg; { u_char *p0, *optend; int code, id, len; int optlen; p0 = p; if (plen < HEADERLEN) return 0; code = p[0]; id = p[1]; len = (p[2] << 8) + p[3]; if (len < HEADERLEN || len > plen) return 0; if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *) && ccp_codenames[code-1] != NULL) printer(arg, " %s", ccp_codenames[code-1]); else printer(arg, " code=0x%x", code); printer(arg, " id=0x%x", id); len -= HEADERLEN; p += HEADERLEN; switch (code) { case CONFREQ: case CONFACK: case CONFNAK: case CONFREJ: /* print list of possible compression methods */ while (len >= 2) { code = p[0]; optlen = p[1]; if (optlen < 2 || optlen > len) break; printer(arg, " <"); len -= optlen; optend = p + optlen; switch (code) { #ifdef MPPE case CI_MPPE: if (optlen >= CILEN_MPPE) { u_char mppe_opts; MPPE_CI_TO_OPTS(&p[2], mppe_opts); printer(arg, "mppe %s %s %s %s %s %s%s", (p[2] & MPPE_H_BIT)? "+H": "-H", (p[5] & MPPE_M_BIT)? "+M": "-M", (p[5] & MPPE_S_BIT)? "+S": "-S", (p[5] & MPPE_L_BIT)? "+L": "-L", (p[5] & MPPE_D_BIT)? "+D": "-D", (p[5] & MPPE_C_BIT)? "+C": "-C", (mppe_opts & MPPE_OPT_UNKNOWN)? " +U": ""); if (mppe_opts & MPPE_OPT_UNKNOWN) printer(arg, " (%.2x %.2x %.2x %.2x)", p[2], p[3], p[4], p[5]); p += CILEN_MPPE; } break; #endif case CI_DEFLATE: case CI_DEFLATE_DRAFT: if (optlen >= CILEN_DEFLATE) { printer(arg, "deflate%s %d", (code == CI_DEFLATE_DRAFT? "(old#)": ""), DEFLATE_SIZE(p[2])); if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL) printer(arg, " method %d", DEFLATE_METHOD(p[2])); if (p[3] != DEFLATE_CHK_SEQUENCE) printer(arg, " check %d", p[3]); p += CILEN_DEFLATE; } break; case CI_BSD_COMPRESS: if (optlen >= CILEN_BSD_COMPRESS) { printer(arg, "bsd v%d %d", BSD_VERSION(p[2]), BSD_NBITS(p[2])); p += CILEN_BSD_COMPRESS; } break; case CI_PREDICTOR_1: if (optlen >= CILEN_PREDICTOR_1) { printer(arg, "predictor 1"); p += CILEN_PREDICTOR_1; } break; case CI_PREDICTOR_2: if (optlen >= CILEN_PREDICTOR_2) { printer(arg, "predictor 2"); p += CILEN_PREDICTOR_2; } break; } while (p < optend) printer(arg, " %.2x", *p++); printer(arg, ">"); } break; case TERMACK: case TERMREQ: if (len > 0 && *p >= ' ' && *p < 0x7f) { print_string((char *)p, len, printer, arg); p += len; len = 0; } break; } /* dump out the rest of the packet in hex */ while (--len >= 0) printer(arg, " %.2x", *p++); return p - p0; } /* * We have received a packet that the decompressor failed to * decompress. Here we would expect to issue a reset-request, but * Motorola has a patent on resetting the compressor as a result of * detecting an error in the decompressed data after decompression. * (See US patent 5,130,993; international patent publication number * WO 91/10289; Australian patent 73296/91.) * * So we ask the kernel whether the error was detected after * decompression; if it was, we take CCP down, thus disabling * compression :-(, otherwise we issue the reset-request. */ static void ccp_datainput(unit, pkt, len) int unit; u_char *pkt; int len; { fsm *f; f = &ccp_fsm[unit]; if (f->state == OPENED) { if (ccp_fatal_error(unit)) { /* * Disable compression by taking CCP down. */ error("Lost compression sync: disabling compression"); ccp_close(unit, "Lost compression sync"); #ifdef MPPE /* * If we were doing MPPE, we must also take the link down. */ if (ccp_gotoptions[unit].mppe) { error("Too many MPPE errors, closing LCP"); lcp_close(unit, "Too many MPPE errors"); } #endif } else { /* * Send a reset-request to reset the peer's compressor. * We don't do that if we are still waiting for an * acknowledgement to a previous reset-request. */ if (!(ccp_localstate[f->unit] & RACK_PENDING)) { fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0); TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); ccp_localstate[f->unit] |= RACK_PENDING; } else ccp_localstate[f->unit] |= RREQ_REPEAT; } } } /* * Timeout waiting for reset-ack. */ static void ccp_rack_timeout(arg) void *arg; { fsm *f = arg; if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) { fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0); TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); ccp_localstate[f->unit] &= ~RREQ_REPEAT; } else ccp_localstate[f->unit] &= ~RACK_PENDING; } ppp-2.4.5/pppd/ccp.h000066400000000000000000000040621130035057700142050ustar00rootroot00000000000000/* * ccp.h - Definitions for PPP Compression Control Protocol. * * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ccp.h,v 1.12 2004/11/04 10:02:26 paulus Exp $ */ typedef struct ccp_options { bool bsd_compress; /* do BSD Compress? */ bool deflate; /* do Deflate? */ bool predictor_1; /* do Predictor-1? */ bool predictor_2; /* do Predictor-2? */ bool deflate_correct; /* use correct code for deflate? */ bool deflate_draft; /* use draft RFC code for deflate? */ bool mppe; /* do MPPE? */ u_short bsd_bits; /* # bits/code for BSD Compress */ u_short deflate_size; /* lg(window size) for Deflate */ short method; /* code for chosen compression method */ } ccp_options; extern fsm ccp_fsm[]; extern ccp_options ccp_wantoptions[]; extern ccp_options ccp_gotoptions[]; extern ccp_options ccp_allowoptions[]; extern ccp_options ccp_hisoptions[]; extern struct protent ccp_protent; ppp-2.4.5/pppd/chap-md5.c000066400000000000000000000064471130035057700150420ustar00rootroot00000000000000/* * chap-md5.c - New CHAP/MD5 implementation. * * Copyright (c) 2003 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: chap-md5.c,v 1.4 2004/11/09 22:39:25 paulus Exp $" #include #include #include "pppd.h" #include "chap-new.h" #include "chap-md5.h" #include "magic.h" #include "md5.h" #define MD5_HASH_SIZE 16 #define MD5_MIN_CHALLENGE 16 #define MD5_MAX_CHALLENGE 24 static void chap_md5_generate_challenge(unsigned char *cp) { int clen; clen = (int)(drand48() * (MD5_MAX_CHALLENGE - MD5_MIN_CHALLENGE)) + MD5_MIN_CHALLENGE; *cp++ = clen; random_bytes(cp, clen); } static int chap_md5_verify_response(int id, char *name, unsigned char *secret, int secret_len, unsigned char *challenge, unsigned char *response, char *message, int message_space) { MD5_CTX ctx; unsigned char idbyte = id; unsigned char hash[MD5_HASH_SIZE]; int challenge_len, response_len; challenge_len = *challenge++; response_len = *response++; if (response_len == MD5_HASH_SIZE) { /* Generate hash of ID, secret, challenge */ MD5_Init(&ctx); MD5_Update(&ctx, &idbyte, 1); MD5_Update(&ctx, secret, secret_len); MD5_Update(&ctx, challenge, challenge_len); MD5_Final(hash, &ctx); /* Test if our hash matches the peer's response */ if (memcmp(hash, response, MD5_HASH_SIZE) == 0) { slprintf(message, message_space, "Access granted"); return 1; } } slprintf(message, message_space, "Access denied"); return 0; } static void chap_md5_make_response(unsigned char *response, int id, char *our_name, unsigned char *challenge, char *secret, int secret_len, unsigned char *private) { MD5_CTX ctx; unsigned char idbyte = id; int challenge_len = *challenge++; MD5_Init(&ctx); MD5_Update(&ctx, &idbyte, 1); MD5_Update(&ctx, (u_char *)secret, secret_len); MD5_Update(&ctx, challenge, challenge_len); MD5_Final(&response[1], &ctx); response[0] = MD5_HASH_SIZE; } static struct chap_digest_type md5_digest = { CHAP_MD5, /* code */ chap_md5_generate_challenge, chap_md5_verify_response, chap_md5_make_response, NULL, /* check_success */ NULL, /* handle_failure */ }; void chap_md5_init(void) { chap_register_digest(&md5_digest); } ppp-2.4.5/pppd/chap-md5.h000066400000000000000000000024121130035057700150330ustar00rootroot00000000000000/* * chap-md5.h - New CHAP/MD5 implementation. * * Copyright (c) 2003 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ extern void chap_md5_init(void); ppp-2.4.5/pppd/chap-new.c000066400000000000000000000407411130035057700151410ustar00rootroot00000000000000/* * chap-new.c - New CHAP implementation. * * Copyright (c) 2003 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: chap-new.c,v 1.9 2007/06/19 02:08:35 carlsonj Exp $" #include #include #include "pppd.h" #include "session.h" #include "chap-new.h" #include "chap-md5.h" #ifdef CHAPMS #include "chap_ms.h" #define MDTYPE_ALL (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5) #else #define MDTYPE_ALL (MDTYPE_MD5) #endif int chap_mdtype_all = MDTYPE_ALL; /* Hook for a plugin to validate CHAP challenge */ int (*chap_verify_hook)(char *name, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space) = NULL; /* * Option variables. */ int chap_timeout_time = 3; int chap_max_transmits = 10; int chap_rechallenge_time = 0; /* * Command-line options. */ static option_t chap_option_list[] = { { "chap-restart", o_int, &chap_timeout_time, "Set timeout for CHAP", OPT_PRIO }, { "chap-max-challenge", o_int, &chap_max_transmits, "Set max #xmits for challenge", OPT_PRIO }, { "chap-interval", o_int, &chap_rechallenge_time, "Set interval for rechallenge", OPT_PRIO }, { NULL } }; /* * Internal state. */ static struct chap_client_state { int flags; char *name; struct chap_digest_type *digest; unsigned char priv[64]; /* private area for digest's use */ } client; /* * These limits apply to challenge and response packets we send. * The +4 is the +1 that we actually need rounded up. */ #define CHAL_MAX_PKTLEN (PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_CHALLENGE_LEN + MAXNAMELEN) #define RESP_MAX_PKTLEN (PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_RESPONSE_LEN + MAXNAMELEN) static struct chap_server_state { int flags; int id; char *name; struct chap_digest_type *digest; int challenge_xmits; int challenge_pktlen; unsigned char challenge[CHAL_MAX_PKTLEN]; char message[256]; } server; /* Values for flags in chap_client_state and chap_server_state */ #define LOWERUP 1 #define AUTH_STARTED 2 #define AUTH_DONE 4 #define AUTH_FAILED 8 #define TIMEOUT_PENDING 0x10 #define CHALLENGE_VALID 0x20 /* * Prototypes. */ static void chap_init(int unit); static void chap_lowerup(int unit); static void chap_lowerdown(int unit); static void chap_timeout(void *arg); static void chap_generate_challenge(struct chap_server_state *ss); static void chap_handle_response(struct chap_server_state *ss, int code, unsigned char *pkt, int len); static int chap_verify_response(char *name, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space); static void chap_respond(struct chap_client_state *cs, int id, unsigned char *pkt, int len); static void chap_handle_status(struct chap_client_state *cs, int code, int id, unsigned char *pkt, int len); static void chap_protrej(int unit); static void chap_input(int unit, unsigned char *pkt, int pktlen); static int chap_print_pkt(unsigned char *p, int plen, void (*printer) __P((void *, char *, ...)), void *arg); /* List of digest types that we know about */ static struct chap_digest_type *chap_digests; /* * chap_init - reset to initial state. */ static void chap_init(int unit) { memset(&client, 0, sizeof(client)); memset(&server, 0, sizeof(server)); chap_md5_init(); #ifdef CHAPMS chapms_init(); #endif } /* * Add a new digest type to the list. */ void chap_register_digest(struct chap_digest_type *dp) { dp->next = chap_digests; chap_digests = dp; } /* * chap_lowerup - we can start doing stuff now. */ static void chap_lowerup(int unit) { struct chap_client_state *cs = &client; struct chap_server_state *ss = &server; cs->flags |= LOWERUP; ss->flags |= LOWERUP; if (ss->flags & AUTH_STARTED) chap_timeout(ss); } static void chap_lowerdown(int unit) { struct chap_client_state *cs = &client; struct chap_server_state *ss = &server; cs->flags = 0; if (ss->flags & TIMEOUT_PENDING) UNTIMEOUT(chap_timeout, ss); ss->flags = 0; } /* * chap_auth_peer - Start authenticating the peer. * If the lower layer is already up, we start sending challenges, * otherwise we wait for the lower layer to come up. */ void chap_auth_peer(int unit, char *our_name, int digest_code) { struct chap_server_state *ss = &server; struct chap_digest_type *dp; if (ss->flags & AUTH_STARTED) { error("CHAP: peer authentication already started!"); return; } for (dp = chap_digests; dp != NULL; dp = dp->next) if (dp->code == digest_code) break; if (dp == NULL) fatal("CHAP digest 0x%x requested but not available", digest_code); ss->digest = dp; ss->name = our_name; /* Start with a random ID value */ ss->id = (unsigned char)(drand48() * 256); ss->flags |= AUTH_STARTED; if (ss->flags & LOWERUP) chap_timeout(ss); } /* * chap_auth_with_peer - Prepare to authenticate ourselves to the peer. * There isn't much to do until we receive a challenge. */ void chap_auth_with_peer(int unit, char *our_name, int digest_code) { struct chap_client_state *cs = &client; struct chap_digest_type *dp; if (cs->flags & AUTH_STARTED) { error("CHAP: authentication with peer already started!"); return; } for (dp = chap_digests; dp != NULL; dp = dp->next) if (dp->code == digest_code) break; if (dp == NULL) fatal("CHAP digest 0x%x requested but not available", digest_code); cs->digest = dp; cs->name = our_name; cs->flags |= AUTH_STARTED; } /* * chap_timeout - It's time to send another challenge to the peer. * This could be either a retransmission of a previous challenge, * or a new challenge to start re-authentication. */ static void chap_timeout(void *arg) { struct chap_server_state *ss = arg; ss->flags &= ~TIMEOUT_PENDING; if ((ss->flags & CHALLENGE_VALID) == 0) { ss->challenge_xmits = 0; chap_generate_challenge(ss); ss->flags |= CHALLENGE_VALID; } else if (ss->challenge_xmits >= chap_max_transmits) { ss->flags &= ~CHALLENGE_VALID; ss->flags |= AUTH_DONE | AUTH_FAILED; auth_peer_fail(0, PPP_CHAP); return; } output(0, ss->challenge, ss->challenge_pktlen); ++ss->challenge_xmits; ss->flags |= TIMEOUT_PENDING; TIMEOUT(chap_timeout, arg, chap_timeout_time); } /* * chap_generate_challenge - generate a challenge string and format * the challenge packet in ss->challenge_pkt. */ static void chap_generate_challenge(struct chap_server_state *ss) { int clen = 1, nlen, len; unsigned char *p; p = ss->challenge; MAKEHEADER(p, PPP_CHAP); p += CHAP_HDRLEN; ss->digest->generate_challenge(p); clen = *p; nlen = strlen(ss->name); memcpy(p + 1 + clen, ss->name, nlen); len = CHAP_HDRLEN + 1 + clen + nlen; ss->challenge_pktlen = PPP_HDRLEN + len; p = ss->challenge + PPP_HDRLEN; p[0] = CHAP_CHALLENGE; p[1] = ++ss->id; p[2] = len >> 8; p[3] = len; } /* * chap_handle_response - check the response to our challenge. */ static void chap_handle_response(struct chap_server_state *ss, int id, unsigned char *pkt, int len) { int response_len, ok, mlen; unsigned char *response, *p; char *name = NULL; /* initialized to shut gcc up */ int (*verifier)(char *, char *, int, struct chap_digest_type *, unsigned char *, unsigned char *, char *, int); char rname[MAXNAMELEN+1]; if ((ss->flags & LOWERUP) == 0) return; if (id != ss->challenge[PPP_HDRLEN+1] || len < 2) return; if (ss->flags & CHALLENGE_VALID) { response = pkt; GETCHAR(response_len, pkt); len -= response_len + 1; /* length of name */ name = (char *)pkt + response_len; if (len < 0) return; if (ss->flags & TIMEOUT_PENDING) { ss->flags &= ~TIMEOUT_PENDING; UNTIMEOUT(chap_timeout, ss); } if (explicit_remote) { name = remote_name; } else { /* Null terminate and clean remote name. */ slprintf(rname, sizeof(rname), "%.*v", len, name); name = rname; } if (chap_verify_hook) verifier = chap_verify_hook; else verifier = chap_verify_response; ok = (*verifier)(name, ss->name, id, ss->digest, ss->challenge + PPP_HDRLEN + CHAP_HDRLEN, response, ss->message, sizeof(ss->message)); if (!ok || !auth_number()) { ss->flags |= AUTH_FAILED; warn("Peer %q failed CHAP authentication", name); } } else if ((ss->flags & AUTH_DONE) == 0) return; /* send the response */ p = outpacket_buf; MAKEHEADER(p, PPP_CHAP); mlen = strlen(ss->message); len = CHAP_HDRLEN + mlen; p[0] = (ss->flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS; p[1] = id; p[2] = len >> 8; p[3] = len; if (mlen > 0) memcpy(p + CHAP_HDRLEN, ss->message, mlen); output(0, outpacket_buf, PPP_HDRLEN + len); if (ss->flags & CHALLENGE_VALID) { ss->flags &= ~CHALLENGE_VALID; if (!(ss->flags & AUTH_DONE) && !(ss->flags & AUTH_FAILED)) { /* * Auth is OK, so now we need to check session restrictions * to ensure everything is OK, but only if we used a * plugin, and only if we're configured to check. This * allows us to do PAM checks on PPP servers that * authenticate against ActiveDirectory, and use AD for * account info (like when using Winbind integrated with * PAM). */ if (session_mgmt && session_check(name, NULL, devnam, NULL) == 0) { ss->flags |= AUTH_FAILED; warn("Peer %q failed CHAP Session verification", name); } } if (ss->flags & AUTH_FAILED) { auth_peer_fail(0, PPP_CHAP); } else { if ((ss->flags & AUTH_DONE) == 0) auth_peer_success(0, PPP_CHAP, ss->digest->code, name, strlen(name)); if (chap_rechallenge_time) { ss->flags |= TIMEOUT_PENDING; TIMEOUT(chap_timeout, ss, chap_rechallenge_time); } } ss->flags |= AUTH_DONE; } } /* * chap_verify_response - check whether the peer's response matches * what we think it should be. Returns 1 if it does (authentication * succeeded), or 0 if it doesn't. */ static int chap_verify_response(char *name, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space) { int ok; unsigned char secret[MAXSECRETLEN]; int secret_len; /* Get the secret that the peer is supposed to know */ if (!get_secret(0, name, ourname, (char *)secret, &secret_len, 1)) { error("No CHAP secret found for authenticating %q", name); return 0; } ok = digest->verify_response(id, name, secret, secret_len, challenge, response, message, message_space); memset(secret, 0, sizeof(secret)); return ok; } /* * chap_respond - Generate and send a response to a challenge. */ static void chap_respond(struct chap_client_state *cs, int id, unsigned char *pkt, int len) { int clen, nlen; int secret_len; unsigned char *p; unsigned char response[RESP_MAX_PKTLEN]; char rname[MAXNAMELEN+1]; char secret[MAXSECRETLEN+1]; if ((cs->flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED)) return; /* not ready */ if (len < 2 || len < pkt[0] + 1) return; /* too short */ clen = pkt[0]; nlen = len - (clen + 1); /* Null terminate and clean remote name. */ slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1); /* Microsoft doesn't send their name back in the PPP packet */ if (explicit_remote || (remote_name[0] != 0 && rname[0] == 0)) strlcpy(rname, remote_name, sizeof(rname)); /* get secret for authenticating ourselves with the specified host */ if (!get_secret(0, cs->name, rname, secret, &secret_len, 0)) { secret_len = 0; /* assume null secret if can't find one */ warn("No CHAP secret found for authenticating us to %q", rname); } p = response; MAKEHEADER(p, PPP_CHAP); p += CHAP_HDRLEN; cs->digest->make_response(p, id, cs->name, pkt, secret, secret_len, cs->priv); memset(secret, 0, secret_len); clen = *p; nlen = strlen(cs->name); memcpy(p + clen + 1, cs->name, nlen); p = response + PPP_HDRLEN; len = CHAP_HDRLEN + clen + 1 + nlen; p[0] = CHAP_RESPONSE; p[1] = id; p[2] = len >> 8; p[3] = len; output(0, response, PPP_HDRLEN + len); } static void chap_handle_status(struct chap_client_state *cs, int code, int id, unsigned char *pkt, int len) { const char *msg = NULL; if ((cs->flags & (AUTH_DONE|AUTH_STARTED|LOWERUP)) != (AUTH_STARTED|LOWERUP)) return; cs->flags |= AUTH_DONE; if (code == CHAP_SUCCESS) { /* used for MS-CHAP v2 mutual auth, yuck */ if (cs->digest->check_success != NULL) { if (!(*cs->digest->check_success)(pkt, len, cs->priv)) code = CHAP_FAILURE; } else msg = "CHAP authentication succeeded"; } else { if (cs->digest->handle_failure != NULL) (*cs->digest->handle_failure)(pkt, len); else msg = "CHAP authentication failed"; } if (msg) { if (len > 0) info("%s: %.*v", msg, len, pkt); else info("%s", msg); } if (code == CHAP_SUCCESS) auth_withpeer_success(0, PPP_CHAP, cs->digest->code); else { cs->flags |= AUTH_FAILED; error("CHAP authentication failed"); auth_withpeer_fail(0, PPP_CHAP); } } static void chap_input(int unit, unsigned char *pkt, int pktlen) { struct chap_client_state *cs = &client; struct chap_server_state *ss = &server; unsigned char code, id; int len; if (pktlen < CHAP_HDRLEN) return; GETCHAR(code, pkt); GETCHAR(id, pkt); GETSHORT(len, pkt); if (len < CHAP_HDRLEN || len > pktlen) return; len -= CHAP_HDRLEN; switch (code) { case CHAP_CHALLENGE: chap_respond(cs, id, pkt, len); break; case CHAP_RESPONSE: chap_handle_response(ss, id, pkt, len); break; case CHAP_FAILURE: case CHAP_SUCCESS: chap_handle_status(cs, code, id, pkt, len); break; } } static void chap_protrej(int unit) { struct chap_client_state *cs = &client; struct chap_server_state *ss = &server; if (ss->flags & TIMEOUT_PENDING) { ss->flags &= ~TIMEOUT_PENDING; UNTIMEOUT(chap_timeout, ss); } if (ss->flags & AUTH_STARTED) { ss->flags = 0; auth_peer_fail(0, PPP_CHAP); } if ((cs->flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) { cs->flags &= ~AUTH_STARTED; error("CHAP authentication failed due to protocol-reject"); auth_withpeer_fail(0, PPP_CHAP); } } /* * chap_print_pkt - print the contents of a CHAP packet. */ static char *chap_code_names[] = { "Challenge", "Response", "Success", "Failure" }; static int chap_print_pkt(unsigned char *p, int plen, void (*printer) __P((void *, char *, ...)), void *arg) { int code, id, len; int clen, nlen; unsigned char x; if (plen < CHAP_HDRLEN) return 0; GETCHAR(code, p); GETCHAR(id, p); GETSHORT(len, p); if (len < CHAP_HDRLEN || len > plen) return 0; if (code >= 1 && code <= sizeof(chap_code_names) / sizeof(char *)) printer(arg, " %s", chap_code_names[code-1]); else printer(arg, " code=0x%x", code); printer(arg, " id=0x%x", id); len -= CHAP_HDRLEN; 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_HDRLEN; } struct protent chap_protent = { PPP_CHAP, chap_init, chap_input, chap_protrej, chap_lowerup, chap_lowerdown, NULL, /* open */ NULL, /* close */ chap_print_pkt, NULL, /* datainput */ 1, /* enabled_flag */ "CHAP", /* name */ NULL, /* data_name */ chap_option_list, NULL, /* check_options */ }; ppp-2.4.5/pppd/chap-new.h000066400000000000000000000104741130035057700151460ustar00rootroot00000000000000/* * chap-new.c - New CHAP implementation. * * Copyright (c) 2003 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * CHAP packets begin with a standard header with code, id, len (2 bytes). */ #define CHAP_HDRLEN 4 /* * Values for the code field. */ #define CHAP_CHALLENGE 1 #define CHAP_RESPONSE 2 #define CHAP_SUCCESS 3 #define CHAP_FAILURE 4 /* * CHAP digest codes. */ #define CHAP_MD5 5 #define CHAP_MICROSOFT 0x80 #define CHAP_MICROSOFT_V2 0x81 /* * Semi-arbitrary limits on challenge and response fields. */ #define MAX_CHALLENGE_LEN 64 #define MAX_RESPONSE_LEN 64 /* bitmask of supported algorithms */ #define MDTYPE_MICROSOFT_V2 0x1 #define MDTYPE_MICROSOFT 0x2 #define MDTYPE_MD5 0x4 #define MDTYPE_NONE 0 /* hashes supported by this instance of pppd */ extern int chap_mdtype_all; /* Return the digest alg. ID for the most preferred digest type. */ #define CHAP_DIGEST(mdtype) \ ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \ ((mdtype) & MDTYPE_MICROSOFT_V2)? CHAP_MICROSOFT_V2: \ ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \ 0 /* Return the bit flag (lsb set) for our most preferred digest type. */ #define CHAP_MDTYPE(mdtype) ((mdtype) ^ ((mdtype) - 1)) & (mdtype) /* Return the bit flag for a given digest algorithm ID. */ #define CHAP_MDTYPE_D(digest) \ ((digest) == CHAP_MICROSOFT_V2)? MDTYPE_MICROSOFT_V2: \ ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \ ((digest) == CHAP_MD5)? MDTYPE_MD5: \ 0 /* Can we do the requested digest? */ #define CHAP_CANDIGEST(mdtype, digest) \ ((digest) == CHAP_MICROSOFT_V2)? (mdtype) & MDTYPE_MICROSOFT_V2: \ ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \ ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \ 0 /* * The code for each digest type has to supply one of these. */ struct chap_digest_type { int code; /* * Note: challenge and response arguments below are formatted as * a length byte followed by the actual challenge/response data. */ void (*generate_challenge)(unsigned char *challenge); int (*verify_response)(int id, char *name, unsigned char *secret, int secret_len, unsigned char *challenge, unsigned char *response, char *message, int message_space); void (*make_response)(unsigned char *response, int id, char *our_name, unsigned char *challenge, char *secret, int secret_len, unsigned char *priv); int (*check_success)(unsigned char *pkt, int len, unsigned char *priv); void (*handle_failure)(unsigned char *pkt, int len); struct chap_digest_type *next; }; /* Hook for a plugin to validate CHAP challenge */ extern int (*chap_verify_hook)(char *name, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space); /* Called by digest code to register a digest type */ extern void chap_register_digest(struct chap_digest_type *); /* Called by authentication code to start authenticating the peer. */ extern void chap_auth_peer(int unit, char *our_name, int digest_code); /* Called by auth. code to start authenticating us to the peer. */ extern void chap_auth_with_peer(int unit, char *our_name, int digest_code); /* Represents the CHAP protocol to the main pppd code */ extern struct protent chap_protent; ppp-2.4.5/pppd/chap_ms.c000066400000000000000000000714001130035057700150450ustar00rootroot00000000000000/* * chap_ms.c - Microsoft MS-CHAP compatible implementation. * * Copyright (c) 1995 Eric Rosenquist. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 * * Implemented LANManager type password response to MS-CHAP challenges. * Now pppd provides both NT style and LANMan style blocks, and the * prefered is set by option "ms-lanman". Default is to use NT. * The hash text (StdText) was taken from Win95 RASAPI32.DLL. * * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 */ /* * Modifications by Frank Cusack, frank@google.com, March 2002. * * Implemented MS-CHAPv2 functionality, heavily based on sample * implementation in RFC 2759. Implemented MPPE functionality, * heavily based on sample implementation in RFC 3079. * * Copyright (c) 2002 Google, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #define RCSID "$Id: chap_ms.c,v 1.38 2007/12/01 20:10:51 carlsonj Exp $" #ifdef CHAPMS #include #include #include #include #include #include #include #include "pppd.h" #include "chap-new.h" #include "chap_ms.h" #include "md4.h" #include "sha1.h" #include "pppcrypt.h" #include "magic.h" static const char rcsid[] = RCSID; static void ascii2unicode __P((char[], int, u_char[])); static void NTPasswordHash __P((u_char *, int, u_char[MD4_SIGNATURE_SIZE])); static void ChallengeResponse __P((u_char *, u_char *, u_char[24])); static void ChapMS_NT __P((u_char *, char *, int, u_char[24])); static void ChapMS2_NT __P((u_char *, u_char[16], char *, char *, int, u_char[24])); static void GenerateAuthenticatorResponsePlain __P((char*, int, u_char[24], u_char[16], u_char *, char *, u_char[41])); #ifdef MSLANMAN static void ChapMS_LANMan __P((u_char *, char *, int, u_char *)); #endif #ifdef MPPE static void Set_Start_Key __P((u_char *, char *, int)); static void SetMasterKeys __P((char *, int, u_char[24], int)); #endif #ifdef MSLANMAN bool ms_lanman = 0; /* Use LanMan password instead of NT */ /* Has meaning only with MS-CHAP challenges */ #endif #ifdef MPPE u_char mppe_send_key[MPPE_MAX_KEY_LEN]; u_char mppe_recv_key[MPPE_MAX_KEY_LEN]; int mppe_keys_set = 0; /* Have the MPPE keys been set? */ #ifdef DEBUGMPPEKEY /* For MPPE debug */ /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */ static char *mschap_challenge = NULL; /* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */ static char *mschap2_peer_challenge = NULL; #endif #include "fsm.h" /* Need to poke MPPE options */ #include "ccp.h" #include #endif /* * Command-line options. */ static option_t chapms_option_list[] = { #ifdef MSLANMAN { "ms-lanman", o_bool, &ms_lanman, "Use LanMan passwd when using MS-CHAP", 1 }, #endif #ifdef DEBUGMPPEKEY { "mschap-challenge", o_string, &mschap_challenge, "specify CHAP challenge" }, { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge, "specify CHAP peer challenge" }, #endif { NULL } }; /* * chapms_generate_challenge - generate a challenge for MS-CHAP. * For MS-CHAP the challenge length is fixed at 8 bytes. * The length goes in challenge[0] and the actual challenge starts * at challenge[1]. */ static void chapms_generate_challenge(unsigned char *challenge) { *challenge++ = 8; #ifdef DEBUGMPPEKEY if (mschap_challenge && strlen(mschap_challenge) == 8) memcpy(challenge, mschap_challenge, 8); else #endif random_bytes(challenge, 8); } static void chapms2_generate_challenge(unsigned char *challenge) { *challenge++ = 16; #ifdef DEBUGMPPEKEY if (mschap_challenge && strlen(mschap_challenge) == 16) memcpy(challenge, mschap_challenge, 16); else #endif random_bytes(challenge, 16); } static int chapms_verify_response(int id, char *name, unsigned char *secret, int secret_len, unsigned char *challenge, unsigned char *response, char *message, int message_space) { unsigned char md[MS_CHAP_RESPONSE_LEN]; int diff; int challenge_len, response_len; challenge_len = *challenge++; /* skip length, is 8 */ response_len = *response++; if (response_len != MS_CHAP_RESPONSE_LEN) goto bad; #ifndef MSLANMAN if (!response[MS_CHAP_USENT]) { /* Should really propagate this into the error packet. */ notice("Peer request for LANMAN auth not supported"); goto bad; } #endif /* Generate the expected response. */ ChapMS(challenge, (char *)secret, secret_len, md); #ifdef MSLANMAN /* Determine which part of response to verify against */ if (!response[MS_CHAP_USENT]) diff = memcmp(&response[MS_CHAP_LANMANRESP], &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN); else #endif diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP], MS_CHAP_NTRESP_LEN); if (diff == 0) { slprintf(message, message_space, "Access granted"); return 1; } bad: /* See comments below for MS-CHAP V2 */ slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0", challenge_len, challenge); return 0; } static int chapms2_verify_response(int id, char *name, unsigned char *secret, int secret_len, unsigned char *challenge, unsigned char *response, char *message, int message_space) { unsigned char md[MS_CHAP2_RESPONSE_LEN]; char saresponse[MS_AUTH_RESPONSE_LENGTH+1]; int challenge_len, response_len; challenge_len = *challenge++; /* skip length, is 16 */ response_len = *response++; if (response_len != MS_CHAP2_RESPONSE_LEN) goto bad; /* not even the right length */ /* Generate the expected response and our mutual auth. */ ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name, (char *)secret, secret_len, md, (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR); /* compare MDs and send the appropriate status */ /* * Per RFC 2759, success message must be formatted as * "S= M=" * where * is the Authenticator Response (mutual auth) * is a text message * * However, some versions of Windows (win98 tested) do not know * about the M= part (required per RFC 2759) and flag * it as an error (reported incorrectly as an encryption error * to the user). Since the RFC requires it, and it can be * useful information, we supply it if the peer is a conforming * system. Luckily (?), win98 sets the Flags field to 0x04 * (contrary to RFC requirements) so we can use that to * distinguish between conforming and non-conforming systems. * * Special thanks to Alex Swiridov for * help debugging this. */ if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP], MS_CHAP2_NTRESP_LEN) == 0) { if (response[MS_CHAP2_FLAGS]) slprintf(message, message_space, "S=%s", saresponse); else slprintf(message, message_space, "S=%s M=%s", saresponse, "Access granted"); return 1; } bad: /* * Failure message must be formatted as * "E=e R=r C=c V=v M=m" * where * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE) * r = retry (we use 1, ok to retry) * c = challenge to use for next response, we reuse previous * v = Change Password version supported, we use 0 * m = text message * * The M=m part is only for MS-CHAPv2. Neither win2k nor * win98 (others untested) display the message to the user anyway. * They also both ignore the E=e code. * * Note that it's safe to reuse the same challenge as we don't * actually accept another response based on the error message * (and no clients try to resend a response anyway). * * Basically, this whole bit is useless code, even the small * implementation here is only because of overspecification. */ slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", challenge_len, challenge, "Access denied"); return 0; } static void chapms_make_response(unsigned char *response, int id, char *our_name, unsigned char *challenge, char *secret, int secret_len, unsigned char *private) { challenge++; /* skip length, should be 8 */ *response++ = MS_CHAP_RESPONSE_LEN; ChapMS(challenge, secret, secret_len, response); } static void chapms2_make_response(unsigned char *response, int id, char *our_name, unsigned char *challenge, char *secret, int secret_len, unsigned char *private) { challenge++; /* skip length, should be 16 */ *response++ = MS_CHAP2_RESPONSE_LEN; ChapMS2(challenge, #ifdef DEBUGMPPEKEY mschap2_peer_challenge, #else NULL, #endif our_name, secret, secret_len, response, private, MS_CHAP2_AUTHENTICATEE); } static int chapms2_check_success(unsigned char *msg, int len, unsigned char *private) { if ((len < MS_AUTH_RESPONSE_LENGTH + 2) || strncmp((char *)msg, "S=", 2) != 0) { /* Packet does not start with "S=" */ error("MS-CHAPv2 Success packet is badly formed."); return 0; } msg += 2; len -= 2; if (len < MS_AUTH_RESPONSE_LENGTH || memcmp(msg, private, MS_AUTH_RESPONSE_LENGTH)) { /* Authenticator Response did not match expected. */ error("MS-CHAPv2 mutual authentication failed."); return 0; } /* Authenticator Response matches. */ msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */ len -= MS_AUTH_RESPONSE_LENGTH; if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) { msg += 3; /* Eat the delimiter */ } else if (len) { /* Packet has extra text which does not begin " M=" */ error("MS-CHAPv2 Success packet is badly formed."); return 0; } return 1; } static void chapms_handle_failure(unsigned char *inp, int len) { int err; char *p, *msg; /* We want a null-terminated string for strxxx(). */ msg = malloc(len + 1); if (!msg) { notice("Out of memory in chapms_handle_failure"); return; } BCOPY(inp, msg, len); msg[len] = 0; p = msg; /* * Deal with MS-CHAP formatted failure messages; just print the * M= part (if any). For MS-CHAP we're not really supposed * to use M=, but it shouldn't hurt. See * chapms[2]_verify_response. */ if (!strncmp(p, "E=", 2)) err = strtol(p+2, NULL, 10); /* Remember the error code. */ else goto print_msg; /* Message is badly formatted. */ if (len && ((p = strstr(p, " M=")) != NULL)) { /* M= field found. */ p += 3; } else { /* No M=; use the error code. */ switch (err) { case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS: p = "E=646 Restricted logon hours"; break; case MS_CHAP_ERROR_ACCT_DISABLED: p = "E=647 Account disabled"; break; case MS_CHAP_ERROR_PASSWD_EXPIRED: p = "E=648 Password expired"; break; case MS_CHAP_ERROR_NO_DIALIN_PERMISSION: p = "E=649 No dialin permission"; break; case MS_CHAP_ERROR_AUTHENTICATION_FAILURE: p = "E=691 Authentication failure"; break; case MS_CHAP_ERROR_CHANGING_PASSWORD: /* Should never see this, we don't support Change Password. */ p = "E=709 Error changing password"; break; default: free(msg); error("Unknown MS-CHAP authentication failure: %.*v", len, inp); return; } } print_msg: if (p != NULL) error("MS-CHAP authentication failed: %v", p); free(msg); } static void ChallengeResponse(u_char *challenge, u_char PasswordHash[MD4_SIGNATURE_SIZE], u_char response[24]) { u_char ZPasswordHash[21]; BZERO(ZPasswordHash, sizeof(ZPasswordHash)); BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE); #if 0 dbglog("ChallengeResponse - ZPasswordHash %.*B", sizeof(ZPasswordHash), ZPasswordHash); #endif (void) DesSetkey(ZPasswordHash + 0); DesEncrypt(challenge, response + 0); (void) DesSetkey(ZPasswordHash + 7); DesEncrypt(challenge, response + 8); (void) DesSetkey(ZPasswordHash + 14); DesEncrypt(challenge, response + 16); #if 0 dbglog("ChallengeResponse - response %.24B", response); #endif } void ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge, char *username, u_char Challenge[8]) { SHA1_CTX sha1Context; u_char sha1Hash[SHA1_SIGNATURE_SIZE]; char *user; /* remove domain from "domain\username" */ if ((user = strrchr(username, '\\')) != NULL) ++user; else user = username; SHA1_Init(&sha1Context); SHA1_Update(&sha1Context, PeerChallenge, 16); SHA1_Update(&sha1Context, rchallenge, 16); SHA1_Update(&sha1Context, (unsigned char *)user, strlen(user)); SHA1_Final(sha1Hash, &sha1Context); BCOPY(sha1Hash, Challenge, 8); } /* * Convert the ASCII version of the password to Unicode. * This implicitly supports 8-bit ISO8859/1 characters. * This gives us the little-endian representation, which * is assumed by all M$ CHAP RFCs. (Unicode byte ordering * is machine-dependent.) */ static void ascii2unicode(char ascii[], int ascii_len, u_char unicode[]) { int i; BZERO(unicode, ascii_len * 2); for (i = 0; i < ascii_len; i++) unicode[i * 2] = (u_char) ascii[i]; } static void NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE]) { #ifdef __NetBSD__ /* NetBSD uses the libc md4 routines which take bytes instead of bits */ int mdlen = secret_len; #else int mdlen = secret_len * 8; #endif MD4_CTX md4Context; MD4Init(&md4Context); /* MD4Update can take at most 64 bytes at a time */ while (mdlen > 512) { MD4Update(&md4Context, secret, 512); secret += 64; mdlen -= 512; } MD4Update(&md4Context, secret, mdlen); MD4Final(hash, &md4Context); } static void ChapMS_NT(u_char *rchallenge, char *secret, int secret_len, u_char NTResponse[24]) { u_char unicodePassword[MAX_NT_PASSWORD * 2]; u_char PasswordHash[MD4_SIGNATURE_SIZE]; /* Hash the Unicode version of the secret (== password). */ ascii2unicode(secret, secret_len, unicodePassword); NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); ChallengeResponse(rchallenge, PasswordHash, NTResponse); } static void ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username, char *secret, int secret_len, u_char NTResponse[24]) { u_char unicodePassword[MAX_NT_PASSWORD * 2]; u_char PasswordHash[MD4_SIGNATURE_SIZE]; u_char Challenge[8]; ChallengeHash(PeerChallenge, rchallenge, username, Challenge); /* Hash the Unicode version of the secret (== password). */ ascii2unicode(secret, secret_len, unicodePassword); NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); ChallengeResponse(Challenge, PasswordHash, NTResponse); } #ifdef MSLANMAN static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ static void ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len, unsigned char *response) { int i; u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ u_char PasswordHash[MD4_SIGNATURE_SIZE]; /* LANMan password is case insensitive */ BZERO(UcasePassword, sizeof(UcasePassword)); for (i = 0; i < secret_len; i++) UcasePassword[i] = (u_char)toupper(secret[i]); (void) DesSetkey(UcasePassword + 0); DesEncrypt( StdText, PasswordHash + 0 ); (void) DesSetkey(UcasePassword + 7); DesEncrypt( StdText, PasswordHash + 8 ); ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]); } #endif void GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], u_char NTResponse[24], u_char PeerChallenge[16], u_char *rchallenge, char *username, u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) { /* * "Magic" constants used in response generation, from RFC 2759. */ u_char Magic1[39] = /* "Magic server to client signing constant" */ { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 }; u_char Magic2[41] = /* "Pad to make it do more than one iteration" */ { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E }; int i; SHA1_CTX sha1Context; u_char Digest[SHA1_SIGNATURE_SIZE]; u_char Challenge[8]; SHA1_Init(&sha1Context); SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); SHA1_Update(&sha1Context, NTResponse, 24); SHA1_Update(&sha1Context, Magic1, sizeof(Magic1)); SHA1_Final(Digest, &sha1Context); ChallengeHash(PeerChallenge, rchallenge, username, Challenge); SHA1_Init(&sha1Context); SHA1_Update(&sha1Context, Digest, sizeof(Digest)); SHA1_Update(&sha1Context, Challenge, sizeof(Challenge)); SHA1_Update(&sha1Context, Magic2, sizeof(Magic2)); SHA1_Final(Digest, &sha1Context); /* Convert to ASCII hex string. */ for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++) sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]); } static void GenerateAuthenticatorResponsePlain (char *secret, int secret_len, u_char NTResponse[24], u_char PeerChallenge[16], u_char *rchallenge, char *username, u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) { u_char unicodePassword[MAX_NT_PASSWORD * 2]; u_char PasswordHash[MD4_SIGNATURE_SIZE]; u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; /* Hash (x2) the Unicode version of the secret (== password). */ ascii2unicode(secret, secret_len, unicodePassword); NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge, rchallenge, username, authResponse); } #ifdef MPPE /* * Set mppe_xxxx_key from the NTPasswordHashHash. * RFC 2548 (RADIUS support) requires us to export this function (ugh). */ void mppe_set_keys(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE]) { SHA1_CTX sha1Context; u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ SHA1_Init(&sha1Context); SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); SHA1_Update(&sha1Context, rchallenge, 8); SHA1_Final(Digest, &sha1Context); /* Same key in both directions. */ BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key)); BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key)); mppe_keys_set = 1; } /* * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079) */ static void Set_Start_Key(u_char *rchallenge, char *secret, int secret_len) { u_char unicodePassword[MAX_NT_PASSWORD * 2]; u_char PasswordHash[MD4_SIGNATURE_SIZE]; u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; /* Hash (x2) the Unicode version of the secret (== password). */ ascii2unicode(secret, secret_len, unicodePassword); NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); mppe_set_keys(rchallenge, PasswordHashHash); } /* * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079) * * This helper function used in the Winbind module, which gets the * NTHashHash from the server. */ void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], u_char NTResponse[24], int IsServer) { SHA1_CTX sha1Context; u_char MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ u_char SHApad1[40] = { 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 }; u_char SHApad2[40] = { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 }; /* "This is the MPPE Master Key" */ u_char Magic1[27] = { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 }; /* "On the client side, this is the send key; " "on the server side, it is the receive key." */ u_char Magic2[84] = { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x2e }; /* "On the client side, this is the receive key; " "on the server side, it is the send key." */ u_char Magic3[84] = { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x2e }; u_char *s; SHA1_Init(&sha1Context); SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); SHA1_Update(&sha1Context, NTResponse, 24); SHA1_Update(&sha1Context, Magic1, sizeof(Magic1)); SHA1_Final(MasterKey, &sha1Context); /* * generate send key */ if (IsServer) s = Magic3; else s = Magic2; SHA1_Init(&sha1Context); SHA1_Update(&sha1Context, MasterKey, 16); SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1)); SHA1_Update(&sha1Context, s, 84); SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2)); SHA1_Final(Digest, &sha1Context); BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key)); /* * generate recv key */ if (IsServer) s = Magic2; else s = Magic3; SHA1_Init(&sha1Context); SHA1_Update(&sha1Context, MasterKey, 16); SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1)); SHA1_Update(&sha1Context, s, 84); SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2)); SHA1_Final(Digest, &sha1Context); BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key)); mppe_keys_set = 1; } /* * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079) */ static void SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer) { u_char unicodePassword[MAX_NT_PASSWORD * 2]; u_char PasswordHash[MD4_SIGNATURE_SIZE]; u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; /* Hash (x2) the Unicode version of the secret (== password). */ ascii2unicode(secret, secret_len, unicodePassword); NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); mppe_set_keys2(PasswordHashHash, NTResponse, IsServer); } #endif /* MPPE */ void ChapMS(u_char *rchallenge, char *secret, int secret_len, unsigned char *response) { BZERO(response, MS_CHAP_RESPONSE_LEN); ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]); #ifdef MSLANMAN ChapMS_LANMan(rchallenge, secret, secret_len, &response[MS_CHAP_LANMANRESP]); /* preferred method is set by option */ response[MS_CHAP_USENT] = !ms_lanman; #else response[MS_CHAP_USENT] = 1; #endif #ifdef MPPE Set_Start_Key(rchallenge, secret, secret_len); #endif } /* * If PeerChallenge is NULL, one is generated and the PeerChallenge * field of response is filled in. Call this way when generating a response. * If PeerChallenge is supplied, it is copied into the PeerChallenge field. * Call this way when verifying a response (or debugging). * Do not call with PeerChallenge = response. * * The PeerChallenge field of response is then used for calculation of the * Authenticator Response. */ void ChapMS2(u_char *rchallenge, u_char *PeerChallenge, char *user, char *secret, int secret_len, unsigned char *response, u_char authResponse[], int authenticator) { /* ARGSUSED */ u_char *p = &response[MS_CHAP2_PEER_CHALLENGE]; int i; BZERO(response, MS_CHAP2_RESPONSE_LEN); /* Generate the Peer-Challenge if requested, or copy it if supplied. */ if (!PeerChallenge) for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++) *p++ = (u_char) (drand48() * 0xff); else BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE], MS_CHAP2_PEER_CHAL_LEN); /* Generate the NT-Response */ ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user, secret, secret_len, &response[MS_CHAP2_NTRESP]); /* Generate the Authenticator Response. */ GenerateAuthenticatorResponsePlain(secret, secret_len, &response[MS_CHAP2_NTRESP], &response[MS_CHAP2_PEER_CHALLENGE], rchallenge, user, authResponse); #ifdef MPPE SetMasterKeys(secret, secret_len, &response[MS_CHAP2_NTRESP], authenticator); #endif } #ifdef MPPE /* * Set MPPE options from plugins. */ void set_mppe_enc_types(int policy, int types) { /* Early exit for unknown policies. */ if (policy != MPPE_ENC_POL_ENC_ALLOWED || policy != MPPE_ENC_POL_ENC_REQUIRED) return; /* Don't modify MPPE if it's optional and wasn't already configured. */ if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe) return; /* * Disable undesirable encryption types. Note that we don't ENABLE * any encryption types, to avoid overriding manual configuration. */ switch(types) { case MPPE_ENC_TYPES_RC4_40: ccp_wantoptions[0].mppe &= ~MPPE_OPT_128; /* disable 128-bit */ break; case MPPE_ENC_TYPES_RC4_128: ccp_wantoptions[0].mppe &= ~MPPE_OPT_40; /* disable 40-bit */ break; default: break; } } #endif /* MPPE */ static struct chap_digest_type chapms_digest = { CHAP_MICROSOFT, /* code */ chapms_generate_challenge, chapms_verify_response, chapms_make_response, NULL, /* check_success */ chapms_handle_failure, }; static struct chap_digest_type chapms2_digest = { CHAP_MICROSOFT_V2, /* code */ chapms2_generate_challenge, chapms2_verify_response, chapms2_make_response, chapms2_check_success, chapms_handle_failure, }; void chapms_init(void) { chap_register_digest(&chapms_digest); chap_register_digest(&chapms2_digest); add_options(chapms_option_list); } #endif /* CHAPMS */ ppp-2.4.5/pppd/chap_ms.h000066400000000000000000000075201130035057700150540ustar00rootroot00000000000000/* * chap_ms.h - Challenge Handshake Authentication Protocol definitions. * * Copyright (c) 1995 Eric Rosenquist. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: chap_ms.h,v 1.13 2004/11/15 22:13:26 paulus Exp $ */ #ifndef __CHAPMS_INCLUDE__ #define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */ #define MAX_NT_PASSWORD 256 /* Max (Unicode) chars in an NT pass */ #define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ #define MS_CHAP2_RESPONSE_LEN 49 /* Response length for MS-CHAPv2 */ #define MS_AUTH_RESPONSE_LENGTH 40 /* MS-CHAPv2 authenticator response, */ /* as ASCII */ /* E=eeeeeeeeee error codes for MS-CHAP failure messages. */ #define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS 646 #define MS_CHAP_ERROR_ACCT_DISABLED 647 #define MS_CHAP_ERROR_PASSWD_EXPIRED 648 #define MS_CHAP_ERROR_NO_DIALIN_PERMISSION 649 #define MS_CHAP_ERROR_AUTHENTICATION_FAILURE 691 #define MS_CHAP_ERROR_CHANGING_PASSWORD 709 /* * Offsets within the response field for MS-CHAP */ #define MS_CHAP_LANMANRESP 0 #define MS_CHAP_LANMANRESP_LEN 24 #define MS_CHAP_NTRESP 24 #define MS_CHAP_NTRESP_LEN 24 #define MS_CHAP_USENT 48 /* * Offsets within the response field for MS-CHAP2 */ #define MS_CHAP2_PEER_CHALLENGE 0 #define MS_CHAP2_PEER_CHAL_LEN 16 #define MS_CHAP2_RESERVED_LEN 8 #define MS_CHAP2_NTRESP 24 #define MS_CHAP2_NTRESP_LEN 24 #define MS_CHAP2_FLAGS 48 #ifdef MPPE #include "mppe.h" /* MPPE_MAX_KEY_LEN */ extern u_char mppe_send_key[MPPE_MAX_KEY_LEN]; extern u_char mppe_recv_key[MPPE_MAX_KEY_LEN]; extern int mppe_keys_set; /* These values are the RADIUS attribute values--see RFC 2548. */ #define MPPE_ENC_POL_ENC_ALLOWED 1 #define MPPE_ENC_POL_ENC_REQUIRED 2 #define MPPE_ENC_TYPES_RC4_40 2 #define MPPE_ENC_TYPES_RC4_128 4 /* used by plugins (using above values) */ extern void set_mppe_enc_types(int, int); #endif /* Are we the authenticator or authenticatee? For MS-CHAPv2 key derivation. */ #define MS_CHAP2_AUTHENTICATEE 0 #define MS_CHAP2_AUTHENTICATOR 1 void ChapMS __P((u_char *, char *, int, u_char *)); void ChapMS2 __P((u_char *, u_char *, char *, char *, int, u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int)); #ifdef MPPE void mppe_set_keys __P((u_char *, u_char[MD4_SIGNATURE_SIZE])); void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], u_char NTResponse[24], int IsServer); #endif void ChallengeHash __P((u_char[16], u_char *, char *, u_char[8])); void GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], u_char NTResponse[24], u_char PeerChallenge[16], u_char *rchallenge, char *username, u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]); void chapms_init(void); #define __CHAPMS_INCLUDE__ #endif /* __CHAPMS_INCLUDE__ */ ppp-2.4.5/pppd/demand.c000066400000000000000000000234161130035057700146670ustar00rootroot00000000000000/* * demand.c - Support routines for demand-dialling. * * Copyright (c) 1996-2002 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: demand.c,v 1.20 2005/08/25 12:14:18 paulus Exp $" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PPP_FILTER #include #endif #include "pppd.h" #include "fsm.h" #include "ipcp.h" #include "lcp.h" static const char rcsid[] = RCSID; char *frame; int framelen; int framemax; int escape_flag; int flush_flag; int fcs; struct packet { int length; struct packet *next; unsigned char data[1]; }; struct packet *pend_q; struct packet *pend_qtail; static int active_packet __P((unsigned char *, int)); /* * demand_conf - configure the interface for doing dial-on-demand. */ void demand_conf() { int i; struct protent *protp; /* framemax = lcp_allowoptions[0].mru; if (framemax < PPP_MRU) */ framemax = PPP_MRU; framemax += PPP_HDRLEN + PPP_FCSLEN; frame = malloc(framemax); if (frame == NULL) novm("demand frame"); framelen = 0; pend_q = NULL; escape_flag = 0; flush_flag = 0; fcs = PPP_INITFCS; netif_set_mtu(0, MIN(lcp_allowoptions[0].mru, PPP_MRU)); if (ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0) < 0 || ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0) < 0) fatal("Couldn't set up demand-dialled PPP interface: %m"); #ifdef PPP_FILTER set_filters(&pass_filter, &active_filter); #endif /* * Call the demand_conf procedure for each protocol that's got one. */ for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->enabled_flag && protp->demand_conf != NULL) if (!((*protp->demand_conf)(0))) die(1); } /* * demand_block - set each network protocol to block further packets. */ void demand_block() { int i; struct protent *protp; for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->enabled_flag && protp->demand_conf != NULL) sifnpmode(0, protp->protocol & ~0x8000, NPMODE_QUEUE); get_loop_output(); } /* * demand_discard - set each network protocol to discard packets * with an error. */ void demand_discard() { struct packet *pkt, *nextpkt; int i; struct protent *protp; for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->enabled_flag && protp->demand_conf != NULL) sifnpmode(0, protp->protocol & ~0x8000, NPMODE_ERROR); get_loop_output(); /* discard all saved packets */ for (pkt = pend_q; pkt != NULL; pkt = nextpkt) { nextpkt = pkt->next; free(pkt); } pend_q = NULL; framelen = 0; flush_flag = 0; escape_flag = 0; fcs = PPP_INITFCS; } /* * demand_unblock - set each enabled network protocol to pass packets. */ void demand_unblock() { int i; struct protent *protp; for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->enabled_flag && protp->demand_conf != NULL) sifnpmode(0, protp->protocol & ~0x8000, NPMODE_PASS); } /* * FCS lookup table as calculated by genfcstab. */ static u_short fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; /* * loop_chars - process characters received from the loopback. * Calls loop_frame when a complete frame has been accumulated. * Return value is 1 if we need to bring up the link, 0 otherwise. */ int loop_chars(p, n) unsigned char *p; int n; { int c, rv; rv = 0; for (; n > 0; --n) { c = *p++; if (c == PPP_FLAG) { if (!escape_flag && !flush_flag && framelen > 2 && fcs == PPP_GOODFCS) { framelen -= 2; if (loop_frame((unsigned char *)frame, framelen)) rv = 1; } framelen = 0; flush_flag = 0; escape_flag = 0; fcs = PPP_INITFCS; continue; } if (flush_flag) continue; if (escape_flag) { c ^= PPP_TRANS; escape_flag = 0; } else if (c == PPP_ESCAPE) { escape_flag = 1; continue; } if (framelen >= framemax) { flush_flag = 1; continue; } frame[framelen++] = c; fcs = PPP_FCS(fcs, c); } return rv; } /* * loop_frame - given a frame obtained from the loopback, * decide whether to bring up the link or not, and, if we want * to transmit this frame later, put it on the pending queue. * Return value is 1 if we need to bring up the link, 0 otherwise. * We assume that the kernel driver has already applied the * pass_filter, so we won't get packets it rejected. * We apply the active_filter to see if we want this packet to * bring up the link. */ int loop_frame(frame, len) unsigned char *frame; int len; { struct packet *pkt; /* dbglog("from loop: %P", frame, len); */ if (len < PPP_HDRLEN) return 0; if ((PPP_PROTOCOL(frame) & 0x8000) != 0) return 0; /* shouldn't get any of these anyway */ if (!active_packet(frame, len)) return 0; pkt = (struct packet *) malloc(sizeof(struct packet) + len); if (pkt != NULL) { pkt->length = len; pkt->next = NULL; memcpy(pkt->data, frame, len); if (pend_q == NULL) pend_q = pkt; else pend_qtail->next = pkt; pend_qtail = pkt; } return 1; } /* * demand_rexmit - Resend all those frames which we got via the * loopback, now that the real serial link is up. */ void demand_rexmit(proto) int proto; { struct packet *pkt, *prev, *nextpkt; prev = NULL; pkt = pend_q; pend_q = NULL; for (; pkt != NULL; pkt = nextpkt) { nextpkt = pkt->next; if (PPP_PROTOCOL(pkt->data) == proto) { output(0, pkt->data, pkt->length); free(pkt); } else { if (prev == NULL) pend_q = pkt; else prev->next = pkt; prev = pkt; } } pend_qtail = prev; if (prev != NULL) prev->next = NULL; } /* * Scan a packet to decide whether it is an "active" packet, * that is, whether it is worth bringing up the link for. */ static int active_packet(p, len) unsigned char *p; int len; { int proto, i; struct protent *protp; if (len < PPP_HDRLEN) return 0; proto = PPP_PROTOCOL(p); #ifdef PPP_FILTER p[0] = 1; /* outbound packet indicator */ if ((pass_filter.bf_len != 0 && bpf_filter(pass_filter.bf_insns, p, len, len) == 0) || (active_filter.bf_len != 0 && bpf_filter(active_filter.bf_insns, p, len, len) == 0)) { p[0] = 0xff; return 0; } p[0] = 0xff; #endif for (i = 0; (protp = protocols[i]) != NULL; ++i) { if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) { if (!protp->enabled_flag) return 0; if (protp->active_pkt == NULL) return 1; return (*protp->active_pkt)(p, len); } } return 0; /* not a supported protocol !!?? */ } ppp-2.4.5/pppd/eap.c000066400000000000000000001632711130035057700142100ustar00rootroot00000000000000/* * eap.c - Extensible Authentication Protocol for PPP (RFC 2284) * * Copyright (c) 2001 by Sun Microsystems, Inc. * All rights reserved. * * Non-exclusive rights to redistribute, modify, translate, and use * this software in source and binary forms, in whole or in part, is * hereby granted, provided that the above copyright notice is * duplicated in any source form, and that neither the name of the * copyright holder nor the author is used to endorse or promote * products derived from this software. * * 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. * * Original version by James Carlson * * This implementation of EAP supports MD5-Challenge and SRP-SHA1 * authentication styles. Note that support of MD5-Challenge is a * requirement of RFC 2284, and that it's essentially just a * reimplementation of regular RFC 1994 CHAP using EAP messages. * * As an authenticator ("server"), there are multiple phases for each * style. In the first phase of each style, the unauthenticated peer * name is queried using the EAP Identity request type. If the * "remotename" option is used, then this phase is skipped, because * the peer's name is presumed to be known. * * For MD5-Challenge, there are two phases, and the second phase * consists of sending the challenge itself and handling the * associated response. * * For SRP-SHA1, there are four phases. The second sends 's', 'N', * and 'g'. The reply contains 'A'. The third sends 'B', and the * reply contains 'M1'. The forth sends the 'M2' value. * * As an authenticatee ("client"), there's just a single phase -- * responding to the queries generated by the peer. EAP is an * authenticator-driven protocol. * * Based on draft-ietf-pppext-eap-srp-03.txt. */ #define RCSID "$Id: eap.c,v 1.4 2004/11/09 22:39:25 paulus Exp $" /* * TODO: */ #include #include #include #include #include #include #include #include #include #include #include "pppd.h" #include "pathnames.h" #include "md5.h" #include "eap.h" #ifdef USE_SRP #include #include #include #include "pppcrypt.h" #endif /* USE_SRP */ #ifndef SHA_DIGESTSIZE #define SHA_DIGESTSIZE 20 #endif static const char rcsid[] = RCSID; eap_state eap_states[NUM_PPP]; /* EAP state; one for each unit */ #ifdef USE_SRP static char *pn_secret = NULL; /* Pseudonym generating secret */ #endif /* * Command-line options. */ static option_t eap_option_list[] = { { "eap-restart", o_int, &eap_states[0].es_server.ea_timeout, "Set retransmit timeout for EAP Requests (server)" }, { "eap-max-sreq", o_int, &eap_states[0].es_server.ea_maxrequests, "Set max number of EAP Requests sent (server)" }, { "eap-timeout", o_int, &eap_states[0].es_client.ea_timeout, "Set time limit for peer EAP authentication" }, { "eap-max-rreq", o_int, &eap_states[0].es_client.ea_maxrequests, "Set max number of EAP Requests allows (client)" }, { "eap-interval", o_int, &eap_states[0].es_rechallenge, "Set interval for EAP rechallenge" }, #ifdef USE_SRP { "srp-interval", o_int, &eap_states[0].es_lwrechallenge, "Set interval for SRP lightweight rechallenge" }, { "srp-pn-secret", o_string, &pn_secret, "Long term pseudonym generation secret" }, { "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo, "Use pseudonym if offered one by server", 1 }, #endif { NULL } }; /* * Protocol entry points. */ static void eap_init __P((int unit)); static void eap_input __P((int unit, u_char *inp, int inlen)); static void eap_protrej __P((int unit)); static void eap_lowerup __P((int unit)); static void eap_lowerdown __P((int unit)); static int eap_printpkt __P((u_char *inp, int inlen, void (*)(void *arg, char *fmt, ...), void *arg)); struct protent eap_protent = { PPP_EAP, /* protocol number */ eap_init, /* initialization procedure */ eap_input, /* process a received packet */ eap_protrej, /* process a received protocol-reject */ eap_lowerup, /* lower layer has gone up */ eap_lowerdown, /* lower layer has gone down */ NULL, /* open the protocol */ NULL, /* close the protocol */ eap_printpkt, /* print a packet in readable form */ NULL, /* process a received data packet */ 1, /* protocol enabled */ "EAP", /* text name of protocol */ NULL, /* text name of corresponding data protocol */ eap_option_list, /* list of command-line options */ NULL, /* check requested options; assign defaults */ NULL, /* configure interface for demand-dial */ NULL /* say whether to bring up link for this pkt */ }; /* * A well-known 2048 bit modulus. */ static const u_char wkmodulus[] = { 0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B, 0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F, 0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07, 0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50, 0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED, 0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D, 0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D, 0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50, 0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0, 0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3, 0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8, 0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8, 0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA, 0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74, 0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7, 0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B, 0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16, 0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81, 0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A, 0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48, 0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D, 0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA, 0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78, 0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6, 0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29, 0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8, 0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82, 0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6, 0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4, 0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75, 0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2, 0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73 }; /* Local forward declarations. */ static void eap_server_timeout __P((void *arg)); /* * Convert EAP state code to printable string for debug. */ static const char * eap_state_name(esc) enum eap_state_code esc; { static const char *state_names[] = { EAP_STATES }; return (state_names[(int)esc]); } /* * eap_init - Initialize state for an EAP user. This is currently * called once by main() during start-up. */ static void eap_init(unit) int unit; { eap_state *esp = &eap_states[unit]; BZERO(esp, sizeof (*esp)); esp->es_unit = unit; esp->es_server.ea_timeout = EAP_DEFTIMEOUT; esp->es_server.ea_maxrequests = EAP_DEFTRANSMITS; esp->es_server.ea_id = (u_char)(drand48() * 0x100); esp->es_client.ea_timeout = EAP_DEFREQTIME; esp->es_client.ea_maxrequests = EAP_DEFALLOWREQ; } /* * eap_client_timeout - Give up waiting for the peer to send any * Request messages. */ static void eap_client_timeout(arg) void *arg; { eap_state *esp = (eap_state *) arg; if (!eap_client_active(esp)) return; error("EAP: timeout waiting for Request from peer"); auth_withpeer_fail(esp->es_unit, PPP_EAP); esp->es_client.ea_state = eapBadAuth; } /* * eap_authwithpeer - Authenticate to our peer (behave as client). * * Start client state and wait for requests. This is called only * after eap_lowerup. */ void eap_authwithpeer(unit, localname) int unit; char *localname; { eap_state *esp = &eap_states[unit]; /* Save the peer name we're given */ esp->es_client.ea_name = localname; esp->es_client.ea_namelen = strlen(localname); esp->es_client.ea_state = eapListen; /* * Start a timer so that if the other end just goes * silent, we don't sit here waiting forever. */ if (esp->es_client.ea_timeout > 0) TIMEOUT(eap_client_timeout, (void *)esp, esp->es_client.ea_timeout); } /* * Format a standard EAP Failure message and send it to the peer. * (Server operation) */ static void eap_send_failure(esp) eap_state *esp; { u_char *outp; outp = outpacket_buf; MAKEHEADER(outp, PPP_EAP); PUTCHAR(EAP_FAILURE, outp); esp->es_server.ea_id++; PUTCHAR(esp->es_server.ea_id, outp); PUTSHORT(EAP_HEADERLEN, outp); output(esp->es_unit, outpacket_buf, EAP_HEADERLEN + PPP_HDRLEN); esp->es_server.ea_state = eapBadAuth; auth_peer_fail(esp->es_unit, PPP_EAP); } /* * Format a standard EAP Success message and send it to the peer. * (Server operation) */ static void eap_send_success(esp) eap_state *esp; { u_char *outp; outp = outpacket_buf; MAKEHEADER(outp, PPP_EAP); PUTCHAR(EAP_SUCCESS, outp); esp->es_server.ea_id++; PUTCHAR(esp->es_server.ea_id, outp); PUTSHORT(EAP_HEADERLEN, outp); output(esp->es_unit, outpacket_buf, PPP_HDRLEN + EAP_HEADERLEN); auth_peer_success(esp->es_unit, PPP_EAP, 0, esp->es_server.ea_peer, esp->es_server.ea_peerlen); } #ifdef USE_SRP /* * Set DES key according to pseudonym-generating secret and current * date. */ static bool pncrypt_setkey(int timeoffs) { struct tm *tp; char tbuf[9]; SHA1_CTX ctxt; u_char dig[SHA_DIGESTSIZE]; time_t reftime; if (pn_secret == NULL) return (0); reftime = time(NULL) + timeoffs; tp = localtime(&reftime); SHA1Init(&ctxt); SHA1Update(&ctxt, pn_secret, strlen(pn_secret)); strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp); SHA1Update(&ctxt, tbuf, strlen(tbuf)); SHA1Final(dig, &ctxt); return (DesSetkey(dig)); } static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; struct b64state { u_int32_t bs_bits; int bs_offs; }; static int b64enc(bs, inp, inlen, outp) struct b64state *bs; u_char *inp; int inlen; u_char *outp; { int outlen = 0; while (inlen > 0) { bs->bs_bits = (bs->bs_bits << 8) | *inp++; inlen--; bs->bs_offs += 8; if (bs->bs_offs >= 24) { *outp++ = base64[(bs->bs_bits >> 18) & 0x3F]; *outp++ = base64[(bs->bs_bits >> 12) & 0x3F]; *outp++ = base64[(bs->bs_bits >> 6) & 0x3F]; *outp++ = base64[bs->bs_bits & 0x3F]; outlen += 4; bs->bs_offs = 0; bs->bs_bits = 0; } } return (outlen); } static int b64flush(bs, outp) struct b64state *bs; u_char *outp; { int outlen = 0; if (bs->bs_offs == 8) { *outp++ = base64[(bs->bs_bits >> 2) & 0x3F]; *outp++ = base64[(bs->bs_bits << 4) & 0x3F]; outlen = 2; } else if (bs->bs_offs == 16) { *outp++ = base64[(bs->bs_bits >> 10) & 0x3F]; *outp++ = base64[(bs->bs_bits >> 4) & 0x3F]; *outp++ = base64[(bs->bs_bits << 2) & 0x3F]; outlen = 3; } bs->bs_offs = 0; bs->bs_bits = 0; return (outlen); } static int b64dec(bs, inp, inlen, outp) struct b64state *bs; u_char *inp; int inlen; u_char *outp; { int outlen = 0; char *cp; while (inlen > 0) { if ((cp = strchr(base64, *inp++)) == NULL) break; bs->bs_bits = (bs->bs_bits << 6) | (cp - base64); inlen--; bs->bs_offs += 6; if (bs->bs_offs >= 8) { *outp++ = bs->bs_bits >> (bs->bs_offs - 8); outlen++; bs->bs_offs -= 8; } } return (outlen); } #endif /* USE_SRP */ /* * Assume that current waiting server state is complete and figure * next state to use based on available authentication data. 'status' * indicates if there was an error in handling the last query. It is * 0 for success and non-zero for failure. */ static void eap_figure_next_state(esp, status) eap_state *esp; int status; { #ifdef USE_SRP unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp; struct t_pw tpw; struct t_confent *tce, mytce; char *cp, *cp2; struct t_server *ts; int id, i, plen, toffs; u_char vals[2]; struct b64state bs; #endif /* USE_SRP */ esp->es_server.ea_timeout = esp->es_savedtime; switch (esp->es_server.ea_state) { case eapBadAuth: return; case eapIdentify: #ifdef USE_SRP /* Discard any previous session. */ ts = (struct t_server *)esp->es_server.ea_session; if (ts != NULL) { t_serverclose(ts); esp->es_server.ea_session = NULL; esp->es_server.ea_skey = NULL; } #endif /* USE_SRP */ if (status != 0) { esp->es_server.ea_state = eapBadAuth; break; } #ifdef USE_SRP /* If we've got a pseudonym, try to decode to real name. */ if (esp->es_server.ea_peerlen > SRP_PSEUDO_LEN && strncmp(esp->es_server.ea_peer, SRP_PSEUDO_ID, SRP_PSEUDO_LEN) == 0 && (esp->es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 < sizeof (secbuf)) { BZERO(&bs, sizeof (bs)); plen = b64dec(&bs, esp->es_server.ea_peer + SRP_PSEUDO_LEN, esp->es_server.ea_peerlen - SRP_PSEUDO_LEN, secbuf); toffs = 0; for (i = 0; i < 5; i++) { pncrypt_setkey(toffs); toffs -= 86400; if (!DesDecrypt(secbuf, clear)) { dbglog("no DES here; cannot decode " "pseudonym"); return; } id = *(unsigned char *)clear; if (id + 1 <= plen && id + 9 > plen) break; } if (plen % 8 == 0 && i < 5) { /* * Note that this is always shorter than the * original stored string, so there's no need * to realloc. */ if ((i = plen = *(unsigned char *)clear) > 7) i = 7; esp->es_server.ea_peerlen = plen; dp = (unsigned char *)esp->es_server.ea_peer; BCOPY(clear + 1, dp, i); plen -= i; dp += i; sp = secbuf + 8; while (plen > 0) { (void) DesDecrypt(sp, dp); sp += 8; dp += 8; plen -= 8; } esp->es_server.ea_peer[ esp->es_server.ea_peerlen] = '\0'; dbglog("decoded pseudonym to \"%.*q\"", esp->es_server.ea_peerlen, esp->es_server.ea_peer); } else { dbglog("failed to decode real name"); /* Stay in eapIdentfy state; requery */ break; } } /* Look up user in secrets database. */ if (get_srp_secret(esp->es_unit, esp->es_server.ea_peer, esp->es_server.ea_name, (char *)secbuf, 1) != 0) { /* Set up default in case SRP entry is bad */ esp->es_server.ea_state = eapMD5Chall; /* Get t_confent based on index in srp-secrets */ id = strtol((char *)secbuf, &cp, 10); if (*cp++ != ':' || id < 0) break; if (id == 0) { mytce.index = 0; mytce.modulus.data = (u_char *)wkmodulus; mytce.modulus.len = sizeof (wkmodulus); mytce.generator.data = (u_char *)"\002"; mytce.generator.len = 1; tce = &mytce; } else if ((tce = gettcid(id)) != NULL) { /* * Client will have to verify this modulus/ * generator combination, and that will take * a while. Lengthen the timeout here. */ if (esp->es_server.ea_timeout > 0 && esp->es_server.ea_timeout < 30) esp->es_server.ea_timeout = 30; } else { break; } if ((cp2 = strchr(cp, ':')) == NULL) break; *cp2++ = '\0'; tpw.pebuf.name = esp->es_server.ea_peer; tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf, cp); tpw.pebuf.password.data = tpw.pwbuf; tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf, cp2); tpw.pebuf.salt.data = tpw.saltbuf; if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL) break; esp->es_server.ea_session = (void *)ts; esp->es_server.ea_state = eapSRP1; vals[0] = esp->es_server.ea_id + 1; vals[1] = EAPT_SRP; t_serveraddexdata(ts, vals, 2); /* Generate B; must call before t_servergetkey() */ t_servergenexp(ts); break; } #endif /* USE_SRP */ esp->es_server.ea_state = eapMD5Chall; break; case eapSRP1: #ifdef USE_SRP ts = (struct t_server *)esp->es_server.ea_session; if (ts != NULL && status != 0) { t_serverclose(ts); esp->es_server.ea_session = NULL; esp->es_server.ea_skey = NULL; } #endif /* USE_SRP */ if (status == 1) { esp->es_server.ea_state = eapMD5Chall; } else if (status != 0 || esp->es_server.ea_session == NULL) { esp->es_server.ea_state = eapBadAuth; } else { esp->es_server.ea_state = eapSRP2; } break; case eapSRP2: #ifdef USE_SRP ts = (struct t_server *)esp->es_server.ea_session; if (ts != NULL && status != 0) { t_serverclose(ts); esp->es_server.ea_session = NULL; esp->es_server.ea_skey = NULL; } #endif /* USE_SRP */ if (status != 0 || esp->es_server.ea_session == NULL) { esp->es_server.ea_state = eapBadAuth; } else { esp->es_server.ea_state = eapSRP3; } break; case eapSRP3: case eapSRP4: #ifdef USE_SRP ts = (struct t_server *)esp->es_server.ea_session; if (ts != NULL && status != 0) { t_serverclose(ts); esp->es_server.ea_session = NULL; esp->es_server.ea_skey = NULL; } #endif /* USE_SRP */ if (status != 0 || esp->es_server.ea_session == NULL) { esp->es_server.ea_state = eapBadAuth; } else { esp->es_server.ea_state = eapOpen; } break; case eapMD5Chall: if (status != 0) { esp->es_server.ea_state = eapBadAuth; } else { esp->es_server.ea_state = eapOpen; } break; default: esp->es_server.ea_state = eapBadAuth; break; } if (esp->es_server.ea_state == eapBadAuth) eap_send_failure(esp); } /* * Format an EAP Request message and send it to the peer. Message * type depends on current state. (Server operation) */ static void eap_send_request(esp) eap_state *esp; { u_char *outp; u_char *lenloc; u_char *ptr; int outlen; int challen; char *str; #ifdef USE_SRP struct t_server *ts; u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp; int i, j; struct b64state b64; SHA1_CTX ctxt; #endif /* USE_SRP */ /* Handle both initial auth and restart */ if (esp->es_server.ea_state < eapIdentify && esp->es_server.ea_state != eapInitial) { esp->es_server.ea_state = eapIdentify; if (explicit_remote) { /* * If we already know the peer's * unauthenticated name, then there's no * reason to ask. Go to next state instead. */ esp->es_server.ea_peer = remote_name; esp->es_server.ea_peerlen = strlen(remote_name); eap_figure_next_state(esp, 0); } } if (esp->es_server.ea_maxrequests > 0 && esp->es_server.ea_requests >= esp->es_server.ea_maxrequests) { if (esp->es_server.ea_responses > 0) error("EAP: too many Requests sent"); else error("EAP: no response to Requests"); eap_send_failure(esp); return; } outp = outpacket_buf; MAKEHEADER(outp, PPP_EAP); PUTCHAR(EAP_REQUEST, outp); PUTCHAR(esp->es_server.ea_id, outp); lenloc = outp; INCPTR(2, outp); switch (esp->es_server.ea_state) { case eapIdentify: PUTCHAR(EAPT_IDENTITY, outp); str = "Name"; challen = strlen(str); BCOPY(str, outp, challen); INCPTR(challen, outp); break; case eapMD5Chall: PUTCHAR(EAPT_MD5CHAP, outp); /* * pick a random challenge length between * MIN_CHALLENGE_LENGTH and MAX_CHALLENGE_LENGTH */ challen = (drand48() * (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) + MIN_CHALLENGE_LENGTH; PUTCHAR(challen, outp); esp->es_challen = challen; ptr = esp->es_challenge; while (--challen >= 0) *ptr++ = (u_char) (drand48() * 0x100); BCOPY(esp->es_challenge, outp, esp->es_challen); INCPTR(esp->es_challen, outp); BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen); INCPTR(esp->es_server.ea_namelen, outp); break; #ifdef USE_SRP case eapSRP1: PUTCHAR(EAPT_SRP, outp); PUTCHAR(EAPSRP_CHALLENGE, outp); PUTCHAR(esp->es_server.ea_namelen, outp); BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen); INCPTR(esp->es_server.ea_namelen, outp); ts = (struct t_server *)esp->es_server.ea_session; assert(ts != NULL); PUTCHAR(ts->s.len, outp); BCOPY(ts->s.data, outp, ts->s.len); INCPTR(ts->s.len, outp); if (ts->g.len == 1 && ts->g.data[0] == 2) { PUTCHAR(0, outp); } else { PUTCHAR(ts->g.len, outp); BCOPY(ts->g.data, outp, ts->g.len); INCPTR(ts->g.len, outp); } if (ts->n.len != sizeof (wkmodulus) || BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) { BCOPY(ts->n.data, outp, ts->n.len); INCPTR(ts->n.len, outp); } break; case eapSRP2: PUTCHAR(EAPT_SRP, outp); PUTCHAR(EAPSRP_SKEY, outp); ts = (struct t_server *)esp->es_server.ea_session; assert(ts != NULL); BCOPY(ts->B.data, outp, ts->B.len); INCPTR(ts->B.len, outp); break; case eapSRP3: PUTCHAR(EAPT_SRP, outp); PUTCHAR(EAPSRP_SVALIDATOR, outp); PUTLONG(SRPVAL_EBIT, outp); ts = (struct t_server *)esp->es_server.ea_session; assert(ts != NULL); BCOPY(t_serverresponse(ts), outp, SHA_DIGESTSIZE); INCPTR(SHA_DIGESTSIZE, outp); if (pncrypt_setkey(0)) { /* Generate pseudonym */ optr = outp; cp = (unsigned char *)esp->es_server.ea_peer; if ((j = i = esp->es_server.ea_peerlen) > 7) j = 7; clear[0] = i; BCOPY(cp, clear + 1, j); i -= j; cp += j; if (!DesEncrypt(clear, cipher)) { dbglog("no DES here; not generating pseudonym"); break; } BZERO(&b64, sizeof (b64)); outp++; /* space for pseudonym length */ outp += b64enc(&b64, cipher, 8, outp); while (i >= 8) { (void) DesEncrypt(cp, cipher); outp += b64enc(&b64, cipher, 8, outp); cp += 8; i -= 8; } if (i > 0) { BCOPY(cp, clear, i); cp += i; while (i < 8) { *cp++ = drand48() * 0x100; i++; } (void) DesEncrypt(clear, cipher); outp += b64enc(&b64, cipher, 8, outp); } outp += b64flush(&b64, outp); /* Set length and pad out to next 20 octet boundary */ i = outp - optr - 1; *optr = i; i %= SHA_DIGESTSIZE; if (i != 0) { while (i < SHA_DIGESTSIZE) { *outp++ = drand48() * 0x100; i++; } } /* Obscure the pseudonym with SHA1 hash */ SHA1Init(&ctxt); SHA1Update(&ctxt, &esp->es_server.ea_id, 1); SHA1Update(&ctxt, esp->es_server.ea_skey, SESSION_KEY_LEN); SHA1Update(&ctxt, esp->es_server.ea_peer, esp->es_server.ea_peerlen); while (optr < outp) { SHA1Final(dig, &ctxt); cp = dig; while (cp < dig + SHA_DIGESTSIZE) *optr++ ^= *cp++; SHA1Init(&ctxt); SHA1Update(&ctxt, &esp->es_server.ea_id, 1); SHA1Update(&ctxt, esp->es_server.ea_skey, SESSION_KEY_LEN); SHA1Update(&ctxt, optr - SHA_DIGESTSIZE, SHA_DIGESTSIZE); } } break; case eapSRP4: PUTCHAR(EAPT_SRP, outp); PUTCHAR(EAPSRP_LWRECHALLENGE, outp); challen = MIN_CHALLENGE_LENGTH + ((MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH) * drand48()); esp->es_challen = challen; ptr = esp->es_challenge; while (--challen >= 0) *ptr++ = drand48() * 0x100; BCOPY(esp->es_challenge, outp, esp->es_challen); INCPTR(esp->es_challen, outp); break; #endif /* USE_SRP */ default: return; } outlen = (outp - outpacket_buf) - PPP_HDRLEN; PUTSHORT(outlen, lenloc); output(esp->es_unit, outpacket_buf, outlen + PPP_HDRLEN); esp->es_server.ea_requests++; if (esp->es_server.ea_timeout > 0) TIMEOUT(eap_server_timeout, esp, esp->es_server.ea_timeout); } /* * eap_authpeer - Authenticate our peer (behave as server). * * Start server state and send first request. This is called only * after eap_lowerup. */ void eap_authpeer(unit, localname) int unit; char *localname; { eap_state *esp = &eap_states[unit]; /* Save the name we're given. */ esp->es_server.ea_name = localname; esp->es_server.ea_namelen = strlen(localname); esp->es_savedtime = esp->es_server.ea_timeout; /* Lower layer up yet? */ if (esp->es_server.ea_state == eapInitial || esp->es_server.ea_state == eapPending) { esp->es_server.ea_state = eapPending; return; } esp->es_server.ea_state = eapPending; /* ID number not updated here intentionally; hashed into M1 */ eap_send_request(esp); } /* * eap_server_timeout - Retransmission timer for sending Requests * expired. */ static void eap_server_timeout(arg) void *arg; { eap_state *esp = (eap_state *) arg; if (!eap_server_active(esp)) return; /* EAP ID number must not change on timeout. */ eap_send_request(esp); } /* * When it's time to send rechallenge the peer, this timeout is * called. Once the rechallenge is successful, the response handler * will restart the timer. If it fails, then the link is dropped. */ static void eap_rechallenge(arg) void *arg; { eap_state *esp = (eap_state *)arg; if (esp->es_server.ea_state != eapOpen && esp->es_server.ea_state != eapSRP4) return; esp->es_server.ea_requests = 0; esp->es_server.ea_state = eapIdentify; eap_figure_next_state(esp, 0); esp->es_server.ea_id++; eap_send_request(esp); } static void srp_lwrechallenge(arg) void *arg; { eap_state *esp = (eap_state *)arg; if (esp->es_server.ea_state != eapOpen || esp->es_server.ea_type != EAPT_SRP) return; esp->es_server.ea_requests = 0; esp->es_server.ea_state = eapSRP4; esp->es_server.ea_id++; eap_send_request(esp); } /* * eap_lowerup - The lower layer is now up. * * This is called before either eap_authpeer or eap_authwithpeer. See * link_established() in auth.c. All that's necessary here is to * return to closed state so that those two routines will do the right * thing. */ static void eap_lowerup(unit) int unit; { eap_state *esp = &eap_states[unit]; /* Discard any (possibly authenticated) peer name. */ if (esp->es_server.ea_peer != NULL && esp->es_server.ea_peer != remote_name) free(esp->es_server.ea_peer); esp->es_server.ea_peer = NULL; if (esp->es_client.ea_peer != NULL) free(esp->es_client.ea_peer); esp->es_client.ea_peer = NULL; esp->es_client.ea_state = eapClosed; esp->es_server.ea_state = eapClosed; } /* * eap_lowerdown - The lower layer is now down. * * Cancel all timeouts and return to initial state. */ static void eap_lowerdown(unit) int unit; { eap_state *esp = &eap_states[unit]; if (eap_client_active(esp) && esp->es_client.ea_timeout > 0) { UNTIMEOUT(eap_client_timeout, (void *)esp); } if (eap_server_active(esp)) { if (esp->es_server.ea_timeout > 0) { UNTIMEOUT(eap_server_timeout, (void *)esp); } } else { if ((esp->es_server.ea_state == eapOpen || esp->es_server.ea_state == eapSRP4) && esp->es_rechallenge > 0) { UNTIMEOUT(eap_rechallenge, (void *)esp); } if (esp->es_server.ea_state == eapOpen && esp->es_lwrechallenge > 0) { UNTIMEOUT(srp_lwrechallenge, (void *)esp); } } esp->es_client.ea_state = esp->es_server.ea_state = eapInitial; esp->es_client.ea_requests = esp->es_server.ea_requests = 0; } /* * eap_protrej - Peer doesn't speak this protocol. * * This shouldn't happen. If it does, it represents authentication * failure. */ static void eap_protrej(unit) int unit; { eap_state *esp = &eap_states[unit]; if (eap_client_active(esp)) { error("EAP authentication failed due to Protocol-Reject"); auth_withpeer_fail(unit, PPP_EAP); } if (eap_server_active(esp)) { error("EAP authentication of peer failed on Protocol-Reject"); auth_peer_fail(unit, PPP_EAP); } eap_lowerdown(unit); } /* * Format and send a regular EAP Response message. */ static void eap_send_response(esp, id, typenum, str, lenstr) eap_state *esp; u_char id; u_char typenum; u_char *str; int lenstr; { u_char *outp; int msglen; outp = outpacket_buf; MAKEHEADER(outp, PPP_EAP); PUTCHAR(EAP_RESPONSE, outp); PUTCHAR(id, outp); esp->es_client.ea_id = id; msglen = EAP_HEADERLEN + sizeof (u_char) + lenstr; PUTSHORT(msglen, outp); PUTCHAR(typenum, outp); if (lenstr > 0) { BCOPY(str, outp, lenstr); } output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); } /* * Format and send an MD5-Challenge EAP Response message. */ static void eap_chap_response(esp, id, hash, name, namelen) eap_state *esp; u_char id; u_char *hash; char *name; int namelen; { u_char *outp; int msglen; outp = outpacket_buf; MAKEHEADER(outp, PPP_EAP); PUTCHAR(EAP_RESPONSE, outp); PUTCHAR(id, outp); esp->es_client.ea_id = id; msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE + namelen; PUTSHORT(msglen, outp); PUTCHAR(EAPT_MD5CHAP, outp); PUTCHAR(MD5_SIGNATURE_SIZE, outp); BCOPY(hash, outp, MD5_SIGNATURE_SIZE); INCPTR(MD5_SIGNATURE_SIZE, outp); if (namelen > 0) { BCOPY(name, outp, namelen); } output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); } #ifdef USE_SRP /* * Format and send a SRP EAP Response message. */ static void eap_srp_response(esp, id, subtypenum, str, lenstr) eap_state *esp; u_char id; u_char subtypenum; u_char *str; int lenstr; { u_char *outp; int msglen; outp = outpacket_buf; MAKEHEADER(outp, PPP_EAP); PUTCHAR(EAP_RESPONSE, outp); PUTCHAR(id, outp); esp->es_client.ea_id = id; msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + lenstr; PUTSHORT(msglen, outp); PUTCHAR(EAPT_SRP, outp); PUTCHAR(subtypenum, outp); if (lenstr > 0) { BCOPY(str, outp, lenstr); } output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); } /* * Format and send a SRP EAP Client Validator Response message. */ static void eap_srpval_response(esp, id, flags, str) eap_state *esp; u_char id; u_int32_t flags; u_char *str; { u_char *outp; int msglen; outp = outpacket_buf; MAKEHEADER(outp, PPP_EAP); PUTCHAR(EAP_RESPONSE, outp); PUTCHAR(id, outp); esp->es_client.ea_id = id; msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u_int32_t) + SHA_DIGESTSIZE; PUTSHORT(msglen, outp); PUTCHAR(EAPT_SRP, outp); PUTCHAR(EAPSRP_CVALIDATOR, outp); PUTLONG(flags, outp); BCOPY(str, outp, SHA_DIGESTSIZE); output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); } #endif /* USE_SRP */ static void eap_send_nak(esp, id, type) eap_state *esp; u_char id; u_char type; { u_char *outp; int msglen; outp = outpacket_buf; MAKEHEADER(outp, PPP_EAP); PUTCHAR(EAP_RESPONSE, outp); PUTCHAR(id, outp); esp->es_client.ea_id = id; msglen = EAP_HEADERLEN + 2 * sizeof (u_char); PUTSHORT(msglen, outp); PUTCHAR(EAPT_NAK, outp); PUTCHAR(type, outp); output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); } #ifdef USE_SRP static char * name_of_pn_file() { char *user, *path, *file; struct passwd *pw; size_t pl; static bool pnlogged = 0; pw = getpwuid(getuid()); if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) { errno = EINVAL; return (NULL); } file = _PATH_PSEUDONYM; pl = strlen(user) + strlen(file) + 2; path = malloc(pl); if (path == NULL) return (NULL); (void) slprintf(path, pl, "%s/%s", user, file); if (!pnlogged) { dbglog("pseudonym file: %s", path); pnlogged = 1; } return (path); } static int open_pn_file(modebits) mode_t modebits; { char *path; int fd, err; if ((path = name_of_pn_file()) == NULL) return (-1); fd = open(path, modebits, S_IRUSR | S_IWUSR); err = errno; free(path); errno = err; return (fd); } static void remove_pn_file() { char *path; if ((path = name_of_pn_file()) != NULL) { (void) unlink(path); (void) free(path); } } static void write_pseudonym(esp, inp, len, id) eap_state *esp; u_char *inp; int len, id; { u_char val; u_char *datp, *digp; SHA1_CTX ctxt; u_char dig[SHA_DIGESTSIZE]; int dsize, fd, olen = len; /* * Do the decoding by working backwards. This eliminates the need * to save the decoded output in a separate buffer. */ val = id; while (len > 0) { if ((dsize = len % SHA_DIGESTSIZE) == 0) dsize = SHA_DIGESTSIZE; len -= dsize; datp = inp + len; SHA1Init(&ctxt); SHA1Update(&ctxt, &val, 1); SHA1Update(&ctxt, esp->es_client.ea_skey, SESSION_KEY_LEN); if (len > 0) { SHA1Update(&ctxt, datp, SHA_DIGESTSIZE); } else { SHA1Update(&ctxt, esp->es_client.ea_name, esp->es_client.ea_namelen); } SHA1Final(dig, &ctxt); for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++) *datp++ ^= *digp; } /* Now check that the result is sane */ if (olen <= 0 || *inp + 1 > olen) { dbglog("EAP: decoded pseudonym is unusable <%.*B>", olen, inp); return; } /* Save it away */ fd = open_pn_file(O_WRONLY | O_CREAT | O_TRUNC); if (fd < 0) { dbglog("EAP: error saving pseudonym: %m"); return; } len = write(fd, inp + 1, *inp); if (close(fd) != -1 && len == *inp) { dbglog("EAP: saved pseudonym"); esp->es_usedpseudo = 0; } else { dbglog("EAP: failed to save pseudonym"); remove_pn_file(); } } #endif /* USE_SRP */ /* * eap_request - Receive EAP Request message (client mode). */ static void eap_request(esp, inp, id, len) eap_state *esp; u_char *inp; int id; int len; { u_char typenum; u_char vallen; int secret_len; char secret[MAXWORDLEN]; char rhostname[256]; MD5_CTX mdContext; u_char hash[MD5_SIGNATURE_SIZE]; #ifdef USE_SRP struct t_client *tc; struct t_num sval, gval, Nval, *Ap, Bval; u_char vals[2]; SHA1_CTX ctxt; u_char dig[SHA_DIGESTSIZE]; int fd; #endif /* USE_SRP */ /* * Note: we update es_client.ea_id *only if* a Response * message is being generated. Otherwise, we leave it the * same for duplicate detection purposes. */ esp->es_client.ea_requests++; if (esp->es_client.ea_maxrequests != 0 && esp->es_client.ea_requests > esp->es_client.ea_maxrequests) { info("EAP: received too many Request messages"); if (esp->es_client.ea_timeout > 0) { UNTIMEOUT(eap_client_timeout, (void *)esp); } auth_withpeer_fail(esp->es_unit, PPP_EAP); return; } if (len <= 0) { error("EAP: empty Request message discarded"); return; } GETCHAR(typenum, inp); len--; switch (typenum) { case EAPT_IDENTITY: if (len > 0) info("EAP: Identity prompt \"%.*q\"", len, inp); #ifdef USE_SRP if (esp->es_usepseudo && (esp->es_usedpseudo == 0 || (esp->es_usedpseudo == 1 && id == esp->es_client.ea_id))) { esp->es_usedpseudo = 1; /* Try to get a pseudonym */ if ((fd = open_pn_file(O_RDONLY)) >= 0) { strcpy(rhostname, SRP_PSEUDO_ID); len = read(fd, rhostname + SRP_PSEUDO_LEN, sizeof (rhostname) - SRP_PSEUDO_LEN); /* XXX NAI unsupported */ if (len > 0) { eap_send_response(esp, id, typenum, rhostname, len + SRP_PSEUDO_LEN); } (void) close(fd); if (len > 0) break; } } /* Stop using pseudonym now. */ if (esp->es_usepseudo && esp->es_usedpseudo != 2) { remove_pn_file(); esp->es_usedpseudo = 2; } #endif /* USE_SRP */ eap_send_response(esp, id, typenum, esp->es_client.ea_name, esp->es_client.ea_namelen); break; case EAPT_NOTIFICATION: if (len > 0) info("EAP: Notification \"%.*q\"", len, inp); eap_send_response(esp, id, typenum, NULL, 0); break; case EAPT_NAK: /* * Avoid the temptation to send Response Nak in reply * to Request Nak here. It can only lead to trouble. */ warn("EAP: unexpected Nak in Request; ignored"); /* Return because we're waiting for something real. */ return; case EAPT_MD5CHAP: if (len < 1) { error("EAP: received MD5-Challenge with no data"); /* Bogus request; wait for something real. */ return; } GETCHAR(vallen, inp); len--; if (vallen < 8 || vallen > len) { error("EAP: MD5-Challenge with bad length %d (8..%d)", vallen, len); /* Try something better. */ eap_send_nak(esp, id, EAPT_SRP); break; } /* Not so likely to happen. */ if (vallen >= len + sizeof (rhostname)) { dbglog("EAP: trimming really long peer name down"); BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1); rhostname[sizeof (rhostname) - 1] = '\0'; } else { BCOPY(inp + vallen, rhostname, len - vallen); rhostname[len - vallen] = '\0'; } /* In case the remote doesn't give us his name. */ if (explicit_remote || (remote_name[0] != '\0' && vallen == len)) strlcpy(rhostname, remote_name, sizeof (rhostname)); /* * Get the secret for authenticating ourselves with * the specified host. */ if (!get_secret(esp->es_unit, esp->es_client.ea_name, rhostname, secret, &secret_len, 0)) { dbglog("EAP: no MD5 secret for auth to %q", rhostname); eap_send_nak(esp, id, EAPT_SRP); break; } MD5_Init(&mdContext); typenum = id; MD5_Update(&mdContext, &typenum, 1); MD5_Update(&mdContext, (u_char *)secret, secret_len); BZERO(secret, sizeof (secret)); MD5_Update(&mdContext, inp, vallen); MD5_Final(hash, &mdContext); eap_chap_response(esp, id, hash, esp->es_client.ea_name, esp->es_client.ea_namelen); break; #ifdef USE_SRP case EAPT_SRP: if (len < 1) { error("EAP: received empty SRP Request"); /* Bogus request; wait for something real. */ return; } /* Get subtype */ GETCHAR(vallen, inp); len--; switch (vallen) { case EAPSRP_CHALLENGE: tc = NULL; if (esp->es_client.ea_session != NULL) { tc = (struct t_client *)esp->es_client. ea_session; /* * If this is a new challenge, then start * over with a new client session context. * Otherwise, just resend last response. */ if (id != esp->es_client.ea_id) { t_clientclose(tc); esp->es_client.ea_session = NULL; tc = NULL; } } /* No session key just yet */ esp->es_client.ea_skey = NULL; if (tc == NULL) { GETCHAR(vallen, inp); len--; if (vallen >= len) { error("EAP: badly-formed SRP Challenge" " (name)"); /* Ignore badly-formed messages */ return; } BCOPY(inp, rhostname, vallen); rhostname[vallen] = '\0'; INCPTR(vallen, inp); len -= vallen; /* * In case the remote doesn't give us his name, * use configured name. */ if (explicit_remote || (remote_name[0] != '\0' && vallen == 0)) { strlcpy(rhostname, remote_name, sizeof (rhostname)); } if (esp->es_client.ea_peer != NULL) free(esp->es_client.ea_peer); esp->es_client.ea_peer = strdup(rhostname); esp->es_client.ea_peerlen = strlen(rhostname); GETCHAR(vallen, inp); len--; if (vallen >= len) { error("EAP: badly-formed SRP Challenge" " (s)"); /* Ignore badly-formed messages */ return; } sval.data = inp; sval.len = vallen; INCPTR(vallen, inp); len -= vallen; GETCHAR(vallen, inp); len--; if (vallen > len) { error("EAP: badly-formed SRP Challenge" " (g)"); /* Ignore badly-formed messages */ return; } /* If no generator present, then use value 2 */ if (vallen == 0) { gval.data = (u_char *)"\002"; gval.len = 1; } else { gval.data = inp; gval.len = vallen; } INCPTR(vallen, inp); len -= vallen; /* * If no modulus present, then use well-known * value. */ if (len == 0) { Nval.data = (u_char *)wkmodulus; Nval.len = sizeof (wkmodulus); } else { Nval.data = inp; Nval.len = len; } tc = t_clientopen(esp->es_client.ea_name, &Nval, &gval, &sval); if (tc == NULL) { eap_send_nak(esp, id, EAPT_MD5CHAP); break; } esp->es_client.ea_session = (void *)tc; /* Add Challenge ID & type to verifier */ vals[0] = id; vals[1] = EAPT_SRP; t_clientaddexdata(tc, vals, 2); } Ap = t_clientgenexp(tc); eap_srp_response(esp, id, EAPSRP_CKEY, Ap->data, Ap->len); break; case EAPSRP_SKEY: tc = (struct t_client *)esp->es_client.ea_session; if (tc == NULL) { warn("EAP: peer sent Subtype 2 without 1"); eap_send_nak(esp, id, EAPT_MD5CHAP); break; } if (esp->es_client.ea_skey != NULL) { /* * ID number should not change here. Warn * if it does (but otherwise ignore). */ if (id != esp->es_client.ea_id) { warn("EAP: ID changed from %d to %d " "in SRP Subtype 2 rexmit", esp->es_client.ea_id, id); } } else { if (get_srp_secret(esp->es_unit, esp->es_client.ea_name, esp->es_client.ea_peer, secret, 0) == 0) { /* * Can't work with this peer because * the secret is missing. Just give * up. */ eap_send_nak(esp, id, EAPT_MD5CHAP); break; } Bval.data = inp; Bval.len = len; t_clientpasswd(tc, secret); BZERO(secret, sizeof (secret)); esp->es_client.ea_skey = t_clientgetkey(tc, &Bval); if (esp->es_client.ea_skey == NULL) { /* Server is rogue; stop now */ error("EAP: SRP server is rogue"); goto client_failure; } } eap_srpval_response(esp, id, SRPVAL_EBIT, t_clientresponse(tc)); break; case EAPSRP_SVALIDATOR: tc = (struct t_client *)esp->es_client.ea_session; if (tc == NULL || esp->es_client.ea_skey == NULL) { warn("EAP: peer sent Subtype 3 without 1/2"); eap_send_nak(esp, id, EAPT_MD5CHAP); break; } /* * If we're already open, then this ought to be a * duplicate. Otherwise, check that the server is * who we think it is. */ if (esp->es_client.ea_state == eapOpen) { if (id != esp->es_client.ea_id) { warn("EAP: ID changed from %d to %d " "in SRP Subtype 3 rexmit", esp->es_client.ea_id, id); } } else { len -= sizeof (u_int32_t) + SHA_DIGESTSIZE; if (len < 0 || t_clientverify(tc, inp + sizeof (u_int32_t)) != 0) { error("EAP: SRP server verification " "failed"); goto client_failure; } GETLONG(esp->es_client.ea_keyflags, inp); /* Save pseudonym if user wants it. */ if (len > 0 && esp->es_usepseudo) { INCPTR(SHA_DIGESTSIZE, inp); write_pseudonym(esp, inp, len, id); } } /* * We've verified our peer. We're now mostly done, * except for waiting on the regular EAP Success * message. */ eap_srp_response(esp, id, EAPSRP_ACK, NULL, 0); break; case EAPSRP_LWRECHALLENGE: if (len < 4) { warn("EAP: malformed Lightweight rechallenge"); return; } SHA1Init(&ctxt); vals[0] = id; SHA1Update(&ctxt, vals, 1); SHA1Update(&ctxt, esp->es_client.ea_skey, SESSION_KEY_LEN); SHA1Update(&ctxt, inp, len); SHA1Update(&ctxt, esp->es_client.ea_name, esp->es_client.ea_namelen); SHA1Final(dig, &ctxt); eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig, SHA_DIGESTSIZE); break; default: error("EAP: unknown SRP Subtype %d", vallen); eap_send_nak(esp, id, EAPT_MD5CHAP); break; } break; #endif /* USE_SRP */ default: info("EAP: unknown authentication type %d; Naking", typenum); eap_send_nak(esp, id, EAPT_SRP); break; } if (esp->es_client.ea_timeout > 0) { UNTIMEOUT(eap_client_timeout, (void *)esp); TIMEOUT(eap_client_timeout, (void *)esp, esp->es_client.ea_timeout); } return; #ifdef USE_SRP client_failure: esp->es_client.ea_state = eapBadAuth; if (esp->es_client.ea_timeout > 0) { UNTIMEOUT(eap_client_timeout, (void *)esp); } esp->es_client.ea_session = NULL; t_clientclose(tc); auth_withpeer_fail(esp->es_unit, PPP_EAP); #endif /* USE_SRP */ } /* * eap_response - Receive EAP Response message (server mode). */ static void eap_response(esp, inp, id, len) eap_state *esp; u_char *inp; int id; int len; { u_char typenum; u_char vallen; int secret_len; char secret[MAXSECRETLEN]; char rhostname[256]; MD5_CTX mdContext; u_char hash[MD5_SIGNATURE_SIZE]; #ifdef USE_SRP struct t_server *ts; struct t_num A; SHA1_CTX ctxt; u_char dig[SHA_DIGESTSIZE]; #endif /* USE_SRP */ if (esp->es_server.ea_id != id) { dbglog("EAP: discarding Response %d; expected ID %d", id, esp->es_server.ea_id); return; } esp->es_server.ea_responses++; if (len <= 0) { error("EAP: empty Response message discarded"); return; } GETCHAR(typenum, inp); len--; switch (typenum) { case EAPT_IDENTITY: if (esp->es_server.ea_state != eapIdentify) { dbglog("EAP discarding unwanted Identify \"%.q\"", len, inp); break; } info("EAP: unauthenticated peer name \"%.*q\"", len, inp); if (esp->es_server.ea_peer != NULL && esp->es_server.ea_peer != remote_name) free(esp->es_server.ea_peer); esp->es_server.ea_peer = malloc(len + 1); if (esp->es_server.ea_peer == NULL) { esp->es_server.ea_peerlen = 0; eap_figure_next_state(esp, 1); break; } BCOPY(inp, esp->es_server.ea_peer, len); esp->es_server.ea_peer[len] = '\0'; esp->es_server.ea_peerlen = len; eap_figure_next_state(esp, 0); break; case EAPT_NOTIFICATION: dbglog("EAP unexpected Notification; response discarded"); break; case EAPT_NAK: if (len < 1) { info("EAP: Nak Response with no suggested protocol"); eap_figure_next_state(esp, 1); break; } GETCHAR(vallen, inp); len--; if (!explicit_remote && esp->es_server.ea_state == eapIdentify){ /* Peer cannot Nak Identify Request */ eap_figure_next_state(esp, 1); break; } switch (vallen) { case EAPT_SRP: /* Run through SRP validator selection again. */ esp->es_server.ea_state = eapIdentify; eap_figure_next_state(esp, 0); break; case EAPT_MD5CHAP: esp->es_server.ea_state = eapMD5Chall; break; default: dbglog("EAP: peer requesting unknown Type %d", vallen); switch (esp->es_server.ea_state) { case eapSRP1: case eapSRP2: case eapSRP3: esp->es_server.ea_state = eapMD5Chall; break; case eapMD5Chall: case eapSRP4: esp->es_server.ea_state = eapIdentify; eap_figure_next_state(esp, 0); break; default: break; } break; } break; case EAPT_MD5CHAP: if (esp->es_server.ea_state != eapMD5Chall) { error("EAP: unexpected MD5-Response"); eap_figure_next_state(esp, 1); break; } if (len < 1) { error("EAP: received MD5-Response with no data"); eap_figure_next_state(esp, 1); break; } GETCHAR(vallen, inp); len--; if (vallen != 16 || vallen > len) { error("EAP: MD5-Response with bad length %d", vallen); eap_figure_next_state(esp, 1); break; } /* Not so likely to happen. */ if (vallen >= len + sizeof (rhostname)) { dbglog("EAP: trimming really long peer name down"); BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1); rhostname[sizeof (rhostname) - 1] = '\0'; } else { BCOPY(inp + vallen, rhostname, len - vallen); rhostname[len - vallen] = '\0'; } /* In case the remote doesn't give us his name. */ if (explicit_remote || (remote_name[0] != '\0' && vallen == len)) strlcpy(rhostname, remote_name, sizeof (rhostname)); /* * Get the secret for authenticating the specified * host. */ if (!get_secret(esp->es_unit, rhostname, esp->es_server.ea_name, secret, &secret_len, 1)) { dbglog("EAP: no MD5 secret for auth of %q", rhostname); eap_send_failure(esp); break; } MD5_Init(&mdContext); MD5_Update(&mdContext, &esp->es_server.ea_id, 1); MD5_Update(&mdContext, (u_char *)secret, secret_len); BZERO(secret, sizeof (secret)); MD5_Update(&mdContext, esp->es_challenge, esp->es_challen); MD5_Final(hash, &mdContext); if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) { eap_send_failure(esp); break; } esp->es_server.ea_type = EAPT_MD5CHAP; eap_send_success(esp); eap_figure_next_state(esp, 0); if (esp->es_rechallenge != 0) TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge); break; #ifdef USE_SRP case EAPT_SRP: if (len < 1) { error("EAP: empty SRP Response"); eap_figure_next_state(esp, 1); break; } GETCHAR(typenum, inp); len--; switch (typenum) { case EAPSRP_CKEY: if (esp->es_server.ea_state != eapSRP1) { error("EAP: unexpected SRP Subtype 1 Response"); eap_figure_next_state(esp, 1); break; } A.data = inp; A.len = len; ts = (struct t_server *)esp->es_server.ea_session; assert(ts != NULL); esp->es_server.ea_skey = t_servergetkey(ts, &A); if (esp->es_server.ea_skey == NULL) { /* Client's A value is bogus; terminate now */ error("EAP: bogus A value from client"); eap_send_failure(esp); } else { eap_figure_next_state(esp, 0); } break; case EAPSRP_CVALIDATOR: if (esp->es_server.ea_state != eapSRP2) { error("EAP: unexpected SRP Subtype 2 Response"); eap_figure_next_state(esp, 1); break; } if (len < sizeof (u_int32_t) + SHA_DIGESTSIZE) { error("EAP: M1 length %d < %d", len, sizeof (u_int32_t) + SHA_DIGESTSIZE); eap_figure_next_state(esp, 1); break; } GETLONG(esp->es_server.ea_keyflags, inp); ts = (struct t_server *)esp->es_server.ea_session; assert(ts != NULL); if (t_serververify(ts, inp)) { info("EAP: unable to validate client identity"); eap_send_failure(esp); break; } eap_figure_next_state(esp, 0); break; case EAPSRP_ACK: if (esp->es_server.ea_state != eapSRP3) { error("EAP: unexpected SRP Subtype 3 Response"); eap_send_failure(esp); break; } esp->es_server.ea_type = EAPT_SRP; eap_send_success(esp); eap_figure_next_state(esp, 0); if (esp->es_rechallenge != 0) TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge); if (esp->es_lwrechallenge != 0) TIMEOUT(srp_lwrechallenge, esp, esp->es_lwrechallenge); break; case EAPSRP_LWRECHALLENGE: if (esp->es_server.ea_state != eapSRP4) { info("EAP: unexpected SRP Subtype 4 Response"); return; } if (len != SHA_DIGESTSIZE) { error("EAP: bad Lightweight rechallenge " "response"); return; } SHA1Init(&ctxt); vallen = id; SHA1Update(&ctxt, &vallen, 1); SHA1Update(&ctxt, esp->es_server.ea_skey, SESSION_KEY_LEN); SHA1Update(&ctxt, esp->es_challenge, esp->es_challen); SHA1Update(&ctxt, esp->es_server.ea_peer, esp->es_server.ea_peerlen); SHA1Final(dig, &ctxt); if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) { error("EAP: failed Lightweight rechallenge"); eap_send_failure(esp); break; } esp->es_server.ea_state = eapOpen; if (esp->es_lwrechallenge != 0) TIMEOUT(srp_lwrechallenge, esp, esp->es_lwrechallenge); break; } break; #endif /* USE_SRP */ default: /* This can't happen. */ error("EAP: unknown Response type %d; ignored", typenum); return; } if (esp->es_server.ea_timeout > 0) { UNTIMEOUT(eap_server_timeout, (void *)esp); } if (esp->es_server.ea_state != eapBadAuth && esp->es_server.ea_state != eapOpen) { esp->es_server.ea_id++; eap_send_request(esp); } } /* * eap_success - Receive EAP Success message (client mode). */ static void eap_success(esp, inp, id, len) eap_state *esp; u_char *inp; int id; int len; { if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)) { dbglog("EAP unexpected success message in state %s (%d)", eap_state_name(esp->es_client.ea_state), esp->es_client.ea_state); return; } if (esp->es_client.ea_timeout > 0) { UNTIMEOUT(eap_client_timeout, (void *)esp); } if (len > 0) { /* This is odd. The spec doesn't allow for this. */ PRINTMSG(inp, len); } esp->es_client.ea_state = eapOpen; auth_withpeer_success(esp->es_unit, PPP_EAP, 0); } /* * eap_failure - Receive EAP Failure message (client mode). */ static void eap_failure(esp, inp, id, len) eap_state *esp; u_char *inp; int id; int len; { if (!eap_client_active(esp)) { dbglog("EAP unexpected failure message in state %s (%d)", eap_state_name(esp->es_client.ea_state), esp->es_client.ea_state); } if (esp->es_client.ea_timeout > 0) { UNTIMEOUT(eap_client_timeout, (void *)esp); } if (len > 0) { /* This is odd. The spec doesn't allow for this. */ PRINTMSG(inp, len); } esp->es_client.ea_state = eapBadAuth; error("EAP: peer reports authentication failure"); auth_withpeer_fail(esp->es_unit, PPP_EAP); } /* * eap_input - Handle received EAP message. */ static void eap_input(unit, inp, inlen) int unit; u_char *inp; int inlen; { eap_state *esp = &eap_states[unit]; u_char code, id; int len; /* * Parse header (code, id and length). If packet too short, * drop it. */ if (inlen < EAP_HEADERLEN) { error("EAP: packet too short: %d < %d", inlen, EAP_HEADERLEN); return; } GETCHAR(code, inp); GETCHAR(id, inp); GETSHORT(len, inp); if (len < EAP_HEADERLEN || len > inlen) { error("EAP: packet has illegal length field %d (%d..%d)", len, EAP_HEADERLEN, inlen); return; } len -= EAP_HEADERLEN; /* Dispatch based on message code */ switch (code) { case EAP_REQUEST: eap_request(esp, inp, id, len); break; case EAP_RESPONSE: eap_response(esp, inp, id, len); break; case EAP_SUCCESS: eap_success(esp, inp, id, len); break; case EAP_FAILURE: eap_failure(esp, inp, id, len); break; default: /* XXX Need code reject */ /* Note: it's not legal to send EAP Nak here. */ warn("EAP: unknown code %d received", code); break; } } /* * eap_printpkt - print the contents of an EAP packet. */ static char *eap_codenames[] = { "Request", "Response", "Success", "Failure" }; static char *eap_typenames[] = { "Identity", "Notification", "Nak", "MD5-Challenge", "OTP", "Generic-Token", NULL, NULL, "RSA", "DSS", "KEA", "KEA-Validate", "TLS", "Defender", "Windows 2000", "Arcot", "Cisco", "Nokia", "SRP" }; static int eap_printpkt(inp, inlen, printer, arg) u_char *inp; int inlen; void (*printer) __P((void *, char *, ...)); void *arg; { int code, id, len, rtype, vallen; u_char *pstart; u_int32_t uval; if (inlen < EAP_HEADERLEN) return (0); pstart = inp; GETCHAR(code, inp); GETCHAR(id, inp); GETSHORT(len, inp); if (len < EAP_HEADERLEN || len > inlen) return (0); if (code >= 1 && code <= sizeof(eap_codenames) / sizeof(char *)) printer(arg, " %s", eap_codenames[code-1]); else printer(arg, " code=0x%x", code); printer(arg, " id=0x%x", id); len -= EAP_HEADERLEN; switch (code) { case EAP_REQUEST: if (len < 1) { printer(arg, " "); break; } GETCHAR(rtype, inp); len--; if (rtype >= 1 && rtype <= sizeof (eap_typenames) / sizeof (char *)) printer(arg, " %s", eap_typenames[rtype-1]); else printer(arg, " type=0x%x", rtype); switch (rtype) { case EAPT_IDENTITY: case EAPT_NOTIFICATION: if (len > 0) { printer(arg, " "); INCPTR(len, inp); len = 0; } else { printer(arg, " "); } break; case EAPT_MD5CHAP: if (len <= 0) break; GETCHAR(vallen, inp); len--; if (vallen > len) goto truncated; printer(arg, " ", vallen, inp); INCPTR(vallen, inp); len -= vallen; if (len > 0) { printer(arg, " "); INCPTR(len, inp); len = 0; } else { printer(arg, " "); } break; case EAPT_SRP: if (len < 3) goto truncated; GETCHAR(vallen, inp); len--; printer(arg, "-%d", vallen); switch (vallen) { case EAPSRP_CHALLENGE: GETCHAR(vallen, inp); len--; if (vallen >= len) goto truncated; if (vallen > 0) { printer(arg, " "); } else { printer(arg, " "); } INCPTR(vallen, inp); len -= vallen; GETCHAR(vallen, inp); len--; if (vallen >= len) goto truncated; printer(arg, " ", vallen, inp); INCPTR(vallen, inp); len -= vallen; GETCHAR(vallen, inp); len--; if (vallen > len) goto truncated; if (vallen == 0) { printer(arg, " "); } else { printer(arg, " ", vallen, inp); } INCPTR(vallen, inp); len -= vallen; if (len == 0) { printer(arg, " "); } else { printer(arg, " ", len, inp); INCPTR(len, inp); len = 0; } break; case EAPSRP_SKEY: printer(arg, " ", len, inp); INCPTR(len, inp); len = 0; break; case EAPSRP_SVALIDATOR: if (len < sizeof (u_int32_t)) break; GETLONG(uval, inp); len -= sizeof (u_int32_t); if (uval & SRPVAL_EBIT) { printer(arg, " E"); uval &= ~SRPVAL_EBIT; } if (uval != 0) { printer(arg, " f<%X>", uval); } if ((vallen = len) > SHA_DIGESTSIZE) vallen = SHA_DIGESTSIZE; printer(arg, " ", len, inp, len < SHA_DIGESTSIZE ? "?" : ""); INCPTR(vallen, inp); len -= vallen; if (len > 0) { printer(arg, " ", len, inp); INCPTR(len, inp); len = 0; } break; case EAPSRP_LWRECHALLENGE: printer(arg, " ", len, inp); INCPTR(len, inp); len = 0; break; } break; } break; case EAP_RESPONSE: if (len < 1) break; GETCHAR(rtype, inp); len--; if (rtype >= 1 && rtype <= sizeof (eap_typenames) / sizeof (char *)) printer(arg, " %s", eap_typenames[rtype-1]); else printer(arg, " type=0x%x", rtype); switch (rtype) { case EAPT_IDENTITY: if (len > 0) { printer(arg, " "); INCPTR(len, inp); len = 0; } break; case EAPT_NAK: if (len <= 0) { printer(arg, " "); break; } GETCHAR(rtype, inp); len--; printer(arg, " = 1 && rtype < sizeof (eap_typenames) / sizeof (char *)) printer(arg, " (%s)", eap_typenames[rtype-1]); printer(arg, ">"); break; case EAPT_MD5CHAP: if (len <= 0) { printer(arg, " "); break; } GETCHAR(vallen, inp); len--; if (vallen > len) goto truncated; printer(arg, " ", vallen, inp); INCPTR(vallen, inp); len -= vallen; if (len > 0) { printer(arg, " "); INCPTR(len, inp); len = 0; } else { printer(arg, " "); } break; case EAPT_SRP: if (len < 1) goto truncated; GETCHAR(vallen, inp); len--; printer(arg, "-%d", vallen); switch (vallen) { case EAPSRP_CKEY: printer(arg, " ", len, inp); INCPTR(len, inp); len = 0; break; case EAPSRP_CVALIDATOR: if (len < sizeof (u_int32_t)) break; GETLONG(uval, inp); len -= sizeof (u_int32_t); if (uval & SRPVAL_EBIT) { printer(arg, " E"); uval &= ~SRPVAL_EBIT; } if (uval != 0) { printer(arg, " f<%X>", uval); } printer(arg, " ", len, inp, len == SHA_DIGESTSIZE ? "" : "?"); INCPTR(len, inp); len = 0; break; case EAPSRP_ACK: break; case EAPSRP_LWRECHALLENGE: printer(arg, " ", len, inp, len == SHA_DIGESTSIZE ? "" : "?"); if ((vallen = len) > SHA_DIGESTSIZE) vallen = SHA_DIGESTSIZE; INCPTR(vallen, inp); len -= vallen; break; } break; } break; case EAP_SUCCESS: /* No payload expected for these! */ case EAP_FAILURE: break; truncated: printer(arg, " "); break; } if (len > 8) printer(arg, "%8B...", inp); else if (len > 0) printer(arg, "%.*B", len, inp); INCPTR(len, inp); return (inp - pstart); } ppp-2.4.5/pppd/eap.h000066400000000000000000000121421130035057700142030ustar00rootroot00000000000000/* * eap.h - Extensible Authentication Protocol for PPP (RFC 2284) * * Copyright (c) 2001 by Sun Microsystems, Inc. * All rights reserved. * * Non-exclusive rights to redistribute, modify, translate, and use * this software in source and binary forms, in whole or in part, is * hereby granted, provided that the above copyright notice is * duplicated in any source form, and that neither the name of the * copyright holder nor the author is used to endorse or promote * products derived from this software. * * 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. * * Original version by James Carlson * * $Id: eap.h,v 1.2 2003/06/11 23:56:26 paulus Exp $ */ #ifndef PPP_EAP_H #define PPP_EAP_H #ifdef __cplusplus extern "C" { #endif /* * Packet header = Code, id, length. */ #define EAP_HEADERLEN 4 /* EAP message codes. */ #define EAP_REQUEST 1 #define EAP_RESPONSE 2 #define EAP_SUCCESS 3 #define EAP_FAILURE 4 /* EAP types */ #define EAPT_IDENTITY 1 #define EAPT_NOTIFICATION 2 #define EAPT_NAK 3 /* (response only) */ #define EAPT_MD5CHAP 4 #define EAPT_OTP 5 /* One-Time Password; RFC 1938 */ #define EAPT_TOKEN 6 /* Generic Token Card */ /* 7 and 8 are unassigned. */ #define EAPT_RSA 9 /* RSA Public Key Authentication */ #define EAPT_DSS 10 /* DSS Unilateral */ #define EAPT_KEA 11 /* KEA */ #define EAPT_KEA_VALIDATE 12 /* KEA-VALIDATE */ #define EAPT_TLS 13 /* EAP-TLS */ #define EAPT_DEFENDER 14 /* Defender Token (AXENT) */ #define EAPT_W2K 15 /* Windows 2000 EAP */ #define EAPT_ARCOT 16 /* Arcot Systems */ #define EAPT_CISCOWIRELESS 17 /* Cisco Wireless */ #define EAPT_NOKIACARD 18 /* Nokia IP smart card */ #define EAPT_SRP 19 /* Secure Remote Password */ /* 20 is deprecated */ /* EAP SRP-SHA1 Subtypes */ #define EAPSRP_CHALLENGE 1 /* Request 1 - Challenge */ #define EAPSRP_CKEY 1 /* Response 1 - Client Key */ #define EAPSRP_SKEY 2 /* Request 2 - Server Key */ #define EAPSRP_CVALIDATOR 2 /* Response 2 - Client Validator */ #define EAPSRP_SVALIDATOR 3 /* Request 3 - Server Validator */ #define EAPSRP_ACK 3 /* Response 3 - final ack */ #define EAPSRP_LWRECHALLENGE 4 /* Req/resp 4 - Lightweight rechal */ #define SRPVAL_EBIT 0x00000001 /* Use shared key for ECP */ #define SRP_PSEUDO_ID "pseudo_" #define SRP_PSEUDO_LEN 7 #define MD5_SIGNATURE_SIZE 16 #define MIN_CHALLENGE_LENGTH 16 #define MAX_CHALLENGE_LENGTH 24 enum eap_state_code { eapInitial = 0, /* No EAP authentication yet requested */ eapPending, /* Waiting for LCP (no timer) */ eapClosed, /* Authentication not in use */ eapListen, /* Client ready (and timer running) */ eapIdentify, /* EAP Identify sent */ eapSRP1, /* Sent EAP SRP-SHA1 Subtype 1 */ eapSRP2, /* Sent EAP SRP-SHA1 Subtype 2 */ eapSRP3, /* Sent EAP SRP-SHA1 Subtype 3 */ eapMD5Chall, /* Sent MD5-Challenge */ eapOpen, /* Completed authentication */ eapSRP4, /* Sent EAP SRP-SHA1 Subtype 4 */ eapBadAuth /* Failed authentication */ }; #define EAP_STATES \ "Initial", "Pending", "Closed", "Listen", "Identify", \ "SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth" #define eap_client_active(esp) ((esp)->es_client.ea_state == eapListen) #define eap_server_active(esp) \ ((esp)->es_server.ea_state >= eapIdentify && \ (esp)->es_server.ea_state <= eapMD5Chall) struct eap_auth { char *ea_name; /* Our name */ char *ea_peer; /* Peer's name */ void *ea_session; /* Authentication library linkage */ u_char *ea_skey; /* Shared encryption key */ int ea_timeout; /* Time to wait (for retransmit/fail) */ int ea_maxrequests; /* Max Requests allowed */ u_short ea_namelen; /* Length of our name */ u_short ea_peerlen; /* Length of peer's name */ enum eap_state_code ea_state; u_char ea_id; /* Current id */ u_char ea_requests; /* Number of Requests sent/received */ u_char ea_responses; /* Number of Responses */ u_char ea_type; /* One of EAPT_* */ u_int32_t ea_keyflags; /* SRP shared key usage flags */ }; /* * Complete EAP state for one PPP session. */ typedef struct eap_state { int es_unit; /* Interface unit number */ struct eap_auth es_client; /* Client (authenticatee) data */ struct eap_auth es_server; /* Server (authenticator) data */ int es_savedtime; /* Saved timeout */ int es_rechallenge; /* EAP rechallenge interval */ int es_lwrechallenge; /* SRP lightweight rechallenge inter */ bool es_usepseudo; /* Use SRP Pseudonym if offered one */ int es_usedpseudo; /* Set if we already sent PN */ int es_challen; /* Length of challenge string */ u_char es_challenge[MAX_CHALLENGE_LENGTH]; } eap_state; /* * Timeouts. */ #define EAP_DEFTIMEOUT 3 /* Timeout (seconds) for rexmit */ #define EAP_DEFTRANSMITS 10 /* max # times to transmit */ #define EAP_DEFREQTIME 20 /* Time to wait for peer request */ #define EAP_DEFALLOWREQ 20 /* max # times to accept requests */ extern eap_state eap_states[]; void eap_authwithpeer __P((int unit, char *localname)); void eap_authpeer __P((int unit, char *localname)); extern struct protent eap_protent; #ifdef __cplusplus } #endif #endif /* PPP_EAP_H */ ppp-2.4.5/pppd/ecp.c000066400000000000000000000120451130035057700142020ustar00rootroot00000000000000/* * ecp.c - PPP Encryption Control Protocol. * * Copyright (c) 2002 Google, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Derived from ccp.c, which is: * * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: ecp.c,v 1.4 2004/11/04 10:02:26 paulus Exp $" static const char rcsid[] = RCSID; #include #include "pppd.h" #include "fsm.h" #include "ecp.h" static option_t ecp_option_list[] = { { "noecp", o_bool, &ecp_protent.enabled_flag, "Disable ECP negotiation" }, { "-ecp", o_bool, &ecp_protent.enabled_flag, "Disable ECP negotiation", OPT_ALIAS }, { NULL } }; /* * Protocol entry points from main code. */ static void ecp_init __P((int unit)); /* static void ecp_open __P((int unit)); static void ecp_close __P((int unit, char *)); static void ecp_lowerup __P((int unit)); static void ecp_lowerdown __P((int)); static void ecp_input __P((int unit, u_char *pkt, int len)); static void ecp_protrej __P((int unit)); */ static int ecp_printpkt __P((u_char *pkt, int len, void (*printer) __P((void *, char *, ...)), void *arg)); /* static void ecp_datainput __P((int unit, u_char *pkt, int len)); */ struct protent ecp_protent = { PPP_ECP, ecp_init, NULL, /* ecp_input, */ NULL, /* ecp_protrej, */ NULL, /* ecp_lowerup, */ NULL, /* ecp_lowerdown, */ NULL, /* ecp_open, */ NULL, /* ecp_close, */ ecp_printpkt, NULL, /* ecp_datainput, */ 0, "ECP", "Encrypted", ecp_option_list, NULL, NULL, NULL }; fsm ecp_fsm[NUM_PPP]; ecp_options ecp_wantoptions[NUM_PPP]; /* what to request the peer to use */ ecp_options ecp_gotoptions[NUM_PPP]; /* what the peer agreed to do */ ecp_options ecp_allowoptions[NUM_PPP]; /* what we'll agree to do */ ecp_options ecp_hisoptions[NUM_PPP]; /* what we agreed to do */ static fsm_callbacks ecp_callbacks = { NULL, /* ecp_resetci, */ NULL, /* ecp_cilen, */ NULL, /* ecp_addci, */ NULL, /* ecp_ackci, */ NULL, /* ecp_nakci, */ NULL, /* ecp_rejci, */ NULL, /* ecp_reqci, */ NULL, /* ecp_up, */ NULL, /* ecp_down, */ NULL, NULL, NULL, NULL, NULL, /* ecp_extcode, */ "ECP" }; /* * ecp_init - initialize ECP. */ static void ecp_init(unit) int unit; { fsm *f = &ecp_fsm[unit]; f->unit = unit; f->protocol = PPP_ECP; f->callbacks = &ecp_callbacks; fsm_init(f); memset(&ecp_wantoptions[unit], 0, sizeof(ecp_options)); memset(&ecp_gotoptions[unit], 0, sizeof(ecp_options)); memset(&ecp_allowoptions[unit], 0, sizeof(ecp_options)); memset(&ecp_hisoptions[unit], 0, sizeof(ecp_options)); } static int ecp_printpkt(p, plen, printer, arg) u_char *p; int plen; void (*printer) __P((void *, char *, ...)); void *arg; { return 0; } ppp-2.4.5/pppd/ecp.h000066400000000000000000000032571130035057700142140ustar00rootroot00000000000000/* * ecp.h - Definitions for PPP Encryption Control Protocol. * * Copyright (c) 2002 Google, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ecp.h,v 1.2 2003/01/10 07:12:36 fcusack Exp $ */ typedef struct ecp_options { bool required; /* Is ECP required? */ unsigned enctype; /* Encryption type */ } ecp_options; extern fsm ecp_fsm[]; extern ecp_options ecp_wantoptions[]; extern ecp_options ecp_gotoptions[]; extern ecp_options ecp_allowoptions[]; extern ecp_options ecp_hisoptions[]; extern struct protent ecp_protent; ppp-2.4.5/pppd/eui64.c000066400000000000000000000037001130035057700143650ustar00rootroot00000000000000/* * eui64.c - EUI64 routines for IPv6CP. * * Copyright (c) 1999 Tommi Komulainen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Tommi Komulainen * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $ */ #define RCSID "$Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $" #include "pppd.h" static const char rcsid[] = RCSID; /* * eui64_ntoa - Make an ascii representation of an interface identifier */ char * eui64_ntoa(e) eui64_t e; { static char buf[32]; snprintf(buf, 32, "%02x%02x:%02x%02x:%02x%02x:%02x%02x", e.e8[0], e.e8[1], e.e8[2], e.e8[3], e.e8[4], e.e8[5], e.e8[6], e.e8[7]); return buf; } ppp-2.4.5/pppd/eui64.h000066400000000000000000000065271130035057700144040ustar00rootroot00000000000000/* * eui64.h - EUI64 routines for IPv6CP. * * Copyright (c) 1999 Tommi Komulainen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Tommi Komulainen * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: eui64.h,v 1.6 2002/12/04 23:03:32 paulus Exp $ */ #ifndef __EUI64_H__ #define __EUI64_H__ #if !defined(INET6) #error "this file should only be included when INET6 is defined" #endif /* not defined(INET6) */ #if defined(SOL2) #include typedef union { uint8_t e8[8]; /* lower 64-bit IPv6 address */ uint32_t e32[2]; /* lower 64-bit IPv6 address */ } eui64_t; /* * Declare the two below, since in.h only defines them when _KERNEL * is declared - which shouldn't be true when dealing with user-land programs */ #define s6_addr8 _S6_un._S6_u8 #define s6_addr32 _S6_un._S6_u32 #else /* else if not defined(SOL2) */ /* * TODO: * * Maybe this should be done by processing struct in6_addr directly... */ typedef union { u_int8_t e8[8]; u_int16_t e16[4]; u_int32_t e32[2]; } eui64_t; #endif /* defined(SOL2) */ #define eui64_iszero(e) (((e).e32[0] | (e).e32[1]) == 0) #define eui64_equals(e, o) (((e).e32[0] == (o).e32[0]) && \ ((e).e32[1] == (o).e32[1])) #define eui64_zero(e) (e).e32[0] = (e).e32[1] = 0; #define eui64_copy(s, d) memcpy(&(d), &(s), sizeof(eui64_t)) #define eui64_magic(e) do { \ (e).e32[0] = magic(); \ (e).e32[1] = magic(); \ (e).e8[0] &= ~2; \ } while (0) #define eui64_magic_nz(x) do { \ eui64_magic(x); \ } while (eui64_iszero(x)) #define eui64_magic_ne(x, y) do { \ eui64_magic(x); \ } while (eui64_equals(x, y)) #define eui64_get(ll, cp) do { \ eui64_copy((*cp), (ll)); \ (cp) += sizeof(eui64_t); \ } while (0) #define eui64_put(ll, cp) do { \ eui64_copy((ll), (*cp)); \ (cp) += sizeof(eui64_t); \ } while (0) #define eui64_set32(e, l) do { \ (e).e32[0] = 0; \ (e).e32[1] = htonl(l); \ } while (0) #define eui64_setlo32(e, l) eui64_set32(e, l) char *eui64_ntoa __P((eui64_t)); /* Returns ascii representation of id */ #endif /* __EUI64_H__ */ ppp-2.4.5/pppd/fsm.c000066400000000000000000000432341130035057700142240ustar00rootroot00000000000000/* * fsm.c - {Link, IP} Control Protocol Finite State Machine. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: fsm.c,v 1.23 2004/11/13 02:28:15 paulus Exp $" /* * TODO: * Randomize fsm id on link/init. * Deal with variable outgoing MTU. */ #include #include #include #include "pppd.h" #include "fsm.h" static const char rcsid[] = RCSID; static void fsm_timeout __P((void *)); static void fsm_rconfreq __P((fsm *, int, u_char *, int)); static void fsm_rconfack __P((fsm *, int, u_char *, int)); static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int)); static void fsm_rtermreq __P((fsm *, int, u_char *, int)); static void fsm_rtermack __P((fsm *)); static void fsm_rcoderej __P((fsm *, u_char *, int)); static void fsm_sconfreq __P((fsm *, int)); #define PROTO_NAME(f) ((f)->callbacks->proto_name) int peer_mru[NUM_PPP]; /* * 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; f->term_reason_len = 0; } /* * 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(("%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, f); /* Cancel timeout */ break; case STOPPING: case REQSENT: case ACKRCVD: case ACKSENT: f->state = STARTING; UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ break; case OPENED: if( f->callbacks->down ) (*f->callbacks->down)(f); f->state = STARTING; break; default: FSMDEBUG(("%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; } } /* * terminate_layer - Start process of shutting down the FSM * * Cancel any timeout running, notify upper layers we're done, and * send a terminate-request message as configured. */ static void terminate_layer(f, nextstate) fsm *f; int nextstate; { if( f->state != OPENED ) UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ else if( f->callbacks->down ) (*f->callbacks->down)(f); /* Inform upper layers we're down */ /* Init restart counter and send Terminate-Request */ f->retransmits = f->maxtermtransmits; fsm_sdata(f, TERMREQ, f->reqid = ++f->id, (u_char *) f->term_reason, f->term_reason_len); if (f->retransmits == 0) { /* * User asked for no terminate requests at all; just close it. * We've already fired off one Terminate-Request just to be nice * to the peer, but we're not going to wait for a reply. */ f->state = nextstate == CLOSING ? CLOSED : STOPPED; if( f->callbacks->finished ) (*f->callbacks->finished)(f); return; } TIMEOUT(fsm_timeout, f, f->timeouttime); --f->retransmits; f->state = nextstate; } /* * fsm_close - Start closing connection. * * Cancel timeouts and either initiate close or possibly go directly to * the CLOSED state. */ void fsm_close(f, reason) fsm *f; char *reason; { f->term_reason = reason; f->term_reason_len = (reason == NULL? 0: strlen(reason)); 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: terminate_layer(f, CLOSING); break; } } /* * fsm_timeout - Timeout expired. */ static void fsm_timeout(arg) void *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, (u_char *) f->term_reason, f->term_reason_len); TIMEOUT(fsm_timeout, f, f->timeouttime); --f->retransmits; } break; case REQSENT: case ACKRCVD: case ACKSENT: if (f->retransmits <= 0) { warn("%s: timeout sending Config-Requests\n", 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(("%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; u_char code, id; int len; /* * Parse header (code, id and length). * If packet too short, drop it. */ inp = inpacket; if (l < HEADERLEN) { FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol)); return; } GETCHAR(code, inp); GETCHAR(id, inp); GETSHORT(len, inp); if (len < HEADERLEN) { FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol)); return; } if (len > l) { FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol)); return; } len -= HEADERLEN; /* subtract header length */ if( f->state == INITIAL || f->state == STARTING ){ FSMDEBUG(("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, inp, len); 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; { int code, reject_if_disagree; 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 */ f->state = REQSENT; 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, 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; { if (id != f->reqid || f->seen_ack) /* Expected id? */ return; /* Nope, toss... */ if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ){ /* Ack is bad - ignore it */ error("Received bad configure-ack: %P", inp, len); return; } f->seen_ack = 1; f->rnakloops = 0; 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 valid Ack? oh well... */ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ fsm_sconfreq(f, 0); f->state = REQSENT; break; case ACKSENT: UNTIMEOUT(fsm_timeout, 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 ret; int treat_as_reject; if (id != f->reqid || f->seen_ack) /* Expected id? */ return; /* Nope, toss... */ if (code == CONFNAK) { ++f->rnakloops; treat_as_reject = (f->rnakloops >= f->maxnakloops); if (f->callbacks->nakci == NULL || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) { error("Received bad configure-nak: %P", inp, len); return; } } else { f->rnakloops = 0; if (f->callbacks->rejci == NULL || !(ret = f->callbacks->rejci(f, inp, len))) { error("Received bad configure-rej: %P", inp, len); return; } } f->seen_ack = 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, f); /* Cancel timeout */ if (ret < 0) f->state = STOPPED; /* kludge for stopping CCP */ else fsm_sconfreq(f, 0); /* Send Configure-Request */ break; case ACKRCVD: /* Got a Nak/reject when we had already had an Ack?? oh well... */ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 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, p, len) fsm *f; int id; u_char *p; int len; { switch (f->state) { case ACKRCVD: case ACKSENT: f->state = REQSENT; /* Start over but keep trying */ break; case OPENED: if (len > 0) { info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p); } else info("%s terminated by peer", PROTO_NAME(f)); f->retransmits = 0; f->state = STOPPING; if (f->callbacks->down) (*f->callbacks->down)(f); /* Inform upper layers */ TIMEOUT(fsm_timeout, f, f->timeouttime); break; } fsm_sdata(f, TERMACK, id, NULL, 0); } /* * fsm_rtermack - Receive Terminate-Ack. */ static void fsm_rtermack(f) fsm *f; { switch (f->state) { case CLOSING: UNTIMEOUT(fsm_timeout, f); f->state = CLOSED; if( f->callbacks->finished ) (*f->callbacks->finished)(f); break; case STOPPING: UNTIMEOUT(fsm_timeout, f); 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); f->state = REQSENT; 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; if (len < HEADERLEN) { FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!")); return; } GETCHAR(code, inp); GETCHAR(id, inp); warn("%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, 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, f); /* Cancel timeout */ /* fall through */ case STOPPED: f->state = STOPPED; if( f->callbacks->finished ) (*f->callbacks->finished)(f); break; case OPENED: terminate_layer(f, STOPPING); break; default: FSMDEBUG(("%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 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; f->rnakloops = 0; } if( !retransmit ){ /* New request - reset retransmission counter, use new ID */ f->retransmits = f->maxconfreqtransmits; f->reqid = ++f->id; } f->seen_ack = 0; /* * Make up the request packet */ outp = outpacket_buf + PPP_HDRLEN + 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, f, f->timeouttime); } /* * 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 + PPP_HDRLEN + HEADERLEN) BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); outlen = datalen + HEADERLEN; MAKEHEADER(outp, f->protocol); PUTCHAR(code, outp); PUTCHAR(id, outp); PUTSHORT(outlen, outp); output(f->unit, outpacket_buf, outlen + PPP_HDRLEN); } ppp-2.4.5/pppd/fsm.h000066400000000000000000000137711130035057700142340ustar00rootroot00000000000000/* * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: fsm.h,v 1.10 2004/11/13 02:28:15 paulus Exp $ */ /* * Packet header = Code, id, length. */ #define HEADERLEN 4 /* * 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 an fsm structure and 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 */ u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ 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 rnakloops; /* Number of naks received */ int maxnakloops; /* Maximum number of nak loops tolerated */ struct fsm_callbacks *callbacks; /* Callback routines */ char *term_reason; /* Reason for closing protocol */ int term_reason_len; /* Length of term_reason */ } fsm; typedef struct fsm_callbacks { void (*resetci) /* Reset our Configuration Information */ __P((fsm *)); int (*cilen) /* Length of our Configuration Information */ __P((fsm *)); void (*addci) /* Add our Configuration Information */ __P((fsm *, u_char *, int *)); int (*ackci) /* ACK our Configuration Information */ __P((fsm *, u_char *, int)); int (*nakci) /* NAK our Configuration Information */ __P((fsm *, u_char *, int, int)); int (*rejci) /* Reject our Configuration Information */ __P((fsm *, u_char *, int)); int (*reqci) /* Request peer's Configuration Information */ __P((fsm *, u_char *, int *, int)); void (*up) /* Called when fsm reaches OPENED state */ __P((fsm *)); void (*down) /* Called when fsm leaves OPENED state */ __P((fsm *)); void (*starting) /* Called when we want the lower layer */ __P((fsm *)); void (*finished) /* Called when we don't want the lower layer */ __P((fsm *)); void (*protreject) /* Called when Protocol-Reject received */ __P((int)); void (*retransmit) /* Retransmission is necessary */ __P((fsm *)); int (*extcode) /* Called when unknown code received */ __P((fsm *, int, int, u_char *, int)); char *proto_name; /* String name for protocol (for messages) */ } fsm_callbacks; /* * 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 5 /* Maximum number of nak loops */ /* * Prototypes */ void fsm_init __P((fsm *)); void fsm_lowerup __P((fsm *)); void fsm_lowerdown __P((fsm *)); void fsm_open __P((fsm *)); void fsm_close __P((fsm *, char *)); void fsm_input __P((fsm *, u_char *, int)); void fsm_protreject __P((fsm *)); void fsm_sdata __P((fsm *, int, int, u_char *, int)); /* * Variables */ extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ ppp-2.4.5/pppd/ipcp.c000066400000000000000000001631071130035057700143740ustar00rootroot00000000000000/* * ipcp.c - PPP IP Control Protocol. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: ipcp.c,v 1.73 2008/05/26 08:33:22 paulus Exp $" /* * TODO: */ #include #include #include #include #include #include #include #include #include #include "pppd.h" #include "fsm.h" #include "ipcp.h" #include "pathnames.h" static const char rcsid[] = RCSID; /* global vars */ ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ u_int32_t netmask = 0; /* IP netmask to set on interface */ bool disable_defaultip = 0; /* Don't use hostname for default IP adrs */ bool noremoteip = 0; /* Let him have no IP address */ /* Hook for a plugin to know when IP protocol has come up */ void (*ip_up_hook) __P((void)) = NULL; /* Hook for a plugin to know when IP protocol has come down */ void (*ip_down_hook) __P((void)) = NULL; /* Hook for a plugin to choose the remote IP address */ void (*ip_choose_hook) __P((u_int32_t *)) = NULL; /* Notifiers for when IPCP goes up and down */ struct notifier *ip_up_notifier = NULL; struct notifier *ip_down_notifier = NULL; /* local vars */ static int default_route_set[NUM_PPP]; /* Have set up a default route */ static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */ static bool usepeerdns; /* Ask peer for DNS addrs */ static int ipcp_is_up; /* have called np_up() */ static int ipcp_is_open; /* haven't called np_finished() */ static bool ask_for_local; /* request our address from peer */ static char vj_value[8]; /* string form of vj option value */ static char netmask_str[20]; /* string form of netmask value */ /* * Callbacks for fsm code. (CI = Configuration Information) */ static void ipcp_resetci __P((fsm *)); /* Reset our CI */ static int ipcp_cilen __P((fsm *)); /* Return length of our CI */ static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */ static int ipcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ static int ipcp_nakci __P((fsm *, u_char *, int, int));/* Peer nak'd our CI */ static int ipcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ static int ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */ static void ipcp_up __P((fsm *)); /* We're UP */ static void ipcp_down __P((fsm *)); /* We're DOWN */ static void ipcp_finished __P((fsm *)); /* Don't need lower layer */ fsm ipcp_fsm[NUM_PPP]; /* 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 */ ipcp_finished, /* 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 */ }; /* * Command-line options. */ static int setvjslots __P((char **)); static int setdnsaddr __P((char **)); static int setwinsaddr __P((char **)); static int setnetmask __P((char **)); int setipaddr __P((char *, char **, int)); static void printipaddr __P((option_t *, void (*)(void *, char *,...),void *)); static option_t ipcp_option_list[] = { { "noip", o_bool, &ipcp_protent.enabled_flag, "Disable IP and IPCP" }, { "-ip", o_bool, &ipcp_protent.enabled_flag, "Disable IP and IPCP", OPT_ALIAS }, { "novj", o_bool, &ipcp_wantoptions[0].neg_vj, "Disable VJ compression", OPT_A2CLR, &ipcp_allowoptions[0].neg_vj }, { "-vj", o_bool, &ipcp_wantoptions[0].neg_vj, "Disable VJ compression", OPT_ALIAS | OPT_A2CLR, &ipcp_allowoptions[0].neg_vj }, { "novjccomp", o_bool, &ipcp_wantoptions[0].cflag, "Disable VJ connection-ID compression", OPT_A2CLR, &ipcp_allowoptions[0].cflag }, { "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag, "Disable VJ connection-ID compression", OPT_ALIAS | OPT_A2CLR, &ipcp_allowoptions[0].cflag }, { "vj-max-slots", o_special, (void *)setvjslots, "Set maximum VJ header slots", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, vj_value }, { "ipcp-accept-local", o_bool, &ipcp_wantoptions[0].accept_local, "Accept peer's address for us", 1 }, { "ipcp-accept-remote", o_bool, &ipcp_wantoptions[0].accept_remote, "Accept peer's address for it", 1 }, { "ipparam", o_string, &ipparam, "Set ip script parameter", OPT_PRIO }, { "noipdefault", o_bool, &disable_defaultip, "Don't use name for default IP adrs", 1 }, { "ms-dns", 1, (void *)setdnsaddr, "DNS address for the peer's use" }, { "ms-wins", 1, (void *)setwinsaddr, "Nameserver for SMB over TCP/IP for peer" }, { "ipcp-restart", o_int, &ipcp_fsm[0].timeouttime, "Set timeout for IPCP", OPT_PRIO }, { "ipcp-max-terminate", o_int, &ipcp_fsm[0].maxtermtransmits, "Set max #xmits for term-reqs", OPT_PRIO }, { "ipcp-max-configure", o_int, &ipcp_fsm[0].maxconfreqtransmits, "Set max #xmits for conf-reqs", OPT_PRIO }, { "ipcp-max-failure", o_int, &ipcp_fsm[0].maxnakloops, "Set max #conf-naks for IPCP", OPT_PRIO }, { "defaultroute", o_bool, &ipcp_wantoptions[0].default_route, "Add default route", OPT_ENABLE|1, &ipcp_allowoptions[0].default_route }, { "nodefaultroute", o_bool, &ipcp_allowoptions[0].default_route, "disable defaultroute option", OPT_A2CLR, &ipcp_wantoptions[0].default_route }, { "-defaultroute", o_bool, &ipcp_allowoptions[0].default_route, "disable defaultroute option", OPT_ALIAS | OPT_A2CLR, &ipcp_wantoptions[0].default_route }, { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp, "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp }, { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp, "disable proxyarp option", OPT_A2CLR, &ipcp_wantoptions[0].proxy_arp }, { "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp, "disable proxyarp option", OPT_ALIAS | OPT_A2CLR, &ipcp_wantoptions[0].proxy_arp }, { "usepeerdns", o_bool, &usepeerdns, "Ask peer for DNS address(es)", 1 }, { "netmask", o_special, (void *)setnetmask, "set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str }, { "ipcp-no-addresses", o_bool, &ipcp_wantoptions[0].old_addrs, "Disable old-style IP-Addresses usage", OPT_A2CLR, &ipcp_allowoptions[0].old_addrs }, { "ipcp-no-address", o_bool, &ipcp_wantoptions[0].neg_addr, "Disable IP-Address usage", OPT_A2CLR, &ipcp_allowoptions[0].neg_addr }, #ifdef __linux__ { "noremoteip", o_bool, &noremoteip, "Allow peer to have no IP address", 1 }, #endif { "nosendip", o_bool, &ipcp_wantoptions[0].neg_addr, "Don't send our IP address to peer", OPT_A2CLR, &ipcp_wantoptions[0].old_addrs}, { "IP addresses", o_wild, (void *) &setipaddr, "set local and remote IP addresses", OPT_NOARG | OPT_A2PRINTER, (void *) &printipaddr }, { NULL } }; /* * Protocol entry points from main code. */ static void ipcp_init __P((int)); static void ipcp_open __P((int)); static void ipcp_close __P((int, char *)); static void ipcp_lowerup __P((int)); static void ipcp_lowerdown __P((int)); static void ipcp_input __P((int, u_char *, int)); static void ipcp_protrej __P((int)); static int ipcp_printpkt __P((u_char *, int, void (*) __P((void *, char *, ...)), void *)); static void ip_check_options __P((void)); static int ip_demand_conf __P((int)); static int ip_active_pkt __P((u_char *, int)); static void create_resolv __P((u_int32_t, u_int32_t)); struct protent ipcp_protent = { PPP_IPCP, ipcp_init, ipcp_input, ipcp_protrej, ipcp_lowerup, ipcp_lowerdown, ipcp_open, ipcp_close, ipcp_printpkt, NULL, 1, "IPCP", "IP", ipcp_option_list, ip_check_options, ip_demand_conf, ip_active_pkt }; static void ipcp_clear_addrs __P((int, u_int32_t, u_int32_t)); static void ipcp_script __P((char *, int)); /* Run an up/down script */ static void ipcp_script_done __P((void *)); /* * 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") /* * This state variable is used to ensure that we don't * run an ipcp-up/down script while one is already running. */ static enum script_state { s_down, s_up, } ipcp_script_state; static pid_t ipcp_script_pid; /* * Make a string representation of a network IP address. */ char * ip_ntoa(ipaddr) u_int32_t ipaddr; { static char b[64]; slprintf(b, sizeof(b), "%I", ipaddr); return b; } /* * Option parsing. */ /* * setvjslots - set maximum number of connection slots for VJ compression */ static int setvjslots(argv) char **argv; { int value; if (!int_option(*argv, &value)) return 0; if (value < 2 || value > 16) { option_error("vj-max-slots value must be between 2 and 16"); return 0; } ipcp_wantoptions [0].maxslotindex = ipcp_allowoptions[0].maxslotindex = value - 1; slprintf(vj_value, sizeof(vj_value), "%d", value); return 1; } /* * setdnsaddr - set the dns address(es) */ static int setdnsaddr(argv) char **argv; { u_int32_t dns; struct hostent *hp; dns = inet_addr(*argv); if (dns == (u_int32_t) -1) { if ((hp = gethostbyname(*argv)) == NULL) { option_error("invalid address parameter '%s' for ms-dns option", *argv); return 0; } dns = *(u_int32_t *)hp->h_addr; } /* We take the last 2 values given, the 2nd-last as the primary and the last as the secondary. If only one is given it becomes both primary and secondary. */ if (ipcp_allowoptions[0].dnsaddr[1] == 0) ipcp_allowoptions[0].dnsaddr[0] = dns; else ipcp_allowoptions[0].dnsaddr[0] = ipcp_allowoptions[0].dnsaddr[1]; /* always set the secondary address value. */ ipcp_allowoptions[0].dnsaddr[1] = dns; return (1); } /* * setwinsaddr - set the wins address(es) * This is primrarly used with the Samba package under UNIX or for pointing * the caller to the existing WINS server on a Windows NT platform. */ static int setwinsaddr(argv) char **argv; { u_int32_t wins; struct hostent *hp; wins = inet_addr(*argv); if (wins == (u_int32_t) -1) { if ((hp = gethostbyname(*argv)) == NULL) { option_error("invalid address parameter '%s' for ms-wins option", *argv); return 0; } wins = *(u_int32_t *)hp->h_addr; } /* We take the last 2 values given, the 2nd-last as the primary and the last as the secondary. If only one is given it becomes both primary and secondary. */ if (ipcp_allowoptions[0].winsaddr[1] == 0) ipcp_allowoptions[0].winsaddr[0] = wins; else ipcp_allowoptions[0].winsaddr[0] = ipcp_allowoptions[0].winsaddr[1]; /* always set the secondary address value. */ ipcp_allowoptions[0].winsaddr[1] = wins; return (1); } /* * setipaddr - Set the IP address * If doit is 0, the call is to check whether this option is * potentially an IP address specification. * Not static so that plugins can call it to set the addresses */ int setipaddr(arg, argv, doit) char *arg; char **argv; int doit; { struct hostent *hp; char *colon; u_int32_t local, remote; ipcp_options *wo = &ipcp_wantoptions[0]; static int prio_local = 0, prio_remote = 0; /* * IP address pair separated by ":". */ if ((colon = strchr(arg, ':')) == NULL) return 0; if (!doit) return 1; /* * If colon first character, then no local addr. */ if (colon != arg && option_priority >= prio_local) { *colon = '\0'; if ((local = inet_addr(arg)) == (u_int32_t) -1) { if ((hp = gethostbyname(arg)) == NULL) { option_error("unknown host: %s", arg); return 0; } local = *(u_int32_t *)hp->h_addr; } if (bad_ip_adrs(local)) { option_error("bad local IP address %s", ip_ntoa(local)); return 0; } if (local != 0) wo->ouraddr = local; *colon = ':'; prio_local = option_priority; } /* * If colon last character, then no remote addr. */ if (*++colon != '\0' && option_priority >= prio_remote) { if ((remote = inet_addr(colon)) == (u_int32_t) -1) { if ((hp = gethostbyname(colon)) == NULL) { option_error("unknown host: %s", colon); return 0; } remote = *(u_int32_t *)hp->h_addr; if (remote_name[0] == 0) strlcpy(remote_name, colon, sizeof(remote_name)); } if (bad_ip_adrs(remote)) { option_error("bad remote IP address %s", ip_ntoa(remote)); return 0; } if (remote != 0) wo->hisaddr = remote; prio_remote = option_priority; } return 1; } static void printipaddr(opt, printer, arg) option_t *opt; void (*printer) __P((void *, char *, ...)); void *arg; { ipcp_options *wo = &ipcp_wantoptions[0]; if (wo->ouraddr != 0) printer(arg, "%I", wo->ouraddr); printer(arg, ":"); if (wo->hisaddr != 0) printer(arg, "%I", wo->hisaddr); } /* * setnetmask - set the netmask to be used on the interface. */ static int setnetmask(argv) char **argv; { u_int32_t mask; int n; char *p; /* * Unfortunately, if we use inet_addr, we can't tell whether * a result of all 1s is an error or a valid 255.255.255.255. */ p = *argv; n = parse_dotted_ip(p, &mask); mask = htonl(mask); if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) { option_error("invalid netmask value '%s'", *argv); return 0; } netmask = mask; slprintf(netmask_str, sizeof(netmask_str), "%I", mask); return (1); } int parse_dotted_ip(p, vp) char *p; u_int32_t *vp; { int n; u_int32_t v, b; char *endp, *p0 = p; v = 0; for (n = 3;; --n) { b = strtoul(p, &endp, 0); if (endp == p) return 0; if (b > 255) { if (n < 3) return 0; /* accept e.g. 0xffffff00 */ *vp = b; return endp - p0; } v |= b << (n * 8); p = endp; if (n == 0) break; if (*p != '.') return 0; ++p; } *vp = v; return p - p0; } /* * ipcp_init - Initialize IPCP. */ static 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 = PPP_IPCP; f->callbacks = &ipcp_callbacks; fsm_init(&ipcp_fsm[unit]); /* * Some 3G modems use repeated IPCP NAKs as a way of stalling * until they can contact a server on the network, so we increase * the default number of NAKs we accept before we start treating * them as rejects. */ f->maxnakloops = 100; memset(wo, 0, sizeof(*wo)); memset(ao, 0, sizeof(*ao)); wo->neg_addr = wo->old_addrs = 1; wo->neg_vj = 1; 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 = ao->old_addrs = 1; ao->neg_vj = 1; ao->maxslotindex = MAX_STATES - 1; ao->cflag = 1; /* * XXX These control whether the user may use the proxyarp * and defaultroute options. */ ao->proxy_arp = 1; ao->default_route = 1; } /* * ipcp_open - IPCP is allowed to come up. */ static void ipcp_open(unit) int unit; { fsm_open(&ipcp_fsm[unit]); ipcp_is_open = 1; } /* * ipcp_close - Take IPCP down. */ static void ipcp_close(unit, reason) int unit; char *reason; { fsm_close(&ipcp_fsm[unit], reason); } /* * ipcp_lowerup - The lower layer is up. */ static void ipcp_lowerup(unit) int unit; { fsm_lowerup(&ipcp_fsm[unit]); } /* * ipcp_lowerdown - The lower layer is down. */ static void ipcp_lowerdown(unit) int unit; { fsm_lowerdown(&ipcp_fsm[unit]); } /* * ipcp_input - Input IPCP packet. */ static 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. */ static void ipcp_protrej(unit) int unit; { fsm_lowerdown(&ipcp_fsm[unit]); } /* * ipcp_resetci - Reset our CI. * Called by fsm_sconfreq, Send Configure Request. */ static void ipcp_resetci(f) fsm *f; { ipcp_options *wo = &ipcp_wantoptions[f->unit]; ipcp_options *go = &ipcp_gotoptions[f->unit]; ipcp_options *ao = &ipcp_allowoptions[f->unit]; wo->req_addr = (wo->neg_addr || wo->old_addrs) && (ao->neg_addr || ao->old_addrs); if (wo->ouraddr == 0) wo->accept_local = 1; if (wo->hisaddr == 0) wo->accept_remote = 1; wo->req_dns1 = usepeerdns; /* Request DNS addresses from the peer */ wo->req_dns2 = usepeerdns; *go = *wo; if (!ask_for_local) go->ouraddr = 0; if (ip_choose_hook) { ip_choose_hook(&wo->hisaddr); if (wo->hisaddr) { wo->accept_remote = 0; } } BZERO(&ipcp_hisoptions[f->unit], sizeof(ipcp_options)); } /* * ipcp_cilen - Return length of our CI. * Called by fsm_sconfreq, Send Configure Request. */ static int ipcp_cilen(f) fsm *f; { ipcp_options *go = &ipcp_gotoptions[f->unit]; ipcp_options *wo = &ipcp_wantoptions[f->unit]; ipcp_options *ho = &ipcp_hisoptions[f->unit]; #define LENCIADDRS(neg) (neg ? CILEN_ADDRS : 0) #define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) #define LENCIADDR(neg) (neg ? CILEN_ADDR : 0) #define LENCIDNS(neg) LENCIADDR(neg) #define LENCIWINS(neg) LENCIADDR(neg) /* * First see if we want to change our options to the old * forms because we have received old forms from the peer. */ if (go->neg_addr && go->old_addrs && !ho->neg_addr && ho->old_addrs) go->neg_addr = 0; if (wo->neg_vj && !go->neg_vj && !go->old_vj) { /* try an older style of VJ negotiation */ /* 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; } } return (LENCIADDRS(!go->neg_addr && go->old_addrs) + LENCIVJ(go->neg_vj, go->old_vj) + LENCIADDR(go->neg_addr) + LENCIDNS(go->req_dns1) + LENCIDNS(go->req_dns2) + LENCIWINS(go->winsaddr[0]) + LENCIWINS(go->winsaddr[1])) ; } /* * ipcp_addci - Add our desired CIs to a packet. * Called by fsm_sconfreq, Send Configure Request. */ static void ipcp_addci(f, ucp, lenp) fsm *f; u_char *ucp; int *lenp; { ipcp_options *go = &ipcp_gotoptions[f->unit]; int len = *lenp; #define ADDCIADDRS(opt, neg, val1, val2) \ if (neg) { \ if (len >= CILEN_ADDRS) { \ u_int32_t l; \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_ADDRS, ucp); \ l = ntohl(val1); \ PUTLONG(l, ucp); \ l = ntohl(val2); \ PUTLONG(l, ucp); \ len -= CILEN_ADDRS; \ } else \ go->old_addrs = 0; \ } #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, val) \ if (neg) { \ if (len >= CILEN_ADDR) { \ u_int32_t l; \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_ADDR, ucp); \ l = ntohl(val); \ PUTLONG(l, ucp); \ len -= CILEN_ADDR; \ } else \ neg = 0; \ } #define ADDCIDNS(opt, neg, addr) \ if (neg) { \ if (len >= CILEN_ADDR) { \ u_int32_t l; \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_ADDR, ucp); \ l = ntohl(addr); \ PUTLONG(l, ucp); \ len -= CILEN_ADDR; \ } else \ neg = 0; \ } #define ADDCIWINS(opt, addr) \ if (addr) { \ if (len >= CILEN_ADDR) { \ u_int32_t l; \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_ADDR, ucp); \ l = ntohl(addr); \ PUTLONG(l, ucp); \ len -= CILEN_ADDR; \ } else \ addr = 0; \ } ADDCIADDRS(CI_ADDRS, !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); ADDCIADDR(CI_ADDR, go->neg_addr, go->ouraddr); ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); ADDCIWINS(CI_MS_WINS1, go->winsaddr[0]); ADDCIWINS(CI_MS_WINS2, go->winsaddr[1]); *lenp -= len; } /* * ipcp_ackci - Ack our CIs. * Called by fsm_rconfack, Receive Configure ACK. * * 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_int32_t 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 ACKCIADDRS(opt, neg, val1, val2) \ if (neg) { \ u_int32_t l; \ if ((len -= CILEN_ADDRS) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_ADDRS || \ citype != opt) \ goto bad; \ GETLONG(l, p); \ cilong = htonl(l); \ if (val1 != cilong) \ goto bad; \ GETLONG(l, p); \ cilong = htonl(l); \ if (val2 != cilong) \ goto 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, val) \ if (neg) { \ u_int32_t l; \ if ((len -= CILEN_ADDR) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_ADDR || \ citype != opt) \ goto bad; \ GETLONG(l, p); \ cilong = htonl(l); \ if (val != cilong) \ goto bad; \ } #define ACKCIDNS(opt, neg, addr) \ if (neg) { \ u_int32_t l; \ if ((len -= CILEN_ADDR) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_ADDR || citype != opt) \ goto bad; \ GETLONG(l, p); \ cilong = htonl(l); \ if (addr != cilong) \ goto bad; \ } ACKCIADDRS(CI_ADDRS, !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); ACKCIADDR(CI_ADDR, go->neg_addr, go->ouraddr); ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); /* * If there are any remaining CIs, then this packet is bad. */ if (len != 0) goto bad; return (1); bad: IPCPDEBUG(("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. * Calback from fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. * * Returns: * 0 - Nak was bad. * 1 - Nak was good. */ static int ipcp_nakci(f, p, len, treat_as_reject) fsm *f; u_char *p; int len; int treat_as_reject; { ipcp_options *go = &ipcp_gotoptions[f->unit]; u_char cimaxslotindex, cicflag; u_char citype, cilen, *next; u_short cishort; u_int32_t ciaddr1, ciaddr2, l, cidnsaddr; 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 NAKCIADDRS(opt, neg, code) \ if ((neg) && \ (cilen = p[1]) == CILEN_ADDRS && \ len >= cilen && \ p[0] == opt) { \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ ciaddr1 = htonl(l); \ GETLONG(l, p); \ ciaddr2 = htonl(l); \ no.old_addrs = 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 \ } #define NAKCIADDR(opt, neg, code) \ if (go->neg && \ (cilen = p[1]) == CILEN_ADDR && \ len >= cilen && \ p[0] == opt) { \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ ciaddr1 = htonl(l); \ no.neg = 1; \ code \ } #define NAKCIDNS(opt, neg, code) \ if (go->neg && \ ((cilen = p[1]) == CILEN_ADDR) && \ len >= cilen && \ p[0] == opt) { \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ cidnsaddr = htonl(l); \ 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. */ NAKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, if (treat_as_reject) { try.old_addrs = 0; } else { if (go->accept_local && ciaddr1) { /* take his idea of our address */ try.ouraddr = ciaddr1; } if (go->accept_remote && ciaddr2) { /* take his idea of his address */ try.hisaddr = 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 (treat_as_reject) { try.neg_vj = 0; } else 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; } } ); NAKCIADDR(CI_ADDR, neg_addr, if (treat_as_reject) { try.neg_addr = 0; try.old_addrs = 0; } else if (go->accept_local && ciaddr1) { /* take his idea of our address */ try.ouraddr = ciaddr1; } ); NAKCIDNS(CI_MS_DNS1, req_dns1, if (treat_as_reject) { try.req_dns1 = 0; } else { try.dnsaddr[0] = cidnsaddr; } ); NAKCIDNS(CI_MS_DNS2, req_dns2, if (treat_as_reject) { try.req_dns2 = 0; } else { try.dnsaddr[1] = cidnsaddr; } ); /* * 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. * If they want us to ask for ms-dns, we do that, since some * peers get huffy if we don't. */ while (len >= CILEN_VOID) { GETCHAR(citype, p); GETCHAR(cilen, p); if ( cilen < CILEN_VOID || (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 = 0; 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.old_addrs = 0; GETLONG(l, p); ciaddr1 = htonl(l); if (ciaddr1 && go->accept_local) try.ouraddr = ciaddr1; if (try.ouraddr != 0) try.neg_addr = 1; no.neg_addr = 1; break; case CI_MS_DNS1: if (go->req_dns1 || no.req_dns1 || cilen != CILEN_ADDR) goto bad; GETLONG(l, p); try.dnsaddr[0] = htonl(l); try.req_dns1 = 1; no.req_dns1 = 1; break; case CI_MS_DNS2: if (go->req_dns2 || no.req_dns2 || cilen != CILEN_ADDR) goto bad; GETLONG(l, p); try.dnsaddr[1] = htonl(l); try.req_dns2 = 1; no.req_dns2 = 1; break; case CI_MS_WINS1: case CI_MS_WINS2: if (cilen != CILEN_ADDR) goto bad; GETLONG(l, p); ciaddr1 = htonl(l); if (ciaddr1) try.winsaddr[citype == CI_MS_WINS2] = ciaddr1; break; } p = next; } /* * OK, the Nak is good. Now we can update state. * If there are any remaining options, we ignore them. */ if (f->state != OPENED) *go = try; return 1; bad: IPCPDEBUG(("ipcp_nakci: received bad Nak!")); return 0; } /* * ipcp_rejci - Reject some of our CIs. * Callback from fsm_rconfnakrej. */ 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_int32_t 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 REJCIADDRS(opt, neg, val1, val2) \ if ((neg) && \ (cilen = p[1]) == CILEN_ADDRS && \ len >= cilen && \ p[0] == opt) { \ u_int32_t l; \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ cilong = htonl(l); \ /* Check rejected value. */ \ if (cilong != val1) \ goto bad; \ GETLONG(l, p); \ cilong = htonl(l); \ /* Check rejected value. */ \ if (cilong != val2) \ goto bad; \ try.old_addrs = 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; \ } #define REJCIADDR(opt, neg, val) \ if (go->neg && \ (cilen = p[1]) == CILEN_ADDR && \ len >= cilen && \ p[0] == opt) { \ u_int32_t l; \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ cilong = htonl(l); \ /* Check rejected value. */ \ if (cilong != val) \ goto bad; \ try.neg = 0; \ } #define REJCIDNS(opt, neg, dnsaddr) \ if (go->neg && \ ((cilen = p[1]) == CILEN_ADDR) && \ len >= cilen && \ p[0] == opt) { \ u_int32_t l; \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ cilong = htonl(l); \ /* Check rejected value. */ \ if (cilong != dnsaddr) \ goto bad; \ try.neg = 0; \ } #define REJCIWINS(opt, addr) \ if (addr && \ ((cilen = p[1]) == CILEN_ADDR) && \ len >= cilen && \ p[0] == opt) { \ u_int32_t l; \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ cilong = htonl(l); \ /* Check rejected value. */ \ if (cilong != addr) \ goto bad; \ try.winsaddr[opt == CI_MS_WINS2] = 0; \ } REJCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr, go->hisaddr); REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, go->maxslotindex, go->cflag); REJCIADDR(CI_ADDR, neg_addr, go->ouraddr); REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); REJCIWINS(CI_MS_WINS1, go->winsaddr[0]); REJCIWINS(CI_MS_WINS2, go->winsaddr[1]); /* * 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(("ipcp_rejci: received bad Reject!")); return 0; } /* * ipcp_reqci - Check the peer's requested CIs and send appropriate response. * Callback from fsm_rconfreq, Receive Configure Request * * 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]; u_char *cip, *next; /* Pointer to current and next CIs */ u_short cilen, citype; /* Parsed len, type */ u_short cishort; /* Parsed short value */ u_int32_t 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; int d; /* * 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(("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: if (!ao->old_addrs || ho->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); if (ciaddr1 != wo->hisaddr && (ciaddr1 == 0 || !wo->accept_remote)) { orc = CONFNAK; if (!reject_if_disagree) { DECPTR(sizeof(u_int32_t), p); tl = ntohl(wo->hisaddr); PUTLONG(tl, p); } } else if (ciaddr1 == 0 && wo->hisaddr == 0) { /* * If neither we nor he knows his address, reject the option. */ orc = CONFREJ; wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ break; } /* * 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); if (ciaddr2 != wo->ouraddr) { if (ciaddr2 == 0 || !wo->accept_local) { orc = CONFNAK; if (!reject_if_disagree) { DECPTR(sizeof(u_int32_t), p); tl = ntohl(wo->ouraddr); PUTLONG(tl, p); } } else { wo->ouraddr = ciaddr2; /* accept peer's idea */ } } ho->old_addrs = 1; ho->hisaddr = ciaddr1; ho->ouraddr = ciaddr2; break; case CI_ADDR: if (!ao->neg_addr || ho->old_addrs || 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); if (ciaddr1 != wo->hisaddr && (ciaddr1 == 0 || !wo->accept_remote)) { orc = CONFNAK; if (!reject_if_disagree) { DECPTR(sizeof(u_int32_t), p); tl = ntohl(wo->hisaddr); PUTLONG(tl, p); } } else if (ciaddr1 == 0 && wo->hisaddr == 0) { /* * Don't ACK an address of 0.0.0.0 - reject it instead. */ orc = CONFREJ; wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ break; } ho->neg_addr = 1; ho->hisaddr = ciaddr1; break; case CI_MS_DNS1: case CI_MS_DNS2: /* Microsoft primary or secondary DNS request */ d = citype == CI_MS_DNS2; /* If we do not have a DNS address then we cannot send it */ if (ao->dnsaddr[d] == 0 || cilen != CILEN_ADDR) { /* Check CI length */ orc = CONFREJ; /* Reject CI */ break; } GETLONG(tl, p); if (htonl(tl) != ao->dnsaddr[d]) { DECPTR(sizeof(u_int32_t), p); tl = ntohl(ao->dnsaddr[d]); PUTLONG(tl, p); orc = CONFNAK; } break; case CI_MS_WINS1: case CI_MS_WINS2: /* Microsoft primary or secondary WINS request */ d = citype == CI_MS_WINS2; /* If we do not have a DNS address then we cannot send it */ if (ao->winsaddr[d] == 0 || cilen != CILEN_ADDR) { /* Check CI length */ orc = CONFREJ; /* Reject CI */ break; } GETLONG(tl, p); if (htonl(tl) != ao->winsaddr[d]) { DECPTR(sizeof(u_int32_t), p); tl = ntohl(ao->winsaddr[d]); PUTLONG(tl, p); orc = CONFNAK; } break; case CI_COMPRESSTYPE: if (!ao->neg_vj || (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { orc = CONFREJ; break; } GETSHORT(cishort, p); 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 = cflag; } else { ho->old_vj = 1; ho->maxslotindex = MAX_STATES - 1; ho->cflag = 1; } break; default: orc = CONFREJ; break; } endswitch: 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 && !ho->old_addrs && wo->req_addr && !reject_if_disagree && !noremoteip) { 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(("ipcp: returning Configure-%s", CODENAME(rc))); return (rc); /* Return final code */ } /* * ip_check_options - check that any IP-related options are OK, * and assign appropriate defaults. */ static void ip_check_options() { struct hostent *hp; u_int32_t local; ipcp_options *wo = &ipcp_wantoptions[0]; /* * Default our local IP address based on our hostname. * If local IP address already given, don't bother. */ if (wo->ouraddr == 0 && !disable_defaultip) { /* * 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) { local = *(u_int32_t *)hp->h_addr; if (local != 0 && !bad_ip_adrs(local)) wo->ouraddr = local; } } ask_for_local = wo->ouraddr != 0 || !disable_defaultip; } /* * ip_demand_conf - configure the interface as though * IPCP were up, for use with dial-on-demand. */ static int ip_demand_conf(u) int u; { ipcp_options *wo = &ipcp_wantoptions[u]; if (wo->hisaddr == 0 && !noremoteip) { /* make up an arbitrary address for the peer */ wo->hisaddr = htonl(0x0a707070 + ifunit); wo->accept_remote = 1; } if (wo->ouraddr == 0) { /* make up an arbitrary address for us */ wo->ouraddr = htonl(0x0a404040 + ifunit); wo->accept_local = 1; ask_for_local = 0; /* don't tell the peer this address */ } if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr))) return 0; ipcp_script(_PATH_IPPREUP, 1); if (!sifup(u)) return 0; if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE)) return 0; if (wo->default_route) if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr)) default_route_set[u] = 1; if (wo->proxy_arp) if (sifproxyarp(u, wo->hisaddr)) proxy_arp_set[u] = 1; notice("local IP address %I", wo->ouraddr); if (wo->hisaddr) notice("remote IP address %I", wo->hisaddr); return 1; } /* * ipcp_up - IPCP has come UP. * * Configure the IP network interface appropriately and bring it up. */ static void ipcp_up(f) fsm *f; { u_int32_t mask; ipcp_options *ho = &ipcp_hisoptions[f->unit]; ipcp_options *go = &ipcp_gotoptions[f->unit]; ipcp_options *wo = &ipcp_wantoptions[f->unit]; IPCPDEBUG(("ipcp: up")); /* * We must have a non-zero IP address for both ends of the link. */ if (!ho->neg_addr && !ho->old_addrs) ho->hisaddr = wo->hisaddr; if (!(go->neg_addr || go->old_addrs) && (wo->neg_addr || wo->old_addrs) && wo->ouraddr != 0) { error("Peer refused to agree to our IP address"); ipcp_close(f->unit, "Refused our IP address"); return; } if (go->ouraddr == 0) { error("Could not determine local IP address"); ipcp_close(f->unit, "Could not determine local IP address"); return; } if (ho->hisaddr == 0 && !noremoteip) { ho->hisaddr = htonl(0x0a404040 + ifunit); warn("Could not determine remote IP address: defaulting to %I", ho->hisaddr); } script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0); if (ho->hisaddr != 0) script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1); if (!go->req_dns1) go->dnsaddr[0] = 0; if (!go->req_dns2) go->dnsaddr[1] = 0; if (go->dnsaddr[0]) script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0); if (go->dnsaddr[1]) script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0); if (usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { script_setenv("USEPEERDNS", "1", 0); create_resolv(go->dnsaddr[0], go->dnsaddr[1]); } /* * Check that the peer is allowed to use the IP address it wants. */ if (ho->hisaddr != 0 && !auth_ip_addr(f->unit, ho->hisaddr)) { error("Peer is not authorized to use remote address %I", ho->hisaddr); ipcp_close(f->unit, "Unauthorized remote IP address"); return; } /* set tcp compression */ sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); /* * If we are doing dial-on-demand, the interface is already * configured, so we put out any saved-up packets, then set the * interface to pass IP packets. */ if (demand) { if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) { ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr); if (go->ouraddr != wo->ouraddr) { warn("Local IP address changed to %I", go->ouraddr); script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0); wo->ouraddr = go->ouraddr; } else script_unsetenv("OLDIPLOCAL"); if (ho->hisaddr != wo->hisaddr && wo->hisaddr != 0) { warn("Remote IP address changed to %I", ho->hisaddr); script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0); wo->hisaddr = ho->hisaddr; } else script_unsetenv("OLDIPREMOTE"); /* Set the interface to the new addresses */ mask = GetMask(go->ouraddr); if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { if (debug) warn("Interface configuration failed"); ipcp_close(f->unit, "Interface configuration failed"); return; } /* assign a default route through the interface if required */ if (ipcp_wantoptions[f->unit].default_route) if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) default_route_set[f->unit] = 1; /* Make a proxy ARP entry if requested. */ if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp) if (sifproxyarp(f->unit, ho->hisaddr)) proxy_arp_set[f->unit] = 1; } demand_rexmit(PPP_IP); sifnpmode(f->unit, PPP_IP, NPMODE_PASS); } else { /* * Set IP addresses and (if specified) netmask. */ mask = GetMask(go->ouraddr); #if !(defined(SVR4) && (defined(SNI) || defined(__USLC__))) if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { if (debug) warn("Interface configuration failed"); ipcp_close(f->unit, "Interface configuration failed"); return; } #endif /* run the pre-up script, if any, and wait for it to finish */ ipcp_script(_PATH_IPPREUP, 1); /* bring the interface up for IP */ if (!sifup(f->unit)) { if (debug) warn("Interface failed to come up"); ipcp_close(f->unit, "Interface configuration failed"); return; } #if (defined(SVR4) && (defined(SNI) || defined(__USLC__))) if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { if (debug) warn("Interface configuration failed"); ipcp_close(f->unit, "Interface configuration failed"); return; } #endif sifnpmode(f->unit, PPP_IP, NPMODE_PASS); /* assign a default route through the interface if required */ if (ipcp_wantoptions[f->unit].default_route) if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) default_route_set[f->unit] = 1; /* Make a proxy ARP entry if requested. */ if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp) if (sifproxyarp(f->unit, ho->hisaddr)) proxy_arp_set[f->unit] = 1; ipcp_wantoptions[0].ouraddr = go->ouraddr; notice("local IP address %I", go->ouraddr); if (ho->hisaddr != 0) notice("remote IP address %I", ho->hisaddr); if (go->dnsaddr[0]) notice("primary DNS address %I", go->dnsaddr[0]); if (go->dnsaddr[1]) notice("secondary DNS address %I", go->dnsaddr[1]); } reset_link_stats(f->unit); np_up(f->unit, PPP_IP); ipcp_is_up = 1; notify(ip_up_notifier, 0); if (ip_up_hook) ip_up_hook(); /* * Execute the ip-up script, like this: * /etc/ppp/ip-up interface tty speed local-IP remote-IP */ if (ipcp_script_state == s_down && ipcp_script_pid == 0) { ipcp_script_state = s_up; ipcp_script(_PATH_IPUP, 0); } } /* * 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; { IPCPDEBUG(("ipcp: down")); /* XXX a bit IPv4-centric here, we only need to get the stats * before the interface is marked down. */ /* XXX more correct: we must get the stats before running the notifiers, * at least for the radius plugin */ update_link_stats(f->unit); notify(ip_down_notifier, 0); if (ip_down_hook) ip_down_hook(); if (ipcp_is_up) { ipcp_is_up = 0; np_down(f->unit, PPP_IP); } sifvjcomp(f->unit, 0, 0, 0); print_link_stats(); /* _after_ running the notifiers and ip_down_hook(), * because print_link_stats() sets link_stats_valid * to 0 (zero) */ /* * If we are doing dial-on-demand, set the interface * to queue up outgoing packets (for now). */ if (demand) { sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE); } else { sifnpmode(f->unit, PPP_IP, NPMODE_DROP); sifdown(f->unit); ipcp_clear_addrs(f->unit, ipcp_gotoptions[f->unit].ouraddr, ipcp_hisoptions[f->unit].hisaddr); } /* Execute the ip-down script */ if (ipcp_script_state == s_up && ipcp_script_pid == 0) { ipcp_script_state = s_down; ipcp_script(_PATH_IPDOWN, 0); } } /* * ipcp_clear_addrs() - clear the interface addresses, routes, * proxy arp entries, etc. */ static void ipcp_clear_addrs(unit, ouraddr, hisaddr) int unit; u_int32_t ouraddr; /* local address */ u_int32_t hisaddr; /* remote address */ { if (proxy_arp_set[unit]) { cifproxyarp(unit, hisaddr); proxy_arp_set[unit] = 0; } if (default_route_set[unit]) { cifdefaultroute(unit, ouraddr, hisaddr); default_route_set[unit] = 0; } cifaddr(unit, ouraddr, hisaddr); } /* * ipcp_finished - possibly shut down the lower layers. */ static void ipcp_finished(f) fsm *f; { if (ipcp_is_open) { ipcp_is_open = 0; np_finished(f->unit, PPP_IP); } } /* * ipcp_script_done - called when the ip-up or ip-down script * has finished. */ static void ipcp_script_done(arg) void *arg; { ipcp_script_pid = 0; switch (ipcp_script_state) { case s_up: if (ipcp_fsm[0].state != OPENED) { ipcp_script_state = s_down; ipcp_script(_PATH_IPDOWN, 0); } break; case s_down: if (ipcp_fsm[0].state == OPENED) { ipcp_script_state = s_up; ipcp_script(_PATH_IPUP, 0); } break; } } /* * ipcp_script - Execute a script with arguments * interface-name tty-name speed local-IP remote-IP. */ static void ipcp_script(script, wait) char *script; int wait; { char strspeed[32], strlocal[32], strremote[32]; char *argv[8]; slprintf(strspeed, sizeof(strspeed), "%d", baud_rate); slprintf(strlocal, sizeof(strlocal), "%I", ipcp_gotoptions[0].ouraddr); slprintf(strremote, sizeof(strremote), "%I", ipcp_hisoptions[0].hisaddr); argv[0] = script; argv[1] = ifname; argv[2] = devnam; argv[3] = strspeed; argv[4] = strlocal; argv[5] = strremote; argv[6] = ipparam; argv[7] = NULL; if (wait) run_program(script, argv, 0, NULL, NULL, 1); else ipcp_script_pid = run_program(script, argv, 0, ipcp_script_done, NULL, 0); } /* * create_resolv - create the replacement resolv.conf file */ static void create_resolv(peerdns1, peerdns2) u_int32_t peerdns1, peerdns2; { FILE *f; f = fopen(_PATH_RESOLV, "w"); if (f == NULL) { error("Failed to create %s: %m", _PATH_RESOLV); return; } if (peerdns1) fprintf(f, "nameserver %s\n", ip_ntoa(peerdns1)); if (peerdns2) fprintf(f, "nameserver %s\n", ip_ntoa(peerdns2)); if (ferror(f)) error("Write failed to %s: %m", _PATH_RESOLV); fclose(f); } /* * ipcp_printpkt - print the contents of an IPCP packet. */ static char *ipcp_codenames[] = { "ConfReq", "ConfAck", "ConfNak", "ConfRej", "TermReq", "TermAck", "CodeRej" }; static int ipcp_printpkt(p, plen, printer, arg) u_char *p; int plen; void (*printer) __P((void *, char *, ...)); void *arg; { int code, id, len, olen; u_char *pstart, *optend; u_short cishort; u_int32_t 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 %I", htonl(cilong)); GETLONG(cilong, p); printer(arg, " %I", 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 %I", htonl(cilong)); } break; case CI_MS_DNS1: case CI_MS_DNS2: p += 2; GETLONG(cilong, p); printer(arg, "ms-dns%d %I", (code == CI_MS_DNS1? 1: 2), htonl(cilong)); break; case CI_MS_WINS1: case CI_MS_WINS2: p += 2; GETLONG(cilong, p); printer(arg, "ms-wins %I", htonl(cilong)); break; } while (p < optend) { GETCHAR(code, p); printer(arg, " %.2x", code); } printer(arg, ">"); } break; case TERMACK: case TERMREQ: if (len > 0 && *p >= ' ' && *p < 0x7f) { printer(arg, " "); print_string((char *)p, len, printer, arg); p += len; len = 0; } break; } /* print the rest of the bytes in the packet */ for (; len > 0; --len) { GETCHAR(code, p); printer(arg, " %.2x", code); } return p - pstart; } /* * ip_active_pkt - see if this IP packet is worth bringing the link up for. * We don't bring the link up for IP fragments or for TCP FIN packets * with no data. */ #define IP_HDRLEN 20 /* bytes */ #define IP_OFFMASK 0x1fff #ifndef IPPROTO_TCP #define IPPROTO_TCP 6 #endif #define TCP_HDRLEN 20 #define TH_FIN 0x01 /* * We use these macros because the IP header may be at an odd address, * and some compilers might use word loads to get th_off or ip_hl. */ #define net_short(x) (((x)[0] << 8) + (x)[1]) #define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) #define get_ipoff(x) net_short((unsigned char *)(x) + 6) #define get_ipproto(x) (((unsigned char *)(x))[9]) #define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) #define get_tcpflags(x) (((unsigned char *)(x))[13]) static int ip_active_pkt(pkt, len) u_char *pkt; int len; { u_char *tcp; int hlen; len -= PPP_HDRLEN; pkt += PPP_HDRLEN; if (len < IP_HDRLEN) return 0; if ((get_ipoff(pkt) & IP_OFFMASK) != 0) return 0; if (get_ipproto(pkt) != IPPROTO_TCP) return 1; hlen = get_iphl(pkt) * 4; if (len < hlen + TCP_HDRLEN) return 0; tcp = pkt + hlen; if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) return 0; return 1; } ppp-2.4.5/pppd/ipcp.h000066400000000000000000000076741130035057700144070ustar00rootroot00000000000000/* * ipcp.h - IP Control Protocol definitions. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ipcp.h,v 1.14 2002/12/04 23:03:32 paulus Exp $ */ /* * Options. */ #define CI_ADDRS 1 /* IP Addresses */ #define CI_COMPRESSTYPE 2 /* Compression Type */ #define CI_ADDR 3 #define CI_MS_DNS1 129 /* Primary DNS value */ #define CI_MS_WINS1 130 /* Primary WINS value */ #define CI_MS_DNS2 131 /* Secondary DNS value */ #define CI_MS_WINS2 132 /* Secondary WINS value */ #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 { bool neg_addr; /* Negotiate IP Address? */ bool old_addrs; /* Use old (IP-Addresses) option? */ bool req_addr; /* Ask peer to send IP address? */ bool default_route; /* Assign default route through interface? */ bool proxy_arp; /* Make proxy ARP entry for peer? */ bool neg_vj; /* Van Jacobson Compression? */ bool old_vj; /* use old (short) form of VJ option? */ bool accept_local; /* accept peer's value for ouraddr */ bool accept_remote; /* accept peer's value for hisaddr */ bool req_dns1; /* Ask peer to send primary DNS address? */ bool req_dns2; /* Ask peer to send secondary DNS address? */ int vj_protocol; /* protocol value to use in VJ option */ int maxslotindex; /* values for RFC1332 VJ compression neg. */ bool cflag; u_int32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ u_int32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ u_int32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ } 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[]; char *ip_ntoa __P((u_int32_t)); extern struct protent ipcp_protent; ppp-2.4.5/pppd/ipv6cp.c000066400000000000000000001201121130035057700146350ustar00rootroot00000000000000/* * ipv6cp.c - PPP IPV6 Control Protocol. * * Copyright (c) 1999 Tommi Komulainen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Tommi Komulainen * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* Original version, based on RFC2023 : Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt, Alain.Durand@imag.fr, IMAG, Jean-Luc.Richier@imag.fr, IMAG-LSR. Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE, Alain.Durand@imag.fr, IMAG, Jean-Luc.Richier@imag.fr, IMAG-LSR. Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt Économique ayant pour membres BULL S.A. et l'INRIA). Ce logiciel informatique est disponible aux conditions usuelles dans la recherche, c'est-à-dire qu'il peut être utilisé, copié, modifié, distribué à l'unique condition que ce texte soit conservé afin que l'origine de ce logiciel soit reconnue. Le nom de l'Institut National de Recherche en Informatique et en Automatique (INRIA), de l'IMAG, ou d'une personne morale ou physique ayant participé à l'élaboration de ce logiciel ne peut être utilisé sans son accord préalable explicite. Ce logiciel est fourni tel quel sans aucune garantie, support ou responsabilité d'aucune sorte. Ce logiciel est dérivé de sources d'origine "University of California at Berkeley" et "Digital Equipment Corporation" couvertes par des copyrights. L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG) est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR). This work has been done in the context of GIE DYADE (joint R & D venture between BULL S.A. and INRIA). This software is available with usual "research" terms with the aim of retain credits of the software. Permission to use, copy, modify and distribute this software for any purpose and without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies, and the name of INRIA, IMAG, or any contributor not be used in advertising or publicity pertaining to this material without the prior explicit permission. The software is provided "as is" without any warranties, support or liabilities of any kind. This software is derived from source code from "University of California at Berkeley" and "Digital Equipment Corporation" protected by copyrights. Grenoble's Institute of Computer Science and Applied Mathematics (IMAG) is a federation of seven research units funded by the CNRS, National Polytechnic Institute of Grenoble and University Joseph Fourier. The research unit in Software, Systems, Networks (LSR) is member of IMAG. */ /* * Derived from : * * * ipcp.c - PPP IP Control Protocol. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $ */ #define RCSID "$Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $" /* * TODO: * * Proxy Neighbour Discovery. * * Better defines for selecting the ordering of * interface up / set address. (currently checks for __linux__, * since SVR4 && (SNI || __USLC__) didn't work properly) */ #include #include #include #include #include #include #include #include #include #include "pppd.h" #include "fsm.h" #include "ipcp.h" #include "ipv6cp.h" #include "magic.h" #include "pathnames.h" static const char rcsid[] = RCSID; /* global vars */ ipv6cp_options ipv6cp_wantoptions[NUM_PPP]; /* Options that we want to request */ ipv6cp_options ipv6cp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ ipv6cp_options ipv6cp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ ipv6cp_options ipv6cp_hisoptions[NUM_PPP]; /* Options that we ack'd */ int no_ifaceid_neg = 0; /* local vars */ static int ipv6cp_is_up; /* * Callbacks for fsm code. (CI = Configuration Information) */ static void ipv6cp_resetci __P((fsm *)); /* Reset our CI */ static int ipv6cp_cilen __P((fsm *)); /* Return length of our CI */ static void ipv6cp_addci __P((fsm *, u_char *, int *)); /* Add our CI */ static int ipv6cp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ static int ipv6cp_nakci __P((fsm *, u_char *, int, int));/* Peer nak'd our CI */ static int ipv6cp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ static int ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */ static void ipv6cp_up __P((fsm *)); /* We're UP */ static void ipv6cp_down __P((fsm *)); /* We're DOWN */ static void ipv6cp_finished __P((fsm *)); /* Don't need lower layer */ fsm ipv6cp_fsm[NUM_PPP]; /* IPV6CP fsm structure */ static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */ ipv6cp_resetci, /* Reset our Configuration Information */ ipv6cp_cilen, /* Length of our Configuration Information */ ipv6cp_addci, /* Add our Configuration Information */ ipv6cp_ackci, /* ACK our Configuration Information */ ipv6cp_nakci, /* NAK our Configuration Information */ ipv6cp_rejci, /* Reject our Configuration Information */ ipv6cp_reqci, /* Request peer's Configuration Information */ ipv6cp_up, /* Called when fsm reaches OPENED state */ ipv6cp_down, /* Called when fsm leaves OPENED state */ NULL, /* Called when we want the lower layer up */ ipv6cp_finished, /* 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 */ "IPV6CP" /* String name of protocol */ }; /* * Command-line options. */ static int setifaceid __P((char **arg)); static void printifaceid __P((option_t *, void (*)(void *, char *, ...), void *)); static option_t ipv6cp_option_list[] = { { "ipv6", o_special, (void *)setifaceid, "Set interface identifiers for IPV6", OPT_A2PRINTER, (void *)printifaceid }, { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag, "Enable IPv6 and IPv6CP", OPT_PRIO | 1 }, { "noipv6", o_bool, &ipv6cp_protent.enabled_flag, "Disable IPv6 and IPv6CP", OPT_PRIOSUB }, { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag, "Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS }, { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local, "Accept peer's interface identifier for us", 1 }, { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip, "Use (default) IPv4 address as interface identifier", 1 }, #if defined(SOL2) || defined(__linux__) { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent, "Use uniquely-available persistent value for link local address", 1 }, #endif /* defined(SOL2) */ { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime, "Set timeout for IPv6CP", OPT_PRIO }, { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits, "Set max #xmits for term-reqs", OPT_PRIO }, { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits, "Set max #xmits for conf-reqs", OPT_PRIO }, { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops, "Set max #conf-naks for IPv6CP", OPT_PRIO }, { NULL } }; /* * Protocol entry points from main code. */ static void ipv6cp_init __P((int)); static void ipv6cp_open __P((int)); static void ipv6cp_close __P((int, char *)); static void ipv6cp_lowerup __P((int)); static void ipv6cp_lowerdown __P((int)); static void ipv6cp_input __P((int, u_char *, int)); static void ipv6cp_protrej __P((int)); static int ipv6cp_printpkt __P((u_char *, int, void (*) __P((void *, char *, ...)), void *)); static void ipv6_check_options __P((void)); static int ipv6_demand_conf __P((int)); static int ipv6_active_pkt __P((u_char *, int)); struct protent ipv6cp_protent = { PPP_IPV6CP, ipv6cp_init, ipv6cp_input, ipv6cp_protrej, ipv6cp_lowerup, ipv6cp_lowerdown, ipv6cp_open, ipv6cp_close, ipv6cp_printpkt, NULL, 0, "IPV6CP", "IPV6", ipv6cp_option_list, ipv6_check_options, ipv6_demand_conf, ipv6_active_pkt }; static void ipv6cp_clear_addrs __P((int, eui64_t, eui64_t)); static void ipv6cp_script __P((char *)); static void ipv6cp_script_done __P((void *)); /* * Lengths of configuration options. */ #define CILEN_VOID 2 #define CILEN_COMPRESS 4 /* length for RFC2023 compress opt. */ #define CILEN_IFACEID 10 /* RFC2472, interface identifier */ #define CODENAME(x) ((x) == CONFACK ? "ACK" : \ (x) == CONFNAK ? "NAK" : "REJ") /* * This state variable is used to ensure that we don't * run an ipcp-up/down script while one is already running. */ static enum script_state { s_down, s_up, } ipv6cp_script_state; static pid_t ipv6cp_script_pid; /* * setifaceid - set the interface identifiers manually */ static int setifaceid(argv) char **argv; { char *comma, *arg, c; ipv6cp_options *wo = &ipv6cp_wantoptions[0]; struct in6_addr addr; static int prio_local, prio_remote; #define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \ (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) ) arg = *argv; if ((comma = strchr(arg, ',')) == NULL) comma = arg + strlen(arg); /* * If comma first character, then no local identifier */ if (comma != arg) { c = *comma; *comma = '\0'; if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) { option_error("Illegal interface identifier (local): %s", arg); return 0; } if (option_priority >= prio_local) { eui64_copy(addr.s6_addr32[2], wo->ourid); wo->opt_local = 1; prio_local = option_priority; } *comma = c; } /* * If comma last character, the no remote identifier */ if (*comma != 0 && *++comma != '\0') { if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) { option_error("Illegal interface identifier (remote): %s", comma); return 0; } if (option_priority >= prio_remote) { eui64_copy(addr.s6_addr32[2], wo->hisid); wo->opt_remote = 1; prio_remote = option_priority; } } if (override_value("+ipv6", option_priority, option_source)) ipv6cp_protent.enabled_flag = 1; return 1; } char *llv6_ntoa(eui64_t ifaceid); static void printifaceid(opt, printer, arg) option_t *opt; void (*printer) __P((void *, char *, ...)); void *arg; { ipv6cp_options *wo = &ipv6cp_wantoptions[0]; if (wo->opt_local) printer(arg, "%s", llv6_ntoa(wo->ourid)); printer(arg, ","); if (wo->opt_remote) printer(arg, "%s", llv6_ntoa(wo->hisid)); } /* * Make a string representation of a network address. */ char * llv6_ntoa(ifaceid) eui64_t ifaceid; { static char b[64]; sprintf(b, "fe80::%s", eui64_ntoa(ifaceid)); return b; } /* * ipv6cp_init - Initialize IPV6CP. */ static void ipv6cp_init(unit) int unit; { fsm *f = &ipv6cp_fsm[unit]; ipv6cp_options *wo = &ipv6cp_wantoptions[unit]; ipv6cp_options *ao = &ipv6cp_allowoptions[unit]; f->unit = unit; f->protocol = PPP_IPV6CP; f->callbacks = &ipv6cp_callbacks; fsm_init(&ipv6cp_fsm[unit]); memset(wo, 0, sizeof(*wo)); memset(ao, 0, sizeof(*ao)); wo->accept_local = 1; wo->neg_ifaceid = 1; ao->neg_ifaceid = 1; #ifdef IPV6CP_COMP wo->neg_vj = 1; ao->neg_vj = 1; wo->vj_protocol = IPV6CP_COMP; #endif } /* * ipv6cp_open - IPV6CP is allowed to come up. */ static void ipv6cp_open(unit) int unit; { fsm_open(&ipv6cp_fsm[unit]); } /* * ipv6cp_close - Take IPV6CP down. */ static void ipv6cp_close(unit, reason) int unit; char *reason; { fsm_close(&ipv6cp_fsm[unit], reason); } /* * ipv6cp_lowerup - The lower layer is up. */ static void ipv6cp_lowerup(unit) int unit; { fsm_lowerup(&ipv6cp_fsm[unit]); } /* * ipv6cp_lowerdown - The lower layer is down. */ static void ipv6cp_lowerdown(unit) int unit; { fsm_lowerdown(&ipv6cp_fsm[unit]); } /* * ipv6cp_input - Input IPV6CP packet. */ static void ipv6cp_input(unit, p, len) int unit; u_char *p; int len; { fsm_input(&ipv6cp_fsm[unit], p, len); } /* * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP. * * Pretend the lower layer went down, so we shut up. */ static void ipv6cp_protrej(unit) int unit; { fsm_lowerdown(&ipv6cp_fsm[unit]); } /* * ipv6cp_resetci - Reset our CI. */ static void ipv6cp_resetci(f) fsm *f; { ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit]; ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid; if (!wo->opt_local) { eui64_magic_nz(wo->ourid); } *go = *wo; eui64_zero(go->hisid); /* last proposed interface identifier */ } /* * ipv6cp_cilen - Return length of our CI. */ static int ipv6cp_cilen(f) fsm *f; { ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; #define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0) #define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0) return (LENCIIFACEID(go->neg_ifaceid) + LENCIVJ(go->neg_vj)); } /* * ipv6cp_addci - Add our desired CIs to a packet. */ static void ipv6cp_addci(f, ucp, lenp) fsm *f; u_char *ucp; int *lenp; { ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; int len = *lenp; #define ADDCIVJ(opt, neg, val) \ if (neg) { \ int vjlen = CILEN_COMPRESS; \ if (len >= vjlen) { \ PUTCHAR(opt, ucp); \ PUTCHAR(vjlen, ucp); \ PUTSHORT(val, ucp); \ len -= vjlen; \ } else \ neg = 0; \ } #define ADDCIIFACEID(opt, neg, val1) \ if (neg) { \ int idlen = CILEN_IFACEID; \ if (len >= idlen) { \ PUTCHAR(opt, ucp); \ PUTCHAR(idlen, ucp); \ eui64_put(val1, ucp); \ len -= idlen; \ } else \ neg = 0; \ } ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid); ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol); *lenp -= len; } /* * ipv6cp_ackci - Ack our CIs. * * Returns: * 0 - Ack was bad. * 1 - Ack was good. */ static int ipv6cp_ackci(f, p, len) fsm *f; u_char *p; int len; { ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; u_short cilen, citype, cishort; eui64_t ifaceid; /* * 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) \ if (neg) { \ int vjlen = CILEN_COMPRESS; \ 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; \ } #define ACKCIIFACEID(opt, neg, val1) \ if (neg) { \ int idlen = CILEN_IFACEID; \ if ((len -= idlen) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != idlen || \ citype != opt) \ goto bad; \ eui64_get(ifaceid, p); \ if (! eui64_equals(val1, ifaceid)) \ goto bad; \ } ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid); ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol); /* * If there are any remaining CIs, then this packet is bad. */ if (len != 0) goto bad; return (1); bad: IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!")); return (0); } /* * ipv6cp_nakci - Peer has sent a NAK for some of our CIs. * This should not modify any state if the Nak is bad * or if IPV6CP is in the OPENED state. * * Returns: * 0 - Nak was bad. * 1 - Nak was good. */ static int ipv6cp_nakci(f, p, len, treat_as_reject) fsm *f; u_char *p; int len; int treat_as_reject; { ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; u_char citype, cilen, *next; u_short cishort; eui64_t ifaceid; ipv6cp_options no; /* options we've seen Naks for */ ipv6cp_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 NAKCIIFACEID(opt, neg, code) \ if (go->neg && \ len >= (cilen = CILEN_IFACEID) && \ p[1] == cilen && \ p[0] == opt) { \ len -= cilen; \ INCPTR(2, p); \ eui64_get(ifaceid, p); \ no.neg = 1; \ code \ } #define NAKCIVJ(opt, neg, code) \ if (go->neg && \ ((cilen = p[1]) == CILEN_COMPRESS) && \ 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} interface identifier, if different * from our idea, only if the accept_{local,remote} flag is set. */ NAKCIIFACEID(CI_IFACEID, neg_ifaceid, if (treat_as_reject) { try.neg_ifaceid = 0; } else if (go->accept_local) { while (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->hisid)) /* bad luck */ eui64_magic(ifaceid); try.ourid = ifaceid; IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid))); } ); #ifdef IPV6CP_COMP NAKCIVJ(CI_COMPRESSTYPE, neg_vj, { if (cishort == IPV6CP_COMP && !treat_as_reject) { try.vj_protocol = cishort; } else { try.neg_vj = 0; } } ); #else NAKCIVJ(CI_COMPRESSTYPE, neg_vj, { try.neg_vj = 0; } ); #endif /* * 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 interface identifier, we comply. * If they want us to ask for compression, we refuse. */ while (len >= CILEN_VOID) { GETCHAR(citype, p); GETCHAR(cilen, p); if ( cilen < CILEN_VOID || (len -= cilen) < 0 ) goto bad; next = p + cilen - 2; switch (citype) { case CI_COMPRESSTYPE: if (go->neg_vj || no.neg_vj || (cilen != CILEN_COMPRESS)) goto bad; no.neg_vj = 1; break; case CI_IFACEID: if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID) goto bad; try.neg_ifaceid = 1; eui64_get(ifaceid, p); if (go->accept_local) { while (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->hisid)) /* bad luck */ eui64_magic(ifaceid); try.ourid = ifaceid; } no.neg_ifaceid = 1; break; } 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: IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!")); return 0; } /* * ipv6cp_rejci - Reject some of our CIs. */ static int ipv6cp_rejci(f, p, len) fsm *f; u_char *p; int len; { ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; u_char cilen; u_short cishort; eui64_t ifaceid; ipv6cp_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 REJCIIFACEID(opt, neg, val1) \ if (go->neg && \ len >= (cilen = CILEN_IFACEID) && \ p[1] == cilen && \ p[0] == opt) { \ len -= cilen; \ INCPTR(2, p); \ eui64_get(ifaceid, p); \ /* Check rejected value. */ \ if (! eui64_equals(ifaceid, val1)) \ goto bad; \ try.neg = 0; \ } #define REJCIVJ(opt, neg, val) \ if (go->neg && \ p[1] == CILEN_COMPRESS && \ len >= p[1] && \ p[0] == opt) { \ len -= p[1]; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ /* Check rejected value. */ \ if (cishort != val) \ goto bad; \ try.neg = 0; \ } REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid); REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol); /* * 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: IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!")); return 0; } /* * ipv6cp_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 ipv6cp_reqci(f, inp, len, reject_if_disagree) fsm *f; u_char *inp; /* Requested CIs */ int *len; /* Length of requested CIs */ int reject_if_disagree; { ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit]; ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit]; ipv6cp_options *ao = &ipv6cp_allowoptions[f->unit]; ipv6cp_options *go = &ipv6cp_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 */ eui64_t ifaceid; /* Parsed interface identifier */ 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 */ /* * 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? */ IPV6CPDEBUG(("ipv6cp_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_IFACEID: IPV6CPDEBUG(("ipv6cp: received interface identifier ")); if (!ao->neg_ifaceid || cilen != CILEN_IFACEID) { /* Check CI length */ orc = CONFREJ; /* Reject CI */ break; } /* * If he has no interface identifier, or if we both have same * identifier then NAK it with new idea. * In particular, if we don't know his identifier, but he does, * then accept it. */ eui64_get(ifaceid, p); IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid))); if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) { orc = CONFREJ; /* Reject CI */ break; } if (!eui64_iszero(wo->hisid) && !eui64_equals(ifaceid, wo->hisid) && eui64_iszero(go->hisid)) { orc = CONFNAK; ifaceid = wo->hisid; go->hisid = ifaceid; DECPTR(sizeof(ifaceid), p); eui64_put(ifaceid, p); } else if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) { orc = CONFNAK; if (eui64_iszero(go->hisid)) /* first time, try option */ ifaceid = wo->hisid; while (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) /* bad luck */ eui64_magic(ifaceid); go->hisid = ifaceid; DECPTR(sizeof(ifaceid), p); eui64_put(ifaceid, p); } ho->neg_ifaceid = 1; ho->hisid = ifaceid; break; case CI_COMPRESSTYPE: IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE ")); if (!ao->neg_vj || (cilen != CILEN_COMPRESS)) { orc = CONFREJ; break; } GETSHORT(cishort, p); IPV6CPDEBUG(("(%d)", cishort)); #ifdef IPV6CP_COMP if (!(cishort == IPV6CP_COMP)) { orc = CONFREJ; break; } ho->neg_vj = 1; ho->vj_protocol = cishort; break; #else orc = CONFREJ; break; #endif default: orc = CONFREJ; break; } endswitch: IPV6CPDEBUG((" (%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 identifier and they didn't send their identifier, then we * send a NAK with a CI_IFACEID option appended. We assume the * input buffer is long enough that we can append the extra * option safely. */ if (rc != CONFREJ && !ho->neg_ifaceid && wo->req_ifaceid && !reject_if_disagree) { if (rc == CONFACK) { rc = CONFNAK; ucp = inp; /* reset pointer */ wo->req_ifaceid = 0; /* don't ask again */ } PUTCHAR(CI_IFACEID, ucp); PUTCHAR(CILEN_IFACEID, ucp); eui64_put(wo->hisid, ucp); } *len = ucp - inp; /* Compute output length */ IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc))); return (rc); /* Return final code */ } /* * ipv6_check_options - check that any IP-related options are OK, * and assign appropriate defaults. */ static void ipv6_check_options() { ipv6cp_options *wo = &ipv6cp_wantoptions[0]; if (!ipv6cp_protent.enabled_flag) return; #if defined(SOL2) || defined(__linux__) /* * Persistent link-local id is only used when user has not explicitly * configure/hard-code the id */ if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) { /* * On systems where there are no Ethernet interfaces used, there * may be other ways to obtain a persistent id. Right now, it * will fall back to using magic [see eui64_magic] below when * an EUI-48 from MAC address can't be obtained. Other possibilities * include obtaining EEPROM serial numbers, or some other unique * yet persistent number. On Sparc platforms, this is possible, * but too bad there's no standards yet for x86 machines. */ if (ether_to_eui64(&wo->ourid)) { wo->opt_local = 1; } } #endif if (!wo->opt_local) { /* init interface identifier */ if (wo->use_ip && eui64_iszero(wo->ourid)) { eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr)); if (!eui64_iszero(wo->ourid)) wo->opt_local = 1; } while (eui64_iszero(wo->ourid)) eui64_magic(wo->ourid); } if (!wo->opt_remote) { if (wo->use_ip && eui64_iszero(wo->hisid)) { eui64_setlo32(wo->hisid, ntohl(ipcp_wantoptions[0].hisaddr)); if (!eui64_iszero(wo->hisid)) wo->opt_remote = 1; } } if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) { option_error("local/remote LL address required for demand-dialling\n"); exit(1); } } /* * ipv6_demand_conf - configure the interface as though * IPV6CP were up, for use with dial-on-demand. */ static int ipv6_demand_conf(u) int u; { ipv6cp_options *wo = &ipv6cp_wantoptions[u]; #if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__))) #if defined(SOL2) if (!sif6up(u)) return 0; #else if (!sifup(u)) return 0; #endif /* defined(SOL2) */ #endif if (!sif6addr(u, wo->ourid, wo->hisid)) return 0; #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__))) if (!sifup(u)) return 0; #endif if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE)) return 0; notice("ipv6_demand_conf"); notice("local LL address %s", llv6_ntoa(wo->ourid)); notice("remote LL address %s", llv6_ntoa(wo->hisid)); return 1; } /* * ipv6cp_up - IPV6CP has come UP. * * Configure the IPv6 network interface appropriately and bring it up. */ static void ipv6cp_up(f) fsm *f; { ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit]; ipv6cp_options *go = &ipv6cp_gotoptions[f->unit]; ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit]; IPV6CPDEBUG(("ipv6cp: up")); /* * We must have a non-zero LL address for both ends of the link. */ if (!ho->neg_ifaceid) ho->hisid = wo->hisid; if(!no_ifaceid_neg) { if (eui64_iszero(ho->hisid)) { error("Could not determine remote LL address"); ipv6cp_close(f->unit, "Could not determine remote LL address"); return; } if (eui64_iszero(go->ourid)) { error("Could not determine local LL address"); ipv6cp_close(f->unit, "Could not determine local LL address"); return; } if (eui64_equals(go->ourid, ho->hisid)) { error("local and remote LL addresses are equal"); ipv6cp_close(f->unit, "local and remote LL addresses are equal"); return; } } script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0); script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0); #ifdef IPV6CP_COMP /* set tcp compression */ sif6comp(f->unit, ho->neg_vj); #endif /* * If we are doing dial-on-demand, the interface is already * configured, so we put out any saved-up packets, then set the * interface to pass IPv6 packets. */ if (demand) { if (! eui64_equals(go->ourid, wo->ourid) || ! eui64_equals(ho->hisid, wo->hisid)) { if (! eui64_equals(go->ourid, wo->ourid)) warn("Local LL address changed to %s", llv6_ntoa(go->ourid)); if (! eui64_equals(ho->hisid, wo->hisid)) warn("Remote LL address changed to %s", llv6_ntoa(ho->hisid)); ipv6cp_clear_addrs(f->unit, go->ourid, ho->hisid); /* Set the interface to the new addresses */ if (!sif6addr(f->unit, go->ourid, ho->hisid)) { if (debug) warn("sif6addr failed"); ipv6cp_close(f->unit, "Interface configuration failed"); return; } } demand_rexmit(PPP_IPV6); sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS); } else { /* * Set LL addresses */ #if !defined(__linux__) && !defined(SOL2) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__))) if (!sif6addr(f->unit, go->ourid, ho->hisid)) { if (debug) warn("sif6addr failed"); ipv6cp_close(f->unit, "Interface configuration failed"); return; } #endif /* bring the interface up for IPv6 */ #if defined(SOL2) if (!sif6up(f->unit)) { if (debug) warn("sifup failed (IPV6)"); ipv6cp_close(f->unit, "Interface configuration failed"); return; } #else if (!sifup(f->unit)) { if (debug) warn("sifup failed (IPV6)"); ipv6cp_close(f->unit, "Interface configuration failed"); return; } #endif /* defined(SOL2) */ #if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__))) if (!sif6addr(f->unit, go->ourid, ho->hisid)) { if (debug) warn("sif6addr failed"); ipv6cp_close(f->unit, "Interface configuration failed"); return; } #endif sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS); notice("local LL address %s", llv6_ntoa(go->ourid)); notice("remote LL address %s", llv6_ntoa(ho->hisid)); } np_up(f->unit, PPP_IPV6); ipv6cp_is_up = 1; /* * Execute the ipv6-up script, like this: * /etc/ppp/ipv6-up interface tty speed local-LL remote-LL */ if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) { ipv6cp_script_state = s_up; ipv6cp_script(_PATH_IPV6UP); } } /* * ipv6cp_down - IPV6CP has gone DOWN. * * Take the IPv6 network interface down, clear its addresses * and delete routes through it. */ static void ipv6cp_down(f) fsm *f; { IPV6CPDEBUG(("ipv6cp: down")); update_link_stats(f->unit); if (ipv6cp_is_up) { ipv6cp_is_up = 0; np_down(f->unit, PPP_IPV6); } #ifdef IPV6CP_COMP sif6comp(f->unit, 0); #endif /* * If we are doing dial-on-demand, set the interface * to queue up outgoing packets (for now). */ if (demand) { sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE); } else { sifnpmode(f->unit, PPP_IPV6, NPMODE_DROP); #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC))) #if defined(SOL2) sif6down(f->unit); #else sifdown(f->unit); #endif /* defined(SOL2) */ #endif ipv6cp_clear_addrs(f->unit, ipv6cp_gotoptions[f->unit].ourid, ipv6cp_hisoptions[f->unit].hisid); #if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC))) sifdown(f->unit); #endif } /* Execute the ipv6-down script */ if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) { ipv6cp_script_state = s_down; ipv6cp_script(_PATH_IPV6DOWN); } } /* * ipv6cp_clear_addrs() - clear the interface addresses, routes, * proxy neighbour discovery entries, etc. */ static void ipv6cp_clear_addrs(unit, ourid, hisid) int unit; eui64_t ourid; eui64_t hisid; { cif6addr(unit, ourid, hisid); } /* * ipv6cp_finished - possibly shut down the lower layers. */ static void ipv6cp_finished(f) fsm *f; { np_finished(f->unit, PPP_IPV6); } /* * ipv6cp_script_done - called when the ipv6-up or ipv6-down script * has finished. */ static void ipv6cp_script_done(arg) void *arg; { ipv6cp_script_pid = 0; switch (ipv6cp_script_state) { case s_up: if (ipv6cp_fsm[0].state != OPENED) { ipv6cp_script_state = s_down; ipv6cp_script(_PATH_IPV6DOWN); } break; case s_down: if (ipv6cp_fsm[0].state == OPENED) { ipv6cp_script_state = s_up; ipv6cp_script(_PATH_IPV6UP); } break; } } /* * ipv6cp_script - Execute a script with arguments * interface-name tty-name speed local-LL remote-LL. */ static void ipv6cp_script(script) char *script; { char strspeed[32], strlocal[32], strremote[32]; char *argv[8]; sprintf(strspeed, "%d", baud_rate); strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid)); strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid)); argv[0] = script; argv[1] = ifname; argv[2] = devnam; argv[3] = strspeed; argv[4] = strlocal; argv[5] = strremote; argv[6] = ipparam; argv[7] = NULL; ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, NULL, 0); } /* * ipv6cp_printpkt - print the contents of an IPV6CP packet. */ static char *ipv6cp_codenames[] = { "ConfReq", "ConfAck", "ConfNak", "ConfRej", "TermReq", "TermAck", "CodeRej" }; static int ipv6cp_printpkt(p, plen, printer, arg) u_char *p; int plen; void (*printer) __P((void *, char *, ...)); void *arg; { int code, id, len, olen; u_char *pstart, *optend; u_short cishort; eui64_t ifaceid; 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(ipv6cp_codenames) / sizeof(char *)) printer(arg, " %s", ipv6cp_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_COMPRESSTYPE: if (olen >= CILEN_COMPRESS) { p += 2; GETSHORT(cishort, p); printer(arg, "compress "); printer(arg, "0x%x", cishort); } break; case CI_IFACEID: if (olen == CILEN_IFACEID) { p += 2; eui64_get(ifaceid, p); printer(arg, "addr %s", llv6_ntoa(ifaceid)); } break; } while (p < optend) { GETCHAR(code, p); printer(arg, " %.2x", code); } printer(arg, ">"); } break; case TERMACK: case TERMREQ: if (len > 0 && *p >= ' ' && *p < 0x7f) { printer(arg, " "); print_string((char *)p, len, printer, arg); p += len; len = 0; } break; } /* print the rest of the bytes in the packet */ for (; len > 0; --len) { GETCHAR(code, p); printer(arg, " %.2x", code); } return p - pstart; } /* * ipv6_active_pkt - see if this IP packet is worth bringing the link up for. * We don't bring the link up for IP fragments or for TCP FIN packets * with no data. */ #define IP6_HDRLEN 40 /* bytes */ #define IP6_NHDR_FRAG 44 /* fragment IPv6 header */ #define TCP_HDRLEN 20 #define TH_FIN 0x01 /* * We use these macros because the IP header may be at an odd address, * and some compilers might use word loads to get th_off or ip_hl. */ #define get_ip6nh(x) (((unsigned char *)(x))[6]) #define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) #define get_tcpflags(x) (((unsigned char *)(x))[13]) static int ipv6_active_pkt(pkt, len) u_char *pkt; int len; { u_char *tcp; len -= PPP_HDRLEN; pkt += PPP_HDRLEN; if (len < IP6_HDRLEN) return 0; if (get_ip6nh(pkt) == IP6_NHDR_FRAG) return 0; if (get_ip6nh(pkt) != IPPROTO_TCP) return 1; if (len < IP6_HDRLEN + TCP_HDRLEN) return 0; tcp = pkt + IP6_HDRLEN; if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4) return 0; return 1; } ppp-2.4.5/pppd/ipv6cp.h000066400000000000000000000161531130035057700146530ustar00rootroot00000000000000/* * ipv6cp.h - PPP IPV6 Control Protocol. * * Copyright (c) 1999 Tommi Komulainen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Tommi Komulainen * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* Original version, based on RFC2023 : Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt, Alain.Durand@imag.fr, IMAG, Jean-Luc.Richier@imag.fr, IMAG-LSR. Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE, Alain.Durand@imag.fr, IMAG, Jean-Luc.Richier@imag.fr, IMAG-LSR. Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt Économique ayant pour membres BULL S.A. et l'INRIA). Ce logiciel informatique est disponible aux conditions usuelles dans la recherche, c'est-à-dire qu'il peut être utilisé, copié, modifié, distribué à l'unique condition que ce texte soit conservé afin que l'origine de ce logiciel soit reconnue. Le nom de l'Institut National de Recherche en Informatique et en Automatique (INRIA), de l'IMAG, ou d'une personne morale ou physique ayant participé à l'élaboration de ce logiciel ne peut être utilisé sans son accord préalable explicite. Ce logiciel est fourni tel quel sans aucune garantie, support ou responsabilité d'aucune sorte. Ce logiciel est dérivé de sources d'origine "University of California at Berkeley" et "Digital Equipment Corporation" couvertes par des copyrights. L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG) est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR). This work has been done in the context of GIE DYADE (joint R & D venture between BULL S.A. and INRIA). This software is available with usual "research" terms with the aim of retain credits of the software. Permission to use, copy, modify and distribute this software for any purpose and without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies, and the name of INRIA, IMAG, or any contributor not be used in advertising or publicity pertaining to this material without the prior explicit permission. The software is provided "as is" without any warranties, support or liabilities of any kind. This software is derived from source code from "University of California at Berkeley" and "Digital Equipment Corporation" protected by copyrights. Grenoble's Institute of Computer Science and Applied Mathematics (IMAG) is a federation of seven research units funded by the CNRS, National Polytechnic Institute of Grenoble and University Joseph Fourier. The research unit in Software, Systems, Networks (LSR) is member of IMAG. */ /* * Derived from : * * * ipcp.h - IP Control Protocol definitions. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ipv6cp.h,v 1.7 2002/12/04 23:03:32 paulus Exp $ */ /* * Options. */ #define CI_IFACEID 1 /* Interface Identifier */ #define CI_COMPRESSTYPE 2 /* Compression Type */ /* No compression types yet defined. *#define IPV6CP_COMP 0x004f */ typedef struct ipv6cp_options { int neg_ifaceid; /* Negotiate interface identifier? */ int req_ifaceid; /* Ask peer to send interface identifier? */ int accept_local; /* accept peer's value for iface id? */ int opt_local; /* ourtoken set by option */ int opt_remote; /* histoken set by option */ int use_ip; /* use IP as interface identifier */ #if defined(SOL2) || defined(__linux__) int use_persistent; /* use uniquely persistent value for address */ #endif /* defined(SOL2) */ int neg_vj; /* Van Jacobson Compression? */ u_short vj_protocol; /* protocol value to use in VJ option */ eui64_t ourid, hisid; /* Interface identifiers */ } ipv6cp_options; extern fsm ipv6cp_fsm[]; extern ipv6cp_options ipv6cp_wantoptions[]; extern ipv6cp_options ipv6cp_gotoptions[]; extern ipv6cp_options ipv6cp_allowoptions[]; extern ipv6cp_options ipv6cp_hisoptions[]; extern struct protent ipv6cp_protent; ppp-2.4.5/pppd/ipxcp.c000066400000000000000000001134121130035057700145560ustar00rootroot00000000000000/* * ipxcp.c - PPP IPX Control Protocol. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef IPX_CHANGE #define RCSID "$Id: ipxcp.c,v 1.24 2005/08/25 23:59:34 paulus Exp $" /* * TODO: */ #include #include #include #include #include #include #include #include "pppd.h" #include "fsm.h" #include "ipxcp.h" #include "pathnames.h" #include "magic.h" static const char rcsid[] = RCSID; /* global vars */ ipxcp_options ipxcp_wantoptions[NUM_PPP]; /* Options that we want to request */ ipxcp_options ipxcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ ipxcp_options ipxcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ ipxcp_options ipxcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ #define wo (&ipxcp_wantoptions[0]) #define ao (&ipxcp_allowoptions[0]) #define go (&ipxcp_gotoptions[0]) #define ho (&ipxcp_hisoptions[0]) /* * Callbacks for fsm code. (CI = Configuration Information) */ static void ipxcp_resetci __P((fsm *)); /* Reset our CI */ static int ipxcp_cilen __P((fsm *)); /* Return length of our CI */ static void ipxcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */ static int ipxcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ static int ipxcp_nakci __P((fsm *, u_char *, int, int));/* Peer nak'd our CI */ static int ipxcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ static int ipxcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */ static void ipxcp_up __P((fsm *)); /* We're UP */ static void ipxcp_down __P((fsm *)); /* We're DOWN */ static void ipxcp_finished __P((fsm *)); /* Don't need lower layer */ static void ipxcp_script __P((fsm *, char *)); /* Run an up/down script */ fsm ipxcp_fsm[NUM_PPP]; /* IPXCP fsm structure */ static fsm_callbacks ipxcp_callbacks = { /* IPXCP callback routines */ ipxcp_resetci, /* Reset our Configuration Information */ ipxcp_cilen, /* Length of our Configuration Information */ ipxcp_addci, /* Add our Configuration Information */ ipxcp_ackci, /* ACK our Configuration Information */ ipxcp_nakci, /* NAK our Configuration Information */ ipxcp_rejci, /* Reject our Configuration Information */ ipxcp_reqci, /* Request peer's Configuration Information */ ipxcp_up, /* Called when fsm reaches OPENED state */ ipxcp_down, /* Called when fsm leaves OPENED state */ NULL, /* Called when we want the lower layer up */ ipxcp_finished, /* 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 */ "IPXCP" /* String name of protocol */ }; /* * Command-line options. */ static int setipxnode __P((char **)); static void printipxnode __P((option_t *, void (*)(void *, char *, ...), void *)); static int setipxname __P((char **)); static option_t ipxcp_option_list[] = { { "ipx", o_bool, &ipxcp_protent.enabled_flag, "Enable IPXCP (and IPX)", OPT_PRIO | 1 }, { "+ipx", o_bool, &ipxcp_protent.enabled_flag, "Enable IPXCP (and IPX)", OPT_PRIOSUB | OPT_ALIAS | 1 }, { "noipx", o_bool, &ipxcp_protent.enabled_flag, "Disable IPXCP (and IPX)", OPT_PRIOSUB }, { "-ipx", o_bool, &ipxcp_protent.enabled_flag, "Disable IPXCP (and IPX)", OPT_PRIOSUB | OPT_ALIAS }, { "ipx-network", o_uint32, &ipxcp_wantoptions[0].our_network, "Set our IPX network number", OPT_PRIO, &ipxcp_wantoptions[0].neg_nn }, { "ipxcp-accept-network", o_bool, &ipxcp_wantoptions[0].accept_network, "Accept peer IPX network number", 1, &ipxcp_allowoptions[0].accept_network }, { "ipx-node", o_special, (void *)setipxnode, "Set IPX node number", OPT_A2PRINTER, (void *)printipxnode }, { "ipxcp-accept-local", o_bool, &ipxcp_wantoptions[0].accept_local, "Accept our IPX address", 1, &ipxcp_allowoptions[0].accept_local }, { "ipxcp-accept-remote", o_bool, &ipxcp_wantoptions[0].accept_remote, "Accept peer's IPX address", 1, &ipxcp_allowoptions[0].accept_remote }, { "ipx-routing", o_int, &ipxcp_wantoptions[0].router, "Set IPX routing proto number", OPT_PRIO, &ipxcp_wantoptions[0].neg_router }, { "ipx-router-name", o_special, setipxname, "Set IPX router name", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, &ipxcp_wantoptions[0].name }, { "ipxcp-restart", o_int, &ipxcp_fsm[0].timeouttime, "Set timeout for IPXCP", OPT_PRIO }, { "ipxcp-max-terminate", o_int, &ipxcp_fsm[0].maxtermtransmits, "Set max #xmits for IPXCP term-reqs", OPT_PRIO }, { "ipxcp-max-configure", o_int, &ipxcp_fsm[0].maxconfreqtransmits, "Set max #xmits for IPXCP conf-reqs", OPT_PRIO }, { "ipxcp-max-failure", o_int, &ipxcp_fsm[0].maxnakloops, "Set max #conf-naks for IPXCP", OPT_PRIO }, { NULL } }; /* * Protocol entry points. */ static void ipxcp_init __P((int)); static void ipxcp_open __P((int)); static void ipxcp_close __P((int, char *)); static void ipxcp_lowerup __P((int)); static void ipxcp_lowerdown __P((int)); static void ipxcp_input __P((int, u_char *, int)); static void ipxcp_protrej __P((int)); static int ipxcp_printpkt __P((u_char *, int, void (*) __P((void *, char *, ...)), void *)); struct protent ipxcp_protent = { PPP_IPXCP, ipxcp_init, ipxcp_input, ipxcp_protrej, ipxcp_lowerup, ipxcp_lowerdown, ipxcp_open, ipxcp_close, ipxcp_printpkt, NULL, 0, "IPXCP", "IPX", ipxcp_option_list, NULL, NULL, NULL }; /* * Lengths of configuration options. */ #define CILEN_VOID 2 #define CILEN_COMPLETE 2 /* length of complete option */ #define CILEN_NETN 6 /* network number length option */ #define CILEN_NODEN 8 /* node number length option */ #define CILEN_PROTOCOL 4 /* Minimum length of routing protocol */ #define CILEN_NAME 3 /* Minimum length of router name */ #define CILEN_COMPRESS 4 /* Minimum length of compression protocol */ #define CODENAME(x) ((x) == CONFACK ? "ACK" : \ (x) == CONFNAK ? "NAK" : "REJ") static int ipxcp_is_up; static char *ipx_ntoa __P((u_int32_t)); /* Used in printing the node number */ #define NODE(base) base[0], base[1], base[2], base[3], base[4], base[5] /* Used to generate the proper bit mask */ #define BIT(num) (1 << (num)) /* * Convert from internal to external notation */ static short int to_external(internal) short int internal; { short int external; if (internal & BIT(IPX_NONE) ) external = IPX_NONE; else external = RIP_SAP; return external; } /* * Make a string representation of a network IP address. */ static char * ipx_ntoa(ipxaddr) u_int32_t ipxaddr; { static char b[64]; slprintf(b, sizeof(b), "%x", ipxaddr); return b; } static u_char * setipxnodevalue(src,dst) u_char *src, *dst; { int indx; int item; for (;;) { if (!isxdigit (*src)) break; for (indx = 0; indx < 5; ++indx) { dst[indx] <<= 4; dst[indx] |= (dst[indx + 1] >> 4) & 0x0F; } item = toupper (*src) - '0'; if (item > 9) item -= 7; dst[5] = (dst[5] << 4) | item; ++src; } return src; } static int ipx_prio_our, ipx_prio_his; static int setipxnode(argv) char **argv; { u_char *end; int have_his = 0; u_char our_node[6]; u_char his_node[6]; memset (our_node, 0, 6); memset (his_node, 0, 6); end = setipxnodevalue (*argv, our_node); if (*end == ':') { have_his = 1; end = setipxnodevalue (++end, his_node); } if (*end == '\0') { ipxcp_wantoptions[0].neg_node = 1; if (option_priority >= ipx_prio_our) { memcpy(&ipxcp_wantoptions[0].our_node[0], our_node, 6); ipx_prio_our = option_priority; } if (have_his && option_priority >= ipx_prio_his) { memcpy(&ipxcp_wantoptions[0].his_node[0], his_node, 6); ipx_prio_his = option_priority; } return 1; } option_error("invalid parameter '%s' for ipx-node option", *argv); return 0; } static void printipxnode(opt, printer, arg) option_t *opt; void (*printer) __P((void *, char *, ...)); void *arg; { unsigned char *p; p = ipxcp_wantoptions[0].our_node; if (ipx_prio_our) printer(arg, "%.2x%.2x%.2x%.2x%.2x%.2x", p[0], p[1], p[2], p[3], p[4], p[5]); printer(arg, ":"); p = ipxcp_wantoptions[0].his_node; if (ipx_prio_his) printer(arg, "%.2x%.2x%.2x%.2x%.2x%.2x", p[0], p[1], p[2], p[3], p[4], p[5]); } static int setipxname (argv) char **argv; { u_char *dest = ipxcp_wantoptions[0].name; char *src = *argv; int count; char ch; ipxcp_wantoptions[0].neg_name = 1; ipxcp_allowoptions[0].neg_name = 1; memset (dest, '\0', sizeof (ipxcp_wantoptions[0].name)); count = 0; while (*src) { ch = *src++; if (! isalnum (ch) && ch != '_') { option_error("IPX router name must be alphanumeric or _"); return 0; } if (count >= sizeof (ipxcp_wantoptions[0].name) - 1) { option_error("IPX router name is limited to %d characters", sizeof (ipxcp_wantoptions[0].name) - 1); return 0; } dest[count++] = toupper (ch); } dest[count] = 0; return 1; } /* * ipxcp_init - Initialize IPXCP. */ static void ipxcp_init(unit) int unit; { fsm *f = &ipxcp_fsm[unit]; f->unit = unit; f->protocol = PPP_IPXCP; f->callbacks = &ipxcp_callbacks; fsm_init(&ipxcp_fsm[unit]); memset (wo->name, 0, sizeof (wo->name)); memset (wo->our_node, 0, sizeof (wo->our_node)); memset (wo->his_node, 0, sizeof (wo->his_node)); wo->neg_nn = 1; wo->neg_complete = 1; wo->network = 0; ao->neg_node = 1; ao->neg_nn = 1; ao->neg_name = 1; ao->neg_complete = 1; ao->neg_router = 1; ao->accept_local = 0; ao->accept_remote = 0; ao->accept_network = 0; wo->tried_rip = 0; wo->tried_nlsp = 0; } /* * Copy the node number */ static void copy_node (src, dst) u_char *src, *dst; { memcpy (dst, src, sizeof (ipxcp_wantoptions[0].our_node)); } /* * Compare node numbers */ static int compare_node (src, dst) u_char *src, *dst; { return memcmp (dst, src, sizeof (ipxcp_wantoptions[0].our_node)) == 0; } /* * Is the node number zero? */ static int zero_node (node) u_char *node; { int indx; for (indx = 0; indx < sizeof (ipxcp_wantoptions[0].our_node); ++indx) if (node [indx] != 0) return 0; return 1; } /* * Increment the node number */ static void inc_node (node) u_char *node; { u_char *outp; u_int32_t magic_num; outp = node; magic_num = magic(); *outp++ = '\0'; *outp++ = '\0'; PUTLONG (magic_num, outp); } /* * ipxcp_open - IPXCP is allowed to come up. */ static void ipxcp_open(unit) int unit; { fsm_open(&ipxcp_fsm[unit]); } /* * ipxcp_close - Take IPXCP down. */ static void ipxcp_close(unit, reason) int unit; char *reason; { fsm_close(&ipxcp_fsm[unit], reason); } /* * ipxcp_lowerup - The lower layer is up. */ static void ipxcp_lowerup(unit) int unit; { fsm_lowerup(&ipxcp_fsm[unit]); } /* * ipxcp_lowerdown - The lower layer is down. */ static void ipxcp_lowerdown(unit) int unit; { fsm_lowerdown(&ipxcp_fsm[unit]); } /* * ipxcp_input - Input IPXCP packet. */ static void ipxcp_input(unit, p, len) int unit; u_char *p; int len; { fsm_input(&ipxcp_fsm[unit], p, len); } /* * ipxcp_protrej - A Protocol-Reject was received for IPXCP. * * Pretend the lower layer went down, so we shut up. */ static void ipxcp_protrej(unit) int unit; { fsm_lowerdown(&ipxcp_fsm[unit]); } /* * ipxcp_resetci - Reset our CI. */ static void ipxcp_resetci(f) fsm *f; { wo->req_node = wo->neg_node && ao->neg_node; wo->req_nn = wo->neg_nn && ao->neg_nn; if (wo->our_network == 0) { wo->neg_node = 1; ao->accept_network = 1; } /* * If our node number is zero then change it. */ if (zero_node (wo->our_node)) { inc_node (wo->our_node); ao->accept_local = 1; wo->neg_node = 1; } /* * If his node number is zero then change it. */ if (zero_node (wo->his_node)) { inc_node (wo->his_node); ao->accept_remote = 1; } /* * If no routing agent was specified then we do RIP/SAP according to the * RFC documents. If you have specified something then OK. Otherwise, we * do RIP/SAP. */ if (ao->router == 0) { ao->router |= BIT(RIP_SAP); wo->router |= BIT(RIP_SAP); } /* Always specify a routing protocol unless it was REJected. */ wo->neg_router = 1; /* * Start with these default values */ *go = *wo; } /* * ipxcp_cilen - Return length of our CI. */ static int ipxcp_cilen(f) fsm *f; { int len; len = go->neg_nn ? CILEN_NETN : 0; len += go->neg_node ? CILEN_NODEN : 0; len += go->neg_name ? CILEN_NAME + strlen ((char *)go->name) - 1 : 0; /* RFC says that defaults should not be included. */ if (go->neg_router && to_external(go->router) != RIP_SAP) len += CILEN_PROTOCOL; return (len); } /* * ipxcp_addci - Add our desired CIs to a packet. */ static void ipxcp_addci(f, ucp, lenp) fsm *f; u_char *ucp; int *lenp; { /* * Add the options to the record. */ if (go->neg_nn) { PUTCHAR (IPX_NETWORK_NUMBER, ucp); PUTCHAR (CILEN_NETN, ucp); PUTLONG (go->our_network, ucp); } if (go->neg_node) { int indx; PUTCHAR (IPX_NODE_NUMBER, ucp); PUTCHAR (CILEN_NODEN, ucp); for (indx = 0; indx < sizeof (go->our_node); ++indx) PUTCHAR (go->our_node[indx], ucp); } if (go->neg_name) { int cilen = strlen ((char *)go->name); int indx; PUTCHAR (IPX_ROUTER_NAME, ucp); PUTCHAR (CILEN_NAME + cilen - 1, ucp); for (indx = 0; indx < cilen; ++indx) PUTCHAR (go->name [indx], ucp); } if (go->neg_router) { short external = to_external (go->router); if (external != RIP_SAP) { PUTCHAR (IPX_ROUTER_PROTOCOL, ucp); PUTCHAR (CILEN_PROTOCOL, ucp); PUTSHORT (external, ucp); } } } /* * ipxcp_ackci - Ack our CIs. * * Returns: * 0 - Ack was bad. * 1 - Ack was good. */ static int ipxcp_ackci(f, p, len) fsm *f; u_char *p; int len; { u_short cilen, citype, cishort; u_char cichar; u_int32_t cilong; #define ACKCIVOID(opt, neg) \ if (neg) { \ if ((len -= CILEN_VOID) < 0) \ break; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_VOID || \ citype != opt) \ break; \ } #define ACKCICOMPLETE(opt,neg) ACKCIVOID(opt, neg) #define ACKCICHARS(opt, neg, val, cnt) \ if (neg) { \ int indx, count = cnt; \ len -= (count + 2); \ if (len < 0) \ break; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != (count + 2) || \ citype != opt) \ break; \ for (indx = 0; indx < count; ++indx) {\ GETCHAR(cichar, p); \ if (cichar != ((u_char *) &val)[indx]) \ break; \ }\ if (indx != count) \ break; \ } #define ACKCINODE(opt,neg,val) ACKCICHARS(opt,neg,val,sizeof(val)) #define ACKCINAME(opt,neg,val) ACKCICHARS(opt,neg,val,strlen((char *)val)) #define ACKCINETWORK(opt, neg, val) \ if (neg) { \ if ((len -= CILEN_NETN) < 0) \ break; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_NETN || \ citype != opt) \ break; \ GETLONG(cilong, p); \ if (cilong != val) \ break; \ } #define ACKCIPROTO(opt, neg, val) \ if (neg) { \ if (len < 2) \ break; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_PROTOCOL || citype != opt) \ break; \ len -= cilen; \ if (len < 0) \ break; \ GETSHORT(cishort, p); \ if (cishort != to_external (val) || cishort == RIP_SAP) \ break; \ } /* * Process the ACK frame in the order in which the frame was assembled */ do { ACKCINETWORK (IPX_NETWORK_NUMBER, go->neg_nn, go->our_network); ACKCINODE (IPX_NODE_NUMBER, go->neg_node, go->our_node); ACKCINAME (IPX_ROUTER_NAME, go->neg_name, go->name); if (len > 0) ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router); /* * This is the end of the record. */ if (len == 0) return (1); } while (0); /* * The frame is invalid */ IPXCPDEBUG(("ipxcp_ackci: received bad Ack!")); return (0); } /* * ipxcp_nakci - Peer has sent a NAK for some of our CIs. * This should not modify any state if the Nak is bad * or if IPXCP is in the OPENED state. * * Returns: * 0 - Nak was bad. * 1 - Nak was good. */ static int ipxcp_nakci(f, p, len, treat_as_reject) fsm *f; u_char *p; int len; int treat_as_reject; { u_char citype, cilen, *next; u_short s; u_int32_t l; ipxcp_options no; /* options we've seen Naks for */ ipxcp_options try; /* options to request next time */ BZERO(&no, sizeof(no)); try = *go; while (len >= CILEN_VOID) { GETCHAR (citype, p); GETCHAR (cilen, p); len -= cilen; if (cilen < CILEN_VOID || len < 0) goto bad; next = &p [cilen - CILEN_VOID]; switch (citype) { case IPX_NETWORK_NUMBER: if (!go->neg_nn || no.neg_nn || (cilen != CILEN_NETN)) goto bad; no.neg_nn = 1; GETLONG(l, p); if (treat_as_reject) try.neg_nn = 0; else if (l && ao->accept_network) try.our_network = l; break; case IPX_NODE_NUMBER: if (!go->neg_node || no.neg_node || (cilen != CILEN_NODEN)) goto bad; no.neg_node = 1; if (treat_as_reject) try.neg_node = 0; else if (!zero_node (p) && ao->accept_local && ! compare_node (p, ho->his_node)) copy_node (p, try.our_node); break; /* This has never been sent. Ignore the NAK frame */ case IPX_COMPRESSION_PROTOCOL: goto bad; case IPX_ROUTER_PROTOCOL: if (!go->neg_router || (cilen < CILEN_PROTOCOL)) goto bad; GETSHORT (s, p); if (s > 15) /* This is just bad, but ignore for now. */ break; s = BIT(s); if (no.router & s) /* duplicate NAKs are always bad */ goto bad; if (no.router == 0) /* Reset on first NAK only */ try.router = 0; no.router |= s; try.router |= s; try.neg_router = 1; break; /* These, according to the RFC, must never be NAKed. */ case IPX_ROUTER_NAME: case IPX_COMPLETE: goto bad; /* These are for options which we have not seen. */ default: break; } p = next; } /* * Do not permit the peer to force a router protocol which we do not * support. However, default to the condition that will accept "NONE". */ try.router &= (ao->router | BIT(IPX_NONE)); if (try.router == 0 && ao->router != 0) try.router = BIT(IPX_NONE); if (try.router != 0) try.neg_router = 1; /* * OK, the Nak is good. Now we can update state. * If there are any options left, we ignore them. */ if (f->state != OPENED) *go = try; return 1; bad: IPXCPDEBUG(("ipxcp_nakci: received bad Nak!")); return 0; } /* * ipxcp_rejci - Reject some of our CIs. */ static int ipxcp_rejci(f, p, len) fsm *f; u_char *p; int len; { u_short cilen, citype, cishort; u_char cichar; u_int32_t cilong; ipxcp_options try; /* options to request next time */ #define REJCINETWORK(opt, neg, val) \ if (neg && p[0] == opt) { \ if ((len -= CILEN_NETN) < 0) \ break; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_NETN || \ citype != opt) \ break; \ GETLONG(cilong, p); \ if (cilong != val) \ break; \ neg = 0; \ } #define REJCICHARS(opt, neg, val, cnt) \ if (neg && p[0] == opt) { \ int indx, count = cnt; \ len -= (count + 2); \ if (len < 0) \ break; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != (count + 2) || \ citype != opt) \ break; \ for (indx = 0; indx < count; ++indx) {\ GETCHAR(cichar, p); \ if (cichar != ((u_char *) &val)[indx]) \ break; \ }\ if (indx != count) \ break; \ neg = 0; \ } #define REJCINODE(opt,neg,val) REJCICHARS(opt,neg,val,sizeof(val)) #define REJCINAME(opt,neg,val) REJCICHARS(opt,neg,val,strlen((char *)val)) #define REJCIVOID(opt, neg) \ if (neg && p[0] == opt) { \ if ((len -= CILEN_VOID) < 0) \ break; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_VOID || citype != opt) \ break; \ neg = 0; \ } /* a reject for RIP/SAP is invalid since we don't send it and you can't reject something which is not sent. (You can NAK, but you can't REJ.) */ #define REJCIPROTO(opt, neg, val, bit) \ if (neg && p[0] == opt) { \ if ((len -= CILEN_PROTOCOL) < 0) \ break; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_PROTOCOL) \ break; \ GETSHORT(cishort, p); \ if (cishort != to_external (val) || cishort == RIP_SAP) \ break; \ neg = 0; \ } /* * 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. */ try = *go; do { REJCINETWORK (IPX_NETWORK_NUMBER, try.neg_nn, try.our_network); REJCINODE (IPX_NODE_NUMBER, try.neg_node, try.our_node); REJCINAME (IPX_ROUTER_NAME, try.neg_name, try.name); REJCIPROTO (IPX_ROUTER_PROTOCOL, try.neg_router, try.router, 0); /* * This is the end of the record. */ if (len == 0) { if (f->state != OPENED) *go = try; return (1); } } while (0); /* * The frame is invalid at this point. */ IPXCPDEBUG(("ipxcp_rejci: received bad Reject!")); return 0; } /* * ipxcp_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 ipxcp_reqci(f, inp, len, reject_if_disagree) fsm *f; u_char *inp; /* Requested CIs */ int *len; /* Length of requested CIs */ int reject_if_disagree; { u_char *cip, *next; /* Pointer to current and next CIs */ u_short cilen, citype; /* Parsed len, type */ u_short cishort; /* Parsed short value */ u_int32_t cinetwork; /* 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 */ /* * 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? */ IPXCPDEBUG(("ipxcp_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 */ /* * The network number must match. Choose the larger of the two. */ case IPX_NETWORK_NUMBER: /* if we wont negotiate the network number or the length is wrong then reject the option */ if ( !ao->neg_nn || cilen != CILEN_NETN ) { orc = CONFREJ; break; } GETLONG(cinetwork, p); /* If the network numbers match then acknowledge them. */ if (cinetwork != 0) { ho->his_network = cinetwork; ho->neg_nn = 1; if (wo->our_network == cinetwork) break; /* * If the network number is not given or we don't accept their change or * the network number is too small then NAK it. */ if (! ao->accept_network || cinetwork < wo->our_network) { DECPTR (sizeof (u_int32_t), p); PUTLONG (wo->our_network, p); orc = CONFNAK; } break; } /* * The peer sent '0' for the network. Give it ours if we have one. */ if (go->our_network != 0) { DECPTR (sizeof (u_int32_t), p); PUTLONG (wo->our_network, p); orc = CONFNAK; /* * We don't have one. Reject the value. */ } else orc = CONFREJ; break; /* * The node number is required */ case IPX_NODE_NUMBER: /* if we wont negotiate the node number or the length is wrong then reject the option */ if ( cilen != CILEN_NODEN ) { orc = CONFREJ; break; } copy_node (p, ho->his_node); ho->neg_node = 1; /* * If the remote does not have a number and we do then NAK it with the value * which we have for it. (We never have a default value of zero.) */ if (zero_node (ho->his_node)) { orc = CONFNAK; copy_node (wo->his_node, p); INCPTR (sizeof (wo->his_node), p); break; } /* * If you have given me the expected network node number then I'll accept * it now. */ if (compare_node (wo->his_node, ho->his_node)) { orc = CONFACK; ho->neg_node = 1; INCPTR (sizeof (wo->his_node), p); break; } /* * If his node number is the same as ours then ask him to try the next * value. */ if (compare_node (ho->his_node, go->our_node)) { inc_node (ho->his_node); orc = CONFNAK; copy_node (ho->his_node, p); INCPTR (sizeof (wo->his_node), p); break; } /* * If we don't accept a new value then NAK it. */ if (! ao->accept_remote) { copy_node (wo->his_node, p); INCPTR (sizeof (wo->his_node), p); orc = CONFNAK; break; } orc = CONFACK; ho->neg_node = 1; INCPTR (sizeof (wo->his_node), p); break; /* * Compression is not desired at this time. It is always rejected. */ case IPX_COMPRESSION_PROTOCOL: orc = CONFREJ; break; /* * The routing protocol is a bitmask of various types. Any combination * of the values RIP_SAP and NLSP are permissible. 'IPX_NONE' for no * routing protocol must be specified only once. */ case IPX_ROUTER_PROTOCOL: if ( !ao->neg_router || cilen < CILEN_PROTOCOL ) { orc = CONFREJ; break; } GETSHORT (cishort, p); if (wo->neg_router == 0) { wo->neg_router = 1; wo->router = BIT(IPX_NONE); } if ((cishort == IPX_NONE && ho->router != 0) || (ho->router & BIT(IPX_NONE))) { orc = CONFREJ; break; } cishort = BIT(cishort); if (ho->router & cishort) { orc = CONFREJ; break; } ho->router |= cishort; ho->neg_router = 1; /* Finally do not allow a router protocol which we do not support. */ if ((cishort & (ao->router | BIT(IPX_NONE))) == 0) { int protocol; if (cishort == BIT(NLSP) && (ao->router & BIT(RIP_SAP)) && !wo->tried_rip) { protocol = RIP_SAP; wo->tried_rip = 1; } else protocol = IPX_NONE; DECPTR (sizeof (u_int16_t), p); PUTSHORT (protocol, p); orc = CONFNAK; } break; /* * The router name is advisorary. Just accept it if it is not too large. */ case IPX_ROUTER_NAME: if (cilen >= CILEN_NAME) { int name_size = cilen - CILEN_NAME; if (name_size > sizeof (ho->name)) name_size = sizeof (ho->name) - 1; memset (ho->name, 0, sizeof (ho->name)); memcpy (ho->name, p, name_size); ho->name [name_size] = '\0'; ho->neg_name = 1; orc = CONFACK; break; } orc = CONFREJ; break; /* * This is advisorary. */ case IPX_COMPLETE: if (cilen != CILEN_COMPLETE) orc = CONFREJ; else { ho->neg_complete = 1; orc = CONFACK; } break; /* * All other entries are not known at this time. */ default: orc = CONFREJ; break; } endswitch: 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 */ 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 IPX_NODE_NUMBER option appended. We assume the * input buffer is long enough that we can append the extra * option safely. */ if (rc != CONFREJ && !ho->neg_node && wo->req_nn && !reject_if_disagree) { if (rc == CONFACK) { rc = CONFNAK; wo->req_nn = 0; /* don't ask again */ ucp = inp; /* reset pointer */ } if (zero_node (wo->his_node)) inc_node (wo->his_node); PUTCHAR (IPX_NODE_NUMBER, ucp); PUTCHAR (CILEN_NODEN, ucp); copy_node (wo->his_node, ucp); INCPTR (sizeof (wo->his_node), ucp); } *len = ucp - inp; /* Compute output length */ IPXCPDEBUG(("ipxcp: returning Configure-%s", CODENAME(rc))); return (rc); /* Return final code */ } /* * ipxcp_up - IPXCP has come UP. * * Configure the IP network interface appropriately and bring it up. */ static void ipxcp_up(f) fsm *f; { int unit = f->unit; IPXCPDEBUG(("ipxcp: up")); /* The default router protocol is RIP/SAP. */ if (ho->router == 0) ho->router = BIT(RIP_SAP); if (go->router == 0) go->router = BIT(RIP_SAP); /* Fetch the network number */ if (!ho->neg_nn) ho->his_network = wo->his_network; if (!ho->neg_node) copy_node (wo->his_node, ho->his_node); if (!wo->neg_node && !go->neg_node) copy_node (wo->our_node, go->our_node); if (zero_node (go->our_node)) { static char errmsg[] = "Could not determine local IPX node address"; if (debug) error(errmsg); ipxcp_close(f->unit, errmsg); return; } go->network = go->our_network; if (ho->his_network != 0 && ho->his_network > go->network) go->network = ho->his_network; if (go->network == 0) { static char errmsg[] = "Can not determine network number"; if (debug) error(errmsg); ipxcp_close (unit, errmsg); return; } /* bring the interface up */ if (!sifup(unit)) { if (debug) warn("sifup failed (IPX)"); ipxcp_close(unit, "Interface configuration failed"); return; } ipxcp_is_up = 1; /* set the network number for IPX */ if (!sipxfaddr(unit, go->network, go->our_node)) { if (debug) warn("sipxfaddr failed"); ipxcp_close(unit, "Interface configuration failed"); return; } np_up(f->unit, PPP_IPX); /* * Execute the ipx-up script, like this: * /etc/ppp/ipx-up interface tty speed local-IPX remote-IPX */ ipxcp_script (f, _PATH_IPXUP); } /* * ipxcp_down - IPXCP has gone DOWN. * * Take the IP network interface down, clear its addresses * and delete routes through it. */ static void ipxcp_down(f) fsm *f; { IPXCPDEBUG(("ipxcp: down")); if (!ipxcp_is_up) return; ipxcp_is_up = 0; np_down(f->unit, PPP_IPX); cipxfaddr(f->unit); sifnpmode(f->unit, PPP_IPX, NPMODE_DROP); sifdown(f->unit); ipxcp_script (f, _PATH_IPXDOWN); } /* * ipxcp_finished - possibly shut down the lower layers. */ static void ipxcp_finished(f) fsm *f; { np_finished(f->unit, PPP_IPX); } /* * ipxcp_script - Execute a script with arguments * interface-name tty-name speed local-IPX remote-IPX networks. */ static void ipxcp_script(f, script) fsm *f; char *script; { char strspeed[32], strlocal[32], strremote[32]; char strnetwork[32], strpid[32]; char *argv[14], strproto_lcl[32], strproto_rmt[32]; slprintf(strpid, sizeof(strpid), "%d", getpid()); slprintf(strspeed, sizeof(strspeed),"%d", baud_rate); strproto_lcl[0] = '\0'; if (go->neg_router && ((go->router & BIT(IPX_NONE)) == 0)) { if (go->router & BIT(RIP_SAP)) strlcpy (strproto_lcl, "RIP ", sizeof(strproto_lcl)); if (go->router & BIT(NLSP)) strlcat (strproto_lcl, "NLSP ", sizeof(strproto_lcl)); } if (strproto_lcl[0] == '\0') strlcpy (strproto_lcl, "NONE ", sizeof(strproto_lcl)); strproto_lcl[strlen (strproto_lcl)-1] = '\0'; strproto_rmt[0] = '\0'; if (ho->neg_router && ((ho->router & BIT(IPX_NONE)) == 0)) { if (ho->router & BIT(RIP_SAP)) strlcpy (strproto_rmt, "RIP ", sizeof(strproto_rmt)); if (ho->router & BIT(NLSP)) strlcat (strproto_rmt, "NLSP ", sizeof(strproto_rmt)); } if (strproto_rmt[0] == '\0') strlcpy (strproto_rmt, "NONE ", sizeof(strproto_rmt)); strproto_rmt[strlen (strproto_rmt)-1] = '\0'; strlcpy (strnetwork, ipx_ntoa (go->network), sizeof(strnetwork)); slprintf (strlocal, sizeof(strlocal), "%0.6B", go->our_node); slprintf (strremote, sizeof(strremote), "%0.6B", ho->his_node); argv[0] = script; argv[1] = ifname; argv[2] = devnam; argv[3] = strspeed; argv[4] = strnetwork; argv[5] = strlocal; argv[6] = strremote; argv[7] = strproto_lcl; argv[8] = strproto_rmt; argv[9] = (char *)go->name; argv[10] = (char *)ho->name; argv[11] = ipparam; argv[12] = strpid; argv[13] = NULL; run_program(script, argv, 0, NULL, NULL, 0); } /* * ipxcp_printpkt - print the contents of an IPXCP packet. */ static char *ipxcp_codenames[] = { "ConfReq", "ConfAck", "ConfNak", "ConfRej", "TermReq", "TermAck", "CodeRej" }; static int ipxcp_printpkt(p, plen, printer, arg) u_char *p; int plen; void (*printer) __P((void *, char *, ...)); void *arg; { int code, id, len, olen; u_char *pstart, *optend; u_short cishort; u_int32_t 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(ipxcp_codenames) / sizeof(char *)) printer(arg, " %s", ipxcp_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 < CILEN_VOID || olen > len) { break; } printer(arg, " <"); len -= olen; optend = p + olen; switch (code) { case IPX_NETWORK_NUMBER: if (olen == CILEN_NETN) { p += 2; GETLONG(cilong, p); printer (arg, "network %s", ipx_ntoa (cilong)); } break; case IPX_NODE_NUMBER: if (olen == CILEN_NODEN) { p += 2; printer (arg, "node "); while (p < optend) { GETCHAR(code, p); printer(arg, "%.2x", (int) (unsigned int) (unsigned char) code); } } break; case IPX_COMPRESSION_PROTOCOL: if (olen == CILEN_COMPRESS) { p += 2; GETSHORT (cishort, p); printer (arg, "compression %d", (int) cishort); } break; case IPX_ROUTER_PROTOCOL: if (olen == CILEN_PROTOCOL) { p += 2; GETSHORT (cishort, p); printer (arg, "router proto %d", (int) cishort); } break; case IPX_ROUTER_NAME: if (olen >= CILEN_NAME) { p += 2; printer (arg, "router name \""); while (p < optend) { GETCHAR(code, p); if (code >= 0x20 && code <= 0x7E) printer (arg, "%c", (int) (unsigned int) (unsigned char) code); else printer (arg, " \\%.2x", (int) (unsigned int) (unsigned char) code); } printer (arg, "\""); } break; case IPX_COMPLETE: if (olen == CILEN_COMPLETE) { p += 2; printer (arg, "complete"); } break; default: break; } while (p < optend) { GETCHAR(code, p); printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code); } printer(arg, ">"); } break; case TERMACK: case TERMREQ: if (len > 0 && *p >= ' ' && *p < 0x7f) { printer(arg, " "); print_string((char *)p, len, printer, arg); p += len; len = 0; } break; } /* print the rest of the bytes in the packet */ for (; len > 0; --len) { GETCHAR(code, p); printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code); } return p - pstart; } #endif /* ifdef IPX_CHANGE */ ppp-2.4.5/pppd/ipxcp.h000066400000000000000000000067751130035057700146000ustar00rootroot00000000000000/* * ipxcp.h - IPX Control Protocol definitions. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ipxcp.h,v 1.5 2002/12/04 23:03:32 paulus Exp $ */ /* * Options. */ #define IPX_NETWORK_NUMBER 1 /* IPX Network Number */ #define IPX_NODE_NUMBER 2 #define IPX_COMPRESSION_PROTOCOL 3 #define IPX_ROUTER_PROTOCOL 4 #define IPX_ROUTER_NAME 5 #define IPX_COMPLETE 6 /* Values for the router protocol */ #define IPX_NONE 0 #define RIP_SAP 2 #define NLSP 4 typedef struct ipxcp_options { bool neg_node; /* Negotiate IPX node number? */ bool req_node; /* Ask peer to send IPX node number? */ bool neg_nn; /* Negotiate IPX network number? */ bool req_nn; /* Ask peer to send IPX network number */ bool neg_name; /* Negotiate IPX router name */ bool neg_complete; /* Negotiate completion */ bool neg_router; /* Negotiate IPX router number */ bool accept_local; /* accept peer's value for ournode */ bool accept_remote; /* accept peer's value for hisnode */ bool accept_network; /* accept network number */ bool tried_nlsp; /* I have suggested NLSP already */ bool tried_rip; /* I have suggested RIP/SAP already */ u_int32_t his_network; /* base network number */ u_int32_t our_network; /* our value for network number */ u_int32_t network; /* the final network number */ u_char his_node[6]; /* peer's node number */ u_char our_node[6]; /* our node number */ u_char name [48]; /* name of the router */ int router; /* routing protocol */ } ipxcp_options; extern fsm ipxcp_fsm[]; extern ipxcp_options ipxcp_wantoptions[]; extern ipxcp_options ipxcp_gotoptions[]; extern ipxcp_options ipxcp_allowoptions[]; extern ipxcp_options ipxcp_hisoptions[]; extern struct protent ipxcp_protent; ppp-2.4.5/pppd/lcp.c000066400000000000000000001655371130035057700142300ustar00rootroot00000000000000/* * lcp.c - PPP Link Control Protocol. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: lcp.c,v 1.76 2006/05/22 00:04:07 paulus Exp $" /* * TODO: */ #include #include #include #include "pppd.h" #include "fsm.h" #include "lcp.h" #include "chap-new.h" #include "magic.h" static const char rcsid[] = RCSID; /* * When the link comes up we want to be able to wait for a short while, * or until seeing some input from the peer, before starting to send * configure-requests. We do this by delaying the fsm_lowerup call. */ /* steal a bit in fsm flags word */ #define DELAYED_UP 0x100 static void lcp_delayed_up __P((void *)); /* * LCP-related command-line options. */ int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ bool lax_recv = 0; /* accept control chars in asyncmap */ bool noendpoint = 0; /* don't send/accept endpoint discriminator */ static int noopt __P((char **)); #ifdef HAVE_MULTILINK static int setendpoint __P((char **)); static void printendpoint __P((option_t *, void (*)(void *, char *, ...), void *)); #endif /* HAVE_MULTILINK */ static option_t lcp_option_list[] = { /* LCP options */ { "-all", o_special_noarg, (void *)noopt, "Don't request/allow any LCP options" }, { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression, "Disable address/control compression", OPT_A2CLR, &lcp_allowoptions[0].neg_accompression }, { "-ac", o_bool, &lcp_wantoptions[0].neg_accompression, "Disable address/control compression", OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_accompression }, { "asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap, "Set asyncmap (for received packets)", OPT_OR, &lcp_wantoptions[0].neg_asyncmap }, { "-as", o_uint32, &lcp_wantoptions[0].asyncmap, "Set asyncmap (for received packets)", OPT_ALIAS | OPT_OR, &lcp_wantoptions[0].neg_asyncmap }, { "default-asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap, "Disable asyncmap negotiation", OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR, &lcp_allowoptions[0].neg_asyncmap }, { "-am", o_uint32, &lcp_wantoptions[0].asyncmap, "Disable asyncmap negotiation", OPT_ALIAS | OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR, &lcp_allowoptions[0].neg_asyncmap }, { "nomagic", o_bool, &lcp_wantoptions[0].neg_magicnumber, "Disable magic number negotiation (looped-back line detection)", OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber }, { "-mn", o_bool, &lcp_wantoptions[0].neg_magicnumber, "Disable magic number negotiation (looped-back line detection)", OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber }, { "mru", o_int, &lcp_wantoptions[0].mru, "Set MRU (maximum received packet size) for negotiation", OPT_PRIO, &lcp_wantoptions[0].neg_mru }, { "default-mru", o_bool, &lcp_wantoptions[0].neg_mru, "Disable MRU negotiation (use default 1500)", OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru }, { "-mru", o_bool, &lcp_wantoptions[0].neg_mru, "Disable MRU negotiation (use default 1500)", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru }, { "mtu", o_int, &lcp_allowoptions[0].mru, "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU }, { "nopcomp", o_bool, &lcp_wantoptions[0].neg_pcompression, "Disable protocol field compression", OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression }, { "-pc", o_bool, &lcp_wantoptions[0].neg_pcompression, "Disable protocol field compression", OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression }, { "passive", o_bool, &lcp_wantoptions[0].passive, "Set passive mode", 1 }, { "-p", o_bool, &lcp_wantoptions[0].passive, "Set passive mode", OPT_ALIAS | 1 }, { "silent", o_bool, &lcp_wantoptions[0].silent, "Set silent mode", 1 }, { "lcp-echo-failure", o_int, &lcp_echo_fails, "Set number of consecutive echo failures to indicate link failure", OPT_PRIO }, { "lcp-echo-interval", o_int, &lcp_echo_interval, "Set time in seconds between LCP echo requests", OPT_PRIO }, { "lcp-restart", o_int, &lcp_fsm[0].timeouttime, "Set time in seconds between LCP retransmissions", OPT_PRIO }, { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits, "Set maximum number of LCP terminate-request transmissions", OPT_PRIO }, { "lcp-max-configure", o_int, &lcp_fsm[0].maxconfreqtransmits, "Set maximum number of LCP configure-request transmissions", OPT_PRIO }, { "lcp-max-failure", o_int, &lcp_fsm[0].maxnakloops, "Set limit on number of LCP configure-naks", OPT_PRIO }, { "receive-all", o_bool, &lax_recv, "Accept all received control characters", 1 }, #ifdef HAVE_MULTILINK { "mrru", o_int, &lcp_wantoptions[0].mrru, "Maximum received packet size for multilink bundle", OPT_PRIO, &lcp_wantoptions[0].neg_mrru }, { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf, "Use short sequence numbers in multilink headers", OPT_PRIO | 1, &lcp_allowoptions[0].neg_ssnhf }, { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf, "Don't use short sequence numbers in multilink headers", OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_ssnhf }, { "endpoint", o_special, (void *) setendpoint, "Endpoint discriminator for multilink", OPT_PRIO | OPT_A2PRINTER, (void *) printendpoint }, #endif /* HAVE_MULTILINK */ { "noendpoint", o_bool, &noendpoint, "Don't send or accept multilink endpoint discriminator", 1 }, {NULL} }; /* global vars */ fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/ lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */ lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ static int lcp_echos_pending = 0; /* Number of outstanding echo msgs */ static int lcp_echo_number = 0; /* ID number of next echo frame */ static int lcp_echo_timer_running = 0; /* set if a timer is running */ static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ /* * Callbacks for fsm code. (CI = Configuration Information) */ static void lcp_resetci __P((fsm *)); /* Reset our CI */ static int lcp_cilen __P((fsm *)); /* Return length of our CI */ static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */ static int lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ static int lcp_nakci __P((fsm *, u_char *, int, int)); /* Peer nak'd our CI */ static int lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ static int lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */ static void lcp_up __P((fsm *)); /* We're UP */ static void lcp_down __P((fsm *)); /* We're DOWN */ static void lcp_starting __P((fsm *)); /* We need lower layer up */ static void lcp_finished __P((fsm *)); /* We need lower layer down */ static int lcp_extcode __P((fsm *, int, int, u_char *, int)); static void lcp_rprotrej __P((fsm *, u_char *, int)); /* * routines to send LCP echos to peer */ static void lcp_echo_lowerup __P((int)); static void lcp_echo_lowerdown __P((int)); static void LcpEchoTimeout __P((void *)); static void lcp_received_echo_reply __P((fsm *, int, u_char *, int)); static void LcpSendEchoRequest __P((fsm *)); static void LcpLinkFailure __P((fsm *)); static void LcpEchoCheck __P((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 */ }; /* * Protocol entry points. * Some of these are called directly. */ static void lcp_init __P((int)); static void lcp_input __P((int, u_char *, int)); static void lcp_protrej __P((int)); static int lcp_printpkt __P((u_char *, int, void (*) __P((void *, char *, ...)), void *)); struct protent lcp_protent = { PPP_LCP, lcp_init, lcp_input, lcp_protrej, lcp_lowerup, lcp_lowerdown, lcp_open, lcp_close, lcp_printpkt, NULL, 1, "LCP", NULL, lcp_option_list, NULL, NULL, NULL }; int lcp_loopbackfail = DEFLOOPBACKFAIL; /* * Length of each type of configuration option (in octets) */ #define CILEN_VOID 2 #define CILEN_CHAR 3 #define CILEN_SHORT 4 /* CILEN_VOID + 2 */ #define CILEN_CHAP 5 /* CILEN_VOID + 2 + 1 */ #define CILEN_LONG 6 /* CILEN_VOID + 4 */ #define CILEN_LQR 8 /* CILEN_VOID + 2 + 4 */ #define CILEN_CBCP 3 #define CODENAME(x) ((x) == CONFACK ? "ACK" : \ (x) == CONFNAK ? "NAK" : "REJ") /* * noopt - Disable all options (why?). */ static int noopt(argv) char **argv; { BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options)); BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options)); return (1); } #ifdef HAVE_MULTILINK static int setendpoint(argv) char **argv; { if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) { lcp_wantoptions[0].neg_endpoint = 1; return 1; } option_error("Can't parse '%s' as an endpoint discriminator", *argv); return 0; } static void printendpoint(opt, printer, arg) option_t *opt; void (*printer) __P((void *, char *, ...)); void *arg; { printer(arg, "%s", epdisc_to_str(&lcp_wantoptions[0].endpoint)); } #endif /* HAVE_MULTILINK */ /* * lcp_init - Initialize LCP. */ static 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 = PPP_LCP; f->callbacks = &lcp_callbacks; fsm_init(f); BZERO(wo, sizeof(*wo)); wo->neg_mru = 1; wo->mru = DEFMRU; wo->neg_asyncmap = 1; wo->neg_magicnumber = 1; wo->neg_pcompression = 1; wo->neg_accompression = 1; BZERO(ao, sizeof(*ao)); ao->neg_mru = 1; ao->mru = MAXMRU; ao->neg_asyncmap = 1; ao->neg_chap = 1; ao->chap_mdtype = chap_mdtype_all; ao->neg_upap = 1; ao->neg_eap = 1; ao->neg_magicnumber = 1; ao->neg_pcompression = 1; ao->neg_accompression = 1; ao->neg_endpoint = 1; } /* * 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 &= ~(OPT_PASSIVE | OPT_SILENT); 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, reason) int unit; char *reason; { fsm *f = &lcp_fsm[unit]; if (phase != PHASE_DEAD && phase != PHASE_MASTER) new_phase(PHASE_TERMINATE); 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() in passive/silent mode when a connection hasn't * been established. */ f->state = CLOSED; lcp_finished(f); } else fsm_close(f, reason); } /* * lcp_lowerup - The lower layer is up. */ void lcp_lowerup(unit) int unit; { lcp_options *wo = &lcp_wantoptions[unit]; fsm *f = &lcp_fsm[unit]; /* * Don't use A/C or protocol compression on transmission, * but accept A/C and protocol compressed packets * if we are going to ask for A/C and protocol compression. */ if (ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0) < 0 || ppp_recv_config(unit, PPP_MRU, (lax_recv? 0: 0xffffffff), wo->neg_pcompression, wo->neg_accompression) < 0) return; peer_mru[unit] = PPP_MRU; if (listen_time != 0) { f->flags |= DELAYED_UP; timeout(lcp_delayed_up, f, 0, listen_time * 1000); } else fsm_lowerup(f); } /* * lcp_lowerdown - The lower layer is down. */ void lcp_lowerdown(unit) int unit; { fsm *f = &lcp_fsm[unit]; if (f->flags & DELAYED_UP) f->flags &= ~DELAYED_UP; else fsm_lowerdown(&lcp_fsm[unit]); } /* * lcp_delayed_up - Bring the lower layer up now. */ static void lcp_delayed_up(arg) void *arg; { fsm *f = arg; if (f->flags & DELAYED_UP) { f->flags &= ~DELAYED_UP; fsm_lowerup(f); } } /* * lcp_input - Input LCP packet. */ static void lcp_input(unit, p, len) int unit; u_char *p; int len; { fsm *f = &lcp_fsm[unit]; if (f->flags & DELAYED_UP) { f->flags &= ~DELAYED_UP; fsm_lowerup(f); } fsm_input(f, 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; magp = inp; PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); fsm_sdata(f, ECHOREP, id, inp, len); break; case ECHOREP: lcp_received_echo_reply(f, id, inp, len); break; case DISCREQ: case IDENTIF: case TIMEREM: 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; { int i; struct protent *protp; u_short prot; const char *pname; if (len < 2) { LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!")); return; } GETSHORT(prot, inp); /* * Protocol-Reject packets received in any state other than the LCP * OPENED state SHOULD be silently discarded. */ if( f->state != OPENED ){ LCPDEBUG(("Protocol-Reject discarded: LCP in state %d", f->state)); return; } pname = protocol_name(prot); /* * Upcall the proper Protocol-Reject routine. */ for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->protocol == prot && protp->enabled_flag) { if (pname == NULL) dbglog("Protocol-Reject for 0x%x received", prot); else dbglog("Protocol-Reject for '%s' (0x%x) received", pname, prot); (*protp->protrej)(f->unit); return; } if (pname == NULL) warn("Protocol-Reject for unsupported protocol 0x%x", prot); else warn("Protocol-Reject for unsupported protocol '%s' (0x%x)", pname, prot); } /* * lcp_protrej - A Protocol-Reject was received. */ /*ARGSUSED*/ static void lcp_protrej(unit) int unit; { /* * Can't reject LCP! */ error("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_options *wo = &lcp_wantoptions[f->unit]; lcp_options *go = &lcp_gotoptions[f->unit]; lcp_options *ao = &lcp_allowoptions[f->unit]; wo->magicnumber = magic(); wo->numloops = 0; *go = *wo; if (!multilink) { go->neg_mrru = 0; go->neg_ssnhf = 0; go->neg_endpoint = 0; } if (noendpoint) ao->neg_endpoint = 0; peer_mru[f->unit] = PPP_MRU; auth_reset(f->unit); } /* * 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) #define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) /* * NB: we only ask for one of CHAP, UPAP, or EAP, even if we will * accept more than one. We prefer EAP first, then CHAP, then * PAP. */ return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + LENCISHORT(go->neg_eap) + LENCICHAP(!go->neg_eap && go->neg_chap) + LENCISHORT(!go->neg_eap && !go->neg_chap && go->neg_upap) + LENCILQR(go->neg_lqr) + LENCICBCP(go->neg_cbcp) + LENCILONG(go->neg_magicnumber) + LENCIVOID(go->neg_pcompression) + LENCIVOID(go->neg_accompression) + LENCISHORT(go->neg_mrru) + LENCIVOID(go->neg_ssnhf) + (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0)); } /* * 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) \ if (neg) { \ PUTCHAR((opt), ucp); \ PUTCHAR(CILEN_CHAP, ucp); \ PUTSHORT(PPP_CHAP, ucp); \ PUTCHAR((CHAP_DIGEST(val)), 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(PPP_LQR, ucp); \ PUTLONG(val, ucp); \ } #define ADDCICHAR(opt, neg, val) \ if (neg) { \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_CHAR, ucp); \ PUTCHAR(val, ucp); \ } #define ADDCIENDP(opt, neg, class, val, len) \ if (neg) { \ int i; \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_CHAR + len, ucp); \ PUTCHAR(class, ucp); \ for (i = 0; i < len; ++i) \ PUTCHAR(val[i], ucp); \ } ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, go->asyncmap); ADDCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP); ADDCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype); ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP); ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru); ADDCIVOID(CI_SSNHF, go->neg_ssnhf); ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class, go->endpoint.value, go->endpoint.length); if (ucp - start_ucp != *lenp) { /* this should never happen, because peer_mtu should be 1500 */ error("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_int32_t 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 ACKCICHAR(opt, neg, val) \ if (neg) { \ if ((len -= CILEN_CHAR) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_CHAR || \ citype != opt) \ goto bad; \ GETCHAR(cichar, p); \ if (cichar != val) \ goto bad; \ } #define ACKCICHAP(opt, neg, val) \ 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 != PPP_CHAP) \ goto bad; \ GETCHAR(cichar, p); \ if (cichar != (CHAP_DIGEST(val))) \ 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 != PPP_LQR) \ goto bad; \ GETLONG(cilong, p); \ if (cilong != val) \ goto bad; \ } #define ACKCIENDP(opt, neg, class, val, vlen) \ if (neg) { \ int i; \ if ((len -= CILEN_CHAR + vlen) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_CHAR + vlen || \ citype != opt) \ goto bad; \ GETCHAR(cichar, p); \ if (cichar != class) \ goto bad; \ for (i = 0; i < vlen; ++i) { \ GETCHAR(cichar, p); \ if (cichar != val[i]) \ goto bad; \ } \ } ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, go->asyncmap); ACKCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP); ACKCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype); ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP); ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru); ACKCIVOID(CI_SSNHF, go->neg_ssnhf); ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class, go->endpoint.value, go->endpoint.length); /* * If there are any remaining CIs, then this packet is bad. */ if (len != 0) goto bad; return (1); bad: LCPDEBUG(("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, treat_as_reject) fsm *f; u_char *p; int len; int treat_as_reject; { lcp_options *go = &lcp_gotoptions[f->unit]; lcp_options *wo = &lcp_wantoptions[f->unit]; u_char citype, cichar, *next; u_short cishort; u_int32_t cilong; lcp_options no; /* options we've seen Naks for */ lcp_options try; /* options to request next time */ int looped_back = 0; int cilen; 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) \ if (go->neg && \ len >= CILEN_VOID && \ p[1] == CILEN_VOID && \ p[0] == opt) { \ len -= CILEN_VOID; \ INCPTR(CILEN_VOID, p); \ no.neg = 1; \ try.neg = 0; \ } #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 NAKCICHAR(opt, neg, code) \ if (go->neg && \ len >= CILEN_CHAR && \ p[1] == CILEN_CHAR && \ p[0] == opt) { \ len -= CILEN_CHAR; \ INCPTR(2, 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 \ } #define NAKCIENDP(opt, neg) \ if (go->neg && \ len >= CILEN_CHAR && \ p[0] == opt && \ p[1] >= CILEN_CHAR && \ p[1] <= len) { \ len -= p[1]; \ INCPTR(p[1], p); \ no.neg = 1; \ try.neg = 0; \ } /* * NOTE! There must be no assignments to individual fields of *go in * the code below. Any such assignment is a BUG! */ /* * 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. */ if (go->neg_mru && go->mru != DEFMRU) { NAKCISHORT(CI_MRU, neg_mru, if (cishort <= wo->mru || cishort <= DEFMRU) try.mru = cishort; ); } /* * Add any characters they want to our (receive-side) asyncmap. */ if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) { NAKCILONG(CI_ASYNCMAP, neg_asyncmap, try.asyncmap = go->asyncmap | cilong; ); } /* * If they've nak'd our authentication-protocol, check whether * they are proposing a different protocol, or a different * hash algorithm for CHAP. */ if ((go->neg_chap || go->neg_upap || go->neg_eap) && len >= CILEN_SHORT && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { cilen = p[1]; len -= cilen; no.neg_chap = go->neg_chap; no.neg_upap = go->neg_upap; no.neg_eap = go->neg_eap; INCPTR(2, p); GETSHORT(cishort, p); if (cishort == PPP_PAP && cilen == CILEN_SHORT) { /* If we were asking for EAP, then we need to stop that. */ if (go->neg_eap) try.neg_eap = 0; /* If we were asking for CHAP, then we need to stop that. */ else if (go->neg_chap) try.neg_chap = 0; /* * If we weren't asking for CHAP or EAP, then we were asking for * PAP, in which case this Nak is bad. */ else goto bad; } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { GETCHAR(cichar, p); /* Stop asking for EAP, if we were. */ if (go->neg_eap) { try.neg_eap = 0; /* Try to set up to use their suggestion, if possible */ if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) try.chap_mdtype = CHAP_MDTYPE_D(cichar); } else if (go->neg_chap) { /* * We were asking for our preferred algorithm, they must * want something different. */ if (cichar != CHAP_DIGEST(go->chap_mdtype)) { if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) { /* Use their suggestion if we support it ... */ try.chap_mdtype = CHAP_MDTYPE_D(cichar); } else { /* ... otherwise, try our next-preferred algorithm. */ try.chap_mdtype &= ~(CHAP_MDTYPE(try.chap_mdtype)); if (try.chap_mdtype == MDTYPE_NONE) /* out of algos */ try.neg_chap = 0; } } else { /* * Whoops, they Nak'd our algorithm of choice * but then suggested it back to us. */ goto bad; } } else { /* * Stop asking for PAP if we were asking for it. */ try.neg_upap = 0; } } else { /* * If we were asking for EAP, and they're Conf-Naking EAP, * well, that's just strange. Nobody should do that. */ if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap) dbglog("Unexpected Conf-Nak for EAP"); /* * We don't recognize what they're suggesting. * Stop asking for what we were asking for. */ if (go->neg_eap) try.neg_eap = 0; else if (go->neg_chap) try.neg_chap = 0; else try.neg_upap = 0; p += cilen - CILEN_SHORT; } } /* * 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 != PPP_LQR) try.neg_lqr = 0; else try.lqr_period = cilong; ); /* * Only implementing CBCP...not the rest of the callback options */ NAKCICHAR(CI_CALLBACK, neg_cbcp, try.neg_cbcp = 0; ); /* * Check for a looped-back line. */ NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, try.magicnumber = magic(); looped_back = 1; ); /* * Peer shouldn't send Nak for protocol compression or * address/control compression requests; they should send * a Reject instead. If they send a Nak, treat it as a Reject. */ NAKCIVOID(CI_PCOMPRESSION, neg_pcompression); NAKCIVOID(CI_ACCOMPRESSION, neg_accompression); /* * Nak for MRRU option - accept their value if it is smaller * than the one we want. */ if (go->neg_mrru) { NAKCISHORT(CI_MRRU, neg_mrru, if (treat_as_reject) try.neg_mrru = 0; else if (cishort <= wo->mrru) try.mrru = cishort; ); } /* * Nak for short sequence numbers shouldn't be sent, treat it * like a reject. */ NAKCIVOID(CI_SSNHF, neg_ssnhf); /* * Nak of the endpoint discriminator option is not permitted, * treat it like a reject. */ NAKCIENDP(CI_EPDISC, neg_endpoint); /* * 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. * An option we don't recognize represents the peer asking to * negotiate some option we don't support, so ignore it. */ while (len >= CILEN_VOID) { GETCHAR(citype, p); GETCHAR(cilen, p); if (cilen < CILEN_VOID || (len -= cilen) < 0) goto bad; next = p + cilen - 2; switch (citype) { case CI_MRU: if ((go->neg_mru && go->mru != DEFMRU) || no.neg_mru || cilen != CILEN_SHORT) goto bad; GETSHORT(cishort, p); if (cishort < DEFMRU) { try.neg_mru = 1; try.mru = cishort; } break; case CI_ASYNCMAP: if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) || 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 || go->neg_eap || no.neg_eap) 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; case CI_MRRU: if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT) goto bad; break; case CI_SSNHF: if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID) goto bad; try.neg_ssnhf = 1; break; case CI_EPDISC: if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR) goto bad; break; } p = next; } /* * OK, the Nak is good. Now we can update state. * If there are any options left we ignore them. */ if (f->state != OPENED) { if (looped_back) { if (++try.numloops >= lcp_loopbackfail) { notice("Serial line is looped back."); status = EXIT_LOOPBACK; lcp_close(f->unit, "Loopback detected"); } } else try.numloops = 0; *go = try; } return 1; bad: LCPDEBUG(("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_int32_t cilong; 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; \ } #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; \ } #define REJCICHAP(opt, neg, val) \ 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 != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ goto bad; \ try.neg = 0; \ try.neg_eap = try.neg_upap = 0; \ } #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; \ } #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 != PPP_LQR || cilong != val) \ goto bad; \ try.neg = 0; \ } #define REJCICBCP(opt, neg, val) \ if (go->neg && \ len >= CILEN_CBCP && \ p[1] == CILEN_CBCP && \ p[0] == opt) { \ len -= CILEN_CBCP; \ INCPTR(2, p); \ GETCHAR(cichar, p); \ /* Check rejected value. */ \ if (cichar != val) \ goto bad; \ try.neg = 0; \ } #define REJCIENDP(opt, neg, class, val, vlen) \ if (go->neg && \ len >= CILEN_CHAR + vlen && \ p[0] == opt && \ p[1] == CILEN_CHAR + vlen) { \ int i; \ len -= CILEN_CHAR + vlen; \ INCPTR(2, p); \ GETCHAR(cichar, p); \ if (cichar != class) \ goto bad; \ for (i = 0; i < vlen; ++i) { \ GETCHAR(cichar, p); \ if (cichar != val[i]) \ goto bad; \ } \ try.neg = 0; \ } REJCISHORT(CI_MRU, neg_mru, go->mru); REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); REJCISHORT(CI_AUTHTYPE, neg_eap, PPP_EAP); if (!go->neg_eap) { REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype); if (!go->neg_chap) { REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); } } REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); REJCIVOID(CI_PCOMPRESSION, neg_pcompression); REJCIVOID(CI_ACCOMPRESSION, neg_accompression); REJCISHORT(CI_MRRU, neg_mrru, go->mrru); REJCIVOID(CI_SSNHF, neg_ssnhf); REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class, go->endpoint.value, go->endpoint.length); /* * 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(("lcp_rejci: received bad Reject!")); 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 */ int cilen, citype, cichar; /* Parsed len, type, char value */ u_short cishort; /* Parsed short value */ u_int32_t 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 *rejp; /* Pointer to next char in reject frame */ u_char *nakp; /* Pointer to next char in Nak frame */ int l = *lenp; /* Length left */ /* * Reset all his options. */ BZERO(ho, sizeof(*ho)); /* * Process all his options. */ next = inp; nakp = nak_buffer; rejp = 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(("lcp_reqci: bad CI length!")); orc = CONFREJ; /* Reject bad CI */ cilen = l; /* Reject till end of packet */ l = 0; /* Don't loop again */ citype = 0; 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: if (!ao->neg_mru || /* Allow option? */ cilen != CILEN_SHORT) { /* Check CI length */ orc = CONFREJ; /* Reject CI */ break; } GETSHORT(cishort, p); /* Parse MRU */ /* * 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 */ PUTCHAR(CI_MRU, nakp); PUTCHAR(CILEN_SHORT, nakp); PUTSHORT(MINMRU, nakp); /* Give him a hint */ break; } ho->neg_mru = 1; /* Remember he sent MRU */ ho->mru = cishort; /* And remember value */ break; case CI_ASYNCMAP: if (!ao->neg_asyncmap || cilen != CILEN_LONG) { orc = CONFREJ; break; } GETLONG(cilong, p); /* * Asyncmap must have set at least the bits * which are set in lcp_allowoptions[unit].asyncmap. */ if ((ao->asyncmap & ~cilong) != 0) { orc = CONFNAK; PUTCHAR(CI_ASYNCMAP, nakp); PUTCHAR(CILEN_LONG, nakp); PUTLONG(ao->asyncmap | cilong, nakp); break; } ho->neg_asyncmap = 1; ho->asyncmap = cilong; break; case CI_AUTHTYPE: if (cilen < CILEN_SHORT || !(ao->neg_upap || ao->neg_chap || ao->neg_eap)) { /* * Reject the option if we're not willing to authenticate. */ dbglog("No auth is possible"); orc = CONFREJ; break; } GETSHORT(cishort, p); /* * Authtype must be PAP, CHAP, or EAP. * * Note: if more than one of ao->neg_upap, ao->neg_chap, and * ao->neg_eap are set, and the peer sends a Configure-Request * with two or more authenticate-protocol requests, then we will * reject the second request. * Whether we end up doing CHAP, UPAP, or EAP depends then on * the ordering of the CIs in the peer's Configure-Request. */ if (cishort == PPP_PAP) { /* we've already accepted CHAP or EAP */ if (ho->neg_chap || ho->neg_eap || cilen != CILEN_SHORT) { LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting...")); orc = CONFREJ; break; } if (!ao->neg_upap) { /* we don't want to do PAP */ orc = CONFNAK; /* NAK it and suggest CHAP or EAP */ PUTCHAR(CI_AUTHTYPE, nakp); if (ao->neg_eap) { PUTCHAR(CILEN_SHORT, nakp); PUTSHORT(PPP_EAP, nakp); } else { PUTCHAR(CILEN_CHAP, nakp); PUTSHORT(PPP_CHAP, nakp); PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp); } break; } ho->neg_upap = 1; break; } if (cishort == PPP_CHAP) { /* we've already accepted PAP or EAP */ if (ho->neg_upap || ho->neg_eap || cilen != CILEN_CHAP) { LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting...")); orc = CONFREJ; break; } if (!ao->neg_chap) { /* we don't want to do CHAP */ orc = CONFNAK; /* NAK it and suggest EAP or PAP */ PUTCHAR(CI_AUTHTYPE, nakp); PUTCHAR(CILEN_SHORT, nakp); if (ao->neg_eap) { PUTSHORT(PPP_EAP, nakp); } else { PUTSHORT(PPP_PAP, nakp); } break; } GETCHAR(cichar, p); /* get digest type */ if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) { /* * We can't/won't do the requested type, * suggest something else. */ orc = CONFNAK; PUTCHAR(CI_AUTHTYPE, nakp); PUTCHAR(CILEN_CHAP, nakp); PUTSHORT(PPP_CHAP, nakp); PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp); break; } ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */ ho->neg_chap = 1; break; } if (cishort == PPP_EAP) { /* we've already accepted CHAP or PAP */ if (ho->neg_chap || ho->neg_upap || cilen != CILEN_SHORT) { LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting...")); orc = CONFREJ; break; } if (!ao->neg_eap) { /* we don't want to do EAP */ orc = CONFNAK; /* NAK it and suggest CHAP or PAP */ PUTCHAR(CI_AUTHTYPE, nakp); if (ao->neg_chap) { PUTCHAR(CILEN_CHAP, nakp); PUTSHORT(PPP_CHAP, nakp); PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp); } else { PUTCHAR(CILEN_SHORT, nakp); PUTSHORT(PPP_PAP, nakp); } break; } ho->neg_eap = 1; break; } /* * We don't recognize the protocol they're asking for. * Nak it with something we're willing to do. * (At this point we know ao->neg_upap || ao->neg_chap || * ao->neg_eap.) */ orc = CONFNAK; PUTCHAR(CI_AUTHTYPE, nakp); if (ao->neg_eap) { PUTCHAR(CILEN_SHORT, nakp); PUTSHORT(PPP_EAP, nakp); } else if (ao->neg_chap) { PUTCHAR(CILEN_CHAP, nakp); PUTSHORT(PPP_CHAP, nakp); PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp); } else { PUTCHAR(CILEN_SHORT, nakp); PUTSHORT(PPP_PAP, nakp); } break; case CI_QUALITY: if (!ao->neg_lqr || cilen != CILEN_LQR) { orc = CONFREJ; break; } GETSHORT(cishort, p); GETLONG(cilong, p); /* * Check the protocol and the reporting period. * XXX When should we Nak this, and what with? */ if (cishort != PPP_LQR) { orc = CONFNAK; PUTCHAR(CI_QUALITY, nakp); PUTCHAR(CILEN_LQR, nakp); PUTSHORT(PPP_LQR, nakp); PUTLONG(ao->lqr_period, nakp); break; } break; case CI_MAGICNUMBER: if (!(ao->neg_magicnumber || go->neg_magicnumber) || cilen != CILEN_LONG) { orc = CONFREJ; break; } GETLONG(cilong, p); /* * He must have a different magic number. */ if (go->neg_magicnumber && cilong == go->magicnumber) { cilong = magic(); /* Don't put magic() inside macro! */ orc = CONFNAK; PUTCHAR(CI_MAGICNUMBER, nakp); PUTCHAR(CILEN_LONG, nakp); PUTLONG(cilong, nakp); break; } ho->neg_magicnumber = 1; ho->magicnumber = cilong; break; case CI_PCOMPRESSION: if (!ao->neg_pcompression || cilen != CILEN_VOID) { orc = CONFREJ; break; } ho->neg_pcompression = 1; break; case CI_ACCOMPRESSION: if (!ao->neg_accompression || cilen != CILEN_VOID) { orc = CONFREJ; break; } ho->neg_accompression = 1; break; case CI_MRRU: if (!ao->neg_mrru || !multilink || cilen != CILEN_SHORT) { orc = CONFREJ; break; } GETSHORT(cishort, p); /* possibly should insist on a minimum/maximum MRRU here */ ho->neg_mrru = 1; ho->mrru = cishort; break; case CI_SSNHF: if (!ao->neg_ssnhf || !multilink || cilen != CILEN_VOID) { orc = CONFREJ; break; } ho->neg_ssnhf = 1; break; case CI_EPDISC: if (!ao->neg_endpoint || cilen < CILEN_CHAR || cilen > CILEN_CHAR + MAX_ENDP_LEN) { orc = CONFREJ; break; } GETCHAR(cichar, p); cilen -= CILEN_CHAR; ho->neg_endpoint = 1; ho->endpoint.class = cichar; ho->endpoint.length = cilen; BCOPY(p, ho->endpoint.value, cilen); INCPTR(cilen, p); break; default: LCPDEBUG(("lcp_reqci: rcvd unknown option %d", citype)); orc = CONFREJ; break; } endswitch: 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? */ && citype != CI_MAGICNUMBER) { orc = CONFREJ; /* Get tough if so */ } else { if (rc == CONFREJ) /* Rejecting prior CI? */ continue; /* Don't send this one */ rc = CONFNAK; } } if (orc == CONFREJ) { /* Reject this CI */ rc = CONFREJ; if (cip != rejp) /* Need to move rejected CI? */ BCOPY(cip, rejp, cilen); /* Move it */ INCPTR(cilen, rejp); /* Update output pointer */ } } /* * If we wanted to send additional NAKs (for unsent CIs), the * code would go here. The extra NAKs would go at *nakp. * At present there are no cases where we want to ask the * peer to negotiate an option. */ switch (rc) { case CONFACK: *lenp = next - inp; break; case CONFNAK: /* * Copy the Nak'd options from the nak_buffer to the caller's buffer. */ *lenp = nakp - nak_buffer; BCOPY(nak_buffer, inp, *lenp); break; case CONFREJ: *lenp = rejp - inp; break; } LCPDEBUG(("lcp_reqci: returning CONF%s.", CODENAME(rc))); return (rc); /* Return final code */ } /* * lcp_up - LCP has come UP. */ 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]; int mtu, mru; 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. * Note on the MTU: the link MTU can be the MRU the peer wanted, * the interface MTU is set to the lowest of that, the * MTU we want to use, and our link MRU. */ mtu = ho->neg_mru? ho->mru: PPP_MRU; mru = go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU; #ifdef HAVE_MULTILINK if (!(multilink && go->neg_mrru && ho->neg_mrru)) #endif /* HAVE_MULTILINK */ netif_set_mtu(f->unit, MIN(MIN(mtu, mru), ao->mru)); ppp_send_config(f->unit, mtu, (ho->neg_asyncmap? ho->asyncmap: 0xffffffff), ho->neg_pcompression, ho->neg_accompression); ppp_recv_config(f->unit, mru, (lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff), go->neg_pcompression, go->neg_accompression); if (ho->neg_mru) peer_mru[f->unit] = ho->mru; 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_options *go = &lcp_gotoptions[f->unit]; lcp_echo_lowerdown(f->unit); link_down(f->unit); ppp_send_config(f->unit, PPP_MRU, 0xffffffff, 0, 0); ppp_recv_config(f->unit, PPP_MRU, (go->neg_asyncmap? go->asyncmap: 0xffffffff), go->neg_pcompression, go->neg_accompression); peer_mru[f->unit] = PPP_MRU; } /* * 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. */ static char *lcp_codenames[] = { "ConfReq", "ConfAck", "ConfNak", "ConfRej", "TermReq", "TermAck", "CodeRej", "ProtRej", "EchoReq", "EchoRep", "DiscReq", "Ident", "TimeRem" }; static int lcp_printpkt(p, plen, printer, arg) u_char *p; int plen; void (*printer) __P((void *, char *, ...)); void *arg; { int code, id, len, olen, i; u_char *pstart, *optend; u_short cishort; u_int32_t 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 PPP_PAP: printer(arg, "pap"); break; case PPP_CHAP: printer(arg, "chap"); if (p < optend) { switch (*p) { case CHAP_MD5: printer(arg, " MD5"); ++p; break; case CHAP_MICROSOFT: printer(arg, " MS"); ++p; break; case CHAP_MICROSOFT_V2: printer(arg, " MS-v2"); ++p; break; } } break; case PPP_EAP: printer(arg, "eap"); 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 PPP_LQR: printer(arg, "lqr"); break; default: printer(arg, "0x%x", cishort); } } break; case CI_CALLBACK: if (olen >= CILEN_CHAR) { p += 2; printer(arg, "callback "); GETCHAR(cishort, p); switch (cishort) { case CBCP_OPT: printer(arg, "CBCP"); 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; case CI_MRRU: if (olen == CILEN_SHORT) { p += 2; GETSHORT(cishort, p); printer(arg, "mrru %d", cishort); } break; case CI_SSNHF: if (olen == CILEN_VOID) { p += 2; printer(arg, "ssnhf"); } break; case CI_EPDISC: #ifdef HAVE_MULTILINK if (olen >= CILEN_CHAR) { struct epdisc epd; p += 2; GETCHAR(epd.class, p); epd.length = olen - CILEN_CHAR; if (epd.length > MAX_ENDP_LEN) epd.length = MAX_ENDP_LEN; if (epd.length > 0) { BCOPY(p, epd.value, epd.length); p += epd.length; } printer(arg, "endpoint [%s]", epdisc_to_str(&epd)); } #else printer(arg, "endpoint"); #endif break; } while (p < optend) { GETCHAR(code, p); printer(arg, " %.2x", code); } printer(arg, ">"); } break; case TERMACK: case TERMREQ: if (len > 0 && *p >= ' ' && *p < 0x7f) { printer(arg, " "); print_string((char *)p, len, printer, arg); p += len; len = 0; } break; case ECHOREQ: case ECHOREP: case DISCREQ: if (len >= 4) { GETLONG(cilong, p); printer(arg, " magic=0x%x", cilong); len -= 4; } break; case IDENTIF: case TIMEREM: if (len >= 4) { GETLONG(cilong, p); printer(arg, " magic=0x%x", cilong); len -= 4; } if (code == TIMEREM) { if (len < 4) break; GETLONG(cilong, p); printer(arg, " seconds=%u", cilong); len -= 4; } if (len > 0) { printer(arg, " "); print_string((char *)p, len, printer, arg); p += len; len = 0; } break; } /* print the rest of the bytes in the packet */ for (i = 0; i < len && i < 32; ++i) { GETCHAR(code, p); printer(arg, " %.2x", code); } if (i < len) { printer(arg, " ..."); p += len - i; } 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) { info("No response to %d echo-requests", lcp_echos_pending); notice("Serial link appears to be disconnected."); status = EXIT_PEER_DEAD; lcp_close(f->unit, "Peer not responding"); } } /* * Timer expired for the LCP echo requests from this process. */ static void LcpEchoCheck (f) fsm *f; { LcpSendEchoRequest (f); if (f->state != OPENED) return; /* * Start the timer for the next interval. */ if (lcp_echo_timer_running) warn("assertion lcp_echo_timer_running==0 failed"); TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval); lcp_echo_timer_running = 1; } /* * LcpEchoTimeout - Timer expired on the LCP echo */ static void LcpEchoTimeout (arg) void *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_int32_t magic; /* Check the magic number - don't count replies from ourselves. */ if (len < 4) { dbglog("lcp: received short Echo-Reply, length %d", len); return; } GETLONG(magic, inp); if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) { warn("appear to have received our own echo-reply!"); 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_int32_t 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].magicnumber; pktp = pkt; PUTLONG(lcp_magic, pktp); fsm_sdata(f, ECHOREQ, lcp_echo_number++ & 0xFF, pkt, pktp - pkt); ++lcp_echos_pending; } } /* * 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, f); lcp_echo_timer_running = 0; } } ppp-2.4.5/pppd/lcp.h000066400000000000000000000127531130035057700142240ustar00rootroot00000000000000/* * lcp.h - Link Control Protocol definitions. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: lcp.h,v 1.20 2004/11/14 22:53:42 carlsonj Exp $ */ /* * Options. */ #define CI_VENDOR 0 /* Vendor Specific */ #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 */ #define CI_FCSALTERN 9 /* FCS-Alternatives */ #define CI_SDP 10 /* Self-Describing-Pad */ #define CI_NUMBERED 11 /* Numbered-Mode */ #define CI_CALLBACK 13 /* callback */ #define CI_MRRU 17 /* max reconstructed receive unit; multilink */ #define CI_SSNHF 18 /* short sequence numbers for multilink */ #define CI_EPDISC 19 /* endpoint discriminator */ #define CI_MPPLUS 22 /* Multi-Link-Plus-Procedure */ #define CI_LDISC 23 /* Link-Discriminator */ #define CI_LCPAUTH 24 /* LCP Authentication */ #define CI_COBS 25 /* Consistent Overhead Byte Stuffing */ #define CI_PREFELIS 26 /* Prefix Elision */ #define CI_MPHDRFMT 27 /* MP Header Format */ #define CI_I18N 28 /* Internationalization */ #define CI_SDL 29 /* Simple Data Link */ /* * LCP-specific packet types (code numbers). */ #define PROTREJ 8 /* Protocol Reject */ #define ECHOREQ 9 /* Echo Request */ #define ECHOREP 10 /* Echo Reply */ #define DISCREQ 11 /* Discard Request */ #define IDENTIF 12 /* Identification */ #define TIMEREM 13 /* Time Remaining */ /* Value used as data for CI_CALLBACK option */ #define CBCP_OPT 6 /* Use callback control protocol */ /* * The state of options is described by an lcp_options structure. */ typedef struct lcp_options { bool passive; /* Don't die if we don't get a response */ bool silent; /* Wait for the other end to start first */ bool restart; /* Restart vs. exit after close */ bool neg_mru; /* Negotiate the MRU? */ bool neg_asyncmap; /* Negotiate the async map? */ bool neg_upap; /* Ask for UPAP authentication? */ bool neg_chap; /* Ask for CHAP authentication? */ bool neg_eap; /* Ask for EAP authentication? */ bool neg_magicnumber; /* Ask for magic number? */ bool neg_pcompression; /* HDLC Protocol Field Compression? */ bool neg_accompression; /* HDLC Address/Control Field Compression? */ bool neg_lqr; /* Negotiate use of Link Quality Reports */ bool neg_cbcp; /* Negotiate use of CBCP */ bool neg_mrru; /* negotiate multilink MRRU */ bool neg_ssnhf; /* negotiate short sequence numbers */ bool neg_endpoint; /* negotiate endpoint discriminator */ int mru; /* Value of MRU */ int mrru; /* Value of MRRU, and multilink enable */ u_char chap_mdtype; /* which MD types (hashing algorithm) */ u_int32_t asyncmap; /* Value of async map */ u_int32_t magicnumber; int numloops; /* Number of loops during magic number neg. */ u_int32_t lqr_period; /* Reporting period for LQR 1/100ths second */ struct epdisc endpoint; /* endpoint discriminator */ } lcp_options; extern fsm lcp_fsm[]; extern lcp_options lcp_wantoptions[]; extern lcp_options lcp_gotoptions[]; extern lcp_options lcp_allowoptions[]; extern lcp_options lcp_hisoptions[]; #define DEFMRU 1500 /* Try for this */ #define MINMRU 128 /* No MRUs below this */ #define MAXMRU 16384 /* Normally limit MRU to this */ void lcp_open __P((int)); void lcp_close __P((int, char *)); void lcp_lowerup __P((int)); void lcp_lowerdown __P((int)); void lcp_sprotrej __P((int, u_char *, int)); /* send protocol reject */ extern struct protent lcp_protent; /* Default number of times we receive our magic number from the peer before deciding the link is looped-back. */ #define DEFLOOPBACKFAIL 10 ppp-2.4.5/pppd/magic.c000066400000000000000000000060731130035057700145170ustar00rootroot00000000000000/* * magic.c - PPP Magic Number routines. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: magic.c,v 1.11 2003/06/11 23:56:26 paulus Exp $" #include #include #include #include #include #include "pppd.h" #include "magic.h" static const char rcsid[] = RCSID; extern long mrand48 __P((void)); extern void srand48 __P((long)); /* * magic_init - Initialize the magic number generator. * * Attempts to compute a random number seed which will not repeat. * The current method uses the current hostid, current process ID * and current time, currently. */ void magic_init() { long seed; struct timeval t; gettimeofday(&t, NULL); seed = get_host_seed() ^ t.tv_sec ^ t.tv_usec ^ getpid(); srand48(seed); } /* * magic - Returns the next magic number. */ u_int32_t magic() { return (u_int32_t) mrand48(); } /* * random_bytes - Fill a buffer with random bytes. */ void random_bytes(unsigned char *buf, int len) { int i; for (i = 0; i < len; ++i) buf[i] = mrand48() >> 24; } #ifdef NO_DRAND48 /* * Substitute procedures for those systems which don't have * drand48 et al. */ double drand48() { return (double)random() / (double)0x7fffffffL; /* 2**31-1 */ } long mrand48() { return random(); } void srand48(seedval) long seedval; { srandom((int)seedval); } #endif ppp-2.4.5/pppd/magic.h000066400000000000000000000041061130035057700145170ustar00rootroot00000000000000/* * magic.h - PPP Magic Number definitions. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: magic.h,v 1.5 2003/06/11 23:56:26 paulus Exp $ */ void magic_init __P((void)); /* Initialize the magic number generator */ u_int32_t magic __P((void)); /* Returns the next magic number */ /* Fill buffer with random bytes */ void random_bytes __P((unsigned char *buf, int len)); ppp-2.4.5/pppd/main.c000066400000000000000000001467231130035057700143720ustar00rootroot00000000000000/* * main.c - Point-to-Point Protocol main module * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Copyright (c) 1999-2004 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: main.c,v 1.156 2008/06/23 11:47:18 paulus Exp $" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pppd.h" #include "magic.h" #include "fsm.h" #include "lcp.h" #include "ipcp.h" #ifdef INET6 #include "ipv6cp.h" #endif #include "upap.h" #include "chap-new.h" #include "eap.h" #include "ccp.h" #include "ecp.h" #include "pathnames.h" #ifdef USE_TDB #include "tdb.h" #endif #ifdef CBCP_SUPPORT #include "cbcp.h" #endif #ifdef IPX_CHANGE #include "ipxcp.h" #endif /* IPX_CHANGE */ #ifdef AT_CHANGE #include "atcp.h" #endif static const char rcsid[] = RCSID; /* interface vars */ char ifname[32]; /* Interface name */ int ifunit; /* Interface unit number */ struct channel *the_channel; char *progname; /* Name of this program */ char hostname[MAXNAMELEN]; /* Our hostname */ static char pidfilename[MAXPATHLEN]; /* name of pid file */ static char linkpidfile[MAXPATHLEN]; /* name of linkname pid file */ char ppp_devnam[MAXPATHLEN]; /* name of PPP tty (maybe ttypx) */ uid_t uid; /* Our real user-id */ struct notifier *pidchange = NULL; struct notifier *phasechange = NULL; struct notifier *exitnotify = NULL; struct notifier *sigreceived = NULL; struct notifier *fork_notifier = NULL; int hungup; /* terminal has been hung up */ int privileged; /* we're running as real uid root */ int need_holdoff; /* need holdoff period before restarting */ int detached; /* have detached from terminal */ volatile int status; /* exit status for pppd */ int unsuccess; /* # unsuccessful connection attempts */ int do_callback; /* != 0 if we should do callback next */ int doing_callback; /* != 0 if we are doing callback */ int ppp_session_number; /* Session number, for channels with such a concept (eg PPPoE) */ int childwait_done; /* have timed out waiting for children */ #ifdef USE_TDB TDB_CONTEXT *pppdb; /* database for storing status etc. */ #endif char db_key[32]; int (*holdoff_hook) __P((void)) = NULL; int (*new_phase_hook) __P((int)) = NULL; void (*snoop_recv_hook) __P((unsigned char *p, int len)) = NULL; void (*snoop_send_hook) __P((unsigned char *p, int len)) = NULL; static int conn_running; /* we have a [dis]connector running */ static int fd_loop; /* fd for getting demand-dial packets */ int fd_devnull; /* fd for /dev/null */ int devfd = -1; /* fd of underlying device */ int fd_ppp = -1; /* fd for talking PPP */ int phase; /* where the link is at */ int kill_link; int asked_to_quit; int open_ccp_flag; int listen_time; int got_sigusr2; int got_sigterm; int got_sighup; static sigset_t signals_handled; static int waiting; static sigjmp_buf sigjmp; char **script_env; /* Env. variable values for scripts */ int s_env_nalloc; /* # words avail at script_env */ u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */ u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */ static int n_children; /* # child processes still running */ static int got_sigchld; /* set if we have received a SIGCHLD */ int privopen; /* don't lock, open device as root */ char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n"; GIDSET_TYPE groups[NGROUPS_MAX];/* groups the user is in */ int ngroups; /* How many groups valid in groups */ static struct timeval start_time; /* Time when link was started. */ static struct pppd_stats old_link_stats; struct pppd_stats link_stats; unsigned link_connect_time; int link_stats_valid; int error_count; bool bundle_eof; bool bundle_terminating; /* * We maintain a list of child process pids and * functions to call when they exit. */ struct subprocess { pid_t pid; char *prog; void (*done) __P((void *)); void *arg; int killable; struct subprocess *next; }; static struct subprocess *children; /* Prototypes for procedures local to this file. */ static void setup_signals __P((void)); static void create_pidfile __P((int pid)); static void create_linkpidfile __P((int pid)); static void cleanup __P((void)); static void get_input __P((void)); static void calltimeout __P((void)); static struct timeval *timeleft __P((struct timeval *)); static void kill_my_pg __P((int)); static void hup __P((int)); static void term __P((int)); static void chld __P((int)); static void toggle_debug __P((int)); static void open_ccp __P((int)); static void bad_signal __P((int)); static void holdoff_end __P((void *)); static void forget_child __P((int pid, int status)); static int reap_kids __P((void)); static void childwait_end __P((void *)); #ifdef USE_TDB static void update_db_entry __P((void)); static void add_db_key __P((const char *)); static void delete_db_key __P((const char *)); static void cleanup_db __P((void)); #endif static void handle_events __P((void)); void print_link_stats __P((void)); extern char *ttyname __P((int)); extern char *getlogin __P((void)); int main __P((int, char *[])); #ifdef ultrix #undef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif #ifdef ULTRIX #define setlogmask(x) #endif /* * PPP Data Link Layer "protocol" table. * One entry per supported protocol. * The last entry must be NULL. */ struct protent *protocols[] = { &lcp_protent, &pap_protent, &chap_protent, #ifdef CBCP_SUPPORT &cbcp_protent, #endif &ipcp_protent, #ifdef INET6 &ipv6cp_protent, #endif &ccp_protent, &ecp_protent, #ifdef IPX_CHANGE &ipxcp_protent, #endif #ifdef AT_CHANGE &atcp_protent, #endif &eap_protent, NULL }; /* * If PPP_DRV_NAME is not defined, use the default "ppp" as the device name. */ #if !defined(PPP_DRV_NAME) #define PPP_DRV_NAME "ppp" #endif /* !defined(PPP_DRV_NAME) */ int main(argc, argv) int argc; char *argv[]; { int i, t; char *p; struct passwd *pw; struct protent *protp; char numbuf[16]; link_stats_valid = 0; new_phase(PHASE_INITIALIZE); script_env = NULL; /* Initialize syslog facilities */ reopen_log(); if (gethostname(hostname, MAXNAMELEN) < 0 ) { option_error("Couldn't get hostname: %m"); exit(1); } hostname[MAXNAMELEN-1] = 0; /* make sure we don't create world or group writable files. */ umask(umask(0777) | 022); uid = getuid(); privileged = uid == 0; slprintf(numbuf, sizeof(numbuf), "%d", uid); script_setenv("ORIG_UID", numbuf, 0); ngroups = getgroups(NGROUPS_MAX, groups); /* * Initialize magic number generator now so that protocols may * use magic numbers in initialization. */ magic_init(); /* * Initialize each protocol. */ for (i = 0; (protp = protocols[i]) != NULL; ++i) (*protp->init)(0); /* * Initialize the default channel. */ tty_init(); progname = *argv; /* * Parse, in order, the system options file, the user's options file, * and the command line arguments. */ if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1) || !options_from_user() || !parse_args(argc-1, argv+1)) exit(EXIT_OPTION_ERROR); devnam_fixed = 1; /* can no longer change device name */ /* * Work out the device name, if it hasn't already been specified, * and parse the tty's options file. */ if (the_channel->process_extra_options) (*the_channel->process_extra_options)(); if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); /* * Check that we are running as root. */ if (geteuid() != 0) { option_error("must be root to run %s, since it is not setuid-root", argv[0]); exit(EXIT_NOT_ROOT); } if (!ppp_available()) { option_error("%s", no_ppp_msg); exit(EXIT_NO_KERNEL_SUPPORT); } /* * Check that the options given are valid and consistent. */ check_options(); if (!sys_check_options()) exit(EXIT_OPTION_ERROR); auth_check_options(); #ifdef HAVE_MULTILINK mp_check_options(); #endif for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->check_options != NULL) (*protp->check_options)(); if (the_channel->check_options) (*the_channel->check_options)(); if (dump_options || dryrun) { init_pr_log(NULL, LOG_INFO); print_options(pr_log, NULL); end_pr_log(); } if (dryrun) die(0); /* Make sure fds 0, 1, 2 are open to somewhere. */ fd_devnull = open(_PATH_DEVNULL, O_RDWR); if (fd_devnull < 0) fatal("Couldn't open %s: %m", _PATH_DEVNULL); while (fd_devnull <= 2) { i = dup(fd_devnull); if (i < 0) fatal("Critical shortage of file descriptors: dup failed: %m"); fd_devnull = i; } /* * Initialize system-dependent stuff. */ sys_init(); #ifdef USE_TDB pppdb = tdb_open(_PATH_PPPDB, 0, 0, O_RDWR|O_CREAT, 0644); if (pppdb != NULL) { slprintf(db_key, sizeof(db_key), "pppd%d", getpid()); update_db_entry(); } else { warn("Warning: couldn't open ppp database %s", _PATH_PPPDB); if (multilink) { warn("Warning: disabling multilink"); multilink = 0; } } #endif /* * Detach ourselves from the terminal, if required, * and identify who is running us. */ if (!nodetach && !updetach) detach(); 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 started by %s, uid %d", VERSION, p, uid); script_setenv("PPPLOGNAME", p, 0); if (devnam[0]) script_setenv("DEVICE", devnam, 1); slprintf(numbuf, sizeof(numbuf), "%d", getpid()); script_setenv("PPPD_PID", numbuf, 1); setup_signals(); create_linkpidfile(getpid()); waiting = 0; /* * If we're doing dial-on-demand, set up the interface now. */ if (demand) { /* * Open the loopback channel and set it up to be the ppp interface. */ fd_loop = open_ppp_loopback(); set_ifunit(1); /* * Configure the interface and mark it up, etc. */ demand_conf(); } do_callback = 0; for (;;) { bundle_eof = 0; bundle_terminating = 0; listen_time = 0; need_holdoff = 1; devfd = -1; status = EXIT_OK; ++unsuccess; doing_callback = do_callback; do_callback = 0; if (demand && !doing_callback) { /* * Don't do anything until we see some activity. */ new_phase(PHASE_DORMANT); demand_unblock(); add_fd(fd_loop); for (;;) { handle_events(); if (asked_to_quit) break; if (get_loop_output()) break; } remove_fd(fd_loop); if (asked_to_quit) break; /* * Now we want to bring up the link. */ demand_block(); info("Starting link"); } gettimeofday(&start_time, NULL); script_unsetenv("CONNECT_TIME"); script_unsetenv("BYTES_SENT"); script_unsetenv("BYTES_RCVD"); lcp_open(0); /* Start protocol */ start_link(0); while (phase != PHASE_DEAD) { handle_events(); get_input(); if (kill_link) lcp_close(0, "User request"); if (asked_to_quit) { bundle_terminating = 1; if (phase == PHASE_MASTER) mp_bundle_terminated(); } if (open_ccp_flag) { if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) { ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */ (*ccp_protent.open)(0); } } } /* restore FSMs to original state */ lcp_close(0, ""); if (!persist || asked_to_quit || (maxfail > 0 && unsuccess >= maxfail)) break; if (demand) demand_discard(); t = need_holdoff? holdoff: 0; if (holdoff_hook) t = (*holdoff_hook)(); if (t > 0) { new_phase(PHASE_HOLDOFF); TIMEOUT(holdoff_end, NULL, t); do { handle_events(); if (kill_link) new_phase(PHASE_DORMANT); /* allow signal to end holdoff */ } while (phase == PHASE_HOLDOFF); if (!persist) break; } } /* Wait for scripts to finish */ reap_kids(); if (n_children > 0) { if (child_wait > 0) TIMEOUT(childwait_end, NULL, child_wait); if (debug) { struct subprocess *chp; dbglog("Waiting for %d child processes...", n_children); for (chp = children; chp != NULL; chp = chp->next) dbglog(" script %s, pid %d", chp->prog, chp->pid); } while (n_children > 0 && !childwait_done) { handle_events(); if (kill_link && !childwait_done) childwait_end(NULL); } } die(status); return 0; } /* * handle_events - wait for something to happen and respond to it. */ static void handle_events() { struct timeval timo; kill_link = open_ccp_flag = 0; if (sigsetjmp(sigjmp, 1) == 0) { sigprocmask(SIG_BLOCK, &signals_handled, NULL); if (got_sighup || got_sigterm || got_sigusr2 || got_sigchld) { sigprocmask(SIG_UNBLOCK, &signals_handled, NULL); } else { waiting = 1; sigprocmask(SIG_UNBLOCK, &signals_handled, NULL); wait_input(timeleft(&timo)); } } waiting = 0; calltimeout(); if (got_sighup) { info("Hangup (SIGHUP)"); kill_link = 1; got_sighup = 0; if (status != EXIT_HANGUP) status = EXIT_USER_REQUEST; } if (got_sigterm) { info("Terminating on signal %d", got_sigterm); kill_link = 1; asked_to_quit = 1; persist = 0; status = EXIT_USER_REQUEST; got_sigterm = 0; } if (got_sigchld) { got_sigchld = 0; reap_kids(); /* Don't leave dead kids lying around */ } if (got_sigusr2) { open_ccp_flag = 1; got_sigusr2 = 0; } } /* * setup_signals - initialize signal handling. */ static void setup_signals() { struct sigaction sa; /* * 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(&signals_handled); sigaddset(&signals_handled, SIGHUP); sigaddset(&signals_handled, SIGINT); sigaddset(&signals_handled, SIGTERM); sigaddset(&signals_handled, SIGCHLD); sigaddset(&signals_handled, SIGUSR2); #define SIGNAL(s, handler) do { \ sa.sa_handler = handler; \ if (sigaction(s, &sa, NULL) < 0) \ fatal("Couldn't establish signal handler (%d): %m", s); \ } while (0) sa.sa_mask = signals_handled; sa.sa_flags = 0; SIGNAL(SIGHUP, hup); /* Hangup */ SIGNAL(SIGINT, term); /* Interrupt */ SIGNAL(SIGTERM, term); /* Terminate */ SIGNAL(SIGCHLD, chld); SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */ SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */ /* * Install a handler for other signals which would otherwise * cause pppd to exit without cleaning up. */ SIGNAL(SIGABRT, bad_signal); SIGNAL(SIGALRM, bad_signal); SIGNAL(SIGFPE, bad_signal); SIGNAL(SIGILL, bad_signal); SIGNAL(SIGPIPE, bad_signal); SIGNAL(SIGQUIT, bad_signal); SIGNAL(SIGSEGV, bad_signal); #ifdef SIGBUS SIGNAL(SIGBUS, bad_signal); #endif #ifdef SIGEMT SIGNAL(SIGEMT, bad_signal); #endif #ifdef SIGPOLL SIGNAL(SIGPOLL, bad_signal); #endif #ifdef SIGPROF SIGNAL(SIGPROF, bad_signal); #endif #ifdef SIGSYS SIGNAL(SIGSYS, bad_signal); #endif #ifdef SIGTRAP SIGNAL(SIGTRAP, bad_signal); #endif #ifdef SIGVTALRM SIGNAL(SIGVTALRM, bad_signal); #endif #ifdef SIGXCPU SIGNAL(SIGXCPU, bad_signal); #endif #ifdef SIGXFSZ SIGNAL(SIGXFSZ, bad_signal); #endif /* * Apparently we can get a SIGPIPE when we call syslog, if * syslogd has died and been restarted. Ignoring it seems * be sufficient. */ signal(SIGPIPE, SIG_IGN); } /* * set_ifunit - do things we need to do once we know which ppp * unit we are using. */ void set_ifunit(iskey) int iskey; { info("Using interface %s%d", PPP_DRV_NAME, ifunit); slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit); script_setenv("IFNAME", ifname, iskey); if (iskey) { create_pidfile(getpid()); /* write pid to file */ create_linkpidfile(getpid()); } } /* * detach - detach us from the controlling terminal. */ void detach() { int pid; char numbuf[16]; int pipefd[2]; if (detached) return; if (pipe(pipefd) == -1) pipefd[0] = pipefd[1] = -1; if ((pid = fork()) < 0) { error("Couldn't detach (fork failed: %m)"); die(1); /* or just return? */ } if (pid != 0) { /* parent */ notify(pidchange, pid); /* update pid files if they have been written already */ if (pidfilename[0]) create_pidfile(pid); if (linkpidfile[0]) create_linkpidfile(pid); exit(0); /* parent dies */ } setsid(); chdir("/"); dup2(fd_devnull, 0); dup2(fd_devnull, 1); dup2(fd_devnull, 2); detached = 1; if (log_default) log_to_fd = -1; slprintf(numbuf, sizeof(numbuf), "%d", getpid()); script_setenv("PPPD_PID", numbuf, 1); /* wait for parent to finish updating pid & lock files and die */ close(pipefd[1]); complete_read(pipefd[0], numbuf, 1); close(pipefd[0]); } /* * reopen_log - (re)open our connection to syslog. */ void reopen_log() { openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); setlogmask(LOG_UPTO(LOG_INFO)); } /* * Create a file containing our process ID. */ static void create_pidfile(pid) int pid; { FILE *pidfile; slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid", _PATH_VARRUN, ifname); if ((pidfile = fopen(pidfilename, "w")) != NULL) { fprintf(pidfile, "%d\n", pid); (void) fclose(pidfile); } else { error("Failed to create pid file %s: %m", pidfilename); pidfilename[0] = 0; } } void create_linkpidfile(pid) int pid; { FILE *pidfile; if (linkname[0] == 0) return; script_setenv("LINKNAME", linkname, 1); slprintf(linkpidfile, sizeof(linkpidfile), "%sppp-%s.pid", _PATH_VARRUN, linkname); if ((pidfile = fopen(linkpidfile, "w")) != NULL) { fprintf(pidfile, "%d\n", pid); if (ifname[0]) fprintf(pidfile, "%s\n", ifname); (void) fclose(pidfile); } else { error("Failed to create pid file %s: %m", linkpidfile); linkpidfile[0] = 0; } } /* * remove_pidfile - remove our pid files */ void remove_pidfiles() { if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) warn("unable to delete pid file %s: %m", pidfilename); pidfilename[0] = 0; if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT) warn("unable to delete pid file %s: %m", linkpidfile); linkpidfile[0] = 0; } /* * holdoff_end - called via a timeout when the holdoff period ends. */ static void holdoff_end(arg) void *arg; { new_phase(PHASE_DORMANT); } /* List of protocol names, to make our messages a little more informative. */ struct protocol_list { u_short proto; const char *name; } protocol_list[] = { { 0x21, "IP" }, { 0x23, "OSI Network Layer" }, { 0x25, "Xerox NS IDP" }, { 0x27, "DECnet Phase IV" }, { 0x29, "Appletalk" }, { 0x2b, "Novell IPX" }, { 0x2d, "VJ compressed TCP/IP" }, { 0x2f, "VJ uncompressed TCP/IP" }, { 0x31, "Bridging PDU" }, { 0x33, "Stream Protocol ST-II" }, { 0x35, "Banyan Vines" }, { 0x39, "AppleTalk EDDP" }, { 0x3b, "AppleTalk SmartBuffered" }, { 0x3d, "Multi-Link" }, { 0x3f, "NETBIOS Framing" }, { 0x41, "Cisco Systems" }, { 0x43, "Ascom Timeplex" }, { 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" }, { 0x47, "DCA Remote Lan" }, { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" }, { 0x4b, "SNA over 802.2" }, { 0x4d, "SNA" }, { 0x4f, "IP6 Header Compression" }, { 0x51, "KNX Bridging Data" }, { 0x53, "Encryption" }, { 0x55, "Individual Link Encryption" }, { 0x57, "IPv6" }, { 0x59, "PPP Muxing" }, { 0x5b, "Vendor-Specific Network Protocol" }, { 0x61, "RTP IPHC Full Header" }, { 0x63, "RTP IPHC Compressed TCP" }, { 0x65, "RTP IPHC Compressed non-TCP" }, { 0x67, "RTP IPHC Compressed UDP 8" }, { 0x69, "RTP IPHC Compressed RTP 8" }, { 0x6f, "Stampede Bridging" }, { 0x73, "MP+" }, { 0xc1, "NTCITS IPI" }, { 0xfb, "single-link compression" }, { 0xfd, "Compressed Datagram" }, { 0x0201, "802.1d Hello Packets" }, { 0x0203, "IBM Source Routing BPDU" }, { 0x0205, "DEC LANBridge100 Spanning Tree" }, { 0x0207, "Cisco Discovery Protocol" }, { 0x0209, "Netcs Twin Routing" }, { 0x020b, "STP - Scheduled Transfer Protocol" }, { 0x020d, "EDP - Extreme Discovery Protocol" }, { 0x0211, "Optical Supervisory Channel Protocol" }, { 0x0213, "Optical Supervisory Channel Protocol" }, { 0x0231, "Luxcom" }, { 0x0233, "Sigma Network Systems" }, { 0x0235, "Apple Client Server Protocol" }, { 0x0281, "MPLS Unicast" }, { 0x0283, "MPLS Multicast" }, { 0x0285, "IEEE p1284.4 standard - data packets" }, { 0x0287, "ETSI TETRA Network Protocol Type 1" }, { 0x0289, "Multichannel Flow Treatment Protocol" }, { 0x2063, "RTP IPHC Compressed TCP No Delta" }, { 0x2065, "RTP IPHC Context State" }, { 0x2067, "RTP IPHC Compressed UDP 16" }, { 0x2069, "RTP IPHC Compressed RTP 16" }, { 0x4001, "Cray Communications Control Protocol" }, { 0x4003, "CDPD Mobile Network Registration Protocol" }, { 0x4005, "Expand accelerator protocol" }, { 0x4007, "ODSICP NCP" }, { 0x4009, "DOCSIS DLL" }, { 0x400B, "Cetacean Network Detection Protocol" }, { 0x4021, "Stacker LZS" }, { 0x4023, "RefTek Protocol" }, { 0x4025, "Fibre Channel" }, { 0x4027, "EMIT Protocols" }, { 0x405b, "Vendor-Specific Protocol (VSP)" }, { 0x8021, "Internet Protocol Control Protocol" }, { 0x8023, "OSI Network Layer Control Protocol" }, { 0x8025, "Xerox NS IDP Control Protocol" }, { 0x8027, "DECnet Phase IV Control Protocol" }, { 0x8029, "Appletalk Control Protocol" }, { 0x802b, "Novell IPX Control Protocol" }, { 0x8031, "Bridging NCP" }, { 0x8033, "Stream Protocol Control Protocol" }, { 0x8035, "Banyan Vines Control Protocol" }, { 0x803d, "Multi-Link Control Protocol" }, { 0x803f, "NETBIOS Framing Control Protocol" }, { 0x8041, "Cisco Systems Control Protocol" }, { 0x8043, "Ascom Timeplex" }, { 0x8045, "Fujitsu LBLB Control Protocol" }, { 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" }, { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" }, { 0x804b, "SNA over 802.2 Control Protocol" }, { 0x804d, "SNA Control Protocol" }, { 0x804f, "IP6 Header Compression Control Protocol" }, { 0x8051, "KNX Bridging Control Protocol" }, { 0x8053, "Encryption Control Protocol" }, { 0x8055, "Individual Link Encryption Control Protocol" }, { 0x8057, "IPv6 Control Protocol" }, { 0x8059, "PPP Muxing Control Protocol" }, { 0x805b, "Vendor-Specific Network Control Protocol (VSNCP)" }, { 0x806f, "Stampede Bridging Control Protocol" }, { 0x8073, "MP+ Control Protocol" }, { 0x80c1, "NTCITS IPI Control Protocol" }, { 0x80fb, "Single Link Compression Control Protocol" }, { 0x80fd, "Compression Control Protocol" }, { 0x8207, "Cisco Discovery Protocol Control" }, { 0x8209, "Netcs Twin Routing" }, { 0x820b, "STP - Control Protocol" }, { 0x820d, "EDPCP - Extreme Discovery Protocol Ctrl Prtcl" }, { 0x8235, "Apple Client Server Protocol Control" }, { 0x8281, "MPLSCP" }, { 0x8285, "IEEE p1284.4 standard - Protocol Control" }, { 0x8287, "ETSI TETRA TNP1 Control Protocol" }, { 0x8289, "Multichannel Flow Treatment Protocol" }, { 0xc021, "Link Control Protocol" }, { 0xc023, "Password Authentication Protocol" }, { 0xc025, "Link Quality Report" }, { 0xc027, "Shiva Password Authentication Protocol" }, { 0xc029, "CallBack Control Protocol (CBCP)" }, { 0xc02b, "BACP Bandwidth Allocation Control Protocol" }, { 0xc02d, "BAP" }, { 0xc05b, "Vendor-Specific Authentication Protocol (VSAP)" }, { 0xc081, "Container Control Protocol" }, { 0xc223, "Challenge Handshake Authentication Protocol" }, { 0xc225, "RSA Authentication Protocol" }, { 0xc227, "Extensible Authentication Protocol" }, { 0xc229, "Mitsubishi Security Info Exch Ptcl (SIEP)" }, { 0xc26f, "Stampede Bridging Authorization Protocol" }, { 0xc281, "Proprietary Authentication Protocol" }, { 0xc283, "Proprietary Authentication Protocol" }, { 0xc481, "Proprietary Node ID Authentication Protocol" }, { 0, NULL }, }; /* * protocol_name - find a name for a PPP protocol. */ const char * protocol_name(proto) int proto; { struct protocol_list *lp; for (lp = protocol_list; lp->proto != 0; ++lp) if (proto == lp->proto) return lp->name; return NULL; } /* * get_input - called when incoming data is available. */ static void get_input() { int len, i; u_char *p; u_short protocol; struct protent *protp; p = inpacket_buf; /* point to beginning of packet buffer */ len = read_packet(inpacket_buf); if (len < 0) return; if (len == 0) { if (bundle_eof && multilink_master) { notice("Last channel has disconnected"); mp_bundle_terminated(); return; } notice("Modem hangup"); hungup = 1; status = EXIT_HANGUP; lcp_lowerdown(0); /* serial link is no longer available */ link_terminated(0); return; } if (len < PPP_HDRLEN) { dbglog("received short packet:%.*B", len, p); return; } dump_packet("rcvd", p, len); if (snoop_recv_hook) snoop_recv_hook(p, len); p += 2; /* Skip address and control */ GETSHORT(protocol, p); len -= PPP_HDRLEN; /* * Toss all non-LCP packets unless LCP is OPEN. */ if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) { dbglog("Discarded non-LCP packet when LCP not open"); return; } /* * Until we get past the authentication phase, toss all packets * except LCP, LQR and authentication packets. */ if (phase <= PHASE_AUTHENTICATE && !(protocol == PPP_LCP || protocol == PPP_LQR || protocol == PPP_PAP || protocol == PPP_CHAP || protocol == PPP_EAP)) { dbglog("discarding proto 0x%x in phase %d", protocol, phase); return; } /* * Upcall the proper protocol input routine. */ for (i = 0; (protp = protocols[i]) != NULL; ++i) { if (protp->protocol == protocol && protp->enabled_flag) { (*protp->input)(0, p, len); return; } if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag && protp->datainput != NULL) { (*protp->datainput)(0, p, len); return; } } if (debug) { const char *pname = protocol_name(protocol); if (pname != NULL) warn("Unsupported protocol '%s' (0x%x) received", pname, protocol); else warn("Unsupported protocol 0x%x received", protocol); } lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN); } /* * ppp_send_config - configure the transmit-side characteristics of * the ppp interface. Returns -1, indicating an error, if the channel * send_config procedure called error() (or incremented error_count * itself), otherwise 0. */ int ppp_send_config(unit, mtu, accm, pcomp, accomp) int unit, mtu; u_int32_t accm; int pcomp, accomp; { int errs; if (the_channel->send_config == NULL) return 0; errs = error_count; (*the_channel->send_config)(mtu, accm, pcomp, accomp); return (error_count != errs)? -1: 0; } /* * ppp_recv_config - configure the receive-side characteristics of * the ppp interface. Returns -1, indicating an error, if the channel * recv_config procedure called error() (or incremented error_count * itself), otherwise 0. */ int ppp_recv_config(unit, mru, accm, pcomp, accomp) int unit, mru; u_int32_t accm; int pcomp, accomp; { int errs; if (the_channel->recv_config == NULL) return 0; errs = error_count; (*the_channel->recv_config)(mru, accm, pcomp, accomp); return (error_count != errs)? -1: 0; } /* * new_phase - signal the start of a new phase of pppd's operation. */ void new_phase(p) int p; { phase = p; if (new_phase_hook) (*new_phase_hook)(p); notify(phasechange, p); } /* * die - clean up state and exit with the specified status. */ void die(status) int status; { if (!doing_multilink || multilink_master) print_link_stats(); cleanup(); notify(exitnotify, status); syslog(LOG_INFO, "Exit."); exit(status); } /* * cleanup - restore anything which needs to be restored before we exit */ /* ARGSUSED */ static void cleanup() { sys_cleanup(); if (fd_ppp >= 0) the_channel->disestablish_ppp(devfd); if (the_channel->cleanup) (*the_channel->cleanup)(); remove_pidfiles(); #ifdef USE_TDB if (pppdb != NULL) cleanup_db(); #endif } void print_link_stats() { /* * Print connect time and statistics. */ if (link_stats_valid) { int t = (link_connect_time + 5) / 6; /* 1/10ths of minutes */ info("Connect time %d.%d minutes.", t/10, t%10); info("Sent %u bytes, received %u bytes.", link_stats.bytes_out, link_stats.bytes_in); link_stats_valid = 0; } } /* * reset_link_stats - "reset" stats when link goes up. */ void reset_link_stats(u) int u; { if (!get_ppp_stats(u, &old_link_stats)) return; gettimeofday(&start_time, NULL); } /* * update_link_stats - get stats at link termination. */ void update_link_stats(u) int u; { struct timeval now; char numbuf[32]; if (!get_ppp_stats(u, &link_stats) || gettimeofday(&now, NULL) < 0) return; link_connect_time = now.tv_sec - start_time.tv_sec; link_stats_valid = 1; link_stats.bytes_in -= old_link_stats.bytes_in; link_stats.bytes_out -= old_link_stats.bytes_out; link_stats.pkts_in -= old_link_stats.pkts_in; link_stats.pkts_out -= old_link_stats.pkts_out; slprintf(numbuf, sizeof(numbuf), "%u", link_connect_time); script_setenv("CONNECT_TIME", numbuf, 0); slprintf(numbuf, sizeof(numbuf), "%u", link_stats.bytes_out); script_setenv("BYTES_SENT", numbuf, 0); slprintf(numbuf, sizeof(numbuf), "%u", link_stats.bytes_in); script_setenv("BYTES_RCVD", numbuf, 0); } struct callout { struct timeval c_time; /* time at which to call routine */ void *c_arg; /* argument to routine */ void (*c_func) __P((void *)); /* routine */ struct callout *c_next; }; static struct callout *callout = NULL; /* Callout list */ static struct timeval timenow; /* Current time */ /* * timeout - Schedule a timeout. */ void timeout(func, arg, secs, usecs) void (*func) __P((void *)); void *arg; int secs, usecs; { struct callout *newp, *p, **pp; /* * Allocate timeout. */ if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) fatal("Out of memory in timeout()!"); newp->c_arg = arg; newp->c_func = func; gettimeofday(&timenow, NULL); newp->c_time.tv_sec = timenow.tv_sec + secs; newp->c_time.tv_usec = timenow.tv_usec + usecs; if (newp->c_time.tv_usec >= 1000000) { newp->c_time.tv_sec += newp->c_time.tv_usec / 1000000; newp->c_time.tv_usec %= 1000000; } /* * Find correct place and link it in. */ for (pp = &callout; (p = *pp); pp = &p->c_next) if (newp->c_time.tv_sec < p->c_time.tv_sec || (newp->c_time.tv_sec == p->c_time.tv_sec && newp->c_time.tv_usec < p->c_time.tv_usec)) break; newp->c_next = p; *pp = newp; } /* * untimeout - Unschedule a timeout. */ void untimeout(func, arg) void (*func) __P((void *)); void *arg; { struct callout **copp, *freep; /* * Find first matching timeout and remove it from the list. */ for (copp = &callout; (freep = *copp); copp = &freep->c_next) if (freep->c_func == func && freep->c_arg == arg) { *copp = freep->c_next; free((char *) freep); break; } } /* * calltimeout - Call any timeout routines which are now due. */ static void calltimeout() { struct callout *p; while (callout != NULL) { p = callout; if (gettimeofday(&timenow, NULL) < 0) fatal("Failed to get time of day: %m"); if (!(p->c_time.tv_sec < timenow.tv_sec || (p->c_time.tv_sec == timenow.tv_sec && p->c_time.tv_usec <= timenow.tv_usec))) break; /* no, it's not time yet */ callout = p->c_next; (*p->c_func)(p->c_arg); free((char *) p); } } /* * timeleft - return the length of time until the next timeout is due. */ static struct timeval * timeleft(tvp) struct timeval *tvp; { if (callout == NULL) return NULL; gettimeofday(&timenow, NULL); tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec; tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec; if (tvp->tv_usec < 0) { tvp->tv_usec += 1000000; tvp->tv_sec -= 1; } if (tvp->tv_sec < 0) tvp->tv_sec = tvp->tv_usec = 0; return tvp; } /* * kill_my_pg - send a signal to our process group, and ignore it ourselves. * We assume that sig is currently blocked. */ static void kill_my_pg(sig) int sig; { struct sigaction act, oldact; struct subprocess *chp; if (!detached) { /* * There might be other things in our process group that we * didn't start that would get hit if we did a kill(0), so * just send the signal individually to our children. */ for (chp = children; chp != NULL; chp = chp->next) if (chp->killable) kill(chp->pid, sig); return; } /* We've done a setsid(), so we can just use a kill(0) */ sigemptyset(&act.sa_mask); /* unnecessary in fact */ act.sa_handler = SIG_IGN; act.sa_flags = 0; kill(0, sig); /* * The kill() above made the signal pending for us, as well as * the rest of our process group, but we don't want it delivered * to us. It is blocked at the moment. Setting it to be ignored * will cause the pending signal to be discarded. If we did the * kill() after setting the signal to be ignored, it is unspecified * (by POSIX) whether the signal is immediately discarded or left * pending, and in fact Linux would leave it pending, and so it * would be delivered after the current signal handler exits, * leading to an infinite loop. */ sigaction(sig, &act, &oldact); sigaction(sig, &oldact, NULL); } /* * hup - Catch SIGHUP signal. * * Indicates that the physical layer has been disconnected. * We don't rely on this indication; if the user has sent this * signal, we just take the link down. */ static void hup(sig) int sig; { /* can't log a message here, it can deadlock */ got_sighup = 1; if (conn_running) /* Send the signal to the [dis]connector process(es) also */ kill_my_pg(sig); notify(sigreceived, sig); if (waiting) siglongjmp(sigjmp, 1); } /* * term - Catch SIGTERM signal and SIGINT signal (^C/del). * * Indicates that we should initiate a graceful disconnect and exit. */ /*ARGSUSED*/ static void term(sig) int sig; { /* can't log a message here, it can deadlock */ got_sigterm = sig; if (conn_running) /* Send the signal to the [dis]connector process(es) also */ kill_my_pg(sig); notify(sigreceived, sig); if (waiting) siglongjmp(sigjmp, 1); } /* * chld - Catch SIGCHLD signal. * Sets a flag so we will call reap_kids in the mainline. */ static void chld(sig) int sig; { got_sigchld = 1; if (waiting) siglongjmp(sigjmp, 1); } /* * toggle_debug - Catch SIGUSR1 signal. * * Toggle debug flag. */ /*ARGSUSED*/ static void toggle_debug(sig) int sig; { debug = !debug; if (debug) { setlogmask(LOG_UPTO(LOG_DEBUG)); } else { setlogmask(LOG_UPTO(LOG_WARNING)); } } /* * open_ccp - Catch SIGUSR2 signal. * * Try to (re)negotiate compression. */ /*ARGSUSED*/ static void open_ccp(sig) int sig; { got_sigusr2 = 1; if (waiting) siglongjmp(sigjmp, 1); } /* * bad_signal - We've caught a fatal signal. Clean up state and exit. */ static void bad_signal(sig) int sig; { static int crashed = 0; if (crashed) _exit(127); crashed = 1; error("Fatal signal %d", sig); if (conn_running) kill_my_pg(SIGTERM); notify(sigreceived, sig); die(127); } /* * safe_fork - Create a child process. The child closes all the * file descriptors that we don't want to leak to a script. * The parent waits for the child to do this before returning. * This also arranges for the specified fds to be dup'd to * fds 0, 1, 2 in the child. */ pid_t safe_fork(int infd, int outfd, int errfd) { pid_t pid; int fd, pipefd[2]; char buf[1]; /* make sure fds 0, 1, 2 are occupied (probably not necessary) */ while ((fd = dup(fd_devnull)) >= 0) { if (fd > 2) { close(fd); break; } } if (pipe(pipefd) == -1) pipefd[0] = pipefd[1] = -1; pid = fork(); if (pid < 0) { error("fork failed: %m"); return -1; } if (pid > 0) { /* parent */ close(pipefd[1]); /* this read() blocks until the close(pipefd[1]) below */ complete_read(pipefd[0], buf, 1); close(pipefd[0]); return pid; } /* Executing in the child */ sys_close(); #ifdef USE_TDB tdb_close(pppdb); #endif /* make sure infd, outfd and errfd won't get tromped on below */ if (infd == 1 || infd == 2) infd = dup(infd); if (outfd == 0 || outfd == 2) outfd = dup(outfd); if (errfd == 0 || errfd == 1) errfd = dup(errfd); closelog(); /* dup the in, out, err fds to 0, 1, 2 */ if (infd != 0) dup2(infd, 0); if (outfd != 1) dup2(outfd, 1); if (errfd != 2) dup2(errfd, 2); if (log_to_fd > 2) close(log_to_fd); if (the_channel->close) (*the_channel->close)(); else close(devfd); /* some plugins don't have a close function */ close(fd_ppp); close(fd_devnull); if (infd != 0) close(infd); if (outfd != 1) close(outfd); if (errfd != 2) close(errfd); notify(fork_notifier, 0); close(pipefd[0]); /* this close unblocks the read() call above in the parent */ close(pipefd[1]); return 0; } /* * device_script - run a program to talk to the specified fds * (e.g. to run the connector or disconnector script). * stderr gets connected to the log fd or to the _PATH_CONNERRS file. */ int device_script(program, in, out, dont_wait) char *program; int in, out; int dont_wait; { int pid; int status = -1; int errfd; if (log_to_fd >= 0) errfd = log_to_fd; else errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600); ++conn_running; pid = safe_fork(in, out, errfd); if (pid != 0 && log_to_fd < 0) close(errfd); if (pid < 0) { --conn_running; error("Failed to create child process: %m"); return -1; } if (pid != 0) { record_child(pid, program, NULL, NULL, 1); status = 0; if (!dont_wait) { while (waitpid(pid, &status, 0) < 0) { if (errno == EINTR) continue; fatal("error waiting for (dis)connection process: %m"); } forget_child(pid, status); --conn_running; } return (status == 0 ? 0 : -1); } /* here we are executing in the child */ setgid(getgid()); setuid(uid); if (getuid() != uid) { fprintf(stderr, "pppd: setuid failed\n"); exit(1); } execl("/bin/sh", "sh", "-c", program, (char *)0); perror("pppd: could not exec /bin/sh"); exit(99); /* NOTREACHED */ } /* * run_program - execute a program with given arguments, * but don't wait for it unless wait is non-zero. * If the program can't be executed, logs an error unless * must_exist is 0 and the program file doesn't exist. * Returns -1 if it couldn't fork, 0 if the file doesn't exist * or isn't an executable plain file, or the process ID of the child. * If done != NULL, (*done)(arg) will be called later (within * reap_kids) iff the return value is > 0. */ pid_t run_program(prog, args, must_exist, done, arg, wait) char *prog; char **args; int must_exist; void (*done) __P((void *)); void *arg; int wait; { int pid, status; struct stat sbuf; /* * First check if the file exists and is executable. * We don't use access() because that would use the * real user-id, which might not be root, and the script * might be accessible only to root. */ errno = EINVAL; if (stat(prog, &sbuf) < 0 || !S_ISREG(sbuf.st_mode) || (sbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0) { if (must_exist || errno != ENOENT) warn("Can't execute %s: %m", prog); return 0; } pid = safe_fork(fd_devnull, fd_devnull, fd_devnull); if (pid == -1) { error("Failed to create child process for %s: %m", prog); return -1; } if (pid != 0) { if (debug) dbglog("Script %s started (pid %d)", prog, pid); record_child(pid, prog, done, arg, 0); if (wait) { while (waitpid(pid, &status, 0) < 0) { if (errno == EINTR) continue; fatal("error waiting for script %s: %m", prog); } forget_child(pid, status); } return pid; } /* Leave the current location */ (void) setsid(); /* No controlling tty. */ (void) umask (S_IRWXG|S_IRWXO); (void) chdir ("/"); /* no current directory. */ setuid(0); /* set real UID = root */ setgid(getegid()); #ifdef BSD /* Force the priority back to zero if pppd is running higher. */ if (setpriority (PRIO_PROCESS, 0, 0) < 0) warn("can't reset priority to 0: %m"); #endif /* run the program */ execve(prog, args, script_env); if (must_exist || errno != ENOENT) { /* have to reopen the log, there's nowhere else for the message to go. */ reopen_log(); syslog(LOG_ERR, "Can't execute %s: %m", prog); closelog(); } _exit(-1); } /* * record_child - add a child process to the list for reap_kids * to use. */ void record_child(pid, prog, done, arg, killable) int pid; char *prog; void (*done) __P((void *)); void *arg; int killable; { struct subprocess *chp; ++n_children; chp = (struct subprocess *) malloc(sizeof(struct subprocess)); if (chp == NULL) { warn("losing track of %s process", prog); } else { chp->pid = pid; chp->prog = prog; chp->done = done; chp->arg = arg; chp->next = children; chp->killable = killable; children = chp; } } /* * childwait_end - we got fed up waiting for the child processes to * exit, send them all a SIGTERM. */ static void childwait_end(arg) void *arg; { struct subprocess *chp; for (chp = children; chp != NULL; chp = chp->next) { if (debug) dbglog("sending SIGTERM to process %d", chp->pid); kill(chp->pid, SIGTERM); } childwait_done = 1; } /* * forget_child - clean up after a dead child */ static void forget_child(pid, status) int pid, status; { struct subprocess *chp, **prevp; for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) { if (chp->pid == pid) { --n_children; *prevp = chp->next; break; } } if (WIFSIGNALED(status)) { warn("Child process %s (pid %d) terminated with signal %d", (chp? chp->prog: "??"), pid, WTERMSIG(status)); } else if (debug) dbglog("Script %s finished (pid %d), status = 0x%x", (chp? chp->prog: "??"), pid, WIFEXITED(status) ? WEXITSTATUS(status) : status); if (chp && chp->done) (*chp->done)(chp->arg); if (chp) free(chp); } /* * reap_kids - get status from any dead child processes, * and log a message for abnormal terminations. */ static int reap_kids() { int pid, status; if (n_children == 0) return 0; while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) { forget_child(pid, status); } if (pid == -1) { if (errno == ECHILD) return -1; if (errno != EINTR) error("Error waiting for child process: %m"); } return 0; } /* * add_notifier - add a new function to be called when something happens. */ void add_notifier(notif, func, arg) struct notifier **notif; notify_func func; void *arg; { struct notifier *np; np = malloc(sizeof(struct notifier)); if (np == 0) novm("notifier struct"); np->next = *notif; np->func = func; np->arg = arg; *notif = np; } /* * remove_notifier - remove a function from the list of things to * be called when something happens. */ void remove_notifier(notif, func, arg) struct notifier **notif; notify_func func; void *arg; { struct notifier *np; for (; (np = *notif) != 0; notif = &np->next) { if (np->func == func && np->arg == arg) { *notif = np->next; free(np); break; } } } /* * notify - call a set of functions registered with add_notifier. */ void notify(notif, val) struct notifier *notif; int val; { struct notifier *np; while ((np = notif) != 0) { notif = np->next; (*np->func)(np->arg, val); } } /* * novm - log an error message saying we ran out of memory, and die. */ void novm(msg) char *msg; { fatal("Virtual memory exhausted allocating %s\n", msg); } /* * script_setenv - set an environment variable value to be used * for scripts that we run (e.g. ip-up, auth-up, etc.) */ void script_setenv(var, value, iskey) char *var, *value; int iskey; { size_t varl = strlen(var); size_t vl = varl + strlen(value) + 2; int i; char *p, *newstring; newstring = (char *) malloc(vl+1); if (newstring == 0) return; *newstring++ = iskey; slprintf(newstring, vl, "%s=%s", var, value); /* check if this variable is already set */ if (script_env != 0) { for (i = 0; (p = script_env[i]) != 0; ++i) { if (strncmp(p, var, varl) == 0 && p[varl] == '=') { #ifdef USE_TDB if (p[-1] && pppdb != NULL) delete_db_key(p); #endif free(p-1); script_env[i] = newstring; #ifdef USE_TDB if (iskey && pppdb != NULL) add_db_key(newstring); update_db_entry(); #endif return; } } } else { /* no space allocated for script env. ptrs. yet */ i = 0; script_env = (char **) malloc(16 * sizeof(char *)); if (script_env == 0) return; s_env_nalloc = 16; } /* reallocate script_env with more space if needed */ if (i + 1 >= s_env_nalloc) { int new_n = i + 17; char **newenv = (char **) realloc((void *)script_env, new_n * sizeof(char *)); if (newenv == 0) return; script_env = newenv; s_env_nalloc = new_n; } script_env[i] = newstring; script_env[i+1] = 0; #ifdef USE_TDB if (pppdb != NULL) { if (iskey) add_db_key(newstring); update_db_entry(); } #endif } /* * script_unsetenv - remove a variable from the environment * for scripts. */ void script_unsetenv(var) char *var; { int vl = strlen(var); int i; char *p; if (script_env == 0) return; for (i = 0; (p = script_env[i]) != 0; ++i) { if (strncmp(p, var, vl) == 0 && p[vl] == '=') { #ifdef USE_TDB if (p[-1] && pppdb != NULL) delete_db_key(p); #endif free(p-1); while ((script_env[i] = script_env[i+1]) != 0) ++i; break; } } #ifdef USE_TDB if (pppdb != NULL) update_db_entry(); #endif } /* * Any arbitrary string used as a key for locking the database. * It doesn't matter what it is as long as all pppds use the same string. */ #define PPPD_LOCK_KEY "pppd lock" /* * lock_db - get an exclusive lock on the TDB database. * Used to ensure atomicity of various lookup/modify operations. */ void lock_db() { #ifdef USE_TDB TDB_DATA key; key.dptr = PPPD_LOCK_KEY; key.dsize = strlen(key.dptr); tdb_chainlock(pppdb, key); #endif } /* * unlock_db - remove the exclusive lock obtained by lock_db. */ void unlock_db() { #ifdef USE_TDB TDB_DATA key; key.dptr = PPPD_LOCK_KEY; key.dsize = strlen(key.dptr); tdb_chainunlock(pppdb, key); #endif } #ifdef USE_TDB /* * update_db_entry - update our entry in the database. */ static void update_db_entry() { TDB_DATA key, dbuf; int vlen, i; char *p, *q, *vbuf; if (script_env == NULL) return; vlen = 0; for (i = 0; (p = script_env[i]) != 0; ++i) vlen += strlen(p) + 1; vbuf = malloc(vlen + 1); if (vbuf == 0) novm("database entry"); q = vbuf; for (i = 0; (p = script_env[i]) != 0; ++i) q += slprintf(q, vbuf + vlen - q, "%s;", p); key.dptr = db_key; key.dsize = strlen(db_key); dbuf.dptr = vbuf; dbuf.dsize = vlen; if (tdb_store(pppdb, key, dbuf, TDB_REPLACE)) error("tdb_store failed: %s", tdb_errorstr(pppdb)); if (vbuf) free(vbuf); } /* * add_db_key - add a key that we can use to look up our database entry. */ static void add_db_key(str) const char *str; { TDB_DATA key, dbuf; key.dptr = (char *) str; key.dsize = strlen(str); dbuf.dptr = db_key; dbuf.dsize = strlen(db_key); if (tdb_store(pppdb, key, dbuf, TDB_REPLACE)) error("tdb_store key failed: %s", tdb_errorstr(pppdb)); } /* * delete_db_key - delete a key for looking up our database entry. */ static void delete_db_key(str) const char *str; { TDB_DATA key; key.dptr = (char *) str; key.dsize = strlen(str); tdb_delete(pppdb, key); } /* * cleanup_db - delete all the entries we put in the database. */ static void cleanup_db() { TDB_DATA key; int i; char *p; key.dptr = db_key; key.dsize = strlen(db_key); tdb_delete(pppdb, key); for (i = 0; (p = script_env[i]) != 0; ++i) if (p[-1]) delete_db_key(p); } #endif /* USE_TDB */ ppp-2.4.5/pppd/md4.c000066400000000000000000000206751130035057700141270ustar00rootroot00000000000000/* ** ******************************************************************** ** md4.c -- Implementation of MD4 Message Digest Algorithm ** ** Updated: 2/16/90 by Ronald L. Rivest ** ** (C) 1990 RSA Data Security, Inc. ** ** ******************************************************************** */ /* ** To use MD4: ** -- Include md4.h in your program ** -- Declare an MDstruct MD to hold the state of the digest ** computation. ** -- Initialize MD using MDbegin(&MD) ** -- For each full block (64 bytes) X you wish to process, call ** MD4Update(&MD,X,512) ** (512 is the number of bits in a full block.) ** -- For the last block (less than 64 bytes) you wish to process, ** MD4Update(&MD,X,n) ** where n is the number of bits in the partial block. A partial ** block terminates the computation, so every MD computation ** should terminate by processing a partial block, even if it ** has n = 0. ** -- The message digest is available in MD.buffer[0] ... ** MD.buffer[3]. (Least-significant byte of each word ** should be output first.) ** -- You can print out the digest using MDprint(&MD) */ /* Implementation notes: ** This implementation assumes that ints are 32-bit quantities. */ #define TRUE 1 #define FALSE 0 /* Compile-time includes */ #include #include "md4.h" #include "pppd.h" /* Compile-time declarations of MD4 "magic constants". */ #define I0 0x67452301 /* Initial values for MD buffer */ #define I1 0xefcdab89 #define I2 0x98badcfe #define I3 0x10325476 #define C2 013240474631 /* round 2 constant = sqrt(2) in octal */ #define C3 015666365641 /* round 3 constant = sqrt(3) in octal */ /* C2 and C3 are from Knuth, The Art of Programming, Volume 2 ** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley. ** Table 2, page 660. */ #define fs1 3 /* round 1 shift amounts */ #define fs2 7 #define fs3 11 #define fs4 19 #define gs1 3 /* round 2 shift amounts */ #define gs2 5 #define gs3 9 #define gs4 13 #define hs1 3 /* round 3 shift amounts */ #define hs2 9 #define hs3 11 #define hs4 15 /* Compile-time macro declarations for MD4. ** Note: The "rot" operator uses the variable "tmp". ** It assumes tmp is declared as unsigned int, so that the >> ** operator will shift in zeros rather than extending the sign bit. */ #define f(X,Y,Z) ((X&Y) | ((~X)&Z)) #define g(X,Y,Z) ((X&Y) | (X&Z) | (Y&Z)) #define h(X,Y,Z) (X^Y^Z) #define rot(X,S) (tmp=X,(tmp<>(32-S))) #define ff(A,B,C,D,i,s) A = rot((A + f(B,C,D) + X[i]),s) #define gg(A,B,C,D,i,s) A = rot((A + g(B,C,D) + X[i] + C2),s) #define hh(A,B,C,D,i,s) A = rot((A + h(B,C,D) + X[i] + C3),s) /* MD4print(MDp) ** Print message digest buffer MDp as 32 hexadecimal digits. ** Order is from low-order byte of buffer[0] to high-order byte of ** buffer[3]. ** Each byte is printed with high-order hexadecimal digit first. ** This is a user-callable routine. */ void MD4Print(MDp) MD4_CTX *MDp; { int i,j; for (i=0;i<4;i++) for (j=0;j<32;j=j+8) printf("%02x",(MDp->buffer[i]>>j) & 0xFF); } /* MD4Init(MDp) ** Initialize message digest buffer MDp. ** This is a user-callable routine. */ void MD4Init(MDp) MD4_CTX *MDp; { int i; MDp->buffer[0] = I0; MDp->buffer[1] = I1; MDp->buffer[2] = I2; MDp->buffer[3] = I3; for (i=0;i<8;i++) MDp->count[i] = 0; MDp->done = 0; } /* MDblock(MDp,X) ** Update message digest buffer MDp->buffer using 16-word data block X. ** Assumes all 16 words of X are full of data. ** Does not update MDp->count. ** This routine is not user-callable. */ static void MDblock(MDp,Xb) MD4_CTX *MDp; unsigned char *Xb; { register unsigned int tmp, A, B, C, D; unsigned int X[16]; int i; for (i = 0; i < 16; ++i) { X[i] = Xb[0] + (Xb[1] << 8) + (Xb[2] << 16) + (Xb[3] << 24); Xb += 4; } A = MDp->buffer[0]; B = MDp->buffer[1]; C = MDp->buffer[2]; D = MDp->buffer[3]; /* Update the message digest buffer */ ff(A , B , C , D , 0 , fs1); /* Round 1 */ ff(D , A , B , C , 1 , fs2); ff(C , D , A , B , 2 , fs3); ff(B , C , D , A , 3 , fs4); ff(A , B , C , D , 4 , fs1); ff(D , A , B , C , 5 , fs2); ff(C , D , A , B , 6 , fs3); ff(B , C , D , A , 7 , fs4); ff(A , B , C , D , 8 , fs1); ff(D , A , B , C , 9 , fs2); ff(C , D , A , B , 10 , fs3); ff(B , C , D , A , 11 , fs4); ff(A , B , C , D , 12 , fs1); ff(D , A , B , C , 13 , fs2); ff(C , D , A , B , 14 , fs3); ff(B , C , D , A , 15 , fs4); gg(A , B , C , D , 0 , gs1); /* Round 2 */ gg(D , A , B , C , 4 , gs2); gg(C , D , A , B , 8 , gs3); gg(B , C , D , A , 12 , gs4); gg(A , B , C , D , 1 , gs1); gg(D , A , B , C , 5 , gs2); gg(C , D , A , B , 9 , gs3); gg(B , C , D , A , 13 , gs4); gg(A , B , C , D , 2 , gs1); gg(D , A , B , C , 6 , gs2); gg(C , D , A , B , 10 , gs3); gg(B , C , D , A , 14 , gs4); gg(A , B , C , D , 3 , gs1); gg(D , A , B , C , 7 , gs2); gg(C , D , A , B , 11 , gs3); gg(B , C , D , A , 15 , gs4); hh(A , B , C , D , 0 , hs1); /* Round 3 */ hh(D , A , B , C , 8 , hs2); hh(C , D , A , B , 4 , hs3); hh(B , C , D , A , 12 , hs4); hh(A , B , C , D , 2 , hs1); hh(D , A , B , C , 10 , hs2); hh(C , D , A , B , 6 , hs3); hh(B , C , D , A , 14 , hs4); hh(A , B , C , D , 1 , hs1); hh(D , A , B , C , 9 , hs2); hh(C , D , A , B , 5 , hs3); hh(B , C , D , A , 13 , hs4); hh(A , B , C , D , 3 , hs1); hh(D , A , B , C , 11 , hs2); hh(C , D , A , B , 7 , hs3); hh(B , C , D , A , 15 , hs4); MDp->buffer[0] += A; MDp->buffer[1] += B; MDp->buffer[2] += C; MDp->buffer[3] += D; } /* MD4Update(MDp,X,count) ** Input: X -- a pointer to an array of unsigned characters. ** count -- the number of bits of X to use. ** (if not a multiple of 8, uses high bits of last byte.) ** Update MDp using the number of bits of X given by count. ** This is the basic input routine for an MD4 user. ** The routine completes the MD computation when count < 512, so ** every MD computation should end with one call to MD4Update with a ** count less than 512. A call with count 0 will be ignored if the ** MD has already been terminated (done != 0), so an extra call with ** count 0 can be given as a "courtesy close" to force termination ** if desired. */ void MD4Update(MDp,X,count) MD4_CTX *MDp; unsigned char *X; unsigned int count; { unsigned int i, tmp, bit, byte, mask; unsigned char XX[64]; unsigned char *p; /* return with no error if this is a courtesy close with count ** zero and MDp->done is true. */ if (count == 0 && MDp->done) return; /* check to see if MD is already done and report error */ if (MDp->done) { printf("\nError: MD4Update MD already done."); return; } /* Add count to MDp->count */ tmp = count; p = MDp->count; while (tmp) { tmp += *p; *p++ = tmp; tmp = tmp >> 8; } /* Process data */ if (count == 512) { /* Full block of data to handle */ MDblock(MDp,X); } else if (count > 512) /* Check for count too large */ { printf("\nError: MD4Update called with illegal count value %d.", count); return; } else /* partial block -- must be last block so finish up */ { /* Find out how many bytes and residual bits there are */ byte = count >> 3; bit = count & 7; /* Copy X into XX since we need to modify it */ if (count) for (i=0;i<=byte;i++) XX[i] = X[i]; for (i=byte+1;i<64;i++) XX[i] = 0; /* Add padding '1' bit and low-order zeros in last byte */ mask = 1 << (7 - bit); XX[byte] = (XX[byte] | mask) & ~( mask - 1); /* If room for bit count, finish up with this block */ if (byte <= 55) { for (i=0;i<8;i++) XX[56+i] = MDp->count[i]; MDblock(MDp,XX); } else /* need to do two blocks to finish up */ { MDblock(MDp,XX); for (i=0;i<56;i++) XX[i] = 0; for (i=0;i<8;i++) XX[56+i] = MDp->count[i]; MDblock(MDp,XX); } /* Set flag saying we're done with MD computation */ MDp->done = 1; } } /* ** Finish up MD4 computation and return message digest. */ void MD4Final(buf, MD) unsigned char *buf; MD4_CTX *MD; { int i, j; unsigned int w; MD4Update(MD, NULL, 0); for (i = 0; i < 4; ++i) { w = MD->buffer[i]; for (j = 0; j < 4; ++j) { *buf++ = w; w >>= 8; } } } /* ** End of md4.c ****************************(cut)***********************************/ ppp-2.4.5/pppd/md4.h000066400000000000000000000041671130035057700141320ustar00rootroot00000000000000 /* ** ******************************************************************** ** md4.h -- Header file for implementation of ** ** MD4 Message Digest Algorithm ** ** Updated: 2/13/90 by Ronald L. Rivest ** ** (C) 1990 RSA Data Security, Inc. ** ** ******************************************************************** */ #ifndef __P # if defined(__STDC__) || defined(__GNUC__) # define __P(x) x # else # define __P(x) () # endif #endif /* MDstruct is the data structure for a message digest computation. */ typedef struct { unsigned int buffer[4]; /* Holds 4-word result of MD computation */ unsigned char count[8]; /* Number of bits processed so far */ unsigned int done; /* Nonzero means MD computation finished */ } MD4_CTX; /* MD4Init(MD4_CTX *) ** Initialize the MD4_CTX prepatory to doing a message digest ** computation. */ extern void MD4Init __P((MD4_CTX *MD)); /* MD4Update(MD,X,count) ** Input: X -- a pointer to an array of unsigned characters. ** count -- the number of bits of X to use (an unsigned int). ** Updates MD using the first "count" bits of X. ** The array pointed to by X is not modified. ** If count is not a multiple of 8, MD4Update uses high bits of ** last byte. ** This is the basic input routine for a user. ** The routine terminates the MD computation when count < 512, so ** every MD computation should end with one call to MD4Update with a ** count less than 512. Zero is OK for a count. */ extern void MD4Update __P((MD4_CTX *MD, unsigned char *X, unsigned int count)); /* MD4Print(MD) ** Prints message digest buffer MD as 32 hexadecimal digits. ** Order is from low-order byte of buffer[0] to high-order byte ** of buffer[3]. ** Each byte is printed with high-order hexadecimal digit first. */ extern void MD4Print __P((MD4_CTX *)); /* MD4Final(buf, MD) ** Returns message digest from MD and terminates the message ** digest computation. */ extern void MD4Final __P((unsigned char *, MD4_CTX *)); /* ** End of md4.h ****************************(cut)***********************************/ ppp-2.4.5/pppd/md5.c000066400000000000000000000264751130035057700141340ustar00rootroot00000000000000 /* *********************************************************************** ** 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 #include "md5.h" /* *********************************************************************** ** Message-digest routines: ** ** To form the message digest for a message M ** ** (1) Initialize a context buffer mdContext using MD5_Init ** ** (2) Call MD5_Update on mdContext and M ** ** (3) Call MD5_Final on mdContext ** ** The message digest is now in mdContext->digest[0...15] ** *********************************************************************** */ /* forward declaration */ static void Transform (UINT4 *buf, UINT4 *in); 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 MD5_Init initializes the message-digest context mdContext. All fields are set to zero. */ void MD5_Init (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 MD5_Update (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 MD5_Final (hash, mdContext) unsigned char hash[]; 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); MD5_Update (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); } memcpy(hash, mdContext->digest, 16); } /* 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) ******************************** */ ppp-2.4.5/pppd/md5.h000066400000000000000000000062461130035057700141330ustar00rootroot00000000000000/* *********************************************************************** ** 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 */ #ifdef _LP64 typedef unsigned int UINT4; typedef int INT4; #else typedef unsigned long UINT4; typedef long INT4; #endif #define _UINT4_T /* 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 MD5_Init (MD5_CTX *mdContext); void MD5_Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); void MD5_Final (unsigned char hash[], MD5_CTX *mdContext); #define __MD5_INCLUDE__ #endif /* __MD5_INCLUDE__ */ ppp-2.4.5/pppd/mppe.h000066400000000000000000000101071130035057700143760ustar00rootroot00000000000000/* * mppe.h - Definitions for MPPE * * Copyright (c) 2008 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define MPPE_PAD 4 /* MPPE growth per frame */ #define MPPE_MAX_KEY_LEN 16 /* largest key length (128-bit) */ /* option bits for ccp_options.mppe */ #define MPPE_OPT_40 0x01 /* 40 bit */ #define MPPE_OPT_128 0x02 /* 128 bit */ #define MPPE_OPT_STATEFUL 0x04 /* stateful mode */ /* unsupported opts */ #define MPPE_OPT_56 0x08 /* 56 bit */ #define MPPE_OPT_MPPC 0x10 /* MPPC compression */ #define MPPE_OPT_D 0x20 /* Unknown */ #define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D) #define MPPE_OPT_UNKNOWN 0x40 /* Bits !defined in RFC 3078 were set */ /* * This is not nice ... the alternative is a bitfield struct though. * And unfortunately, we cannot share the same bits for the option * names above since C and H are the same bit. We could do a u_int32 * but then we have to do a htonl() all the time and/or we still need * to know which octet is which. */ #define MPPE_C_BIT 0x01 /* MPPC */ #define MPPE_D_BIT 0x10 /* Obsolete, usage unknown */ #define MPPE_L_BIT 0x20 /* 40-bit */ #define MPPE_S_BIT 0x40 /* 128-bit */ #define MPPE_M_BIT 0x80 /* 56-bit, not supported */ #define MPPE_H_BIT 0x01 /* Stateless (in a different byte) */ /* Does not include H bit; used for least significant octet only. */ #define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT) /* Build a CI from mppe opts (see RFC 3078) */ #define MPPE_OPTS_TO_CI(opts, ci) \ do { \ u_char *ptr = ci; /* u_char[4] */ \ \ /* H bit */ \ if (opts & MPPE_OPT_STATEFUL) \ *ptr++ = 0x0; \ else \ *ptr++ = MPPE_H_BIT; \ *ptr++ = 0; \ *ptr++ = 0; \ \ /* S,L bits */ \ *ptr = 0; \ if (opts & MPPE_OPT_128) \ *ptr |= MPPE_S_BIT; \ if (opts & MPPE_OPT_40) \ *ptr |= MPPE_L_BIT; \ /* M,D,C bits not supported */ \ } while (/* CONSTCOND */ 0) /* The reverse of the above */ #define MPPE_CI_TO_OPTS(ci, opts) \ do { \ u_char *ptr = ci; /* u_char[4] */ \ \ opts = 0; \ \ /* H bit */ \ if (!(ptr[0] & MPPE_H_BIT)) \ opts |= MPPE_OPT_STATEFUL; \ \ /* S,L bits */ \ if (ptr[3] & MPPE_S_BIT) \ opts |= MPPE_OPT_128; \ if (ptr[3] & MPPE_L_BIT) \ opts |= MPPE_OPT_40; \ \ /* M,D,C bits */ \ if (ptr[3] & MPPE_M_BIT) \ opts |= MPPE_OPT_56; \ if (ptr[3] & MPPE_D_BIT) \ opts |= MPPE_OPT_D; \ if (ptr[3] & MPPE_C_BIT) \ opts |= MPPE_OPT_MPPC; \ \ /* Other bits */ \ if (ptr[0] & ~MPPE_H_BIT) \ opts |= MPPE_OPT_UNKNOWN; \ if (ptr[1] || ptr[2]) \ opts |= MPPE_OPT_UNKNOWN; \ if (ptr[3] & ~MPPE_ALL_BITS) \ opts |= MPPE_OPT_UNKNOWN; \ } while (/* CONSTCOND */ 0) ppp-2.4.5/pppd/multilink.c000066400000000000000000000323301130035057700154420ustar00rootroot00000000000000/* * multilink.c - support routines for multilink. * * Copyright (c) 2000-2002 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "pppd.h" #include "fsm.h" #include "lcp.h" #include "tdb.h" bool endpoint_specified; /* user gave explicit endpoint discriminator */ char *bundle_id; /* identifier for our bundle */ char *blinks_id; /* key for the list of links */ bool doing_multilink; /* multilink was enabled and agreed to */ bool multilink_master; /* we own the multilink bundle */ extern TDB_CONTEXT *pppdb; extern char db_key[]; static void make_bundle_links __P((int append)); static void remove_bundle_link __P((void)); static void iterate_bundle_links __P((void (*func) __P((char *)))); static int get_default_epdisc __P((struct epdisc *)); static int parse_num __P((char *str, const char *key, int *valp)); static int owns_unit __P((TDB_DATA pid, int unit)); #define set_ip_epdisc(ep, addr) do { \ ep->length = 4; \ ep->value[0] = addr >> 24; \ ep->value[1] = addr >> 16; \ ep->value[2] = addr >> 8; \ ep->value[3] = addr; \ } while (0) #define LOCAL_IP_ADDR(addr) \ (((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \ || ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \ || ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */ #define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH) void mp_check_options() { lcp_options *wo = &lcp_wantoptions[0]; lcp_options *ao = &lcp_allowoptions[0]; doing_multilink = 0; if (!multilink) return; /* if we're doing multilink, we have to negotiate MRRU */ if (!wo->neg_mrru) { /* mrru not specified, default to mru */ wo->mrru = wo->mru; wo->neg_mrru = 1; } ao->mrru = ao->mru; ao->neg_mrru = 1; if (!wo->neg_endpoint && !noendpoint) { /* get a default endpoint value */ wo->neg_endpoint = get_default_epdisc(&wo->endpoint); } } /* * Make a new bundle or join us to an existing bundle * if we are doing multilink. */ int mp_join_bundle() { lcp_options *go = &lcp_gotoptions[0]; lcp_options *ho = &lcp_hisoptions[0]; lcp_options *ao = &lcp_allowoptions[0]; int unit, pppd_pid; int l, mtu; char *p; TDB_DATA key, pid, rec; if (doing_multilink) { /* have previously joined a bundle */ if (!go->neg_mrru || !ho->neg_mrru) { notice("oops, didn't get multilink on renegotiation"); lcp_close(0, "multilink required"); return 0; } /* XXX should check the peer_authname and ho->endpoint are the same as previously */ return 0; } if (!go->neg_mrru || !ho->neg_mrru) { /* not doing multilink */ if (go->neg_mrru) notice("oops, multilink negotiated only for receive"); mtu = ho->neg_mru? ho->mru: PPP_MRU; if (mtu > ao->mru) mtu = ao->mru; if (demand) { /* already have a bundle */ cfg_bundle(0, 0, 0, 0); netif_set_mtu(0, mtu); return 0; } make_new_bundle(0, 0, 0, 0); set_ifunit(1); netif_set_mtu(0, mtu); return 0; } doing_multilink = 1; /* * Find the appropriate bundle or join a new one. * First we make up a name for the bundle. * The length estimate is worst-case assuming every * character has to be quoted. */ l = 4 * strlen(peer_authname) + 10; if (ho->neg_endpoint) l += 3 * ho->endpoint.length + 8; if (bundle_name) l += 3 * strlen(bundle_name) + 2; bundle_id = malloc(l); if (bundle_id == 0) novm("bundle identifier"); p = bundle_id; p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname); if (ho->neg_endpoint || bundle_name) *p++ = '/'; if (ho->neg_endpoint) p += slprintf(p, bundle_id+l-p, "%s", epdisc_to_str(&ho->endpoint)); if (bundle_name) p += slprintf(p, bundle_id+l-p, "/%v", bundle_name); /* Make the key for the list of links belonging to the bundle */ l = p - bundle_id; blinks_id = malloc(l + 7); if (blinks_id == NULL) novm("bundle links key"); slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7); /* * For demand mode, we only need to configure the bundle * and attach the link. */ mtu = MIN(ho->mrru, ao->mru); if (demand) { cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); netif_set_mtu(0, mtu); script_setenv("BUNDLE", bundle_id + 7, 1); return 0; } /* * Check if the bundle ID is already in the database. */ unit = -1; lock_db(); key.dptr = bundle_id; key.dsize = p - bundle_id; pid = tdb_fetch(pppdb, key); if (pid.dptr != NULL) { /* bundle ID exists, see if the pppd record exists */ rec = tdb_fetch(pppdb, pid); if (rec.dptr != NULL && rec.dsize > 0) { /* make sure the string is null-terminated */ rec.dptr[rec.dsize-1] = 0; /* parse the interface number */ parse_num(rec.dptr, "IFNAME=ppp", &unit); /* check the pid value */ if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid) || !process_exists(pppd_pid) || !owns_unit(pid, unit)) unit = -1; free(rec.dptr); } free(pid.dptr); } if (unit >= 0) { /* attach to existing unit */ if (bundle_attach(unit)) { set_ifunit(0); script_setenv("BUNDLE", bundle_id + 7, 0); make_bundle_links(1); unlock_db(); info("Link attached to %s", ifname); return 1; } /* attach failed because bundle doesn't exist */ } /* we have to make a new bundle */ make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); set_ifunit(1); netif_set_mtu(0, mtu); script_setenv("BUNDLE", bundle_id + 7, 1); make_bundle_links(0); unlock_db(); info("New bundle %s created", ifname); multilink_master = 1; return 0; } void mp_exit_bundle() { lock_db(); remove_bundle_link(); unlock_db(); } static void sendhup(char *str) { int pid; if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) { if (debug) dbglog("sending SIGHUP to process %d", pid); kill(pid, SIGHUP); } } void mp_bundle_terminated() { TDB_DATA key; bundle_terminating = 1; upper_layers_down(0); notice("Connection terminated."); print_link_stats(); if (!demand) { remove_pidfiles(); script_unsetenv("IFNAME"); } lock_db(); destroy_bundle(); iterate_bundle_links(sendhup); key.dptr = blinks_id; key.dsize = strlen(blinks_id); tdb_delete(pppdb, key); unlock_db(); new_phase(PHASE_DEAD); doing_multilink = 0; multilink_master = 0; } static void make_bundle_links(int append) { TDB_DATA key, rec; char *p; char entry[32]; int l; key.dptr = blinks_id; key.dsize = strlen(blinks_id); slprintf(entry, sizeof(entry), "%s;", db_key); p = entry; if (append) { rec = tdb_fetch(pppdb, key); if (rec.dptr != NULL && rec.dsize > 0) { rec.dptr[rec.dsize-1] = 0; if (strstr(rec.dptr, db_key) != NULL) { /* already in there? strange */ warn("link entry already exists in tdb"); return; } l = rec.dsize + strlen(entry); p = malloc(l); if (p == NULL) novm("bundle link list"); slprintf(p, l, "%s%s", rec.dptr, entry); } else { warn("bundle link list not found"); } if (rec.dptr != NULL) free(rec.dptr); } rec.dptr = p; rec.dsize = strlen(p) + 1; if (tdb_store(pppdb, key, rec, TDB_REPLACE)) error("couldn't %s bundle link list", append? "update": "create"); if (p != entry) free(p); } static void remove_bundle_link() { TDB_DATA key, rec; char entry[32]; char *p, *q; int l; key.dptr = blinks_id; key.dsize = strlen(blinks_id); slprintf(entry, sizeof(entry), "%s;", db_key); rec = tdb_fetch(pppdb, key); if (rec.dptr == NULL || rec.dsize <= 0) { if (rec.dptr != NULL) free(rec.dptr); return; } rec.dptr[rec.dsize-1] = 0; p = strstr(rec.dptr, entry); if (p != NULL) { q = p + strlen(entry); l = strlen(q) + 1; memmove(p, q, l); rec.dsize = p - rec.dptr + l; if (tdb_store(pppdb, key, rec, TDB_REPLACE)) error("couldn't update bundle link list (removal)"); } free(rec.dptr); } static void iterate_bundle_links(void (*func)(char *)) { TDB_DATA key, rec, pp; char *p, *q; key.dptr = blinks_id; key.dsize = strlen(blinks_id); rec = tdb_fetch(pppdb, key); if (rec.dptr == NULL || rec.dsize <= 0) { error("bundle link list not found (iterating list)"); if (rec.dptr != NULL) free(rec.dptr); return; } p = rec.dptr; p[rec.dsize-1] = 0; while ((q = strchr(p, ';')) != NULL) { *q = 0; key.dptr = p; key.dsize = q - p; pp = tdb_fetch(pppdb, key); if (pp.dptr != NULL && pp.dsize > 0) { pp.dptr[pp.dsize-1] = 0; func(pp.dptr); } if (pp.dptr != NULL) free(pp.dptr); p = q + 1; } free(rec.dptr); } static int parse_num(str, key, valp) char *str; const char *key; int *valp; { char *p, *endp; int i; p = strstr(str, key); if (p != 0) { p += strlen(key); i = strtol(p, &endp, 10); if (endp != p && (*endp == 0 || *endp == ';')) { *valp = i; return 1; } } return 0; } /* * Check whether the pppd identified by `key' still owns ppp unit `unit'. */ static int owns_unit(key, unit) TDB_DATA key; int unit; { char ifkey[32]; TDB_DATA kd, vd; int ret = 0; slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit); kd.dptr = ifkey; kd.dsize = strlen(ifkey); vd = tdb_fetch(pppdb, kd); if (vd.dptr != NULL) { ret = vd.dsize == key.dsize && memcmp(vd.dptr, key.dptr, vd.dsize) == 0; free(vd.dptr); } return ret; } static int get_default_epdisc(ep) struct epdisc *ep; { char *p; struct hostent *hp; u_int32_t addr; /* First try for an ethernet MAC address */ p = get_first_ethernet(); if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) { ep->class = EPD_MAC; ep->length = 6; return 1; } /* see if our hostname corresponds to a reasonable IP address */ hp = gethostbyname(hostname); if (hp != NULL) { addr = *(u_int32_t *)hp->h_addr; if (!bad_ip_adrs(addr)) { addr = ntohl(addr); if (!LOCAL_IP_ADDR(addr)) { ep->class = EPD_IP; set_ip_epdisc(ep, addr); return 1; } } } return 0; } /* * epdisc_to_str - make a printable string from an endpoint discriminator. */ static char *endp_class_names[] = { "null", "local", "IP", "MAC", "magic", "phone" }; char * epdisc_to_str(ep) struct epdisc *ep; { static char str[MAX_ENDP_LEN*3+8]; u_char *p = ep->value; int i, mask = 0; char *q, c, c2; if (ep->class == EPD_NULL && ep->length == 0) return "null"; if (ep->class == EPD_IP && ep->length == 4) { u_int32_t addr; GETLONG(addr, p); slprintf(str, sizeof(str), "IP:%I", htonl(addr)); return str; } c = ':'; c2 = '.'; if (ep->class == EPD_MAC && ep->length == 6) c2 = ':'; else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0) mask = 3; q = str; if (ep->class <= EPD_PHONENUM) q += slprintf(q, sizeof(str)-1, "%s", endp_class_names[ep->class]); else q += slprintf(q, sizeof(str)-1, "%d", ep->class); c = ':'; for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) { if ((i & mask) == 0) { *q++ = c; c = c2; } q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]); } return str; } static int hexc_val(int c) { if (c >= 'a') return c - 'a' + 10; if (c >= 'A') return c - 'A' + 10; return c - '0'; } int str_to_epdisc(ep, str) struct epdisc *ep; char *str; { int i, l; char *p, *endp; for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) { int sl = strlen(endp_class_names[i]); if (strncasecmp(str, endp_class_names[i], sl) == 0) { str += sl; break; } } if (i > EPD_PHONENUM) { /* not a class name, try a decimal class number */ i = strtol(str, &endp, 10); if (endp == str) return 0; /* can't parse class number */ str = endp; } ep->class = i; if (*str == 0) { ep->length = 0; return 1; } if (*str != ':' && *str != '.') return 0; ++str; if (i == EPD_IP) { u_int32_t addr; i = parse_dotted_ip(str, &addr); if (i == 0 || str[i] != 0) return 0; set_ip_epdisc(ep, addr); return 1; } if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) { ep->length = 6; return 1; } p = str; for (l = 0; l < MAX_ENDP_LEN; ++l) { if (*str == 0) break; if (p <= str) for (p = str; isxdigit(*p); ++p) ; i = p - str; if (i == 0) return 0; ep->value[l] = hexc_val(*str++); if ((i & 1) == 0) ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++); if (*str == ':' || *str == '.') ++str; } if (*str != 0 || (ep->class == EPD_MAC && l != 6)) return 0; ep->length = l; return 1; } ppp-2.4.5/pppd/options.c000066400000000000000000001154061130035057700151330ustar00rootroot00000000000000/* * options.c - handles option processing for PPP. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: options.c,v 1.102 2008/06/15 06:53:06 paulus Exp $" #include #include #include #include #include #include #include #include #include #ifdef PLUGIN #include #endif #ifdef PPP_FILTER #include /* * There have been 3 or 4 different names for this in libpcap CVS, but * this seems to be what they have settled on... * For older versions of libpcap, use DLT_PPP - but that means * we lose the inbound and outbound qualifiers. */ #ifndef DLT_PPP_PPPD #ifdef DLT_PPP_WITHDIRECTION #define DLT_PPP_PPPD DLT_PPP_WITHDIRECTION #else #define DLT_PPP_PPPD DLT_PPP #endif #endif #endif /* PPP_FILTER */ #include "pppd.h" #include "pathnames.h" #if defined(ultrix) || defined(NeXT) char *strdup __P((char *)); #endif static const char rcsid[] = RCSID; struct option_value { struct option_value *next; const char *source; char value[1]; }; /* * Option variables and default values. */ int debug = 0; /* Debug flag */ int kdebugflag = 0; /* Tell kernel to print debug messages */ int default_device = 1; /* Using /dev/tty or equivalent */ char devnam[MAXPATHLEN]; /* Device name */ bool nodetach = 0; /* Don't detach from controlling tty */ bool updetach = 0; /* Detach once link is up */ int maxconnect = 0; /* Maximum connect time */ char user[MAXNAMELEN]; /* Username for PAP */ char passwd[MAXSECRETLEN]; /* Password for PAP */ bool persist = 0; /* Reopen link after it goes down */ char our_name[MAXNAMELEN]; /* Our name for authentication purposes */ bool demand = 0; /* do dial-on-demand */ char *ipparam = NULL; /* Extra parameter for ip up/down scripts */ int idle_time_limit = 0; /* Disconnect if idle for this many seconds */ int holdoff = 30; /* # seconds to pause before reconnecting */ bool holdoff_specified; /* true if a holdoff value has been given */ int log_to_fd = 1; /* send log messages to this fd too */ bool log_default = 1; /* log_to_fd is default (stdout) */ int maxfail = 10; /* max # of unsuccessful connection attempts */ char linkname[MAXPATHLEN]; /* logical name for link */ bool tune_kernel; /* may alter kernel settings */ int connect_delay = 1000; /* wait this many ms after connect script */ int req_unit = -1; /* requested interface unit */ bool multilink = 0; /* Enable multilink operation */ char *bundle_name = NULL; /* bundle name for multilink */ bool dump_options; /* print out option values */ bool dryrun; /* print out option values and exit */ char *domain; /* domain name set by domain option */ int child_wait = 5; /* # seconds to wait for children at exit */ #ifdef MAXOCTETS unsigned int maxoctets = 0; /* default - no limit */ int maxoctets_dir = 0; /* default - sum of traffic */ int maxoctets_timeout = 1; /* default 1 second */ #endif extern option_t auth_options[]; extern struct stat devstat; #ifdef PPP_FILTER struct bpf_program pass_filter;/* Filter program for packets to pass */ struct bpf_program active_filter; /* Filter program for link-active pkts */ #endif char *current_option; /* the name of the option being parsed */ int privileged_option; /* set iff the current option came from root */ char *option_source; /* string saying where the option came from */ int option_priority = OPRIO_CFGFILE; /* priority of the current options */ bool devnam_fixed; /* can no longer change device name */ static int logfile_fd = -1; /* fd opened for log file */ static char logfile_name[MAXPATHLEN]; /* name of log file */ /* * Prototypes */ static int setdomain __P((char **)); static int readfile __P((char **)); static int callfile __P((char **)); static int showversion __P((char **)); static int showhelp __P((char **)); static void usage __P((void)); static int setlogfile __P((char **)); #ifdef PLUGIN static int loadplugin __P((char **)); #endif #ifdef PPP_FILTER static int setpassfilter __P((char **)); static int setactivefilter __P((char **)); #endif #ifdef MAXOCTETS static int setmodir __P((char **)); #endif static option_t *find_option __P((const char *name)); static int process_option __P((option_t *, char *, char **)); static int n_arguments __P((option_t *)); static int number_option __P((char *, u_int32_t *, int)); /* * Structure to store extra lists of options. */ struct option_list { option_t *options; struct option_list *next; }; static struct option_list *extra_options = NULL; /* * Valid arguments. */ option_t general_options[] = { { "debug", o_int, &debug, "Increase debugging level", OPT_INC | OPT_NOARG | 1 }, { "-d", o_int, &debug, "Increase debugging level", OPT_ALIAS | OPT_INC | OPT_NOARG | 1 }, { "kdebug", o_int, &kdebugflag, "Set kernel driver debug level", OPT_PRIO }, { "nodetach", o_bool, &nodetach, "Don't detach from controlling tty", OPT_PRIO | 1 }, { "-detach", o_bool, &nodetach, "Don't detach from controlling tty", OPT_ALIAS | OPT_PRIOSUB | 1 }, { "updetach", o_bool, &updetach, "Detach from controlling tty once link is up", OPT_PRIOSUB | OPT_A2CLR | 1, &nodetach }, { "holdoff", o_int, &holdoff, "Set time in seconds before retrying connection", OPT_PRIO, &holdoff_specified }, { "idle", o_int, &idle_time_limit, "Set time in seconds before disconnecting idle link", OPT_PRIO }, { "maxconnect", o_int, &maxconnect, "Set connection time limit", OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF }, { "domain", o_special, (void *)setdomain, "Add given domain name to hostname", OPT_PRIO | OPT_PRIV | OPT_A2STRVAL, &domain }, { "file", o_special, (void *)readfile, "Take options from a file", OPT_NOPRINT }, { "call", o_special, (void *)callfile, "Take options from a privileged file", OPT_NOPRINT }, { "persist", o_bool, &persist, "Keep on reopening connection after close", OPT_PRIO | 1 }, { "nopersist", o_bool, &persist, "Turn off persist option", OPT_PRIOSUB }, { "demand", o_bool, &demand, "Dial on demand", OPT_INITONLY | 1, &persist }, { "--version", o_special_noarg, (void *)showversion, "Show version number" }, { "--help", o_special_noarg, (void *)showhelp, "Show brief listing of options" }, { "-h", o_special_noarg, (void *)showhelp, "Show brief listing of options", OPT_ALIAS }, { "logfile", o_special, (void *)setlogfile, "Append log messages to this file", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, &logfile_name }, { "logfd", o_int, &log_to_fd, "Send log messages to this file descriptor", OPT_PRIOSUB | OPT_A2CLR, &log_default }, { "nolog", o_int, &log_to_fd, "Don't send log messages to any file", OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) }, { "nologfd", o_int, &log_to_fd, "Don't send log messages to any file descriptor", OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) }, { "linkname", o_string, linkname, "Set logical name for link", OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXPATHLEN }, { "maxfail", o_int, &maxfail, "Maximum number of unsuccessful connection attempts to allow", OPT_PRIO }, { "ktune", o_bool, &tune_kernel, "Alter kernel settings as necessary", OPT_PRIO | 1 }, { "noktune", o_bool, &tune_kernel, "Don't alter kernel settings", OPT_PRIOSUB }, { "connect-delay", o_int, &connect_delay, "Maximum time (in ms) to wait after connect script finishes", OPT_PRIO }, { "unit", o_int, &req_unit, "PPP interface unit number to use if possible", OPT_PRIO | OPT_LLIMIT, 0, 0 }, { "dump", o_bool, &dump_options, "Print out option values after parsing all options", 1 }, { "dryrun", o_bool, &dryrun, "Stop after parsing, printing, and checking options", 1 }, { "child-timeout", o_int, &child_wait, "Number of seconds to wait for child processes at exit", OPT_PRIO }, #ifdef HAVE_MULTILINK { "multilink", o_bool, &multilink, "Enable multilink operation", OPT_PRIO | 1 }, { "mp", o_bool, &multilink, "Enable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 1 }, { "nomultilink", o_bool, &multilink, "Disable multilink operation", OPT_PRIOSUB | 0 }, { "nomp", o_bool, &multilink, "Disable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 0 }, { "bundle", o_string, &bundle_name, "Bundle name for multilink", OPT_PRIO }, #endif /* HAVE_MULTILINK */ #ifdef PLUGIN { "plugin", o_special, (void *)loadplugin, "Load a plug-in module into pppd", OPT_PRIV | OPT_A2LIST }, #endif #ifdef PPP_FILTER { "pass-filter", o_special, setpassfilter, "set filter for packets to pass", OPT_PRIO }, { "active-filter", o_special, setactivefilter, "set filter for active pkts", OPT_PRIO }, #endif #ifdef MAXOCTETS { "maxoctets", o_int, &maxoctets, "Set connection traffic limit", OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF }, { "mo", o_int, &maxoctets, "Set connection traffic limit", OPT_ALIAS | OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF }, { "mo-direction", o_special, setmodir, "Set direction for limit traffic (sum,in,out,max)" }, { "mo-timeout", o_int, &maxoctets_timeout, "Check for traffic limit every N seconds", OPT_PRIO | OPT_LLIMIT | 1 }, #endif { NULL } }; #ifndef IMPLEMENTATION #define IMPLEMENTATION "" #endif static char *usage_string = "\ pppd version %s\n\ Usage: %s [ options ], where options 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\ See pppd(8) for more options.\n\ "; /* * parse_args - parse a string of arguments from the command line. */ int parse_args(argc, argv) int argc; char **argv; { char *arg; option_t *opt; int n; privileged_option = privileged; option_source = "command line"; option_priority = OPRIO_CMDLINE; while (argc > 0) { arg = *argv++; --argc; opt = find_option(arg); if (opt == NULL) { option_error("unrecognized option '%s'", arg); usage(); return 0; } n = n_arguments(opt); if (argc < n) { option_error("too few parameters for option %s", arg); return 0; } if (!process_option(opt, arg, argv)) return 0; argc -= n; argv += n; } return 1; } /* * options_from_file - Read a string of options from a file, * and interpret them. */ int options_from_file(filename, must_exist, check_prot, priv) char *filename; int must_exist; int check_prot; int priv; { FILE *f; int i, newline, ret, err; option_t *opt; int oldpriv, n; char *oldsource; uid_t euid; char *argv[MAXARGS]; char args[MAXARGS][MAXWORDLEN]; char cmd[MAXWORDLEN]; euid = geteuid(); if (check_prot && seteuid(getuid()) == -1) { option_error("unable to drop privileges to open %s: %m", filename); return 0; } f = fopen(filename, "r"); err = errno; if (check_prot && seteuid(euid) == -1) fatal("unable to regain privileges"); if (f == NULL) { errno = err; if (!must_exist) { if (err != ENOENT && err != ENOTDIR) warn("Warning: can't open options file %s: %m", filename); return 1; } option_error("Can't open options file %s: %m", filename); return 0; } oldpriv = privileged_option; privileged_option = priv; oldsource = option_source; option_source = strdup(filename); if (option_source == NULL) option_source = "file"; ret = 0; while (getword(f, cmd, &newline, filename)) { opt = find_option(cmd); if (opt == NULL) { option_error("In file %s: unrecognized option '%s'", filename, cmd); goto err; } n = n_arguments(opt); for (i = 0; i < n; ++i) { if (!getword(f, args[i], &newline, filename)) { option_error( "In file %s: too few parameters for option '%s'", filename, cmd); goto err; } argv[i] = args[i]; } if (!process_option(opt, cmd, argv)) goto err; } ret = 1; err: fclose(f); privileged_option = oldpriv; option_source = oldsource; return ret; } /* * 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; size_t pl; pw = getpwuid(getuid()); if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) return 1; file = _PATH_USEROPT; pl = strlen(user) + strlen(file) + 2; path = malloc(pl); if (path == NULL) novm("init file name"); slprintf(path, pl, "%s/%s", user, file); option_priority = OPRIO_CFGFILE; ret = options_from_file(path, 0, 1, privileged); free(path); return ret; } /* * options_for_tty - See if an options file exists for the serial * device, and if so, interpret options from it. * We only allow the per-tty options file to override anything from * the command line if it is something that the user can't override * once it has been set by root; this is done by giving configuration * files a lower priority than the command line. */ int options_for_tty() { char *dev, *path, *p; int ret; size_t pl; dev = devnam; if ((p = strstr(dev, "/dev/")) != NULL) dev = p + 5; if (dev[0] == 0 || strcmp(dev, "tty") == 0) return 1; /* don't look for /etc/ppp/options.tty */ pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1; path = malloc(pl); if (path == NULL) novm("tty init file name"); slprintf(path, pl, "%s%s", _PATH_TTYOPT, dev); /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */ for (p = path + strlen(_PATH_TTYOPT); *p != 0; ++p) if (*p == '/') *p = '.'; option_priority = OPRIO_CFGFILE; ret = options_from_file(path, 0, 0, 1); free(path); return ret; } /* * options_from_list - process a string of options in a wordlist. */ int options_from_list(w, priv) struct wordlist *w; int priv; { char *argv[MAXARGS]; option_t *opt; int i, n, ret = 0; struct wordlist *w0; privileged_option = priv; option_source = "secrets file"; option_priority = OPRIO_SECFILE; while (w != NULL) { opt = find_option(w->word); if (opt == NULL) { option_error("In secrets file: unrecognized option '%s'", w->word); goto err; } n = n_arguments(opt); w0 = w; for (i = 0; i < n; ++i) { w = w->next; if (w == NULL) { option_error( "In secrets file: too few parameters for option '%s'", w0->word); goto err; } argv[i] = w->word; } if (!process_option(opt, w0->word, argv)) goto err; w = w->next; } ret = 1; err: return ret; } /* * match_option - see if this option matches an option_t structure. */ static int match_option(name, opt, dowild) char *name; option_t *opt; int dowild; { int (*match) __P((char *, char **, int)); if (dowild != (opt->type == o_wild)) return 0; if (!dowild) return strcmp(name, opt->name) == 0; match = (int (*) __P((char *, char **, int))) opt->addr; return (*match)(name, NULL, 0); } /* * find_option - scan the option lists for the various protocols * looking for an entry with the given name. * This could be optimized by using a hash table. */ static option_t * find_option(name) const char *name; { option_t *opt; struct option_list *list; int i, dowild; for (dowild = 0; dowild <= 1; ++dowild) { for (opt = general_options; opt->name != NULL; ++opt) if (match_option(name, opt, dowild)) return opt; for (opt = auth_options; opt->name != NULL; ++opt) if (match_option(name, opt, dowild)) return opt; for (list = extra_options; list != NULL; list = list->next) for (opt = list->options; opt->name != NULL; ++opt) if (match_option(name, opt, dowild)) return opt; for (opt = the_channel->options; opt->name != NULL; ++opt) if (match_option(name, opt, dowild)) return opt; for (i = 0; protocols[i] != NULL; ++i) if ((opt = protocols[i]->options) != NULL) for (; opt->name != NULL; ++opt) if (match_option(name, opt, dowild)) return opt; } return NULL; } /* * process_option - process one new-style option. */ static int process_option(opt, cmd, argv) option_t *opt; char *cmd; char **argv; { u_int32_t v; int iv, a; char *sv; int (*parser) __P((char **)); int (*wildp) __P((char *, char **, int)); char *optopt = (opt->type == o_wild)? "": " option"; int prio = option_priority; option_t *mainopt = opt; current_option = opt->name; if ((opt->flags & OPT_PRIVFIX) && privileged_option) prio += OPRIO_ROOT; while (mainopt->flags & OPT_PRIOSUB) --mainopt; if (mainopt->flags & OPT_PRIO) { if (prio < mainopt->priority) { /* new value doesn't override old */ if (prio == OPRIO_CMDLINE && mainopt->priority > OPRIO_ROOT) { option_error("%s%s set in %s cannot be overridden\n", opt->name, optopt, mainopt->source); return 0; } return 1; } if (prio > OPRIO_ROOT && mainopt->priority == OPRIO_CMDLINE) warn("%s%s from %s overrides command line", opt->name, optopt, option_source); } if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) { option_error("%s%s cannot be changed after initialization", opt->name, optopt); return 0; } if ((opt->flags & OPT_PRIV) && !privileged_option) { option_error("using the %s%s requires root privilege", opt->name, optopt); return 0; } if ((opt->flags & OPT_ENABLE) && *(bool *)(opt->addr2) == 0) { option_error("%s%s is disabled", opt->name, optopt); return 0; } if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) { option_error("the %s%s may not be changed in %s", opt->name, optopt, option_source); return 0; } switch (opt->type) { case o_bool: v = opt->flags & OPT_VALUE; *(bool *)(opt->addr) = v; if (opt->addr2 && (opt->flags & OPT_A2COPY)) *(bool *)(opt->addr2) = v; else if (opt->addr2 && (opt->flags & OPT_A2CLR)) *(bool *)(opt->addr2) = 0; else if (opt->addr2 && (opt->flags & OPT_A2CLRB)) *(u_char *)(opt->addr2) &= ~v; else if (opt->addr2 && (opt->flags & OPT_A2OR)) *(u_char *)(opt->addr2) |= v; break; case o_int: iv = 0; if ((opt->flags & OPT_NOARG) == 0) { if (!int_option(*argv, &iv)) return 0; if ((((opt->flags & OPT_LLIMIT) && iv < opt->lower_limit) || ((opt->flags & OPT_ULIMIT) && iv > opt->upper_limit)) && !((opt->flags & OPT_ZEROOK && iv == 0))) { char *zok = (opt->flags & OPT_ZEROOK)? " zero or": ""; switch (opt->flags & OPT_LIMITS) { case OPT_LLIMIT: option_error("%s value must be%s >= %d", opt->name, zok, opt->lower_limit); break; case OPT_ULIMIT: option_error("%s value must be%s <= %d", opt->name, zok, opt->upper_limit); break; case OPT_LIMITS: option_error("%s value must be%s between %d and %d", opt->name, zok, opt->lower_limit, opt->upper_limit); break; } return 0; } } a = opt->flags & OPT_VALUE; if (a >= 128) a -= 256; /* sign extend */ iv += a; if (opt->flags & OPT_INC) iv += *(int *)(opt->addr); if ((opt->flags & OPT_NOINCR) && !privileged_option) { int oldv = *(int *)(opt->addr); if ((opt->flags & OPT_ZEROINF) ? (oldv != 0 && (iv == 0 || iv > oldv)) : (iv > oldv)) { option_error("%s value cannot be increased", opt->name); return 0; } } *(int *)(opt->addr) = iv; if (opt->addr2 && (opt->flags & OPT_A2COPY)) *(int *)(opt->addr2) = iv; break; case o_uint32: if (opt->flags & OPT_NOARG) { v = opt->flags & OPT_VALUE; if (v & 0x80) v |= 0xffffff00U; } else if (!number_option(*argv, &v, 16)) return 0; if (opt->flags & OPT_OR) v |= *(u_int32_t *)(opt->addr); *(u_int32_t *)(opt->addr) = v; if (opt->addr2 && (opt->flags & OPT_A2COPY)) *(u_int32_t *)(opt->addr2) = v; break; case o_string: if (opt->flags & OPT_STATIC) { strlcpy((char *)(opt->addr), *argv, opt->upper_limit); } else { sv = strdup(*argv); if (sv == NULL) novm("option argument"); *(char **)(opt->addr) = sv; } break; case o_special_noarg: case o_special: parser = (int (*) __P((char **))) opt->addr; if (!(*parser)(argv)) return 0; if (opt->flags & OPT_A2LIST) { struct option_value *ovp, *pp; ovp = malloc(sizeof(*ovp) + strlen(*argv)); if (ovp != 0) { strcpy(ovp->value, *argv); ovp->source = option_source; ovp->next = NULL; if (opt->addr2 == NULL) { opt->addr2 = ovp; } else { for (pp = opt->addr2; pp->next != NULL; pp = pp->next) ; pp->next = ovp; } } } break; case o_wild: wildp = (int (*) __P((char *, char **, int))) opt->addr; if (!(*wildp)(cmd, argv, 1)) return 0; break; } /* * If addr2 wasn't used by any flag (OPT_A2COPY, etc.) but is set, * treat it as a bool and set/clear it based on the OPT_A2CLR bit. */ if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE |OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST|OPT_A2OR)) == 0) *(bool *)(opt->addr2) = !(opt->flags & OPT_A2CLR); mainopt->source = option_source; mainopt->priority = prio; mainopt->winner = opt - mainopt; return 1; } /* * override_value - if the option priorities would permit us to * override the value of option, return 1 and update the priority * and source of the option value. Otherwise returns 0. */ int override_value(option, priority, source) const char *option; int priority; const char *source; { option_t *opt; opt = find_option(option); if (opt == NULL) return 0; while (opt->flags & OPT_PRIOSUB) --opt; if ((opt->flags & OPT_PRIO) && priority < opt->priority) return 0; opt->priority = priority; opt->source = source; opt->winner = -1; return 1; } /* * n_arguments - tell how many arguments an option takes */ static int n_arguments(opt) option_t *opt; { return (opt->type == o_bool || opt->type == o_special_noarg || (opt->flags & OPT_NOARG))? 0: 1; } /* * add_options - add a list of options to the set we grok. */ void add_options(opt) option_t *opt; { struct option_list *list; list = malloc(sizeof(*list)); if (list == 0) novm("option list entry"); list->options = opt; list->next = extra_options; extra_options = list; } /* * check_options - check that options are valid and consistent. */ void check_options() { if (logfile_fd >= 0 && logfile_fd != log_to_fd) close(logfile_fd); } /* * print_option - print out an option and its value */ static void print_option(opt, mainopt, printer, arg) option_t *opt, *mainopt; void (*printer) __P((void *, char *, ...)); void *arg; { int i, v; char *p; if (opt->flags & OPT_NOPRINT) return; switch (opt->type) { case o_bool: v = opt->flags & OPT_VALUE; if (*(bool *)opt->addr != v) /* this can happen legitimately, e.g. lock option turned off for default device */ break; printer(arg, "%s", opt->name); break; case o_int: v = opt->flags & OPT_VALUE; if (v >= 128) v -= 256; i = *(int *)opt->addr; if (opt->flags & OPT_NOARG) { printer(arg, "%s", opt->name); if (i != v) { if (opt->flags & OPT_INC) { for (; i > v; i -= v) printer(arg, " %s", opt->name); } else printer(arg, " # oops: %d not %d\n", i, v); } } else { printer(arg, "%s %d", opt->name, i); } break; case o_uint32: printer(arg, "%s", opt->name); if ((opt->flags & OPT_NOARG) == 0) printer(arg, " %x", *(u_int32_t *)opt->addr); break; case o_string: if (opt->flags & OPT_HIDE) { p = "??????"; } else { p = (char *) opt->addr; if ((opt->flags & OPT_STATIC) == 0) p = *(char **)p; } printer(arg, "%s %q", opt->name, p); break; case o_special: case o_special_noarg: case o_wild: if (opt->type != o_wild) { printer(arg, "%s", opt->name); if (n_arguments(opt) == 0) break; printer(arg, " "); } if (opt->flags & OPT_A2PRINTER) { void (*oprt) __P((option_t *, void ((*)__P((void *, char *, ...))), void *)); oprt = (void (*) __P((option_t *, void ((*)__P((void *, char *, ...))), void *)))opt->addr2; (*oprt)(opt, printer, arg); } else if (opt->flags & OPT_A2STRVAL) { p = (char *) opt->addr2; if ((opt->flags & OPT_STATIC) == 0) p = *(char **)p; printer("%q", p); } else if (opt->flags & OPT_A2LIST) { struct option_value *ovp; ovp = (struct option_value *) opt->addr2; for (;;) { printer(arg, "%q", ovp->value); if ((ovp = ovp->next) == NULL) break; printer(arg, "\t\t# (from %s)\n%s ", ovp->source, opt->name); } } else { printer(arg, "xxx # [don't know how to print value]"); } break; default: printer(arg, "# %s value (type %d\?\?)", opt->name, opt->type); break; } printer(arg, "\t\t# (from %s)\n", mainopt->source); } /* * print_option_list - print out options in effect from an * array of options. */ static void print_option_list(opt, printer, arg) option_t *opt; void (*printer) __P((void *, char *, ...)); void *arg; { while (opt->name != NULL) { if (opt->priority != OPRIO_DEFAULT && opt->winner != (short int) -1) print_option(opt + opt->winner, opt, printer, arg); do { ++opt; } while (opt->flags & OPT_PRIOSUB); } } /* * print_options - print out what options are in effect. */ void print_options(printer, arg) void (*printer) __P((void *, char *, ...)); void *arg; { struct option_list *list; int i; printer(arg, "pppd options in effect:\n"); print_option_list(general_options, printer, arg); print_option_list(auth_options, printer, arg); for (list = extra_options; list != NULL; list = list->next) print_option_list(list->options, printer, arg); print_option_list(the_channel->options, printer, arg); for (i = 0; protocols[i] != NULL; ++i) print_option_list(protocols[i]->options, printer, arg); } /* * usage - print out a message telling how to use the program. */ static void usage() { if (phase == PHASE_INITIALIZE) fprintf(stderr, usage_string, VERSION, progname); } /* * showhelp - print out usage message and exit. */ static int showhelp(argv) char **argv; { if (phase == PHASE_INITIALIZE) { usage(); exit(0); } return 0; } /* * showversion - print out the version number and exit. */ static int showversion(argv) char **argv; { if (phase == PHASE_INITIALIZE) { fprintf(stderr, "pppd version %s\n", VERSION); exit(0); } return 0; } /* * option_error - print a message about an error in an option. * The message is logged, and also sent to * stderr if phase == PHASE_INITIALIZE. */ void option_error __V((char *fmt, ...)) { va_list args; char buf[1024]; #if defined(__STDC__) va_start(args, fmt); #else char *fmt; va_start(args); fmt = va_arg(args, char *); #endif vslprintf(buf, sizeof(buf), fmt, args); va_end(args); if (phase == PHASE_INITIALIZE) fprintf(stderr, "%s: %s\n", progname, buf); syslog(LOG_ERR, "%s", buf); } #if 0 /* * readable - check if a file is readable by the real user. */ int readable(fd) int fd; { uid_t uid; int i; struct stat sbuf; 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; for (i = 0; i < ngroups; ++i) if (sbuf.st_gid == groups[i]) return sbuf.st_mode & S_IRGRP; return sbuf.st_mode & S_IROTH; } #endif /* * Read a word from a file. * Words are delimited by white-space or by quotes (" or '). * 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, comment; int value, digit, got, n; #define isoctal(c) ((c) >= '0' && (c) < '8') *newlinep = 0; len = 0; escape = 0; comment = 0; /* * First skip white-space and comments. */ for (;;) { c = getc(f); if (c == EOF) break; /* * A newline means the end of a comment; backslash-newline * is ignored. Note that we cannot have escape && comment. */ if (c == '\n') { if (!escape) { *newlinep = 1; comment = 0; } else escape = 0; continue; } /* * Ignore characters other than newline in a comment. */ if (comment) continue; /* * If this character is escaped, we have a word start. */ if (escape) break; /* * If this is the escape character, look at the next character. */ if (c == '\\') { escape = 1; continue; } /* * If this is the start of a comment, ignore the rest of the line. */ if (c == '#') { comment = 1; continue; } /* * A non-whitespace character is the start of a word. */ if (!isspace(c)) break; } /* * Save the delimiter for quoted strings. */ if (!escape && (c == '"' || c == '\'')) { quoted = c; c = getc(f); } else quoted = 0; /* * Process characters until the end of the word. */ while (c != EOF) { if (escape) { /* * This character is escaped: backslash-newline is ignored, * various other characters indicate particular values * as for C backslash-escapes. */ escape = 0; if (c == '\n') { c = getc(f); continue; } got = 0; switch (c) { case 'a': value = '\a'; break; case 'b': value = '\b'; break; case 'f': value = '\f'; break; case 'n': value = '\n'; break; case 'r': value = '\r'; break; case 's': value = ' '; break; case 't': value = '\t'; break; default: if (isoctal(c)) { /* * \ddd octal sequence */ value = 0; for (n = 0; n < 3 && isoctal(c); ++n) { value = (value << 3) + (c & 07); c = getc(f); } got = 1; break; } if (c == 'x') { /* * \x sequence */ value = 0; c = getc(f); for (n = 0; n < 2 && isxdigit(c); ++n) { digit = toupper(c) - '0'; if (digit > 10) digit += '0' + 10 - 'A'; value = (value << 4) + digit; c = getc (f); } got = 1; break; } /* * Otherwise the character stands for itself. */ value = c; break; } /* * Store the resulting character for the escape sequence. */ if (len < MAXWORDLEN-1) word[len] = value; ++len; if (!got) c = getc(f); continue; } /* * Not escaped: see if we've reached the end of the word. */ if (quoted) { if (c == quoted) break; } else { if (isspace(c) || c == '#') { ungetc (c, f); break; } } /* * Backslash starts an escape sequence. */ if (c == '\\') { escape = 1; c = getc(f); continue; } /* * An ordinary character: store it in the word and get another. */ if (len < MAXWORDLEN-1) word[len] = c; ++len; c = getc(f); } /* * End of the word: check for errors. */ if (c == EOF) { if (ferror(f)) { if (errno == 0) errno = EIO; option_error("Error reading %s: %m", filename); die(1); } /* * If len is zero, then we didn't find a word before the * end of the file. */ if (len == 0) return 0; } /* * Warn if the word was too long, and append a terminating null. */ if (len >= MAXWORDLEN) { option_error("warning: word in file %s too long (%.20s...)", filename, word); len = MAXWORDLEN - 1; } word[len] = 0; return 1; #undef isoctal } /* * number_option - parse an unsigned numeric parameter for an option. */ static int number_option(str, valp, base) char *str; u_int32_t *valp; int base; { char *ptr; *valp = strtoul(str, &ptr, base); if (ptr == str) { option_error("invalid numeric parameter '%s' for %s option", str, current_option); 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. */ int int_option(str, valp) char *str; int *valp; { u_int32_t v; if (!number_option(str, &v, 0)) return 0; *valp = (int) v; return 1; } /* * The following procedures parse options. */ /* * readfile - take commands from a file. */ static int readfile(argv) char **argv; { return options_from_file(*argv, 1, 1, privileged_option); } /* * callfile - take commands from /etc/ppp/peers/. * Name may not contain /../, start with / or ../, or end in /.. */ static int callfile(argv) char **argv; { char *fname, *arg, *p; int l, ok; arg = *argv; ok = 1; if (arg[0] == '/' || arg[0] == 0) ok = 0; else { for (p = arg; *p != 0; ) { if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == 0)) { ok = 0; break; } while (*p != '/' && *p != 0) ++p; if (*p == '/') ++p; } } if (!ok) { option_error("call option value may not contain .. or start with /"); return 0; } l = strlen(arg) + strlen(_PATH_PEERFILES) + 1; if ((fname = (char *) malloc(l)) == NULL) novm("call file name"); slprintf(fname, l, "%s%s", _PATH_PEERFILES, arg); ok = options_from_file(fname, 1, 1, 1); free(fname); return ok; } #ifdef PPP_FILTER /* * setpassfilter - Set the pass filter for packets */ static int setpassfilter(argv) char **argv; { pcap_t *pc; int ret = 1; pc = pcap_open_dead(DLT_PPP_PPPD, 65535); if (pcap_compile(pc, &pass_filter, *argv, 1, netmask) == -1) { option_error("error in pass-filter expression: %s\n", pcap_geterr(pc)); ret = 0; } pcap_close(pc); return ret; } /* * setactivefilter - Set the active filter for packets */ static int setactivefilter(argv) char **argv; { pcap_t *pc; int ret = 1; pc = pcap_open_dead(DLT_PPP_PPPD, 65535); if (pcap_compile(pc, &active_filter, *argv, 1, netmask) == -1) { option_error("error in active-filter expression: %s\n", pcap_geterr(pc)); ret = 0; } pcap_close(pc); return ret; } #endif /* * setdomain - Set domain name to append to hostname */ static int setdomain(argv) char **argv; { gethostname(hostname, MAXNAMELEN); if (**argv != 0) { if (**argv != '.') strncat(hostname, ".", MAXNAMELEN - strlen(hostname)); domain = hostname + strlen(hostname); strncat(hostname, *argv, MAXNAMELEN - strlen(hostname)); } hostname[MAXNAMELEN-1] = 0; return (1); } static int setlogfile(argv) char **argv; { int fd, err; uid_t euid; euid = geteuid(); if (!privileged_option && seteuid(getuid()) == -1) { option_error("unable to drop permissions to open %s: %m", *argv); return 0; } fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644); if (fd < 0 && errno == EEXIST) fd = open(*argv, O_WRONLY | O_APPEND); err = errno; if (!privileged_option && seteuid(euid) == -1) fatal("unable to regain privileges: %m"); if (fd < 0) { errno = err; option_error("Can't open log file %s: %m", *argv); return 0; } strlcpy(logfile_name, *argv, sizeof(logfile_name)); if (logfile_fd >= 0) close(logfile_fd); logfile_fd = fd; log_to_fd = fd; log_default = 0; return 1; } #ifdef MAXOCTETS static int setmodir(argv) char **argv; { if(*argv == NULL) return 0; if(!strcmp(*argv,"in")) { maxoctets_dir = PPP_OCTETS_DIRECTION_IN; } else if (!strcmp(*argv,"out")) { maxoctets_dir = PPP_OCTETS_DIRECTION_OUT; } else if (!strcmp(*argv,"max")) { maxoctets_dir = PPP_OCTETS_DIRECTION_MAXOVERAL; } else { maxoctets_dir = PPP_OCTETS_DIRECTION_SUM; } return 1; } #endif #ifdef PLUGIN static int loadplugin(argv) char **argv; { char *arg = *argv; void *handle; const char *err; void (*init) __P((void)); char *path = arg; const char *vers; if (strchr(arg, '/') == 0) { const char *base = _PATH_PLUGIN; int l = strlen(base) + strlen(arg) + 2; path = malloc(l); if (path == 0) novm("plugin file path"); strlcpy(path, base, l); strlcat(path, "/", l); strlcat(path, arg, l); } handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW); if (handle == 0) { err = dlerror(); if (err != 0) option_error("%s", err); option_error("Couldn't load plugin %s", arg); goto err; } init = (void (*)(void))dlsym(handle, "plugin_init"); if (init == 0) { option_error("%s has no initialization entry point", arg); goto errclose; } vers = (const char *) dlsym(handle, "pppd_version"); if (vers == 0) { warn("Warning: plugin %s has no version information", arg); } else if (strcmp(vers, VERSION) != 0) { option_error("Plugin %s is for pppd version %s, this is %s", arg, vers, VERSION); goto errclose; } info("Plugin %s loaded.", arg); (*init)(); return 1; errclose: dlclose(handle); err: if (path != arg) free(path); return 0; } #endif /* PLUGIN */ ppp-2.4.5/pppd/patchlevel.h000066400000000000000000000000721130035057700155640ustar00rootroot00000000000000#define VERSION "2.4.5" #define DATE "17 November 2009" ppp-2.4.5/pppd/pathnames.h000066400000000000000000000034251130035057700154220ustar00rootroot00000000000000/* * define path names * * $Id: pathnames.h,v 1.18 2005/08/25 23:59:34 paulus Exp $ */ #ifdef HAVE_PATHS_H #include #else /* HAVE_PATHS_H */ #ifndef _PATH_VARRUN #define _PATH_VARRUN "/etc/ppp/" #endif #define _PATH_DEVNULL "/dev/null" #endif /* HAVE_PATHS_H */ #ifndef _ROOT_PATH #define _ROOT_PATH #endif #define _PATH_UPAPFILE _ROOT_PATH "/etc/ppp/pap-secrets" #define _PATH_CHAPFILE _ROOT_PATH "/etc/ppp/chap-secrets" #define _PATH_SRPFILE _ROOT_PATH "/etc/ppp/srp-secrets" #define _PATH_SYSOPTIONS _ROOT_PATH "/etc/ppp/options" #define _PATH_IPUP _ROOT_PATH "/etc/ppp/ip-up" #define _PATH_IPDOWN _ROOT_PATH "/etc/ppp/ip-down" #define _PATH_IPPREUP _ROOT_PATH "/etc/ppp/ip-pre-up" #define _PATH_AUTHUP _ROOT_PATH "/etc/ppp/auth-up" #define _PATH_AUTHDOWN _ROOT_PATH "/etc/ppp/auth-down" #define _PATH_TTYOPT _ROOT_PATH "/etc/ppp/options." #define _PATH_CONNERRS _ROOT_PATH "/etc/ppp/connect-errors" #define _PATH_PEERFILES _ROOT_PATH "/etc/ppp/peers/" #define _PATH_RESOLV _ROOT_PATH "/etc/ppp/resolv.conf" #define _PATH_USEROPT ".ppprc" #define _PATH_PSEUDONYM ".ppp_pseudonym" #ifdef INET6 #define _PATH_IPV6UP _ROOT_PATH "/etc/ppp/ipv6-up" #define _PATH_IPV6DOWN _ROOT_PATH "/etc/ppp/ipv6-down" #endif #ifdef IPX_CHANGE #define _PATH_IPXUP _ROOT_PATH "/etc/ppp/ipx-up" #define _PATH_IPXDOWN _ROOT_PATH "/etc/ppp/ipx-down" #endif /* IPX_CHANGE */ #ifdef __STDC__ #define _PATH_PPPDB _ROOT_PATH _PATH_VARRUN "pppd2.tdb" #else /* __STDC__ */ #ifdef HAVE_PATHS_H #define _PATH_PPPDB "/var/run/pppd2.tdb" #else #define _PATH_PPPDB "/etc/ppp/pppd2.tdb" #endif #endif /* __STDC__ */ #ifdef PLUGIN #ifdef __STDC__ #define _PATH_PLUGIN DESTDIR "/lib/pppd/" VERSION #else /* __STDC__ */ #define _PATH_PLUGIN "/usr/lib/pppd" #endif /* __STDC__ */ #endif /* PLUGIN */ ppp-2.4.5/pppd/plugins/000077500000000000000000000000001130035057700147465ustar00rootroot00000000000000ppp-2.4.5/pppd/plugins/Makefile.linux000066400000000000000000000020271130035057700175450ustar00rootroot00000000000000#CC = gcc COPTS = -O2 -g CFLAGS = $(COPTS) -I.. -I../../include -fPIC LDFLAGS = -shared INSTALL = install DESTDIR = $(INSTROOT)@DESTDIR@ BINDIR = $(DESTDIR)/sbin MANDIR = $(DESTDIR)/share/man/man8 LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION) SUBDIRS := rp-pppoe pppoatm pppol2tp # Uncomment the next line to include the radius authentication plugin SUBDIRS += radius PLUGINS := minconn.so passprompt.so passwordfd.so winbind.so # include dependencies if present ifeq (.depend,$(wildcard .depend)) include .depend endif all: $(PLUGINS) for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d all; done %.so: %.c $(CC) -o $@ $(LDFLAGS) $(CFLAGS) $^ VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../patchlevel.h) install: $(PLUGINS) $(INSTALL) -d $(LIBDIR) $(INSTALL) $? $(LIBDIR) for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d install; done clean: rm -f *.o *.so *.a for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d clean; done depend: $(CPP) -M $(CFLAGS) *.c >.depend for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d depend; done ppp-2.4.5/pppd/plugins/Makefile.sol2000066400000000000000000000007131130035057700172650ustar00rootroot00000000000000# # Makefile for plugins on Solaris 2 # # $Id: Makefile.sol2,v 1.3 2002/09/07 05:15:25 carlsonj Exp $ # include ../../Makedefs.com CFLAGS = -c -O -I.. -I../../include $(COPTS) LDFLAGS = -G all: minconn.so minconn.so: minconn.o ld -o $@ $(LDFLAGS) -h $@ minconn.o minconn.o: minconn.c $(CC) $(CFLAGS) -c $? passprompt.so: passprompt.o ld -o $@ $(LDFLAGS) -h $@ passprompt.o passprompt.o: passprompt.c $(CC) $(CFLAGS) -c $? clean: rm -f *.o *.so ppp-2.4.5/pppd/plugins/minconn.c000066400000000000000000000040701130035057700165540ustar00rootroot00000000000000/* * minconn.c - pppd plugin to implement a `minconnect' option. * * Copyright (c) 1999 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "pppd.h" char pppd_version[] = VERSION; static int minconnect = 0; static option_t my_options[] = { { "minconnect", o_int, &minconnect, "Set minimum connect time before idle timeout applies" }, { NULL } }; static int my_get_idle(struct ppp_idle *idle) { time_t t; if (idle == NULL) return minconnect? minconnect: idle_time_limit; t = idle->xmit_idle; if (idle->recv_idle < t) t = idle->recv_idle; return idle_time_limit - t; } void plugin_init(void) { info("plugin_init"); add_options(my_options); idle_time_hook = my_get_idle; } ppp-2.4.5/pppd/plugins/passprompt.c000066400000000000000000000046231130035057700173270ustar00rootroot00000000000000/* * passprompt.c - pppd plugin to invoke an external PAP password prompter * * Copyright 1999 Paul Mackerras, Alan Curry. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include "pppd.h" char pppd_version[] = VERSION; static char promptprog[PATH_MAX+1]; static option_t options[] = { { "promptprog", o_string, promptprog, "External PAP password prompting program", OPT_STATIC, NULL, PATH_MAX }, { NULL } }; static int promptpass(char *user, char *passwd) { int p[2]; pid_t kid; int readgood, wstat; ssize_t red; if (promptprog[0] == 0 || access(promptprog, X_OK) < 0) return -1; /* sorry, can't help */ if (!passwd) return 1; if (pipe(p)) { warn("Can't make a pipe for %s", promptprog); return 0; } if ((kid = fork()) == (pid_t) -1) { warn("Can't fork to run %s", promptprog); close(p[0]); close(p[1]); return 0; } if (!kid) { /* we are the child, exec the program */ char *argv[5], fdstr[32]; sys_close(); closelog(); close(p[0]); seteuid(getuid()); setegid(getgid()); argv[0] = promptprog; argv[1] = user; argv[2] = remote_name; sprintf(fdstr, "%d", p[1]); argv[3] = fdstr; argv[4] = 0; execv(*argv, argv); _exit(127); } /* we are the parent, read the password from the pipe */ close(p[1]); readgood = 0; do { red = read(p[0], passwd + readgood, MAXSECRETLEN-1 - readgood); if (red == 0) break; if (red < 0) { if (errno == EINTR) continue; error("Can't read secret from %s: %m", promptprog); readgood = -1; break; } readgood += red; } while (readgood < MAXSECRETLEN - 1); passwd[readgood] = 0; close(p[0]); /* now wait for child to exit */ while (waitpid(kid, &wstat, 0) < 0) { if (errno != EINTR) { warn("error waiting for %s: %m", promptprog); break; } } if (readgood < 0) return 0; if (!WIFEXITED(wstat)) warn("%s terminated abnormally", promptprog); if (WEXITSTATUS(wstat)) warn("%s exited with code %d", promptprog, WEXITSTATUS(status)); return 1; } void plugin_init(void) { add_options(options); pap_passwd_hook = promptpass; } ppp-2.4.5/pppd/plugins/passwordfd.c000066400000000000000000000026511130035057700172720ustar00rootroot00000000000000 /* * Author: Arvin Schnell * * This plugin let's you pass the password to the pppd via * a file descriptor. That's easy and secure - no fiddling * with pap- and chap-secrets files. */ #include #include #include #include #include "pppd.h" char pppd_version[] = VERSION; static int passwdfd = -1; static char save_passwd[MAXSECRETLEN]; static option_t options[] = { { "passwordfd", o_int, &passwdfd, "Receive password on this file descriptor" }, { NULL } }; static int pwfd_check (void) { return 1; } static int pwfd_passwd (char *user, char *passwd) { int readgood, red; if (passwdfd == -1) return -1; if (passwd == NULL) return 1; if (passwdfd == -2) { strcpy (passwd, save_passwd); return 1; } readgood = 0; do { red = read (passwdfd, passwd + readgood, MAXSECRETLEN - 1 - readgood); if (red == 0) break; if (red < 0) { error ("Can't read secret from fd\n"); readgood = -1; break; } readgood += red; } while (readgood < MAXSECRETLEN - 1); close (passwdfd); if (readgood < 0) return 0; passwd[readgood] = 0; strcpy (save_passwd, passwd); passwdfd = -2; return 1; } void plugin_init (void) { add_options (options); pap_check_hook = pwfd_check; pap_passwd_hook = pwfd_passwd; chap_check_hook = pwfd_check; chap_passwd_hook = pwfd_passwd; } ppp-2.4.5/pppd/plugins/pppoatm/000077500000000000000000000000001130035057700164265ustar00rootroot00000000000000ppp-2.4.5/pppd/plugins/pppoatm/COPYING000066400000000000000000000004711130035057700174630ustar00rootroot00000000000000The files ans.c, atm.h, atmres.h, atmsap.h, misc.c, text2atm.c and text2qos.c are taken from the linux-atm libraries. These are Copyright 1995-2000 EPFL-LRC/ICA, and are licensed under the GNU Lesser General Public License. The file pppoatm.c contains its own copyright notice, and is licensed under the GPL. ppp-2.4.5/pppd/plugins/pppoatm/Makefile.linux000066400000000000000000000016641130035057700212330ustar00rootroot00000000000000#CC = gcc COPTS = -O2 -g CFLAGS = $(COPTS) -I../.. -I../../../include -fPIC LDFLAGS = -shared INSTALL = install #*********************************************************************** DESTDIR = $(INSTROOT)@DESTDIR@ LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION) VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h) PLUGIN := pppoatm.so PLUGIN_OBJS := pppoatm.o #******* # Do we have the ATM libraries installed? Set HAVE_LIBATM to use them, # or leave it unset to build the few routines we actually _use_ into # the plugin directly. # #HAVE_LIBATM=yes ifdef HAVE_LIBATM LIBS := -latm else CFLAGS += -I. PLUGIN_OBJS += text2qos.o text2atm.o misc.o ans.o LIBS := -lresolv endif #********* all: $(PLUGIN) $(PLUGIN): $(PLUGIN_OBJS) $(CC) $(CFLAGS) -o $@ -shared $^ $(LIBS) install: all $(INSTALL) -d -m 755 $(LIBDIR) $(INSTALL) -c -m 4550 $(PLUGIN) $(LIBDIR) clean: rm -f *.o *.so %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< ppp-2.4.5/pppd/plugins/pppoatm/ans.c000066400000000000000000000150231130035057700173540ustar00rootroot00000000000000/* ans.c - Interface for text2atm and atm2text to ANS */ /* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */ /* * This stuff is a temporary hack to avoid using gethostbyname_nsap and such * without doing the "full upgrade" to getaddrinfo/getnameinfo. This also * serves as an exercise for me to get all the details right before I propose * a patch that would eventually end up in libc (and that should therefore be * as stable as possible). */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "atm.h" #include "atmres.h" #define MAX_ANSWER 2048 #define MAX_NAME 1024 #define MAX_LINE 2048 /* in /etc/e164_cc */ #define E164_CC_DEFAULT_LEN 2 #define E164_CC_FILE "/etc/e164_cc" #define GET16(pos) (((pos)[0] << 8) | (pos)[1]) static int ans(const char *text,int wanted,void *result,int res_len) { unsigned char answer[MAX_ANSWER]; unsigned char name[MAX_NAME]; unsigned char *pos,*data,*found; int answer_len,name_len,data_len,found_len; int questions,answers; found_len = 0; /* gcc wants it */ if ((answer_len = res_search(text,C_IN,wanted,answer,MAX_ANSWER)) < 0) return TRY_OTHER; /* * Response header: id, flags, #queries, #answers, #authority, * #additional (all 16 bits) */ pos = answer+12; if (answer[3] & 15) return TRY_OTHER; /* rcode != 0 */ questions = GET16(answer+4); if (questions != 1) return TRY_OTHER; /* trouble ... */ answers = GET16(answer+6); if (answers < 1) return TRY_OTHER; /* * Query: name, type (16), class (16) */ if ((name_len = dn_expand(answer,answer+answer_len,pos,name,MAX_NAME)) < 0) return TRY_OTHER; pos += name_len; if (GET16(pos) != wanted || GET16(pos+2) != C_IN) return TRY_OTHER; pos += 4; /* * Iterate over answers until we find something we like, giving priority * to ATMA_AESA (until signaling is fixed to work with E.164 too) */ found = NULL; while (answers--) { /* * RR: name, type (16), class (16), TTL (32), resource_len (16), * resource_data ... */ if ((name_len = dn_expand(answer,answer+answer_len,pos,name,MAX_NAME)) < 0) return TRY_OTHER; pos += name_len; data_len = GET16(pos+8); data = pos+10; pos = data+data_len; if (GET16(data-10) != wanted || GET16(data-8) != C_IN || !--data_len) continue; switch (wanted) { case T_NSAP: data_len++; if (data_len != ATM_ESA_LEN) continue; memcpy(((struct sockaddr_atmsvc *) result)-> sas_addr.prv,data,ATM_ESA_LEN); return 0; case T_ATMA: switch (*data++) { case ATMA_AESA: if (data_len != ATM_ESA_LEN) continue; memcpy(((struct sockaddr_atmsvc *) result)-> sas_addr.prv,data,ATM_ESA_LEN); return 0; case ATMA_E164: if (data_len > ATM_E164_LEN) continue; if (!found) { found = data; found_len = data_len; } break; default: continue; } case T_PTR: if (dn_expand(answer,answer+answer_len,data,result, res_len) < 0) return FATAL; return 0; default: continue; } } if (!found) return TRY_OTHER; memcpy(((struct sockaddr_atmsvc *) result)->sas_addr.pub,found, found_len); ((struct sockaddr_atmsvc *) result)->sas_addr.pub[found_len] = 0; return 0; } int ans_byname(const char *text,struct sockaddr_atmsvc *addr,int length, int flags) { if (!(flags & T2A_SVC) || length != sizeof(*addr)) return TRY_OTHER; memset(addr,0,sizeof(*addr)); addr->sas_family = AF_ATMSVC; if (!ans(text,T_ATMA,addr,length)) return 0; return ans(text,T_NSAP,addr,length); } static int encode_nsap(char *buf,const unsigned char *addr) { static int fmt_dcc[] = { 2,12,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 4,2,0 }; static int fmt_e164[] = { 2,12,1,1,1,1,1,1,1,1,16,2,0 }; int *fmt; int pos,i,j; switch (*addr) { case ATM_AFI_DCC: case ATM_AFI_ICD: case ATM_AFI_LOCAL: case ATM_AFI_DCC_GROUP: case ATM_AFI_ICD_GROUP: case ATM_AFI_LOCAL_GROUP: fmt = fmt_dcc; break; case ATM_AFI_E164: case ATM_AFI_E164_GROUP: fmt = fmt_e164; break; default: return TRY_OTHER; } pos = 2*ATM_ESA_LEN; for (i = 0; fmt[i]; i++) { pos -= fmt[i]; for (j = 0; j < fmt[i]; j++) sprintf(buf++,"%x", (addr[(pos+j) >> 1] >> 4*(1-((pos+j) & 1))) & 0xf); *buf++ = '.'; } strcpy(buf,"AESA.ATMA.INT."); return 0; } static int encode_nsap_new(char *buf,const unsigned char *addr) { int i; int digit; for (i = 20; i; ) { i--; digit = addr[i] & 0x0F; *(buf++) = digit + (digit >= 10 ? '7' : '0'); *(buf++) = '.'; digit = ((unsigned char) (addr[i])) >> 4; *(buf++) = digit + (digit >= 10 ? '7' : '0'); *(buf++) = '.'; } strcpy (buf, "NSAP.INT."); return 0; } static int cc_len(int p0,int p1) { static char *cc_table = NULL; FILE *file; char buffer[MAX_LINE]; char *here; int cc; if (!cc_table) { if (!(cc_table = malloc(100))) { perror("malloc"); return E164_CC_DEFAULT_LEN; } memset(cc_table,E164_CC_DEFAULT_LEN,100); if (!(file = fopen(E164_CC_FILE,"r"))) perror(E164_CC_FILE); else { while (fgets(buffer,MAX_LINE,file)) { here = strchr(buffer,'#'); if (here) *here = 0; if (sscanf(buffer,"%d",&cc) == 1) { if (cc < 10) cc_table[cc] = 1; else if (cc < 100) cc_table[cc] = 2; else cc_table[cc/10] = 3; } } fclose(file); } } if (cc_table[p0] == 1) return 1; return cc_table[p0*10+p1]; } static int encode_e164(char *buf,const char *addr) { const char *prefix,*here; prefix = addr+cc_len(addr[0]-48,addr[1]-48); here = strchr(addr,0); while (here > prefix) { *buf++ = *--here; *buf++ = '.'; } while (here > addr) *buf++ = *addr++; strcpy(buf,".E164.ATMA.INT."); return 0; } int ans_byaddr(char *buffer,int length,const struct sockaddr_atmsvc *addr, int flags) { char tmp[MAX_NAME]; /* could be smaller ... */ int res; if (addr->sas_addr.prv) { res = encode_nsap(tmp,addr->sas_addr.prv); if (!res && !ans(tmp,T_PTR,buffer,length)) return 0; res = encode_nsap_new(tmp,addr->sas_addr.prv); if (res < 0) return res; return ans(tmp,T_PTR,buffer,length); } else { res = encode_e164(tmp,addr->sas_addr.pub); if (res < 0) return res; return ans(tmp,T_PTR,buffer,length); } } ppp-2.4.5/pppd/plugins/pppoatm/atm.h000066400000000000000000000060151130035057700173620ustar00rootroot00000000000000/* atm.h - Functions useful for ATM applications */ /* Written 1995-2000 by Werner Almesberger, EPFL-LRC/ICA */ #ifndef _ATM_H #define _ATM_H #include #include #include /* * For versions of glibc < 2.1 */ #ifndef AF_ATMPVC #define AF_ATMPVC 8 #endif #ifndef AF_ATMSVC #define AF_ATMSVC 20 #endif #ifndef PF_ATMPVC #define PF_ATMPVC AF_ATMPVC #endif #ifndef PF_ATMSVC #define PF_ATMSVC AF_ATMSVC #endif #ifndef SOL_ATM #define SOL_ATM 264 #endif #ifndef SOL_AAL #define SOL_AAL 265 #endif #define HOSTS_ATM "/etc/hosts.atm" /* text2atm flags */ #define T2A_PVC 1 /* address is PVC */ #define T2A_SVC 2 /* address is SVC */ #define T2A_UNSPEC 4 /* allow unspecified parts in PVC address */ #define T2A_WILDCARD 8 /* allow wildcards in PVC or SVC address */ #define T2A_NNI 16 /* allow NNI VPI range (PVC) */ #define T2A_NAME 32 /* allow name resolution */ #define T2A_REMOTE 64 /* OBSOLETE */ #define T2A_LOCAL 128 /* don't use ANS */ /* atm2text flags */ #define A2T_PRETTY 1 /* add syntactic sugar */ #define A2T_NAME 2 /* attempt name lookup */ #define A2T_REMOTE 4 /* OBSOLETE */ #define A2T_LOCAL 8 /* don't use ANS */ /* atm_equal flags */ #define AXE_WILDCARD 1 /* allow wildcard match */ #define AXE_PRVOPT 2 /* private part of SVC address is optional */ /* text2qos flags */ #define T2Q_DEFAULTS 1 /* structure contains default values */ /* text2sap flags */ #define T2S_NAME 1 /* attempt name lookup */ #define T2S_LOCAL 2 /* we may support NIS or such in the future */ /* sap2text flags */ #define S2T_NAME 1 /* attempt name lookup */ #define S2T_LOCAL 2 /* we may support NIS or such in the future */ /* sap_equal flags */ #define SXE_COMPATIBLE 1 /* check for compatibility instead of identity*/ #define SXE_NEGOTIATION 2 /* allow negotiation; requires SXE_COMPATIBLE; assumes "a" defines the available capabilities */ #define SXE_RESULT 4 /* return selected SAP */ #define MAX_ATM_ADDR_LEN (2*ATM_ESA_LEN+ATM_E164_LEN+5) /* 4 dots, 1 plus */ #define MAX_ATM_NAME_LEN 256 /* wild guess */ #define MAX_ATM_QOS_LEN 116 /* 5+4+2*(3+3*(7+9)+2)+1 */ #define MAX_ATM_SAP_LEN 255 /* BHLI(27)+1+3*BLLI(L2=33,L3=41,+1)+2 */ int text2atm(const char *text,struct sockaddr *addr,int length,int flags); int atm2text(char *buffer,int length,const struct sockaddr *addr,int flags); int atm_equal(const struct sockaddr *a,const struct sockaddr *b,int len, int flags); int sdu2cell(int s,int sizes,const int *sdu_size,int *num_sdu); int text2qos(const char *text,struct atm_qos *qos,int flags); int qos2text(char *buffer,int length,const struct atm_qos *qos,int flags); int qos_equal(const struct atm_qos *a,const struct atm_qos *b); int text2sap(const char *text,struct atm_sap *sap,int flags); int sap2text(char *buffer,int length,const struct atm_sap *sap,int flags); int sap_equal(const struct atm_sap *a,const struct atm_sap *b,int flags,...); int __t2q_get_rate(const char **text,int up); int __atmlib_fetch(const char **pos,...); /* internal use only */ #endif ppp-2.4.5/pppd/plugins/pppoatm/atmres.h000066400000000000000000000013241130035057700200720ustar00rootroot00000000000000/* atmres.h - Common definitions and prototypes for resolver functions */ /* Written 1996,1998 by Werner Almesberger, EPFL-LRC/ICA */ #ifndef _ATMRES_H #define _ATMRES_H #include #include /* Some #defines that may be needed if ANS isn't installed on that system */ #ifndef T_ATMA #define T_ATMA 34 #endif #ifndef ATMA_AESA #define ATMA_AESA 0 #endif #ifndef ATMA_E164 #define ATMA_E164 1 #endif /* Return codes for text2atm and atm2text */ #define TRY_OTHER -2 #define FATAL -1 /* must be -1 */ int ans_byname(const char *text,struct sockaddr_atmsvc *addr,int length, int flags); int ans_byaddr(char *buffer,int length,const struct sockaddr_atmsvc *addr, int flags); #endif ppp-2.4.5/pppd/plugins/pppoatm/atmsap.h000066400000000000000000000023401130035057700200630ustar00rootroot00000000000000/* atmsap.h - ATM Service Access Point addressing definitions */ /* Written 1996-1998 by Werner Almesberger, EPFL LRC/ICA */ #ifndef _ATMSAP_H #define _ATMSAP_H #include #include /* * Selected ISO/IEC TR 9577 Network Layer Protocol Identifiers (NLPID) */ #define NLPID_IEEE802_1_SNAP 0x80 /* IEEE 802.1 SNAP */ /* * Selected Organizationally Unique Identifiers (OUIs) */ #define ATM_FORUM_OUI "\x00\xA0\x3E" /* ATM Forum */ #define EPFL_OUI "\x00\x60\xD7" /* EPF Lausanne, CH */ /* * Selected vendor-specific application identifiers (for B-HLI). Such an * identifier consists of three bytes containing the OUI, followed by four * bytes assigned by the organization owning the OUI. */ #define ANS_HLT_VS_ID ATM_FORUM_OUI "\x00\x00\x00\x01" /* ATM Name System, af-saa-0069.000 */ #define VOD_HLT_VS_ID ATM_FORUM_OUI "\x00\x00\x00\x02" /* VoD, af-saa-0049.001 */ #define AREQUIPA_HLT_VS_ID EPFL_OUI "\x01\x00\x00\x01" /* Arequipa */ #define TTCP_HLT_VS_ID EPFL_OUI "\x01\x00\x00\x03" /* ttcp_atm */ /* Mapping of "well-known" TCP, UDP, etc. port numbers to ATM BHLIs. btd-saa-api-bhli-01.02 */ void atm_tcpip_port_mapping(char *vs_id,uint8_t protocol,uint16_t port); #endif ppp-2.4.5/pppd/plugins/pppoatm/misc.c000066400000000000000000000021401130035057700175220ustar00rootroot00000000000000/* misc.c - Miscellaneous library functions */ /* Written 1997-2000 by Werner Almesberger, EPFL-ICA/ICA */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include /* for htons */ #include #include int __atmlib_fetch(const char **pos,...) { const char *value; int ref_len,best_len,len; int i,best; va_list ap; va_start(ap,pos); ref_len = strlen(*pos); best_len = 0; best = -1; for (i = 0; (value = va_arg(ap,const char *)); i++) { len = strlen(value); if (*value != '!' && len <= ref_len && len > best_len && !strncasecmp(*pos,value,len)) { best = i; best_len = len; } } va_end(ap); if (best > -1) (*pos) += best_len; return best; } void atm_tcpip_port_mapping(char *vs_id,uint8_t protocol,uint16_t port) { memcpy(vs_id,ATM_FORUM_OUI "\x01",4); vs_id[4] = protocol; /* e.g. IP_TCP or IP_UDP; from netinet/protocols.h */ vs_id[5] = (htons(port) >> 8) & 255; vs_id[6] = htons(port) & 255; } ppp-2.4.5/pppd/plugins/pppoatm/pppoatm.c000066400000000000000000000132561130035057700202610ustar00rootroot00000000000000/* pppoatm.c - pppd plugin to implement PPPoATM protocol. * * Copyright 2000 Mitchell Blank Jr. * Based in part on work from Jens Axboe and Paul Mackerras. * Updated to ppp-2.4.1 by Bernhard Kaindl * * Updated to ppp-2.4.2 by David Woodhouse 2004. * - disconnect method added * - remove_options() abuse removed. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include "pppd.h" #include "pathnames.h" #include "fsm.h" /* Needed for lcp.h to include cleanly */ #include "lcp.h" #include #include #include #include #include #include const char pppd_version[] = VERSION; static struct sockaddr_atmpvc pvcaddr; static char *qosstr = NULL; static bool llc_encaps = 0; static bool vc_encaps = 0; static int device_got_set = 0; static int pppoatm_max_mtu, pppoatm_max_mru; static int setdevname_pppoatm(const char *cp, const char **argv, int doit); struct channel pppoa_channel; static int pppoa_fd = -1; static option_t pppoa_options[] = { { "device name", o_wild, (void *) &setdevname_pppoatm, "ATM service provider IDs: VPI.VCI", OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, devnam}, { "llc-encaps", o_bool, &llc_encaps, "use LLC encapsulation for PPPoATM", 1}, { "vc-encaps", o_bool, &vc_encaps, "use VC multiplexing for PPPoATM (default)", 1}, { "qos", o_string, &qosstr, "set QoS for PPPoATM connection", 1}, { NULL } }; /* returns: * -1 if there's a problem with setting the device * 0 if we can't parse "cp" as a valid name of a device * 1 if "cp" is a reasonable thing to name a device * Note that we don't actually open the device at this point * We do need to fill in: * devnam: a string representation of the device * devstat: a stat structure of the device. In this case * we're not opening a device, so we just make sure * to set up S_ISCHR(devstat.st_mode) != 1, so we * don't get confused that we're on stdin. */ int (*old_setdevname_hook)(const char* cp) = NULL; static int setdevname_pppoatm(const char *cp, const char **argv, int doit) { struct sockaddr_atmpvc addr; extern struct stat devstat; if (device_got_set) return 0; //info("PPPoATM setdevname_pppoatm: '%s'", cp); memset(&addr, 0, sizeof addr); if (text2atm(cp, (struct sockaddr *) &addr, sizeof(addr), T2A_PVC | T2A_NAME) < 0) { if(doit) info("atm does not recognize: %s", cp); return 0; } if (!doit) return 1; //if (!dev_set_ok()) return -1; memcpy(&pvcaddr, &addr, sizeof pvcaddr); strlcpy(devnam, cp, sizeof devnam); devstat.st_mode = S_IFSOCK; if (the_channel != &pppoa_channel) { the_channel = &pppoa_channel; lcp_wantoptions[0].neg_accompression = 0; lcp_allowoptions[0].neg_accompression = 0; lcp_wantoptions[0].neg_asyncmap = 0; lcp_allowoptions[0].neg_asyncmap = 0; lcp_wantoptions[0].neg_pcompression = 0; } info("PPPoATM setdevname_pppoatm - SUCCESS:%s", cp); device_got_set = 1; return 1; } #define pppoatm_overhead() (llc_encaps ? 6 : 2) static void no_device_given_pppoatm(void) { fatal("No vpi.vci specified"); } static void set_line_discipline_pppoatm(int fd) { struct atm_backend_ppp be; be.backend_num = ATM_BACKEND_PPP; if (!llc_encaps) be.encaps = PPPOATM_ENCAPS_VC; else if (!vc_encaps) be.encaps = PPPOATM_ENCAPS_LLC; else be.encaps = PPPOATM_ENCAPS_AUTODETECT; if (ioctl(fd, ATM_SETBACKEND, &be) < 0) fatal("ioctl(ATM_SETBACKEND): %m"); } #if 0 static void reset_line_discipline_pppoatm(int fd) { atm_backend_t be = ATM_BACKEND_RAW; /* 2.4 doesn't support this yet */ (void) ioctl(fd, ATM_SETBACKEND, &be); } #endif static int connect_pppoatm(void) { int fd; struct atm_qos qos; system ("/sbin/modprobe -q pppoatm"); if (!device_got_set) no_device_given_pppoatm(); fd = socket(AF_ATMPVC, SOCK_DGRAM, 0); if (fd < 0) fatal("failed to create socket: %m"); memset(&qos, 0, sizeof qos); qos.txtp.traffic_class = qos.rxtp.traffic_class = ATM_UBR; /* TODO: support simplified QoS setting */ if (qosstr != NULL) if (text2qos(qosstr, &qos, 0)) fatal("Can't parse QoS: \"%s\""); qos.txtp.max_sdu = lcp_allowoptions[0].mru + pppoatm_overhead(); qos.rxtp.max_sdu = lcp_wantoptions[0].mru + pppoatm_overhead(); qos.aal = ATM_AAL5; if (setsockopt(fd, SOL_ATM, SO_ATMQOS, &qos, sizeof(qos)) < 0) fatal("setsockopt(SO_ATMQOS): %m"); /* TODO: accept on SVCs... */ if (connect(fd, (struct sockaddr *) &pvcaddr, sizeof(struct sockaddr_atmpvc))) fatal("connect(%s): %m", devnam); pppoatm_max_mtu = lcp_allowoptions[0].mru; pppoatm_max_mru = lcp_wantoptions[0].mru; set_line_discipline_pppoatm(fd); strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); pppoa_fd = fd; return fd; } static void disconnect_pppoatm(void) { close(pppoa_fd); } void plugin_init(void) { #if defined(__linux__) extern int new_style_driver; /* From sys-linux.c */ if (!ppp_available() && !new_style_driver) fatal("Kernel doesn't support ppp_generic - " "needed for PPPoATM"); #else fatal("No PPPoATM support on this OS"); #endif info("PPPoATM plugin_init"); add_options(pppoa_options); } struct channel pppoa_channel = { options: pppoa_options, process_extra_options: NULL, check_options: NULL, connect: &connect_pppoatm, disconnect: &disconnect_pppoatm, establish_ppp: &generic_establish_ppp, disestablish_ppp: &generic_disestablish_ppp, send_config: NULL, recv_config: NULL, close: NULL, cleanup: NULL }; ppp-2.4.5/pppd/plugins/pppoatm/text2atm.c000066400000000000000000000151131130035057700203430ustar00rootroot00000000000000/* text2atm.c - Converts textual representation of ATM address to binary encoding */ /* Written 1995-2000 by Werner Almesberger, EPFL-LRC/ICA */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "atm.h" #include "atmsap.h" #include "atmres.h" static int try_pvc(const char *text,struct sockaddr_atmpvc *addr,int flags) { int part[3]; int i; part[0] = part[1] = part[2] = 0; i = 0; while (1) { if (!*text) return FATAL; /* empty or ends with a dot */ if (i == 3) return TRY_OTHER; /* too long */ if (isdigit(*text)) { if (*text == '0' && isdigit(text[1])) return TRY_OTHER; /* no leading zeroes */ do { if (part[i] > INT_MAX/10) return TRY_OTHER;/* number too big */ part[i] = part[i]*10+*text++-'0'; } while (isdigit(*text)); i++; if (!*text) break; if (*text++ != '.') return TRY_OTHER; /* non-PVC character */ continue; } if (*text == '*') { if (!(flags & T2A_WILDCARD)) return FATAL; /* not allowed */ part[i++] = ATM_ITF_ANY; /* all *_ANY have the same value */ } else { if (*text != '?') return TRY_OTHER; /* invalid character */ if (!(flags & T2A_UNSPEC)) return FATAL; /* not allowed */ part[i++] = ATM_VPI_UNSPEC; /* all *_UNSPEC have the same value */ } if (!*++text) break; if (*text++ != '.') return FATAL; /* dot required */ } if (i < 2) return TRY_OTHER; /* no dots */ if (i == 2) { part[2] = part[1]; part[1] = part[0]; part[0] = 0; /* default interface */ } if (part[0] > SHRT_MAX || part[2] > ATM_MAX_VCI) return TRY_OTHER; /* too big */ if (part[1] > (flags & T2A_NNI ? ATM_MAX_VPI_NNI : ATM_MAX_VPI)) return TRY_OTHER; /* too big */ if (part[0] == ATM_VPI_UNSPEC) return FATAL; /* bad */ addr->sap_family = AF_ATMPVC; addr->sap_addr.itf = part[0]; addr->sap_addr.vpi = part[1]; addr->sap_addr.vci = part[2]; return 0; } static int do_try_nsap(const char *text,struct sockaddr_atmsvc *addr,int flags) { const char *walk; int count,pos,dot; int offset,len; char value; count = dot = 0; for (walk = text; *walk; walk++) if (isdigit(*walk)) { if (count++ == 15) break; dot = 1; } else if (*text != '.') break; else if (!dot) return FATAL; /* two dots in a row */ else dot = 0; if (*walk != ':') { pos = 0; offset = 0; } else { if (!dot || *text == '0') return FATAL; addr->sas_addr.prv[0] = ATM_AFI_E164; addr->sas_addr.prv[1] = 0; memset(addr->sas_addr.prv+1,0,8); for (pos = 18-count-1; *text; text++) { if (*text == '.') continue; if (*text == ':') break; else { if (pos & 1) addr->sas_addr.prv[pos >> 1] |= *text-'0'; else addr->sas_addr.prv[pos >> 1] = (*text-'0') << 4; pos++; } } addr->sas_addr.prv[8] |= 0xf; text++; pos++; offset = 72; } for (dot = 0; *text; text++) if (isxdigit(*text)) { if (pos == ATM_ESA_LEN*2) return TRY_OTHER; /* too long */ value = isdigit(*text) ? *text-'0' : (islower(*text) ? toupper(*text) : *text)-'A'+10; if (pos & 1) addr->sas_addr.prv[pos >> 1] |= value; else addr->sas_addr.prv[pos >> 1] = value << 4; pos++; dot = 1; } else if (*text == '/' && (flags & T2A_WILDCARD)) break; else if (*text != '.') return TRY_OTHER; else { if (!dot) return FATAL; /* two dots in a row */ dot = 0; } if (!dot) return FATAL; if (pos > 1 && !*addr->sas_addr.prv) return TRY_OTHER; /* no leading zeroes */ if (!*text) return pos != ATM_ESA_LEN*2 ? TRY_OTHER : ATM_ESA_LEN*2; /* handle bad length */ len = 0; while (*++text) { if (!isdigit(*text)) return -1; /* non-digit in length */ if (len >= pos*4) return -1; /* too long */ len = len*10+*text-'0'; } if (len > 7 && addr->sas_addr.prv[0] != ATM_AFI_E164) offset = 72; if (len < offset) return FATAL; return len > pos*4 ? TRY_OTHER : len; } static int try_nsap(const char *text,struct sockaddr_atmsvc *addr,int flags) { int result; result = do_try_nsap(text,addr,flags); if (result < 0) return result; addr->sas_family = AF_ATMSVC; *addr->sas_addr.pub = 0; return result; } static int try_e164(const char *text,struct sockaddr_atmsvc *addr,int flags) { int i,dot,result; if (*text == ':' || *text == '+') text++; for (i = dot = 0; *text; text++) if (isdigit(*text)) { if (i == ATM_E164_LEN) return TRY_OTHER; /* too long */ addr->sas_addr.pub[i++] = *text; dot = 1; } else if (*text != '.') break; else { if (!dot) return TRY_OTHER; /* two dots in a row */ dot = 0; } if (!dot) return TRY_OTHER; addr->sas_addr.pub[i] = 0; *addr->sas_addr.prv = 0; result = 0; if (*text) { if (*text++ != '+') return TRY_OTHER; else { result = do_try_nsap(text,addr,flags); if (result < 0) return FATAL; } } addr->sas_family = AF_ATMSVC; return result; } static int search(FILE *file,const char *text,struct sockaddr *addr,int length, int flags) { char line[MAX_ATM_NAME_LEN+1]; const char *here; int result; while (fgets(line,MAX_ATM_NAME_LEN,file)) { if (!strtok(line,"\t\n ")) continue; while ((here = strtok(NULL,"\t\n "))) if (!strcasecmp(here,text)) { here = strtok(line,"\t\n "); result = text2atm(here,addr,length,flags); if (result >= 0) return result; } } return TRY_OTHER; } static int try_name(const char *text,struct sockaddr *addr,int length, int flags) { FILE *file; int result; if (!(file = fopen(HOSTS_ATM,"r"))) return TRY_OTHER; result = search(file,text,addr,length,flags); (void) fclose(file); return result; } int text2atm(const char *text,struct sockaddr *addr,int length,int flags) { int result; if (!*text) return -1; if (!(flags & (T2A_PVC | T2A_SVC))) flags |= T2A_PVC | T2A_SVC; if (length < sizeof(struct sockaddr_atmpvc)) return -1; if (flags & T2A_PVC) { result = try_pvc(text,(struct sockaddr_atmpvc *) addr,flags); if (result != TRY_OTHER) return result; } if ((flags & T2A_SVC) && length >= sizeof(struct sockaddr_atmsvc)) { result = try_nsap(text,(struct sockaddr_atmsvc *) addr,flags); if (result != TRY_OTHER) return result; result = try_e164(text,(struct sockaddr_atmsvc *) addr,flags); if (result != TRY_OTHER) return result; } if (!(flags & T2A_NAME)) return -1; result = try_name(text,addr,length,flags & ~T2A_NAME); if (result == TRY_OTHER && !(flags & T2A_LOCAL)) result = ans_byname(text,(struct sockaddr_atmsvc *) addr,length,flags); if (result != TRY_OTHER) return result; return -1; } ppp-2.4.5/pppd/plugins/pppoatm/text2qos.c000066400000000000000000000077301130035057700203720ustar00rootroot00000000000000/* text2qos.c - Converts textual representation of QOS parameters to binary encoding */ /* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "atm.h" #define fetch __atmlib_fetch #define RATE_ERROR -2 int __t2q_get_rate(const char **text,int up) { const char mult[] = "kKmMgGg"; const char *multiplier; char *end; unsigned int rate,fract; int power; if (!strncmp(*text,"max",3)) { *text += 3; return ATM_MAX_PCR; } rate = strtoul(*text,&end,10); power = fract = 0; if (*end == '.') for (end++; *end && isdigit(*end); end++) { fract = fract*10+*end-48; if (--power == -9) break; } multiplier = NULL; if (*end && (multiplier = strchr(mult,*end))) { while (multiplier >= mult) { if (rate > UINT_MAX/1000) return RATE_ERROR; rate *= 1000; power += 3; multiplier -= 2; } end++; } while (power && fract) if (power < 0) { fract /= 10; power++; } else { fract *= 10; power--; } rate += fract; if (strlen(end) < 3) { if (multiplier) return RATE_ERROR; } else if (!strncmp(end,"cps",3)) end += 3; else if (!strncmp(end,"bps",3)) { rate = (rate+(up ? 8*ATM_CELL_PAYLOAD-1 : 0))/8/ ATM_CELL_PAYLOAD; end += 3; } else if (multiplier) return RATE_ERROR; if (rate > INT_MAX) return RATE_ERROR; *text = end; return rate; } static int params(const char **text,struct atm_trafprm *a, struct atm_trafprm *b) { int value; char *end; if (*(*text)++ != ':') return -1; while (1) { if (!**text) return -1; switch (fetch(text,"max_pcr=","pcr=","min_pcr=","max_sdu=","sdu=", NULL)) { case 0: if ((value = __t2q_get_rate(text,0)) == RATE_ERROR) return -1; if (a) a->max_pcr = value; if (b) b->max_pcr = value; break; case 1: if ((value = __t2q_get_rate(text,0)) == RATE_ERROR) return -1; if (a) a->pcr = value; if (b) b->pcr = value; break; case 2: if ((value = __t2q_get_rate(text,1)) == RATE_ERROR) return -1; if (value == ATM_MAX_PCR) return -1; if (a) a->min_pcr = value; if (b) b->min_pcr = value; break; case 3: case 4: value = strtol(*text,&end,10); if (value < 0) return -1; *text = end; if (a) a->max_sdu = value; if (b) b->max_sdu = value; break; default: return 0; } if (!**text) break; if (*(*text)++ != ',') return -1; } return 0; } int text2qos(const char *text,struct atm_qos *qos,int flags) { int traffic_class,aal; traffic_class = ATM_NONE; aal = ATM_NO_AAL; do { static const unsigned char aal_number[] = { ATM_AAL0, ATM_AAL5 }; int item; item = fetch(&text,"!none","ubr","cbr","vbr","abr","aal0","aal5",NULL); switch (item) { case 1: case 2: /* we don't support VBR yet */ case 4: traffic_class = item; break; case 5: case 6: aal = aal_number[item-5]; break; default: return -1; } } while (*text == ',' ? text++ : 0); if (!traffic_class) return -1; if (qos && !(flags & T2Q_DEFAULTS)) memset(qos,0,sizeof(*qos)); if (qos) qos->txtp.traffic_class = qos->rxtp.traffic_class = traffic_class; if (qos && aal) qos->aal = aal; if (!*text) return 0; if (params(&text,qos ? &qos->txtp : NULL,qos ? &qos->rxtp : NULL)) return -1; if (!*text) return 0; switch (fetch(&text,"tx","rx",NULL)) { case 0: if (!fetch(&text,":none",NULL)) { if (qos) qos->txtp.traffic_class = ATM_NONE; if (*text == ',') text++; break; } if (params(&text,qos ? &qos->txtp : NULL,NULL)) return -1; break; case 1: text -= 2; break; default: return -1; } if (!*text) return 0; if (fetch(&text,"rx",NULL)) return -1; if (!fetch(&text,":none",NULL) && qos) qos->rxtp.traffic_class = ATM_NONE; else if (params(&text,qos ? &qos->rxtp : NULL,NULL)) return -1; return *text ? -1 : 0; } ppp-2.4.5/pppd/plugins/pppol2tp/000077500000000000000000000000001130035057700165265ustar00rootroot00000000000000ppp-2.4.5/pppd/plugins/pppol2tp/Makefile.linux000066400000000000000000000011131130035057700213200ustar00rootroot00000000000000#CC = gcc COPTS = -O2 -g CFLAGS = $(COPTS) -I. -I../.. -I../../../include -fPIC LDFLAGS = -shared INSTALL = install #*********************************************************************** DESTDIR = @DESTDIR@ LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION) VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h) PLUGINS := pppol2tp.so openl2tp.so all: $(PLUGINS) %.so: %.o $(CC) $(CFLAGS) -o $@ -shared $^ $(LIBS) install: all $(INSTALL) -d -m 755 $(LIBDIR) $(INSTALL) -c -m 4550 $(PLUGINS) $(LIBDIR) clean: rm -f *.o *.so %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< ppp-2.4.5/pppd/plugins/pppol2tp/l2tp_event.h000066400000000000000000000063271130035057700207710ustar00rootroot00000000000000/***************************************************************************** * Copyright (C) 2008 Katalix Systems Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *****************************************************************************/ /* * OpenL2TP application event interface definition. * * This plugin is used by OpenL2TP to receive events from pppd. * * Events are used as follows:- * PPP_UPDOWN_IND - tells OpenL2TP of PPP session state changes. * PPP_ACCM_IND - tells OpenL2TP of PPP ACCM negotiated options * * Non-GPL applications are permitted to use this API, provided that * any changes to this source file are made available under GPL terms. */ #ifndef L2TP_EVENT_H #define L2TP_EVENT_H #include /***************************************************************************** * API definition *****************************************************************************/ #define OPENL2TP_EVENT_SOCKET_NAME "/tmp/openl2tp-event.sock" #define OPENL2TP_MSG_TYPE_NULL 0 #define OPENL2TP_MSG_TYPE_PPP_UPDOWN_IND 1 #define OPENL2TP_MSG_TYPE_PPP_ACCM_IND 2 #define OPENL2TP_MSG_TYPE_MAX 3 enum { OPENL2TP_TLV_TYPE_TUNNEL_ID, OPENL2TP_TLV_TYPE_SESSION_ID, OPENL2TP_TLV_TYPE_PPP_ACCM, OPENL2TP_TLV_TYPE_PPP_UNIT, OPENL2TP_TLV_TYPE_PPP_IFNAME, OPENL2TP_TLV_TYPE_PPP_USER_NAME, OPENL2TP_TLV_TYPE_PPP_STATE }; #define OPENL2TP_TLV_TYPE_MAX (OPENL2TP_TLV_TYPE_PPP_STATE + 1) #define OPENL2TP_MSG_MAX_LEN 512 #define OPENL2TP_MSG_SIGNATURE 0x6b6c7831 #define ALIGN32(n) (((n) + 3) & ~3) /* Each data field in a message is defined by a Type-Length-Value * (TLV) tuplet. */ struct openl2tp_event_tlv { uint16_t tlv_type; uint16_t tlv_len; uint8_t tlv_value[0]; }; /* Messages contain a small header followed by a list of TLVs. Each * TLV starts on a 4-byte boundary. */ struct openl2tp_event_msg { uint32_t msg_signature; /* OPENL2TP_MSG_SIGNATURE */ uint16_t msg_type; /* OPENL2TP_MSG_TYPE_* */ uint16_t msg_len; /* length of data that follows */ uint8_t msg_data[0]; /* list of TLVs, each always longword aligned */ }; /* These structs define the data field layout of each TLV. */ struct openl2tp_tlv_tunnel_id { uint16_t tunnel_id; }; struct openl2tp_tlv_session_id { uint16_t session_id; }; struct openl2tp_tlv_ppp_accm { uint32_t send_accm; uint32_t recv_accm; }; struct openl2tp_tlv_ppp_unit { uint32_t unit; }; struct openl2tp_tlv_ppp_state { uint8_t up; /* 0=down, 1=up */ }; struct openl2tp_tlv_ppp_ifname { char ifname[0]; }; struct openl2tp_tlv_ppp_user_name { char user_name[0]; }; #endif /* L2TP_EVENT_H */ ppp-2.4.5/pppd/plugins/pppol2tp/openl2tp.c000066400000000000000000000217251130035057700204440ustar00rootroot00000000000000/***************************************************************************** * Copyright (C) 2006,2007,2008 Katalix Systems Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *****************************************************************************/ /* pppd plugin for interfacing to openl2tpd */ #include #include #include #include #include "pppd.h" #include "pathnames.h" #include "fsm.h" #include "lcp.h" #include "ccp.h" #include "ipcp.h" #include #include #include #include #include #include #include #include #include #ifndef aligned_u64 /* should be defined in sys/types.h */ #define aligned_u64 unsigned long long __attribute__((aligned(8))) #endif #include #include #include #include #include #include #include "l2tp_event.h" extern int pppol2tp_tunnel_id; extern int pppol2tp_session_id; extern void (*pppol2tp_send_accm_hook)(int tunnel_id, int session_id, uint32_t send_accm, uint32_t recv_accm); extern void (*pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up); const char pppd_version[] = VERSION; static int openl2tp_fd = -1; static void (*old_pppol2tp_send_accm_hook)(int tunnel_id, int session_id, uint32_t send_accm, uint32_t recv_accm) = NULL; static void (*old_pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up) = NULL; static void (*old_multilink_join_hook)(void) = NULL; /***************************************************************************** * OpenL2TP interface. * We send a PPP_ACCM_IND to openl2tpd to report ACCM values and * SESSION_PPP_UPDOWN_IND to indicate when the PPP link comes up or * goes down. *****************************************************************************/ static int openl2tp_client_create(void) { struct sockaddr_un addr; int result; if (openl2tp_fd < 0) { openl2tp_fd = socket(PF_UNIX, SOCK_DGRAM, 0); if (openl2tp_fd < 0) { error("openl2tp connection create: %m"); return -ENOTCONN; } addr.sun_family = AF_UNIX; strcpy(&addr.sun_path[0], OPENL2TP_EVENT_SOCKET_NAME); result = connect(openl2tp_fd, (struct sockaddr *) &addr, sizeof(addr)); if (result < 0) { error("openl2tp connection connect: %m"); return -ENOTCONN; } } return 0; } static void openl2tp_send_accm_ind(int tunnel_id, int session_id, uint32_t send_accm, uint32_t recv_accm) { int result; uint8_t buf[OPENL2TP_MSG_MAX_LEN]; struct openl2tp_event_msg *msg = (void *) &buf[0]; struct openl2tp_event_tlv *tlv; uint16_t tid = tunnel_id; uint16_t sid = session_id; struct openl2tp_tlv_ppp_accm accm; if (openl2tp_fd < 0) { result = openl2tp_client_create(); if (result < 0) { goto out; } } accm.send_accm = send_accm; accm.recv_accm = recv_accm; msg->msg_signature = OPENL2TP_MSG_SIGNATURE; msg->msg_type = OPENL2TP_MSG_TYPE_PPP_ACCM_IND; msg->msg_len = 0; tlv = (void *) &msg->msg_data[msg->msg_len]; tlv->tlv_type = OPENL2TP_TLV_TYPE_TUNNEL_ID; tlv->tlv_len = sizeof(tid); memcpy(&tlv->tlv_value[0], &tid, tlv->tlv_len); msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); tlv = (void *) &msg->msg_data[msg->msg_len]; tlv->tlv_type = OPENL2TP_TLV_TYPE_SESSION_ID; tlv->tlv_len = sizeof(sid); memcpy(&tlv->tlv_value[0], &sid, tlv->tlv_len); msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); tlv = (void *) &msg->msg_data[msg->msg_len]; tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_ACCM; tlv->tlv_len = sizeof(accm); memcpy(&tlv->tlv_value[0], &accm, tlv->tlv_len); msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); result = send(openl2tp_fd, msg, sizeof(*msg) + msg->msg_len, MSG_NOSIGNAL); if (result < 0) { error("openl2tp send: %m"); } if (result != (sizeof(*msg) + msg->msg_len)) { warn("openl2tp send: unexpected byte count %d, expected %d", result, sizeof(msg) + msg->msg_len); } dbglog("openl2tp send: sent PPP_ACCM_IND, %d bytes", result); out: if (old_pppol2tp_send_accm_hook != NULL) { (*old_pppol2tp_send_accm_hook)(tunnel_id, session_id, send_accm, recv_accm); } return; } static void openl2tp_ppp_updown_ind(int tunnel_id, int session_id, int up) { int result; uint8_t buf[OPENL2TP_MSG_MAX_LEN]; struct openl2tp_event_msg *msg = (void *) &buf[0]; struct openl2tp_event_tlv *tlv; uint16_t tid = tunnel_id; uint16_t sid = session_id; uint8_t state = up; int unit = ifunit; char *user_name = NULL; if (openl2tp_fd < 0) { result = openl2tp_client_create(); if (result < 0) { goto out; } } if (peer_authname[0] != '\0') { user_name = strdup(peer_authname); } msg->msg_signature = OPENL2TP_MSG_SIGNATURE; msg->msg_type = OPENL2TP_MSG_TYPE_PPP_UPDOWN_IND; msg->msg_len = 0; tlv = (void *) &msg->msg_data[msg->msg_len]; tlv->tlv_type = OPENL2TP_TLV_TYPE_TUNNEL_ID; tlv->tlv_len = sizeof(tid); memcpy(&tlv->tlv_value[0], &tid, tlv->tlv_len); msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); tlv = (void *) &msg->msg_data[msg->msg_len]; tlv->tlv_type = OPENL2TP_TLV_TYPE_SESSION_ID; tlv->tlv_len = sizeof(sid); memcpy(&tlv->tlv_value[0], &sid, tlv->tlv_len); msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); tlv = (void *) &msg->msg_data[msg->msg_len]; tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_STATE; tlv->tlv_len = sizeof(state); memcpy(&tlv->tlv_value[0], &state, tlv->tlv_len); msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); tlv = (void *) &msg->msg_data[msg->msg_len]; tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_UNIT; tlv->tlv_len = sizeof(unit); memcpy(&tlv->tlv_value[0], &unit, tlv->tlv_len); msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); tlv = (void *) &msg->msg_data[msg->msg_len]; tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_IFNAME; tlv->tlv_len = strlen(ifname) + 1; memcpy(&tlv->tlv_value[0], ifname, tlv->tlv_len); msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); if (user_name != NULL) { tlv = (void *) &msg->msg_data[msg->msg_len]; tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_USER_NAME; tlv->tlv_len = strlen(user_name) + 1; memcpy(&tlv->tlv_value[0], user_name, tlv->tlv_len); msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); } result = send(openl2tp_fd, msg, sizeof(*msg) + msg->msg_len, MSG_NOSIGNAL); if (result < 0) { error("openl2tp send: %m"); } if (result != (sizeof(*msg) + msg->msg_len)) { warn("openl2tp send: unexpected byte count %d, expected %d", result, sizeof(msg) + msg->msg_len); } dbglog("openl2tp send: sent PPP_UPDOWN_IND, %d bytes", result); out: if (old_pppol2tp_ip_updown_hook != NULL) { (*old_pppol2tp_ip_updown_hook)(tunnel_id, session_id, up); } return; } /***************************************************************************** * When a multilink interface is created, there are 2 cases to consider. * * 1. The new interface is the first of a multilink bundle (master). * 2. The new interface is being attached to an existing bundle. * * The first case is handled by existing code because the interface * generates ip-up events just like standard interfaces. But in the * second case, where the interface is added to an existing ppp * bundle, pppd does not do IP negotiation and so as a result, no * ip-up event is generated when the interface is created. Since * openl2tpd needs the SESSION_PPP_UPDOWN_IND for all interfaces of a * PPP bundle, we must fake the event. * * We use the ip_multilink_join_hook to hear when an interface joins a * multilink bundle. *****************************************************************************/ static void openl2tp_multilink_join_ind(void) { if (doing_multilink && !multilink_master) { /* send event only if not master */ openl2tp_ppp_updown_ind(pppol2tp_tunnel_id, pppol2tp_session_id, 1); } } /***************************************************************************** * Application init *****************************************************************************/ void plugin_init(void) { old_pppol2tp_send_accm_hook = pppol2tp_send_accm_hook; pppol2tp_send_accm_hook = openl2tp_send_accm_ind; old_pppol2tp_ip_updown_hook = pppol2tp_ip_updown_hook; pppol2tp_ip_updown_hook = openl2tp_ppp_updown_ind; old_multilink_join_hook = multilink_join_hook; multilink_join_hook = openl2tp_multilink_join_ind; } ppp-2.4.5/pppd/plugins/pppol2tp/pppol2tp.c000066400000000000000000000343741130035057700204650ustar00rootroot00000000000000/* pppol2tp.c - pppd plugin to implement PPPoL2TP protocol * for Linux using kernel pppol2tp support. * * Requires kernel pppol2tp driver which is integrated into the kernel * from 2.6.23 onwards. For earlier kernels, a version can be obtained * from the OpenL2TP project at * http://www.sourceforge.net/projects/openl2tp/ * * Original by Martijn van Oosterhout * Modified by jchapman@katalix.com * * Heavily based upon pppoatm.c: original notice follows * * Copyright 2000 Mitchell Blank Jr. * Based in part on work from Jens Axboe and Paul Mackerras. * Updated to ppp-2.4.1 by Bernhard Kaindl * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include "pppd.h" #include "pathnames.h" #include "fsm.h" #include "lcp.h" #include "ccp.h" #include "ipcp.h" #include #include #include #include #include #include #include #include #ifndef aligned_u64 /* should be defined in sys/types.h */ #define aligned_u64 unsigned long long __attribute__((aligned(8))) #endif #include #include #include #include #include #include /* should be added to system's socket.h... */ #ifndef SOL_PPPOL2TP #define SOL_PPPOL2TP 273 #endif const char pppd_version[] = VERSION; static int setdevname_pppol2tp(char **argv); static int pppol2tp_fd = -1; static char *pppol2tp_fd_str; static bool pppol2tp_lns_mode = 0; static bool pppol2tp_recv_seq = 0; static bool pppol2tp_send_seq = 0; static int pppol2tp_debug_mask = 0; static int pppol2tp_reorder_timeout = 0; static char pppol2tp_ifname[32] = { 0, }; int pppol2tp_tunnel_id = 0; int pppol2tp_session_id = 0; static int device_got_set = 0; struct channel pppol2tp_channel; static void (*old_snoop_recv_hook)(unsigned char *p, int len) = NULL; static void (*old_snoop_send_hook)(unsigned char *p, int len) = NULL; static void (*old_ip_up_hook)(void) = NULL; static void (*old_ip_down_hook)(void) = NULL; /* Hook provided to allow other plugins to handle ACCM changes */ void (*pppol2tp_send_accm_hook)(int tunnel_id, int session_id, uint32_t send_accm, uint32_t recv_accm) = NULL; /* Hook provided to allow other plugins to handle IP up/down */ void (*pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up) = NULL; static option_t pppol2tp_options[] = { { "pppol2tp", o_special, &setdevname_pppol2tp, "FD for PPPoL2TP socket", OPT_DEVNAM | OPT_A2STRVAL, &pppol2tp_fd_str }, { "pppol2tp_lns_mode", o_bool, &pppol2tp_lns_mode, "PPPoL2TP LNS behavior. Default off.", OPT_PRIO | OPRIO_CFGFILE }, { "pppol2tp_send_seq", o_bool, &pppol2tp_send_seq, "PPPoL2TP enable sequence numbers in transmitted data packets. " "Default off.", OPT_PRIO | OPRIO_CFGFILE }, { "pppol2tp_recv_seq", o_bool, &pppol2tp_recv_seq, "PPPoL2TP enforce sequence numbers in received data packets. " "Default off.", OPT_PRIO | OPRIO_CFGFILE }, { "pppol2tp_reorderto", o_int, &pppol2tp_reorder_timeout, "PPPoL2TP data packet reorder timeout. Default 0 (no reordering).", OPT_PRIO }, { "pppol2tp_debug_mask", o_int, &pppol2tp_debug_mask, "PPPoL2TP debug mask. Default: no debug.", OPT_PRIO }, { "pppol2tp_ifname", o_string, &pppol2tp_ifname, "Set interface name of PPP interface", OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, 16 }, { "pppol2tp_tunnel_id", o_int, &pppol2tp_tunnel_id, "PPPoL2TP tunnel_id.", OPT_PRIO }, { "pppol2tp_session_id", o_int, &pppol2tp_session_id, "PPPoL2TP session_id.", OPT_PRIO }, { NULL } }; static int setdevname_pppol2tp(char **argv) { union { char buffer[128]; struct sockaddr pppol2tp; } s; int len = sizeof(s); char **a; int tmp; int tmp_len = sizeof(tmp); if (device_got_set) return 0; if (!int_option(*argv, &pppol2tp_fd)) return 0; if(getsockname(pppol2tp_fd, (struct sockaddr *)&s, &len) < 0) { fatal("Given FD for PPPoL2TP socket invalid (%s)", strerror(errno)); } if(s.pppol2tp.sa_family != AF_PPPOX) { fatal("Socket of not a PPPoX socket"); } /* Do a test getsockopt() to ensure that the kernel has the necessary * feature available. */ if (getsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_DEBUG, &tmp, &tmp_len) < 0) { fatal("PPPoL2TP kernel driver not installed"); } /* Setup option defaults. Compression options are disabled! */ modem = 0; lcp_allowoptions[0].neg_accompression = 1; lcp_wantoptions[0].neg_accompression = 0; lcp_allowoptions[0].neg_pcompression = 1; lcp_wantoptions[0].neg_pcompression = 0; ccp_allowoptions[0].deflate = 0; ccp_wantoptions[0].deflate = 0; ipcp_allowoptions[0].neg_vj = 0; ipcp_wantoptions[0].neg_vj = 0; ccp_allowoptions[0].bsd_compress = 0; ccp_wantoptions[0].bsd_compress = 0; the_channel = &pppol2tp_channel; device_got_set = 1; return 1; } static int connect_pppol2tp(void) { if(pppol2tp_fd == -1) { fatal("No PPPoL2TP FD specified"); } return pppol2tp_fd; } static void disconnect_pppol2tp(void) { if (pppol2tp_fd >= 0) { close(pppol2tp_fd); pppol2tp_fd = -1; } } static void send_config_pppol2tp(int mtu, u_int32_t asyncmap, int pcomp, int accomp) { struct ifreq ifr; int on = 1; int fd; char reorderto[16]; char tid[8]; char sid[8]; if (pppol2tp_ifname[0]) { struct ifreq ifr; int fd; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd >= 0) { memset (&ifr, '\0', sizeof (ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); strlcpy(ifr.ifr_newname, pppol2tp_ifname, sizeof(ifr.ifr_name)); ioctl(fd, SIOCSIFNAME, (caddr_t) &ifr); strlcpy(ifname, pppol2tp_ifname, 32); if (pppol2tp_debug_mask & PPPOL2TP_MSG_CONTROL) { dbglog("ppp%d: interface name %s", ifunit, ifname); } } close(fd); } if ((lcp_allowoptions[0].mru > 0) && (mtu > lcp_allowoptions[0].mru)) { warn("Overriding mtu %d to %d", mtu, lcp_allowoptions[0].mru); mtu = lcp_allowoptions[0].mru; } netif_set_mtu(ifunit, mtu); reorderto[0] = '\0'; if (pppol2tp_reorder_timeout > 0) sprintf(&reorderto[0], "%d ", pppol2tp_reorder_timeout); tid[0] = '\0'; if (pppol2tp_tunnel_id > 0) sprintf(&tid[0], "%hu ", pppol2tp_tunnel_id); sid[0] = '\0'; if (pppol2tp_session_id > 0) sprintf(&sid[0], "%hu ", pppol2tp_session_id); dbglog("PPPoL2TP options: %s%s%s%s%s%s%s%s%sdebugmask %d", pppol2tp_recv_seq ? "recvseq " : "", pppol2tp_send_seq ? "sendseq " : "", pppol2tp_lns_mode ? "lnsmode " : "", pppol2tp_reorder_timeout ? "reorderto " : "", reorderto, pppol2tp_tunnel_id ? "tid " : "", tid, pppol2tp_session_id ? "sid " : "", sid, pppol2tp_debug_mask); if (pppol2tp_recv_seq) if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_RECVSEQ, &on, sizeof(on)) < 0) fatal("setsockopt(PPPOL2TP_RECVSEQ): %m"); if (pppol2tp_send_seq) if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_SENDSEQ, &on, sizeof(on)) < 0) fatal("setsockopt(PPPOL2TP_SENDSEQ): %m"); if (pppol2tp_lns_mode) if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_LNSMODE, &on, sizeof(on)) < 0) fatal("setsockopt(PPPOL2TP_LNSMODE): %m"); if (pppol2tp_reorder_timeout) if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_REORDERTO, &pppol2tp_reorder_timeout, sizeof(pppol2tp_reorder_timeout)) < 0) fatal("setsockopt(PPPOL2TP_REORDERTO): %m"); if (pppol2tp_debug_mask) if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_DEBUG, &pppol2tp_debug_mask, sizeof(pppol2tp_debug_mask)) < 0) fatal("setsockopt(PPPOL2TP_DEBUG): %m"); } static void recv_config_pppol2tp(int mru, u_int32_t asyncmap, int pcomp, int accomp) { if ((lcp_allowoptions[0].mru > 0) && (mru > lcp_allowoptions[0].mru)) { warn("Overriding mru %d to mtu value %d", mru, lcp_allowoptions[0].mru); mru = lcp_allowoptions[0].mru; } if ((ifunit >= 0) && ioctl(pppol2tp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) error("Couldn't set PPP MRU: %m"); } /***************************************************************************** * Snoop LCP message exchanges to capture negotiated ACCM values. * When asyncmap values have been seen from both sides, give the values to * L2TP. * This code is derived from Roaring Penguin L2TP. *****************************************************************************/ static void pppol2tp_lcp_snoop(unsigned char *buf, int len, int incoming) { static bool got_send_accm = 0; static bool got_recv_accm = 0; static uint32_t recv_accm = 0xffffffff; static uint32_t send_accm = 0xffffffff; static bool snooping = 1; uint16_t protocol; uint16_t lcp_pkt_len; int opt, opt_len; int reject; unsigned char const *opt_data; uint32_t accm; /* Skip HDLC header */ buf += 2; len -= 2; /* Unreasonably short frame?? */ if (len <= 0) return; /* Get protocol */ if (buf[0] & 0x01) { /* Compressed protcol field */ protocol = buf[0]; } else { protocol = ((unsigned int) buf[0]) * 256 + buf[1]; } /* If it's a network protocol, stop snooping */ if (protocol <= 0x3fff) { if (pppol2tp_debug_mask & PPPOL2TP_MSG_DEBUG) { dbglog("Turning off snooping: " "Network protocol %04x found.", protocol); } snooping = 0; return; } /* If it's not LCP, do not snoop */ if (protocol != 0xc021) { return; } /* Skip protocol; go to packet data */ buf += 2; len -= 2; /* Unreasonably short frame?? */ if (len <= 0) return; /* Look for Configure-Ack or Configure-Reject code */ if (buf[0] != CONFACK && buf[0] != CONFREJ) return; reject = (buf[0] == CONFREJ); lcp_pkt_len = ((unsigned int) buf[2]) * 256 + buf[3]; /* Something fishy with length field? */ if (lcp_pkt_len > len) return; /* Skip to options */ len = lcp_pkt_len - 4; buf += 4; while (len > 0) { /* Pull off an option */ opt = buf[0]; opt_len = buf[1]; opt_data = &buf[2]; if (opt_len > len || opt_len < 2) break; len -= opt_len; buf += opt_len; if (pppol2tp_debug_mask & PPPOL2TP_MSG_DEBUG) { dbglog("Found option type %02x; len %d", opt, opt_len); } /* We are specifically interested in ACCM */ if (opt == CI_ASYNCMAP && opt_len == 0x06) { if (reject) { /* ACCM negotiation REJECTED; use default */ accm = 0xffffffff; if (pppol2tp_debug_mask & PPPOL2TP_MSG_DATA) { dbglog("Rejected ACCM negotiation; " "defaulting (%s)", incoming ? "incoming" : "outgoing"); } recv_accm = accm; send_accm = accm; got_recv_accm = 1; got_send_accm = 1; } else { memcpy(&accm, opt_data, sizeof(accm)); if (pppol2tp_debug_mask & PPPOL2TP_MSG_DATA) { dbglog("Found ACCM of %08x (%s)", accm, incoming ? "incoming" : "outgoing"); } if (incoming) { recv_accm = accm; got_recv_accm = 1; } else { send_accm = accm; got_send_accm = 1; } } if (got_recv_accm && got_send_accm) { if (pppol2tp_debug_mask & PPPOL2TP_MSG_CONTROL) { dbglog("Telling L2TP: Send ACCM = %08x; " "Receive ACCM = %08x", send_accm, recv_accm); } if (pppol2tp_send_accm_hook != NULL) { (*pppol2tp_send_accm_hook)(pppol2tp_tunnel_id, pppol2tp_session_id, send_accm, recv_accm); } got_recv_accm = 0; got_send_accm = 0; } } } } static void pppol2tp_lcp_snoop_recv(unsigned char *p, int len) { if (old_snoop_recv_hook != NULL) (*old_snoop_recv_hook)(p, len); pppol2tp_lcp_snoop(p, len, 1); } static void pppol2tp_lcp_snoop_send(unsigned char *p, int len) { if (old_snoop_send_hook != NULL) (*old_snoop_send_hook)(p, len); pppol2tp_lcp_snoop(p, len, 0); } /***************************************************************************** * Interface up/down events *****************************************************************************/ static void pppol2tp_ip_up_hook(void) { if (old_ip_up_hook != NULL) (*old_ip_up_hook)(); if (pppol2tp_ip_updown_hook != NULL) { (*pppol2tp_ip_updown_hook)(pppol2tp_tunnel_id, pppol2tp_session_id, 1); } } static void pppol2tp_ip_down_hook(void) { if (old_ip_down_hook != NULL) (*old_ip_down_hook)(); if (pppol2tp_ip_updown_hook != NULL) { (*pppol2tp_ip_updown_hook)(pppol2tp_tunnel_id, pppol2tp_session_id, 0); } } /***************************************************************************** * Application init *****************************************************************************/ static void pppol2tp_check_options(void) { /* Enable LCP snooping for ACCM options only for LNS */ if (pppol2tp_lns_mode) { if ((pppol2tp_tunnel_id == 0) || (pppol2tp_session_id == 0)) { fatal("tunnel_id/session_id values not specified"); } if (pppol2tp_debug_mask & PPPOL2TP_MSG_CONTROL) { dbglog("Enabling LCP snooping"); } old_snoop_recv_hook = snoop_recv_hook; old_snoop_send_hook = snoop_send_hook; snoop_recv_hook = pppol2tp_lcp_snoop_recv; snoop_send_hook = pppol2tp_lcp_snoop_send; } /* Hook up ip up/down hooks to send indicator to openl2tpd * that the link is up */ old_ip_up_hook = ip_up_hook; ip_up_hook = pppol2tp_ip_up_hook; old_ip_down_hook = ip_down_hook; ip_down_hook = pppol2tp_ip_down_hook; } /* Called just before pppd exits. */ static void pppol2tp_cleanup(void) { if (pppol2tp_debug_mask & PPPOL2TP_MSG_DEBUG) { dbglog("pppol2tp: exiting."); } disconnect_pppol2tp(); } void plugin_init(void) { #if defined(__linux__) extern int new_style_driver; /* From sys-linux.c */ if (!ppp_available() && !new_style_driver) fatal("Kernel doesn't support ppp_generic - " "needed for PPPoL2TP"); #else fatal("No PPPoL2TP support on this OS"); #endif add_options(pppol2tp_options); } struct channel pppol2tp_channel = { options: pppol2tp_options, process_extra_options: NULL, check_options: &pppol2tp_check_options, connect: &connect_pppol2tp, disconnect: &disconnect_pppol2tp, establish_ppp: &generic_establish_ppp, disestablish_ppp: &generic_disestablish_ppp, send_config: &send_config_pppol2tp, recv_config: &recv_config_pppol2tp, close: NULL, cleanup: NULL }; ppp-2.4.5/pppd/plugins/radius/000077500000000000000000000000001130035057700162355ustar00rootroot00000000000000ppp-2.4.5/pppd/plugins/radius/COPYRIGHT000066400000000000000000000112751130035057700175360ustar00rootroot00000000000000See the respective source files to find out which copyrights apply. ------------------------------------------------------------------------------ Copyright (C) 2002 Roaring Penguin Software Inc. Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted, provided that this copyright and permission notice appear on all copies and supporting documentation, the name of Roaring Penguin Software Inc. not be used in advertising or publicity pertaining to distribution of the program without specific prior permission, and notice be given in supporting documentation that copying and distribution is by permission of Roaring Penguin Software Inc.. Roaring Penguin Software Inc. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ------------------------------------------------------------------------------ Copyright (C) 1995,1996,1997,1998 Lars Fenneberg Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted, provided that this copyright and permission notice appear on all copies and supporting documentation, the name of Lars Fenneberg not be used in advertising or publicity pertaining to distribution of the program without specific prior permission, and notice be given in supporting documentation that copying and distribution is by permission of Lars Fenneberg. Lars Fenneberg makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ------------------------------------------------------------------------------ Copyright 1992 Livingston Enterprises, Inc. Livingston Enterprises, Inc. 6920 Koll Center Parkway Pleasanton, CA 94566 Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted, provided that this copyright and permission notice appear on all copies and supporting documentation, the name of Livingston Enterprises, Inc. not be used in advertising or publicity pertaining to distribution of the program without specific prior permission, and notice be given in supporting documentation that copying and distribution is by permission of Livingston Enterprises, Inc. Livingston Enterprises, Inc. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ------------------------------------------------------------------------------ [C] The Regents of the University of Michigan and Merit Network, Inc. 1992, 1993, 1994, 1995 All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies of the software and derivative works or modified versions thereof, and that both the copyright notice and this permission and disclaimer notice appear in supporting documentation. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN AND MERIT NETWORK, INC. DO NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. The Regents of the University of Michigan and Merit Network, Inc. shall not be liable for any special, indirect, incidental or consequential damages with respect to any claim by Licensee or any third party arising from use of the software. ------------------------------------------------------------------------------ Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. 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. ------------------------------------------------------------------------------ ppp-2.4.5/pppd/plugins/radius/Makefile.linux000066400000000000000000000027721130035057700210430ustar00rootroot00000000000000# Makefile for RADIUS plugin # # Copyright 2002 Roaring Penguin Software Inc. # DESTDIR = $(INSTROOT)@DESTDIR@ MANDIR = $(DESTDIR)/share/man/man8 LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION) VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h) INSTALL = install PLUGIN=radius.so radattr.so radrealms.so CFLAGS=-I. -I../.. -I../../../include -O2 -fPIC -DRC_LOG_FACILITY=LOG_DAEMON # Uncomment the next line to include support for Microsoft's # MS-CHAP authentication protocol. CHAPMS=y # Uncomment the next line to include support for MPPE. MPPE=y # Uncomment the next lint to include support for traffic limiting MAXOCTETS=y ifdef CHAPMS CFLAGS += -DCHAPMS=1 ifdef MPPE CFLAGS += -DMPPE=1 endif endif ifdef MAXOCTETS CFLAGS += -DMAXOCTETS=1 endif all: $(PLUGIN) install: all $(INSTALL) -d -m 755 $(LIBDIR) $(INSTALL) -s -c -m 755 radius.so $(LIBDIR) $(INSTALL) -s -c -m 755 radattr.so $(LIBDIR) $(INSTALL) -s -c -m 755 radrealms.so $(LIBDIR) $(INSTALL) -c -m 444 pppd-radius.8 $(MANDIR) $(INSTALL) -c -m 444 pppd-radattr.8 $(MANDIR) radius.so: radius.o libradiusclient.a $(CC) -o radius.so -shared radius.o libradiusclient.a radattr.so: radattr.o $(CC) -o radattr.so -shared radattr.o radrealms.so: radrealms.o $(CC) -o radrealms.so -shared radrealms.o CLIENTOBJS = avpair.o buildreq.o config.o dict.o ip_util.o \ clientid.o sendserver.o lock.o util.o md5.o libradiusclient.a: $(CLIENTOBJS) $(AR) rv $@ $? clean: rm -f *.o *.so *.a distclean: rm -f *.o *.so *.a dist-clean: distclean ppp-2.4.5/pppd/plugins/radius/avpair.c000066400000000000000000000375041130035057700176740ustar00rootroot00000000000000/* * $Id: avpair.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1995 Lars Fenneberg * * Copyright 1992 Livingston Enterprises, Inc. * * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan * and Merit Network, Inc. All Rights Reserved * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include #include static void rc_extract_vendor_specific_attributes(int attrlen, unsigned char *ptr, VALUE_PAIR **vp); /* * Function: rc_avpair_add * * Purpose: add an attribute-value pair to the given list. * * Returns: pointer to added a/v pair upon success, NULL pointer upon failure. * * Remarks: Always appends the new pair to the end of the list. * */ VALUE_PAIR *rc_avpair_add (VALUE_PAIR **list, int attrid, void *pval, int len, int vendorcode) { VALUE_PAIR *vp; vp = rc_avpair_new (attrid, pval, len, vendorcode); if (vp != (VALUE_PAIR *) NULL) { rc_avpair_insert (list, (VALUE_PAIR *) NULL, vp); } return vp; } /* * Function: rc_avpair_assign * * Purpose: assign the given value to an attribute-value pair. * * Returns: 0 on success, * -1 on failure. * */ int rc_avpair_assign (VALUE_PAIR *vp, void *pval, int len) { int result = -1; switch (vp->type) { case PW_TYPE_STRING: if (((len == 0) && (strlen ((char *) pval)) > AUTH_STRING_LEN) || (len > AUTH_STRING_LEN)) { error("rc_avpair_assign: bad attribute length"); return result; } if (len > 0) { memcpy(vp->strvalue, (char *)pval, len); vp->strvalue[len] = '\0'; vp->lvalue = len; } else { strncpy (vp->strvalue, (char *) pval, AUTH_STRING_LEN); vp->lvalue = strlen((char *) pval); } result = 0; break; case PW_TYPE_DATE: case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: vp->lvalue = * (UINT4 *) pval; result = 0; break; default: error("rc_avpair_assign: unknown attribute %d", vp->type); } return result; } /* * Function: rc_avpair_new * * Purpose: make a new attribute-value pair with given parameters. * * Returns: pointer to generated a/v pair when successful, NULL when failure. * */ VALUE_PAIR *rc_avpair_new (int attrid, void *pval, int len, int vendorcode) { VALUE_PAIR *vp = (VALUE_PAIR *) NULL; DICT_ATTR *pda; if ((pda = rc_dict_getattr (attrid, vendorcode)) == (DICT_ATTR *) NULL) { error("rc_avpair_new: unknown attribute %d", attrid); } else { if ((vp = (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR))) != (VALUE_PAIR *) NULL) { strncpy (vp->name, pda->name, sizeof (vp->name)); vp->attribute = attrid; vp->vendorcode = vendorcode; vp->next = (VALUE_PAIR *) NULL; vp->type = pda->type; if (rc_avpair_assign (vp, pval, len) == 0) { return vp; } free (vp); vp = (VALUE_PAIR *) NULL; } else novm("rc_avpair_new"); } return vp; } /* * * Function: rc_avpair_gen * * Purpose: takes attribute/value pairs from buffer and builds a * value_pair list using allocated memory. * * Returns: value_pair list or NULL on failure */ VALUE_PAIR *rc_avpair_gen (AUTH_HDR *auth) { int length; int x_len; int attribute; int attrlen; UINT4 lvalue; unsigned char *x_ptr; unsigned char *ptr; DICT_ATTR *attr; VALUE_PAIR *vp; VALUE_PAIR *pair; unsigned char hex[3]; /* For hex string conversion. */ char buffer[512]; /* * Extract attribute-value pairs */ ptr = auth->data; length = ntohs ((unsigned short) auth->length) - AUTH_HDR_LEN; vp = (VALUE_PAIR *) NULL; while (length > 0) { attribute = *ptr++; attrlen = *ptr++; attrlen -= 2; if (attrlen < 0) { error("rc_avpair_gen: received attribute with invalid length"); break; } /* Handle vendor-specific specially */ if (attribute == PW_VENDOR_SPECIFIC) { rc_extract_vendor_specific_attributes(attrlen, ptr, &vp); ptr += attrlen; length -= (attrlen + 2); continue; } if ((attr = rc_dict_getattr (attribute, VENDOR_NONE)) == (DICT_ATTR *) NULL) { *buffer= '\0'; /* Initial length. */ for (x_ptr = ptr, x_len = attrlen ; x_len > 0 ; x_len--, x_ptr++) { sprintf (hex, "%2.2X", *x_ptr); strcat (buffer, hex); } warn("rc_avpair_gen: received unknown attribute %d of length %d: 0x%s", attribute, attrlen, buffer); } else { if ((pair = (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR))) == (VALUE_PAIR *) NULL) { novm("rc_avpair_gen"); rc_avpair_free(vp); return NULL; } strcpy (pair->name, attr->name); pair->attribute = attr->value; pair->vendorcode = VENDOR_NONE; pair->type = attr->type; pair->next = (VALUE_PAIR *) NULL; switch (attr->type) { case PW_TYPE_STRING: memcpy (pair->strvalue, (char *) ptr, (size_t) attrlen); pair->strvalue[attrlen] = '\0'; pair->lvalue = attrlen; rc_avpair_insert (&vp, (VALUE_PAIR *) NULL, pair); break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: memcpy ((char *) &lvalue, (char *) ptr, sizeof (UINT4)); pair->lvalue = ntohl (lvalue); rc_avpair_insert (&vp, (VALUE_PAIR *) NULL, pair); break; default: warn("rc_avpair_gen: %s has unknown type", attr->name); free (pair); break; } } ptr += attrlen; length -= attrlen + 2; } return (vp); } /* * Function: rc_extract_vendor_specific_attributes * * Purpose: Extracts vendor-specific attributes, assuming they are in * the "SHOULD" format recommended by RCF 2138. * * Returns: found value_pair * */ static void rc_extract_vendor_specific_attributes(int attrlen, unsigned char *ptr, VALUE_PAIR **vp) { int vendor_id; int vtype; int vlen; UINT4 lvalue; DICT_ATTR *attr; VALUE_PAIR *pair; /* ptr is sitting at vendor-ID */ if (attrlen < 8) { /* Nothing to see here... */ return; } /* High-order octet of Vendor-Id must be zero (RFC2138) */ if (*ptr) { return; } /* Extract vendor_id */ vendor_id = (int) ( ((unsigned int) ptr[1]) * 256 * 256 + ((unsigned int) ptr[2]) * 256 + ((unsigned int) ptr[3])); /* Bump ptr up to contents */ ptr += 4; /* Set attrlen to length of data */ attrlen -= 4; for (; attrlen; attrlen -= vlen+2, ptr += vlen) { vtype = *ptr++; vlen = *ptr++; vlen -= 2; if (vlen < 0 || vlen > attrlen - 2) { /* Do not log an error. We are supposed to be able to cope with arbitrary vendor-specific gunk */ return; } /* Looks plausible... */ if ((attr = rc_dict_getattr(vtype, vendor_id)) == NULL) { continue; } /* TODO: Check that length matches data size!!!!! */ pair = (VALUE_PAIR *) malloc(sizeof(VALUE_PAIR)); if (!pair) { novm("rc_avpair_gen"); return; } strcpy(pair->name, attr->name); pair->attribute = attr->value; pair->vendorcode = vendor_id; pair->type = attr->type; pair->next = NULL; switch (attr->type) { case PW_TYPE_STRING: memcpy (pair->strvalue, (char *) ptr, (size_t) vlen); pair->strvalue[vlen] = '\0'; pair->lvalue = vlen; rc_avpair_insert (vp, (VALUE_PAIR *) NULL, pair); break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: memcpy ((char *) &lvalue, (char *) ptr, sizeof (UINT4)); pair->lvalue = ntohl (lvalue); rc_avpair_insert (vp, (VALUE_PAIR *) NULL, pair); break; default: warn("rc_avpair_gen: %s has unknown type", attr->name); free (pair); break; } } } /* * Function: rc_avpair_get * * Purpose: Find the first attribute value-pair (which matches the given * attribute) from the specified value-pair list. * * Returns: found value_pair * */ VALUE_PAIR *rc_avpair_get (VALUE_PAIR *vp, UINT4 attr) { for (; vp != (VALUE_PAIR *) NULL && vp->attribute != attr; vp = vp->next) { continue; } return (vp); } /* * Function: rc_avpair_copy * * Purpose: Return a copy of the existing list "p" ala strdup(). * */ VALUE_PAIR *rc_avpair_copy(VALUE_PAIR *p) { VALUE_PAIR *vp, *fp = NULL, *lp = NULL; while (p) { vp = malloc(sizeof(VALUE_PAIR)); if (!vp) { novm("rc_avpair_copy"); return NULL; /* leaks a little but so what */ } *vp = *p; if (!fp) fp = vp; if (lp) lp->next = vp; lp = vp; p = p->next; } return fp; } /* * Function: rc_avpair_insert * * Purpose: Given the address of an existing list "a" and a pointer * to an entry "p" in that list, add the list "b" to * the "a" list after the "p" entry. If "p" is NULL, add * the list "b" to the end of "a". * */ void rc_avpair_insert (VALUE_PAIR **a, VALUE_PAIR *p, VALUE_PAIR *b) { VALUE_PAIR *this_node = NULL; VALUE_PAIR *vp; if (*a == (VALUE_PAIR *) NULL) { *a = b; return; } if (!b) return; vp = *a; if ( p == (VALUE_PAIR *) NULL) /* run to end of "a" list */ { while (vp != (VALUE_PAIR *) NULL) { this_node = vp; vp = vp->next; } } else /* look for the "p" entry in the "a" list (or run to end) */ { this_node = *a; while (this_node != (VALUE_PAIR *) NULL) { if (this_node == p) { break; } this_node = this_node->next; } } /* add "b" at this_node */ vp = this_node->next; this_node->next = b; /* run to end of "b" and connect the rest of "a" */ while (b->next) b = b->next; b->next = vp; return; } /* * Function: rc_avpair_free * * Purpose: frees all value_pairs in the list * */ void rc_avpair_free (VALUE_PAIR *pair) { VALUE_PAIR *next; while (pair != (VALUE_PAIR *) NULL) { next = pair->next; free (pair); pair = next; } } /* * Function: rc_fieldcpy * * Purpose: Copy a data field from the buffer. Advance the buffer * past the data field. * */ static void rc_fieldcpy (char *string, char **uptr) { char *ptr; ptr = *uptr; if (*ptr == '"') { ptr++; while (*ptr != '"' && *ptr != '\0' && *ptr != '\n') { *string++ = *ptr++; } *string = '\0'; if (*ptr == '"') { ptr++; } *uptr = ptr; return; } while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0' && *ptr != '\n' && *ptr != '=' && *ptr != ',') { *string++ = *ptr++; } *string = '\0'; *uptr = ptr; return; } /* * Function: rc_avpair_parse * * Purpose: parses the buffer to extract the attribute-value pairs. * * Returns: 0 = successful parse of attribute-value pair, * -1 = syntax (or other) error detected. * */ #define PARSE_MODE_NAME 0 #define PARSE_MODE_EQUAL 1 #define PARSE_MODE_VALUE 2 #define PARSE_MODE_INVALID 3 int rc_avpair_parse (char *buffer, VALUE_PAIR **first_pair) { int mode; char attrstr[AUTH_ID_LEN]; char valstr[AUTH_ID_LEN]; DICT_ATTR *attr = NULL; DICT_VALUE *dval; VALUE_PAIR *pair; VALUE_PAIR *link; struct tm *tm; time_t timeval; mode = PARSE_MODE_NAME; while (*buffer != '\n' && *buffer != '\0') { if (*buffer == ' ' || *buffer == '\t') { buffer++; continue; } switch (mode) { case PARSE_MODE_NAME: /* Attribute Name */ rc_fieldcpy (attrstr, &buffer); if ((attr = rc_dict_findattr (attrstr)) == (DICT_ATTR *) NULL) { error("rc_avpair_parse: unknown attribute"); if (*first_pair) { rc_avpair_free(*first_pair); *first_pair = (VALUE_PAIR *) NULL; } return (-1); } mode = PARSE_MODE_EQUAL; break; case PARSE_MODE_EQUAL: /* Equal sign */ if (*buffer == '=') { mode = PARSE_MODE_VALUE; buffer++; } else { error("rc_avpair_parse: missing or misplaced equal sign"); if (*first_pair) { rc_avpair_free(*first_pair); *first_pair = (VALUE_PAIR *) NULL; } return (-1); } break; case PARSE_MODE_VALUE: /* Value */ rc_fieldcpy (valstr, &buffer); if ((pair = (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR))) == (VALUE_PAIR *) NULL) { novm("rc_avpair_parse"); if (*first_pair) { rc_avpair_free(*first_pair); *first_pair = (VALUE_PAIR *) NULL; } return (-1); } strcpy (pair->name, attr->name); pair->attribute = attr->value; pair->type = attr->type; pair->vendorcode = attr->vendorcode; switch (pair->type) { case PW_TYPE_STRING: strcpy (pair->strvalue, valstr); pair->lvalue = strlen(valstr); break; case PW_TYPE_INTEGER: if (isdigit (*valstr)) { pair->lvalue = atoi (valstr); } else { if ((dval = rc_dict_findval (valstr)) == (DICT_VALUE *) NULL) { error("rc_avpair_parse: unknown attribute value: %s", valstr); if (*first_pair) { rc_avpair_free(*first_pair); *first_pair = (VALUE_PAIR *) NULL; } free (pair); return (-1); } else { pair->lvalue = dval->value; } } break; case PW_TYPE_IPADDR: pair->lvalue = rc_get_ipaddr(valstr); break; case PW_TYPE_DATE: timeval = time (0); tm = localtime (&timeval); tm->tm_hour = 0; tm->tm_min = 0; tm->tm_sec = 0; rc_str2tm (valstr, tm); #ifdef TIMELOCAL pair->lvalue = (UINT4) timelocal (tm); #else /* TIMELOCAL */ pair->lvalue = (UINT4) mktime (tm); #endif /* TIMELOCAL */ break; default: error("rc_avpair_parse: unknown attribute type %d", pair->type); if (*first_pair) { rc_avpair_free(*first_pair); *first_pair = (VALUE_PAIR *) NULL; } free (pair); return (-1); } pair->next = (VALUE_PAIR *) NULL; if (*first_pair == (VALUE_PAIR *) NULL) { *first_pair = pair; } else { link = *first_pair; while (link->next != (VALUE_PAIR *) NULL) { link = link->next; } link->next = pair; } mode = PARSE_MODE_NAME; break; default: mode = PARSE_MODE_NAME; break; } } return (0); } /* * Function: rc_avpair_tostr * * Purpose: Translate an av_pair into two strings * * Returns: 0 on success, -1 on failure * */ int rc_avpair_tostr (VALUE_PAIR *pair, char *name, int ln, char *value, int lv) { DICT_VALUE *dval; char buffer[32]; struct in_addr inad; unsigned char *ptr; *name = *value = '\0'; if (!pair || pair->name[0] == '\0') { error("rc_avpair_tostr: pair is NULL or empty"); return (-1); } strncpy(name, pair->name, (size_t) ln); switch (pair->type) { case PW_TYPE_STRING: lv--; ptr = (unsigned char *) pair->strvalue; while (*ptr != '\0') { if (!(isprint (*ptr))) { sprintf (buffer, "\\%03o", *ptr); strncat(value, buffer, (size_t) lv); lv -= 4; if (lv < 0) break; } else { strncat(value, ptr, 1); lv--; if (lv < 0) break; } ptr++; } break; case PW_TYPE_INTEGER: dval = rc_dict_getval (pair->lvalue, pair->name); if (dval != (DICT_VALUE *) NULL) { strncpy(value, dval->name, (size_t) lv-1); } else { sprintf (buffer, "%ld", pair->lvalue); strncpy(value, buffer, (size_t) lv); } break; case PW_TYPE_IPADDR: inad.s_addr = htonl(pair->lvalue); strncpy (value, inet_ntoa (inad), (size_t) lv-1); break; case PW_TYPE_DATE: strftime (buffer, sizeof (buffer), "%m/%d/%y %H:%M:%S", gmtime ((time_t *) & pair->lvalue)); strncpy(value, buffer, lv-1); break; default: error("rc_avpair_tostr: unknown attribute type %d", pair->type); return (-1); break; } return 0; } /* * Function: rc_avpair_readin * * Purpose: get a sequence of attribute value pairs from the file input * and make them into a list of value_pairs * */ VALUE_PAIR *rc_avpair_readin(FILE *input) { VALUE_PAIR *vp = NULL; char buffer[1024], *q; while (fgets(buffer, sizeof(buffer), input) != NULL) { q = buffer; while(*q && isspace(*q)) q++; if ((*q == '\n') || (*q == '#') || (*q == '\0')) continue; if (rc_avpair_parse(q, &vp) < 0) { error("rc_avpair_readin: malformed attribute: %s", buffer); rc_avpair_free(vp); return NULL; } } return vp; } ppp-2.4.5/pppd/plugins/radius/buildreq.c000066400000000000000000000234771130035057700202250ustar00rootroot00000000000000/* * $Id: buildreq.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1995,1997 Lars Fenneberg * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include #include unsigned char rc_get_seqnbr(void); /* * Function: rc_get_nas_id * * Purpose: fills in NAS-Identifier or NAS-IP-Address in request * */ int rc_get_nas_id(VALUE_PAIR **sendpairs) { UINT4 client_id; char *nasid; nasid = rc_conf_str("nas_identifier"); if (strlen(nasid)) { /* * Fill in NAS-Identifier */ if (rc_avpair_add(sendpairs, PW_NAS_IDENTIFIER, nasid, 0, VENDOR_NONE) == NULL) return (ERROR_RC); return (OK_RC); } else { /* * Fill in NAS-IP-Address */ if ((client_id = rc_own_ipaddress()) == 0) return (ERROR_RC); if (rc_avpair_add(sendpairs, PW_NAS_IP_ADDRESS, &client_id, 0, VENDOR_NONE) == NULL) return (ERROR_RC); } return (OK_RC); } /* * Function: rc_buildreq * * Purpose: builds a skeleton RADIUS request using information from the * config file. * */ void rc_buildreq(SEND_DATA *data, int code, char *server, unsigned short port, int timeout, int retries) { data->server = server; data->svc_port = port; data->seq_nbr = rc_get_seqnbr(); data->timeout = timeout; data->retries = retries; data->code = code; } /* * Function: rc_guess_seqnbr * * Purpose: return a random sequence number * */ static unsigned char rc_guess_seqnbr(void) { return (unsigned char)(magic() & UCHAR_MAX); } /* * Function: rc_get_seqnbr * * Purpose: generate a sequence number * */ unsigned char rc_get_seqnbr(void) { FILE *sf; int tries = 1; int seq_nbr, pos; char *seqfile = rc_conf_str("seqfile"); if ((sf = fopen(seqfile, "a+")) == NULL) { error("rc_get_seqnbr: couldn't open sequence file %s: %s", seqfile, strerror(errno)); /* well, so guess a sequence number */ return rc_guess_seqnbr(); } while (do_lock_exclusive(fileno(sf))!= 0) { if (errno != EWOULDBLOCK) { error("rc_get_seqnbr: flock failure: %s: %s", seqfile, strerror(errno)); fclose(sf); return rc_guess_seqnbr(); } tries++; if (tries <= 10) rc_mdelay(500); else break; } if (tries > 10) { error("rc_get_seqnbr: couldn't get lock after %d tries: %s", tries-1, seqfile); fclose(sf); return rc_guess_seqnbr(); } pos = ftell(sf); rewind(sf); if (fscanf(sf, "%d", &seq_nbr) != 1) { if (pos != ftell(sf)) { /* file was not empty */ error("rc_get_seqnbr: fscanf failure: %s", seqfile); } seq_nbr = rc_guess_seqnbr(); } rewind(sf); ftruncate(fileno(sf),0); fprintf(sf,"%d\n", (seq_nbr+1) & UCHAR_MAX); fflush(sf); /* fflush because a process may read it between the do_unlock and fclose */ if (do_unlock(fileno(sf)) != 0) error("rc_get_seqnbr: couldn't release lock on %s: %s", seqfile, strerror(errno)); fclose(sf); return (unsigned char)seq_nbr; } /* * Function: rc_auth * * Purpose: Builds an authentication request for port id client_port * with the value_pairs send and submits it to a server * * Returns: received value_pairs in received, messages from the server in msg * and 0 on success, negative on failure as return value * */ int rc_auth(UINT4 client_port, VALUE_PAIR *send, VALUE_PAIR **received, char *msg, REQUEST_INFO *info) { SERVER *authserver = rc_conf_srv("authserver"); if (!authserver) { return (ERROR_RC); } return rc_auth_using_server(authserver, client_port, send, received, msg, info); } /* * Function: rc_auth_using_server * * Purpose: Builds an authentication request for port id client_port * with the value_pairs send and submits it to a server. You * explicitly supply a server list. * * Returns: received value_pairs in received, messages from the server in msg * and 0 on success, negative on failure as return value * */ int rc_auth_using_server(SERVER *authserver, UINT4 client_port, VALUE_PAIR *send, VALUE_PAIR **received, char *msg, REQUEST_INFO *info) { SEND_DATA data; int result; int i; int timeout = rc_conf_int("radius_timeout"); int retries = rc_conf_int("radius_retries"); data.send_pairs = send; data.receive_pairs = NULL; /* * Fill in NAS-IP-Address or NAS-Identifier */ if (rc_get_nas_id(&(data.send_pairs)) == ERROR_RC) return (ERROR_RC); /* * Fill in NAS-Port */ if (rc_avpair_add(&(data.send_pairs), PW_NAS_PORT, &client_port, 0, VENDOR_NONE) == NULL) return (ERROR_RC); result = ERROR_RC; for(i=0; (imax) && (result != OK_RC) && (result != BADRESP_RC) ; i++) { if (data.receive_pairs != NULL) { rc_avpair_free(data.receive_pairs); data.receive_pairs = NULL; } rc_buildreq(&data, PW_ACCESS_REQUEST, authserver->name[i], authserver->port[i], timeout, retries); result = rc_send_server (&data, msg, info); } *received = data.receive_pairs; return result; } /* * Function: rc_auth_proxy * * Purpose: Builds an authentication request * with the value_pairs send and submits it to a server. * Works for a proxy; does not add IP address, and does * does not rely on config file. * * Returns: received value_pairs in received, messages from the server in msg * and 0 on success, negative on failure as return value * */ int rc_auth_proxy(VALUE_PAIR *send, VALUE_PAIR **received, char *msg) { SEND_DATA data; int result; int i; SERVER *authserver = rc_conf_srv("authserver"); int timeout = rc_conf_int("radius_timeout"); int retries = rc_conf_int("radius_retries"); data.send_pairs = send; data.receive_pairs = NULL; result = ERROR_RC; for(i=0; (imax) && (result != OK_RC) && (result != BADRESP_RC) ; i++) { if (data.receive_pairs != NULL) { rc_avpair_free(data.receive_pairs); data.receive_pairs = NULL; } rc_buildreq(&data, PW_ACCESS_REQUEST, authserver->name[i], authserver->port[i], timeout, retries); result = rc_send_server (&data, msg, NULL); } *received = data.receive_pairs; return result; } /* * Function: rc_acct_using_server * * Purpose: Builds an accounting request for port id client_port * with the value_pairs send. You explicitly supply server list. * * Remarks: NAS-Identifier/NAS-IP-Address, NAS-Port and Acct-Delay-Time get * filled in by this function, the rest has to be supplied. */ int rc_acct_using_server(SERVER *acctserver, UINT4 client_port, VALUE_PAIR *send) { SEND_DATA data; VALUE_PAIR *adt_vp; int result; time_t start_time, dtime; char msg[4096]; int i; int timeout = rc_conf_int("radius_timeout"); int retries = rc_conf_int("radius_retries"); data.send_pairs = send; data.receive_pairs = NULL; /* * Fill in NAS-IP-Address or NAS-Identifier */ if (rc_get_nas_id(&(data.send_pairs)) == ERROR_RC) return (ERROR_RC); /* * Fill in NAS-Port */ if (rc_avpair_add(&(data.send_pairs), PW_NAS_PORT, &client_port, 0, VENDOR_NONE) == NULL) return (ERROR_RC); /* * Fill in Acct-Delay-Time */ dtime = 0; if ((adt_vp = rc_avpair_add(&(data.send_pairs), PW_ACCT_DELAY_TIME, &dtime, 0, VENDOR_NONE)) == NULL) return (ERROR_RC); start_time = time(NULL); result = ERROR_RC; for(i=0; (imax) && (result != OK_RC) && (result != BADRESP_RC) ; i++) { if (data.receive_pairs != NULL) { rc_avpair_free(data.receive_pairs); data.receive_pairs = NULL; } rc_buildreq(&data, PW_ACCOUNTING_REQUEST, acctserver->name[i], acctserver->port[i], timeout, retries); dtime = time(NULL) - start_time; rc_avpair_assign(adt_vp, &dtime, 0); result = rc_send_server (&data, msg, NULL); } rc_avpair_free(data.receive_pairs); return result; } /* * Function: rc_acct * * Purpose: Builds an accounting request for port id client_port * with the value_pairs send * * Remarks: NAS-Identifier/NAS-IP-Address, NAS-Port and Acct-Delay-Time get * filled in by this function, the rest has to be supplied. */ int rc_acct(UINT4 client_port, VALUE_PAIR *send) { SERVER *acctserver = rc_conf_srv("acctserver"); if (!acctserver) return (ERROR_RC); return rc_acct_using_server(acctserver, client_port, send); } /* * Function: rc_acct_proxy * * Purpose: Builds an accounting request with the value_pairs send * */ int rc_acct_proxy(VALUE_PAIR *send) { SEND_DATA data; int result; char msg[4096]; int i; SERVER *acctserver = rc_conf_srv("authserver"); int timeout = rc_conf_int("radius_timeout"); int retries = rc_conf_int("radius_retries"); data.send_pairs = send; data.receive_pairs = NULL; result = ERROR_RC; for(i=0; (imax) && (result != OK_RC) && (result != BADRESP_RC) ; i++) { if (data.receive_pairs != NULL) { rc_avpair_free(data.receive_pairs); data.receive_pairs = NULL; } rc_buildreq(&data, PW_ACCOUNTING_REQUEST, acctserver->name[i], acctserver->port[i], timeout, retries); result = rc_send_server (&data, msg, NULL); } rc_avpair_free(data.receive_pairs); return result; } /* * Function: rc_check * * Purpose: ask the server hostname on the specified port for a * status message * */ int rc_check(char *host, unsigned short port, char *msg) { SEND_DATA data; int result; UINT4 service_type; int timeout = rc_conf_int("radius_timeout"); int retries = rc_conf_int("radius_retries"); data.send_pairs = data.receive_pairs = NULL; /* * Fill in NAS-IP-Address or NAS-Identifier, * although it isn't neccessary */ if (rc_get_nas_id(&(data.send_pairs)) == ERROR_RC) return (ERROR_RC); /* * Fill in Service-Type */ service_type = PW_ADMINISTRATIVE; rc_avpair_add(&(data.send_pairs), PW_SERVICE_TYPE, &service_type, 0, VENDOR_NONE); rc_buildreq(&data, PW_STATUS_SERVER, host, port, timeout, retries); result = rc_send_server (&data, msg, NULL); rc_avpair_free(data.receive_pairs); return result; } ppp-2.4.5/pppd/plugins/radius/clientid.c000066400000000000000000000040711130035057700201760ustar00rootroot00000000000000/* * $Id: clientid.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1995,1996,1997 Lars Fenneberg * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include #include struct map2id_s { char *name; UINT4 id; struct map2id_s *next; }; static struct map2id_s *map2id_list = NULL; /* * Function: rc_read_mapfile * * Purpose: Read in the ttyname to port id map file * * Arguments: the file name of the map file * * Returns: zero on success, negative integer on failure */ int rc_read_mapfile(char *filename) { char buffer[1024]; FILE *mapfd; char *c, *name, *id, *q; struct map2id_s *p; int lnr = 0; if ((mapfd = fopen(filename,"r")) == NULL) { error("rc_read_mapfile: can't read %s: %s", filename, strerror(errno)); return (-1); } #define SKIP(p) while(*p && isspace(*p)) p++; while (fgets(buffer, sizeof(buffer), mapfd) != NULL) { lnr++; q = buffer; SKIP(q); if ((*q == '\n') || (*q == '#') || (*q == '\0')) continue; if (( c = strchr(q, ' ')) || (c = strchr(q,'\t'))) { *c = '\0'; c++; SKIP(c); name = q; id = c; if ((p = (struct map2id_s *)malloc(sizeof(*p))) == NULL) { novm("rc_read_mapfile"); return (-1); } p->name = strdup(name); p->id = atoi(id); p->next = map2id_list; map2id_list = p; } else { error("rc_read_mapfile: malformed line in %s, line %d", filename, lnr); return (-1); } } #undef SKIP fclose(mapfd); return 0; } /* * Function: rc_map2id * * Purpose: Map ttyname to port id * * Arguments: full pathname of the tty * * Returns: port id, zero if no entry found */ UINT4 rc_map2id(char *name) { struct map2id_s *p; char ttyname[PATH_MAX]; *ttyname = '\0'; if (*name != '/') strcpy(ttyname, "/dev/"); strncat(ttyname, name, sizeof(ttyname)); for(p = map2id_list; p; p = p->next) if (!strcmp(ttyname, p->name)) return p->id; warn("rc_map2id: can't find tty %s in map database", ttyname); return 0; } ppp-2.4.5/pppd/plugins/radius/config.c000066400000000000000000000254171130035057700176570ustar00rootroot00000000000000/* * $Id: config.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1995,1996,1997 Lars Fenneberg * * Copyright 1992 Livingston Enterprises, Inc. * * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan * and Merit Network, Inc. All Rights Reserved * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include #include #include static int test_config(char *); /* * Function: find_option * * Purpose: find an option in the option list * * Returns: pointer to option on success, NULL otherwise */ static OPTION *find_option(char *optname, unsigned int type) { int i; /* there're so few options that a binary search seems not necessary */ for (i = 0; i < num_options; i++) { if (!strcmp(config_options[i].name, optname) && (config_options[i].type & type)) return &config_options[i]; } return NULL; } /* * Function: set_option_... * * Purpose: set a specific option doing type conversions * * Returns: 0 on success, -1 on failure */ static int set_option_str(char *filename, int line, OPTION *option, char *p) { if (p) option->val = (void *) strdup(p); else option->val = NULL; return 0; } static int set_option_int(char *filename, int line, OPTION *option, char *p) { int *iptr; if (p == NULL) { error("%s: line %d: bogus option value", filename, line); return (-1); } if ((iptr = (int *) malloc(sizeof(iptr))) == NULL) { novm("read_config"); return (-1); } *iptr = atoi(p); option->val = (void *) iptr; return 0; } static int set_option_srv(char *filename, int line, OPTION *option, char *p) { SERVER *serv; char *q; struct servent *svp; int i; if (p == NULL) { error("%s: line %d: bogus option value", filename, line); return (-1); } serv = (SERVER *) option->val; for (i = 0; i < serv->max; i++) { free(serv->name[i]); } serv->max = 0; while ((p = strtok(p, ", \t")) != NULL) { if ((q = strchr(p,':')) != NULL) { *q = '\0'; q++; serv->port[serv->max] = atoi(q); } else { if (!strcmp(option->name,"authserver")) if ((svp = getservbyname ("radius", "udp")) == NULL) serv->port[serv->max] = PW_AUTH_UDP_PORT; else serv->port[serv->max] = ntohs ((unsigned int) svp->s_port); else if (!strcmp(option->name, "acctserver")) if ((svp = getservbyname ("radacct", "udp")) == NULL) serv->port[serv->max] = PW_ACCT_UDP_PORT; else serv->port[serv->max] = ntohs ((unsigned int) svp->s_port); else { error("%s: line %d: no default port for %s", filename, line, option->name); return (-1); } } serv->name[serv->max++] = strdup(p); p = NULL; } return 0; } static int set_option_auo(char *filename, int line, OPTION *option, char *p) { int *iptr; if (p == NULL) { warn("%s: line %d: bogus option value", filename, line); return (-1); } if ((iptr = (int *) malloc(sizeof(iptr))) == NULL) { novm("read_config"); return (-1); } *iptr = 0; p = strtok(p, ", \t"); if (!strncmp(p, "local", 5)) *iptr = AUTH_LOCAL_FST; else if (!strncmp(p, "radius", 6)) *iptr = AUTH_RADIUS_FST; else { error("%s: auth_order: unknown keyword: %s", filename, p); return (-1); } p = strtok(NULL, ", \t"); if (p && (*p != '\0')) { if ((*iptr & AUTH_RADIUS_FST) && !strcmp(p, "local")) *iptr = (*iptr) | AUTH_LOCAL_SND; else if ((*iptr & AUTH_LOCAL_FST) && !strcmp(p, "radius")) *iptr = (*iptr) | AUTH_RADIUS_SND; else { error("%s: auth_order: unknown or unexpected keyword: %s", filename, p); return (-1); } } option->val = (void *) iptr; return 0; } /* * Function: rc_read_config * * Purpose: read the global config file * * Returns: 0 on success, -1 when failure */ int rc_read_config(char *filename) { FILE *configfd; char buffer[512], *p; OPTION *option; int line, pos; if ((configfd = fopen(filename,"r")) == NULL) { error("rc_read_config: can't open %s: %m", filename); return (-1); } line = 0; while ((fgets(buffer, sizeof(buffer), configfd) != NULL)) { line++; p = buffer; if ((*p == '\n') || (*p == '#') || (*p == '\0')) continue; p[strlen(p)-1] = '\0'; if ((pos = strcspn(p, "\t ")) == 0) { error("%s: line %d: bogus format: %s", filename, line, p); return (-1); } p[pos] = '\0'; if ((option = find_option(p, OT_ANY)) == NULL) { error("%s: line %d: unrecognized keyword: %s", filename, line, p); return (-1); } if (option->status != ST_UNDEF) { error("%s: line %d: duplicate option line: %s", filename, line, p); return (-1); } p += pos+1; while (isspace(*p)) p++; switch (option->type) { case OT_STR: if (set_option_str(filename, line, option, p) < 0) return (-1); break; case OT_INT: if (set_option_int(filename, line, option, p) < 0) return (-1); break; case OT_SRV: if (set_option_srv(filename, line, option, p) < 0) return (-1); break; case OT_AUO: if (set_option_auo(filename, line, option, p) < 0) return (-1); break; default: fatal("rc_read_config: impossible case branch!"); abort(); } } fclose(configfd); return test_config(filename); } /* * Function: rc_conf_str, rc_conf_int, rc_conf_src * * Purpose: get the value of a config option * * Returns: config option value */ char *rc_conf_str(char *optname) { OPTION *option; option = find_option(optname, OT_STR); if (option == NULL) fatal("rc_conf_str: unkown config option requested: %s", optname); return (char *)option->val; } int rc_conf_int(char *optname) { OPTION *option; option = find_option(optname, OT_INT|OT_AUO); if (option == NULL) fatal("rc_conf_int: unkown config option requested: %s", optname); return *((int *)option->val); } SERVER *rc_conf_srv(char *optname) { OPTION *option; option = find_option(optname, OT_SRV); if (option == NULL) fatal("rc_conf_srv: unkown config option requested: %s", optname); return (SERVER *)option->val; } /* * Function: test_config * * Purpose: test the configuration the user supplied * * Returns: 0 on success, -1 when failure */ static int test_config(char *filename) { #if 0 struct stat st; char *file; #endif if (!(rc_conf_srv("authserver")->max)) { error("%s: no authserver specified", filename); return (-1); } if (!(rc_conf_srv("acctserver")->max)) { error("%s: no acctserver specified", filename); return (-1); } if (!rc_conf_str("servers")) { error("%s: no servers file specified", filename); return (-1); } if (!rc_conf_str("dictionary")) { error("%s: no dictionary specified", filename); return (-1); } if (rc_conf_int("radius_timeout") <= 0) { error("%s: radius_timeout <= 0 is illegal", filename); return (-1); } if (rc_conf_int("radius_retries") <= 0) { error("%s: radius_retries <= 0 is illegal", filename); return (-1); } #if 0 file = rc_conf_str("login_local"); if (stat(file, &st) == 0) { if (!S_ISREG(st.st_mode)) { error("%s: not a regular file: %s", filename, file); return (-1); } } else { error("%s: file not found: %s", filename, file); return (-1); } file = rc_conf_str("login_radius"); if (stat(file, &st) == 0) { if (!S_ISREG(st.st_mode)) { error("%s: not a regular file: %s", filename, file); return (-1); } } else { error("%s: file not found: %s", filename, file); return (-1); } #endif if (rc_conf_int("login_tries") <= 0) { error("%s: login_tries <= 0 is illegal", filename); return (-1); } if (rc_conf_str("seqfile") == NULL) { error("%s: seqfile not specified", filename); return (-1); } if (rc_conf_int("login_timeout") <= 0) { error("%s: login_timeout <= 0 is illegal", filename); return (-1); } if (rc_conf_str("mapfile") == NULL) { error("%s: mapfile not specified", filename); return (-1); } if (rc_conf_str("nologin") == NULL) { error("%s: nologin not specified", filename); return (-1); } return 0; } /* * Function: rc_find_match * * Purpose: see if ip_addr is one of the ip addresses of hostname * * Returns: 0 on success, -1 when failure * */ static int find_match (UINT4 *ip_addr, char *hostname) { UINT4 addr; char **paddr; struct hostent *hp; if (rc_good_ipaddr (hostname) == 0) { if (*ip_addr == ntohl(inet_addr (hostname))) { return (0); } } else { if ((hp = gethostbyname (hostname)) == (struct hostent *) NULL) { return (-1); } for (paddr = hp->h_addr_list; *paddr; paddr++) { addr = ** (UINT4 **) paddr; if (ntohl(addr) == *ip_addr) { return (0); } } } return (-1); } /* * Function: rc_find_server * * Purpose: search a server in the servers file * * Returns: 0 on success, -1 on failure * */ int rc_find_server (char *server_name, UINT4 *ip_addr, char *secret) { UINT4 myipaddr = 0; int len; int result; FILE *clientfd; char *h; char *s; char *host2; char buffer[128]; char hostnm[AUTH_ID_LEN + 1]; /* Get the IP address of the authentication server */ if ((*ip_addr = rc_get_ipaddr (server_name)) == (UINT4) 0) return (-1); if ((clientfd = fopen (rc_conf_str("servers"), "r")) == (FILE *) NULL) { error("rc_find_server: couldn't open file: %m: %s", rc_conf_str("servers")); return (-1); } myipaddr = rc_own_ipaddress(); result = 0; while (fgets (buffer, sizeof (buffer), clientfd) != (char *) NULL) { if (*buffer == '#') continue; if ((h = strtok (buffer, " \t\n")) == NULL) /* first hostname */ continue; memset (hostnm, '\0', AUTH_ID_LEN); len = strlen (h); if (len > AUTH_ID_LEN) { len = AUTH_ID_LEN; } strncpy (hostnm, h, (size_t) len); hostnm[AUTH_ID_LEN] = '\0'; if ((s = strtok (NULL, " \t\n")) == NULL) /* and secret field */ continue; memset (secret, '\0', MAX_SECRET_LENGTH); len = strlen (s); if (len > MAX_SECRET_LENGTH) { len = MAX_SECRET_LENGTH; } strncpy (secret, s, (size_t) len); secret[MAX_SECRET_LENGTH] = '\0'; if (!strchr (hostnm, '/')) /* If single name form */ { if (find_match (ip_addr, hostnm) == 0) { result++; break; } } else /* / "paired" form */ { strtok (hostnm, "/"); if (find_match (&myipaddr, hostnm) == 0) { /* If we're the 1st name, target is 2nd */ host2 = strtok (NULL, " "); if (find_match (ip_addr, host2) == 0) { result++; break; } } else /* If we were 2nd name, target is 1st name */ { if (find_match (ip_addr, hostnm) == 0) { result++; break; } } } } fclose (clientfd); if (result == 0) { memset (buffer, '\0', sizeof (buffer)); memset (secret, '\0', sizeof (secret)); error("rc_find_server: couldn't find RADIUS server %s in %s", server_name, rc_conf_str("servers")); return (-1); } return 0; } ppp-2.4.5/pppd/plugins/radius/dict.c000066400000000000000000000234221130035057700173270ustar00rootroot00000000000000/* * $Id: dict.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 2002 Roaring Penguin Software Inc. * * Copyright (C) 1995,1996,1997 Lars Fenneberg * * Copyright 1992 Livingston Enterprises, Inc. * * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan * and Merit Network, Inc. All Rights Reserved * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include #include static DICT_ATTR *dictionary_attributes = NULL; static DICT_VALUE *dictionary_values = NULL; static VENDOR_DICT *vendor_dictionaries = NULL; /* * Function: rc_read_dictionary * * Purpose: Initialize the dictionary. Read all ATTRIBUTES into * the dictionary_attributes list. Read all VALUES into * the dictionary_values list. Construct VENDOR dictionaries * as required. * */ int rc_read_dictionary (char *filename) { FILE *dictfd; char dummystr[AUTH_ID_LEN]; char namestr[AUTH_ID_LEN]; char valstr[AUTH_ID_LEN]; char attrstr[AUTH_ID_LEN]; char typestr[AUTH_ID_LEN]; char vendorstr[AUTH_ID_LEN]; int line_no; DICT_ATTR *attr; DICT_VALUE *dval; VENDOR_DICT *vdict; char buffer[256]; int value; int type; int n; int retcode; if ((dictfd = fopen (filename, "r")) == (FILE *) NULL) { error( "rc_read_dictionary: couldn't open dictionary %s: %s", filename, strerror(errno)); return (-1); } line_no = 0; retcode = 0; while (fgets (buffer, sizeof (buffer), dictfd) != (char *) NULL) { line_no++; /* Skip empty space */ if (*buffer == '#' || *buffer == '\0' || *buffer == '\n') { continue; } if (strncmp (buffer, "VENDOR", 6) == 0) { /* Read the VENDOR line */ if (sscanf(buffer, "%s%s%d", dummystr, namestr, &value) != 3) { error("rc_read_dictionary: invalid vendor on line %d of dictionary %s", line_no, filename); retcode = -1; break; } /* Validate entry */ if (strlen (namestr) > NAME_LENGTH) { error("rc_read_dictionary: invalid name length on line %d of dictionary %s", line_no, filename); retcode = -1; break; } /* Create new vendor entry */ vdict = (VENDOR_DICT *) malloc (sizeof (VENDOR_DICT)); if (!vdict) { novm("rc_read_dictionary"); retcode = -1; break; } strcpy(vdict->vendorname, namestr); vdict->vendorcode = value; vdict->attributes = NULL; vdict->next = vendor_dictionaries; vendor_dictionaries = vdict; } else if (strncmp (buffer, "ATTRIBUTE", 9) == 0) { /* Read the ATTRIBUTE line. It is one of: * ATTRIBUTE attr_name attr_val type OR * ATTRIBUTE attr_name attr_val type vendor */ vendorstr[0] = 0; n = sscanf(buffer, "%s%s%s%s%s", dummystr, namestr, valstr, typestr, vendorstr); if (n != 4 && n != 5) { error("rc_read_dictionary: invalid attribute on line %d of dictionary %s", line_no, filename); retcode = -1; break; } /* * Validate all entries */ if (strlen (namestr) > NAME_LENGTH) { error("rc_read_dictionary: invalid name length on line %d of dictionary %s", line_no, filename); retcode = -1; break; } if (strlen (vendorstr) > NAME_LENGTH) { error("rc_read_dictionary: invalid name length on line %d of dictionary %s", line_no, filename); retcode = -1; break; } if (!isdigit (*valstr)) { error("rc_read_dictionary: invalid value on line %d of dictionary %s", line_no, filename); retcode = -1; break; } value = atoi (valstr); if (strcmp (typestr, "string") == 0) { type = PW_TYPE_STRING; } else if (strcmp (typestr, "integer") == 0) { type = PW_TYPE_INTEGER; } else if (strcmp (typestr, "ipaddr") == 0) { type = PW_TYPE_IPADDR; } else if (strcmp (typestr, "date") == 0) { type = PW_TYPE_DATE; } else { error("rc_read_dictionary: invalid type on line %d of dictionary %s", line_no, filename); retcode = -1; break; } /* Search for vendor if supplied */ if (*vendorstr) { vdict = rc_dict_findvendor(vendorstr); if (!vdict) { error("rc_read_dictionary: unknown vendor on line %d of dictionary %s", line_no, filename); retcode = -1; break; } } else { vdict = NULL; } /* Create a new attribute for the list */ if ((attr = (DICT_ATTR *) malloc (sizeof (DICT_ATTR))) == (DICT_ATTR *) NULL) { novm("rc_read_dictionary"); retcode = -1; break; } strcpy (attr->name, namestr); if (vdict) { attr->vendorcode = vdict->vendorcode; } else { attr->vendorcode = VENDOR_NONE; } attr->value = value; attr->type = type; /* Insert it into the list */ if (vdict) { attr->next = vdict->attributes; vdict->attributes = attr; } else { attr->next = dictionary_attributes; dictionary_attributes = attr; } } else if (strncmp (buffer, "VALUE", 5) == 0) { /* Read the VALUE line */ if (sscanf (buffer, "%s%s%s%s", dummystr, attrstr, namestr, valstr) != 4) { error("rc_read_dictionary: invalid value entry on line %d of dictionary %s", line_no, filename); retcode = -1; break; } /* * Validate all entries */ if (strlen (attrstr) > NAME_LENGTH) { error("rc_read_dictionary: invalid attribute length on line %d of dictionary %s", line_no, filename); retcode = -1; break; } if (strlen (namestr) > NAME_LENGTH) { error("rc_read_dictionary: invalid name length on line %d of dictionary %s", line_no, filename); retcode = -1; break; } if (!isdigit (*valstr)) { error("rc_read_dictionary: invalid value on line %d of dictionary %s", line_no, filename); retcode = -1; break; } value = atoi (valstr); /* Create a new VALUE entry for the list */ if ((dval = (DICT_VALUE *) malloc (sizeof (DICT_VALUE))) == (DICT_VALUE *) NULL) { novm("rc_read_dictionary"); retcode = -1; break; } strcpy (dval->attrname, attrstr); strcpy (dval->name, namestr); dval->value = value; /* Insert it into the list */ dval->next = dictionary_values; dictionary_values = dval; } else if (strncmp (buffer, "INCLUDE", 7) == 0) { /* Read the INCLUDE line */ if (sscanf (buffer, "%s%s", dummystr, namestr) != 2) { error("rc_read_dictionary: invalid include entry on line %d of dictionary %s", line_no, filename); retcode = -1; break; } if (rc_read_dictionary(namestr) == -1) { retcode = -1; break; } } } fclose (dictfd); return retcode; } /* * Function: rc_dict_getattr * * Purpose: Return the full attribute structure based on the * attribute id number and vendor code. If vendor code is VENDOR_NONE, * non-vendor-specific attributes are used * */ DICT_ATTR *rc_dict_getattr (int attribute, int vendor) { DICT_ATTR *attr; VENDOR_DICT *dict; if (vendor == VENDOR_NONE) { attr = dictionary_attributes; while (attr != (DICT_ATTR *) NULL) { if (attr->value == attribute) { return (attr); } attr = attr->next; } } else { dict = rc_dict_getvendor(vendor); if (!dict) { return NULL; } attr = dict->attributes; while (attr) { if (attr->value == attribute) { return attr; } attr = attr->next; } } return NULL; } /* * Function: rc_dict_findattr * * Purpose: Return the full attribute structure based on the * attribute name. * */ DICT_ATTR *rc_dict_findattr (char *attrname) { DICT_ATTR *attr; VENDOR_DICT *dict; attr = dictionary_attributes; while (attr != (DICT_ATTR *) NULL) { if (strcasecmp (attr->name, attrname) == 0) { return (attr); } attr = attr->next; } /* Search vendor-specific dictionaries */ dict = vendor_dictionaries; while (dict) { attr = dict->attributes; while (attr) { if (strcasecmp (attr->name, attrname) == 0) { return (attr); } attr = attr->next; } dict = dict->next; } return ((DICT_ATTR *) NULL); } /* * Function: rc_dict_findval * * Purpose: Return the full value structure based on the * value name. * */ DICT_VALUE *rc_dict_findval (char *valname) { DICT_VALUE *val; val = dictionary_values; while (val != (DICT_VALUE *) NULL) { if (strcasecmp (val->name, valname) == 0) { return (val); } val = val->next; } return ((DICT_VALUE *) NULL); } /* * Function: dict_getval * * Purpose: Return the full value structure based on the * actual value and the associated attribute name. * */ DICT_VALUE * rc_dict_getval (UINT4 value, char *attrname) { DICT_VALUE *val; val = dictionary_values; while (val != (DICT_VALUE *) NULL) { if (strcmp (val->attrname, attrname) == 0 && val->value == value) { return (val); } val = val->next; } return ((DICT_VALUE *) NULL); } /* * Function: rc_dict_findvendor * * Purpose: Return the vendor's dictionary given the vendor name. * */ VENDOR_DICT * rc_dict_findvendor (char *vendorname) { VENDOR_DICT *dict; dict = vendor_dictionaries; while (dict) { if (!strcmp(vendorname, dict->vendorname)) { return dict; } dict = dict->next; } return NULL; } /* * Function: rc_dict_getvendor * * Purpose: Return the vendor's dictionary given the vendor ID * */ VENDOR_DICT * rc_dict_getvendor (int id) { VENDOR_DICT *dict; dict = vendor_dictionaries; while (dict) { if (id == dict->vendorcode) { return dict; } dict = dict->next; } return NULL; } ppp-2.4.5/pppd/plugins/radius/etc/000077500000000000000000000000001130035057700170105ustar00rootroot00000000000000ppp-2.4.5/pppd/plugins/radius/etc/dictionary000066400000000000000000000167501130035057700211110ustar00rootroot00000000000000# # Updated 97/06/13 to livingston-radius-2.01 miquels@cistron.nl # # This file contains dictionary translations for parsing # requests and generating responses. All transactions are # composed of Attribute/Value Pairs. The value of each attribute # is specified as one of 4 data types. Valid data types are: # # string - 0-253 octets # ipaddr - 4 octets in network byte order # integer - 32 bit value in big endian order (high byte first) # date - 32 bit value in big endian order - seconds since # 00:00:00 GMT, Jan. 1, 1970 # # Enumerated values are stored in the user file with dictionary # VALUE translations for easy administration. # # Example: # # ATTRIBUTE VALUE # --------------- ----- # Framed-Protocol = PPP # 7 = 1 (integer encoding) # # The dictionary format now supports vendor-specific attributes. # Vendors are introduced like this: # # VENDOR vendor_name vendor_number # # For example: # # VENDOR RoaringPenguin 10055 # # Vendor-specific attributes have a fifth field with the name of the # vendor. For example: # # ATTRIBUTE RP-Upstream-Speed-Limit 1 integer RoaringPenguin # # introduces a Roaring Penguin vendor-specific attribbute with name # RP-Upstream-Speed-Limit, number 1, type integer and vendor RoaringPenguin. # # Following are the proper new names. Use these. # ATTRIBUTE User-Name 1 string ATTRIBUTE Password 2 string ATTRIBUTE CHAP-Password 3 string ATTRIBUTE NAS-IP-Address 4 ipaddr ATTRIBUTE NAS-Port-Id 5 integer ATTRIBUTE Service-Type 6 integer ATTRIBUTE Framed-Protocol 7 integer ATTRIBUTE Framed-IP-Address 8 ipaddr ATTRIBUTE Framed-IP-Netmask 9 ipaddr ATTRIBUTE Framed-Routing 10 integer ATTRIBUTE Filter-Id 11 string ATTRIBUTE Framed-MTU 12 integer ATTRIBUTE Framed-Compression 13 integer ATTRIBUTE Login-IP-Host 14 ipaddr ATTRIBUTE Login-Service 15 integer ATTRIBUTE Login-TCP-Port 16 integer ATTRIBUTE Reply-Message 18 string ATTRIBUTE Callback-Number 19 string ATTRIBUTE Callback-Id 20 string ATTRIBUTE Framed-Route 22 string ATTRIBUTE Framed-IPX-Network 23 ipaddr ATTRIBUTE State 24 string ATTRIBUTE Class 25 string ATTRIBUTE Session-Timeout 27 integer ATTRIBUTE Idle-Timeout 28 integer ATTRIBUTE Termination-Action 29 integer ATTRIBUTE Called-Station-Id 30 string ATTRIBUTE Calling-Station-Id 31 string ATTRIBUTE NAS-Identifier 32 string ATTRIBUTE Acct-Status-Type 40 integer ATTRIBUTE Acct-Delay-Time 41 integer ATTRIBUTE Acct-Input-Octets 42 integer ATTRIBUTE Acct-Output-Octets 43 integer ATTRIBUTE Acct-Session-Id 44 string ATTRIBUTE Acct-Authentic 45 integer ATTRIBUTE Acct-Session-Time 46 integer ATTRIBUTE Acct-Input-Packets 47 integer ATTRIBUTE Acct-Output-Packets 48 integer ATTRIBUTE Acct-Terminate-Cause 49 integer ATTRIBUTE Chap-Challenge 60 string ATTRIBUTE NAS-Port-Type 61 integer ATTRIBUTE Port-Limit 62 integer ATTRIBUTE Connect-Info 77 string # RFC 2869 ATTRIBUTE Acct-Interim-Interval 85 integer # # Experimental Non Protocol Attributes used by Cistron-Radiusd # ATTRIBUTE Huntgroup-Name 221 string ATTRIBUTE User-Category 1029 string ATTRIBUTE Group-Name 1030 string ATTRIBUTE Simultaneous-Use 1034 integer ATTRIBUTE Strip-User-Name 1035 integer ATTRIBUTE Fall-Through 1036 integer ATTRIBUTE Add-Port-To-IP-Address 1037 integer ATTRIBUTE Exec-Program 1038 string ATTRIBUTE Exec-Program-Wait 1039 string ATTRIBUTE Hint 1040 string # # Non-Protocol Attributes # These attributes are used internally by the server # ATTRIBUTE Expiration 21 date ATTRIBUTE Auth-Type 1000 integer ATTRIBUTE Menu 1001 string ATTRIBUTE Termination-Menu 1002 string ATTRIBUTE Prefix 1003 string ATTRIBUTE Suffix 1004 string ATTRIBUTE Group 1005 string ATTRIBUTE Crypt-Password 1006 string ATTRIBUTE Connect-Rate 1007 integer # # Experimental, implementation specific attributes # # Limit session traffic ATTRIBUTE Session-Octets-Limit 227 integer # What to assume as limit - 0 in+out, 1 in, 2 out, 3 max(in,out) ATTRIBUTE Octets-Direction 228 integer # # Integer Translations # # User Types VALUE Service-Type Login-User 1 VALUE Service-Type Framed-User 2 VALUE Service-Type Callback-Login-User 3 VALUE Service-Type Callback-Framed-User 4 VALUE Service-Type Outbound-User 5 VALUE Service-Type Administrative-User 6 VALUE Service-Type NAS-Prompt-User 7 # Framed Protocols VALUE Framed-Protocol PPP 1 VALUE Framed-Protocol SLIP 2 # Framed Routing Values VALUE Framed-Routing None 0 VALUE Framed-Routing Broadcast 1 VALUE Framed-Routing Listen 2 VALUE Framed-Routing Broadcast-Listen 3 # Framed Compression Types VALUE Framed-Compression None 0 VALUE Framed-Compression Van-Jacobson-TCP-IP 1 # Login Services VALUE Login-Service Telnet 0 VALUE Login-Service Rlogin 1 VALUE Login-Service TCP-Clear 2 VALUE Login-Service PortMaster 3 # Status Types VALUE Acct-Status-Type Start 1 VALUE Acct-Status-Type Stop 2 VALUE Acct-Status-Type Accounting-On 7 VALUE Acct-Status-Type Accounting-Off 8 # Authentication Types VALUE Acct-Authentic RADIUS 1 VALUE Acct-Authentic Local 2 VALUE Acct-Authentic PowerLink128 100 # Termination Options VALUE Termination-Action Default 0 VALUE Termination-Action RADIUS-Request 1 # NAS Port Types, available in 3.3.1 and later VALUE NAS-Port-Type Async 0 VALUE NAS-Port-Type Sync 1 VALUE NAS-Port-Type ISDN 2 VALUE NAS-Port-Type ISDN-V120 3 VALUE NAS-Port-Type ISDN-V110 4 # Acct Terminate Causes, available in 3.3.2 and later VALUE Acct-Terminate-Cause User-Request 1 VALUE Acct-Terminate-Cause Lost-Carrier 2 VALUE Acct-Terminate-Cause Lost-Service 3 VALUE Acct-Terminate-Cause Idle-Timeout 4 VALUE Acct-Terminate-Cause Session-Timeout 5 VALUE Acct-Terminate-Cause Admin-Reset 6 VALUE Acct-Terminate-Cause Admin-Reboot 7 VALUE Acct-Terminate-Cause Port-Error 8 VALUE Acct-Terminate-Cause NAS-Error 9 VALUE Acct-Terminate-Cause NAS-Request 10 VALUE Acct-Terminate-Cause NAS-Reboot 11 VALUE Acct-Terminate-Cause Port-Unneeded 12 VALUE Acct-Terminate-Cause Port-Preempted 13 VALUE Acct-Terminate-Cause Port-Suspended 14 VALUE Acct-Terminate-Cause Service-Unavailable 15 VALUE Acct-Terminate-Cause Callback 16 VALUE Acct-Terminate-Cause User-Error 17 VALUE Acct-Terminate-Cause Host-Request 18 # # Non-Protocol Integer Translations # VALUE Auth-Type Local 0 VALUE Auth-Type System 1 VALUE Auth-Type SecurID 2 VALUE Auth-Type Crypt-Local 3 VALUE Auth-Type Reject 4 # # Cistron extensions # VALUE Auth-Type Pam 253 VALUE Auth-Type None 254 # # Experimental Non-Protocol Integer Translations for Cistron-Radiusd # VALUE Fall-Through No 0 VALUE Fall-Through Yes 1 VALUE Add-Port-To-IP-Address No 0 VALUE Add-Port-To-IP-Address Yes 1 # # Configuration Values # uncomment these two lines to turn account expiration on # #VALUE Server-Config Password-Expiration 30 #VALUE Server-Config Password-Warning 5 # Octets-Direction VALUE Octets-Direction Sum 0 VALUE Octets-Direction Input 1 VALUE Octets-Direction Output 2 VALUE Octets-Direction MaxOveral 3 VALUE Octets-Direction MaxSession 4 INCLUDE /etc/radiusclient/dictionary.microsoft ppp-2.4.5/pppd/plugins/radius/etc/dictionary.ascend000066400000000000000000000300071130035057700223340ustar00rootroot00000000000000# # Ascend dictionary. # # # Version: 1.00 21-Jul-1997 Jens Glaser # # # Ascend specific extensions # Used by ASCEND MAX/Pipeline products # ATTRIBUTE Ascend-FCP-Parameter 119 string ATTRIBUTE Ascend-Modem-PortNo 120 integer ATTRIBUTE Ascend-Modem-SlotNo 121 integer ATTRIBUTE Ascend-Modem-ShelfNo 122 integer ATTRIBUTE Ascend-Call-Attempt-Limit 123 integer ATTRIBUTE Ascend-Call-Block-Duration 124 integer ATTRIBUTE Ascend-Maximum-Call-Duration 125 integer ATTRIBUTE Ascend-Temporary-Rtes 126 integer ATTRIBUTE Tunneling-Protocol 127 integer ATTRIBUTE Ascend-Shared-Profile-Enable 128 integer ATTRIBUTE Ascend-Primary-Home-Agent 129 string ATTRIBUTE Ascend-Secondary-Home-Agent 130 string ATTRIBUTE Ascend-Dialout-Allowed 131 integer ATTRIBUTE Ascend-Client-Gateway 132 ipaddr ATTRIBUTE Ascend-BACP-Enable 133 integer ATTRIBUTE Ascend-DHCP-Maximum-Leases 134 integer ATTRIBUTE Ascend-Client-Primary-DNS 135 ipaddr ATTRIBUTE Ascend-Client-Secondary-DNS 136 ipaddr ATTRIBUTE Ascend-Client-Assign-DNS 137 integer ATTRIBUTE Ascend-User-Acct-Type 138 integer ATTRIBUTE Ascend-User-Acct-Host 139 ipaddr ATTRIBUTE Ascend-User-Acct-Port 140 integer ATTRIBUTE Ascend-User-Acct-Key 141 string ATTRIBUTE Ascend-User-Acct-Base 142 integer ATTRIBUTE Ascend-User-Acct-Time 143 integer ATTRIBUTE Ascend-Assign-IP-Client 144 ipaddr ATTRIBUTE Ascend-Assign-IP-Server 145 ipaddr ATTRIBUTE Ascend-Assign-IP-Global-Pool 146 string ATTRIBUTE Ascend-DHCP-Reply 147 integer ATTRIBUTE Ascend-DHCP-Pool-Number 148 integer ATTRIBUTE Ascend-Expect-Callback 149 integer ATTRIBUTE Ascend-Event-Type 150 integer ATTRIBUTE Ascend-Session-Svr-Key 151 string ATTRIBUTE Ascend-Multicast-Rate-Limit 152 integer ATTRIBUTE Ascend-IF-Netmask 153 ipaddr ATTRIBUTE Ascend-Remote-Addr 154 ipaddr ATTRIBUTE Ascend-Multicast-Client 155 integer ATTRIBUTE Ascend-FR-Circuit-Name 156 string ATTRIBUTE Ascend-FR-LinkUp 157 integer ATTRIBUTE Ascend-FR-Nailed-Grp 158 integer ATTRIBUTE Ascend-FR-Type 159 integer ATTRIBUTE Ascend-FR-Link-Mgt 160 integer ATTRIBUTE Ascend-FR-N391 161 integer ATTRIBUTE Ascend-FR-DCE-N392 162 integer ATTRIBUTE Ascend-FR-DTE-N392 163 integer ATTRIBUTE Ascend-FR-DCE-N393 164 integer ATTRIBUTE Ascend-FR-DTE-N393 165 integer ATTRIBUTE Ascend-FR-T391 166 integer ATTRIBUTE Ascend-FR-T392 167 integer ATTRIBUTE Ascend-Bridge-Address 168 string ATTRIBUTE Ascend-TS-Idle-Limit 169 integer ATTRIBUTE Ascend-TS-Idle-Mode 170 integer ATTRIBUTE Ascend-DBA-Monitor 171 integer ATTRIBUTE Ascend-Base-Channel-Count 172 integer ATTRIBUTE Ascend-Minimum-Channels 173 integer ATTRIBUTE Ascend-IPX-Route 174 string ATTRIBUTE Ascend-FT1-Caller 175 integer ATTRIBUTE Ascend-Backup 176 string ATTRIBUTE Ascend-Call-Type 177 integer ATTRIBUTE Ascend-Group 178 string ATTRIBUTE Ascend-FR-DLCI 179 integer ATTRIBUTE Ascend-FR-Profile-Name 180 string ATTRIBUTE Ascend-Ara-PW 181 string ATTRIBUTE Ascend-IPX-Node-Addr 182 string ATTRIBUTE Ascend-Home-Agent-IP-Addr 183 ipaddr ATTRIBUTE Ascend-Home-Agent-Password 184 string ATTRIBUTE Ascend-Home-Network-Name 185 string ATTRIBUTE Ascend-Home-Agent-UDP-Port 186 integer ATTRIBUTE Ascend-Multilink-ID 187 integer ATTRIBUTE Ascend-Num-In-Multilink 188 integer ATTRIBUTE Ascend-First-Dest 189 ipaddr ATTRIBUTE Ascend-Pre-Input-Octets 190 integer ATTRIBUTE Ascend-Pre-Output-Octets 191 integer ATTRIBUTE Ascend-Pre-Input-Packets 192 integer ATTRIBUTE Ascend-Pre-Output-Packets 193 integer ATTRIBUTE Ascend-Maximum-Time 194 integer ATTRIBUTE Ascend-Disconnect-Cause 195 integer ATTRIBUTE Ascend-Connect-Progress 196 integer ATTRIBUTE Ascend-Data-Rate 197 integer ATTRIBUTE Ascend-PreSession-Time 198 integer ATTRIBUTE Ascend-Token-Idle 199 integer ATTRIBUTE Ascend-Token-Immediate 200 integer ATTRIBUTE Ascend-Require-Auth 201 integer ATTRIBUTE Ascend-Number-Sessions 202 string ATTRIBUTE Ascend-Authen-Alias 203 string ATTRIBUTE Ascend-Token-Expiry 204 integer ATTRIBUTE Ascend-Menu-Selector 205 string ATTRIBUTE Ascend-Menu-Item 206 string ATTRIBUTE Ascend-PW-Warntime 207 integer ATTRIBUTE Ascend-PW-Lifetime 208 integer ATTRIBUTE Ascend-IP-Direct 209 ipaddr ATTRIBUTE Ascend-PPP-VJ-Slot-Comp 210 integer ATTRIBUTE Ascend-PPP-VJ-1172 211 integer ATTRIBUTE Ascend-PPP-Async-Map 212 integer ATTRIBUTE Ascend-Third-Prompt 213 string ATTRIBUTE Ascend-Send-Secret 214 string ATTRIBUTE Ascend-Receive-Secret 215 string ATTRIBUTE Ascend-IPX-Peer-Mode 216 integer ATTRIBUTE Ascend-IP-Pool-Definition 217 string ATTRIBUTE Ascend-Assign-IP-Pool 218 integer ATTRIBUTE Ascend-FR-Direct 219 integer ATTRIBUTE Ascend-FR-Direct-Profile 220 string ATTRIBUTE Ascend-FR-Direct-DLCI 221 integer ATTRIBUTE Ascend-Handle-IPX 222 integer ATTRIBUTE Ascend-Netware-timeout 223 integer ATTRIBUTE Ascend-IPX-Alias 224 integer ATTRIBUTE Ascend-Metric 225 integer ATTRIBUTE Ascend-PRI-Number-Type 226 integer ATTRIBUTE Ascend-Dial-Number 227 string ATTRIBUTE Ascend-Route-IP 228 integer ATTRIBUTE Ascend-Route-IPX 229 integer ATTRIBUTE Ascend-Bridge 230 integer ATTRIBUTE Ascend-Send-Auth 231 integer ATTRIBUTE Ascend-Send-Passwd 232 string ATTRIBUTE Ascend-Link-Compression 233 integer ATTRIBUTE Ascend-Target-Util 234 integer ATTRIBUTE Ascend-Maximum-Channels 235 integer ATTRIBUTE Ascend-Inc-Channel-Count 236 integer ATTRIBUTE Ascend-Dec-Channel-Count 237 integer ATTRIBUTE Ascend-Seconds-Of-History 238 integer ATTRIBUTE Ascend-History-Weigh-Type 239 integer ATTRIBUTE Ascend-Add-Seconds 240 integer ATTRIBUTE Ascend-Remove-Seconds 241 integer ATTRIBUTE Ascend-Idle-Limit 244 integer ATTRIBUTE Ascend-Preempt-Limit 245 integer ATTRIBUTE Ascend-Callback 246 integer ATTRIBUTE Ascend-Data-Svc 247 integer ATTRIBUTE Ascend-Force-56 248 integer ATTRIBUTE Ascend-Billing-Number 249 string ATTRIBUTE Ascend-Call-By-Call 250 integer ATTRIBUTE Ascend-Transit-Number 251 string ATTRIBUTE Ascend-Host-Info 252 string ATTRIBUTE Ascend-PPP-Address 253 ipaddr ATTRIBUTE Ascend-MPP-Idle-Percent 254 integer ATTRIBUTE Ascend-Xmit-Rate 255 integer # Ascend protocols VALUE Service-Type Dialout-Framed-User 5 VALUE Framed-Protocol ARA 255 VALUE Framed-Protocol MPP 256 VALUE Framed-Protocol EURAW 257 VALUE Framed-Protocol EUUI 258 VALUE Framed-Protocol X25 259 VALUE Framed-Protocol COMB 260 VALUE Framed-Protocol FR 261 VALUE Framed-Protocol MP 262 VALUE Framed-Protocol FR-CIR 263 # # Ascend specific extensions # Used by ASCEND MAX/Pipeline products (see above) # VALUE Ascend-FR-Direct FR-Direct-No 0 VALUE Ascend-FR-Direct FR-Direct-Yes 1 VALUE Ascend-Handle-IPX Handle-IPX-None 0 VALUE Ascend-Handle-IPX Handle-IPX-Client 1 VALUE Ascend-Handle-IPX Handle-IPX-Server 2 VALUE Ascend-IPX-Peer-Mode IPX-Peer-Router 0 VALUE Ascend-IPX-Peer-Mode IPX-Peer-Dialin 1 VALUE Ascend-Call-Type Nailed 1 VALUE Ascend-Call-Type Nailed/Mpp 2 VALUE Ascend-Call-Type Perm/Switched 3 VALUE Ascend-FT1-Caller FT1-No 0 VALUE Ascend-FT1-Caller FT1-Yes 1 VALUE Ascend-PRI-Number-Type Unknown-Number 0 VALUE Ascend-PRI-Number-Type Intl-Number 1 VALUE Ascend-PRI-Number-Type National-Number 2 VALUE Ascend-PRI-Number-Type Local-Number 4 VALUE Ascend-PRI-Number-Type Abbrev-Number 5 VALUE Ascend-Route-IPX Route-IPX-No 0 VALUE Ascend-Route-IPX Route-IPX-Yes 1 VALUE Ascend-Bridge Bridge-No 0 VALUE Ascend-Bridge Bridge-Yes 1 VALUE Ascend-TS-Idle-Mode TS-Idle-None 0 VALUE Ascend-TS-Idle-Mode TS-Idle-Input 1 VALUE Ascend-TS-Idle-Mode TS-Idle-Input-Output 2 VALUE Ascend-Send-Auth Send-Auth-None 0 VALUE Ascend-Send-Auth Send-Auth-PAP 1 VALUE Ascend-Send-Auth Send-Auth-CHAP 2 VALUE Ascend-Send-Auth Send-Auth-MS-CHAP 3 VALUE Ascend-Link-Compression Link-Comp-None 0 VALUE Ascend-Link-Compression Link-Comp-Stac 1 VALUE Ascend-Link-Compression Link-Comp-Stac-Draft-9 2 VALUE Ascend-Link-Compression Link-Comp-MS-Stac 3 VALUE Ascend-History-Weigh-Type History-Constant 0 VALUE Ascend-History-Weigh-Type History-Linear 1 VALUE Ascend-History-Weigh-Type History-Quadratic 2 VALUE Ascend-Callback Callback-No 0 VALUE Ascend-Callback Callback-Yes 1 VALUE Ascend-Expect-Callback Expect-Callback-No 0 VALUE Ascend-Expect-Callback Expect-Callback-Yes 1 VALUE Ascend-Data-Svc Switched-Voice-Bearer 0 VALUE Ascend-Data-Svc Switched-56KR 1 VALUE Ascend-Data-Svc Switched-64K 2 VALUE Ascend-Data-Svc Switched-64KR 3 VALUE Ascend-Data-Svc Switched-56K 4 VALUE Ascend-Data-Svc Switched-384KR 5 VALUE Ascend-Data-Svc Switched-384K 6 VALUE Ascend-Data-Svc Switched-1536K 7 VALUE Ascend-Data-Svc Switched-1536KR 8 VALUE Ascend-Data-Svc Switched-128K 9 VALUE Ascend-Data-Svc Switched-192K 10 VALUE Ascend-Data-Svc Switched-256K 11 VALUE Ascend-Data-Svc Switched-320K 12 VALUE Ascend-Data-Svc Switched-384K-MR 13 VALUE Ascend-Data-Svc Switched-448K 14 VALUE Ascend-Data-Svc Switched-512K 15 VALUE Ascend-Data-Svc Switched-576K 16 VALUE Ascend-Data-Svc Switched-640K 17 VALUE Ascend-Data-Svc Switched-704K 18 VALUE Ascend-Data-Svc Switched-768K 19 VALUE Ascend-Data-Svc Switched-832K 20 VALUE Ascend-Data-Svc Switched-896K 21 VALUE Ascend-Data-Svc Switched-960K 22 VALUE Ascend-Data-Svc Switched-1024K 23 VALUE Ascend-Data-Svc Switched-1088K 24 VALUE Ascend-Data-Svc Switched-1152K 25 VALUE Ascend-Data-Svc Switched-1216K 26 VALUE Ascend-Data-Svc Switched-1280K 27 VALUE Ascend-Data-Svc Switched-1344K 28 VALUE Ascend-Data-Svc Switched-1408K 29 VALUE Ascend-Data-Svc Switched-1472K 30 VALUE Ascend-Data-Svc Switched-1600K 31 VALUE Ascend-Data-Svc Switched-1664K 32 VALUE Ascend-Data-Svc Switched-1728K 33 VALUE Ascend-Data-Svc Switched-1792K 34 VALUE Ascend-Data-Svc Switched-1856K 35 VALUE Ascend-Data-Svc Switched-1920K 36 VALUE Ascend-Data-Svc Switched-inherited 37 VALUE Ascend-Data-Svc Switched-restricted-bearer-x30 38 VALUE Ascend-Data-Svc Switched-clear-bearer-v110 39 VALUE Ascend-Data-Svc Switched-restricted-64-x30 40 VALUE Ascend-Data-Svc Switched-clear-56-v110 41 VALUE Ascend-Data-Svc Switched-modem 42 VALUE Ascend-Data-Svc Switched-atmodem 43 VALUE Ascend-Data-Svc Nailed-56KR 1 VALUE Ascend-Data-Svc Nailed-64K 2 VALUE Ascend-Force-56 Force-56-No 0 VALUE Ascend-Force-56 Force-56-Yes 1 VALUE Ascend-PW-Lifetime Lifetime-In-Days 0 VALUE Ascend-PW-Warntime Days-Of-Warning 0 VALUE Ascend-PPP-VJ-1172 PPP-VJ-1172 1 VALUE Ascend-PPP-VJ-Slot-Comp VJ-Slot-Comp-No 1 VALUE Ascend-Require-Auth Not-Require-Auth 0 VALUE Ascend-Require-Auth Require-Auth 1 VALUE Ascend-Token-Immediate Tok-Imm-No 0 VALUE Ascend-Token-Immediate Tok-Imm-Yes 1 VALUE Ascend-DBA-Monitor DBA-Transmit 0 VALUE Ascend-DBA-Monitor DBA-Transmit-Recv 1 VALUE Ascend-DBA-Monitor DBA-None 2 VALUE Ascend-FR-Type Ascend-FR-DTE 0 VALUE Ascend-FR-Type Ascend-FR-DCE 1 VALUE Ascend-FR-Type Ascend-FR-NNI 2 VALUE Ascend-FR-Link-Mgt Ascend-FR-No-Link-Mgt 0 VALUE Ascend-FR-Link-Mgt Ascend-FR-T1-617D 1 VALUE Ascend-FR-Link-Mgt Ascend-FR-Q-933A 2 VALUE Ascend-FR-LinkUp Ascend-LinkUp-Default 0 VALUE Ascend-FR-LinkUp Ascend-LinkUp-AlwaysUp 1 VALUE Ascend-Multicast-Client Multicast-No 0 VALUE Ascend-Multicast-Client Multicast-Yes 1 VALUE Ascend-User-Acct-Type Ascend-User-Acct-None 0 VALUE Ascend-User-Acct-Type Ascend-User-Acct-User 1 VALUE Ascend-User-Acct-Type Ascend-User-Acct-User-Default 2 VALUE Ascend-User-Acct-Base Base-10 0 VALUE Ascend-User-Acct-Base Base-16 1 VALUE Ascend-DHCP-Reply DHCP-Reply-No 0 VALUE Ascend-DHCP-Reply DHCP-Reply-Yes 1 VALUE Ascend-Client-Assign-DNS DNS-Assign-No 0 VALUE Ascend-Client-Assign-DNS DNS-Assign-Yes 1 VALUE Ascend-Event-Type Ascend-ColdStart 1 VALUE Ascend-Event-Type Ascend-Session-Event 2 VALUE Ascend-BACP-Enable BACP-No 0 VALUE Ascend-BACP-Enable BACP-Yes 1 VALUE Ascend-Dialout-Allowed Dialout-Not-Allowed 0 VALUE Ascend-Dialout-Allowed Dialout-Allowed 1 VALUE Ascend-Shared-Profile-Enable Shared-Profile-No 0 VALUE Ascend-Shared-Profile-Enable Shared-Profile-Yes 1 VALUE Ascend-Temporary-Rtes Temp-Rtes-No 0 VALUE Ascend-Temporary-Rtes Temp-Rtes-Yes 1 ppp-2.4.5/pppd/plugins/radius/etc/dictionary.compat000066400000000000000000000025631130035057700223700ustar00rootroot00000000000000# # Obsolete names for backwards compatibility with older users files. # ATTRIBUTE Client-Id 4 ipaddr ATTRIBUTE Client-Port-Id 5 integer ATTRIBUTE User-Service-Type 6 integer ATTRIBUTE Framed-Address 8 ipaddr ATTRIBUTE Framed-Netmask 9 ipaddr ATTRIBUTE Framed-Filter-Id 11 string ATTRIBUTE Login-Host 14 ipaddr ATTRIBUTE Login-Port 16 integer ATTRIBUTE Old-Password 17 string ATTRIBUTE Port-Message 18 string ATTRIBUTE Dialback-No 19 string ATTRIBUTE Dialback-Name 20 string ATTRIBUTE Challenge-State 24 string VALUE Framed-Compression Van-Jacobsen-TCP-IP 1 VALUE Framed-Compression VJ-TCP-IP 1 VALUE Service-Type Shell-User 6 VALUE Auth-Type Unix 1 VALUE Service-Type Dialback-Login-User 3 VALUE Service-Type Dialback-Framed-User 4 # # For compatibility with MERIT users files. # ATTRIBUTE NAS-Port 5 integer ATTRIBUTE Login-Host 14 ipaddr ATTRIBUTE Login-Callback-Number 19 string ATTRIBUTE Framed-Callback-Id 20 string ATTRIBUTE Client-Port-DNIS 30 string ATTRIBUTE Caller-ID 31 string VALUE Service-Type Login 1 VALUE Service-Type Framed 2 VALUE Service-Type Callback-Login 3 VALUE Service-Type Callback-Framed 4 VALUE Service-Type Exec-User 7 # # For compatibility with ESVA RADIUS, Old Cistron RADIUS # ATTRIBUTE Session 1034 integer ATTRIBUTE User-Name-Is-Star 1035 integer VALUE User-Name-Is-Star No 0 VALUE User-Name-Is-Star Yes 1 ppp-2.4.5/pppd/plugins/radius/etc/dictionary.merit000066400000000000000000000011271130035057700222200ustar00rootroot00000000000000# # Experimental extensions, configuration only (for check-items) # Names/numbers as per the MERIT extensions (if possible). # ATTRIBUTE NAS-Identifier 32 string ATTRIBUTE Proxy-State 33 string ATTRIBUTE Login-LAT-Service 34 string ATTRIBUTE Login-LAT-Node 35 string ATTRIBUTE Login-LAT-Group 36 string ATTRIBUTE Framed-AppleTalk-Link 37 integer ATTRIBUTE Framed-AppleTalk-Network 38 integer ATTRIBUTE Framed-AppleTalk-Zone 39 string ATTRIBUTE Acct-Input-Packets 47 integer ATTRIBUTE Acct-Output-Packets 48 integer # 8 is a MERIT extension. VALUE Service-Type Authenticate-Only 8 ppp-2.4.5/pppd/plugins/radius/etc/dictionary.microsoft000066400000000000000000000051311130035057700231040ustar00rootroot00000000000000# # Microsoft's VSA's, from RFC 2548 # # $Id: dictionary.microsoft,v 1.1 2004/11/14 07:26:26 paulus Exp $ # VENDOR Microsoft 311 Microsoft ATTRIBUTE MS-CHAP-Response 1 string Microsoft ATTRIBUTE MS-CHAP-Error 2 string Microsoft ATTRIBUTE MS-CHAP-CPW-1 3 string Microsoft ATTRIBUTE MS-CHAP-CPW-2 4 string Microsoft ATTRIBUTE MS-CHAP-LM-Enc-PW 5 string Microsoft ATTRIBUTE MS-CHAP-NT-Enc-PW 6 string Microsoft ATTRIBUTE MS-MPPE-Encryption-Policy 7 string Microsoft # This is referred to as both singular and plural in the RFC. # Plural seems to make more sense. ATTRIBUTE MS-MPPE-Encryption-Type 8 string Microsoft ATTRIBUTE MS-MPPE-Encryption-Types 8 string Microsoft ATTRIBUTE MS-RAS-Vendor 9 integer Microsoft ATTRIBUTE MS-CHAP-Domain 10 string Microsoft ATTRIBUTE MS-CHAP-Challenge 11 string Microsoft ATTRIBUTE MS-CHAP-MPPE-Keys 12 string Microsoft ATTRIBUTE MS-BAP-Usage 13 integer Microsoft ATTRIBUTE MS-Link-Utilization-Threshold 14 integer Microsoft ATTRIBUTE MS-Link-Drop-Time-Limit 15 integer Microsoft ATTRIBUTE MS-MPPE-Send-Key 16 string Microsoft ATTRIBUTE MS-MPPE-Recv-Key 17 string Microsoft ATTRIBUTE MS-RAS-Version 18 string Microsoft ATTRIBUTE MS-Old-ARAP-Password 19 string Microsoft ATTRIBUTE MS-New-ARAP-Password 20 string Microsoft ATTRIBUTE MS-ARAP-PW-Change-Reason 21 integer Microsoft ATTRIBUTE MS-Filter 22 string Microsoft ATTRIBUTE MS-Acct-Auth-Type 23 integer Microsoft ATTRIBUTE MS-Acct-EAP-Type 24 integer Microsoft ATTRIBUTE MS-CHAP2-Response 25 string Microsoft ATTRIBUTE MS-CHAP2-Success 26 string Microsoft ATTRIBUTE MS-CHAP2-CPW 27 string Microsoft ATTRIBUTE MS-Primary-DNS-Server 28 ipaddr Microsoft ATTRIBUTE MS-Secondary-DNS-Server 29 ipaddr Microsoft ATTRIBUTE MS-Primary-NBNS-Server 30 ipaddr Microsoft ATTRIBUTE MS-Secondary-NBNS-Server 31 ipaddr Microsoft #ATTRIBUTE MS-ARAP-Challenge 33 string Microsoft # # Integer Translations # # MS-BAP-Usage Values VALUE MS-BAP-Usage Not-Allowed 0 VALUE MS-BAP-Usage Allowed 1 VALUE MS-BAP-Usage Required 2 # MS-ARAP-Password-Change-Reason Values VALUE MS-ARAP-PW-Change-Reason Just-Change-Password 1 VALUE MS-ARAP-PW-Change-Reason Expired-Password 2 VALUE MS-ARAP-PW-Change-Reason Admin-Requires-Password-Change 3 VALUE MS-ARAP-PW-Change-Reason Password-Too-Short 4 # MS-Acct-Auth-Type Values VALUE MS-Acct-Auth-Type PAP 1 VALUE MS-Acct-Auth-Type CHAP 2 VALUE MS-Acct-Auth-Type MS-CHAP-1 3 VALUE MS-Acct-Auth-Type MS-CHAP-2 4 VALUE MS-Acct-Auth-Type EAP 5 # MS-Acct-EAP-Type Values VALUE MS-Acct-EAP-Type MD5 4 VALUE MS-Acct-EAP-Type OTP 5 VALUE MS-Acct-EAP-Type Generic-Token-Card 6 VALUE MS-Acct-EAP-Type TLS 13 ppp-2.4.5/pppd/plugins/radius/etc/issue000066400000000000000000000002071130035057700200620ustar00rootroot00000000000000(\I) ----------------------------------------------------- \S \R (\N) (port \L) ----------------------------------------------------- ppp-2.4.5/pppd/plugins/radius/etc/port-id-map000066400000000000000000000006321130035057700210650ustar00rootroot00000000000000# # port-id-map # # This file describes the ttyname to port id mapping. The port id # is reported as part of a RADIUS authentication or accouting request. # #ttyname (as returned by ttyname(3)) port-id /dev/tty1 1 /dev/tty2 2 /dev/tty3 3 /dev/tty4 4 /dev/tty5 5 /dev/tty6 6 /dev/tty7 7 /dev/tty8 8 /dev/ttyS0 9 /dev/ttyS1 10 /dev/ttyS2 11 /dev/ttyS3 12 /dev/ttyS4 13 /dev/ttyS5 14 /dev/ttyS6 15 /dev/ttyS7 16 ppp-2.4.5/pppd/plugins/radius/etc/radiusclient.conf000066400000000000000000000061051130035057700223470ustar00rootroot00000000000000# General settings # specify which authentication comes first respectively which # authentication is used. possible values are: "radius" and "local". # if you specify "radius,local" then the RADIUS server is asked # first then the local one. if only one keyword is specified only # this server is asked. auth_order radius # maximum login tries a user has (default 4) login_tries 4 # timeout for all login tries (default 60) # if this time is exceeded the user is kicked out login_timeout 60 # name of the nologin file which when it exists disables logins. # it may be extended by the ttyname which will result in # a terminal specific lock (e.g. /etc/nologin.ttyS2 will disable # logins on /dev/ttyS2) (default /etc/nologin) nologin /etc/nologin # name of the issue file. it's only display when no username is passed # on the radlogin command line (default /etc/radiusclient/issue) issue /usr/local/etc/radiusclient/issue # RADIUS settings # RADIUS server to use for authentication requests. this config # item can appear more then one time. if multiple servers are # defined they are tried in a round robin fashion if one # server is not answering. # optionally you can specify a the port number on which is remote # RADIUS listens separated by a colon from the hostname. if # no port is specified /etc/services is consulted of the radius # service. if this fails also a compiled in default is used. authserver localhost:1812 # RADIUS server to use for accouting requests. All that I # said for authserver applies, too. # acctserver localhost:1813 # file holding shared secrets used for the communication # between the RADIUS client and server servers /usr/local/etc/radiusclient/servers # dictionary of allowed attributes and values # just like in the normal RADIUS distributions dictionary /usr/local/etc/radiusclient/dictionary # program to call for a RADIUS authenticated login # (default /usr/sbin/login.radius) login_radius /usr/local/sbin/login.radius # file which holds sequence number for communication with the # RADIUS server seqfile /var/run/radius.seq # file which specifies mapping between ttyname and NAS-Port attribute mapfile /usr/local/etc/radiusclient/port-id-map # default authentication realm to append to all usernames if no # realm was explicitly specified by the user # the radiusd directly form Livingston doesnt use any realms, so leave # it blank then default_realm # time to wait for a reply from the RADIUS server radius_timeout 10 # resend request this many times before trying the next server radius_retries 3 # NAS-Identifier # # If supplied, this option will cause the client to send the given string # as the contents of the NAS-Identifier attribute in RADIUS requests. No # NAS-IP-Address attribute will be sent in this case. # # The default behavior is to send a NAS-IP-Address option and not send # a NAS-Identifier. The value of the NAS-IP-Address option is chosen # by resolving the system hostname. # nas_identifier MyUniqueNASName # LOCAL settings # program to execute for local login # it must support the -f flag for preauthenticated login login_local /bin/login ppp-2.4.5/pppd/plugins/radius/etc/radiusclient.conf.in000066400000000000000000000060171130035057700227560ustar00rootroot00000000000000# General settings # specify which authentication comes first respectively which # authentication is used. possible values are: "radius" and "local". # if you specify "radius,local" then the RADIUS server is asked # first then the local one. if only one keyword is specified only # this server is asked. auth_order radius # maximum login tries a user has (default 4) login_tries 4 # timeout for all login tries (default 60) # if this time is exceeded the user is kicked out login_timeout 60 # name of the nologin file which when it exists disables logins. # it may be extended by the ttyname which will result in # a terminal specific lock (e.g. /etc/nologin.ttyS2 will disable # logins on /dev/ttyS2) (default /etc/nologin) nologin /etc/nologin # name of the issue file. it's only display when no username is passed # on the radlogin command line (default /etc/radiusclient/issue) issue @pkgsysconfdir@/issue # RADIUS settings # RADIUS server to use for authentication requests. this config # item can appear more then one time. if multiple servers are # defined they are tried in a round robin fashion if one # server is not answering. # optionally you can specify a the port number on which is remote # RADIUS listens separated by a colon from the hostname. if # no port is specified /etc/services is consulted of the radius # service. if this fails also a compiled in default is used. authserver localhost:1812 # RADIUS server to use for accouting requests. All that I # said for authserver applies, too. # acctserver localhost:1813 # file holding shared secrets used for the communication # between the RADIUS client and server servers @pkgsysconfdir@/servers # dictionary of allowed attributes and values # just like in the normal RADIUS distributions dictionary @pkgsysconfdir@/dictionary # program to call for a RADIUS authenticated login # (default /usr/sbin/login.radius) login_radius @sbindir@/login.radius # file which holds sequence number for communication with the # RADIUS server seqfile /var/run/radius.seq # file which specifies mapping between ttyname and NAS-Port attribute mapfile @pkgsysconfdir@/port-id-map # default authentication realm to append to all usernames if no # realm was explicitly specified by the user # the radiusd directly form Livingston doesnt use any realms, so leave # it blank then default_realm # time to wait for a reply from the RADIUS server radius_timeout 10 # resend request this many times before trying the next server radius_retries 3 # NAS-Identifier # # If supplied, this option will cause the client to send the given string # as the contents of the NAS-Identifier attribute in RADIUS requests. No # NAS-IP-Address attribute will be sent in this case. # # The default behavior is to send a NAS-IP-Address option and not send # a NAS-Identifier. The value of the NAS-IP-Address option is chosen # by resolving the system hostname. # nas_identifier MyUniqueNASName # LOCAL settings # program to execute for local login # it must support the -f flag for preauthenticated login login_local /bin/login ppp-2.4.5/pppd/plugins/radius/etc/realms000066400000000000000000000013551130035057700202220ustar00rootroot00000000000000# /etc/radiusclient/realms # # Handle realm @netservers.co.uk on an internal RADIUS server # (note the server must be told to strip the realm) #authserver netservers.co.uk 192.168.1.1:1812 #acctserver netservers.co.uk 192.168.1.1:1813 # users in realm @example.com are handled by separate servers #authserver example.com 10.0.0.1:1812 #acctserver example.com 10.0.0.2:1813 # the DEFAULT realm matches users that do not supply a realm #authserver DEFAULT 192.168.1.1:1812 #acctserver DEFAULT 192.168.1.1:1813 # Any realms that do not match in the realms file automatically fall # through to the standard radius plugin which uses the servers in the # radiusclient.conf file. Note that this is different than the # DEFAULT realm match, above. ppp-2.4.5/pppd/plugins/radius/etc/servers000066400000000000000000000002471130035057700204270ustar00rootroot00000000000000#Server Name or Client/Server pair Key #---------------- --------------- #portmaster.elemental.net hardlyasecret #portmaster2.elemental.net donttellanyone ppp-2.4.5/pppd/plugins/radius/includes.h000066400000000000000000000017611130035057700202210ustar00rootroot00000000000000/* * $Id: includes.h,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1997 Lars Fenneberg * * Copyright 1992 Livingston Enterprises, Inc. * * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan * and Merit Network, Inc. All Rights Reserved * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX 1024 #endif #ifndef UCHAR_MAX # define UCHAR_MAX 255 #endif #include #include #include #include #include #include "magic.h" /* rlib/lock.c */ int do_lock_exclusive(int); int do_unlock(int); ppp-2.4.5/pppd/plugins/radius/ip_util.c000066400000000000000000000047751130035057700200630ustar00rootroot00000000000000/* * $Id: ip_util.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1995,1996,1997 Lars Fenneberg * * Copyright 1992 Livingston Enterprises, Inc. * * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan * and Merit Network, Inc. All Rights Reserved * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include #include /* * Function: rc_get_ipaddr * * Purpose: return an IP address in host long notation from a host * name or address in dot notation. * * Returns: 0 on failure */ UINT4 rc_get_ipaddr (char *host) { struct hostent *hp; if (rc_good_ipaddr (host) == 0) { return ntohl(inet_addr (host)); } else if ((hp = gethostbyname (host)) == (struct hostent *) NULL) { error("rc_get_ipaddr: couldn't resolve hostname: %s", host); return ((UINT4) 0); } return ntohl((*(UINT4 *) hp->h_addr)); } /* * Function: rc_good_ipaddr * * Purpose: check for valid IP address in standard dot notation. * * Returns: 0 on success, -1 when failure * */ int rc_good_ipaddr (char *addr) { int dot_count; int digit_count; if (addr == NULL) return (-1); dot_count = 0; digit_count = 0; while (*addr != '\0' && *addr != ' ') { if (*addr == '.') { dot_count++; digit_count = 0; } else if (!isdigit (*addr)) { dot_count = 5; } else { digit_count++; if (digit_count > 3) { dot_count = 5; } } addr++; } if (dot_count != 3) { return (-1); } else { return (0); } } /* * Function: rc_ip_hostname * * Purpose: Return a printable host name (or IP address in dot notation) * for the supplied IP address. * */ const char *rc_ip_hostname (UINT4 h_ipaddr) { struct hostent *hp; UINT4 n_ipaddr = htonl (h_ipaddr); if ((hp = gethostbyaddr ((char *) &n_ipaddr, sizeof (struct in_addr), AF_INET)) == NULL) { error("rc_ip_hostname: couldn't look up host by addr: %08lX", h_ipaddr); } return ((hp==NULL)?"unknown":hp->h_name); } /* * Function: rc_own_ipaddress * * Purpose: get the IP address of this host in host order * * Returns: IP address on success, 0 on failure * */ UINT4 rc_own_ipaddress(void) { static UINT4 this_host_ipaddr = 0; if (!this_host_ipaddr) { if ((this_host_ipaddr = rc_get_ipaddr (hostname)) == 0) { error("rc_own_ipaddress: couldn't get own IP address"); return 0; } } return this_host_ipaddr; } ppp-2.4.5/pppd/plugins/radius/lock.c000066400000000000000000000015221130035057700173310ustar00rootroot00000000000000/* * $Id: lock.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1997 Lars Fenneberg * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include "includes.h" #include #include int do_lock_exclusive(int fd) { struct flock fl; int res; memset((void *)&fl, 0, sizeof(fl)); fl.l_type = F_WRLCK; fl.l_whence = fl.l_start = 0; fl.l_len = 0; /* 0 means "to end of file" */ res = fcntl(fd, F_SETLK, &fl); if ((res == -1) && (errno == EAGAIN)) errno = EWOULDBLOCK; return res; } int do_unlock(int fd) { struct flock fl; memset((void *)&fl, 0, sizeof(fl)); fl.l_type = F_UNLCK; fl.l_whence = fl.l_start = 0; fl.l_len = 0; /* 0 means "to end of file" */ return fcntl(fd, F_SETLK, &fl); } ppp-2.4.5/pppd/plugins/radius/md5.c000066400000000000000000000004331130035057700170660ustar00rootroot00000000000000/* * $Id: md5.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ */ #include "md5.h" void rc_md5_calc (unsigned char *output, unsigned char *input, unsigned int inlen) { MD5_CTX context; MD5_Init (&context); MD5_Update (&context, input, inlen); MD5_Final (output, &context); } ppp-2.4.5/pppd/plugins/radius/options.h000066400000000000000000000036671130035057700201150ustar00rootroot00000000000000/* * $Id: options.h,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1996 Lars Fenneberg * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #define OPTION_LEN 64 /* ids for different option types */ #define OT_STR (1<<0) /* string */ #define OT_INT (1<<1) /* integer */ #define OT_SRV (1<<2) /* server list */ #define OT_AUO (1<<3) /* authentication order */ #define OT_ANY ((unsigned int)~0) /* used internally */ /* status types */ #define ST_UNDEF (1<<0) /* option is undefined */ typedef struct _option { char name[OPTION_LEN]; /* name of the option */ int type, status; /* type and status */ void *val; /* pointer to option value */ } OPTION; static SERVER acctserver = {0}; static SERVER authserver = {0}; int default_tries = 4; int default_timeout = 60; static OPTION config_options[] = { /* internally used options */ {"config_file", OT_STR, ST_UNDEF, NULL}, /* General options */ {"auth_order", OT_AUO, ST_UNDEF, NULL}, {"login_tries", OT_INT, ST_UNDEF, &default_tries}, {"login_timeout", OT_INT, ST_UNDEF, &default_timeout}, {"nologin", OT_STR, ST_UNDEF, "/etc/nologin"}, {"issue", OT_STR, ST_UNDEF, "/etc/radiusclient/issue"}, /* RADIUS specific options */ {"authserver", OT_SRV, ST_UNDEF, &authserver}, {"acctserver", OT_SRV, ST_UNDEF, &acctserver}, {"servers", OT_STR, ST_UNDEF, NULL}, {"dictionary", OT_STR, ST_UNDEF, NULL}, {"login_radius", OT_STR, ST_UNDEF, "/usr/sbin/login.radius"}, {"seqfile", OT_STR, ST_UNDEF, NULL}, {"mapfile", OT_STR, ST_UNDEF, NULL}, {"default_realm", OT_STR, ST_UNDEF, NULL}, {"radius_timeout", OT_INT, ST_UNDEF, NULL}, {"radius_retries", OT_INT, ST_UNDEF, NULL}, {"nas_identifier", OT_STR, ST_UNDEF, ""}, /* local options */ {"login_local", OT_STR, ST_UNDEF, NULL}, }; static int num_options = ((sizeof(config_options))/(sizeof(config_options[0]))); ppp-2.4.5/pppd/plugins/radius/pathnames.h000066400000000000000000000013711130035057700203700ustar00rootroot00000000000000/* * $Id: pathnames.h,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1995,1996 Lars Fenneberg * * Copyright 1992 Livingston Enterprises, Inc. * * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan * and Merit Network, Inc. All Rights Reserved * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #ifndef PATHNAMES_H #define PATHNAMES_H #define _PATH_DEV_URANDOM "/dev/urandom" /* Linux only */ #define _PATH_ETC_ISSUE "/etc/issue" /* normally defined in the Makefile */ #ifndef _PATH_ETC_RADIUSCLIENT_CONF #define _PATH_ETC_RADIUSCLIENT_CONF "/etc/radiusclient.conf" #endif #endif /* PATHNAMES_H */ ppp-2.4.5/pppd/plugins/radius/pppd-radattr.8000066400000000000000000000021511130035057700207270ustar00rootroot00000000000000.\" manual page [] for RADATTR plugin for pppd 2.4 .\" $Id: pppd-radattr.8,v 1.2 2003/04/25 07:33:20 fcusack Exp $ .\" SH section heading .\" SS subsection heading .\" LP paragraph .\" IP indented paragraph .\" TP hanging label .TH PPPD-RADATTR 8 .SH NAME radattr.so \- RADIUS utility plugin for .BR pppd (8) .SH SYNOPSIS .B pppd [ .I options ] plugin radius.so plugin radattr.so .SH DESCRIPTION .LP The radattr plugin for pppd causes all radius attributes returned by the RADIUS server at authentication time to be stored in the file .I /var/run/radattr.pppN where .I pppN is the name of the PPP interface. The RADIUS attributes are stored one per line in the format "Attribute-Name Attribute-Value". This format is convenient for use in /etc/ppp/ip-up and /etc/ppp/ip-down scripts. .LP Note that you .I must load the radius.so plugin before loading the radattr.so plugin; radattr.so depends on symbols defined in radius.so. .SH USAGE To use the plugin, simply supply the .B plugin radius.so plugin radattr.so options to pppd. .SH SEE ALSO .BR pppd (8) " pppd-radius" (8) .SH AUTHOR David F. Skoll ppp-2.4.5/pppd/plugins/radius/pppd-radius.8000066400000000000000000000035301130035057700205570ustar00rootroot00000000000000.\" manual page [] for RADIUS plugin for pppd 2.4 .\" $Id: pppd-radius.8,v 1.5 2004/03/26 13:27:17 kad Exp $ .\" SH section heading .\" SS subsection heading .\" LP paragraph .\" IP indented paragraph .\" TP hanging label .TH PPPD-RADIUS 8 .SH NAME radius.so \- RADIUS authentication plugin for .BR pppd (8) .SH SYNOPSIS .B pppd [ .I options ] plugin radius.so .SH DESCRIPTION .LP The RADIUS plugin for pppd permits pppd to perform PAP, CHAP, MS-CHAP and MS-CHAPv2 authentication against a RADIUS server instead of the usual .I /etc/ppp/pap-secrets and .I /etc/ppp/chap-secrets files. .LP The RADIUS plugin is built on a library called .B radiusclient which has its own configuration files (usually in \fI/etc/radiusclient\fR), consult those files for more information on configuring the RADIUS plugin .SH OPTIONS The RADIUS plugin introduces one additional pppd option: .TP .BI "radius-config-file " filename The file .I filename is taken as the radiusclient configuration file. If this option is not used, then the plugin uses .I /etc/radiusclient/radiusclient.conf as the configuration file. .TP .BI "avpair " attribute=value Adds an Attribute-Value pair to be passed on to the RADIUS server on each request. .TP .BI map-to-ifname Sets Radius NAS-Port attribute to number equal to interface name (Default) .TP .BI map-to-ttyname Sets Radius NAS-Port attribute value via libradiusclient library .SH USAGE To use the plugin, simply supply the .B plugin radius.so option to pppd, and edit .I /etc/radiusclient/radiusclient.conf appropriately. If you use the RADIUS plugin, the normal pppd authentication schemes (login, checking the /etc/ppp/*-secrets files) are skipped. The RADIUS server should assign an IP address to the peer using the RADIUS Framed-IP-Address attribute. .SH SEE ALSO .BR pppd (8) " pppd-radattr" (8) .SH AUTHOR David F. Skoll ppp-2.4.5/pppd/plugins/radius/radattr.c000066400000000000000000000061371130035057700200510ustar00rootroot00000000000000/*********************************************************************** * * radattr.c * * A plugin which is stacked on top of radius.so. This plugin writes * all RADIUS attributes from the server's authentication confirmation * into /var/run/radattr.pppN. These attributes are available for * consumption by /etc/ppp/ip-{up,down} scripts. * * Copyright (C) 2002 Roaring Penguin Software Inc. * * This plugin may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * ***********************************************************************/ static char const RCSID[] = "$Id: radattr.c,v 1.2 2004/10/28 00:24:40 paulus Exp $"; #include "pppd.h" #include "radiusclient.h" #include extern void (*radius_attributes_hook)(VALUE_PAIR *); static void print_attributes(VALUE_PAIR *); static void cleanup(void *opaque, int arg); char pppd_version[] = VERSION; /********************************************************************** * %FUNCTION: plugin_init * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Initializes radattr plugin. ***********************************************************************/ void plugin_init(void) { radius_attributes_hook = print_attributes; #if 0 /* calling cleanup() on link down is problematic because print_attributes() is called only after PAP or CHAP authentication, but not when the link should go up again for any other reason */ add_notifier(&link_down_notifier, cleanup, NULL); #endif /* Just in case... */ add_notifier(&exitnotify, cleanup, NULL); info("RADATTR plugin initialized."); } /********************************************************************** * %FUNCTION: print_attributes * %ARGUMENTS: * vp -- linked-list of RADIUS attribute-value pairs * %RETURNS: * Nothing * %DESCRIPTION: * Prints the attribute pairs to /var/run/radattr.pppN. Each line of the * file contains "name value" pairs. ***********************************************************************/ static void print_attributes(VALUE_PAIR *vp) { FILE *fp; char fname[512]; char name[2048]; char value[2048]; int cnt = 0; slprintf(fname, sizeof(fname), "/var/run/radattr.%s", ifname); fp = fopen(fname, "w"); if (!fp) { warn("radattr plugin: Could not open %s for writing: %m", fname); return; } for (; vp; vp=vp->next) { if (rc_avpair_tostr(vp, name, sizeof(name), value, sizeof(value)) < 0) { continue; } fprintf(fp, "%s %s\n", name, value); cnt++; } fclose(fp); dbglog("RADATTR plugin wrote %d line(s) to file %s.", cnt, fname); } /********************************************************************** * %FUNCTION: cleanup * %ARGUMENTS: * opaque -- not used * arg -- not used * %RETURNS: * Nothing * %DESCRIPTION: * Deletes /var/run/radattr.pppN ***********************************************************************/ static void cleanup(void *opaque, int arg) { char fname[512]; slprintf(fname, sizeof(fname), "/var/run/radattr.%s", ifname); (void) remove(fname); dbglog("RADATTR plugin removed file %s.", fname); } ppp-2.4.5/pppd/plugins/radius/radius.c000066400000000000000000001070251130035057700176750ustar00rootroot00000000000000/*********************************************************************** * * radius.c * * RADIUS plugin for pppd. Performs PAP, CHAP, MS-CHAP, MS-CHAPv2 * authentication using RADIUS. * * Copyright (C) 2002 Roaring Penguin Software Inc. * * Based on a patch for ipppd, which is: * Copyright (C) 1996, Matjaz Godec * Copyright (C) 1996, Lars Fenneberg * Copyright (C) 1997, Miguel A.L. Paraz * * Uses radiusclient library, which is: * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg * Copyright (C) 2002 Roaring Penguin Software Inc. * * MPPE support is by Ralf Hofmann, , with * modification from Frank Cusack, . * * This plugin may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * ***********************************************************************/ static char const RCSID[] = "$Id: radius.c,v 1.32 2008/05/26 09:18:08 paulus Exp $"; #include "pppd.h" #include "chap-new.h" #ifdef CHAPMS #include "chap_ms.h" #ifdef MPPE #include "md5.h" #endif #endif #include "radiusclient.h" #include "fsm.h" #include "ipcp.h" #include #include #include #include #include #include #define BUF_LEN 1024 #define MD5_HASH_SIZE 16 static char *config_file = NULL; static int add_avp(char **); static struct avpopt { char *vpstr; struct avpopt *next; } *avpopt = NULL; static bool portnummap = 0; static option_t Options[] = { { "radius-config-file", o_string, &config_file }, { "avpair", o_special, add_avp }, { "map-to-ttyname", o_bool, &portnummap, "Set Radius NAS-Port attribute value via libradiusclient library", OPT_PRIO | 1 }, { "map-to-ifname", o_bool, &portnummap, "Set Radius NAS-Port attribute to number as in interface name (Default)", OPT_PRIOSUB | 0 }, { NULL } }; static int radius_secret_check(void); static int radius_pap_auth(char *user, char *passwd, char **msgp, struct wordlist **paddrs, struct wordlist **popts); static int radius_chap_verify(char *user, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space); static void radius_ip_up(void *opaque, int arg); static void radius_ip_down(void *opaque, int arg); static void make_username_realm(char *user); static int radius_setparams(VALUE_PAIR *vp, char *msg, REQUEST_INFO *req_info, struct chap_digest_type *digest, unsigned char *challenge, char *message, int message_space); static void radius_choose_ip(u_int32_t *addrp); static int radius_init(char *msg); static int get_client_port(char *ifname); static int radius_allowed_address(u_int32_t addr); static void radius_acct_interim(void *); #ifdef MPPE static int radius_setmppekeys(VALUE_PAIR *vp, REQUEST_INFO *req_info, unsigned char *); static int radius_setmppekeys2(VALUE_PAIR *vp, REQUEST_INFO *req_info); #endif #ifndef MAXSESSIONID #define MAXSESSIONID 32 #endif #ifndef MAXCLASSLEN #define MAXCLASSLEN 500 #endif struct radius_state { int accounting_started; int initialized; int client_port; int choose_ip; int any_ip_addr_ok; int done_chap_once; u_int32_t ip_addr; char user[MAXNAMELEN]; char config_file[MAXPATHLEN]; char session_id[MAXSESSIONID + 1]; time_t start_time; int acct_interim_interval; SERVER *authserver; /* Authentication server to use */ SERVER *acctserver; /* Accounting server to use */ int class_len; char class[MAXCLASSLEN]; VALUE_PAIR *avp; /* Additional (user supplied) vp's to send to server */ }; void (*radius_attributes_hook)(VALUE_PAIR *) = NULL; /* The pre_auth_hook MAY set authserver and acctserver if it wants. In that case, they override the values in the radiusclient.conf file */ void (*radius_pre_auth_hook)(char const *user, SERVER **authserver, SERVER **acctserver) = NULL; static struct radius_state rstate; char pppd_version[] = VERSION; /********************************************************************** * %FUNCTION: plugin_init * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Initializes RADIUS plugin. ***********************************************************************/ void plugin_init(void) { pap_check_hook = radius_secret_check; pap_auth_hook = radius_pap_auth; chap_check_hook = radius_secret_check; chap_verify_hook = radius_chap_verify; ip_choose_hook = radius_choose_ip; allowed_address_hook = radius_allowed_address; add_notifier(&ip_up_notifier, radius_ip_up, NULL); add_notifier(&ip_down_notifier, radius_ip_down, NULL); memset(&rstate, 0, sizeof(rstate)); strlcpy(rstate.config_file, "/etc/radiusclient/radiusclient.conf", sizeof(rstate.config_file)); add_options(Options); info("RADIUS plugin initialized."); } /********************************************************************** * %FUNCTION: add_avp * %ARGUMENTS: * argv -- the pair to add * %RETURNS: * 1 * %DESCRIPTION: * Adds an av pair to be passed on to the RADIUS server on each request. ***********************************************************************/ static int add_avp(char **argv) { struct avpopt *p = malloc(sizeof(struct avpopt)); /* Append to a list of vp's for later parsing */ p->vpstr = strdup(*argv); p->next = avpopt; avpopt = p; return 1; } /********************************************************************** * %FUNCTION: radius_secret_check * %ARGUMENTS: * None * %RETURNS: * 1 -- we are ALWAYS willing to supply a secret. :-) * %DESCRIPTION: * Tells pppd that we will try to authenticate the peer, and not to * worry about looking in /etc/ppp/*-secrets ***********************************************************************/ static int radius_secret_check(void) { return 1; } /********************************************************************** * %FUNCTION: radius_choose_ip * %ARGUMENTS: * addrp -- where to store the IP address * %RETURNS: * Nothing * %DESCRIPTION: * If RADIUS server has specified an IP address, it is stored in *addrp. ***********************************************************************/ static void radius_choose_ip(u_int32_t *addrp) { if (rstate.choose_ip) { *addrp = rstate.ip_addr; } } /********************************************************************** * %FUNCTION: radius_pap_auth * %ARGUMENTS: * user -- user-name of peer * passwd -- password supplied by peer * msgp -- Message which will be sent in PAP response * paddrs -- set to a list of possible peer IP addresses * popts -- set to a list of additional pppd options * %RETURNS: * 1 if we can authenticate, -1 if we cannot. * %DESCRIPTION: * Performs PAP authentication using RADIUS ***********************************************************************/ static int radius_pap_auth(char *user, char *passwd, char **msgp, struct wordlist **paddrs, struct wordlist **popts) { VALUE_PAIR *send, *received; UINT4 av_type; int result; static char radius_msg[BUF_LEN]; radius_msg[0] = 0; *msgp = radius_msg; if (radius_init(radius_msg) < 0) { return 0; } /* Put user with potentially realm added in rstate.user */ make_username_realm(user); if (radius_pre_auth_hook) { radius_pre_auth_hook(rstate.user, &rstate.authserver, &rstate.acctserver); } send = NULL; received = NULL; /* Hack... the "port" is the ppp interface number. Should really be the tty */ rstate.client_port = get_client_port(portnummap ? devnam : ifname); av_type = PW_FRAMED; rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE); av_type = PW_PPP; rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE); rc_avpair_add(&send, PW_USER_NAME, rstate.user , 0, VENDOR_NONE); rc_avpair_add(&send, PW_USER_PASSWORD, passwd, 0, VENDOR_NONE); if (*remote_number) { rc_avpair_add(&send, PW_CALLING_STATION_ID, remote_number, 0, VENDOR_NONE); } else if (ipparam) rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE); /* Add user specified vp's */ if (rstate.avp) rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp)); if (rstate.authserver) { result = rc_auth_using_server(rstate.authserver, rstate.client_port, send, &received, radius_msg, NULL); } else { result = rc_auth(rstate.client_port, send, &received, radius_msg, NULL); } if (result == OK_RC) { if (radius_setparams(received, radius_msg, NULL, NULL, NULL, NULL, 0) < 0) { result = ERROR_RC; } } /* free value pairs */ rc_avpair_free(received); rc_avpair_free(send); return (result == OK_RC) ? 1 : 0; } /********************************************************************** * %FUNCTION: radius_chap_verify * %ARGUMENTS: * user -- name of the peer * ourname -- name for this machine * id -- the ID byte in the challenge * digest -- points to the structure representing the digest type * challenge -- the challenge string we sent (length in first byte) * response -- the response (hash) the peer sent back (length in 1st byte) * message -- space for a message to be returned to the peer * message_space -- number of bytes available at *message. * %RETURNS: * 1 if the response is good, 0 if it is bad * %DESCRIPTION: * Performs CHAP, MS-CHAP and MS-CHAPv2 authentication using RADIUS. ***********************************************************************/ static int radius_chap_verify(char *user, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space) { VALUE_PAIR *send, *received; UINT4 av_type; static char radius_msg[BUF_LEN]; int result; int challenge_len, response_len; u_char cpassword[MAX_RESPONSE_LEN + 1]; #ifdef MPPE /* Need the RADIUS secret and Request Authenticator to decode MPPE */ REQUEST_INFO request_info, *req_info = &request_info; #else REQUEST_INFO *req_info = NULL; #endif challenge_len = *challenge++; response_len = *response++; radius_msg[0] = 0; if (radius_init(radius_msg) < 0) { error("%s", radius_msg); return 0; } /* return error for types we can't handle */ if ((digest->code != CHAP_MD5) #ifdef CHAPMS && (digest->code != CHAP_MICROSOFT) && (digest->code != CHAP_MICROSOFT_V2) #endif ) { error("RADIUS: Challenge type %u unsupported", digest->code); return 0; } /* Put user with potentially realm added in rstate.user */ if (!rstate.done_chap_once) { make_username_realm(user); rstate.client_port = get_client_port (portnummap ? devnam : ifname); if (radius_pre_auth_hook) { radius_pre_auth_hook(rstate.user, &rstate.authserver, &rstate.acctserver); } } send = received = NULL; av_type = PW_FRAMED; rc_avpair_add (&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE); av_type = PW_PPP; rc_avpair_add (&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE); rc_avpair_add (&send, PW_USER_NAME, rstate.user , 0, VENDOR_NONE); /* * add the challenge and response fields */ switch (digest->code) { case CHAP_MD5: /* CHAP-Challenge and CHAP-Password */ if (response_len != MD5_HASH_SIZE) return 0; cpassword[0] = id; memcpy(&cpassword[1], response, MD5_HASH_SIZE); rc_avpair_add(&send, PW_CHAP_CHALLENGE, challenge, challenge_len, VENDOR_NONE); rc_avpair_add(&send, PW_CHAP_PASSWORD, cpassword, MD5_HASH_SIZE + 1, VENDOR_NONE); break; #ifdef CHAPMS case CHAP_MICROSOFT: { /* MS-CHAP-Challenge and MS-CHAP-Response */ u_char *p = cpassword; if (response_len != MS_CHAP_RESPONSE_LEN) return 0; *p++ = id; /* The idiots use a different field order in RADIUS than PPP */ *p++ = response[MS_CHAP_USENT]; memcpy(p, response, MS_CHAP_LANMANRESP_LEN + MS_CHAP_NTRESP_LEN); rc_avpair_add(&send, PW_MS_CHAP_CHALLENGE, challenge, challenge_len, VENDOR_MICROSOFT); rc_avpair_add(&send, PW_MS_CHAP_RESPONSE, cpassword, MS_CHAP_RESPONSE_LEN + 1, VENDOR_MICROSOFT); break; } case CHAP_MICROSOFT_V2: { /* MS-CHAP-Challenge and MS-CHAP2-Response */ u_char *p = cpassword; if (response_len != MS_CHAP2_RESPONSE_LEN) return 0; *p++ = id; /* The idiots use a different field order in RADIUS than PPP */ *p++ = response[MS_CHAP2_FLAGS]; memcpy(p, response, (MS_CHAP2_PEER_CHAL_LEN + MS_CHAP2_RESERVED_LEN + MS_CHAP2_NTRESP_LEN)); rc_avpair_add(&send, PW_MS_CHAP_CHALLENGE, challenge, challenge_len, VENDOR_MICROSOFT); rc_avpair_add(&send, PW_MS_CHAP2_RESPONSE, cpassword, MS_CHAP2_RESPONSE_LEN + 1, VENDOR_MICROSOFT); break; } #endif } if (*remote_number) { rc_avpair_add(&send, PW_CALLING_STATION_ID, remote_number, 0, VENDOR_NONE); } else if (ipparam) rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE); /* Add user specified vp's */ if (rstate.avp) rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp)); /* * make authentication with RADIUS server */ if (rstate.authserver) { result = rc_auth_using_server(rstate.authserver, rstate.client_port, send, &received, radius_msg, req_info); } else { result = rc_auth(rstate.client_port, send, &received, radius_msg, req_info); } strlcpy(message, radius_msg, message_space); if (result == OK_RC) { if (!rstate.done_chap_once) { if (radius_setparams(received, radius_msg, req_info, digest, challenge, message, message_space) < 0) { error("%s", radius_msg); result = ERROR_RC; } else { rstate.done_chap_once = 1; } } } rc_avpair_free(received); rc_avpair_free (send); return (result == OK_RC); } /********************************************************************** * %FUNCTION: make_username_realm * %ARGUMENTS: * user -- the user given to pppd * %RETURNS: * Nothing * %DESCRIPTION: * Copies user into rstate.user. If it lacks a realm (no "@domain" part), * then the default realm from the radiusclient config file is added. ***********************************************************************/ static void make_username_realm(char *user) { char *default_realm; if ( user != NULL ) { strlcpy(rstate.user, user, sizeof(rstate.user)); } else { rstate.user[0] = 0; } default_realm = rc_conf_str("default_realm"); if (!strchr(rstate.user, '@') && default_realm && (*default_realm != '\0')) { strlcat(rstate.user, "@", sizeof(rstate.user)); strlcat(rstate.user, default_realm, sizeof(rstate.user)); } } /********************************************************************** * %FUNCTION: radius_setparams * %ARGUMENTS: * vp -- received value-pairs * msg -- buffer in which to place error message. Holds up to BUF_LEN chars * %RETURNS: * >= 0 on success; -1 on failure * %DESCRIPTION: * Parses attributes sent by RADIUS server and sets them in pppd. ***********************************************************************/ static int radius_setparams(VALUE_PAIR *vp, char *msg, REQUEST_INFO *req_info, struct chap_digest_type *digest, unsigned char *challenge, char *message, int message_space) { u_int32_t remote; int ms_chap2_success = 0; #ifdef MPPE int mppe_enc_keys = 0; /* whether or not these were received */ int mppe_enc_policy = 0; int mppe_enc_types = 0; #endif /* Send RADIUS attributes to anyone else who might be interested */ if (radius_attributes_hook) { (*radius_attributes_hook)(vp); } /* * service type (if not framed then quit), * new IP address (RADIUS can define static IP for some users), */ while (vp) { if (vp->vendorcode == VENDOR_NONE) { switch (vp->attribute) { case PW_SERVICE_TYPE: /* check for service type */ /* if not FRAMED then exit */ if (vp->lvalue != PW_FRAMED) { slprintf(msg, BUF_LEN, "RADIUS: wrong service type %ld for %s", vp->lvalue, rstate.user); return -1; } break; case PW_FRAMED_PROTOCOL: /* check for framed protocol type */ /* if not PPP then also exit */ if (vp->lvalue != PW_PPP) { slprintf(msg, BUF_LEN, "RADIUS: wrong framed protocol %ld for %s", vp->lvalue, rstate.user); return -1; } break; case PW_SESSION_TIMEOUT: /* Session timeout */ maxconnect = vp->lvalue; break; #ifdef MAXOCTETS case PW_SESSION_OCTETS_LIMIT: /* Session traffic limit */ maxoctets = vp->lvalue; break; case PW_OCTETS_DIRECTION: /* Session traffic limit direction check */ maxoctets_dir = ( vp->lvalue > 4 ) ? 0 : vp->lvalue ; break; #endif case PW_ACCT_INTERIM_INTERVAL: /* Send accounting updates every few seconds */ rstate.acct_interim_interval = vp->lvalue; /* RFC says it MUST NOT be less than 60 seconds */ /* We use "0" to signify not sending updates */ if (rstate.acct_interim_interval && rstate.acct_interim_interval < 60) { rstate.acct_interim_interval = 60; } break; case PW_FRAMED_IP_ADDRESS: /* seting up remote IP addresses */ remote = vp->lvalue; if (remote == 0xffffffff) { /* 0xffffffff means user should be allowed to select one */ rstate.any_ip_addr_ok = 1; } else if (remote != 0xfffffffe) { /* 0xfffffffe means NAS should select an ip address */ remote = htonl(vp->lvalue); if (bad_ip_adrs (remote)) { slprintf(msg, BUF_LEN, "RADIUS: bad remote IP address %I for %s", remote, rstate.user); return -1; } rstate.choose_ip = 1; rstate.ip_addr = remote; } break; case PW_CLASS: /* Save Class attribute to pass it in accounting request */ if (vp->lvalue <= MAXCLASSLEN) { rstate.class_len=vp->lvalue; memcpy(rstate.class, vp->strvalue, rstate.class_len); } /* else too big for our buffer - ignore it */ break; } #ifdef CHAPMS } else if (vp->vendorcode == VENDOR_MICROSOFT) { switch (vp->attribute) { case PW_MS_CHAP2_SUCCESS: if ((vp->lvalue != 43) || strncmp(vp->strvalue + 1, "S=", 2)) { slprintf(msg,BUF_LEN,"RADIUS: bad MS-CHAP2-Success packet"); return -1; } if (message != NULL) strlcpy(message, vp->strvalue + 1, message_space); ms_chap2_success = 1; break; #ifdef MPPE case PW_MS_CHAP_MPPE_KEYS: if (radius_setmppekeys(vp, req_info, challenge) < 0) { slprintf(msg, BUF_LEN, "RADIUS: bad MS-CHAP-MPPE-Keys attribute"); return -1; } mppe_enc_keys = 1; break; case PW_MS_MPPE_SEND_KEY: case PW_MS_MPPE_RECV_KEY: if (radius_setmppekeys2(vp, req_info) < 0) { slprintf(msg, BUF_LEN, "RADIUS: bad MS-MPPE-%s-Key attribute", (vp->attribute == PW_MS_MPPE_SEND_KEY)? "Send": "Recv"); return -1; } mppe_enc_keys = 1; break; case PW_MS_MPPE_ENCRYPTION_POLICY: mppe_enc_policy = vp->lvalue; /* save for later */ break; case PW_MS_MPPE_ENCRYPTION_TYPES: mppe_enc_types = vp->lvalue; /* save for later */ break; #endif /* MPPE */ #if 0 case PW_MS_PRIMARY_DNS_SERVER: case PW_MS_SECONDARY_DNS_SERVER: case PW_MS_PRIMARY_NBNS_SERVER: case PW_MS_SECONDARY_NBNS_SERVER: break; #endif } #endif /* CHAPMS */ } vp = vp->next; } /* Require a valid MS-CHAP2-SUCCESS for MS-CHAPv2 auth */ if (digest && (digest->code == CHAP_MICROSOFT_V2) && !ms_chap2_success) return -1; #ifdef MPPE /* * Require both policy and key attributes to indicate a valid key. * Note that if the policy value was '0' we don't set the key! */ if (mppe_enc_policy && mppe_enc_keys) { mppe_keys_set = 1; /* Set/modify allowed encryption types. */ if (mppe_enc_types) set_mppe_enc_types(mppe_enc_policy, mppe_enc_types); } #endif return 0; } #ifdef MPPE /********************************************************************** * %FUNCTION: radius_setmppekeys * %ARGUMENTS: * vp -- value pair holding MS-CHAP-MPPE-KEYS attribute * req_info -- radius request information used for encryption * %RETURNS: * >= 0 on success; -1 on failure * %DESCRIPTION: * Decrypt the "key" provided by the RADIUS server for MPPE encryption. * See RFC 2548. ***********************************************************************/ static int radius_setmppekeys(VALUE_PAIR *vp, REQUEST_INFO *req_info, unsigned char *challenge) { int i; MD5_CTX Context; u_char plain[32]; u_char buf[16]; if (vp->lvalue != 32) { error("RADIUS: Incorrect attribute length (%d) for MS-CHAP-MPPE-Keys", vp->lvalue); return -1; } memcpy(plain, vp->strvalue, sizeof(plain)); MD5_Init(&Context); MD5_Update(&Context, req_info->secret, strlen(req_info->secret)); MD5_Update(&Context, req_info->request_vector, AUTH_VECTOR_LEN); MD5_Final(buf, &Context); for (i = 0; i < 16; i++) plain[i] ^= buf[i]; MD5_Init(&Context); MD5_Update(&Context, req_info->secret, strlen(req_info->secret)); MD5_Update(&Context, vp->strvalue, 16); MD5_Final(buf, &Context); for(i = 0; i < 16; i++) plain[i + 16] ^= buf[i]; /* * Annoying. The "key" returned is just the NTPasswordHashHash, which * the NAS (us) doesn't need; we only need the start key. So we have * to generate the start key, sigh. NB: We do not support the LM-Key. */ mppe_set_keys(challenge, &plain[8]); return 0; } /********************************************************************** * %FUNCTION: radius_setmppekeys2 * %ARGUMENTS: * vp -- value pair holding MS-MPPE-SEND-KEY or MS-MPPE-RECV-KEY attribute * req_info -- radius request information used for encryption * %RETURNS: * >= 0 on success; -1 on failure * %DESCRIPTION: * Decrypt the key provided by the RADIUS server for MPPE encryption. * See RFC 2548. ***********************************************************************/ static int radius_setmppekeys2(VALUE_PAIR *vp, REQUEST_INFO *req_info) { int i; MD5_CTX Context; u_char *salt = vp->strvalue; u_char *crypt = vp->strvalue + 2; u_char plain[32]; u_char buf[MD5_HASH_SIZE]; char *type = "Send"; if (vp->attribute == PW_MS_MPPE_RECV_KEY) type = "Recv"; if (vp->lvalue != 34) { error("RADIUS: Incorrect attribute length (%d) for MS-MPPE-%s-Key", vp->lvalue, type); return -1; } if ((salt[0] & 0x80) == 0) { error("RADIUS: Illegal salt value for MS-MPPE-%s-Key attribute", type); return -1; } memcpy(plain, crypt, 32); MD5_Init(&Context); MD5_Update(&Context, req_info->secret, strlen(req_info->secret)); MD5_Update(&Context, req_info->request_vector, AUTH_VECTOR_LEN); MD5_Update(&Context, salt, 2); MD5_Final(buf, &Context); for (i = 0; i < 16; i++) plain[i] ^= buf[i]; if (plain[0] != sizeof(mppe_send_key) /* 16 */) { error("RADIUS: Incorrect key length (%d) for MS-MPPE-%s-Key attribute", (int) plain[0], type); return -1; } MD5_Init(&Context); MD5_Update(&Context, req_info->secret, strlen(req_info->secret)); MD5_Update(&Context, crypt, 16); MD5_Final(buf, &Context); plain[16] ^= buf[0]; /* only need the first byte */ if (vp->attribute == PW_MS_MPPE_SEND_KEY) memcpy(mppe_send_key, plain + 1, 16); else memcpy(mppe_recv_key, plain + 1, 16); return 0; } #endif /* MPPE */ /********************************************************************** * %FUNCTION: radius_acct_start * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Sends a "start" accounting message to the RADIUS server. ***********************************************************************/ static void radius_acct_start(void) { UINT4 av_type; int result; VALUE_PAIR *send = NULL; ipcp_options *ho = &ipcp_hisoptions[0]; u_int32_t hisaddr; if (!rstate.initialized) { return; } rstate.start_time = time(NULL); strncpy(rstate.session_id, rc_mksid(), sizeof(rstate.session_id)); rc_avpair_add(&send, PW_ACCT_SESSION_ID, rstate.session_id, 0, VENDOR_NONE); rc_avpair_add(&send, PW_USER_NAME, rstate.user, 0, VENDOR_NONE); if (rstate.class_len > 0) rc_avpair_add(&send, PW_CLASS, rstate.class, rstate.class_len, VENDOR_NONE); av_type = PW_STATUS_START; rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE); av_type = PW_FRAMED; rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE); av_type = PW_PPP; rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE); if (*remote_number) { rc_avpair_add(&send, PW_CALLING_STATION_ID, remote_number, 0, VENDOR_NONE); } else if (ipparam) rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE); av_type = PW_RADIUS; rc_avpair_add(&send, PW_ACCT_AUTHENTIC, &av_type, 0, VENDOR_NONE); av_type = ( using_pty ? PW_VIRTUAL : ( sync_serial ? PW_SYNC : PW_ASYNC ) ); rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0, VENDOR_NONE); hisaddr = ho->hisaddr; av_type = htonl(hisaddr); rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE); /* Add user specified vp's */ if (rstate.avp) rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp)); if (rstate.acctserver) { result = rc_acct_using_server(rstate.acctserver, rstate.client_port, send); } else { result = rc_acct(rstate.client_port, send); } rc_avpair_free(send); if (result != OK_RC) { /* RADIUS server could be down so make this a warning */ syslog(LOG_WARNING, "Accounting START failed for %s", rstate.user); } else { rstate.accounting_started = 1; /* Kick off periodic accounting reports */ if (rstate.acct_interim_interval) { TIMEOUT(radius_acct_interim, NULL, rstate.acct_interim_interval); } } } /********************************************************************** * %FUNCTION: radius_acct_stop * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Sends a "stop" accounting message to the RADIUS server. ***********************************************************************/ static void radius_acct_stop(void) { UINT4 av_type; VALUE_PAIR *send = NULL; ipcp_options *ho = &ipcp_hisoptions[0]; u_int32_t hisaddr; int result; if (!rstate.initialized) { return; } if (!rstate.accounting_started) { return; } if (rstate.acct_interim_interval) UNTIMEOUT(radius_acct_interim, NULL); rstate.accounting_started = 0; rc_avpair_add(&send, PW_ACCT_SESSION_ID, rstate.session_id, 0, VENDOR_NONE); rc_avpair_add(&send, PW_USER_NAME, rstate.user, 0, VENDOR_NONE); av_type = PW_STATUS_STOP; rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE); av_type = PW_FRAMED; rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE); av_type = PW_PPP; rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE); av_type = PW_RADIUS; rc_avpair_add(&send, PW_ACCT_AUTHENTIC, &av_type, 0, VENDOR_NONE); if (link_stats_valid) { av_type = link_connect_time; rc_avpair_add(&send, PW_ACCT_SESSION_TIME, &av_type, 0, VENDOR_NONE); av_type = link_stats.bytes_out; rc_avpair_add(&send, PW_ACCT_OUTPUT_OCTETS, &av_type, 0, VENDOR_NONE); av_type = link_stats.bytes_in; rc_avpair_add(&send, PW_ACCT_INPUT_OCTETS, &av_type, 0, VENDOR_NONE); av_type = link_stats.pkts_out; rc_avpair_add(&send, PW_ACCT_OUTPUT_PACKETS, &av_type, 0, VENDOR_NONE); av_type = link_stats.pkts_in; rc_avpair_add(&send, PW_ACCT_INPUT_PACKETS, &av_type, 0, VENDOR_NONE); } if (*remote_number) { rc_avpair_add(&send, PW_CALLING_STATION_ID, remote_number, 0, VENDOR_NONE); } else if (ipparam) rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE); av_type = ( using_pty ? PW_VIRTUAL : ( sync_serial ? PW_SYNC : PW_ASYNC ) ); rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0, VENDOR_NONE); av_type = PW_NAS_ERROR; switch( status ) { case EXIT_OK: case EXIT_USER_REQUEST: av_type = PW_USER_REQUEST; break; case EXIT_HANGUP: case EXIT_PEER_DEAD: case EXIT_CONNECT_FAILED: av_type = PW_LOST_CARRIER; break; case EXIT_INIT_FAILED: case EXIT_OPEN_FAILED: case EXIT_LOCK_FAILED: case EXIT_PTYCMD_FAILED: av_type = PW_PORT_ERROR; break; case EXIT_PEER_AUTH_FAILED: case EXIT_AUTH_TOPEER_FAILED: case EXIT_NEGOTIATION_FAILED: case EXIT_CNID_AUTH_FAILED: av_type = PW_SERVICE_UNAVAILABLE; break; case EXIT_IDLE_TIMEOUT: av_type = PW_ACCT_IDLE_TIMEOUT; break; case EXIT_CALLBACK: av_type = PW_CALLBACK; break; case EXIT_CONNECT_TIME: av_type = PW_ACCT_SESSION_TIMEOUT; break; #ifdef MAXOCTETS case EXIT_TRAFFIC_LIMIT: av_type = PW_NAS_REQUEST; break; #endif default: av_type = PW_NAS_ERROR; break; } rc_avpair_add(&send, PW_ACCT_TERMINATE_CAUSE, &av_type, 0, VENDOR_NONE); hisaddr = ho->hisaddr; av_type = htonl(hisaddr); rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE); /* Add user specified vp's */ if (rstate.avp) rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp)); if (rstate.acctserver) { result = rc_acct_using_server(rstate.acctserver, rstate.client_port, send); } else { result = rc_acct(rstate.client_port, send); } if (result != OK_RC) { /* RADIUS server could be down so make this a warning */ syslog(LOG_WARNING, "Accounting STOP failed for %s", rstate.user); } rc_avpair_free(send); } /********************************************************************** * %FUNCTION: radius_acct_interim * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Sends an interim accounting message to the RADIUS server ***********************************************************************/ static void radius_acct_interim(void *ignored) { UINT4 av_type; VALUE_PAIR *send = NULL; ipcp_options *ho = &ipcp_hisoptions[0]; u_int32_t hisaddr; int result; if (!rstate.initialized) { return; } if (!rstate.accounting_started) { return; } rc_avpair_add(&send, PW_ACCT_SESSION_ID, rstate.session_id, 0, VENDOR_NONE); rc_avpair_add(&send, PW_USER_NAME, rstate.user, 0, VENDOR_NONE); av_type = PW_STATUS_ALIVE; rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE); av_type = PW_FRAMED; rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE); av_type = PW_PPP; rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE); av_type = PW_RADIUS; rc_avpair_add(&send, PW_ACCT_AUTHENTIC, &av_type, 0, VENDOR_NONE); /* Update link stats */ update_link_stats(0); if (link_stats_valid) { link_stats_valid = 0; /* Force later code to update */ av_type = link_connect_time; rc_avpair_add(&send, PW_ACCT_SESSION_TIME, &av_type, 0, VENDOR_NONE); av_type = link_stats.bytes_out; rc_avpair_add(&send, PW_ACCT_OUTPUT_OCTETS, &av_type, 0, VENDOR_NONE); av_type = link_stats.bytes_in; rc_avpair_add(&send, PW_ACCT_INPUT_OCTETS, &av_type, 0, VENDOR_NONE); av_type = link_stats.pkts_out; rc_avpair_add(&send, PW_ACCT_OUTPUT_PACKETS, &av_type, 0, VENDOR_NONE); av_type = link_stats.pkts_in; rc_avpair_add(&send, PW_ACCT_INPUT_PACKETS, &av_type, 0, VENDOR_NONE); } if (*remote_number) { rc_avpair_add(&send, PW_CALLING_STATION_ID, remote_number, 0, VENDOR_NONE); } else if (ipparam) rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE); av_type = ( using_pty ? PW_VIRTUAL : ( sync_serial ? PW_SYNC : PW_ASYNC ) ); rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0, VENDOR_NONE); hisaddr = ho->hisaddr; av_type = htonl(hisaddr); rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE); /* Add user specified vp's */ if (rstate.avp) rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp)); if (rstate.acctserver) { result = rc_acct_using_server(rstate.acctserver, rstate.client_port, send); } else { result = rc_acct(rstate.client_port, send); } if (result != OK_RC) { /* RADIUS server could be down so make this a warning */ syslog(LOG_WARNING, "Interim accounting failed for %s", rstate.user); } rc_avpair_free(send); /* Schedule another one */ TIMEOUT(radius_acct_interim, NULL, rstate.acct_interim_interval); } /********************************************************************** * %FUNCTION: radius_ip_up * %ARGUMENTS: * opaque -- ignored * arg -- ignored * %RETURNS: * Nothing * %DESCRIPTION: * Called when IPCP is up. We'll do a start-accounting record. ***********************************************************************/ static void radius_ip_up(void *opaque, int arg) { radius_acct_start(); } /********************************************************************** * %FUNCTION: radius_ip_down * %ARGUMENTS: * opaque -- ignored * arg -- ignored * %RETURNS: * Nothing * %DESCRIPTION: * Called when IPCP is down. We'll do a stop-accounting record. ***********************************************************************/ static void radius_ip_down(void *opaque, int arg) { radius_acct_stop(); } /********************************************************************** * %FUNCTION: radius_init * %ARGUMENTS: * msg -- buffer of size BUF_LEN for error message * %RETURNS: * negative on failure; non-negative on success * %DESCRIPTION: * Initializes radiusclient library ***********************************************************************/ static int radius_init(char *msg) { if (rstate.initialized) { return 0; } if (config_file && *config_file) { strlcpy(rstate.config_file, config_file, MAXPATHLEN-1); } rstate.initialized = 1; if (rc_read_config(rstate.config_file) != 0) { slprintf(msg, BUF_LEN, "RADIUS: Can't read config file %s", rstate.config_file); return -1; } if (rc_read_dictionary(rc_conf_str("dictionary")) != 0) { slprintf(msg, BUF_LEN, "RADIUS: Can't read dictionary file %s", rc_conf_str("dictionary")); return -1; } if (rc_read_mapfile(rc_conf_str("mapfile")) != 0) { slprintf(msg, BUF_LEN, "RADIUS: Can't read map file %s", rc_conf_str("mapfile")); return -1; } /* Add av pairs saved during option parsing */ while (avpopt) { struct avpopt *n = avpopt->next; rc_avpair_parse(avpopt->vpstr, &rstate.avp); free(avpopt->vpstr); free(avpopt); avpopt = n; } return 0; } /********************************************************************** * %FUNCTION: get_client_port * %ARGUMENTS: * ifname -- PPP interface name (e.g. "ppp7") * %RETURNS: * The NAS port number (e.g. 7) * %DESCRIPTION: * Extracts the port number from the interface name ***********************************************************************/ static int get_client_port(char *ifname) { int port; if (sscanf(ifname, "ppp%d", &port) == 1) { return port; } return rc_map2id(ifname); } /********************************************************************** * %FUNCTION: radius_allowed_address * %ARGUMENTS: * addr -- IP address * %RETURNS: * 1 if we're allowed to use that IP address; 0 if not; -1 if we do * not know. ***********************************************************************/ static int radius_allowed_address(u_int32_t addr) { ipcp_options *wo = &ipcp_wantoptions[0]; if (!rstate.choose_ip) { /* If RADIUS server said any address is OK, then fine... */ if (rstate.any_ip_addr_ok) { return 1; } /* Sigh... if an address was supplied for remote host in pppd options, it has to match that. */ if (wo->hisaddr != 0 && wo->hisaddr == addr) { return 1; } return 0; } if (addr == rstate.ip_addr) return 1; return 0; } /* Useful for other plugins */ char *radius_logged_in_user(void) { return rstate.user; } ppp-2.4.5/pppd/plugins/radius/radiusclient.h000066400000000000000000000312501130035057700210750ustar00rootroot00000000000000/* * $Id: radiusclient.h,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg * * Copyright 1992 Livingston Enterprises, Inc. * * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan * and Merit Network, Inc. All Rights Reserved * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #ifndef RADIUSCLIENT_H #define RADIUSCLIENT_H #include #include #include #include "pppd.h" #ifndef _UINT4_T /* This works for all machines that Linux runs on... */ typedef unsigned int UINT4; typedef int INT4; #endif #define AUTH_VECTOR_LEN 16 #define AUTH_PASS_LEN (3 * 16) /* multiple of 16 */ #define AUTH_ID_LEN 64 #define AUTH_STRING_LEN 128 /* maximum of 253 */ #define BUFFER_LEN 8192 #define NAME_LENGTH 32 #define GETSTR_LENGTH 128 /* must be bigger than AUTH_PASS_LEN */ /* codes for radius_buildreq, radius_getport, etc. */ #define AUTH 0 #define ACCT 1 /* defines for config.c */ #define SERVER_MAX 8 #define AUTH_LOCAL_FST (1<<0) #define AUTH_RADIUS_FST (1<<1) #define AUTH_LOCAL_SND (1<<2) #define AUTH_RADIUS_SND (1<<3) typedef struct server { int max; char *name[SERVER_MAX]; unsigned short port[SERVER_MAX]; } SERVER; typedef struct pw_auth_hdr { u_char code; u_char id; u_short length; u_char vector[AUTH_VECTOR_LEN]; u_char data[2]; } AUTH_HDR; #define AUTH_HDR_LEN 20 #define MAX_SECRET_LENGTH (3 * 16) /* MUST be multiple of 16 */ #define CHAP_VALUE_LENGTH 16 #define PW_AUTH_UDP_PORT 1812 #define PW_ACCT_UDP_PORT 1813 #define PW_TYPE_STRING 0 #define PW_TYPE_INTEGER 1 #define PW_TYPE_IPADDR 2 #define PW_TYPE_DATE 3 /* standard RADIUS codes */ #define PW_ACCESS_REQUEST 1 #define PW_ACCESS_ACCEPT 2 #define PW_ACCESS_REJECT 3 #define PW_ACCOUNTING_REQUEST 4 #define PW_ACCOUNTING_RESPONSE 5 #define PW_ACCOUNTING_STATUS 6 #define PW_PASSWORD_REQUEST 7 #define PW_PASSWORD_ACK 8 #define PW_PASSWORD_REJECT 9 #define PW_ACCOUNTING_MESSAGE 10 #define PW_ACCESS_CHALLENGE 11 #define PW_STATUS_SERVER 12 #define PW_STATUS_CLIENT 13 /* standard RADIUS attribute-value pairs */ #define PW_USER_NAME 1 /* string */ #define PW_USER_PASSWORD 2 /* string */ #define PW_CHAP_PASSWORD 3 /* string */ #define PW_NAS_IP_ADDRESS 4 /* ipaddr */ #define PW_NAS_PORT 5 /* integer */ #define PW_SERVICE_TYPE 6 /* integer */ #define PW_FRAMED_PROTOCOL 7 /* integer */ #define PW_FRAMED_IP_ADDRESS 8 /* ipaddr */ #define PW_FRAMED_IP_NETMASK 9 /* ipaddr */ #define PW_FRAMED_ROUTING 10 /* integer */ #define PW_FILTER_ID 11 /* string */ #define PW_FRAMED_MTU 12 /* integer */ #define PW_FRAMED_COMPRESSION 13 /* integer */ #define PW_LOGIN_IP_HOST 14 /* ipaddr */ #define PW_LOGIN_SERVICE 15 /* integer */ #define PW_LOGIN_PORT 16 /* integer */ #define PW_OLD_PASSWORD 17 /* string */ /* deprecated */ #define PW_REPLY_MESSAGE 18 /* string */ #define PW_LOGIN_CALLBACK_NUMBER 19 /* string */ #define PW_FRAMED_CALLBACK_ID 20 /* string */ #define PW_EXPIRATION 21 /* date */ /* deprecated */ #define PW_FRAMED_ROUTE 22 /* string */ #define PW_FRAMED_IPX_NETWORK 23 /* integer */ #define PW_STATE 24 /* string */ #define PW_CLASS 25 /* string */ #define PW_VENDOR_SPECIFIC 26 /* string */ #define PW_SESSION_TIMEOUT 27 /* integer */ #define PW_IDLE_TIMEOUT 28 /* integer */ #define PW_TERMINATION_ACTION 29 /* integer */ #define PW_CALLED_STATION_ID 30 /* string */ #define PW_CALLING_STATION_ID 31 /* string */ #define PW_NAS_IDENTIFIER 32 /* string */ #define PW_PROXY_STATE 33 /* string */ #define PW_LOGIN_LAT_SERVICE 34 /* string */ #define PW_LOGIN_LAT_NODE 35 /* string */ #define PW_LOGIN_LAT_GROUP 36 /* string */ #define PW_FRAMED_APPLETALK_LINK 37 /* integer */ #define PW_FRAMED_APPLETALK_NETWORK 38 /* integer */ #define PW_FRAMED_APPLETALK_ZONE 39 /* string */ #define PW_CHAP_CHALLENGE 60 /* string */ #define PW_NAS_PORT_TYPE 61 /* integer */ #define PW_PORT_LIMIT 62 /* integer */ #define PW_LOGIN_LAT_PORT 63 /* string */ /* Vendor RADIUS attribute-value pairs */ #define PW_MS_CHAP_CHALLENGE 11 /* string */ #define PW_MS_CHAP_RESPONSE 1 /* string */ #define PW_MS_CHAP2_RESPONSE 25 /* string */ #define PW_MS_CHAP2_SUCCESS 26 /* string */ #define PW_MS_MPPE_ENCRYPTION_POLICY 7 /* string */ #define PW_MS_MPPE_ENCRYPTION_TYPE 8 /* string */ #define PW_MS_MPPE_ENCRYPTION_TYPES PW_MS_MPPE_ENCRYPTION_TYPE #define PW_MS_CHAP_MPPE_KEYS 12 /* string */ #define PW_MS_MPPE_SEND_KEY 16 /* string */ #define PW_MS_MPPE_RECV_KEY 17 /* string */ /* Accounting */ #define PW_ACCT_STATUS_TYPE 40 /* integer */ #define PW_ACCT_DELAY_TIME 41 /* integer */ #define PW_ACCT_INPUT_OCTETS 42 /* integer */ #define PW_ACCT_OUTPUT_OCTETS 43 /* integer */ #define PW_ACCT_SESSION_ID 44 /* string */ #define PW_ACCT_AUTHENTIC 45 /* integer */ #define PW_ACCT_SESSION_TIME 46 /* integer */ #define PW_ACCT_INPUT_PACKETS 47 /* integer */ #define PW_ACCT_OUTPUT_PACKETS 48 /* integer */ #define PW_ACCT_TERMINATE_CAUSE 49 /* integer */ #define PW_ACCT_MULTI_SESSION_ID 50 /* string */ #define PW_ACCT_LINK_COUNT 51 /* integer */ /* From RFC 2869 */ #define PW_ACCT_INTERIM_INTERVAL 85 /* integer */ /* Merit Experimental Extensions */ #define PW_USER_ID 222 /* string */ #define PW_USER_REALM 223 /* string */ /* Session limits */ #define PW_SESSION_OCTETS_LIMIT 227 /* integer */ #define PW_OCTETS_DIRECTION 228 /* integer */ /* Integer Translations */ /* SERVICE TYPES */ #define PW_LOGIN 1 #define PW_FRAMED 2 #define PW_CALLBACK_LOGIN 3 #define PW_CALLBACK_FRAMED 4 #define PW_OUTBOUND 5 #define PW_ADMINISTRATIVE 6 #define PW_NAS_PROMPT 7 #define PW_AUTHENTICATE_ONLY 8 #define PW_CALLBACK_NAS_PROMPT 9 /* FRAMED PROTOCOLS */ #define PW_PPP 1 #define PW_SLIP 2 #define PW_ARA 3 #define PW_GANDALF 4 #define PW_XYLOGICS 5 /* FRAMED ROUTING VALUES */ #define PW_NONE 0 #define PW_BROADCAST 1 #define PW_LISTEN 2 #define PW_BROADCAST_LISTEN 3 /* FRAMED COMPRESSION TYPES */ #define PW_VAN_JACOBSON_TCP_IP 1 #define PW_IPX_HEADER_COMPRESSION 2 /* LOGIN SERVICES */ #define PW_TELNET 0 #define PW_RLOGIN 1 #define PW_TCP_CLEAR 2 #define PW_PORTMASTER 3 #define PW_LAT 4 #define PW_X25_PAD 5 #define PW_X25_T3POS 6 /* TERMINATION ACTIONS */ #define PW_DEFAULT 0 #define PW_RADIUS_REQUEST 1 /* PROHIBIT PROTOCOL */ #define PW_DUMB 0 /* 1 and 2 are defined in FRAMED PROTOCOLS */ #define PW_AUTH_ONLY 3 #define PW_ALL 255 /* ACCOUNTING STATUS TYPES */ #define PW_STATUS_START 1 #define PW_STATUS_STOP 2 #define PW_STATUS_ALIVE 3 #define PW_STATUS_MODEM_START 4 #define PW_STATUS_MODEM_STOP 5 #define PW_STATUS_CANCEL 6 #define PW_ACCOUNTING_ON 7 #define PW_ACCOUNTING_OFF 8 /* ACCOUNTING TERMINATION CAUSES */ #define PW_USER_REQUEST 1 #define PW_LOST_CARRIER 2 #define PW_LOST_SERVICE 3 #define PW_ACCT_IDLE_TIMEOUT 4 #define PW_ACCT_SESSION_TIMEOUT 5 #define PW_ADMIN_RESET 6 #define PW_ADMIN_REBOOT 7 #define PW_PORT_ERROR 8 #define PW_NAS_ERROR 9 #define PW_NAS_REQUEST 10 #define PW_NAS_REBOOT 11 #define PW_PORT_UNNEEDED 12 #define PW_PORT_PREEMPTED 13 #define PW_PORT_SUSPENDED 14 #define PW_SERVICE_UNAVAILABLE 15 #define PW_CALLBACK 16 #define PW_USER_ERROR 17 #define PW_HOST_REQUEST 18 /* NAS PORT TYPES */ #define PW_ASYNC 0 #define PW_SYNC 1 #define PW_ISDN_SYNC 2 #define PW_ISDN_SYNC_V120 3 #define PW_ISDN_SYNC_V110 4 #define PW_VIRTUAL 5 /* AUTHENTIC TYPES */ #define PW_RADIUS 1 #define PW_LOCAL 2 #define PW_REMOTE 3 /* Session-Octets-Limit */ #define PW_OCTETS_DIRECTION_SUM 0 #define PW_OCTETS_DIRECTION_IN 1 #define PW_OCTETS_DIRECTION_OUT 2 #define PW_OCTETS_DIRECTION_MAX 3 /* Vendor codes */ #define VENDOR_NONE (-1) #define VENDOR_MICROSOFT 311 /* Server data structures */ typedef struct dict_attr { char name[NAME_LENGTH + 1]; /* attribute name */ int value; /* attribute index */ int type; /* string, int, etc. */ int vendorcode; /* vendor code */ struct dict_attr *next; } DICT_ATTR; typedef struct dict_value { char attrname[NAME_LENGTH +1]; char name[NAME_LENGTH + 1]; int value; struct dict_value *next; } DICT_VALUE; typedef struct vendor_dict { char vendorname[NAME_LENGTH + 1]; int vendorcode; DICT_ATTR *attributes; struct vendor_dict *next; } VENDOR_DICT; typedef struct value_pair { char name[NAME_LENGTH + 1]; int attribute; int vendorcode; int type; UINT4 lvalue; u_char strvalue[AUTH_STRING_LEN + 1]; struct value_pair *next; } VALUE_PAIR; /* don't change this, as it has to be the same as in the Merit radiusd code */ #define MGMT_POLL_SECRET "Hardlyasecret" /* Define return codes from "SendServer" utility */ #define BADRESP_RC -2 #define ERROR_RC -1 #define OK_RC 0 #define TIMEOUT_RC 1 typedef struct send_data /* Used to pass information to sendserver() function */ { u_char code; /* RADIUS packet code */ u_char seq_nbr; /* Packet sequence number */ char *server; /* Name/addrress of RADIUS server */ int svc_port; /* RADIUS protocol destination port */ int timeout; /* Session timeout in seconds */ int retries; VALUE_PAIR *send_pairs; /* More a/v pairs to send */ VALUE_PAIR *receive_pairs; /* Where to place received a/v pairs */ } SEND_DATA; typedef struct request_info { char secret[MAX_SECRET_LENGTH + 1]; u_char request_vector[AUTH_VECTOR_LEN]; } REQUEST_INFO; #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifndef PATH_MAX #define PATH_MAX 1024 #endif typedef struct env { int maxsize, size; char **env; } ENV; #define ENV_SIZE 128 /* Function prototypes */ /* avpair.c */ VALUE_PAIR *rc_avpair_add __P((VALUE_PAIR **, int, void *, int, int)); int rc_avpair_assign __P((VALUE_PAIR *, void *, int)); VALUE_PAIR *rc_avpair_new __P((int, void *, int, int)); VALUE_PAIR *rc_avpair_gen __P((AUTH_HDR *)); VALUE_PAIR *rc_avpair_get __P((VALUE_PAIR *, UINT4)); VALUE_PAIR *rc_avpair_copy __P((VALUE_PAIR *)); void rc_avpair_insert __P((VALUE_PAIR **, VALUE_PAIR *, VALUE_PAIR *)); void rc_avpair_free __P((VALUE_PAIR *)); int rc_avpair_parse __P((char *, VALUE_PAIR **)); int rc_avpair_tostr __P((VALUE_PAIR *, char *, int, char *, int)); VALUE_PAIR *rc_avpair_readin __P((FILE *)); /* buildreq.c */ void rc_buildreq __P((SEND_DATA *, int, char *, unsigned short, int, int)); unsigned char rc_get_seqnbr __P((void)); int rc_auth __P((UINT4, VALUE_PAIR *, VALUE_PAIR **, char *, REQUEST_INFO *)); int rc_auth_using_server __P((SERVER *, UINT4, VALUE_PAIR *, VALUE_PAIR **, char *, REQUEST_INFO *)); int rc_auth_proxy __P((VALUE_PAIR *, VALUE_PAIR **, char *)); int rc_acct __P((UINT4, VALUE_PAIR *)); int rc_acct_using_server __P((SERVER *, UINT4, VALUE_PAIR *)); int rc_acct_proxy __P((VALUE_PAIR *)); int rc_check __P((char *, unsigned short, char *)); /* clientid.c */ int rc_read_mapfile __P((char *)); UINT4 rc_map2id __P((char *)); /* config.c */ int rc_read_config __P((char *)); char *rc_conf_str __P((char *)); int rc_conf_int __P((char *)); SERVER *rc_conf_srv __P((char *)); int rc_find_server __P((char *, UINT4 *, char *)); /* dict.c */ int rc_read_dictionary __P((char *)); DICT_ATTR *rc_dict_getattr __P((int, int)); DICT_ATTR *rc_dict_findattr __P((char *)); DICT_VALUE *rc_dict_findval __P((char *)); DICT_VALUE * rc_dict_getval __P((UINT4, char *)); VENDOR_DICT * rc_dict_findvendor __P((char *)); VENDOR_DICT * rc_dict_getvendor __P((int)); /* ip_util.c */ UINT4 rc_get_ipaddr __P((char *)); int rc_good_ipaddr __P((char *)); const char *rc_ip_hostname __P((UINT4)); UINT4 rc_own_ipaddress __P((void)); /* sendserver.c */ int rc_send_server __P((SEND_DATA *, char *, REQUEST_INFO *)); /* util.c */ void rc_str2tm __P((char *, struct tm *)); char *rc_mksid __P((void)); void rc_mdelay __P((int)); /* md5.c */ void rc_md5_calc __P((unsigned char *, unsigned char *, unsigned int)); #endif /* RADIUSCLIENT_H */ ppp-2.4.5/pppd/plugins/radius/radrealms.c000066400000000000000000000064301130035057700203560ustar00rootroot00000000000000/* * * radrealms.c * * A pppd plugin which is stacked on top of radius.so. This plugin * allows selection of alternate set of servers based on the user's realm. * * Author: Ben McKeegan ben@netservers.co.uk * * Copyright (C) 2002 Netservers * * This plugin may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * */ static char const RCSID[] = "$Id: radrealms.c,v 1.2 2004/11/14 07:26:26 paulus Exp $"; #include "pppd.h" #include "radiusclient.h" #include #include #include char pppd_version[] = VERSION; char radrealms_config[MAXPATHLEN] = "/etc/radiusclient/realms"; static option_t Options[] = { { "realms-config-file", o_string, &radrealms_config }, { NULL } }; extern void (*radius_pre_auth_hook)(char const *user, SERVER **authserver, SERVER **acctserver); static void lookup_realm(char const *user, SERVER **authserver, SERVER **acctserver) { char *realm; FILE *fd; SERVER *accts, *auths, *s; char buffer[512], *p; int line = 0; auths = (SERVER *) malloc(sizeof(SERVER)); auths->max = 0; accts = (SERVER *) malloc(sizeof(SERVER)); accts->max = 0; realm = strrchr(user, '@'); if (realm) { info("Looking up servers for realm '%s'", realm); } else { info("Looking up servers for DEFAULT realm"); } if (realm) { if (*(++realm) == '\0') { realm = NULL; } } if ((fd = fopen(radrealms_config, "r")) == NULL) { option_error("cannot open %s", radrealms_config); return; } info("Reading %s", radrealms_config); while ((fgets(buffer, sizeof(buffer), fd) != NULL)) { line++; if ((*buffer == '\n') || (*buffer == '#') || (*buffer == '\0')) continue; buffer[strlen(buffer)-1] = '\0'; p = strtok(buffer, "\t "); if (p == NULL || (strcmp(p, "authserver") !=0 && strcmp(p, "acctserver"))) { fclose(fd); option_error("%s: invalid line %d: %s", radrealms_config, line, buffer); return; } info("Parsing '%s' entry:", p); s = auths; if (p[1] == 'c') { s = accts; } if (s->max >= SERVER_MAX) continue; if ((p = strtok(NULL, "\t ")) == NULL) { fclose(fd); option_error("%s: realm name missing on line %d: %s", radrealms_config, line, buffer); return; } if ((realm != NULL && strcmp(p, realm) == 0) || (realm == NULL && strcmp(p, "DEFAULT") == 0) ) { info(" - Matched realm %s", p); if ((p = strtok(NULL, ":")) == NULL) { fclose(fd); option_error("%s: server address missing on line %d: %s", radrealms_config, line, buffer); return; } s->name[s->max] = strdup(p); info(" - Address is '%s'",p); if ((p = strtok(NULL, "\t ")) == NULL) { fclose(fd); option_error("%s: server port missing on line %d: %s", radrealms_config, line, buffer); return; } s->port[s->max] = atoi(p); info(" - Port is '%d'", s->port[s->max]); s->max++; } else info(" - Skipping realm '%s'", p); } fclose(fd); if (accts->max) *acctserver = accts; if (auths->max) *authserver = auths; return; } void plugin_init(void) { radius_pre_auth_hook = lookup_realm; add_options(Options); info("RADIUS Realms plugin initialized."); } ppp-2.4.5/pppd/plugins/radius/sendserver.c000066400000000000000000000320401130035057700205600ustar00rootroot00000000000000/* * $Id: sendserver.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1995,1996,1997 Lars Fenneberg * * Copyright 1992 Livingston Enterprises, Inc. * * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan * and Merit Network, Inc. All Rights Reserved * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include #include #include static void rc_random_vector (unsigned char *); static int rc_check_reply (AUTH_HDR *, int, char *, unsigned char *, unsigned char); /* * Function: rc_pack_list * * Purpose: Packs an attribute value pair list into a buffer. * * Returns: Number of octets packed. * */ static int rc_pack_list (VALUE_PAIR *vp, char *secret, AUTH_HDR *auth) { int length, i, pc, secretlen, padded_length; int total_length = 0; UINT4 lvalue; unsigned char passbuf[MAX(AUTH_PASS_LEN, CHAP_VALUE_LENGTH)]; unsigned char md5buf[256]; unsigned char *buf, *vector, *lenptr; buf = auth->data; while (vp != (VALUE_PAIR *) NULL) { if (vp->vendorcode != VENDOR_NONE) { *buf++ = PW_VENDOR_SPECIFIC; /* Place-holder for where to put length */ lenptr = buf++; /* Insert vendor code */ *buf++ = 0; *buf++ = (((unsigned int) vp->vendorcode) >> 16) & 255; *buf++ = (((unsigned int) vp->vendorcode) >> 8) & 255; *buf++ = ((unsigned int) vp->vendorcode) & 255; /* Insert vendor-type */ *buf++ = vp->attribute; /* Insert value */ switch(vp->type) { case PW_TYPE_STRING: length = vp->lvalue; *lenptr = length + 8; *buf++ = length+2; memcpy(buf, vp->strvalue, (size_t) length); buf += length; total_length += length+8; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: length = sizeof(UINT4); *lenptr = length + 8; *buf++ = length+2; lvalue = htonl(vp->lvalue); memcpy(buf, (char *) &lvalue, sizeof(UINT4)); buf += length; total_length += length+8; break; default: break; } } else { *buf++ = vp->attribute; switch (vp->attribute) { case PW_USER_PASSWORD: /* Encrypt the password */ /* Chop off password at AUTH_PASS_LEN */ length = vp->lvalue; if (length > AUTH_PASS_LEN) length = AUTH_PASS_LEN; /* Calculate the padded length */ padded_length = (length+(AUTH_VECTOR_LEN-1)) & ~(AUTH_VECTOR_LEN-1); /* Record the attribute length */ *buf++ = padded_length + 2; /* Pad the password with zeros */ memset ((char *) passbuf, '\0', AUTH_PASS_LEN); memcpy ((char *) passbuf, vp->strvalue, (size_t) length); secretlen = strlen (secret); vector = (char *)auth->vector; for(i = 0; i < padded_length; i += AUTH_VECTOR_LEN) { /* Calculate the MD5 digest*/ strcpy ((char *) md5buf, secret); memcpy ((char *) md5buf + secretlen, vector, AUTH_VECTOR_LEN); rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN); /* Remeber the start of the digest */ vector = buf; /* Xor the password into the MD5 digest */ for (pc = i; pc < (i + AUTH_VECTOR_LEN); pc++) { *buf++ ^= passbuf[pc]; } } total_length += padded_length + 2; break; #if 0 case PW_CHAP_PASSWORD: *buf++ = CHAP_VALUE_LENGTH + 2; /* Encrypt the Password */ length = vp->lvalue; if (length > CHAP_VALUE_LENGTH) { length = CHAP_VALUE_LENGTH; } memset ((char *) passbuf, '\0', CHAP_VALUE_LENGTH); memcpy ((char *) passbuf, vp->strvalue, (size_t) length); /* Calculate the MD5 Digest */ secretlen = strlen (secret); strcpy ((char *) md5buf, secret); memcpy ((char *) md5buf + secretlen, (char *) auth->vector, AUTH_VECTOR_LEN); rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN); /* Xor the password into the MD5 digest */ for (i = 0; i < CHAP_VALUE_LENGTH; i++) { *buf++ ^= passbuf[i]; } total_length += CHAP_VALUE_LENGTH + 2; break; #endif default: switch (vp->type) { case PW_TYPE_STRING: length = vp->lvalue; *buf++ = length + 2; memcpy (buf, vp->strvalue, (size_t) length); buf += length; total_length += length + 2; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: *buf++ = sizeof (UINT4) + 2; lvalue = htonl (vp->lvalue); memcpy (buf, (char *) &lvalue, sizeof (UINT4)); buf += sizeof (UINT4); total_length += sizeof (UINT4) + 2; break; default: break; } break; } } vp = vp->next; } return total_length; } /* * Function: rc_send_server * * Purpose: send a request to a RADIUS server and wait for the reply * */ int rc_send_server (SEND_DATA *data, char *msg, REQUEST_INFO *info) { int sockfd; struct sockaddr salocal; struct sockaddr saremote; struct sockaddr_in *sin; struct timeval authtime; fd_set readfds; AUTH_HDR *auth, *recv_auth; UINT4 auth_ipaddr; char *server_name; /* Name of server to query */ int salen; int result; int total_length; int length; int retry_max; int secretlen; char secret[MAX_SECRET_LENGTH + 1]; unsigned char vector[AUTH_VECTOR_LEN]; char recv_buffer[BUFFER_LEN]; char send_buffer[BUFFER_LEN]; int retries; VALUE_PAIR *vp; server_name = data->server; if (server_name == (char *) NULL || server_name[0] == '\0') return (ERROR_RC); if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE)) && \ (vp->lvalue == PW_ADMINISTRATIVE)) { strcpy(secret, MGMT_POLL_SECRET); if ((auth_ipaddr = rc_get_ipaddr(server_name)) == 0) return (ERROR_RC); } else { if (rc_find_server (server_name, &auth_ipaddr, secret) != 0) { return (ERROR_RC); } } sockfd = socket (AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { memset (secret, '\0', sizeof (secret)); error("rc_send_server: socket: %s", strerror(errno)); return (ERROR_RC); } length = sizeof (salocal); sin = (struct sockaddr_in *) & salocal; memset ((char *) sin, '\0', (size_t) length); sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(INADDR_ANY); sin->sin_port = htons ((unsigned short) 0); if (bind (sockfd, (struct sockaddr *) sin, length) < 0 || getsockname (sockfd, (struct sockaddr *) sin, &length) < 0) { close (sockfd); memset (secret, '\0', sizeof (secret)); error("rc_send_server: bind: %s: %m", server_name); return (ERROR_RC); } retry_max = data->retries; /* Max. numbers to try for reply */ retries = 0; /* Init retry cnt for blocking call */ /* Build a request */ auth = (AUTH_HDR *) send_buffer; auth->code = data->code; auth->id = data->seq_nbr; if (data->code == PW_ACCOUNTING_REQUEST) { total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons ((unsigned short) total_length); memset((char *) auth->vector, 0, AUTH_VECTOR_LEN); secretlen = strlen (secret); memcpy ((char *) auth + total_length, secret, secretlen); rc_md5_calc (vector, (char *) auth, total_length + secretlen); memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); } else { rc_random_vector (vector); memcpy (auth->vector, vector, AUTH_VECTOR_LEN); total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons ((unsigned short) total_length); } sin = (struct sockaddr_in *) & saremote; memset ((char *) sin, '\0', sizeof (saremote)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl (auth_ipaddr); sin->sin_port = htons ((unsigned short) data->svc_port); for (;;) { sendto (sockfd, (char *) auth, (unsigned int) total_length, (int) 0, (struct sockaddr *) sin, sizeof (struct sockaddr_in)); authtime.tv_usec = 0L; authtime.tv_sec = (long) data->timeout; FD_ZERO (&readfds); FD_SET (sockfd, &readfds); if (select (sockfd + 1, &readfds, NULL, NULL, &authtime) < 0) { if (errno == EINTR) continue; error("rc_send_server: select: %m"); memset (secret, '\0', sizeof (secret)); close (sockfd); return (ERROR_RC); } if (FD_ISSET (sockfd, &readfds)) break; /* * Timed out waiting for response. Retry "retry_max" times * before giving up. If retry_max = 0, don't retry at all. */ if (++retries >= retry_max) { error("rc_send_server: no reply from RADIUS server %s:%u", rc_ip_hostname (auth_ipaddr), data->svc_port); close (sockfd); memset (secret, '\0', sizeof (secret)); return (TIMEOUT_RC); } } salen = sizeof (saremote); length = recvfrom (sockfd, (char *) recv_buffer, (int) sizeof (recv_buffer), (int) 0, &saremote, &salen); if (length <= 0) { error("rc_send_server: recvfrom: %s:%d: %m", server_name,\ data->svc_port); close (sockfd); memset (secret, '\0', sizeof (secret)); return (ERROR_RC); } recv_auth = (AUTH_HDR *)recv_buffer; result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr); data->receive_pairs = rc_avpair_gen(recv_auth); close (sockfd); if (info) { memcpy(info->secret, secret, sizeof(info->secret)); memcpy(info->request_vector, vector, sizeof(info->request_vector)); } memset (secret, '\0', sizeof (secret)); if (result != OK_RC) return (result); *msg = '\0'; vp = data->receive_pairs; while (vp) { if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE))) { strcat(msg, vp->strvalue); strcat(msg, "\n"); vp = vp->next; } } if ((recv_auth->code == PW_ACCESS_ACCEPT) || (recv_auth->code == PW_PASSWORD_ACK) || (recv_auth->code == PW_ACCOUNTING_RESPONSE)) { result = OK_RC; } else { result = BADRESP_RC; } return (result); } /* * Function: rc_check_reply * * Purpose: verify items in returned packet. * * Returns: OK_RC -- upon success, * BADRESP_RC -- if anything looks funny. * */ static int rc_check_reply (AUTH_HDR *auth, int bufferlen, char *secret, unsigned char *vector, unsigned char seq_nbr) { int secretlen; int totallen; unsigned char calc_digest[AUTH_VECTOR_LEN]; unsigned char reply_digest[AUTH_VECTOR_LEN]; totallen = ntohs (auth->length); secretlen = strlen (secret); /* Do sanity checks on packet length */ if ((totallen < 20) || (totallen > 4096)) { error("rc_check_reply: received RADIUS server response with invalid length"); return (BADRESP_RC); } /* Verify buffer space, should never trigger with current buffer size and check above */ if ((totallen + secretlen) > bufferlen) { error("rc_check_reply: not enough buffer space to verify RADIUS server response"); return (BADRESP_RC); } /* Verify that id (seq. number) matches what we sent */ if (auth->id != seq_nbr) { error("rc_check_reply: received non-matching id in RADIUS server response"); return (BADRESP_RC); } /* Verify the reply digest */ memcpy ((char *) reply_digest, (char *) auth->vector, AUTH_VECTOR_LEN); memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); memcpy ((char *) auth + totallen, secret, secretlen); rc_md5_calc (calc_digest, (char *) auth, totallen + secretlen); #ifdef DIGEST_DEBUG { int i; fputs("reply_digest: ", stderr); for (i = 0; i < AUTH_VECTOR_LEN; i++) { fprintf(stderr,"%.2x ", (int) reply_digest[i]); } fputs("\ncalc_digest: ", stderr); for (i = 0; i < AUTH_VECTOR_LEN; i++) { fprintf(stderr,"%.2x ", (int) calc_digest[i]); } fputs("\n", stderr); } #endif if (memcmp ((char *) reply_digest, (char *) calc_digest, AUTH_VECTOR_LEN) != 0) { #ifdef RADIUS_116 /* the original Livingston radiusd v1.16 seems to have a bug in digest calculation with accounting requests, authentication request are ok. i looked at the code but couldn't find any bugs. any help to get this kludge out are welcome. preferably i want to reproduce the calculation bug here to be compatible to stock Livingston radiusd v1.16. -lf, 03/14/96 */ if (auth->code == PW_ACCOUNTING_RESPONSE) return (OK_RC); #endif error("rc_check_reply: received invalid reply digest from RADIUS server"); return (BADRESP_RC); } return (OK_RC); } /* * Function: rc_random_vector * * Purpose: generates a random vector of AUTH_VECTOR_LEN octets. * * Returns: the vector (call by reference) * */ static void rc_random_vector (unsigned char *vector) { int randno; int i; int fd; /* well, I added this to increase the security for user passwords. we use /dev/urandom here, as /dev/random might block and we don't need that much randomness. BTW, great idea, Ted! -lf, 03/18/95 */ if ((fd = open(_PATH_DEV_URANDOM, O_RDONLY)) >= 0) { unsigned char *pos; int readcount; i = AUTH_VECTOR_LEN; pos = vector; while (i > 0) { readcount = read(fd, (char *)pos, i); pos += readcount; i -= readcount; } close(fd); return; } /* else fall through */ for (i = 0; i < AUTH_VECTOR_LEN;) { randno = magic(); memcpy ((char *) vector, (char *) &randno, sizeof (int)); vector += sizeof (int); i += sizeof (int); } return; } ppp-2.4.5/pppd/plugins/radius/util.c000066400000000000000000000030601130035057700173550ustar00rootroot00000000000000/* * $Id: util.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ * * Copyright (C) 1995,1996,1997 Lars Fenneberg * * Copyright 1992 Livingston Enterprises, Inc. * * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan * and Merit Network, Inc. All Rights Reserved * * See the file COPYRIGHT for the respective terms and conditions. * If the file is missing contact me at lf@elemental.net * and I'll send you a copy. * */ #include #include /* * Function: rc_str2tm * * Purpose: Turns printable string into correct tm struct entries. * */ static const char * months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; void rc_str2tm (char *valstr, struct tm *tm) { int i; /* Get the month */ for (i = 0; i < 12; i++) { if (strncmp (months[i], valstr, 3) == 0) { tm->tm_mon = i; i = 13; } } /* Get the Day */ tm->tm_mday = atoi (&valstr[4]); /* Now the year */ tm->tm_year = atoi (&valstr[7]) - 1900; } void rc_mdelay(int msecs) { struct timeval tv; tv.tv_sec = (int) msecs / 1000; tv.tv_usec = (msecs % 1000) * 1000; select(0,(fd_set *)NULL,(fd_set *)NULL,(fd_set *)NULL, &tv); } /* * Function: rc_mksid * * Purpose: generate a quite unique string * * Remarks: not that unique at all... * */ char * rc_mksid (void) { static char buf[15]; static unsigned short int cnt = 0; sprintf (buf, "%08lX%04X%02hX", (unsigned long int) time (NULL), (unsigned int) getpid (), cnt & 0xFF); cnt++; return buf; } ppp-2.4.5/pppd/plugins/rp-pppoe/000077500000000000000000000000001130035057700165105ustar00rootroot00000000000000ppp-2.4.5/pppd/plugins/rp-pppoe/.gitignore000066400000000000000000000000201130035057700204700ustar00rootroot00000000000000pppoe-discovery ppp-2.4.5/pppd/plugins/rp-pppoe/Makefile.linux000066400000000000000000000035241130035057700213120ustar00rootroot00000000000000# Generated automatically from Makefile.in by configure. #*********************************************************************** # # Makefile # # Makefile for Roaring Penguin's Linux PPPoE plugin. # Modified for integration with pppd sources by Paul Mackerras. # # Copyright (C) 2001 Roaring Penguin Software Inc. # # This program may be distributed according to the terms of the GNU # General Public License, version 2 or (at your option) any later version. # # $Id: Makefile.linux,v 1.8 2008/06/09 08:34:23 paulus Exp $ #*********************************************************************** DESTDIR = $(INSTROOT)@DESTDIR@ BINDIR = $(DESTDIR)/sbin LIBDIR = $(DESTDIR)/lib/pppd/$(PPPDVERSION) PPPDVERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h) INSTALL = install # Version is set ONLY IN THE MAKEFILE! Don't delete this! RP_VERSION=3.8p COPTS=-O2 -g CFLAGS=$(COPTS) -I../../../include '-DRP_VERSION="$(RP_VERSION)"' all: rp-pppoe.so pppoe-discovery pppoe-discovery: pppoe-discovery.o debug.o $(CC) -o pppoe-discovery pppoe-discovery.o debug.o pppoe-discovery.o: pppoe-discovery.c $(CC) $(CFLAGS) -c -o pppoe-discovery.o pppoe-discovery.c debug.o: debug.c $(CC) $(CFLAGS) -c -o debug.o debug.c rp-pppoe.so: plugin.o discovery.o if.o common.o $(CC) -o rp-pppoe.so -shared plugin.o discovery.o if.o common.o install: all $(INSTALL) -d -m 755 $(LIBDIR) $(INSTALL) -s -c -m 4550 rp-pppoe.so $(LIBDIR) $(INSTALL) -d -m 755 $(BINDIR) $(INSTALL) -s -c -m 555 pppoe-discovery $(BINDIR) clean: rm -f *.o *.so pppoe-discovery plugin.o: plugin.c $(CC) $(CFLAGS) -I../../.. -c -o plugin.o -fPIC plugin.c discovery.o: discovery.c $(CC) $(CFLAGS) -I../../.. -c -o discovery.o -fPIC discovery.c if.o: if.c $(CC) $(CFLAGS) -I../../.. -c -o if.o -fPIC if.c common.o: common.c $(CC) $(CFLAGS) -I../../.. -c -o common.o -fPIC common.c ppp-2.4.5/pppd/plugins/rp-pppoe/common.c000066400000000000000000000176531130035057700201600ustar00rootroot00000000000000/*********************************************************************** * * common.c * * Implementation of user-space PPPoE redirector for Linux. * * Common functions used by PPPoE client and server * * Copyright (C) 2000 by Roaring Penguin Software Inc. * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * ***********************************************************************/ static char const RCSID[] = "$Id: common.c,v 1.3 2008/06/09 08:34:23 paulus Exp $"; #define _GNU_SOURCE 1 #include "pppoe.h" #include "pppd/pppd.h" #include #include #include #include /* for LOG_DEBUG */ #ifdef HAVE_UNISTD_H #include #endif /********************************************************************** *%FUNCTION: parsePacket *%ARGUMENTS: * packet -- the PPPoE discovery packet to parse * func -- function called for each tag in the packet * extra -- an opaque data pointer supplied to parsing function *%RETURNS: * 0 if everything went well; -1 if there was an error *%DESCRIPTION: * Parses a PPPoE discovery packet, calling "func" for each tag in the packet. * "func" is passed the additional argument "extra". ***********************************************************************/ int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra) { UINT16_t len = ntohs(packet->length); unsigned char *curTag; UINT16_t tagType, tagLen; if (PPPOE_VER(packet->vertype) != 1) { error("Invalid PPPoE version (%d)", PPPOE_VER(packet->vertype)); return -1; } if (PPPOE_TYPE(packet->vertype) != 1) { error("Invalid PPPoE type (%d)", PPPOE_TYPE(packet->vertype)); return -1; } /* Do some sanity checks on packet */ if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */ error("Invalid PPPoE packet length (%u)", len); return -1; } /* Step through the tags */ curTag = packet->payload; while(curTag - packet->payload < len) { /* Alignment is not guaranteed, so do this by hand... */ tagType = (curTag[0] << 8) + curTag[1]; tagLen = (curTag[2] << 8) + curTag[3]; if (tagType == TAG_END_OF_LIST) { return 0; } if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { error("Invalid PPPoE tag length (%u)", tagLen); return -1; } func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); curTag = curTag + TAG_HDR_SIZE + tagLen; } return 0; } /*********************************************************************** *%FUNCTION: sendPADT *%ARGUMENTS: * conn -- PPPoE connection * msg -- if non-NULL, extra error message to include in PADT packet. *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADT packet ***********************************************************************/ void sendPADT(PPPoEConnection *conn, char const *msg) { PPPoEPacket packet; unsigned char *cursor = packet.payload; UINT16_t plen = 0; /* Do nothing if no session established yet */ if (!conn->session) return; /* Do nothing if no discovery socket */ if (conn->discoverySocket < 0) return; memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_PADT; packet.session = conn->session; /* Reset Session to zero so there is no possibility of recursive calls to this function by any signal handler */ conn->session = 0; /* If we're using Host-Uniq, copy it over */ if (conn->useHostUniq) { PPPoETag hostUniq; pid_t pid = getpid(); hostUniq.type = htons(TAG_HOST_UNIQ); hostUniq.length = htons(sizeof(pid)); memcpy(hostUniq.payload, &pid, sizeof(pid)); memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); cursor += sizeof(pid) + TAG_HDR_SIZE; plen += sizeof(pid) + TAG_HDR_SIZE; } /* Copy error message */ if (msg) { PPPoETag err; size_t elen = strlen(msg); err.type = htons(TAG_GENERIC_ERROR); err.length = htons(elen); strcpy(err.payload, msg); memcpy(cursor, &err, elen + TAG_HDR_SIZE); cursor += elen + TAG_HDR_SIZE; plen += elen + TAG_HDR_SIZE; } /* Copy cookie and relay-ID if needed */ if (conn->cookie.type) { CHECK_ROOM(cursor, packet.payload, ntohs(conn->cookie.length) + TAG_HDR_SIZE); memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; } if (conn->relayId.type) { CHECK_ROOM(cursor, packet.payload, ntohs(conn->relayId.length) + TAG_HDR_SIZE); memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; } packet.length = htons(plen); sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); info("Sent PADT"); } #define EH(x) (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5] /* Print out a PPPOE packet for debugging */ void pppoe_printpkt(PPPoEPacket *packet, void (*printer)(void *, char *, ...), void *arg) { int len = ntohs(packet->length); int i, tag, tlen, text; switch (ntohs(packet->ethHdr.h_proto)) { case ETH_PPPOE_DISCOVERY: printer(arg, "PPPOE Discovery V%dT%d ", PPPOE_VER(packet->vertype), PPPOE_TYPE(packet->vertype)); switch (packet->code) { case CODE_PADI: printer(arg, "PADI"); break; case CODE_PADO: printer(arg, "PADO"); break; case CODE_PADR: printer(arg, "PADR"); break; case CODE_PADS: printer(arg, "PADS"); break; case CODE_PADT: printer(arg, "PADT"); break; default: printer(arg, "unknown code %x", packet->code); } printer(arg, " session 0x%x length %d\n", ntohs(packet->session), len); break; case ETH_PPPOE_SESSION: printer(arg, "PPPOE Session V%dT%d", PPPOE_VER(packet->vertype), PPPOE_TYPE(packet->vertype)); printer(arg, " code 0x%x session 0x%x length %d\n", packet->code, ntohs(packet->session), len); break; default: printer(arg, "Unknown ethernet frame with proto = 0x%x\n", ntohs(packet->ethHdr.h_proto)); } printer(arg, " dst %x:%x:%x:%x:%x:%x ", EH(packet->ethHdr.h_dest)); printer(arg, " src %x:%x:%x:%x:%x:%x\n", EH(packet->ethHdr.h_source)); if (ntohs(packet->ethHdr.h_proto) != ETH_PPPOE_DISCOVERY) return; for (i = 0; i + TAG_HDR_SIZE <= len; i += tlen) { tag = (packet->payload[i] << 8) + packet->payload[i+1]; tlen = (packet->payload[i+2] << 8) + packet->payload[i+3]; if (i + tlen + TAG_HDR_SIZE > len) break; text = 0; i += TAG_HDR_SIZE; printer(arg, " ["); switch (tag) { case TAG_END_OF_LIST: printer(arg, "end-of-list"); break; case TAG_SERVICE_NAME: printer(arg, "service-name"); text = 1; break; case TAG_AC_NAME: printer(arg, "AC-name"); text = 1; break; case TAG_HOST_UNIQ: printer(arg, "host-uniq"); break; case TAG_AC_COOKIE: printer(arg, "AC-cookie"); break; case TAG_VENDOR_SPECIFIC: printer(arg, "vendor-specific"); break; case TAG_RELAY_SESSION_ID: printer(arg, "relay-session-id"); break; case TAG_SERVICE_NAME_ERROR: printer(arg, "service-name-error"); text = 1; break; case TAG_AC_SYSTEM_ERROR: printer(arg, "AC-system-error"); text = 1; break; case TAG_GENERIC_ERROR: printer(arg, "generic-error"); text = 1; break; default: printer(arg, "unknown tag 0x%x", tag); } if (tlen) { if (text) printer(arg, " %.*v", tlen, &packet->payload[i]); else if (tlen <= 32) printer(arg, " %.*B", tlen, &packet->payload[i]); else printer(arg, " %.32B... (length %d)", &packet->payload[i], tlen); } printer(arg, "]"); } printer(arg, "\n"); } void pppoe_log_packet(const char *prefix, PPPoEPacket *packet) { init_pr_log(prefix, LOG_DEBUG); pppoe_printpkt(packet, pr_log, NULL); end_pr_log(); } ppp-2.4.5/pppd/plugins/rp-pppoe/config.h000066400000000000000000000074001130035057700201270ustar00rootroot00000000000000/* config.h. Generated automatically by configure. */ /* config.h.in. Generated automatically from configure.in by autoheader. */ /* Define to empty if the keyword does not work. */ /* #undef const */ /* Define if you have that is POSIX.1 compatible. */ #define HAVE_SYS_WAIT_H 1 /* Define to `int' if doesn't define. */ /* #undef pid_t */ /* Define as the return type of signal handlers (int or void). */ #define RETSIGTYPE void /* Define if the setvbuf function takes the buffering type as its second argument and the buffer pointer as the third, as on System V before release 3. */ /* #undef SETVBUF_REVERSED */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you can safely include both and . */ #define TIME_WITH_SYS_TIME 1 /* Define if your declares struct tm. */ /* #undef TM_IN_SYS_TIME */ #define HAVE_STRUCT_SOCKADDR_LL 1 /* The number of bytes in a unsigned int. */ #define SIZEOF_UNSIGNED_INT 4 /* The number of bytes in a unsigned long. */ #define SIZEOF_UNSIGNED_LONG 4 /* The number of bytes in a unsigned short. */ #define SIZEOF_UNSIGNED_SHORT 2 /* Define if you have the select function. */ #define HAVE_SELECT 1 /* Define if you have the socket function. */ #define HAVE_SOCKET 1 /* Define if you have the strerror function. */ #define HAVE_STRERROR 1 /* Define if you have the strtol function. */ #define HAVE_STRTOL 1 /* Define if you have the header file. */ #define HAVE_ASM_TYPES_H 1 /* Define if you have the header file. */ #define HAVE_FCNTL_H 1 /* Define if you have the header file. */ #define HAVE_GETOPT_H 1 /* Define if you have the header file. */ #define HAVE_LINUX_IF_ETHER_H 1 /* Define if you have kernel-mode PPPoE in Linux file. */ #define HAVE_LINUX_KERNEL_PPPOE 1 /* Define if you have the header file. */ #define HAVE_LINUX_IF_PACKET_H 1 /* Define if you have the header file. */ #define HAVE_LINUX_IF_PPPOX_H 1 /* Define if you have the header file. */ #define HAVE_NET_BPF_H 1 /* Define if you have the header file. */ #define HAVE_NET_IF_ARP_H 1 /* Define if you have the header file. */ #define HAVE_NET_ETHERNET_H 1 /* Define if you have the header file. */ #define HAVE_NET_IF_H 1 /* Define if you have the header file. */ #define HAVE_LINUX_IF_H 1 /* Define if you have the header file. */ /* #undef HAVE_NET_IF_DL_H */ /* Define if you have the header file. */ /* #undef HAVE_NET_IF_ETHER_H */ /* Define if you have the header file. */ /* #undef HAVE_NET_IF_TYPES_H */ /* Define if you have the header file. */ #define HAVE_NETINET_IF_ETHER_H 1 /* Define if you have the header file. */ #define HAVE_NETPACKET_PACKET_H 1 /* Define if you have the header file. */ #define HAVE_SYS_CDEFS_H 1 /* Define if you have the header file. */ /* #undef HAVE_SYS_DLPI_H */ /* Define if you have the header file. */ #define HAVE_SYS_IOCTL_H 1 /* Define if you have the header file. */ #define HAVE_SYS_PARAM_H 1 /* Define if you have the header file. */ #define HAVE_SYS_SOCKET_H 1 /* Define if you have the header file. */ #define HAVE_SYS_TIME_H 1 /* Define if you have the header file. */ #define HAVE_SYS_UIO_H 1 /* Define if you have the header file. */ #define HAVE_SYSLOG_H 1 /* Define if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define if you have the N_HDLC line discipline in linux/termios.h */ #define HAVE_N_HDLC 1 ppp-2.4.5/pppd/plugins/rp-pppoe/debug.c000066400000000000000000000077361130035057700177570ustar00rootroot00000000000000/*********************************************************************** * * debug.c * * Implementation of user-space PPPoE redirector for Linux. * * Functions for printing debugging information * * Copyright (C) 2000 by Roaring Penguin Software Inc. * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * ***********************************************************************/ static char const RCSID[] = "$Id: debug.c,v 1.2 2008/06/09 08:34:23 paulus Exp $"; #include "pppoe.h" #include #include #include #include /********************************************************************** *%FUNCTION: dumpHex *%ARGUMENTS: * fp -- file to dump to * buf -- buffer to dump * len -- length of data *%RETURNS: * Nothing *%DESCRIPTION: * Dumps buffer to fp in an easy-to-read format ***********************************************************************/ void dumpHex(FILE *fp, unsigned char const *buf, int len) { int i; int base; if (!fp) return; /* do NOT dump PAP packets */ if (len >= 2 && buf[0] == 0xC0 && buf[1] == 0x23) { fprintf(fp, "(PAP Authentication Frame -- Contents not dumped)\n"); return; } for (base=0; baselength); /* Sheesh... printing times is a pain... */ struct timeval tv; time_t now; int millisec; struct tm *lt; char timebuf[256]; UINT16_t type = etherType(packet); if (!fp) return; gettimeofday(&tv, NULL); now = (time_t) tv.tv_sec; millisec = tv.tv_usec / 1000; lt = localtime(&now); strftime(timebuf, 256, "%H:%M:%S", lt); fprintf(fp, "%s.%03d %s PPPoE ", timebuf, millisec, dir); if (type == Eth_PPPOE_Discovery) { fprintf(fp, "Discovery (%x) ", (unsigned) type); } else if (type == Eth_PPPOE_Session) { fprintf(fp, "Session (%x) ", (unsigned) type); } else { fprintf(fp, "Unknown (%x) ", (unsigned) type); } switch(packet->code) { case CODE_PADI: fprintf(fp, "PADI "); break; case CODE_PADO: fprintf(fp, "PADO "); break; case CODE_PADR: fprintf(fp, "PADR "); break; case CODE_PADS: fprintf(fp, "PADS "); break; case CODE_PADT: fprintf(fp, "PADT "); break; case CODE_PADM: fprintf(fp, "PADM "); break; case CODE_PADN: fprintf(fp, "PADN "); break; case CODE_SESS: fprintf(fp, "SESS "); break; } fprintf(fp, "sess-id %d length %d\n", (int) ntohs(packet->session), len); /* Ugly... I apologize... */ fprintf(fp, "SourceAddr %02x:%02x:%02x:%02x:%02x:%02x " "DestAddr %02x:%02x:%02x:%02x:%02x:%02x\n", (unsigned) packet->ethHdr.h_source[0], (unsigned) packet->ethHdr.h_source[1], (unsigned) packet->ethHdr.h_source[2], (unsigned) packet->ethHdr.h_source[3], (unsigned) packet->ethHdr.h_source[4], (unsigned) packet->ethHdr.h_source[5], (unsigned) packet->ethHdr.h_dest[0], (unsigned) packet->ethHdr.h_dest[1], (unsigned) packet->ethHdr.h_dest[2], (unsigned) packet->ethHdr.h_dest[3], (unsigned) packet->ethHdr.h_dest[4], (unsigned) packet->ethHdr.h_dest[5]); dumpHex(fp, packet->payload, ntohs(packet->length)); } ppp-2.4.5/pppd/plugins/rp-pppoe/discovery.c000066400000000000000000000363431130035057700206740ustar00rootroot00000000000000/*********************************************************************** * * discovery.c * * Perform PPPoE discovery * * Copyright (C) 1999 by Roaring Penguin Software Inc. * ***********************************************************************/ static char const RCSID[] = "$Id: discovery.c,v 1.6 2008/06/15 04:35:50 paulus Exp $"; #define _GNU_SOURCE 1 #include "pppoe.h" #include "pppd/pppd.h" #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef USE_LINUX_PACKET #include #include #endif #include /********************************************************************** *%FUNCTION: parseForHostUniq *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data. * extra -- user-supplied pointer. This is assumed to be a pointer to int. *%RETURNS: * Nothing *%DESCRIPTION: * If a HostUnique tag is found which matches our PID, sets *extra to 1. ***********************************************************************/ static void parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data, void *extra) { int *val = (int *) extra; if (type == TAG_HOST_UNIQ && len == sizeof(pid_t)) { pid_t tmp; memcpy(&tmp, data, len); if (tmp == getpid()) { *val = 1; } } } /********************************************************************** *%FUNCTION: packetIsForMe *%ARGUMENTS: * conn -- PPPoE connection info * packet -- a received PPPoE packet *%RETURNS: * 1 if packet is for this PPPoE daemon; 0 otherwise. *%DESCRIPTION: * If we are using the Host-Unique tag, verifies that packet contains * our unique identifier. ***********************************************************************/ static int packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) { int forMe = 0; /* If packet is not directed to our MAC address, forget it */ if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; /* If we're not using the Host-Unique tag, then accept the packet */ if (!conn->useHostUniq) return 1; parsePacket(packet, parseForHostUniq, &forMe); return forMe; } /********************************************************************** *%FUNCTION: parsePADOTags *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data. Should point to a PacketCriteria structure * which gets filled in according to selected AC name and service * name. *%RETURNS: * Nothing *%DESCRIPTION: * Picks interesting tags out of a PADO packet ***********************************************************************/ static void parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data, void *extra) { struct PacketCriteria *pc = (struct PacketCriteria *) extra; PPPoEConnection *conn = pc->conn; int i; switch(type) { case TAG_AC_NAME: pc->seenACName = 1; if (conn->printACNames) { info("Access-Concentrator: %.*s", (int) len, data); } if (conn->acName && len == strlen(conn->acName) && !strncmp((char *) data, conn->acName, len)) { pc->acNameOK = 1; } break; case TAG_SERVICE_NAME: pc->seenServiceName = 1; if (conn->serviceName && len == strlen(conn->serviceName) && !strncmp((char *) data, conn->serviceName, len)) { pc->serviceNameOK = 1; } break; case TAG_AC_COOKIE: conn->cookie.type = htons(type); conn->cookie.length = htons(len); memcpy(conn->cookie.payload, data, len); break; case TAG_RELAY_SESSION_ID: conn->relayId.type = htons(type); conn->relayId.length = htons(len); memcpy(conn->relayId.payload, data, len); break; case TAG_SERVICE_NAME_ERROR: error("PADO: Service-Name-Error: %.*s", (int) len, data); conn->error = 1; break; case TAG_AC_SYSTEM_ERROR: error("PADO: System-Error: %.*s", (int) len, data); conn->error = 1; break; case TAG_GENERIC_ERROR: error("PADO: Generic-Error: %.*s", (int) len, data); conn->error = 1; break; } } /********************************************************************** *%FUNCTION: parsePADSTags *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data (pointer to PPPoEConnection structure) *%RETURNS: * Nothing *%DESCRIPTION: * Picks interesting tags out of a PADS packet ***********************************************************************/ static void parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data, void *extra) { PPPoEConnection *conn = (PPPoEConnection *) extra; switch(type) { case TAG_SERVICE_NAME: dbglog("PADS: Service-Name: '%.*s'", (int) len, data); break; case TAG_SERVICE_NAME_ERROR: error("PADS: Service-Name-Error: %.*s", (int) len, data); conn->error = 1; break; case TAG_AC_SYSTEM_ERROR: error("PADS: System-Error: %.*s", (int) len, data); conn->error = 1; break; case TAG_GENERIC_ERROR: error("PADS: Generic-Error: %.*s", (int) len, data); conn->error = 1; break; case TAG_RELAY_SESSION_ID: conn->relayId.type = htons(type); conn->relayId.length = htons(len); memcpy(conn->relayId.payload, data, len); break; } } /*********************************************************************** *%FUNCTION: sendPADI *%ARGUMENTS: * conn -- PPPoEConnection structure *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADI packet ***********************************************************************/ static void sendPADI(PPPoEConnection *conn) { PPPoEPacket packet; unsigned char *cursor = packet.payload; PPPoETag *svc = (PPPoETag *) (&packet.payload); UINT16_t namelen = 0; UINT16_t plen; int omit_service_name = 0; if (conn->serviceName) { namelen = (UINT16_t) strlen(conn->serviceName); if (!strcmp(conn->serviceName, "NO-SERVICE-NAME-NON-RFC-COMPLIANT")) { omit_service_name = 1; } } /* Set destination to Ethernet broadcast address */ memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_PADI; packet.session = 0; if (!omit_service_name) { plen = TAG_HDR_SIZE + namelen; CHECK_ROOM(cursor, packet.payload, plen); svc->type = TAG_SERVICE_NAME; svc->length = htons(namelen); if (conn->serviceName) { memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); } cursor += namelen + TAG_HDR_SIZE; } else { plen = 0; } /* If we're using Host-Uniq, copy it over */ if (conn->useHostUniq) { PPPoETag hostUniq; pid_t pid = getpid(); hostUniq.type = htons(TAG_HOST_UNIQ); hostUniq.length = htons(sizeof(pid)); memcpy(hostUniq.payload, &pid, sizeof(pid)); CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE); memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); cursor += sizeof(pid) + TAG_HDR_SIZE; plen += sizeof(pid) + TAG_HDR_SIZE; } packet.length = htons(plen); sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); } /********************************************************************** *%FUNCTION: waitForPADO *%ARGUMENTS: * conn -- PPPoEConnection structure * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADO packet and copies useful information ***********************************************************************/ void waitForPADO(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; PPPoEPacket packet; int len; struct PacketCriteria pc; pc.conn = conn; pc.acNameOK = (conn->acName) ? 0 : 1; pc.serviceNameOK = (conn->serviceName) ? 0 : 1; pc.seenACName = 0; pc.seenServiceName = 0; conn->error = 0; do { if (BPF_BUFFER_IS_EMPTY) { tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&readable); FD_SET(conn->discoverySocket, &readable); while(1) { r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); if (r >= 0 || errno != EINTR) break; } if (r < 0) { error("select (waitForPADO): %m"); return; } if (r == 0) return; /* Timed out */ } /* Get the packet */ receivePacket(conn->discoverySocket, &packet, &len); /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { error("Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); continue; } #ifdef USE_BPF /* If it's not a Discovery packet, loop again */ if (etherType(&packet) != Eth_PPPOE_Discovery) continue; #endif /* If it's not for us, loop again */ if (!packetIsForMe(conn, &packet)) continue; if (packet.code == CODE_PADO) { if (NOT_UNICAST(packet.ethHdr.h_source)) { error("Ignoring PADO packet from non-unicast MAC address"); continue; } if (conn->req_peer && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) { warn("Ignoring PADO packet from wrong MAC address"); continue; } if (parsePacket(&packet, parsePADOTags, &pc) < 0) return; if (conn->error) return; if (!pc.seenACName) { error("Ignoring PADO packet with no AC-Name tag"); continue; } if (!pc.seenServiceName) { error("Ignoring PADO packet with no Service-Name tag"); continue; } conn->numPADOs++; if (pc.acNameOK && pc.serviceNameOK) { memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); conn->discoveryState = STATE_RECEIVED_PADO; break; } } } while (conn->discoveryState != STATE_RECEIVED_PADO); } /*********************************************************************** *%FUNCTION: sendPADR *%ARGUMENTS: * conn -- PPPoE connection structur *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADR packet ***********************************************************************/ static void sendPADR(PPPoEConnection *conn) { PPPoEPacket packet; PPPoETag *svc = (PPPoETag *) packet.payload; unsigned char *cursor = packet.payload; UINT16_t namelen = 0; UINT16_t plen; if (conn->serviceName) { namelen = (UINT16_t) strlen(conn->serviceName); } plen = TAG_HDR_SIZE + namelen; CHECK_ROOM(cursor, packet.payload, plen); memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_PADR; packet.session = 0; svc->type = TAG_SERVICE_NAME; svc->length = htons(namelen); if (conn->serviceName) { memcpy(svc->payload, conn->serviceName, namelen); } cursor += namelen + TAG_HDR_SIZE; /* If we're using Host-Uniq, copy it over */ if (conn->useHostUniq) { PPPoETag hostUniq; pid_t pid = getpid(); hostUniq.type = htons(TAG_HOST_UNIQ); hostUniq.length = htons(sizeof(pid)); memcpy(hostUniq.payload, &pid, sizeof(pid)); CHECK_ROOM(cursor, packet.payload, sizeof(pid)+TAG_HDR_SIZE); memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); cursor += sizeof(pid) + TAG_HDR_SIZE; plen += sizeof(pid) + TAG_HDR_SIZE; } /* Copy cookie and relay-ID if needed */ if (conn->cookie.type) { CHECK_ROOM(cursor, packet.payload, ntohs(conn->cookie.length) + TAG_HDR_SIZE); memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; } if (conn->relayId.type) { CHECK_ROOM(cursor, packet.payload, ntohs(conn->relayId.length) + TAG_HDR_SIZE); memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; } packet.length = htons(plen); sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); } /********************************************************************** *%FUNCTION: waitForPADS *%ARGUMENTS: * conn -- PPPoE connection info * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADS packet and copies useful information ***********************************************************************/ static void waitForPADS(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; PPPoEPacket packet; int len; conn->error = 0; do { if (BPF_BUFFER_IS_EMPTY) { tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&readable); FD_SET(conn->discoverySocket, &readable); while(1) { r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); if (r >= 0 || errno != EINTR) break; } if (r < 0) { error("select (waitForPADS): %m"); return; } if (r == 0) return; } /* Get the packet */ receivePacket(conn->discoverySocket, &packet, &len); /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { error("Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); continue; } #ifdef USE_BPF /* If it's not a Discovery packet, loop again */ if (etherType(&packet) != Eth_PPPOE_Discovery) continue; #endif /* If it's not from the AC, it's not for me */ if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue; /* If it's not for us, loop again */ if (!packetIsForMe(conn, &packet)) continue; /* Is it PADS? */ if (packet.code == CODE_PADS) { /* Parse for goodies */ if (parsePacket(&packet, parsePADSTags, conn) < 0) return; if (conn->error) return; conn->discoveryState = STATE_SESSION; break; } } while (conn->discoveryState != STATE_SESSION); /* Don't bother with ntohs; we'll just end up converting it back... */ conn->session = packet.session; info("PPP session is %d", (int) ntohs(conn->session)); /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */ if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) { error("Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session)); } } /********************************************************************** *%FUNCTION: discovery *%ARGUMENTS: * conn -- PPPoE connection info structure *%RETURNS: * Nothing *%DESCRIPTION: * Performs the PPPoE discovery phase ***********************************************************************/ void discovery(PPPoEConnection *conn) { int padiAttempts = 0; int padrAttempts = 0; int timeout = conn->discoveryTimeout; conn->discoverySocket = openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); do { padiAttempts++; if (padiAttempts > MAX_PADI_ATTEMPTS) { warn("Timeout waiting for PADO packets"); close(conn->discoverySocket); conn->discoverySocket = -1; return; } sendPADI(conn); conn->discoveryState = STATE_SENT_PADI; waitForPADO(conn, timeout); timeout *= 2; } while (conn->discoveryState == STATE_SENT_PADI); timeout = conn->discoveryTimeout; do { padrAttempts++; if (padrAttempts > MAX_PADI_ATTEMPTS) { warn("Timeout waiting for PADS packets"); close(conn->discoverySocket); conn->discoverySocket = -1; return; } sendPADR(conn); conn->discoveryState = STATE_SENT_PADR; waitForPADS(conn, timeout); timeout *= 2; } while (conn->discoveryState == STATE_SENT_PADR); /* We're done. */ conn->discoveryState = STATE_SESSION; return; } ppp-2.4.5/pppd/plugins/rp-pppoe/if.c000066400000000000000000000147401130035057700172600ustar00rootroot00000000000000/*********************************************************************** * * if.c * * Implementation of user-space PPPoE redirector for Linux. * * Functions for opening a raw socket and reading/writing raw Ethernet frames. * * Copyright (C) 2000 by Roaring Penguin Software Inc. * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * ***********************************************************************/ static char const RCSID[] = "$Id: if.c,v 1.2 2008/06/09 08:34:23 paulus Exp $"; #define _GNU_SOURCE 1 #include "pppoe.h" #include "pppd/pppd.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_NETPACKET_PACKET_H #include #elif defined(HAVE_LINUX_IF_PACKET_H) #include #endif #ifdef HAVE_NET_ETHERNET_H #include #endif #ifdef HAVE_ASM_TYPES_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #include #include #include #ifdef HAVE_NET_IF_ARP_H #include #endif /* Initialize frame types to RFC 2516 values. Some broken peers apparently use different frame types... sigh... */ UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; /********************************************************************** *%FUNCTION: etherType *%ARGUMENTS: * packet -- a received PPPoE packet *%RETURNS: * ethernet packet type (see /usr/include/net/ethertypes.h) *%DESCRIPTION: * Checks the ethernet packet header to determine its type. * We should only be receveing DISCOVERY and SESSION types if the BPF * is set up correctly. Logs an error if an unexpected type is received. * Note that the ethernet type names come from "pppoe.h" and the packet * packet structure names use the LINUX dialect to maintain consistency * with the rest of this file. See the BSD section of "pppoe.h" for * translations of the data structure names. ***********************************************************************/ UINT16_t etherType(PPPoEPacket *packet) { UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { error("Invalid ether type 0x%x", type); } return type; } /********************************************************************** *%FUNCTION: openInterface *%ARGUMENTS: * ifname -- name of interface * type -- Ethernet frame type * hwaddr -- if non-NULL, set to the hardware address *%RETURNS: * A raw socket for talking to the Ethernet card. Exits on error. *%DESCRIPTION: * Opens a raw Ethernet socket ***********************************************************************/ int openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) { int optval=1; int fd; struct ifreq ifr; int domain, stype; #ifdef HAVE_STRUCT_SOCKADDR_LL struct sockaddr_ll sa; #else struct sockaddr sa; #endif memset(&sa, 0, sizeof(sa)); #ifdef HAVE_STRUCT_SOCKADDR_LL domain = PF_PACKET; stype = SOCK_RAW; #else domain = PF_INET; stype = SOCK_PACKET; #endif if ((fd = socket(domain, stype, htons(type))) < 0) { /* Give a more helpful message for the common error case */ if (errno == EPERM) { fatal("Cannot create raw socket -- pppoe must be run as root."); } error("Can't open socket for pppoe: %m"); return -1; } if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { error("Can't set socket options for pppoe: %m"); close(fd); return -1; } /* Fill in hardware address */ if (hwaddr) { strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { error("Can't get hardware address for %s: %m", ifname); close(fd); return -1; } memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); #ifdef ARPHRD_ETHER if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { warn("Interface %.16s is not Ethernet", ifname); } #endif if (NOT_UNICAST(hwaddr)) { fatal("Can't use interface %.16s: it has broadcast/multicast MAC address", ifname); } } /* Sanity check on MTU */ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { error("Can't get MTU for %s: %m", ifname); } else if (ifr.ifr_mtu < ETH_DATA_LEN) { error("Interface %.16s has MTU of %d -- should be at least %d.", ifname, ifr.ifr_mtu, ETH_DATA_LEN); error("This may cause serious connection problems."); } #ifdef HAVE_STRUCT_SOCKADDR_LL /* Get interface index */ sa.sll_family = AF_PACKET; sa.sll_protocol = htons(type); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { error("Could not get interface index for %s: %m", ifname); close(fd); return -1; } sa.sll_ifindex = ifr.ifr_ifindex; #else strcpy(sa.sa_data, ifname); #endif /* We're only interested in packets on specified interface */ if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { error("Failed to bind to interface %s: %m", ifname); close(fd); return -1; } return fd; } /*********************************************************************** *%FUNCTION: sendPacket *%ARGUMENTS: * sock -- socket to send to * pkt -- the packet to transmit * size -- size of packet (in bytes) *%RETURNS: * 0 on success; -1 on failure *%DESCRIPTION: * Transmits a packet ***********************************************************************/ int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) { int err; if (debug) pppoe_log_packet("Send ", pkt); #if defined(HAVE_STRUCT_SOCKADDR_LL) err = send(sock, pkt, size, 0); #else struct sockaddr sa; strcpy(sa.sa_data, conn->ifName); err = sendto(sock, pkt, size, 0, &sa, sizeof(sa)); #endif if (err < 0) { error("error sending pppoe packet: %m"); return -1; } return 0; } /*********************************************************************** *%FUNCTION: receivePacket *%ARGUMENTS: * sock -- socket to read from * pkt -- place to store the received packet * size -- set to size of packet in bytes *%RETURNS: * >= 0 if all OK; < 0 if error *%DESCRIPTION: * Receives a packet ***********************************************************************/ int receivePacket(int sock, PPPoEPacket *pkt, int *size) { if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { error("error receiving pppoe packet: %m"); return -1; } if (debug) pppoe_log_packet("Recv ", pkt); return 0; } ppp-2.4.5/pppd/plugins/rp-pppoe/plugin.c000066400000000000000000000256551130035057700201670ustar00rootroot00000000000000/*********************************************************************** * * plugin.c * * pppd plugin for kernel-mode PPPoE on Linux * * Copyright (C) 2001 by Roaring Penguin Software Inc., Michal Ostrowski * and Jamal Hadi Salim. * * Much code and many ideas derived from pppoe plugin by Michal * Ostrowski and Jamal Hadi Salim, which carries this copyright: * * Copyright 2000 Michal Ostrowski , * Jamal Hadi Salim * Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr., * which is based in part on work from Jens Axboe and Paul Mackerras. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ***********************************************************************/ static char const RCSID[] = "$Id: plugin.c,v 1.17 2008/06/15 04:35:50 paulus Exp $"; #define _GNU_SOURCE 1 #include "pppoe.h" #include "pppd/pppd.h" #include "pppd/fsm.h" #include "pppd/lcp.h" #include "pppd/ipcp.h" #include "pppd/ccp.h" /* #include "pppd/pathnames.h" */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _ROOT_PATH #define _ROOT_PATH "" #endif #define _PATH_ETHOPT _ROOT_PATH "/etc/ppp/options." char pppd_version[] = VERSION; /* From sys-linux.c in pppd -- MUST FIX THIS! */ extern int new_style_driver; char *pppd_pppoe_service = NULL; static char *acName = NULL; static char *existingSession = NULL; static int printACNames = 0; static char *pppoe_reqd_mac = NULL; unsigned char pppoe_reqd_mac_addr[6]; static int PPPoEDevnameHook(char *cmd, char **argv, int doit); static option_t Options[] = { { "device name", o_wild, (void *) &PPPoEDevnameHook, "PPPoE device name", OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, devnam}, { "rp_pppoe_service", o_string, &pppd_pppoe_service, "Desired PPPoE service name" }, { "rp_pppoe_ac", o_string, &acName, "Desired PPPoE access concentrator name" }, { "rp_pppoe_sess", o_string, &existingSession, "Attach to existing session (sessid:macaddr)" }, { "rp_pppoe_verbose", o_int, &printACNames, "Be verbose about discovered access concentrators"}, { "pppoe-mac", o_string, &pppoe_reqd_mac, "Only connect to specified MAC address" }, { NULL } }; int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL; static PPPoEConnection *conn = NULL; /********************************************************************** * %FUNCTION: PPPOEInitDevice * %ARGUMENTS: * None * %RETURNS: * * %DESCRIPTION: * Initializes PPPoE device. ***********************************************************************/ static int PPPOEInitDevice(void) { conn = malloc(sizeof(PPPoEConnection)); if (!conn) { novm("PPPoE session data"); } memset(conn, 0, sizeof(PPPoEConnection)); conn->acName = acName; conn->serviceName = pppd_pppoe_service; conn->ifName = devnam; conn->discoverySocket = -1; conn->sessionSocket = -1; conn->useHostUniq = 1; conn->printACNames = printACNames; conn->discoveryTimeout = PADI_TIMEOUT; return 1; } /********************************************************************** * %FUNCTION: PPPOEConnectDevice * %ARGUMENTS: * None * %RETURNS: * Non-negative if all goes well; -1 otherwise * %DESCRIPTION: * Connects PPPoE device. ***********************************************************************/ static int PPPOEConnectDevice(void) { struct sockaddr_pppox sp; strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); if (existingSession) { unsigned int mac[ETH_ALEN]; int i, ses; if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x", &ses, &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 7) { fatal("Illegal value for rp_pppoe_sess option"); } conn->session = htons(ses); for (i=0; ipeerEth[i] = (unsigned char) mac[i]; } } else { discovery(conn); if (conn->discoveryState != STATE_SESSION) { error("Unable to complete PPPoE Discovery"); return -1; } } /* Set PPPoE session-number for further consumption */ ppp_session_number = ntohs(conn->session); /* Make the session socket */ conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE); if (conn->sessionSocket < 0) { error("Failed to create PPPoE socket: %m"); goto errout; } sp.sa_family = AF_PPPOX; sp.sa_protocol = PX_PROTO_OE; sp.sa_addr.pppoe.sid = conn->session; memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); /* Set remote_number for ServPoET */ sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X", (unsigned) conn->peerEth[0], (unsigned) conn->peerEth[1], (unsigned) conn->peerEth[2], (unsigned) conn->peerEth[3], (unsigned) conn->peerEth[4], (unsigned) conn->peerEth[5]); warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s", (unsigned) conn->peerEth[0], (unsigned) conn->peerEth[1], (unsigned) conn->peerEth[2], (unsigned) conn->peerEth[3], (unsigned) conn->peerEth[4], (unsigned) conn->peerEth[5], conn->ifName); script_setenv("MACREMOTE", remote_number, 0); if (connect(conn->sessionSocket, (struct sockaddr *) &sp, sizeof(struct sockaddr_pppox)) < 0) { error("Failed to connect PPPoE socket: %d %m", errno); close(conn->sessionSocket); goto errout; } return conn->sessionSocket; errout: if (conn->discoverySocket >= 0) { sendPADT(conn, NULL); close(conn->discoverySocket); conn->discoverySocket = -1; } return -1; } static void PPPOERecvConfig(int mru, u_int32_t asyncmap, int pcomp, int accomp) { #if 0 /* broken protocol, but no point harrassing the users I guess... */ if (mru > MAX_PPPOE_MTU) warn("Couldn't increase MRU to %d", mru); #endif } /********************************************************************** * %FUNCTION: PPPOEDisconnectDevice * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Disconnects PPPoE device ***********************************************************************/ static void PPPOEDisconnectDevice(void) { struct sockaddr_pppox sp; sp.sa_family = AF_PPPOX; sp.sa_protocol = PX_PROTO_OE; sp.sa_addr.pppoe.sid = 0; memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); if (connect(conn->sessionSocket, (struct sockaddr *) &sp, sizeof(struct sockaddr_pppox)) < 0) error("Failed to disconnect PPPoE socket: %d %m", errno); close(conn->sessionSocket); /* don't send PADT?? */ if (conn->discoverySocket >= 0) close(conn->discoverySocket); } static void PPPOEDeviceOptions(void) { char buf[256]; snprintf(buf, 256, _PATH_ETHOPT "%s", devnam); if (!options_from_file(buf, 0, 0, 1)) exit(EXIT_OPTION_ERROR); } struct channel pppoe_channel; /********************************************************************** * %FUNCTION: PPPoEDevnameHook * %ARGUMENTS: * cmd -- the command (actually, the device name * argv -- argument vector * doit -- if non-zero, set device name. Otherwise, just check if possible * %RETURNS: * 1 if we will handle this device; 0 otherwise. * %DESCRIPTION: * Checks if name is a valid interface name; if so, returns 1. Also * sets up devnam (string representation of device). ***********************************************************************/ static int PPPoEDevnameHook(char *cmd, char **argv, int doit) { int r = 1; int fd; struct ifreq ifr; /* * Take any otherwise-unrecognized option as a possible device name, * and test if it is the name of a network interface with a * hardware address whose sa_family is ARPHRD_ETHER. */ if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) { /* Strip off "nic-" */ cmd += 4; } /* Open a socket */ if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { r = 0; } /* Try getting interface index */ if (r) { strncpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { r = 0; } else { if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { r = 0; } else { if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { if (doit) error("Interface %s not Ethernet", cmd); r = 0; } } } } /* Close socket */ close(fd); if (r && doit) { strncpy(devnam, cmd, sizeof(devnam)); if (the_channel != &pppoe_channel) { the_channel = &pppoe_channel; modem = 0; PPPOEInitDevice(); } return 1; } return r; } /********************************************************************** * %FUNCTION: plugin_init * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Initializes hooks for pppd plugin ***********************************************************************/ void plugin_init(void) { if (!ppp_available() && !new_style_driver) { fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?"); } add_options(Options); info("RP-PPPoE plugin version %s compiled against pppd %s", RP_VERSION, VERSION); } void pppoe_check_options(void) { unsigned int mac[6]; int i; if (pppoe_reqd_mac != NULL) { if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) { option_error("cannot parse pppoe-mac option value"); exit(EXIT_OPTION_ERROR); } for (i = 0; i < 6; ++i) conn->req_peer_mac[i] = mac[i]; conn->req_peer = 1; } lcp_allowoptions[0].neg_accompression = 0; lcp_wantoptions[0].neg_accompression = 0; lcp_allowoptions[0].neg_asyncmap = 0; lcp_wantoptions[0].neg_asyncmap = 0; lcp_allowoptions[0].neg_pcompression = 0; lcp_wantoptions[0].neg_pcompression = 0; if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU) lcp_allowoptions[0].mru = MAX_PPPOE_MTU; if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU) lcp_wantoptions[0].mru = MAX_PPPOE_MTU; ccp_allowoptions[0].deflate = 0; ccp_wantoptions[0].deflate = 0; ipcp_allowoptions[0].neg_vj = 0; ipcp_wantoptions[0].neg_vj = 0; ccp_allowoptions[0].bsd_compress = 0; ccp_wantoptions[0].bsd_compress = 0; } struct channel pppoe_channel = { .options = Options, .process_extra_options = &PPPOEDeviceOptions, .check_options = pppoe_check_options, .connect = &PPPOEConnectDevice, .disconnect = &PPPOEDisconnectDevice, .establish_ppp = &generic_establish_ppp, .disestablish_ppp = &generic_disestablish_ppp, .send_config = NULL, .recv_config = &PPPOERecvConfig, .close = NULL, .cleanup = NULL }; ppp-2.4.5/pppd/plugins/rp-pppoe/pppoe-discovery.c000066400000000000000000000453361130035057700220170ustar00rootroot00000000000000/* * Perform PPPoE discovery * * Copyright (C) 2000-2001 by Roaring Penguin Software Inc. * Copyright (C) 2004 Marco d'Itri * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * */ #include #include #include #include #include #include "pppoe.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_NETPACKET_PACKET_H #include #elif defined(HAVE_LINUX_IF_PACKET_H) #include #endif #ifdef HAVE_NET_ETHERNET_H #include #endif #ifdef HAVE_ASM_TYPES_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #include #include #include #ifdef HAVE_NET_IF_ARP_H #include #endif char *xstrdup(const char *s); void usage(void); void die(int status) { exit(status); } /* Initialize frame types to RFC 2516 values. Some broken peers apparently use different frame types... sigh... */ UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; /********************************************************************** *%FUNCTION: etherType *%ARGUMENTS: * packet -- a received PPPoE packet *%RETURNS: * ethernet packet type (see /usr/include/net/ethertypes.h) *%DESCRIPTION: * Checks the ethernet packet header to determine its type. * We should only be receveing DISCOVERY and SESSION types if the BPF * is set up correctly. Logs an error if an unexpected type is received. * Note that the ethernet type names come from "pppoe.h" and the packet * packet structure names use the LINUX dialect to maintain consistency * with the rest of this file. See the BSD section of "pppoe.h" for * translations of the data structure names. ***********************************************************************/ UINT16_t etherType(PPPoEPacket *packet) { UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { fprintf(stderr, "Invalid ether type 0x%x\n", type); } return type; } /********************************************************************** *%FUNCTION: openInterface *%ARGUMENTS: * ifname -- name of interface * type -- Ethernet frame type * hwaddr -- if non-NULL, set to the hardware address *%RETURNS: * A raw socket for talking to the Ethernet card. Exits on error. *%DESCRIPTION: * Opens a raw Ethernet socket ***********************************************************************/ int openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) { int optval=1; int fd; struct ifreq ifr; int domain, stype; #ifdef HAVE_STRUCT_SOCKADDR_LL struct sockaddr_ll sa; #else struct sockaddr sa; #endif memset(&sa, 0, sizeof(sa)); #ifdef HAVE_STRUCT_SOCKADDR_LL domain = PF_PACKET; stype = SOCK_RAW; #else domain = PF_INET; stype = SOCK_PACKET; #endif if ((fd = socket(domain, stype, htons(type))) < 0) { /* Give a more helpful message for the common error case */ if (errno == EPERM) { rp_fatal("Cannot create raw socket -- pppoe must be run as root."); } fatalSys("socket"); } if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { fatalSys("setsockopt"); } /* Fill in hardware address */ if (hwaddr) { strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { fatalSys("ioctl(SIOCGIFHWADDR)"); } memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); #ifdef ARPHRD_ETHER if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { char buffer[256]; sprintf(buffer, "Interface %.16s is not Ethernet", ifname); rp_fatal(buffer); } #endif if (NOT_UNICAST(hwaddr)) { char buffer[256]; sprintf(buffer, "Interface %.16s has broadcast/multicast MAC address??", ifname); rp_fatal(buffer); } } /* Sanity check on MTU */ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { fatalSys("ioctl(SIOCGIFMTU)"); } if (ifr.ifr_mtu < ETH_DATA_LEN) { fprintf(stderr, "Interface %.16s has MTU of %d -- should be %d.\n", ifname, ifr.ifr_mtu, ETH_DATA_LEN); fprintf(stderr, "You may have serious connection problems.\n"); } #ifdef HAVE_STRUCT_SOCKADDR_LL /* Get interface index */ sa.sll_family = AF_PACKET; sa.sll_protocol = htons(type); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index"); } sa.sll_ifindex = ifr.ifr_ifindex; #else strcpy(sa.sa_data, ifname); #endif /* We're only interested in packets on specified interface */ if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { fatalSys("bind"); } return fd; } /*********************************************************************** *%FUNCTION: sendPacket *%ARGUMENTS: * sock -- socket to send to * pkt -- the packet to transmit * size -- size of packet (in bytes) *%RETURNS: * 0 on success; -1 on failure *%DESCRIPTION: * Transmits a packet ***********************************************************************/ int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) { #if defined(HAVE_STRUCT_SOCKADDR_LL) if (send(sock, pkt, size, 0) < 0) { sysErr("send (sendPacket)"); return -1; } #else struct sockaddr sa; if (!conn) { rp_fatal("relay and server not supported on Linux 2.0 kernels"); } strcpy(sa.sa_data, conn->ifName); if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) { sysErr("sendto (sendPacket)"); return -1; } #endif return 0; } /*********************************************************************** *%FUNCTION: receivePacket *%ARGUMENTS: * sock -- socket to read from * pkt -- place to store the received packet * size -- set to size of packet in bytes *%RETURNS: * >= 0 if all OK; < 0 if error *%DESCRIPTION: * Receives a packet ***********************************************************************/ int receivePacket(int sock, PPPoEPacket *pkt, int *size) { if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { sysErr("recv (receivePacket)"); return -1; } return 0; } /********************************************************************** *%FUNCTION: parsePacket *%ARGUMENTS: * packet -- the PPPoE discovery packet to parse * func -- function called for each tag in the packet * extra -- an opaque data pointer supplied to parsing function *%RETURNS: * 0 if everything went well; -1 if there was an error *%DESCRIPTION: * Parses a PPPoE discovery packet, calling "func" for each tag in the packet. * "func" is passed the additional argument "extra". ***********************************************************************/ int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra) { UINT16_t len = ntohs(packet->length); unsigned char *curTag; UINT16_t tagType, tagLen; if (PPPOE_VER(packet->vertype) != 1) { fprintf(stderr, "Invalid PPPoE version (%d)\n", PPPOE_VER(packet->vertype)); return -1; } if (PPPOE_TYPE(packet->vertype) != 1) { fprintf(stderr, "Invalid PPPoE type (%d)\n", PPPOE_TYPE(packet->vertype)); return -1; } /* Do some sanity checks on packet */ if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */ fprintf(stderr, "Invalid PPPoE packet length (%u)\n", len); return -1; } /* Step through the tags */ curTag = packet->payload; while(curTag - packet->payload < len) { /* Alignment is not guaranteed, so do this by hand... */ tagType = (curTag[0] << 8) + curTag[1]; tagLen = (curTag[2] << 8) + curTag[3]; if (tagType == TAG_END_OF_LIST) { return 0; } if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { fprintf(stderr, "Invalid PPPoE tag length (%u)\n", tagLen); return -1; } func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); curTag = curTag + TAG_HDR_SIZE + tagLen; } return 0; } /********************************************************************** *%FUNCTION: parseForHostUniq *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data. * extra -- user-supplied pointer. This is assumed to be a pointer to int. *%RETURNS: * Nothing *%DESCRIPTION: * If a HostUnique tag is found which matches our PID, sets *extra to 1. ***********************************************************************/ void parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data, void *extra) { int *val = (int *) extra; if (type == TAG_HOST_UNIQ && len == sizeof(pid_t)) { pid_t tmp; memcpy(&tmp, data, len); if (tmp == getpid()) { *val = 1; } } } /********************************************************************** *%FUNCTION: packetIsForMe *%ARGUMENTS: * conn -- PPPoE connection info * packet -- a received PPPoE packet *%RETURNS: * 1 if packet is for this PPPoE daemon; 0 otherwise. *%DESCRIPTION: * If we are using the Host-Unique tag, verifies that packet contains * our unique identifier. ***********************************************************************/ int packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) { int forMe = 0; /* If packet is not directed to our MAC address, forget it */ if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; /* If we're not using the Host-Unique tag, then accept the packet */ if (!conn->useHostUniq) return 1; parsePacket(packet, parseForHostUniq, &forMe); return forMe; } /********************************************************************** *%FUNCTION: parsePADOTags *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data. Should point to a PacketCriteria structure * which gets filled in according to selected AC name and service * name. *%RETURNS: * Nothing *%DESCRIPTION: * Picks interesting tags out of a PADO packet ***********************************************************************/ void parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data, void *extra) { struct PacketCriteria *pc = (struct PacketCriteria *) extra; PPPoEConnection *conn = pc->conn; int i; switch(type) { case TAG_AC_NAME: pc->seenACName = 1; printf("Access-Concentrator: %.*s\n", (int) len, data); if (conn->acName && len == strlen(conn->acName) && !strncmp((char *) data, conn->acName, len)) { pc->acNameOK = 1; } break; case TAG_SERVICE_NAME: pc->seenServiceName = 1; if (len > 0) { printf(" Service-Name: %.*s\n", (int) len, data); } if (conn->serviceName && len == strlen(conn->serviceName) && !strncmp((char *) data, conn->serviceName, len)) { pc->serviceNameOK = 1; } break; case TAG_AC_COOKIE: printf("Got a cookie:"); /* Print first 20 bytes of cookie */ for (i=0; icookie.type = htons(type); conn->cookie.length = htons(len); memcpy(conn->cookie.payload, data, len); break; case TAG_RELAY_SESSION_ID: printf("Got a Relay-ID:"); /* Print first 20 bytes of relay ID */ for (i=0; irelayId.type = htons(type); conn->relayId.length = htons(len); memcpy(conn->relayId.payload, data, len); break; case TAG_SERVICE_NAME_ERROR: printf("Got a Service-Name-Error tag: %.*s\n", (int) len, data); break; case TAG_AC_SYSTEM_ERROR: printf("Got a System-Error tag: %.*s\n", (int) len, data); break; case TAG_GENERIC_ERROR: printf("Got a Generic-Error tag: %.*s\n", (int) len, data); break; } } /*********************************************************************** *%FUNCTION: sendPADI *%ARGUMENTS: * conn -- PPPoEConnection structure *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADI packet ***********************************************************************/ void sendPADI(PPPoEConnection *conn) { PPPoEPacket packet; unsigned char *cursor = packet.payload; PPPoETag *svc = (PPPoETag *) (&packet.payload); UINT16_t namelen = 0; UINT16_t plen; if (conn->serviceName) { namelen = (UINT16_t) strlen(conn->serviceName); } plen = TAG_HDR_SIZE + namelen; CHECK_ROOM(cursor, packet.payload, plen); /* Set destination to Ethernet broadcast address */ memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_PADI; packet.session = 0; svc->type = TAG_SERVICE_NAME; svc->length = htons(namelen); CHECK_ROOM(cursor, packet.payload, namelen+TAG_HDR_SIZE); if (conn->serviceName) { memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); } cursor += namelen + TAG_HDR_SIZE; /* If we're using Host-Uniq, copy it over */ if (conn->useHostUniq) { PPPoETag hostUniq; pid_t pid = getpid(); hostUniq.type = htons(TAG_HOST_UNIQ); hostUniq.length = htons(sizeof(pid)); memcpy(hostUniq.payload, &pid, sizeof(pid)); CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE); memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); cursor += sizeof(pid) + TAG_HDR_SIZE; plen += sizeof(pid) + TAG_HDR_SIZE; } packet.length = htons(plen); sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "SENT"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } } /********************************************************************** *%FUNCTION: waitForPADO *%ARGUMENTS: * conn -- PPPoEConnection structure * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADO packet and copies useful information ***********************************************************************/ void waitForPADO(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; PPPoEPacket packet; int len; struct PacketCriteria pc; pc.conn = conn; pc.acNameOK = (conn->acName) ? 0 : 1; pc.serviceNameOK = (conn->serviceName) ? 0 : 1; pc.seenACName = 0; pc.seenServiceName = 0; conn->error = 0; do { if (BPF_BUFFER_IS_EMPTY) { tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&readable); FD_SET(conn->discoverySocket, &readable); while(1) { r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); if (r >= 0 || errno != EINTR) break; } if (r < 0) { perror("select (waitForPADO)"); return; } if (r == 0) return; /* Timed out */ } /* Get the packet */ receivePacket(conn->discoverySocket, &packet, &len); /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { fprintf(stderr, "Bogus PPPoE length field (%u)\n", (unsigned int) ntohs(packet.length)); continue; } #ifdef USE_BPF /* If it's not a Discovery packet, loop again */ if (etherType(&packet) != Eth_PPPOE_Discovery) continue; #endif if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } /* If it's not for us, loop again */ if (!packetIsForMe(conn, &packet)) continue; if (packet.code == CODE_PADO) { if (BROADCAST(packet.ethHdr.h_source)) { fprintf(stderr, "Ignoring PADO packet from broadcast MAC address\n"); continue; } parsePacket(&packet, parsePADOTags, &pc); if (conn->error) return; if (!pc.seenACName) { fprintf(stderr, "Ignoring PADO packet with no AC-Name tag\n"); continue; } if (!pc.seenServiceName) { fprintf(stderr, "Ignoring PADO packet with no Service-Name tag\n"); continue; } conn->numPADOs++; printf("--------------------------------------------------\n"); if (pc.acNameOK && pc.serviceNameOK) { memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); if (conn->printACNames) { printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n", (unsigned) conn->peerEth[0], (unsigned) conn->peerEth[1], (unsigned) conn->peerEth[2], (unsigned) conn->peerEth[3], (unsigned) conn->peerEth[4], (unsigned) conn->peerEth[5]); continue; } conn->discoveryState = STATE_RECEIVED_PADO; break; } } } while (conn->discoveryState != STATE_RECEIVED_PADO); } /********************************************************************** *%FUNCTION: discovery *%ARGUMENTS: * conn -- PPPoE connection info structure *%RETURNS: * Nothing *%DESCRIPTION: * Performs the PPPoE discovery phase ***********************************************************************/ void discovery(PPPoEConnection *conn) { int padiAttempts = 0; int timeout = PADI_TIMEOUT; conn->discoverySocket = openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); do { padiAttempts++; if (padiAttempts > MAX_PADI_ATTEMPTS) { fprintf(stderr, "Timeout waiting for PADO packets\n"); close(conn->discoverySocket); conn->discoverySocket = -1; return; } sendPADI(conn); conn->discoveryState = STATE_SENT_PADI; waitForPADO(conn, timeout); } while (!conn->numPADOs); } int main(int argc, char *argv[]) { int opt; PPPoEConnection *conn; conn = malloc(sizeof(PPPoEConnection)); if (!conn) fatalSys("malloc"); memset(conn, 0, sizeof(PPPoEConnection)); while ((opt = getopt(argc, argv, "I:D:VUAS:C:h")) > 0) { switch(opt) { case 'S': conn->serviceName = xstrdup(optarg); break; case 'C': conn->acName = xstrdup(optarg); break; case 'U': conn->useHostUniq = 1; break; case 'D': conn->debugFile = fopen(optarg, "w"); if (!conn->debugFile) { fprintf(stderr, "Could not open %s: %s\n", optarg, strerror(errno)); exit(1); } fprintf(conn->debugFile, "pppoe-discovery %s\n", RP_VERSION); break; case 'I': conn->ifName = xstrdup(optarg); break; case 'A': /* this is the default */ break; case 'V': case 'h': usage(); exit(0); default: usage(); exit(1); } } /* default interface name */ if (!conn->ifName) conn->ifName = strdup("eth0"); conn->discoverySocket = -1; conn->sessionSocket = -1; conn->printACNames = 1; discovery(conn); exit(0); } void rp_fatal(char const *str) { fprintf(stderr, "%s\n", str); exit(1); } void fatalSys(char const *str) { perror(str); exit(1); } void sysErr(char const *str) { rp_fatal(str); } char *xstrdup(const char *s) { register char *ret = strdup(s); if (!ret) sysErr("strdup"); return ret; } void usage(void) { fprintf(stderr, "Usage: pppoe-discovery [options]\n"); fprintf(stderr, "\nVersion " RP_VERSION "\n"); } ppp-2.4.5/pppd/plugins/rp-pppoe/pppoe.h000066400000000000000000000220521130035057700200050ustar00rootroot00000000000000/*********************************************************************** * * pppoe.h * * Declaration of various PPPoE constants * * Copyright (C) 2000 Roaring Penguin Software Inc. * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * $Id: pppoe.h,v 1.4 2008/06/15 04:35:50 paulus Exp $ * ***********************************************************************/ #include "config.h" #if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) #define _POSIX_SOURCE 1 /* For sigaction defines */ #endif #include /* For FILE */ #include /* For pid_t */ /* How do we access raw Ethernet devices? */ #undef USE_LINUX_PACKET #undef USE_BPF #if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) #define USE_LINUX_PACKET 1 #elif defined(HAVE_SYS_DLPI_H) #define USE_DLPI #elif defined(HAVE_NET_BPF_H) #define USE_BPF 1 #endif /* Sanity check */ #if !defined(USE_BPF) && !defined(USE_LINUX_PACKET) && !defined(USE_DLPI) #error Unknown method for accessing raw Ethernet frames #endif #ifdef HAVE_SYS_CDEFS_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif /* Ugly header files on some Linux boxes... */ #if defined(HAVE_LINUX_IF_H) #include #elif defined(HAVE_NET_IF_H) #include #endif #ifdef HAVE_NET_IF_TYPES_H #include #endif #define BPF_BUFFER_IS_EMPTY 1 #define BPF_BUFFER_HAS_DATA 0 /* Define various integer types -- assumes a char is 8 bits */ #if SIZEOF_UNSIGNED_SHORT == 2 typedef unsigned short UINT16_t; #elif SIZEOF_UNSIGNED_INT == 2 typedef unsigned int UINT16_t; #else #error Could not find a 16-bit integer type #endif #if SIZEOF_UNSIGNED_SHORT == 4 typedef unsigned short UINT32_t; #elif SIZEOF_UNSIGNED_INT == 4 typedef unsigned int UINT32_t; #elif SIZEOF_UNSIGNED_LONG == 4 typedef unsigned long UINT32_t; #else #error Could not find a 32-bit integer type #endif #ifdef HAVE_LINUX_IF_ETHER_H #include #endif #include #ifdef HAVE_NETINET_IF_ETHER_H #include #ifdef HAVE_SYS_SOCKET_H #include #endif #ifndef HAVE_SYS_DLPI_H #include #endif #endif /* Ethernet frame types according to RFC 2516 */ #define ETH_PPPOE_DISCOVERY 0x8863 #define ETH_PPPOE_SESSION 0x8864 /* But some brain-dead peers disobey the RFC, so frame types are variables */ extern UINT16_t Eth_PPPOE_Discovery; extern UINT16_t Eth_PPPOE_Session; /* PPPoE codes */ #define CODE_PADI 0x09 #define CODE_PADO 0x07 #define CODE_PADR 0x19 #define CODE_PADS 0x65 #define CODE_PADT 0xA7 /* Extensions from draft-carrel-info-pppoe-ext-00 */ /* I do NOT like PADM or PADN, but they are here for completeness */ #define CODE_PADM 0xD3 #define CODE_PADN 0xD4 #define CODE_SESS 0x00 /* PPPoE Tags */ #define TAG_END_OF_LIST 0x0000 #define TAG_SERVICE_NAME 0x0101 #define TAG_AC_NAME 0x0102 #define TAG_HOST_UNIQ 0x0103 #define TAG_AC_COOKIE 0x0104 #define TAG_VENDOR_SPECIFIC 0x0105 #define TAG_RELAY_SESSION_ID 0x0110 #define TAG_SERVICE_NAME_ERROR 0x0201 #define TAG_AC_SYSTEM_ERROR 0x0202 #define TAG_GENERIC_ERROR 0x0203 /* Extensions from draft-carrel-info-pppoe-ext-00 */ /* I do NOT like these tags one little bit */ #define TAG_HURL 0x111 #define TAG_MOTM 0x112 #define TAG_IP_ROUTE_ADD 0x121 /* Discovery phase states */ #define STATE_SENT_PADI 0 #define STATE_RECEIVED_PADO 1 #define STATE_SENT_PADR 2 #define STATE_SESSION 3 #define STATE_TERMINATED 4 /* How many PADI/PADS attempts? */ #define MAX_PADI_ATTEMPTS 3 /* Initial timeout for PADO/PADS */ #define PADI_TIMEOUT 5 /* States for scanning PPP frames */ #define STATE_WAITFOR_FRAME_ADDR 0 #define STATE_DROP_PROTO 1 #define STATE_BUILDING_PACKET 2 /* Special PPP frame characters */ #define FRAME_ESC 0x7D #define FRAME_FLAG 0x7E #define FRAME_ADDR 0xFF #define FRAME_CTRL 0x03 #define FRAME_ENC 0x20 #define IPV4ALEN 4 #define SMALLBUF 256 /* A PPPoE Packet, including Ethernet headers */ typedef struct PPPoEPacketStruct { struct ethhdr ethHdr; /* Ethernet header */ unsigned int vertype:8; /* PPPoE Version and Type (must both be 1) */ unsigned int code:8; /* PPPoE code */ unsigned int session:16; /* PPPoE session */ unsigned int length:16; /* Payload length */ unsigned char payload[ETH_DATA_LEN]; /* A bit of room to spare */ } PPPoEPacket; #define PPPOE_VER(vt) ((vt) >> 4) #define PPPOE_TYPE(vt) ((vt) & 0xf) #define PPPOE_VER_TYPE(v, t) (((v) << 4) | (t)) /* Header size of a PPPoE packet */ #define PPPOE_OVERHEAD 6 /* type, code, session, length */ #define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD) #define MAX_PPPOE_PAYLOAD (ETH_DATA_LEN - PPPOE_OVERHEAD) #define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - 2) /* PPPoE Tag */ typedef struct PPPoETagStruct { unsigned int type:16; /* tag type */ unsigned int length:16; /* Length of payload */ unsigned char payload[ETH_DATA_LEN]; /* A LOT of room to spare */ } PPPoETag; /* Header size of a PPPoE tag */ #define TAG_HDR_SIZE 4 /* Chunk to read from stdin */ #define READ_CHUNK 4096 /* Function passed to parsePacket */ typedef void ParseFunc(UINT16_t type, UINT16_t len, unsigned char *data, void *extra); #define PPPINITFCS16 0xffff /* Initial FCS value */ /* Keep track of the state of a connection -- collect everything in one spot */ typedef struct PPPoEConnectionStruct { int discoveryState; /* Where we are in discovery */ int discoverySocket; /* Raw socket for discovery frames */ int sessionSocket; /* Raw socket for session frames */ unsigned char myEth[ETH_ALEN]; /* My MAC address */ unsigned char peerEth[ETH_ALEN]; /* Peer's MAC address */ unsigned char req_peer_mac[ETH_ALEN]; /* required peer MAC address */ unsigned char req_peer; /* require mac addr to match req_peer_mac */ UINT16_t session; /* Session ID */ char *ifName; /* Interface name */ char *serviceName; /* Desired service name, if any */ char *acName; /* Desired AC name, if any */ int synchronous; /* Use synchronous PPP */ int useHostUniq; /* Use Host-Uniq tag */ int printACNames; /* Just print AC names */ FILE *debugFile; /* Debug file for dumping packets */ int numPADOs; /* Number of PADO packets received */ PPPoETag cookie; /* We have to send this if we get it */ PPPoETag relayId; /* Ditto */ int error; /* Error packet received */ int debug; /* Set to log packets sent and received */ int discoveryTimeout; /* Timeout for discovery packets */ } PPPoEConnection; /* Structure used to determine acceptable PADO or PADS packet */ struct PacketCriteria { PPPoEConnection *conn; int acNameOK; int serviceNameOK; int seenACName; int seenServiceName; }; /* Function Prototypes */ UINT16_t etherType(PPPoEPacket *packet); int openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr); int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size); int receivePacket(int sock, PPPoEPacket *pkt, int *size); void fatalSys(char const *str); void rp_fatal(char const *str); void printErr(char const *str); void sysErr(char const *str); void dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir); void dumpHex(FILE *fp, unsigned char const *buf, int len); int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra); void parseLogErrs(UINT16_t typ, UINT16_t len, unsigned char *data, void *xtra); void syncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet); void asyncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet); void asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss); void syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss); char *strDup(char const *str); void sendPADT(PPPoEConnection *conn, char const *msg); void sendSessionPacket(PPPoEConnection *conn, PPPoEPacket *packet, int len); void initPPP(void); void clampMSS(PPPoEPacket *packet, char const *dir, int clampMss); UINT16_t computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr); UINT16_t pppFCS16(UINT16_t fcs, unsigned char *cp, int len); void discovery(PPPoEConnection *conn); unsigned char *findTag(PPPoEPacket *packet, UINT16_t tagType, PPPoETag *tag); void pppoe_printpkt(PPPoEPacket *packet, void (*printer)(void *, char *, ...), void *arg); void pppoe_log_packet(const char *prefix, PPPoEPacket *packet); #define SET_STRING(var, val) do { if (var) free(var); var = strDup(val); } while(0); #define CHECK_ROOM(cursor, start, len) \ do {\ if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \ error("Would create too-long packet"); \ return; \ } \ } while(0) /* True if Ethernet address is broadcast or multicast */ #define NOT_UNICAST(e) ((e[0] & 0x01) != 0) #define BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) == 0xFF) #define NOT_BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) != 0xFF) ppp-2.4.5/pppd/plugins/winbind.c000066400000000000000000000426651130035057700165610ustar00rootroot00000000000000/*********************************************************************** * * winbind.c * * WINBIND plugin for pppd. Performs PAP, CHAP, MS-CHAP, MS-CHAPv2 * authentication using WINBIND to contact a NT-style PDC. * * Based on the structure of the radius module. * * Copyright (C) 2003 Andrew Bartlet * * Copyright 1999 Paul Mackerras, Alan Curry. * (pipe read code from passpromt.c) * * Copyright (C) 2002 Roaring Penguin Software Inc. * * Based on a patch for ipppd, which is: * Copyright (C) 1996, Matjaz Godec * Copyright (C) 1996, Lars Fenneberg * Copyright (C) 1997, Miguel A.L. Paraz * * Uses radiusclient library, which is: * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg * Copyright (C) 2002 Roaring Penguin Software Inc. * * MPPE support is by Ralf Hofmann, , with * modification from Frank Cusack, . * * Updated on 2003-12-12 to support updated PPP plugin API from latest CVS * Copyright (C) 2003, Sean E. Millichamp * * This plugin may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * ***********************************************************************/ #include "pppd.h" #include "chap-new.h" #include "chap_ms.h" #ifdef MPPE #include "md5.h" #endif #include "fsm.h" #include "ipcp.h" #include #include #include #include #include #include #include #include #include #include #include #define BUF_LEN 1024 #define NOT_AUTHENTICATED 0 #define AUTHENTICATED 1 static char *ntlm_auth = NULL; static int set_ntlm_auth(char **argv) { char *p; p = argv[0]; if (p[0] != '/') { option_error("ntlm_auth-helper argument must be full path"); return 0; } p = strdup(p); if (p == NULL) { novm("ntlm_auth-helper argument"); return 0; } if (ntlm_auth != NULL) free(ntlm_auth); ntlm_auth = p; return 1; } static option_t Options[] = { { "ntlm_auth-helper", o_special, (void *) &set_ntlm_auth, "Path to ntlm_auth executable", OPT_PRIV }, { NULL } }; static int winbind_secret_check(void); static int winbind_pap_auth(char *user, char *passwd, char **msgp, struct wordlist **paddrs, struct wordlist **popts); static int winbind_chap_verify(char *user, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space); static int winbind_allowed_address(u_int32_t addr); char pppd_version[] = VERSION; /********************************************************************** * %FUNCTION: plugin_init * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Initializes WINBIND plugin. ***********************************************************************/ void plugin_init(void) { pap_check_hook = winbind_secret_check; pap_auth_hook = winbind_pap_auth; chap_check_hook = winbind_secret_check; chap_verify_hook = winbind_chap_verify; allowed_address_hook = winbind_allowed_address; /* Don't ask the peer for anything other than MS-CHAP or MS-CHAP V2 */ chap_mdtype_all &= (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT); add_options(Options); info("WINBIND plugin initialized."); } /** Routine to get hex characters and turn them into a 16 byte array. the array can be variable length, and any non-hex-numeric characters are skipped. "0xnn" or "0Xnn" is specially catered for. valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n" **/ /* Unix SMB/CIFS implementation. Samba utility functions Copyright (C) Andrew Tridgell 1992-2001 Copyright (C) Simo Sorce 2001-2002 Copyright (C) Martin Pool 2003 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ size_t strhex_to_str(char *p, size_t len, const char *strhex) { size_t i; size_t num_chars = 0; unsigned char lonybble, hinybble; const char *hexchars = "0123456789ABCDEF"; char *p1 = NULL, *p2 = NULL; for (i = 0; i < len && strhex[i] != 0; i++) { if (strncmp(hexchars, "0x", 2) == 0) { i++; /* skip two chars */ continue; } if (!(p1 = strchr(hexchars, toupper(strhex[i])))) break; i++; /* next hex digit */ if (!(p2 = strchr(hexchars, toupper(strhex[i])))) break; /* get the two nybbles */ hinybble = (p1 - hexchars); lonybble = (p2 - hexchars); p[num_chars] = (hinybble << 4) | lonybble; num_chars++; p1 = NULL; p2 = NULL; } return num_chars; } static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * Encode a base64 string into a malloc()ed string caller to free. * *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments **/ char * base64_encode(const char *data) { size_t out_cnt = 0; size_t len = strlen(data); size_t output_len = 4 * ((len + 2) / 3) + 2; const unsigned char *ptr = (const unsigned char *) data; char *result = malloc(output_len); /* get us plenty of space */ unsigned int bits; for (; len >= 3; len -= 3) { bits = (ptr[0] << 16) + (ptr[1] << 8) + ptr[2]; ptr += 3; result[out_cnt++] = b64[bits >> 18]; result[out_cnt++] = b64[(bits >> 12) & 0x3f]; result[out_cnt++] = b64[(bits >> 6) & 0x3f]; result[out_cnt++] = b64[bits & 0x3f]; } if (len != 0) { bits = ptr[0] << 16; if (len > 1) bits |= ptr[1] << 8; result[out_cnt++] = b64[bits >> 18]; result[out_cnt++] = b64[(bits >> 12) & 0x3f]; result[out_cnt++] = (len > 1)? b64[(bits >> 6) & 0x3f]: '='; result[out_cnt++] = '='; } result[out_cnt] = '\0'; /* terminate */ return result; } unsigned int run_ntlm_auth(const char *username, const char *domain, const char *full_username, const char *plaintext_password, const u_char *challenge, size_t challenge_length, const u_char *lm_response, size_t lm_response_length, const u_char *nt_response, size_t nt_response_length, u_char nt_key[16], char **error_string) { pid_t forkret; int child_in[2]; int child_out[2]; int status; int authenticated = NOT_AUTHENTICATED; /* not auth */ int got_user_session_key = 0; /* not got key */ char buffer[1024]; FILE *pipe_in; FILE *pipe_out; int i; char *challenge_hex; char *lm_hex_hash; char *nt_hex_hash; /* First see if we have a program to run... */ if (ntlm_auth == NULL) return NOT_AUTHENTICATED; /* Make first child */ if (pipe(child_out) == -1) { error("pipe creation failed for child OUT!"); return NOT_AUTHENTICATED; } if (pipe(child_in) == -1) { error("pipe creation failed for child IN!"); return NOT_AUTHENTICATED; } forkret = safe_fork(child_in[0], child_out[1], 2); if (forkret == -1) { if (error_string) { *error_string = strdup("fork failed!"); } return NOT_AUTHENTICATED; } if (forkret == 0) { /* child process */ uid_t uid; close(child_out[0]); close(child_in[1]); /* run winbind as the user that invoked pppd */ setgid(getgid()); uid = getuid(); if (setuid(uid) == -1 || getuid() != uid) fatal("pppd/winbind: could not setuid to %d: %m", uid); execl("/bin/sh", "sh", "-c", ntlm_auth, NULL); fatal("pppd/winbind: could not exec /bin/sh: %m"); } /* parent */ close(child_out[1]); close(child_in[0]); /* Need to write the User's info onto the pipe */ pipe_in = fdopen(child_in[1], "w"); pipe_out = fdopen(child_out[0], "r"); /* look for session key coming back */ if (username) { char *b64_username = base64_encode(username); fprintf(pipe_in, "Username:: %s\n", b64_username); free(b64_username); } if (domain) { char *b64_domain = base64_encode(domain); fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain); free(b64_domain); } if (full_username) { char *b64_full_username = base64_encode(full_username); fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username); free(b64_full_username); } if (plaintext_password) { char *b64_plaintext_password = base64_encode(plaintext_password); fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password); free(b64_plaintext_password); } if (challenge_length) { fprintf(pipe_in, "Request-User-Session-Key: yes\n"); challenge_hex = malloc(challenge_length*2+1); for (i = 0; i < challenge_length; i++) sprintf(challenge_hex + i * 2, "%02X", challenge[i]); fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex); free(challenge_hex); } if (lm_response_length) { lm_hex_hash = malloc(lm_response_length*2+1); for (i = 0; i < lm_response_length; i++) sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]); fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash); free(lm_hex_hash); } if (nt_response_length) { nt_hex_hash = malloc(nt_response_length*2+1); for (i = 0; i < nt_response_length; i++) sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]); fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash); free(nt_hex_hash); } fprintf(pipe_in, ".\n"); fflush(pipe_in); while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) { char *message, *parameter; if (buffer[strlen(buffer)-1] != '\n') { break; } buffer[strlen(buffer)-1] = '\0'; message = buffer; if (!(parameter = strstr(buffer, ": "))) { break; } parameter[0] = '\0'; parameter++; parameter[0] = '\0'; parameter++; if (strcmp(message, ".") == 0) { /* end of sequence */ break; } else if (strcasecmp(message, "Authenticated") == 0) { if (strcasecmp(parameter, "Yes") == 0) { authenticated = AUTHENTICATED; } else { notice("Winbind has declined authentication for user!"); authenticated = NOT_AUTHENTICATED; } } else if (strcasecmp(message, "User-session-key") == 0) { /* length is the number of characters to parse */ if (nt_key) { if (strhex_to_str(nt_key, 32, parameter) == 16) { got_user_session_key = 1; } else { notice("NT session key for user was not 16 bytes!"); } } } else if (strcasecmp(message, "Error") == 0) { authenticated = NOT_AUTHENTICATED; if (error_string) *error_string = strdup(parameter); } else if (strcasecmp(message, "Authentication-Error") == 0) { authenticated = NOT_AUTHENTICATED; if (error_string) *error_string = strdup(parameter); } else { notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter); } } /* parent */ if (close(child_out[0]) == -1) { notice("error closing pipe?!? for child OUT[0]"); return NOT_AUTHENTICATED; } /* parent */ if (close(child_in[1]) == -1) { notice("error closing pipe?!? for child IN[1]"); return NOT_AUTHENTICATED; } while ((wait(&status) == -1) && errno == EINTR) ; if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) { notice("Did not get user session key, despite being authenticated!"); return NOT_AUTHENTICATED; } return authenticated; } /********************************************************************** * %FUNCTION: winbind_secret_check * %ARGUMENTS: * None * %RETURNS: * 0 if we don't have an ntlm_auth program to run, otherwise 1. * %DESCRIPTION: * Tells pppd that we will try to authenticate the peer, and not to * worry about looking in /etc/ppp/ *-secrets ***********************************************************************/ static int winbind_secret_check(void) { return ntlm_auth != NULL; } /********************************************************************** * %FUNCTION: winbind_pap_auth * %ARGUMENTS: * user -- user-name of peer * passwd -- password supplied by peer * msgp -- Message which will be sent in PAP response * paddrs -- set to a list of possible peer IP addresses * popts -- set to a list of additional pppd options * %RETURNS: * 1 if we can authenticate, -1 if we cannot. * %DESCRIPTION: * Performs PAP authentication using WINBIND ***********************************************************************/ static int winbind_pap_auth(char *user, char *password, char **msgp, struct wordlist **paddrs, struct wordlist **popts) { if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) { return 1; } return -1; } /********************************************************************** * %FUNCTION: winbind_chap_auth * %ARGUMENTS: * user -- user-name of peer * remmd -- hash received from peer * remmd_len -- length of remmd * cstate -- pppd's chap_state structure * %RETURNS: * AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot. * %DESCRIPTION: * Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND. ***********************************************************************/ static int winbind_chap_verify(char *user, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space) { int challenge_len, response_len; char domainname[256]; char *domain; char *username; char *p; char saresponse[MS_AUTH_RESPONSE_LENGTH+1]; /* The first byte of each of these strings contains their length */ challenge_len = *challenge++; response_len = *response++; /* remove domain from "domain\username" */ if ((username = strrchr(user, '\\')) != NULL) ++username; else username = user; strlcpy(domainname, user, sizeof(domainname)); /* remove domain from "domain\username" */ if ((p = strrchr(domainname, '\\')) != NULL) { *p = '\0'; domain = domainname; } else { domain = NULL; } /* generate MD based on negotiated type */ switch (digest->code) { case CHAP_MICROSOFT: { char *error_string = NULL; u_char *nt_response = NULL; u_char *lm_response = NULL; int nt_response_size = 0; int lm_response_size = 0; u_char session_key[16]; if (response_len != MS_CHAP_RESPONSE_LEN) break; /* not even the right length */ /* Determine which part of response to verify against */ if (response[MS_CHAP_USENT]) { nt_response = &response[MS_CHAP_NTRESP]; nt_response_size = MS_CHAP_NTRESP_LEN; } else { #ifdef MSLANMAN lm_response = &response[MS_CHAP_LANMANRESP]; lm_response_size = MS_CHAP_LANMANRESP_LEN; #else /* Should really propagate this into the error packet. */ notice("Peer request for LANMAN auth not supported"); return NOT_AUTHENTICATED; #endif /* MSLANMAN */ } /* ship off to winbind, and check */ if (run_ntlm_auth(username, domain, NULL, NULL, challenge, challenge_len, lm_response, lm_response_size, nt_response, nt_response_size, session_key, &error_string) == AUTHENTICATED) { mppe_set_keys(challenge, session_key); slprintf(message, message_space, "Access granted"); return AUTHENTICATED; } else { if (error_string) { notice(error_string); free(error_string); } slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0", challenge_len, challenge); return NOT_AUTHENTICATED; } break; } case CHAP_MICROSOFT_V2: { u_char Challenge[8]; u_char session_key[MD4_SIGNATURE_SIZE]; char *error_string = NULL; if (response_len != MS_CHAP2_RESPONSE_LEN) break; /* not even the right length */ ChallengeHash(&response[MS_CHAP2_PEER_CHALLENGE], challenge, user, Challenge); /* ship off to winbind, and check */ if (run_ntlm_auth(username, domain, NULL, NULL, Challenge, 8, NULL, 0, &response[MS_CHAP2_NTRESP], MS_CHAP2_NTRESP_LEN, session_key, &error_string) == AUTHENTICATED) { GenerateAuthenticatorResponse(session_key, &response[MS_CHAP2_NTRESP], &response[MS_CHAP2_PEER_CHALLENGE], challenge, user, saresponse); mppe_set_keys2(session_key, &response[MS_CHAP2_NTRESP], MS_CHAP2_AUTHENTICATOR); if (response[MS_CHAP2_FLAGS]) { slprintf(message, message_space, "S=%s", saresponse); } else { slprintf(message, message_space, "S=%s M=%s", saresponse, "Access granted"); } return AUTHENTICATED; } else { if (error_string) { notice(error_string); slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", challenge_len, challenge, error_string); free(error_string); } else { slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", challenge_len, challenge, "Access denied"); } return NOT_AUTHENTICATED; } break; } default: error("WINBIND: Challenge type %u unsupported", digest->code); } return NOT_AUTHENTICATED; } static int winbind_allowed_address(u_int32_t addr) { ipcp_options *wo = &ipcp_wantoptions[0]; if (wo->hisaddr !=0 && wo->hisaddr == addr) { return 1; } return -1; } ppp-2.4.5/pppd/ppp.pam000066400000000000000000000002671130035057700145700ustar00rootroot00000000000000#%PAM-1.0 # Information for the PPPD process with the 'login' option. auth required pam_nologin.so auth required pam_unix.so account required pam_unix.so session required pam_unix.so ppp-2.4.5/pppd/pppcrypt.c000066400000000000000000000103071130035057700153130ustar00rootroot00000000000000/* * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1 * * Extracted from chap_ms.c by James Carlson. * * Copyright (c) 1995 Eric Rosenquist. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "pppd.h" #include "pppcrypt.h" static u_char Get7Bits(input, startBit) u_char *input; int startBit; { unsigned int word; word = (unsigned)input[startBit / 8] << 8; word |= (unsigned)input[startBit / 8 + 1]; word >>= 15 - (startBit % 8 + 7); return word & 0xFE; } static void MakeKey(key, des_key) u_char *key; /* IN 56 bit DES key missing parity bits */ u_char *des_key; /* OUT 64 bit DES key with parity bits added */ { des_key[0] = Get7Bits(key, 0); des_key[1] = Get7Bits(key, 7); des_key[2] = Get7Bits(key, 14); des_key[3] = Get7Bits(key, 21); des_key[4] = Get7Bits(key, 28); des_key[5] = Get7Bits(key, 35); des_key[6] = Get7Bits(key, 42); des_key[7] = Get7Bits(key, 49); #ifndef USE_CRYPT des_set_odd_parity((des_cblock *)des_key); #endif } #ifdef USE_CRYPT /* * in == 8-byte string (expanded version of the 56-bit key) * out == 64-byte string where each byte is either 1 or 0 * Note that the low-order "bit" is always ignored by by setkey() */ static void Expand(in, out) u_char *in; u_char *out; { int j, c; int i; for (i = 0; i < 64; in++){ c = *in; for (j = 7; j >= 0; j--) *out++ = (c >> j) & 01; i += 8; } } /* The inverse of Expand */ static void Collapse(in, out) u_char *in; u_char *out; { int j; int i; unsigned int c; for (i = 0; i < 64; i += 8, out++) { c = 0; for (j = 7; j >= 0; j--, in++) c |= *in << j; *out = c & 0xff; } } bool DesSetkey(key) u_char *key; { u_char des_key[8]; u_char crypt_key[66]; MakeKey(key, des_key); Expand(des_key, crypt_key); errno = 0; setkey((const char *)crypt_key); if (errno != 0) return (0); return (1); } bool DesEncrypt(clear, cipher) u_char *clear; /* IN 8 octets */ u_char *cipher; /* OUT 8 octets */ { u_char des_input[66]; Expand(clear, des_input); errno = 0; encrypt((char *)des_input, 0); if (errno != 0) return (0); Collapse(des_input, cipher); return (1); } bool DesDecrypt(cipher, clear) u_char *cipher; /* IN 8 octets */ u_char *clear; /* OUT 8 octets */ { u_char des_input[66]; Expand(cipher, des_input); errno = 0; encrypt((char *)des_input, 1); if (errno != 0) return (0); Collapse(des_input, clear); return (1); } #else /* USE_CRYPT */ static des_key_schedule key_schedule; bool DesSetkey(key) u_char *key; { des_cblock des_key; MakeKey(key, des_key); des_set_key(&des_key, key_schedule); return (1); } bool DesEncrypt(clear, key, cipher) u_char *clear; /* IN 8 octets */ u_char *cipher; /* OUT 8 octets */ { des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); return (1); } bool DesDecrypt(cipher, clear) u_char *cipher; /* IN 8 octets */ u_char *clear; /* OUT 8 octets */ { des_ecb_encrypt((des_cblock *)cipher, (des_cblock *)clear, key_schedule, 0); return (1); } #endif /* USE_CRYPT */ ppp-2.4.5/pppd/pppcrypt.h000066400000000000000000000031711130035057700153210ustar00rootroot00000000000000/* * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1 * * Extracted from chap_ms.c by James Carlson. * * Copyright (c) 1995 Eric Rosenquist. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef PPPCRYPT_H #define PPPCRYPT_H #ifdef HAVE_CRYPT_H #include #endif #ifndef USE_CRYPT #include #endif extern bool DesSetkey __P((u_char *)); extern bool DesEncrypt __P((u_char *, u_char *)); extern bool DesDecrypt __P((u_char *, u_char *)); #endif /* PPPCRYPT_H */ ppp-2.4.5/pppd/pppd.8000066400000000000000000002415421130035057700143310ustar00rootroot00000000000000.\" manual page [] for pppd 2.4 .\" $Id: pppd.8,v 1.90 2008/03/26 12:09:40 paulus Exp $ .\" SH section heading .\" SS subsection heading .\" LP paragraph .\" IP indented paragraph .\" TP hanging label .\" .\" Copyright (c) 1993-2003 Paul Mackerras .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .TH PPPD 8 .SH NAME pppd \- Point-to-Point Protocol Daemon .SH SYNOPSIS .B pppd [ .I options ] .SH DESCRIPTION .LP PPP is the protocol used for establishing internet links over dial-up modems, DSL connections, and many other types of point-to-point links. The \fIpppd\fR daemon works together with the kernel PPP driver to establish and maintain a PPP link with another system (called the \fIpeer\fR) and to negotiate Internet Protocol (IP) addresses for each end of the link. Pppd can also authenticate the peer and/or supply authentication information to the peer. PPP can be used with other network protocols besides IP, but such use is becoming increasingly rare. .SH FREQUENTLY USED OPTIONS .TP .I ttyname Use the serial port called \fIttyname\fR to communicate with the peer. If \fIttyname\fR does not begin with a slash (/), the string "/dev/" is prepended to \fIttyname\fR to form the name of the device to open. If no device name is given, or if the name of the terminal connected to the standard input is given, pppd will use that terminal, and will not fork to put itself in the background. A value for this option from a privileged source cannot be overridden by a non-privileged user. .TP .I speed An option that is a decimal number is taken as the desired baud rate for the serial device. On systems such as 4.4BSD and NetBSD, any speed can be specified. Other systems (e.g. Linux, SunOS) only support the commonly-used baud rates. .TP .B asyncmap \fImap This option sets the Async-Control-Character-Map (ACCM) for this end of the link. The ACCM is a set of 32 bits, one for each of the ASCII control characters with values from 0 to 31, where a 1 bit indicates that the corresponding control character should not be used in PPP packets sent to this system. The map is encoded as a hexadecimal number (without a leading 0x) where the least significant bit (00000001) represents character 0 and the most significant bit (80000000) represents character 31. Pppd will ask the peer to send these characters as a 2-byte escape sequence. If multiple \fIasyncmap\fR options are given, the values are ORed together. If no \fIasyncmap\fR option is given, the default is zero, so pppd will ask the peer not to escape any control characters. To escape transmitted characters, use the \fIescape\fR option. .TP .B auth Require the peer to authenticate itself before allowing network packets to be sent or received. This option is the default if the system has a default route. If neither this option nor the \fInoauth\fR option is specified, pppd will only allow the peer to use IP addresses to which the system does not already have a route. .TP .B call \fIname Read additional options from the file /etc/ppp/peers/\fIname\fR. This file may contain privileged options, such as \fInoauth\fR, even if pppd is not being run by root. The \fIname\fR string may not begin with / or include .. as a pathname component. The format of the options file is described below. .TP .B connect \fIscript Usually there is something which needs to be done to prepare the link before the PPP protocol can be started; for instance, with a dial-up modem, commands need to be sent to the modem to dial the appropriate phone number. This option specifies an command for pppd to execute (by passing it to a shell) before attempting to start PPP negotiation. The chat (8) program is often useful here, as it provides a way to send arbitrary strings to a modem and respond to received characters. A value for this option from a privileged source cannot be overridden by a non-privileged user. .TP .B crtscts Specifies that pppd should set the serial port to use hardware flow control using the RTS and CTS signals in the RS-232 interface. If neither the \fIcrtscts\fR, the \fInocrtscts\fR, the \fIcdtrcts\fR nor the \fInocdtrcts\fR option is given, the hardware flow control setting for the serial port is left unchanged. Some serial ports (such as Macintosh serial ports) lack a true RTS output. Such serial ports use this mode to implement unidirectional flow control. The serial port will suspend transmission when requested by the modem (via CTS) but will be unable to request the modem to stop sending to the computer. This mode retains the ability to use DTR as a modem control line. .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. This option is privileged if the \fInodefaultroute\fR option has been specified. .TP .B disconnect \fIscript Execute the command specified by \fIscript\fR, by passing it to a shell, after pppd has terminated the link. This command could, for example, issue commands to the modem to cause it to hang up if hardware modem control signals were not available. The disconnect script is not run if the modem has already hung up. A value for this option from a privileged source cannot be overridden by a non-privileged user. .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 \fIescape\fR option, unlike the \fIasyncmap\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 \fIname Read options from file \fIname\fR (the format is described below). The file must be readable by the user who has invoked pppd. .TP .B init \fIscript Execute the command specified by \fIscript\fR, by passing it to a shell, to initialize the serial line. This script would typically use the chat(8) program to configure the modem to enable auto answer. A value for this option from a privileged source cannot be overridden by a non-privileged user. .TP .B lock Specifies that pppd should create a UUCP-style lock file for the serial device to ensure exclusive access to the device. By default, pppd will not create a lock file. .TP .B mru \fIn Set the MRU [Maximum Receive Unit] value to \fIn\fR. Pppd will ask the peer to send packets of no more than \fIn\fR bytes. The value of \fIn\fR must be between 128 and 16384; the default is 1500. A value of 296 works well on very slow links (40 bytes for TCP/IP header + 256 bytes of data). Note that for the IPv6 protocol, the MRU must be at least 1280. .TP .B mtu \fIn Set the MTU [Maximum Transmit Unit] value to \fIn\fR. Unless the peer requests a smaller value via MRU negotiation, pppd will request that the kernel networking code send data packets of no more than \fIn\fR bytes through the PPP network interface. Note that for the IPv6 protocol, the MTU must be at least 1280. .TP .B passive Enables the "passive" option in the LCP. With this option, pppd will attempt to initiate a connection; if no reply is received from the peer, pppd will then just wait passively for a valid LCP packet from the peer, instead of exiting, as it would without this option. .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 \fInoipdefault\fR 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, pppd will not accept a different value from the peer in the IPCP negotiation, unless the \fIipcp\-accept\-local\fR and/or \fIipcp\-accept\-remote\fR options are given, respectively. .TP .B ipv6 \fI\fR,\fI Set the local and/or remote 64-bit interface identifier. Either one may be omitted. The identifier must be specified in standard ascii notation of IPv6 addresses (e.g. ::dead:beef). If the \fIipv6cp\-use\-ipaddr\fR option is given, the local identifier is the local IPv4 address (see above). On systems which supports a unique persistent id, such as EUI\-48 derived from the Ethernet MAC address, \fIipv6cp\-use\-persistent\fR option can be used to replace the \fIipv6 ,\fR option. Otherwise the identifier is randomized. .TP .B active\-filter \fIfilter\-expression Specifies a packet filter to be applied to data packets to determine which packets are to be regarded as link activity, and therefore reset the idle timer, or cause the link to be brought up in demand-dialling mode. This option is useful in conjunction with the \fBidle\fR option if there are packets being sent or received regularly over the link (for example, routing information packets) which would otherwise prevent the link from ever appearing to be idle. The \fIfilter\-expression\fR syntax is as described for tcpdump(1), except that qualifiers which are inappropriate for a PPP link, such as \fBether\fR and \fBarp\fR, are not permitted. Generally the filter expression should be enclosed in single-quotes to prevent whitespace in the expression from being interpreted by the shell. This option is currently only available under Linux, and requires that the kernel was configured to include PPP filtering support (CONFIG_PPP_FILTER). Note that it is possible to apply different constraints to incoming and outgoing packets using the \fBinbound\fR and \fBoutbound\fR qualifiers. .TP .B allow\-ip \fIaddress(es) Allow peers to use the given IP address or subnet without authenticating themselves. The parameter is parsed as for each element of the list of allowed IP addresses in the secrets files (see the AUTHENTICATION section below). .TP .B allow\-number \fInumber Allow peers to connect from the given telephone number. A trailing `*' character will match all numbers beginning with the leading part. .TP .B bsdcomp \fInr,nt Request that the peer compress packets that it sends, using the BSD-Compress scheme, with a maximum code size of \fInr\fR bits, and agree to compress packets sent to the peer with a maximum code size of \fInt\fR bits. If \fInt\fR is not specified, it defaults to the value given for \fInr\fR. Values in the range 9 to 15 may be used for \fInr\fR and \fInt\fR; larger values give better compression but consume more kernel memory for compression dictionaries. Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables compression in the corresponding direction. Use \fInobsdcomp\fR or \fIbsdcomp 0\fR to disable BSD-Compress compression entirely. .TP .B cdtrcts Use a non-standard hardware flow control (i.e. DTR/CTS) to control the flow of data on the serial port. If neither the \fIcrtscts\fR, the \fInocrtscts\fR, the \fIcdtrcts\fR nor the \fInocdtrcts\fR option is given, the hardware flow control setting for the serial port is left unchanged. Some serial ports (such as Macintosh serial ports) lack a true RTS output. Such serial ports use this mode to implement true bi-directional flow control. The sacrifice is that this flow control mode does not permit using DTR as a modem control line. .TP .B chap\-interval \fIn If this option is given, pppd will rechallenge the peer every \fIn\fR seconds. .TP .B chap\-max\-challenge \fIn Set the maximum number of CHAP challenge transmissions to \fIn\fR (default 10). .TP .B chap\-restart \fIn Set the CHAP restart interval (retransmission timeout for challenges) to \fIn\fR seconds (default 3). .TP .B child\-timeout \fIn When exiting, wait for up to \fIn\fR seconds for any child processes (such as the command specified with the \fBpty\fR command) to exit before exiting. At the end of the timeout, pppd will send a SIGTERM signal to any remaining child processes and exit. A value of 0 means no timeout, that is, pppd will wait until all child processes have exited. .TP .B connect\-delay \fIn Wait for up to \fIn\fR milliseconds after the connect script finishes for a valid PPP packet from the peer. At the end of this time, or when a valid PPP packet is received from the peer, pppd will commence negotiation by sending its first LCP packet. The default value is 1000 (1 second). This wait period only applies if the \fBconnect\fR or \fBpty\fR option is used. .TP .B debug Enables connection debugging facilities. If this option is given, pppd 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)). .TP .B default\-asyncmap Disable asyncmap negotiation, forcing all control characters to be escaped for both the transmit and the receive direction. .TP .B default\-mru Disable MRU [Maximum Receive Unit] negotiation. With this option, pppd will use the default MRU value of 1500 bytes for both the transmit and receive direction. .TP .B deflate \fInr,nt Request that the peer compress packets that it sends, using the Deflate scheme, with a maximum window size of \fI2**nr\fR bytes, and agree to compress packets sent to the peer with a maximum window size of \fI2**nt\fR bytes. If \fInt\fR is not specified, it defaults to the value given for \fInr\fR. Values in the range 9 to 15 may be used for \fInr\fR and \fInt\fR; larger values give better compression but consume more kernel memory for compression dictionaries. Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables compression in the corresponding direction. Use \fInodeflate\fR or \fIdeflate 0\fR to disable Deflate compression entirely. (Note: pppd requests Deflate compression in preference to BSD-Compress if the peer can do either.) .TP .B demand Initiate the link only on demand, i.e. when data traffic is present. With this option, the remote IP address must be specified by the user on the command line or in an options file. Pppd will initially configure the interface and enable it for IP traffic without connecting to the peer. When traffic is available, pppd will connect to the peer and perform negotiation, authentication, etc. When this is completed, pppd will commence passing data packets (i.e., IP packets) across the link. The \fIdemand\fR option implies the \fIpersist\fR option. If this behaviour is not desired, use the \fInopersist\fR option after the \fIdemand\fR option. The \fIidle\fR and \fIholdoff\fR options are also useful in conjuction with the \fIdemand\fR option. .TP .B domain \fId Append the domain name \fId\fR 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 could specify \fIdomain Quotron.COM\fR. Pppd would then use the name \fIporsche.Quotron.COM\fR for looking up secrets in the secrets file, and as the default name to send to the peer when authenticating itself to the peer. This option is privileged. .TP .B dryrun With the \fBdryrun\fR option, pppd will print out all the option values which have been set and then exit, after parsing the command line and options files and checking the option values, but before initiating the link. The option values are logged at level info, and also printed to standard output unless the device on standard output is the device that pppd would be using to communicate with the peer. .TP .B dump With the \fBdump\fR option, pppd will print out all the option values which have been set. This option is like the \fBdryrun\fR option except that pppd proceeds as normal rather than exiting. .TP .B enable-session Enables session accounting via PAM or wtwp/wtmpx, as appropriate. When PAM is enabled, the PAM "account" and "session" module stacks determine behavior, and are enabled for all PPP authentication protocols. When PAM is disabled, wtmp/wtmpx entries are recorded regardless of whether the peer name identifies a valid user on the local system, making peers visible in the last(1) log. This feature is automatically enabled when the pppd \fBlogin\fR option is used. Session accounting is disabled by default. .TP .B endpoint \fI Sets the endpoint discriminator sent by the local machine to the peer during multilink negotiation to \fI\fR. The default is to use the MAC address of the first ethernet interface on the system, if any, otherwise the IPv4 address corresponding to the hostname, if any, provided it is not in the multicast or locally-assigned IP address ranges, or the localhost address. The endpoint discriminator can be the string \fBnull\fR or of the form \fItype\fR:\fIvalue\fR, where type is a decimal number or one of the strings \fBlocal\fR, \fBIP\fR, \fBMAC\fR, \fBmagic\fR, or \fBphone\fR. The value is an IP address in dotted-decimal notation for the \fBIP\fR type, or a string of bytes in hexadecimal, separated by periods or colons for the other types. For the MAC type, the value may also be the name of an ethernet or similar network interface. This option is currently only available under Linux. .TP .B eap\-interval \fIn If this option is given and pppd authenticates the peer with EAP (i.e., is the server), pppd will restart EAP authentication every \fIn\fR seconds. For EAP SRP\-SHA1, see also the \fBsrp\-interval\fR option, which enables lightweight rechallenge. .TP .B eap\-max\-rreq \fIn Set the maximum number of EAP Requests to which pppd will respond (as a client) without hearing EAP Success or Failure. (Default is 20.) .TP .B eap\-max\-sreq \fIn Set the maximum number of EAP Requests that pppd will issue (as a server) while attempting authentication. (Default is 10.) .TP .B eap\-restart \fIn Set the retransmit timeout for EAP Requests when acting as a server (authenticator). (Default is 3 seconds.) .TP .B eap\-timeout \fIn Set the maximum time to wait for the peer to send an EAP Request when acting as a client (authenticatee). (Default is 20 seconds.) .TP .B hide\-password When logging the contents of PAP packets, this option causes pppd to exclude the password string from the log. This is the default. .TP .B holdoff \fIn Specifies how many seconds to wait before re-initiating the link after it terminates. This option only has any effect if the \fIpersist\fR or \fIdemand\fR option is used. The holdoff period is not applied if the link was terminated because it was idle. .TP .B idle \fIn Specifies that pppd should disconnect if the link is idle for \fIn\fR seconds. The link is idle when no data packets (i.e. IP packets) are being sent or received. Note: it is not advisable to use this option with the \fIpersist\fR option without the \fIdemand\fR option. If the \fBactive\-filter\fR option is given, data packets which are rejected by the specified activity filter also count as the link being idle. .TP .B ipcp\-accept\-local With this option, 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, pppd will accept the peer's idea of its (remote) IP address, even if the remote IP address was specified in an option. .TP .B ipcp\-max\-configure \fIn Set the maximum number of IPCP configure-request transmissions to \fIn\fR (default 10). .TP .B ipcp\-max\-failure \fIn Set the maximum number of IPCP configure-NAKs returned before starting to send configure-Rejects instead to \fIn\fR (default 10). .TP .B ipcp\-max\-terminate \fIn Set the maximum number of IPCP terminate-request transmissions to \fIn\fR (default 3). .TP .B ipcp\-restart \fIn Set the IPCP restart interval (retransmission timeout) to \fIn\fR seconds (default 3). .TP .B ipparam \fIstring Provides an extra parameter to the ip\-up, ip\-pre\-up and ip\-down scripts. If this option is given, the \fIstring\fR supplied is given as the 6th parameter to those scripts. .TP .B ipv6cp\-max\-configure \fIn Set the maximum number of IPv6CP configure-request transmissions to \fIn\fR (default 10). .TP .B ipv6cp\-max\-failure \fIn Set the maximum number of IPv6CP configure-NAKs returned before starting to send configure-Rejects instead to \fIn\fR (default 10). .TP .B ipv6cp\-max\-terminate \fIn Set the maximum number of IPv6CP terminate-request transmissions to \fIn\fR (default 3). .TP .B ipv6cp\-restart \fIn Set the IPv6CP restart interval (retransmission timeout) to \fIn\fR seconds (default 3). .TP .B ipx Enable the IPXCP and IPX protocols. This option is presently only supported under Linux, and only if your kernel has been configured to include IPX support. .TP .B ipx\-network \fIn Set the IPX network number in the IPXCP configure request frame to \fIn\fR, a hexadecimal number (without a leading 0x). There is no valid default. If this option is not specified, the network number is obtained from the peer. If the peer does not have the network number, the IPX protocol will not be started. .TP .B ipx\-node \fIn\fB:\fIm Set the IPX node numbers. The two node numbers are separated from each other with a colon character. The first number \fIn\fR is the local node number. The second number \fIm\fR is the peer's node number. Each node number is a hexadecimal number, at most 10 digits long. The node numbers on the ipx\-network must be unique. There is no valid default. If this option is not specified then the node numbers are obtained from the peer. .TP .B ipx\-router\-name \fI Set the name of the router. This is a string and is sent to the peer as information data. .TP .B ipx\-routing \fIn Set the routing protocol to be received by this option. More than one instance of \fIipx\-routing\fR may be specified. The '\fInone\fR' option (0) may be specified as the only instance of ipx\-routing. The values may be \fI0\fR for \fINONE\fR, \fI2\fR for \fIRIP/SAP\fR, and \fI4\fR for \fINLSP\fR. .TP .B ipxcp\-accept\-local Accept the peer's NAK for the node number specified in the ipx\-node option. If a node number was specified, and non-zero, the default is to insist that the value be used. If you include this option then you will permit the peer to override the entry of the node number. .TP .B ipxcp\-accept\-network Accept the peer's NAK for the network number specified in the ipx\-network option. If a network number was specified, and non-zero, the default is to insist that the value be used. If you include this option then you will permit the peer to override the entry of the node number. .TP .B ipxcp\-accept\-remote Use the peer's network number specified in the configure request frame. If a node number was specified for the peer and this option was not specified, the peer will be forced to use the value which you have specified. .TP .B ipxcp\-max\-configure \fIn Set the maximum number of IPXCP configure request frames which the system will send to \fIn\fR. The default is 10. .TP .B ipxcp\-max\-failure \fIn Set the maximum number of IPXCP NAK frames which the local system will send before it rejects the options. The default value is 3. .TP .B ipxcp\-max\-terminate \fIn Set the maximum nuber of IPXCP terminate request frames before the local system considers that the peer is not listening to them. The default value is 3. .TP .B kdebug \fIn Enable debugging code in the kernel-level PPP driver. The argument values depend on the specific kernel driver, but in general a value of 1 will enable general kernel debug messages. (Note that these messages are usually only useful for debugging the kernel driver itself.) For the Linux 2.2.x kernel driver, the value is a sum of bits: 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. On most systems, messages printed by the kernel are logged by syslog(1) to a file as directed in the /etc/syslog.conf configuration file. .TP .B ktune Enables pppd to alter kernel settings as appropriate. Under Linux, pppd will enable IP forwarding (i.e. set /proc/sys/net/ipv4/ip_forward to 1) if the \fIproxyarp\fR option is used, and will enable the dynamic IP address option (i.e. set /proc/sys/net/ipv4/ip_dynaddr to 1) in demand mode if the local address changes. .TP .B lcp\-echo\-failure \fIn If this option is given, pppd 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, pppd 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 pppd 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\-echo\-interval \fIn If this option is given, pppd will send an LCP echo\-request frame to the peer every \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\-max\-configure \fIn Set the maximum number of LCP configure-request transmissions to \fIn\fR (default 10). .TP .B lcp\-max\-failure \fIn Set the maximum number of LCP configure-NAKs returned before starting to send configure-Rejects instead to \fIn\fR (default 10). .TP .B lcp\-max\-terminate \fIn Set the maximum number of LCP terminate-request transmissions to \fIn\fR (default 3). .TP .B lcp\-restart \fIn Set the LCP restart interval (retransmission timeout) to \fIn\fR seconds (default 3). .TP .B linkname \fIname\fR Sets the logical name of the link to \fIname\fR. Pppd will create a file named \fBppp\-\fIname\fB.pid\fR in /var/run (or /etc/ppp on some systems) containing its process ID. This can be useful in determining which instance of pppd is responsible for the link to a given peer system. This is a privileged option. .TP .B local Don't use the modem control lines. With this option, pppd will ignore the state of the CD (Carrier Detect) signal from the modem and will not change the state of the DTR (Data Terminal Ready) signal. This is the opposite of the \fBmodem\fR option. .TP .B logfd \fIn Send log messages to file descriptor \fIn\fR. Pppd will send log messages to at most one file or file descriptor (as well as sending the log messages to syslog), so this option and the \fBlogfile\fR option are mutually exclusive. The default is for pppd to send log messages to stdout (file descriptor 1), unless the serial port is already open on stdout. .TP .B logfile \fIfilename Append log messages to the file \fIfilename\fR (as well as sending the log messages to syslog). The file is opened with the privileges of the user who invoked pppd, in append mode. .TP .B login Use the system password database for authenticating the peer using PAP, and record the user in the system wtmp file. Note that the peer must have an entry in the /etc/ppp/pap\-secrets file as well as the system password database to be allowed access. See also the \fBenable\-session\fR option. .TP .B maxconnect \fIn Terminate the connection when it has been available for network traffic for \fIn\fR seconds (i.e. \fIn\fR seconds after the first network control protocol comes up). .TP .B maxfail \fIn Terminate after \fIn\fR consecutive failed connection attempts. A value of 0 means no limit. The default value is 10. .TP .B modem Use the modem control lines. This option is the default. With this option, pppd will wait for the CD (Carrier Detect) signal from the modem to be asserted when opening the serial device (unless a connect script is specified), and it will drop the DTR (Data Terminal Ready) signal briefly when the connection is terminated and before executing the connect script. On Ultrix, this option implies hardware flow control, as for the \fIcrtscts\fR option. This is the opposite of the \fBlocal\fR option. .TP .B mp Enables the use of PPP multilink; this is an alias for the `multilink' option. This option is currently only available under Linux. .TP .B mppe\-stateful Allow MPPE to use stateful mode. Stateless mode is still attempted first. The default is to disallow stateful mode. .TP .B mpshortseq Enables the use of short (12-bit) sequence numbers in multilink headers, as opposed to 24-bit sequence numbers. This option is only available under Linux, and only has any effect if multilink is enabled (see the multilink option). .TP .B mrru \fIn Sets the Maximum Reconstructed Receive Unit to \fIn\fR. The MRRU is the maximum size for a received packet on a multilink bundle, and is analogous to the MRU for the individual links. This option is currently only available under Linux, and only has any effect if multilink is enabled (see the multilink option). .TP .B ms\-dns \fI If pppd is acting as a server for Microsoft Windows clients, this option allows pppd to supply one or two DNS (Domain Name Server) addresses to the clients. The first instance of this option specifies the primary DNS address; the second instance (if given) specifies the secondary DNS address. (This option was present in some older versions of pppd under the name \fBdns\-addr\fR.) .TP .B ms\-wins \fI If pppd is acting as a server for Microsoft Windows or "Samba" clients, this option allows pppd to supply one or two WINS (Windows Internet Name Services) server addresses to the clients. The first instance of this option specifies the primary WINS address; the second instance (if given) specifies the secondary WINS address. .TP .B multilink Enables the use of the PPP multilink protocol. If the peer also supports multilink, then this link can become part of a bundle between the local system and the peer. If there is an existing bundle to the peer, pppd will join this link to that bundle, otherwise pppd will create a new bundle. See the MULTILINK section below. This option is currently only available under Linux. .TP .B name \fIname Set the name of the local system for authentication purposes to \fIname\fR. This is a privileged option. With this option, pppd will use lines in the secrets files which have \fIname\fR as the second field when looking for a secret to use in authenticating the peer. In addition, unless overridden with the \fIuser\fR option, \fIname\fR will be used as the name to send to the peer when authenticating the local system to the peer. (Note that pppd does not append the domain name to \fIname\fR.) .TP .B noaccomp Disable Address/Control compression in both directions (send and receive). .TP .B noauth Do not require the peer to authenticate itself. This option is privileged. .TP .B nobsdcomp Disables BSD-Compress compression; \fBpppd\fR will not request or agree to compress packets using the BSD-Compress scheme. .TP .B noccp Disable CCP (Compression Control Protocol) negotiation. This option should only be required if the peer is buggy and gets confused by requests from pppd for CCP negotiation. .TP .B nocrtscts Disable hardware flow control (i.e. RTS/CTS) on the serial port. If neither the \fIcrtscts\fR nor the \fInocrtscts\fR nor the \fIcdtrcts\fR nor the \fInocdtrcts\fR option is given, the hardware flow control setting for the serial port is left unchanged. .TP .B nocdtrcts This option is a synonym for \fInocrtscts\fR. Either of these options will disable both forms of hardware flow control. .TP .B nodefaultroute Disable the \fIdefaultroute\fR option. The system administrator who wishes to prevent users from creating default routes with pppd can do so by placing this option in the /etc/ppp/options file. .TP .B nodeflate Disables Deflate compression; pppd will not request or agree to compress packets using the Deflate scheme. .TP .B nodetach Don't detach from the controlling terminal. Without this option, if a serial device other than the terminal on the standard input is specified, pppd will fork to become a background process. .TP .B noendpoint Disables pppd from sending an endpoint discriminator to the peer or accepting one from the peer (see the MULTILINK section below). This option should only be required if the peer is buggy. .TP .B noip Disable IPCP negotiation and IP communication. This option should only be required if the peer is buggy and gets confused by requests from pppd for IPCP negotiation. .TP .B noipv6 Disable IPv6CP negotiation and IPv6 communication. This option should only be required if the peer is buggy and gets confused by requests from pppd for IPv6CP negotiation. .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 noipx Disable the IPXCP and IPX protocols. This option should only be required if the peer is buggy and gets confused by requests from pppd for IPXCP negotiation. .TP .B noktune Opposite of the \fIktune\fR option; disables pppd from changing system settings. .TP .B nolock Opposite of the \fIlock\fR option; specifies that pppd should not create a UUCP-style lock file for the serial device. This option is privileged. .TP .B nolog Do not send log messages to a file or file descriptor. This option cancels the \fBlogfd\fR and \fBlogfile\fR options. .TP .B nomagic Disable magic number negotiation. With this option, pppd cannot detect a looped-back line. This option should only be needed if the peer is buggy. .TP .B nomp Disables the use of PPP multilink. This option is currently only available under Linux. .TP .B nomppe Disables MPPE (Microsoft Point to Point Encryption). This is the default. .TP .B nomppe\-40 Disable 40-bit encryption with MPPE. .TP .B nomppe\-128 Disable 128-bit encryption with MPPE. .TP .B nomppe\-stateful Disable MPPE stateful mode. This is the default. .TP .B nompshortseq Disables the use of short (12-bit) sequence numbers in the PPP multilink protocol, forcing the use of 24-bit sequence numbers. This option is currently only available under Linux, and only has any effect if multilink is enabled. .TP .B nomultilink Disables the use of PPP multilink. This option is currently only available under Linux. .TP .B nopcomp Disable protocol field compression negotiation in both the receive and the transmit direction. .TP .B nopersist Exit once a connection has been made and terminated. This is the default unless the \fIpersist\fR or \fIdemand\fR option has been specified. .TP .B nopredictor1 Do not accept or agree to Predictor\-1 compression. .TP .B noproxyarp Disable the \fIproxyarp\fR option. The system administrator who wishes to prevent users from creating proxy ARP entries with pppd can do so by placing this option in the /etc/ppp/options file. .TP .B noremoteip Allow pppd to operate without having an IP address for the peer. This option is only available under Linux. Normally, pppd will request the peer's IP address, and if the peer does not supply it, pppd will not bring up the link for IP traffic. With this option, if the peer does not supply its IP address, pppd will not ask the peer for it, and will not set the destination address of the ppp interface. In this situation, the ppp interface can be used for routing by creating device routes, but the peer itself cannot be addressed directly for IP traffic. .TP .B notty Normally, pppd requires a terminal device. With this option, pppd will allocate itself a pseudo-tty master/slave pair and use the slave as its terminal device. Pppd will create a child process to act as a `character shunt' to transfer characters between the pseudo-tty master and its standard input and output. Thus pppd will transmit characters on its standard output and receive characters on its standard input even if they are not terminal devices. This option increases the latency and CPU overhead of transferring data over the ppp interface as all of the characters sent and received must flow through the character shunt process. An explicit device name may not be given if this option is used. .TP .B novj Disable Van Jacobson style TCP/IP header compression in both the transmit and the receive direction. .TP .B novjccomp Disable the connection-ID compression option in Van Jacobson style TCP/IP header compression. With this option, pppd will not omit the connection-ID byte from Van Jacobson compressed TCP/IP headers, nor ask the peer to do so. .TP .B papcrypt Indicates that all secrets in the /etc/ppp/pap\-secrets file which are used for checking the identity of the peer are encrypted, and thus pppd should not accept a password which, before encryption, is identical to the secret from the /etc/ppp/pap\-secrets file. .TP .B pap\-max\-authreq \fIn Set the maximum number of PAP authenticate-request transmissions to \fIn\fR (default 10). .TP .B pap\-restart \fIn Set the PAP restart interval (retransmission timeout) to \fIn\fR seconds (default 3). .TP .B pap\-timeout \fIn Set the maximum time that pppd will wait for the peer to authenticate itself with PAP to \fIn\fR seconds (0 means no limit). .TP .B pass\-filter \fIfilter\-expression Specifies a packet filter to applied to data packets being sent or received to determine which packets should be allowed to pass. Packets which are rejected by the filter are silently discarded. This option can be used to prevent specific network daemons (such as routed) using up link bandwidth, or to provide a very basic firewall capability. The \fIfilter\-expression\fR syntax is as described for tcpdump(1), except that qualifiers which are inappropriate for a PPP link, such as \fBether\fR and \fBarp\fR, are not permitted. Generally the filter expression should be enclosed in single-quotes to prevent whitespace in the expression from being interpreted by the shell. Note that it is possible to apply different constraints to incoming and outgoing packets using the \fBinbound\fR and \fBoutbound\fR qualifiers. This option is currently only available under Linux, and requires that the kernel was configured to include PPP filtering support (CONFIG_PPP_FILTER). .TP .B password \fIpassword\-string Specifies the password to use for authenticating to the peer. Use of this option is discouraged, as the password is likely to be visible to other users on the system (for example, by using ps(1)). .TP .B persist Do not exit after a connection is terminated; instead try to reopen the connection. The \fBmaxfail\fR option still has an effect on persistent connections. .TP .B plugin \fIfilename Load the shared library object file \fIfilename\fR as a plugin. This is a privileged option. If \fIfilename\fR does not contain a slash (/), pppd will look in the \fB/usr/lib/pppd/\fIversion\fR directory for the plugin, where \fIversion\fR is the version number of pppd (for example, 2.4.2). .TP .B predictor1 Request that the peer compress frames that it sends using Predictor-1 compression, and agree to compress transmitted frames with Predictor-1 if requested. This option has no effect unless the kernel driver supports Predictor-1 compression. .TP .B privgroup \fIgroup\-name Allows members of group \fIgroup\-name\fR to use privileged options. This is a privileged option. Use of this option requires care as there is no guarantee that members of \fIgroup\-name\fR cannot use pppd to become root themselves. Consider it equivalent to putting the members of \fIgroup\-name\fR in the kmem or disk group. .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. This will have the effect of making the peer appear to other systems to be on the local ethernet. .TP .B pty \fIscript Specifies that the command \fIscript\fR is to be used to communicate rather than a specific terminal device. Pppd will allocate itself a pseudo-tty master/slave pair and use the slave as its terminal device. The \fIscript\fR will be run in a child process with the pseudo-tty master as its standard input and output. An explicit device name may not be given if this option is used. (Note: if the \fIrecord\fR option is used in conjuction with the \fIpty\fR option, the child process will have pipes on its standard input and output.) .TP .B receive\-all With this option, pppd will accept all control characters from the peer, including those marked in the receive asyncmap. Without this option, pppd will discard those characters as specified in RFC1662. This option should only be needed if the peer is buggy. .TP .B record \fIfilename Specifies that pppd should record all characters sent and received to a file named \fIfilename\fR. This file is opened in append mode, using the user's user-ID and permissions. This option is implemented using a pseudo-tty and a process to transfer characters between the pseudo-tty and the real serial device, so it will increase the latency and CPU overhead of transferring data over the ppp interface. The characters are stored in a tagged format with timestamps, which can be displayed in readable form using the pppdump(8) program. .TP .B remotename \fIname Set the assumed name of the remote system for authentication purposes to \fIname\fR. .TP .B remotenumber \fInumber Set the assumed telephone number of the remote system for authentication purposes to \fInumber\fR. .TP .B refuse\-chap With this option, pppd will not agree to authenticate itself to the peer using CHAP. .TP .B refuse\-mschap With this option, pppd will not agree to authenticate itself to the peer using MS\-CHAP. .TP .B refuse\-mschap\-v2 With this option, pppd will not agree to authenticate itself to the peer using MS\-CHAPv2. .TP .B refuse\-eap With this option, pppd will not agree to authenticate itself to the peer using EAP. .TP .B refuse\-pap With this option, pppd will not agree to authenticate itself to the peer using PAP. .TP .B require\-chap Require the peer to authenticate itself using CHAP [Challenge Handshake Authentication Protocol] authentication. .TP .B require\-mppe Require the use of MPPE (Microsoft Point to Point Encryption). This option disables all other compression types. This option enables both 40-bit and 128-bit encryption. In order for MPPE to successfully come up, you must have authenticated with either MS\-CHAP or MS\-CHAPv2. This option is presently only supported under Linux, and only if your kernel has been configured to include MPPE support. .TP .B require\-mppe\-40 Require the use of MPPE, with 40-bit encryption. .TP .B require\-mppe\-128 Require the use of MPPE, with 128-bit encryption. .TP .B require\-mschap Require the peer to authenticate itself using MS\-CHAP [Microsoft Challenge Handshake Authentication Protocol] authentication. .TP .B require\-mschap\-v2 Require the peer to authenticate itself using MS\-CHAPv2 [Microsoft Challenge Handshake Authentication Protocol, Version 2] authentication. .TP .B require\-eap Require the peer to authenticate itself using EAP [Extensible Authentication Protocol] authentication. .TP .B require\-pap Require the peer to authenticate itself using PAP [Password Authentication Protocol] authentication. .TP .B show\-password When logging the contents of PAP packets, this option causes pppd to show the password string in the log message. .TP .B silent With this option, 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 ancient versions of pppd). .TP .B srp\-interval \fIn If this parameter is given and pppd uses EAP SRP\-SHA1 to authenticate the peer (i.e., is the server), then pppd will use the optional lightweight SRP rechallenge mechanism at intervals of \fIn\fR seconds. This option is faster than \fBeap\-interval\fR reauthentication because it uses a hash\-based mechanism and does not derive a new session key. .TP .B srp\-pn\-secret \fIstring Set the long-term pseudonym-generating secret for the server. This value is optional and if set, needs to be known at the server (authenticator) side only, and should be different for each server (or poll of identical servers). It is used along with the current date to generate a key to encrypt and decrypt the client's identity contained in the pseudonym. .TP .B srp\-use\-pseudonym When operating as an EAP SRP\-SHA1 client, attempt to use the pseudonym stored in ~/.ppp_psuedonym first as the identity, and save in this file any pseudonym offered by the peer during authentication. .TP .B sync Use synchronous HDLC serial encoding instead of asynchronous. The device used by pppd with this option must have sync support. Currently supports Microgate SyncLink adapters under Linux and FreeBSD 2.2.8 and later. .TP .B unit \fInum Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound connections. .TP .B updetach With this option, pppd will detach from its controlling terminal once it has successfully established the ppp connection (to the point where the first network control protocol, usually the IP control protocol, has come up). .TP .B usehostname Enforce the use of the hostname (with domain name appended, if given) as the name of the local system for authentication purposes (overrides the \fIname\fR option). This option is not normally needed since the \fIname\fR option is privileged. .TP .B usepeerdns Ask the peer for up to 2 DNS server addresses. The addresses supplied by the peer (if any) are passed to the /etc/ppp/ip\-up script in the environment variables DNS1 and DNS2, and the environment variable USEPEERDNS will be set to 1. In addition, pppd will create an /etc/ppp/resolv.conf file containing one or two nameserver lines with the address(es) supplied by the peer. .TP .B user \fIname Sets the name used for authenticating the local system to the peer to \fIname\fR. .TP .B vj\-max\-slots \fIn Sets the number of connection slots to be used by the Van Jacobson TCP/IP header compression and decompression code to \fIn\fR, which must be between 2 and 16 (inclusive). .TP .B welcome \fIscript Run the executable or shell command specified by \fIscript\fR before initiating PPP negotiation, after the connect script (if any) has completed. A value for this option from a privileged source cannot be overridden by a non-privileged user. .TP .B xonxoff Use software flow control (i.e. XON/XOFF) to control the flow of data on the serial port. .SH OPTIONS FILES Options can be taken from files as well as the command line. Pppd reads options from the files /etc/ppp/options, ~/.ppprc and /etc/ppp/options.\fIttyname\fR (in that order) before processing the options on the command line. (In fact, the command-line options are scanned to find the terminal name before the options.\fIttyname\fR file is read.) In forming the name of the options.\fIttyname\fR file, the initial /dev/ is removed from the terminal name, and any remaining / characters are replaced with dots. .PP 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 double-quotes ("). A backslash (\\) quotes the following character. A hash (#) starts a comment, which continues until the end of the line. There is no restriction on using the \fIfile\fR or \fIcall\fR options within an options file. .SH SECURITY .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. This control is provided through restrictions on which IP addresses the peer may use, based on its authenticated identity (if any), and through restrictions on which options a non-privileged user may use. Several of pppd's options are privileged, in particular those which permit potentially insecure configurations; these options are only accepted in files which are under the control of the system administrator, or if pppd is being run by root. .PP The default behaviour of pppd is to allow an unauthenticated peer to use a given IP address only if the system does not already have a route to that IP address. For example, a system with a permanent connection to the wider internet will normally have a default route, and thus all peers will have to authenticate themselves in order to set up a connection. On such a system, the \fIauth\fR option is the default. On the other hand, a system where the PPP link is the only connection to the internet will not normally have a default route, so the peer will be able to use almost any IP address without authenticating itself. .PP As indicated above, some security-sensitive options are privileged, which means that they may not be used by an ordinary non-privileged user running a setuid-root pppd, either on the command line, in the user's ~/.ppprc file, or in an options file read using the \fIfile\fR option. Privileged options may be used in /etc/ppp/options file or in an options file read using the \fIcall\fR option. If pppd is being run by the root user, privileged options can be used without restriction. .PP When opening the device, pppd uses either the invoking user's user ID or the root UID (that is, 0), depending on whether the device name was specified by the user or the system administrator. If the device name comes from a privileged source, that is, /etc/ppp/options or an options file read using the \fIcall\fR option, pppd uses full root privileges when opening the device. Thus, by creating an appropriate file under /etc/ppp/peers, the system administrator can allow users to establish a ppp connection via a device which they would not normally have permission to access. Otherwise pppd uses the invoking user's real UID when opening the device. .SH AUTHENTICATION Authentication is the process whereby one peer convinces the other of its identity. This involves the first peer sending its name to the other, together with some kind of secret information which could only come from the genuine authorized user of that name. In such an exchange, we will call the first peer the "client" and the other the "server". The client has a name by which it identifies itself to the server, and the server also has a name by which it identifies itself to the client. Generally the genuine client shares some secret (or password) with the server, and authenticates itself by proving that it knows that secret. Very often, the names used for authentication correspond to the internet hostnames of the peers, but this is not essential. .LP At present, pppd supports three authentication protocols: the Password Authentication Protocol (PAP), Challenge Handshake Authentication Protocol (CHAP), and Extensible Authentication Protocol (EAP). PAP involves the client sending its name and a cleartext password to the server to authenticate itself. In contrast, the server initiates the CHAP authentication exchange by sending a challenge to the client (the challenge packet includes the server's name). The client must respond with a response which includes its name plus a hash value derived from the shared secret and the challenge, in order to prove that it knows the secret. EAP supports CHAP-style authentication, and also includes the SRP\-SHA1 mechanism, which is resistant to dictionary-based attacks and does not require a cleartext password on the server side. .LP The PPP protocol, being symmetrical, allows both peers to require the other to authenticate itself. In that case, two separate and independent authentication exchanges will occur. The two exchanges could use different authentication protocols, and in principle, different names could be used in the two exchanges. .LP The default behaviour of pppd is 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. .LP Pppd stores secrets for use in authentication in secrets files (/etc/ppp/pap\-secrets for PAP, /etc/ppp/chap\-secrets for CHAP, MS\-CHAP, MS\-CHAPv2, and EAP MD5-Challenge, and /etc/ppp/srp\-secrets for EAP SRP\-SHA1). All secrets files have the same format. The secrets files can contain secrets for pppd to use in authenticating itself to other systems, as well as secrets for pppd to use when authenticating other systems to itself. .LP Each line in a secrets file contains one secret. A given secret is specific to a particular combination of client and server - it can only be used by that client to authenticate itself to that server. Thus each line in a secrets file has at least 3 fields: the name of the client, the name of the server, and the secret. These fields may be followed by a list of the IP addresses that the specified client may use when connecting to the specified server. .LP A secrets file is parsed into words as for a options file, so the client name, server name and secrets fields must each be one word, with any embedded spaces or other special characters quoted or escaped. Note that case is significant in the client and server names and in the secret. .LP 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. .LP 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, or if the first word is "\-", then all IP addresses are disallowed. To allow any address, use "*". A word starting with "!" indicates that the specified address is \fInot\fR acceptable. An address may be followed by "/" and a number \fIn\fR, to indicate a whole subnet, i.e. all addresses which have the same value in the most significant \fIn\fR bits. In this form, the address may be followed by a plus sign ("+") to indicate that one address from the subnet is authorized, based on the ppp network interface unit number in use. In this case, the host part of the address will be set to the unit number plus one. .LP Thus a secrets file contains both secrets for use in authenticating other hosts, plus secrets which we use for authenticating ourselves to others. When pppd is authenticating the peer (checking the peer's identity), it chooses a secret with the peer's name in the first field and the name of the local system in the second field. The name of the local system defaults to the hostname, with the domain name appended if the \fIdomain\fR option is used. This default can be overridden with the \fIname\fR option, except when the \fIusehostname\fR option is used. (For EAP SRP\-SHA1, see the srp\-entry(8) utility for generating proper validator entries to be used in the "secret" field.) .LP When pppd is choosing a secret to use in authenticating itself to the peer, it first determines what name it is going to use to identify itself to the peer. This name can be specified by the user with the \fIuser\fR option. If this option is not used, the name defaults to the name of the local system, determined as described in the previous paragraph. Then pppd looks for a secret with this name in the first field and the peer's name in the second field. Pppd will know the name of the peer if CHAP or EAP authentication is being used, because the peer will have sent it in the challenge packet. However, if PAP is being used, pppd will have to determine the peer's name from the options specified by the user. The user can specify the peer's name directly with the \fIremotename\fR option. Otherwise, if the remote IP address was specified by a name (rather than in numeric form), that name will be used as the peer's name. Failing that, pppd will use the null string as the peer's name. .LP When authenticating the peer with PAP, the supplied password is first compared with the secret from the secrets file. 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 desired. If the \fIpapcrypt\fR option is given, the first (unencrypted) comparison is omitted, for better security. .LP Furthermore, if the \fIlogin\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. Typically, when using the \fIlogin\fR option, the secret in /etc/ppp/pap\-secrets would be "", which will match any password supplied by the peer. This avoids the need to have the same secret in two places. .LP Authentication must be satisfactorily completed before IPCP (or any other Network Control Protocol) can be started. If the peer is required to authenticate itself, and fails to do so, 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 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, pppd 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, 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 most cases the \fIdefaultroute\fR and/or \fIproxyarp\fR options are sufficient for this, but in some cases further intervention is required. The /etc/ppp/ip\-up script can be used for this. .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 \fIdefaultroute\fR option causes pppd 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 \fIproxyarp\fR option causes pppd 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, pppd creates a permanent, published ARP entry with the IP address of the remote host and the hardware address of the network interface found. .LP When the \fIdemand\fR option is used, the interface IP addresses have already been set at the point when IPCP comes up. If pppd has not been able to negotiate the same addresses that it used to configure the interface (for example when the peer is an ISP that uses dynamic IP address assignment), pppd has to change the interface IP addresses to the negotiated addresses. This may disrupt existing connections, and the use of demand dialling with peers that do dynamic IP address assignment is not recommended. .SH MULTILINK Multilink PPP provides the capability to combine two or more PPP links between a pair of machines into a single `bundle', which appears as a single virtual PPP link which has the combined bandwidth of the individual links. Currently, multilink PPP is only supported under Linux. .LP Pppd detects that the link it is controlling is connected to the same peer as another link using the peer's endpoint discriminator and the authenticated identity of the peer (if it authenticates itself). The endpoint discriminator is a block of data which is hopefully unique for each peer. Several types of data can be used, including locally-assigned strings of bytes, IP addresses, MAC addresses, randomly strings of bytes, or E\-164 phone numbers. The endpoint discriminator sent to the peer by pppd can be set using the endpoint option. .LP In some circumstances the peer may send no endpoint discriminator or a non-unique value. The bundle option adds an extra string which is added to the peer's endpoint discriminator and authenticated identity when matching up links to be joined together in a bundle. The bundle option can also be used to allow the establishment of multiple bundles between the local system and the peer. Pppd uses a TDB database in /var/run/pppd2.tdb to match up links. .LP Assuming that multilink is enabled and the peer is willing to negotiate multilink, then when pppd is invoked to bring up the first link to the peer, it will detect that no other link is connected to the peer and create a new bundle, that is, another ppp network interface unit. When another pppd is invoked to bring up another link to the peer, it will detect the existing bundle and join its link to it. .LP If the first link terminates (for example, because of a hangup or a received LCP terminate-request) the bundle is not destroyed unless there are no other links remaining in the bundle. Rather than exiting, the first pppd keeps running after its link terminates, until all the links in the bundle have terminated. If the first pppd receives a SIGTERM or SIGINT signal, it will destroy the bundle and send a SIGHUP to the pppd processes for each of the links in the bundle. If the first pppd receives a SIGHUP signal, it will terminate its link but not the bundle. .LP Note: demand mode is not currently supported with multilink. .SH EXAMPLES .LP The following examples assume that the /etc/ppp/options file contains the \fIauth\fR option (as in the default /etc/ppp/options file in the ppp distribution). .LP Probably the most common use of pppd is to dial out to an ISP. This can be done with a command such as .IP pppd call isp .LP where the /etc/ppp/peers/isp file is set up by the system administrator to contain something like this: .IP ttyS0 19200 crtscts .br connect '/usr/sbin/chat \-v \-f /etc/ppp/chat\-isp' .br noauth .LP In this example, we are using chat to dial the ISP's modem and go through any logon sequence required. The /etc/ppp/chat\-isp file contains the script used by chat; it could for example contain something like this: .IP ABORT "NO CARRIER" .br ABORT "NO DIALTONE" .br ABORT "ERROR" .br ABORT "NO ANSWER" .br ABORT "BUSY" .br ABORT "Username/Password Incorrect" .br "" "at" .br OK "at&d0&c1" .br OK "atdt2468135" .br "name:" "^Umyuserid" .br "word:" "\\qmypassword" .br "ispts" "\\q^Uppp" .br "~\-^Uppp\-~" .LP See the chat(8) man page for details of chat scripts. .LP Pppd can also be used to provide a dial-in ppp service for users. If the users already have login accounts, the simplest way to set up the ppp service is to let the users log in to their accounts and run pppd (installed setuid-root) with a command such as .IP pppd proxyarp .LP To allow a user to use the PPP facilities, you need to allocate an IP address for that user's machine and create an entry in /etc/ppp/pap\-secrets, /etc/ppp/chap\-secrets, or /etc/ppp/srp\-secrets (depending on which authentication method the PPP implementation on the user's machine supports), so that the user's machine can authenticate itself. For example, if Joe has a machine called "joespc" that is to be allowed to dial in to the machine called "server" and use the IP address joespc.my.net, you would add an entry like this to /etc/ppp/pap\-secrets or /etc/ppp/chap\-secrets: .IP joespc server "joe's secret" joespc.my.net .LP (See srp\-entry(8) for a means to generate the server's entry when SRP\-SHA1 is in use.) Alternatively, you can create a username called (for example) "ppp", whose login shell is pppd and whose home directory is /etc/ppp. Options to be used when pppd is run this way can be put in /etc/ppp/.ppprc. .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 \fIasyncmap a0000\fR. If the path includes a telnet, you probably should escape ^] as well (\fIasyncmap 200a0000\fR). If the path includes an rlogin, you will need to use the \fIescape 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 overridden by recompiling pppd with the macro LOG_PPP defined as the desired facility.) See the syslog(8) documentation for details of where the syslog daemon will write the messages. On most systems, the syslog daemon uses the /etc/syslog.conf file to specify the destination(s) for syslog messages. You may need to edit that file to suit. .LP The \fIdebug\fR option causes the contents of all control packets sent or received to be logged, that is, all LCP, PAP, CHAP, EAP, or IPCP packets. This can be useful if the PPP negotiation does not succeed or if authentication fails. If debugging is enabled at compile time, the \fIdebug\fR option also causes other debugging messages to be logged. .LP Debugging can also be enabled or disabled by sending a SIGUSR1 signal to the pppd process. This signal acts as a toggle. .SH EXIT STATUS The exit status of pppd is set to indicate whether any error was detected, or the reason for the link being terminated. The values used are: .TP .B 0 Pppd has detached, or otherwise the connection was successfully established and terminated at the peer's request. .TP .B 1 An immediately fatal error of some kind occurred, such as an essential system call failing, or running out of virtual memory. .TP .B 2 An error was detected in processing the options given, such as two mutually exclusive options being used. .TP .B 3 Pppd is not setuid-root and the invoking user is not root. .TP .B 4 The kernel does not support PPP, for example, the PPP kernel driver is not included or cannot be loaded. .TP .B 5 Pppd terminated because it was sent a SIGINT, SIGTERM or SIGHUP signal. .TP .B 6 The serial port could not be locked. .TP .B 7 The serial port could not be opened. .TP .B 8 The connect script failed (returned a non-zero exit status). .TP .B 9 The command specified as the argument to the \fIpty\fR option could not be run. .TP .B 10 The PPP negotiation failed, that is, it didn't reach the point where at least one network protocol (e.g. IP) was running. .TP .B 11 The peer system failed (or refused) to authenticate itself. .TP .B 12 The link was established successfully and terminated because it was idle. .TP .B 13 The link was established successfully and terminated because the connect time limit was reached. .TP .B 14 Callback was negotiated and an incoming call should arrive shortly. .TP .B 15 The link was terminated because the peer is not responding to echo requests. .TP .B 16 The link was terminated by the modem hanging up. .TP .B 17 The PPP negotiation failed because serial loopback was detected. .TP .B 18 The init script failed (returned a non-zero exit status). .TP .B 19 We failed to authenticate ourselves to the peer. .SH SCRIPTS Pppd invokes scripts at various stages in its processing which can be used to perform site-specific ancillary processing. These scripts are usually shell scripts, but could be executable code files instead. Pppd does not wait for the scripts to finish (except for the ip-pre-up script). The scripts are executed as root (with the real and effective user-id set to 0), so that they can do things such as update routing tables or run privileged daemons. Be careful that the contents of these scripts do not compromise your system's security. Pppd runs the scripts with standard input, output and error redirected to /dev/null, and with an environment that is empty except for some environment variables that give information about the link. The environment variables that pppd sets are: .TP .B DEVICE The name of the serial tty device being used. .TP .B IFNAME The name of the network interface being used. .TP .B IPLOCAL The IP address for the local end of the link. This is only set when IPCP has come up. .TP .B IPREMOTE The IP address for the remote end of the link. This is only set when IPCP has come up. .TP .B PEERNAME The authenticated name of the peer. This is only set if the peer authenticates itself. .TP .B SPEED The baud rate of the tty device. .TP .B ORIG_UID The real user-id of the user who invoked pppd. .TP .B PPPLOGNAME The username of the real user-id that invoked pppd. This is always set. .P For the ip-down and auth-down scripts, pppd also sets the following variables giving statistics for the connection: .TP .B CONNECT_TIME The number of seconds from when the PPP negotiation started until the connection was terminated. .TP .B BYTES_SENT The number of bytes sent (at the level of the serial port) during the connection. .TP .B BYTES_RCVD The number of bytes received (at the level of the serial port) during the connection. .TP .B LINKNAME The logical name of the link, set with the \fIlinkname\fR option. .TP .B DNS1 If the peer supplies DNS server addresses, this variable is set to the first DNS server address supplied. .TP .B DNS2 If the peer supplies DNS server addresses, this variable is set to the second DNS server address supplied. .P Pppd invokes the following scripts, if they exist. It is not an error if they don't exist. .TP .B /etc/ppp/auth\-up A program or script which is executed after the remote system successfully authenticates itself. It is executed with the parameters .IP \fIinterface\-name peer\-name user\-name tty\-device speed\fR .IP Note that this script is not executed if the peer doesn't authenticate itself, for example when the \fInoauth\fR option is used. .TP .B /etc/ppp/auth\-down A program or script which is executed when the link goes down, if /etc/ppp/auth\-up was previously executed. It is executed in the same manner with the same parameters as /etc/ppp/auth\-up. .TP .B /etc/ppp/ip\-pre\-up A program or script which is executed just before the ppp network interface is brought up. It is executed with the same parameters as the ip\-up script (below). At this point the interface exists and has IP addresses assigned but is still down. This can be used to add firewall rules before any IP traffic can pass through the interface. Pppd will wait for this script to finish before bringing the interface up, so this script should run quickly. .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 .IP \fIinterface\-name tty\-device speed local\-IP\-address remote\-IP\-address ipparam\fR .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 and /etc/ppp/ip\-pre\-up scripts. It is invoked in the same manner and with the same parameters as the ip\-up script. .TP .B /etc/ppp/ipv6\-up Like /etc/ppp/ip\-up, except that it is executed when the link is available for sending and receiving IPv6 packets. It is executed with the parameters .IP \fIinterface\-name tty\-device speed local\-link\-local\-address remote\-link\-local\-address ipparam\fR .TP .B /etc/ppp/ipv6\-down Similar to /etc/ppp/ip\-down, but it is executed when IPv6 packets can no longer be transmitted on the link. It is executed with the same parameters as the ipv6\-up script. .TP .B /etc/ppp/ipx\-up A program or script which is executed when the link is available for sending and receiving IPX packets (that is, IPXCP has come up). It is executed with the parameters .IP \fIinterface\-name tty\-device speed network\-number local\-IPX\-node\-address remote\-IPX\-node\-address local\-IPX\-routing\-protocol remote\-IPX\-routing\-protocol local\-IPX\-router\-name remote\-IPX\-router\-name ipparam pppd\-pid\fR .IP The local\-IPX\-routing\-protocol and remote\-IPX\-routing\-protocol field may be one of the following: .IP NONE to indicate that there is no routing protocol .br RIP to indicate that RIP/SAP should be used .br NLSP to indicate that Novell NLSP should be used .br RIP NLSP to indicate that both RIP/SAP and NLSP should be used .TP .B /etc/ppp/ipx\-down A program or script which is executed when the link is no longer available for sending and receiving IPX packets. This script can be used for undoing the effects of the /etc/ppp/ipx\-up script. It is invoked in the same manner and with the same parameters as the ipx\-up script. .SH FILES .TP .B /var/run/ppp\fIn\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp\fIn\fB.pid \fR(others) Process-ID for pppd process on ppp interface unit \fIn\fR. .TP .B /var/run/ppp\-\fIname\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp\-\fIname\fB.pid \fR(others) Process-ID for pppd process for logical link \fIname\fR (see the \fIlinkname\fR option). .TP .B /var/run/pppd2.tdb Database containing information about pppd processes, interfaces and links, used for matching links to bundles in multilink operation. May be examined by external programs to obtain information about running pppd instances, the interfaces and devices they are using, IP address assignments, etc. .B /etc/ppp/pap\-secrets Usernames, passwords and IP addresses for PAP authentication. This file should be owned by root and not readable or writable by any other user. Pppd will log a warning if this is not the case. .TP .B /etc/ppp/chap\-secrets Names, secrets and IP addresses for CHAP/MS\-CHAP/MS\-CHAPv2 authentication. As for /etc/ppp/pap\-secrets, this file should be owned by root and not readable or writable by any other user. Pppd will log a warning if this is not the case. .TP .B /etc/ppp/srp\-secrets Names, secrets, and IP addresses for EAP authentication. As for /etc/ppp/pap\-secrets, this file should be owned by root and not readable or writable by any other user. Pppd will log a warning if this is not the case. .TP .B ~/.ppp_pseudonym Saved client-side SRP\-SHA1 pseudonym. See the \fIsrp\-use\-pseudonym\fR option for details. .TP .B /etc/ppp/options System default options for pppd, read before user default options or command-line options. .TP .B ~/.ppprc User default options, read before /etc/ppp/options.\fIttyname\fR. .TP .B /etc/ppp/options.\fIttyname System default options for the serial port being used, read after ~/.ppprc. In forming the \fIttyname\fR part of this filename, an initial /dev/ is stripped from the port name (if present), and any slashes in the remaining part are converted to dots. .TP .B /etc/ppp/peers A directory containing options files which may contain privileged options, even if pppd was invoked by a user other than root. The system administrator can create options files in this directory to permit non-privileged users to dial out without requiring the peer to authenticate, but only to certain trusted peers. .SH SEE ALSO .BR chat (8), .BR pppstats (8) .TP .B RFC1144 Jacobson, V. \fICompressing TCP/IP headers for low-speed serial links.\fR February 1990. .TP .B RFC1321 Rivest, R. .I The MD5 Message-Digest Algorithm. April 1992. .TP .B RFC1332 McGregor, G. .I PPP Internet Protocol Control Protocol (IPCP). May 1992. .TP .B RFC1334 Lloyd, B.; Simpson, W.A. .I PPP authentication protocols. October 1992. .TP .B RFC1661 Simpson, W.A. .I The Point-to-Point Protocol (PPP). July 1994. .TP .B RFC1662 Simpson, W.A. .I PPP in HDLC-like Framing. July 1994. .TP .B RFC2284 Blunk, L.; Vollbrecht, J., .I PPP Extensible Authentication Protocol (EAP). March 1998. .TP .B RFC2472 Haskin, D. .I IP Version 6 over PPP December 1998. .TP .B RFC2945 Wu, T., .I The SRP Authentication and Key Exchange System September 2000. .TP .B draft\-ietf\-pppext\-eap\-srp\-03.txt Carlson, J.; et al., .I EAP SRP\-SHA1 Authentication Protocol. July 2001. .SH NOTES Some limited degree of control can be exercised over a running pppd process by sending it a signal from the list below. .TP .B SIGINT, SIGTERM These signals cause pppd to terminate the link (by closing LCP), restore the serial device settings, and exit. If a connector or disconnector process is currently running, pppd will send the same signal to its process group, so as to terminate the connector or disconnector process. .TP .B SIGHUP This signal causes pppd to terminate the link, restore the serial device settings, and close the serial device. If the \fIpersist\fR or \fIdemand\fR option has been specified, pppd will try to reopen the serial device and start another connection (after the holdoff period). Otherwise pppd will exit. If this signal is received during the holdoff period, it causes pppd to end the holdoff period immediately. If a connector or disconnector process is running, pppd will send the same signal to its process group. .TP .B SIGUSR1 This signal toggles the state of the \fIdebug\fR option. .TP .B SIGUSR2 This signal causes pppd to renegotiate compression. This can be useful to re-enable compression after it has been disabled as a result of a fatal decompression error. (Fatal decompression errors generally indicate a bug in one or other implementation.) .SH AUTHORS Paul Mackerras (paulus@samba.org), based on earlier work by Drew Perkins, Brad Clements, Karl Fox, Greg Christy, and Brad Parker. .SH COPYRIGHT Pppd is copyrighted and made available under conditions which provide that it may be copied and used in source or binary forms provided that the conditions listed below are met. Portions of pppd are covered by the following copyright notices: .LP Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. .br Copyright (c) 1993-2004 Paul Mackerras. All rights reserved. .br Copyright (c) 1995 Pedro Roque Marques. All rights reserved. .br Copyright (c) 1995 Eric Rosenquist. All rights reserved. .br Copyright (c) 1999 Tommi Komulainen. All rights reserved. .br Copyright (C) Andrew Tridgell 1999 .br Copyright (c) 2000 by Sun Microsystems, Inc. All rights reserved. .br Copyright (c) 2001 by Sun Microsystems, Inc. All rights reserved. .br Copyright (c) 2002 Google, Inc. All rights reserved. .LP The copyright notices contain the following statements. .LP Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: .LP 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. .LP 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. .LP 3. The name "Carnegie Mellon University" must not be used to endorse or promote products derived from this software without prior written permission. For permission or any legal details, please contact .br Office of Technology Transfer .br Carnegie Mellon University .br 5000 Forbes Avenue .br Pittsburgh, PA 15213-3890 .br (412) 268-4387, fax: (412) 268-7395 .br tech-transfer@andrew.cmu.edu .LP 3b. The name(s) of the authors of this software must not be used to endorse or promote products derived from this software without prior written permission. .LP 4. Redistributions of any form whatsoever must retain the following acknowledgments: .br "This product includes software developed by Computing Services at Carnegie Mellon University (http://www.cmu.edu/computing/)." .br "This product includes software developed by Paul Mackerras ". .br "This product includes software developed by Pedro Roque Marques ". .br "This product includes software developed by Tommi Komulainen ". .LP CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .LP THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ppp-2.4.5/pppd/pppd.h000066400000000000000000001046521130035057700144110ustar00rootroot00000000000000/* * pppd.h - PPP daemon global declarations. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: pppd.h,v 1.96 2008/06/23 11:47:18 paulus Exp $ */ /* * TODO: */ #ifndef __PPPD_H__ #define __PPPD_H__ #include /* for FILE */ #include /* for NGROUPS_MAX */ #include /* for MAXPATHLEN and BSD4_4, if defined */ #include /* for u_int32_t, if defined */ #include /* for struct timeval */ #include #include "patchlevel.h" #if defined(__STDC__) #include #define __V(x) x #else #include #define __V(x) (va_alist) va_dcl #define const #define volatile #endif #ifdef INET6 #include "eui64.h" #endif /* * Limits. */ #define NUM_PPP 1 /* One PPP interface supported (per process) */ #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 */ /* * Option descriptor structure. */ typedef unsigned char bool; enum opt_type { o_special_noarg = 0, o_special = 1, o_bool, o_int, o_uint32, o_string, o_wild }; typedef struct { char *name; /* name of the option */ enum opt_type type; void *addr; char *description; unsigned int flags; void *addr2; int upper_limit; int lower_limit; const char *source; short int priority; short int winner; } option_t; /* Values for flags */ #define OPT_VALUE 0xff /* mask for presupplied value */ #define OPT_HEX 0x100 /* int option is in hex */ #define OPT_NOARG 0x200 /* option doesn't take argument */ #define OPT_OR 0x400 /* for u32, OR in argument to value */ #define OPT_INC 0x400 /* for o_int, increment value */ #define OPT_A2OR 0x800 /* for o_bool, OR arg to *(u_char *)addr2 */ #define OPT_PRIV 0x1000 /* privileged option */ #define OPT_STATIC 0x2000 /* string option goes into static array */ #define OPT_NOINCR 0x2000 /* for o_int, value mustn't be increased */ #define OPT_LLIMIT 0x4000 /* check value against lower limit */ #define OPT_ULIMIT 0x8000 /* check value against upper limit */ #define OPT_LIMITS (OPT_LLIMIT|OPT_ULIMIT) #define OPT_ZEROOK 0x10000 /* 0 value is OK even if not within limits */ #define OPT_HIDE 0x10000 /* for o_string, print value as ?????? */ #define OPT_A2LIST 0x20000 /* for o_special, keep list of values */ #define OPT_A2CLRB 0x20000 /* o_bool, clr val bits in *(u_char *)addr2 */ #define OPT_ZEROINF 0x40000 /* with OPT_NOINCR, 0 == infinity */ #define OPT_PRIO 0x80000 /* process option priorities for this option */ #define OPT_PRIOSUB 0x100000 /* subsidiary member of priority group */ #define OPT_ALIAS 0x200000 /* option is alias for previous option */ #define OPT_A2COPY 0x400000 /* addr2 -> second location to rcv value */ #define OPT_ENABLE 0x800000 /* use *addr2 as enable for option */ #define OPT_A2CLR 0x1000000 /* clear *(bool *)addr2 */ #define OPT_PRIVFIX 0x2000000 /* user can't override if set by root */ #define OPT_INITONLY 0x4000000 /* option can only be set in init phase */ #define OPT_DEVEQUIV 0x8000000 /* equiv to device name */ #define OPT_DEVNAM (OPT_INITONLY | OPT_DEVEQUIV) #define OPT_A2PRINTER 0x10000000 /* *addr2 is a fn for printing option */ #define OPT_A2STRVAL 0x20000000 /* *addr2 points to current string value */ #define OPT_NOPRINT 0x40000000 /* don't print this option at all */ #define OPT_VAL(x) ((x) & OPT_VALUE) /* Values for priority */ #define OPRIO_DEFAULT 0 /* a default value */ #define OPRIO_CFGFILE 1 /* value from a configuration file */ #define OPRIO_CMDLINE 2 /* value from the command line */ #define OPRIO_SECFILE 3 /* value from options in a secrets file */ #define OPRIO_ROOT 100 /* added to priority if OPT_PRIVFIX && root */ #ifndef GIDSET_TYPE #define GIDSET_TYPE gid_t #endif /* Structure representing a list of permitted IP addresses. */ struct permitted_ip { int permit; /* 1 = permit, 0 = forbid */ u_int32_t base; /* match if (addr & mask) == base */ u_int32_t mask; /* base and mask are in network byte order */ }; /* * Unfortunately, the linux kernel driver uses a different structure * for statistics from the rest of the ports. * This structure serves as a common representation for the bits * pppd needs. */ struct pppd_stats { unsigned int bytes_in; unsigned int bytes_out; unsigned int pkts_in; unsigned int pkts_out; }; /* Used for storing a sequence of words. Usually malloced. */ struct wordlist { struct wordlist *next; char *word; }; /* An endpoint discriminator, used with multilink. */ #define MAX_ENDP_LEN 20 /* maximum length of discriminator value */ struct epdisc { unsigned char class; unsigned char length; unsigned char value[MAX_ENDP_LEN]; }; /* values for epdisc.class */ #define EPD_NULL 0 /* null discriminator, no data */ #define EPD_LOCAL 1 #define EPD_IP 2 #define EPD_MAC 3 #define EPD_MAGIC 4 #define EPD_PHONENUM 5 typedef void (*notify_func) __P((void *, int)); struct notifier { struct notifier *next; notify_func func; void *arg; }; /* * Global variables. */ extern int hungup; /* Physical layer has disconnected */ extern int ifunit; /* Interface unit number */ extern char ifname[]; /* Interface name */ extern char hostname[]; /* Our hostname */ extern u_char outpacket_buf[]; /* Buffer for outgoing packets */ extern int devfd; /* fd of underlying device */ extern int fd_ppp; /* fd for talking PPP */ extern int phase; /* Current state of link - see values below */ extern int baud_rate; /* Current link speed in bits/sec */ extern char *progname; /* Name of this program */ extern int redirect_stderr;/* Connector's stderr should go to file */ extern char peer_authname[];/* Authenticated name of peer */ extern int auth_done[NUM_PPP]; /* Methods actually used for auth */ extern int privileged; /* We were run by real-uid root */ extern int need_holdoff; /* Need holdoff period after link terminates */ extern char **script_env; /* Environment variables for scripts */ extern int detached; /* Have detached from controlling tty */ extern GIDSET_TYPE groups[NGROUPS_MAX]; /* groups the user is in */ extern int ngroups; /* How many groups valid in groups */ extern struct pppd_stats link_stats; /* byte/packet counts etc. for link */ extern int link_stats_valid; /* set if link_stats is valid */ extern unsigned link_connect_time; /* time the link was up for */ extern int using_pty; /* using pty as device (notty or pty opt.) */ extern int log_to_fd; /* logging to this fd as well as syslog */ extern bool log_default; /* log_to_fd is default (stdout) */ extern char *no_ppp_msg; /* message to print if ppp not in kernel */ extern volatile int status; /* exit status for pppd */ extern bool devnam_fixed; /* can no longer change devnam */ extern int unsuccess; /* # unsuccessful connection attempts */ extern int do_callback; /* set if we want to do callback next */ extern int doing_callback; /* set if this is a callback */ extern int error_count; /* # of times error() has been called */ extern char ppp_devnam[MAXPATHLEN]; extern char remote_number[MAXNAMELEN]; /* Remote telephone number, if avail. */ extern int ppp_session_number; /* Session number (eg PPPoE session) */ extern int fd_devnull; /* fd open to /dev/null */ extern int listen_time; /* time to listen first (ms) */ extern bool doing_multilink; extern bool multilink_master; extern bool bundle_eof; extern bool bundle_terminating; extern struct notifier *pidchange; /* for notifications of pid changing */ extern struct notifier *phasechange; /* for notifications of phase changes */ extern struct notifier *exitnotify; /* for notification that we're exiting */ extern struct notifier *sigreceived; /* notification of received signal */ extern struct notifier *ip_up_notifier; /* IPCP has come up */ extern struct notifier *ip_down_notifier; /* IPCP has gone down */ extern struct notifier *auth_up_notifier; /* peer has authenticated */ extern struct notifier *link_down_notifier; /* link has gone down */ extern struct notifier *fork_notifier; /* we are a new child process */ /* Values for do_callback and doing_callback */ #define CALLBACK_DIALIN 1 /* we are expecting the call back */ #define CALLBACK_DIALOUT 2 /* we are dialling out to call back */ /* * Variables set by command-line options. */ extern int debug; /* Debug flag */ extern int kdebugflag; /* Tell kernel to print debug messages */ extern int default_device; /* Using /dev/tty or equivalent */ extern char devnam[MAXPATHLEN]; /* Device name */ extern int crtscts; /* Use hardware flow control */ extern bool modem; /* Use modem control lines */ extern int inspeed; /* Input/Output speed requested */ extern u_int32_t netmask; /* IP netmask to set on interface */ extern bool lockflag; /* Create lock file to lock the serial dev */ extern bool nodetach; /* Don't detach from controlling tty */ extern bool updetach; /* Detach from controlling tty when link up */ extern char *initializer; /* Script to initialize physical link */ extern char *connect_script; /* Script to establish physical link */ extern char *disconnect_script; /* Script to disestablish physical link */ extern char *welcomer; /* Script to welcome client after connection */ extern char *ptycommand; /* Command to run on other side of pty */ extern int maxconnect; /* Maximum connect time (seconds) */ extern char user[MAXNAMELEN];/* Our name for authenticating ourselves */ extern char passwd[MAXSECRETLEN]; /* Password for PAP or CHAP */ extern bool auth_required; /* Peer is required to authenticate */ extern bool persist; /* Reopen link after it goes down */ extern bool uselogin; /* Use /etc/passwd for checking PAP */ extern bool session_mgmt; /* Do session management (login records) */ extern char our_name[MAXNAMELEN];/* Our name for authentication purposes */ extern char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ extern bool explicit_remote;/* remote_name specified with remotename opt */ extern bool demand; /* Do dial-on-demand */ extern char *ipparam; /* Extra parameter for ip up/down scripts */ extern bool cryptpap; /* Others' PAP passwords are encrypted */ extern int idle_time_limit;/* Shut down link if idle for this long */ extern int holdoff; /* Dead time before restarting */ extern bool holdoff_specified; /* true if user gave a holdoff value */ extern bool notty; /* Stdin/out is not a tty */ extern char *pty_socket; /* Socket to connect to pty */ extern char *record_file; /* File to record chars sent/received */ extern bool sync_serial; /* Device is synchronous serial device */ extern int maxfail; /* Max # of unsuccessful connection attempts */ extern char linkname[MAXPATHLEN]; /* logical name for link */ extern bool tune_kernel; /* May alter kernel settings as necessary */ extern int connect_delay; /* Time to delay after connect script */ extern int max_data_rate; /* max bytes/sec through charshunt */ extern int req_unit; /* interface unit number to use */ extern bool multilink; /* enable multilink operation */ extern bool noendpoint; /* don't send or accept endpt. discrim. */ extern char *bundle_name; /* bundle name for multilink */ extern bool dump_options; /* print out option values */ extern bool dryrun; /* check everything, print options, exit */ extern int child_wait; /* # seconds to wait for children at end */ #ifdef MAXOCTETS extern unsigned int maxoctets; /* Maximum octetes per session (in bytes) */ extern int maxoctets_dir; /* Direction : 0 - in+out (default) 1 - in 2 - out 3 - max(in,out) */ extern int maxoctets_timeout; /* Timeout for check of octets limit */ #define PPP_OCTETS_DIRECTION_SUM 0 #define PPP_OCTETS_DIRECTION_IN 1 #define PPP_OCTETS_DIRECTION_OUT 2 #define PPP_OCTETS_DIRECTION_MAXOVERAL 3 /* same as previos, but little different on RADIUS side */ #define PPP_OCTETS_DIRECTION_MAXSESSION 4 #endif #ifdef PPP_FILTER extern struct bpf_program pass_filter; /* Filter for pkts to pass */ extern struct bpf_program active_filter; /* Filter for link-active pkts */ #endif #ifdef MSLANMAN extern bool ms_lanman; /* Use LanMan password instead of NT */ /* Has meaning only with MS-CHAP challenges */ #endif /* Values for auth_pending, auth_done */ #define PAP_WITHPEER 0x1 #define PAP_PEER 0x2 #define CHAP_WITHPEER 0x4 #define CHAP_PEER 0x8 #define EAP_WITHPEER 0x10 #define EAP_PEER 0x20 /* Values for auth_done only */ #define CHAP_MD5_WITHPEER 0x40 #define CHAP_MD5_PEER 0x80 #define CHAP_MS_SHIFT 8 /* LSB position for MS auths */ #define CHAP_MS_WITHPEER 0x100 #define CHAP_MS_PEER 0x200 #define CHAP_MS2_WITHPEER 0x400 #define CHAP_MS2_PEER 0x800 extern char *current_option; /* the name of the option being parsed */ extern int privileged_option; /* set iff the current option came from root */ extern char *option_source; /* string saying where the option came from */ extern int option_priority; /* priority of current options */ /* * Values for phase. */ #define PHASE_DEAD 0 #define PHASE_INITIALIZE 1 #define PHASE_SERIALCONN 2 #define PHASE_DORMANT 3 #define PHASE_ESTABLISH 4 #define PHASE_AUTHENTICATE 5 #define PHASE_CALLBACK 6 #define PHASE_NETWORK 7 #define PHASE_RUNNING 8 #define PHASE_TERMINATE 9 #define PHASE_DISCONNECT 10 #define PHASE_HOLDOFF 11 #define PHASE_MASTER 12 /* * The following struct gives the addresses of procedures to call * for a particular protocol. */ struct protent { u_short protocol; /* PPP protocol number */ /* Initialization procedure */ void (*init) __P((int unit)); /* Process a received packet */ void (*input) __P((int unit, u_char *pkt, int len)); /* Process a received protocol-reject */ void (*protrej) __P((int unit)); /* Lower layer has come up */ void (*lowerup) __P((int unit)); /* Lower layer has gone down */ void (*lowerdown) __P((int unit)); /* Open the protocol */ void (*open) __P((int unit)); /* Close the protocol */ void (*close) __P((int unit, char *reason)); /* Print a packet in readable form */ int (*printpkt) __P((u_char *pkt, int len, void (*printer) __P((void *, char *, ...)), void *arg)); /* Process a received data packet */ void (*datainput) __P((int unit, u_char *pkt, int len)); bool enabled_flag; /* 0 iff protocol is disabled */ char *name; /* Text name of protocol */ char *data_name; /* Text name of corresponding data protocol */ option_t *options; /* List of command-line options */ /* Check requested options, assign defaults */ void (*check_options) __P((void)); /* Configure interface for demand-dial */ int (*demand_conf) __P((int unit)); /* Say whether to bring up link for this pkt */ int (*active_pkt) __P((u_char *pkt, int len)); }; /* Table of pointers to supported protocols */ extern struct protent *protocols[]; /* * This struct contains pointers to a set of procedures for * doing operations on a "channel". A channel provides a way * to send and receive PPP packets - the canonical example is * a serial port device in PPP line discipline (or equivalently * with PPP STREAMS modules pushed onto it). */ struct channel { /* set of options for this channel */ option_t *options; /* find and process a per-channel options file */ void (*process_extra_options) __P((void)); /* check all the options that have been given */ void (*check_options) __P((void)); /* get the channel ready to do PPP, return a file descriptor */ int (*connect) __P((void)); /* we're finished with the channel */ void (*disconnect) __P((void)); /* put the channel into PPP `mode' */ int (*establish_ppp) __P((int)); /* take the channel out of PPP `mode', restore loopback if demand */ void (*disestablish_ppp) __P((int)); /* set the transmit-side PPP parameters of the channel */ void (*send_config) __P((int, u_int32_t, int, int)); /* set the receive-side PPP parameters of the channel */ void (*recv_config) __P((int, u_int32_t, int, int)); /* cleanup on error or normal exit */ void (*cleanup) __P((void)); /* close the device, called in children after fork */ void (*close) __P((void)); }; extern struct channel *the_channel; /* * Prototypes. */ /* Procedures exported from main.c. */ void set_ifunit __P((int)); /* set stuff that depends on ifunit */ void detach __P((void)); /* Detach from controlling tty */ void die __P((int)); /* Cleanup and exit */ void quit __P((void)); /* like die(1) */ void novm __P((char *)); /* Say we ran out of memory, and die */ void timeout __P((void (*func)(void *), void *arg, int s, int us)); /* Call func(arg) after s.us seconds */ void untimeout __P((void (*func)(void *), void *arg)); /* Cancel call to func(arg) */ void record_child __P((int, char *, void (*) (void *), void *, int)); pid_t safe_fork __P((int, int, int)); /* Fork & close stuff in child */ int device_script __P((char *cmd, int in, int out, int dont_wait)); /* Run `cmd' with given stdin and stdout */ pid_t run_program __P((char *prog, char **args, int must_exist, void (*done)(void *), void *arg, int wait)); /* Run program prog with args in child */ void reopen_log __P((void)); /* (re)open the connection to syslog */ void print_link_stats __P((void)); /* Print stats, if available */ void reset_link_stats __P((int)); /* Reset (init) stats when link goes up */ void update_link_stats __P((int)); /* Get stats at link termination */ void script_setenv __P((char *, char *, int)); /* set script env var */ void script_unsetenv __P((char *)); /* unset script env var */ void new_phase __P((int)); /* signal start of new phase */ void add_notifier __P((struct notifier **, notify_func, void *)); void remove_notifier __P((struct notifier **, notify_func, void *)); void notify __P((struct notifier *, int)); int ppp_send_config __P((int, int, u_int32_t, int, int)); int ppp_recv_config __P((int, int, u_int32_t, int, int)); const char *protocol_name __P((int)); void remove_pidfiles __P((void)); void lock_db __P((void)); void unlock_db __P((void)); /* Procedures exported from tty.c. */ void tty_init __P((void)); /* Procedures exported from utils.c. */ void log_packet __P((u_char *, int, char *, int)); /* Format a packet and log it with syslog */ void print_string __P((char *, int, void (*) (void *, char *, ...), void *)); /* Format a string for output */ int slprintf __P((char *, int, char *, ...)); /* sprintf++ */ int vslprintf __P((char *, int, char *, va_list)); /* vsprintf++ */ size_t strlcpy __P((char *, const char *, size_t)); /* safe strcpy */ size_t strlcat __P((char *, const char *, size_t)); /* safe strncpy */ void dbglog __P((char *, ...)); /* log a debug message */ void info __P((char *, ...)); /* log an informational message */ void notice __P((char *, ...)); /* log a notice-level message */ void warn __P((char *, ...)); /* log a warning message */ void error __P((char *, ...)); /* log an error message */ void fatal __P((char *, ...)); /* log an error message and die(1) */ void init_pr_log __P((const char *, int)); /* initialize for using pr_log */ void pr_log __P((void *, char *, ...)); /* printer fn, output to syslog */ void end_pr_log __P((void)); /* finish up after using pr_log */ void dump_packet __P((const char *, u_char *, int)); /* dump packet to debug log if interesting */ ssize_t complete_read __P((int, void *, size_t)); /* read a complete buffer */ /* Procedures exported from auth.c */ void link_required __P((int)); /* we are starting to use the link */ void start_link __P((int)); /* bring the link up now */ void link_terminated __P((int)); /* we are finished with the link */ void link_down __P((int)); /* the LCP layer has left the Opened state */ void upper_layers_down __P((int));/* take all NCPs down */ void link_established __P((int)); /* the link is up; authenticate now */ void start_networks __P((int)); /* start all the network control protos */ void continue_networks __P((int)); /* start network [ip, etc] control protos */ void np_up __P((int, int)); /* a network protocol has come up */ void np_down __P((int, int)); /* a network protocol has gone down */ void np_finished __P((int, int)); /* a network protocol no longer needs link */ void auth_peer_fail __P((int, int)); /* peer failed to authenticate itself */ void auth_peer_success __P((int, int, int, char *, int)); /* peer successfully authenticated itself */ void auth_withpeer_fail __P((int, int)); /* we failed to authenticate ourselves */ void auth_withpeer_success __P((int, int, int)); /* we successfully authenticated ourselves */ void auth_check_options __P((void)); /* check authentication options supplied */ void auth_reset __P((int)); /* check what secrets we have */ int check_passwd __P((int, char *, int, char *, int, char **)); /* Check peer-supplied username/password */ int get_secret __P((int, char *, char *, char *, int *, int)); /* get "secret" for chap */ int get_srp_secret __P((int unit, char *client, char *server, char *secret, int am_server)); int auth_ip_addr __P((int, u_int32_t)); /* check if IP address is authorized */ int auth_number __P((void)); /* check if remote number is authorized */ int bad_ip_adrs __P((u_int32_t)); /* check if IP address is unreasonable */ /* Procedures exported from demand.c */ void demand_conf __P((void)); /* config interface(s) for demand-dial */ void demand_block __P((void)); /* set all NPs to queue up packets */ void demand_unblock __P((void)); /* set all NPs to pass packets */ void demand_discard __P((void)); /* set all NPs to discard packets */ void demand_rexmit __P((int)); /* retransmit saved frames for an NP */ int loop_chars __P((unsigned char *, int)); /* process chars from loopback */ int loop_frame __P((unsigned char *, int)); /* should we bring link up? */ /* Procedures exported from multilink.c */ #ifdef HAVE_MULTILINK void mp_check_options __P((void)); /* Check multilink-related options */ int mp_join_bundle __P((void)); /* join our link to an appropriate bundle */ void mp_exit_bundle __P((void)); /* have disconnected our link from bundle */ void mp_bundle_terminated __P((void)); char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */ int str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */ #else #define mp_bundle_terminated() /* nothing */ #define mp_exit_bundle() /* nothing */ #define doing_multilink 0 #define multilink_master 0 #endif /* Procedures exported from sys-*.c */ void sys_init __P((void)); /* Do system-dependent initialization */ void sys_cleanup __P((void)); /* Restore system state before exiting */ int sys_check_options __P((void)); /* Check options specified */ void sys_close __P((void)); /* Clean up in a child before execing */ int ppp_available __P((void)); /* Test whether ppp kernel support exists */ int get_pty __P((int *, int *, char *, int)); /* Get pty master/slave */ int open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */ int tty_establish_ppp __P((int)); /* Turn serial port into a ppp interface */ void tty_disestablish_ppp __P((int)); /* Restore port to normal operation */ void generic_disestablish_ppp __P((int dev_fd)); /* Restore device setting */ int generic_establish_ppp __P((int dev_fd)); /* Make a ppp interface */ void make_new_bundle __P((int, int, int, int)); /* Create new bundle */ int bundle_attach __P((int)); /* Attach link to existing bundle */ void cfg_bundle __P((int, int, int, int)); /* Configure existing bundle */ void destroy_bundle __P((void)); /* Tell driver to destroy bundle */ void clean_check __P((void)); /* Check if line was 8-bit clean */ void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */ void restore_tty __P((int)); /* Restore port's original parameters */ void setdtr __P((int, int)); /* Raise or lower port's DTR line */ void output __P((int, u_char *, int)); /* Output a PPP packet */ void wait_input __P((struct timeval *)); /* Wait for input, with timeout */ void add_fd __P((int)); /* Add fd to set to wait for */ void remove_fd __P((int)); /* Remove fd from set to wait for */ int read_packet __P((u_char *)); /* Read PPP packet */ int get_loop_output __P((void)); /* Read pkts from loopback */ void tty_send_config __P((int, u_int32_t, int, int)); /* Configure i/f transmit parameters */ void tty_set_xaccm __P((ext_accm)); /* Set extended transmit ACCM */ void tty_recv_config __P((int, u_int32_t, int, int)); /* Configure i/f receive parameters */ int ccp_test __P((int, u_char *, int, int)); /* Test support for compression scheme */ void ccp_flags_set __P((int, int, int)); /* Set kernel CCP state */ int ccp_fatal_error __P((int)); /* Test for fatal decomp error in kernel */ int get_idle_time __P((int, struct ppp_idle *)); /* Find out how long link has been idle */ int get_ppp_stats __P((int, struct pppd_stats *)); /* Return link statistics */ void netif_set_mtu __P((int, int)); /* Set PPP interface MTU */ int netif_get_mtu __P((int)); /* Get PPP interface MTU */ int sifvjcomp __P((int, int, int, int)); /* Configure VJ TCP header compression */ int sifup __P((int)); /* Configure i/f up for one protocol */ int sifnpmode __P((int u, int proto, enum NPmode mode)); /* Set mode for handling packets for proto */ int sifdown __P((int)); /* Configure i/f down for one protocol */ int sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t)); /* Configure IPv4 addresses for i/f */ int cifaddr __P((int, u_int32_t, u_int32_t)); /* Reset i/f IP addresses */ #ifdef INET6 int sif6addr __P((int, eui64_t, eui64_t)); /* Configure IPv6 addresses for i/f */ int cif6addr __P((int, eui64_t, eui64_t)); /* Remove an IPv6 address from i/f */ #endif int sifdefaultroute __P((int, u_int32_t, u_int32_t)); /* Create default route through i/f */ int cifdefaultroute __P((int, u_int32_t, u_int32_t)); /* Delete default route through i/f */ int sifproxyarp __P((int, u_int32_t)); /* Add proxy ARP entry for peer */ int cifproxyarp __P((int, u_int32_t)); /* Delete proxy ARP entry for peer */ u_int32_t GetMask __P((u_int32_t)); /* Get appropriate netmask for address */ int lock __P((char *)); /* Create lock file for device */ int relock __P((int)); /* Rewrite lock file with new pid */ void unlock __P((void)); /* Delete previously-created lock file */ void logwtmp __P((const char *, const char *, const char *)); /* Write entry to wtmp file */ int get_host_seed __P((void)); /* Get host-dependent random number seed */ int have_route_to __P((u_int32_t)); /* Check if route to addr exists */ #ifdef PPP_FILTER int set_filters __P((struct bpf_program *pass, struct bpf_program *active)); /* Set filter programs in kernel */ #endif #ifdef IPX_CHANGE int sipxfaddr __P((int, unsigned long, unsigned char *)); int cipxfaddr __P((int)); #endif int get_if_hwaddr __P((u_char *addr, char *name)); char *get_first_ethernet __P((void)); /* Procedures exported from options.c */ int setipaddr __P((char *, char **, int)); /* Set local/remote ip addresses */ int parse_args __P((int argc, char **argv)); /* Parse options from arguments given */ int options_from_file __P((char *filename, int must_exist, int check_prot, int privileged)); /* Parse options from an options file */ int options_from_user __P((void)); /* Parse options from user's .ppprc */ int options_for_tty __P((void)); /* Parse options from /etc/ppp/options.tty */ int options_from_list __P((struct wordlist *, int privileged)); /* Parse options from a wordlist */ int getword __P((FILE *f, char *word, int *newlinep, char *filename)); /* Read a word from a file */ void option_error __P((char *fmt, ...)); /* Print an error message about an option */ int int_option __P((char *, int *)); /* Simplified number_option for decimal ints */ void add_options __P((option_t *)); /* Add extra options */ void check_options __P((void)); /* check values after all options parsed */ int override_value __P((const char *, int, const char *)); /* override value if permitted by priority */ void print_options __P((void (*) __P((void *, char *, ...)), void *)); /* print out values of all options */ int parse_dotted_ip __P((char *, u_int32_t *)); /* * Hooks to enable plugins to change various things. */ extern int (*new_phase_hook) __P((int)); extern int (*idle_time_hook) __P((struct ppp_idle *)); extern int (*holdoff_hook) __P((void)); extern int (*pap_check_hook) __P((void)); extern int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp, struct wordlist **paddrs, struct wordlist **popts)); extern void (*pap_logout_hook) __P((void)); extern int (*pap_passwd_hook) __P((char *user, char *passwd)); extern int (*allowed_address_hook) __P((u_int32_t addr)); extern void (*ip_up_hook) __P((void)); extern void (*ip_down_hook) __P((void)); extern void (*ip_choose_hook) __P((u_int32_t *)); extern int (*chap_check_hook) __P((void)); extern int (*chap_passwd_hook) __P((char *user, char *passwd)); extern void (*multilink_join_hook) __P((void)); /* Let a plugin snoop sent and received packets. Useful for L2TP */ extern void (*snoop_recv_hook) __P((unsigned char *p, int len)); extern void (*snoop_send_hook) __P((unsigned char *p, int len)); /* * 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 TIMEOUT(r, f, t) timeout((r), (f), (t), 0) #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 BCMP(s1, s2, l) memcmp(s1, s2, l) #define PRINTMSG(m, l) { info("Remote message: %0.*v", l, m); } /* * MAKEHEADER - Add Header fields to a packet. */ #define MAKEHEADER(p, t) { \ PUTCHAR(PPP_ALLSTATIONS, p); \ PUTCHAR(PPP_UI, p); \ PUTSHORT(t, p); } /* * Exit status values. */ #define EXIT_OK 0 #define EXIT_FATAL_ERROR 1 #define EXIT_OPTION_ERROR 2 #define EXIT_NOT_ROOT 3 #define EXIT_NO_KERNEL_SUPPORT 4 #define EXIT_USER_REQUEST 5 #define EXIT_LOCK_FAILED 6 #define EXIT_OPEN_FAILED 7 #define EXIT_CONNECT_FAILED 8 #define EXIT_PTYCMD_FAILED 9 #define EXIT_NEGOTIATION_FAILED 10 #define EXIT_PEER_AUTH_FAILED 11 #define EXIT_IDLE_TIMEOUT 12 #define EXIT_CONNECT_TIME 13 #define EXIT_CALLBACK 14 #define EXIT_PEER_DEAD 15 #define EXIT_HANGUP 16 #define EXIT_LOOPBACK 17 #define EXIT_INIT_FAILED 18 #define EXIT_AUTH_TOPEER_FAILED 19 #ifdef MAXOCTETS #define EXIT_TRAFFIC_LIMIT 20 #endif #define EXIT_CNID_AUTH_FAILED 21 /* * Debug macros. Slightly useful for finding bugs in pppd, not particularly * useful for finding out why your connection isn't being established. */ #ifdef DEBUGALL #define DEBUGMAIN 1 #define DEBUGFSM 1 #define DEBUGLCP 1 #define DEBUGIPCP 1 #define DEBUGIPV6CP 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(DEBUGSYS) \ || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \ || defined(DEBUGCHAP) || defined(DEBUG) || defined(DEBUGIPV6CP) #define LOG_PPP LOG_LOCAL2 #else #define LOG_PPP LOG_DAEMON #endif #endif /* LOG_PPP */ #ifdef DEBUGMAIN #define MAINDEBUG(x) if (debug) dbglog x #else #define MAINDEBUG(x) #endif #ifdef DEBUGSYS #define SYSDEBUG(x) if (debug) dbglog x #else #define SYSDEBUG(x) #endif #ifdef DEBUGFSM #define FSMDEBUG(x) if (debug) dbglog x #else #define FSMDEBUG(x) #endif #ifdef DEBUGLCP #define LCPDEBUG(x) if (debug) dbglog x #else #define LCPDEBUG(x) #endif #ifdef DEBUGIPCP #define IPCPDEBUG(x) if (debug) dbglog x #else #define IPCPDEBUG(x) #endif #ifdef DEBUGIPV6CP #define IPV6CPDEBUG(x) if (debug) dbglog x #else #define IPV6CPDEBUG(x) #endif #ifdef DEBUGUPAP #define UPAPDEBUG(x) if (debug) dbglog x #else #define UPAPDEBUG(x) #endif #ifdef DEBUGCHAP #define CHAPDEBUG(x) if (debug) dbglog x #else #define CHAPDEBUG(x) #endif #ifdef DEBUGIPXCP #define IPXCPDEBUG(x) if (debug) dbglog x #else #define IPXCPDEBUG(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 #ifndef offsetof #define offsetof(type, member) ((size_t) &((type *)0)->member) #endif #endif /* __PPP_H__ */ ppp-2.4.5/pppd/session.c000066400000000000000000000316401130035057700151200ustar00rootroot00000000000000/* * session.c - PPP session control. * * Copyright (c) 2007 Diego Rivera. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Derived from auth.c, which is: * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #ifdef HAS_SHADOW #include #endif #include #include #include #include #include "pppd.h" #include "session.h" #ifdef USE_PAM #include #endif /* #ifdef USE_PAM */ #define SET_MSG(var, msg) if (var != NULL) { var[0] = msg; } #define COPY_STRING(s) ((s) ? strdup(s) : NULL) #define SUCCESS_MSG "Session started successfully" #define ABORT_MSG "Session can't be started without a username" #define SERVICE_NAME "ppp" #define SESSION_FAILED 0 #define SESSION_OK 1 /* We have successfully started a session */ static bool logged_in = 0; #ifdef USE_PAM /* * Static variables used to communicate between the conversation function * and the server_login function */ static const char *PAM_username; static const char *PAM_password; static int PAM_session = 0; static pam_handle_t *pamh = NULL; /* PAM conversation function * Here we assume (for now, at least) that echo on means login name, and * echo off means password. */ static int conversation (int num_msg, #ifndef SOL2 const #endif struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { int replies = 0; struct pam_response *reply = NULL; reply = malloc(sizeof(struct pam_response) * num_msg); if (!reply) return PAM_CONV_ERR; for (replies = 0; replies < num_msg; replies++) { switch (msg[replies]->msg_style) { case PAM_PROMPT_ECHO_ON: reply[replies].resp_retcode = PAM_SUCCESS; reply[replies].resp = COPY_STRING(PAM_username); /* PAM frees resp */ break; case PAM_PROMPT_ECHO_OFF: reply[replies].resp_retcode = PAM_SUCCESS; reply[replies].resp = COPY_STRING(PAM_password); /* PAM frees resp */ break; case PAM_TEXT_INFO: /* fall through */ case PAM_ERROR_MSG: /* ignore it, but pam still wants a NULL response... */ reply[replies].resp_retcode = PAM_SUCCESS; reply[replies].resp = NULL; break; default: /* Must be an error of some sort... */ free (reply); return PAM_CONV_ERR; } } *resp = reply; return PAM_SUCCESS; } static struct pam_conv pam_conv_data = { &conversation, NULL }; #endif /* #ifdef USE_PAM */ int session_start(flags, user, passwd, ttyName, msg) const int flags; const char *user; const char *passwd; const char *ttyName; char **msg; { #ifdef USE_PAM bool ok = 1; const char *usr; int pam_error; bool try_session = 0; #else /* #ifdef USE_PAM */ struct passwd *pw; #ifdef HAS_SHADOW struct spwd *spwd; struct spwd *getspnam(); long now = 0; #endif /* #ifdef HAS_SHADOW */ #endif /* #ifdef USE_PAM */ SET_MSG(msg, SUCCESS_MSG); /* If no verification is requested, then simply return an OK */ if (!(SESS_ALL & flags)) { return SESSION_OK; } if (user == NULL) { SET_MSG(msg, ABORT_MSG); return SESSION_FAILED; } #ifdef USE_PAM /* Find the '\\' in the username */ /* This needs to be fixed to support different username schemes */ if ((usr = strchr(user, '\\')) == NULL) usr = user; else usr++; PAM_session = 0; PAM_username = usr; PAM_password = passwd; dbglog("Initializing PAM (%d) for user %s", flags, usr); pam_error = pam_start (SERVICE_NAME, usr, &pam_conv_data, &pamh); dbglog("---> PAM INIT Result = %d", pam_error); ok = (pam_error == PAM_SUCCESS); if (ok) { ok = (pam_set_item(pamh, PAM_TTY, ttyName) == PAM_SUCCESS) && (pam_set_item(pamh, PAM_RHOST, ifname) == PAM_SUCCESS); } if (ok && (SESS_AUTH & flags)) { dbglog("Attempting PAM authentication"); pam_error = pam_authenticate (pamh, PAM_SILENT); if (pam_error == PAM_SUCCESS) { /* PAM auth was OK */ dbglog("PAM Authentication OK for %s", user); } else { /* No matter the reason, we fail because we're authenticating */ ok = 0; if (pam_error == PAM_USER_UNKNOWN) { dbglog("User unknown, failing PAM authentication"); SET_MSG(msg, "User unknown - cannot authenticate via PAM"); } else { /* Any other error means authentication was bad */ dbglog("PAM Authentication failed: %d: %s", pam_error, pam_strerror(pamh, pam_error)); SET_MSG(msg, (char *) pam_strerror (pamh, pam_error)); } } } if (ok && (SESS_ACCT & flags)) { dbglog("Attempting PAM account checks"); pam_error = pam_acct_mgmt (pamh, PAM_SILENT); if (pam_error == PAM_SUCCESS) { /* * PAM account was OK, set the flag which indicates that we should * try to perform the session checks. */ try_session = 1; dbglog("PAM Account OK for %s", user); } else { /* * If the account checks fail, then we should not try to perform * the session check, because they don't make sense. */ try_session = 0; if (pam_error == PAM_USER_UNKNOWN) { /* * We're checking the account, so it's ok to not have one * because the user might come from the secrets files, or some * other plugin. */ dbglog("User unknown, ignoring PAM restrictions"); SET_MSG(msg, "User unknown - ignoring PAM restrictions"); } else { /* Any other error means session is rejected */ ok = 0; dbglog("PAM Account checks failed: %d: %s", pam_error, pam_strerror(pamh, pam_error)); SET_MSG(msg, (char *) pam_strerror (pamh, pam_error)); } } } if (ok && try_session && (SESS_ACCT & flags)) { /* Only open a session if the user's account was found */ pam_error = pam_open_session (pamh, PAM_SILENT); if (pam_error == PAM_SUCCESS) { dbglog("PAM Session opened for user %s", user); PAM_session = 1; } else { dbglog("PAM Session denied for user %s", user); SET_MSG(msg, (char *) pam_strerror (pamh, pam_error)); ok = 0; } } /* This is needed because apparently the PAM stuff closes the log */ reopen_log(); /* If our PAM checks have already failed, then we must return a failure */ if (!ok) return SESSION_FAILED; #else /* #ifdef USE_PAM */ /* * Use the non-PAM methods directly. 'pw' will remain NULL if the user * has not been authenticated using local UNIX system services. */ pw = NULL; if ((SESS_AUTH & flags)) { pw = getpwnam(user); endpwent(); /* * Here, we bail if we have no user account, because there is nothing * to verify against. */ if (pw == NULL) return SESSION_FAILED; #ifdef HAS_SHADOW spwd = getspnam(user); endspent(); /* * If there is no shadow entry for the user, then we can't verify the * account. */ if (spwd == NULL) return SESSION_FAILED; /* * We check validity all the time, because if the password has expired, * then clearly we should not authenticate against it (if we're being * called for authentication only). Thus, in this particular instance, * there is no real difference between using the AUTH, SESS or ACCT * flags, or combinations thereof. */ now = time(NULL) / 86400L; if ((spwd->sp_expire > 0 && now >= spwd->sp_expire) || ((spwd->sp_max >= 0 && spwd->sp_max < 10000) && spwd->sp_lstchg >= 0 && now >= spwd->sp_lstchg + spwd->sp_max)) { warn("Password for %s has expired", user); return SESSION_FAILED; } /* We have a valid shadow entry, keep the password */ pw->pw_passwd = spwd->sp_pwdp; #endif /* #ifdef HAS_SHADOW */ /* * If no passwd, don't let them login if we're authenticating. */ if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2 || strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0) return SESSION_FAILED; } #endif /* #ifdef USE_PAM */ /* * Write a wtmp entry for this user. */ if (SESS_ACCT & flags) { if (strncmp(ttyName, "/dev/", 5) == 0) ttyName += 5; logwtmp(ttyName, user, ifname); /* Add wtmp login entry */ logged_in = 1; #if defined(_PATH_LASTLOG) && !defined(USE_PAM) /* * Enter the user in lastlog only if he has been authenticated using * local system services. If he has not, then we don't know what his * UID might be, and lastlog is indexed by UID. */ if (pw != NULL) { struct lastlog ll; int fd; time_t tnow; if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET); memset((void *)&ll, 0, sizeof(ll)); (void)time(&tnow); ll.ll_time = tnow; (void)strncpy(ll.ll_line, ttyName, sizeof(ll.ll_line)); (void)strncpy(ll.ll_host, ifname, sizeof(ll.ll_host)); (void)write(fd, (char *)&ll, sizeof(ll)); (void)close(fd); } } #endif /* _PATH_LASTLOG and not USE_PAM */ info("user %s logged in on tty %s intf %s", user, ttyName, ifname); } return SESSION_OK; } /* * session_end - Logout the user. */ void session_end(const char* ttyName) { #ifdef USE_PAM int pam_error = PAM_SUCCESS; if (pamh != NULL) { if (PAM_session) pam_error = pam_close_session (pamh, PAM_SILENT); PAM_session = 0; pam_end (pamh, pam_error); pamh = NULL; /* Apparently the pam stuff does closelog(). */ reopen_log(); } #endif if (logged_in) { if (strncmp(ttyName, "/dev/", 5) == 0) ttyName += 5; logwtmp(ttyName, "", ""); /* Wipe out utmp logout entry */ logged_in = 0; } } ppp-2.4.5/pppd/session.h000066400000000000000000000054741130035057700151330ustar00rootroot00000000000000/* * session.c - PPP session control. * * Copyright (c) 2007 Diego Rivera. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __SESSION_H #define __SESSION_H #define SESS_AUTH 1 /* Check User Authentication */ #define SESS_ACCT 2 /* Check Account Validity */ /* Convenience parameter to do the whole enchilada */ #define SESS_ALL (SESS_AUTH | SESS_ACCT) /* * int session_start(...) * * Start a session, performing any necessary validations. * * Parameters: * const int flags : * Any combination of the SESS_XXX flags, to indicate what the function * should do as part of its checks * * const char* user : * The username to validate. May safely be null. * * const char* passwd : * The password to validate the user with. May safely be null. * * const char* tty : * The TTY the user is connected on. May safely be null. * * char** msg : * A char* to return an error or success message. This message will be returned * regardless of the result. May safely be null. * * Return Value: * Zero value for failure, non-zero value for successful session verification. */ int session_start(const int flags, const char* user, const char* passwd, const char* tty, char** msg); /* Added these macros for convenience... */ #define session_auth(user, pass, tty, msg) \ session_start(SESS_AUTH, user, pass, tty, msg) #define session_check(user, pass, tty, msg) \ session_start(SESS_ACCT, user, pass, tty, msg) #define session_full(user, pass, tty, msg) \ session_start(SESS_ALL, user, pass, tty, msg) /* * void session_end(...) * * End a previously-started session. * * Parameters: * const char* tty : * The TTY the user is connected on. May safely be null. */ void session_end(const char* tty); #endif ppp-2.4.5/pppd/sha1.c000066400000000000000000000132211130035057700142640ustar00rootroot00000000000000/* * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c * * SHA-1 in C * By Steve Reid * 100% Public Domain * * Test Vectors (from FIPS PUB 180-1) * "abc" * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 * A million repetitions of "a" * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ /* #define SHA1HANDSOFF * Copies data before messing with it. */ #include #include /* htonl() */ #include #include "sha1.h" static void SHA1_Transform(u_int32_t[5], const unsigned char[64]); #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #define blk0(i) (block->l[i] = htonl(block->l[i])) #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ static void SHA1_Transform(u_int32_t state[5], const unsigned char buffer[64]) { u_int32_t a, b, c, d, e; typedef union { unsigned char c[64]; u_int32_t l[16]; } CHAR64LONG16; CHAR64LONG16 *block; #ifdef SHA1HANDSOFF static unsigned char workspace[64]; block = (CHAR64LONG16 *) workspace; memcpy(block, buffer, 64); #else block = (CHAR64LONG16 *) buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* SHA1Init - Initialize new context */ void SHA1_Init(SHA1_CTX *context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1_Update(SHA1_CTX *context, const unsigned char *data, unsigned int len) { unsigned int i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); i = 64 - j; while (len >= i) { memcpy(&context->buffer[j], data, i); SHA1_Transform(context->state, context->buffer); data += i; len -= i; i = 64; j = 0; } memcpy(&context->buffer[j], data, len); } /* Add padding and return the message digest. */ void SHA1_Final(unsigned char digest[20], SHA1_CTX *context) { u_int32_t i, j; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1_Update(context, (unsigned char *) "\200", 1); while ((context->count[0] & 504) != 448) { SHA1_Update(context, (unsigned char *) "\0", 1); } SHA1_Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } /* Wipe variables */ i = j = 0; memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(&finalcount, 0, 8); #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ SHA1Transform(context->state, context->buffer); #endif } ppp-2.4.5/pppd/sha1.h000066400000000000000000000012151130035057700142710ustar00rootroot00000000000000/* sha1.h */ /* If OpenSSL is in use, then use that version of SHA-1 */ #ifdef OPENSSL #include #define __SHA1_INCLUDE_ #endif #ifndef __SHA1_INCLUDE_ #ifndef SHA1_SIGNATURE_SIZE #ifdef SHA_DIGESTSIZE #define SHA1_SIGNATURE_SIZE SHA_DIGESTSIZE #else #define SHA1_SIGNATURE_SIZE 20 #endif #endif typedef struct { u_int32_t state[5]; u_int32_t count[2]; unsigned char buffer[64]; } SHA1_CTX; extern void SHA1_Init(SHA1_CTX *); extern void SHA1_Update(SHA1_CTX *, const unsigned char *, unsigned int); extern void SHA1_Final(unsigned char[SHA1_SIGNATURE_SIZE], SHA1_CTX *); #define __SHA1_INCLUDE_ #endif /* __SHA1_INCLUDE_ */ ppp-2.4.5/pppd/spinlock.c000066400000000000000000000217121130035057700152560ustar00rootroot00000000000000/* Unix SMB/CIFS implementation. trivial database library Copyright (C) Anton Blanchard 2001 ** NOTE! The following LGPL license applies to the tdb ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include "tdb.h" #include "spinlock.h" #define DEBUG #ifdef USE_SPINLOCKS /* * ARCH SPECIFIC */ #if defined(SPARC_SPINLOCKS) static inline int __spin_trylock(spinlock_t *lock) { unsigned int result; asm volatile("ldstub [%1], %0" : "=r" (result) : "r" (lock) : "memory"); return (result == 0) ? 0 : EBUSY; } static inline void __spin_unlock(spinlock_t *lock) { asm volatile("":::"memory"); *lock = 0; } static inline void __spin_lock_init(spinlock_t *lock) { *lock = 0; } static inline int __spin_is_locked(spinlock_t *lock) { return (*lock != 0); } #elif defined(POWERPC_SPINLOCKS) static inline int __spin_trylock(spinlock_t *lock) { unsigned int result; __asm__ __volatile__( "1: lwarx %0,0,%1\n\ cmpwi 0,%0,0\n\ li %0,0\n\ bne- 2f\n\ li %0,1\n\ stwcx. %0,0,%1\n\ bne- 1b\n\ isync\n\ 2:" : "=&r"(result) : "r"(lock) : "cr0", "memory"); return (result == 1) ? 0 : EBUSY; } static inline void __spin_unlock(spinlock_t *lock) { asm volatile("eieio":::"memory"); *lock = 0; } static inline void __spin_lock_init(spinlock_t *lock) { *lock = 0; } static inline int __spin_is_locked(spinlock_t *lock) { return (*lock != 0); } #elif defined(INTEL_SPINLOCKS) static inline int __spin_trylock(spinlock_t *lock) { int oldval; asm volatile("xchgl %0,%1" : "=r" (oldval), "=m" (*lock) : "0" (0) : "memory"); return oldval > 0 ? 0 : EBUSY; } static inline void __spin_unlock(spinlock_t *lock) { asm volatile("":::"memory"); *lock = 1; } static inline void __spin_lock_init(spinlock_t *lock) { *lock = 1; } static inline int __spin_is_locked(spinlock_t *lock) { return (*lock != 1); } #elif defined(MIPS_SPINLOCKS) && defined(sgi) && (_COMPILER_VERSION >= 730) /* Implement spinlocks on IRIX using the MIPSPro atomic fetch operations. See * sync(3) for the details of the intrinsic operations. * * "sgi" and "_COMPILER_VERSION" are always defined by MIPSPro. */ #ifdef STANDALONE /* MIPSPro 7.3 has "__inline" as an extension, but not "inline. */ #define inline __inline #endif /* STANDALONE */ /* Returns 0 if the lock is acquired, EBUSY otherwise. */ static inline int __spin_trylock(spinlock_t *lock) { unsigned int val; val = __lock_test_and_set(lock, 1); return val == 0 ? 0 : EBUSY; } static inline void __spin_unlock(spinlock_t *lock) { __lock_release(lock); } static inline void __spin_lock_init(spinlock_t *lock) { __lock_release(lock); } /* Returns 1 if the lock is held, 0 otherwise. */ static inline int __spin_is_locked(spinlock_t *lock) { unsigned int val; val = __add_and_fetch(lock, 0); return val; } #elif defined(MIPS_SPINLOCKS) static inline unsigned int load_linked(unsigned long addr) { unsigned int res; __asm__ __volatile__("ll\t%0,(%1)" : "=r" (res) : "r" (addr)); return res; } static inline unsigned int store_conditional(unsigned long addr, unsigned int value) { unsigned int res; __asm__ __volatile__("sc\t%0,(%2)" : "=r" (res) : "0" (value), "r" (addr)); return res; } static inline int __spin_trylock(spinlock_t *lock) { unsigned int mw; do { mw = load_linked(lock); if (mw) return EBUSY; } while (!store_conditional(lock, 1)); asm volatile("":::"memory"); return 0; } static inline void __spin_unlock(spinlock_t *lock) { asm volatile("":::"memory"); *lock = 0; } static inline void __spin_lock_init(spinlock_t *lock) { *lock = 0; } static inline int __spin_is_locked(spinlock_t *lock) { return (*lock != 0); } #else #error Need to implement spinlock code in spinlock.c #endif /* * OS SPECIFIC */ static void yield_cpu(void) { struct timespec tm; #ifdef USE_SCHED_YIELD sched_yield(); #else /* Linux will busy loop for delays < 2ms on real time tasks */ tm.tv_sec = 0; tm.tv_nsec = 2000000L + 1; nanosleep(&tm, NULL); #endif } static int this_is_smp(void) { #if defined(HAVE_SYSCONF) && defined(SYSCONF_SC_NPROC_ONLN) return (sysconf(_SC_NPROC_ONLN) > 1) ? 1 : 0; #else return 0; #endif } /* * GENERIC */ static int smp_machine = 0; static inline void __spin_lock(spinlock_t *lock) { int ntries = 0; while(__spin_trylock(lock)) { while(__spin_is_locked(lock)) { if (smp_machine && ntries++ < MAX_BUSY_LOOPS) continue; yield_cpu(); } } } static void __read_lock(tdb_rwlock_t *rwlock) { int ntries = 0; while(1) { __spin_lock(&rwlock->lock); if (!(rwlock->count & RWLOCK_BIAS)) { rwlock->count++; __spin_unlock(&rwlock->lock); return; } __spin_unlock(&rwlock->lock); while(rwlock->count & RWLOCK_BIAS) { if (smp_machine && ntries++ < MAX_BUSY_LOOPS) continue; yield_cpu(); } } } static void __write_lock(tdb_rwlock_t *rwlock) { int ntries = 0; while(1) { __spin_lock(&rwlock->lock); if (rwlock->count == 0) { rwlock->count |= RWLOCK_BIAS; __spin_unlock(&rwlock->lock); return; } __spin_unlock(&rwlock->lock); while(rwlock->count != 0) { if (smp_machine && ntries++ < MAX_BUSY_LOOPS) continue; yield_cpu(); } } } static void __write_unlock(tdb_rwlock_t *rwlock) { __spin_lock(&rwlock->lock); #ifdef DEBUG if (!(rwlock->count & RWLOCK_BIAS)) fprintf(stderr, "bug: write_unlock\n"); #endif rwlock->count &= ~RWLOCK_BIAS; __spin_unlock(&rwlock->lock); } static void __read_unlock(tdb_rwlock_t *rwlock) { __spin_lock(&rwlock->lock); #ifdef DEBUG if (!rwlock->count) fprintf(stderr, "bug: read_unlock\n"); if (rwlock->count & RWLOCK_BIAS) fprintf(stderr, "bug: read_unlock\n"); #endif rwlock->count--; __spin_unlock(&rwlock->lock); } /* TDB SPECIFIC */ /* lock a list in the database. list -1 is the alloc list */ int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { tdb_rwlock_t *rwlocks; if (!tdb->map_ptr) return -1; rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks); switch(rw_type) { case F_RDLCK: __read_lock(&rwlocks[list+1]); break; case F_WRLCK: __write_lock(&rwlocks[list+1]); break; default: return TDB_ERRCODE(TDB_ERR_LOCK, -1); } return 0; } /* unlock the database. */ int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { tdb_rwlock_t *rwlocks; if (!tdb->map_ptr) return -1; rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks); switch(rw_type) { case F_RDLCK: __read_unlock(&rwlocks[list+1]); break; case F_WRLCK: __write_unlock(&rwlocks[list+1]); break; default: return TDB_ERRCODE(TDB_ERR_LOCK, -1); } return 0; } int tdb_create_rwlocks(int fd, unsigned int hash_size) { unsigned size, i; tdb_rwlock_t *rwlocks; size = TDB_SPINLOCK_SIZE(hash_size); rwlocks = malloc(size); if (!rwlocks) return -1; for(i = 0; i < hash_size+1; i++) { __spin_lock_init(&rwlocks[i].lock); rwlocks[i].count = 0; } /* Write it out (appending to end) */ if (write(fd, rwlocks, size) != size) { free(rwlocks); return -1; } smp_machine = this_is_smp(); free(rwlocks); return 0; } int tdb_clear_spinlocks(TDB_CONTEXT *tdb) { tdb_rwlock_t *rwlocks; unsigned i; if (tdb->header.rwlocks == 0) return 0; if (!tdb->map_ptr) return -1; /* We're mmapped here */ rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks); for(i = 0; i < tdb->header.hash_size+1; i++) { __spin_lock_init(&rwlocks[i].lock); rwlocks[i].count = 0; } return 0; } #else int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; } int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; } int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; } /* Non-spinlock version: remove spinlock pointer */ int tdb_clear_spinlocks(TDB_CONTEXT *tdb) { tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks - (char *)&tdb->header); tdb->header.rwlocks = 0; if (lseek(tdb->fd, off, SEEK_SET) != off || write(tdb->fd, (void *)&tdb->header.rwlocks, sizeof(tdb->header.rwlocks)) != sizeof(tdb->header.rwlocks)) return -1; return 0; } #endif ppp-2.4.5/pppd/spinlock.h000066400000000000000000000027221130035057700152630ustar00rootroot00000000000000#ifndef __SPINLOCK_H__ #define __SPINLOCK_H__ #ifdef HAVE_CONFIG_H #include #endif #include "tdb.h" #ifdef USE_SPINLOCKS #define RWLOCK_BIAS 0x1000UL /* OS SPECIFIC */ #define MAX_BUSY_LOOPS 1000 #undef USE_SCHED_YIELD /* ARCH SPECIFIC */ /* We should make sure these are padded to a cache line */ #if defined(SPARC_SPINLOCKS) typedef volatile char spinlock_t; #elif defined(POWERPC_SPINLOCKS) typedef volatile unsigned long spinlock_t; #elif defined(INTEL_SPINLOCKS) typedef volatile int spinlock_t; #elif defined(MIPS_SPINLOCKS) typedef volatile unsigned long spinlock_t; #else #error Need to implement spinlock code in spinlock.h #endif typedef struct { spinlock_t lock; volatile int count; } tdb_rwlock_t; int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type); int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type); int tdb_create_rwlocks(int fd, unsigned int hash_size); int tdb_clear_spinlocks(TDB_CONTEXT *tdb); #define TDB_SPINLOCK_SIZE(hash_size) (((hash_size) + 1) * sizeof(tdb_rwlock_t)) #else /* !USE_SPINLOCKS */ #if 0 #define tdb_create_rwlocks(fd, hash_size) 0 #define tdb_spinlock(tdb, list, rw_type) (-1) #define tdb_spinunlock(tdb, list, rw_type) (-1) #else int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type); int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type); int tdb_create_rwlocks(int fd, unsigned int hash_size); #endif int tdb_clear_spinlocks(TDB_CONTEXT *tdb); #define TDB_SPINLOCK_SIZE(hash_size) 0 #endif #endif ppp-2.4.5/pppd/srp-entry.8000066400000000000000000000056021130035057700153240ustar00rootroot00000000000000.\" manual page [] for srp-entry .\" $Id: srp-entry.8,v 1.2 2004/11/13 12:22:49 paulus Exp $ .\" SH section heading .\" SS subsection heading .\" LP paragraph .\" IP indented paragraph .\" TP hanging label .TH SRP-ENTRY 8 .SH NAME srp\-entry \- Generate a SRP\-SHA1 Server Entry .SH SYNOPSIS .B srp\-entry [ .I \-i index ] [ .I clientname ] .SH DESCRIPTION .LP This utility generates an entry suitable for use in the /etc/ppp/srp\-secrets file on a PPP EAP SRP\-SHA1 authenticator ("server"). This file has the same basic layout as the other pppd(8) authentication files, /etc/ppp/pap\-secrets and /etc/ppp/chap\-secrets. Thus, the entry generated has at least four main fields separated by spaces. The first field is the authenticatee ("client") name. The second is the server name. The third is the secret. The fourth is the allowed (or assigned) IP address for the client, and defaults to "*". Additional fields can contain additional IP addresses or pppd options; see pppd(8) for details. .LP The third field has three subfields, separated by colons. The first subfield is the index of the modulus and generator from SRP's /etc/tpasswd.conf. The special value 0 is used to represent the well-known modulus and generator specified in the EAP SRP\-SHA1 draft. The second subfield is the password validator. The third is the password salt. These latter two values are encoded in base64 notation. .SH OPTIONS .TP .I \-i Specifies the modulus/generator index in /etc/tpasswd.conf. In order to use this option, you will need to run the "tconf" utility from the SRP package to generate local entries for this file. Note that if these values are not known to the client, the client will be forced to run time-consuming safety tests on the values used. For this reason, using the well-known values is recommended. .TP .I Specifies the client name. The password validator is a hashed combination of the client's name and password, and both are required. If the client name is not supplied on the command line, srp\-entry will prompt for the client name first. .SH FILES .TP .B /etc/ppp/srp\-secrets Usernames, passwords and IP addresses for SRP authentication. This file should be owned by root and not readable or writable by any other user. Pppd will log a warning if this is not the case. Note that srp\-entry does not write to this file. The user is responsible for copying the output of srp\-entry into this file. .TP .B /etc/tpasswd.conf Indexed copies of tested modulus/generator combinations; part of the SRP package. .SH SEE ALSO .TP pppd(8) .TP .B RFC2284 Blunk, L., Vollbrecht, J., .I PPP Extensible Authentication Protocol (EAP). March 1998. .TP .B draft\-ietf\-pppext\-eap\-srp\-03.txt Carlson, J., et al., .I EAP SRP\-SHA1 Authentication Protocol. July 2001. .TP .B RFC2945 Wu, T., .I The SRP Authentication and Key Exchange System September 2000. .SH AUTHOR James Carlson (james.d.carlson@sun.com) ppp-2.4.5/pppd/srp-entry.c000066400000000000000000000125371130035057700154040ustar00rootroot00000000000000/* * Utility program for generating entries in /etc/ppp/srp-secrets * * Copyright (c) 2001 by Sun Microsystems, Inc. * All rights reserved. * * Non-exclusive rights to redistribute, modify, translate, and use * this software in source and binary forms, in whole or in part, is * hereby granted, provided that the above copyright notice is * duplicated in any source form, and that neither the name of the * copyright holder nor the author is used to endorse or promote * products derived from this software. * * 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. * * Original version by James Carlson * * Usage: * srp-entry [-i index] [clientname] * * Index, if supplied, is the modulus/generator index from * /etc/tpasswd.conf. If not supplied, then the last (highest * numbered) entry from that file is used. If the file doesn't exist, * then the default "well known" EAP SRP-SHA1 modulus/generator is * used. * * The default modulus/generator can be requested as index 0. */ #include #include #include #include #include #include #ifndef SOL2 #define getpassphrase getpass #endif #define HAS_SPACE 1 #define HAS_DQUOTE 2 #define HAS_SQUOTE 4 #define HAS_BACKSLASH 8 static const u_char wkmodulus[] = { 0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B, 0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F, 0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07, 0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50, 0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED, 0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D, 0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D, 0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50, 0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0, 0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3, 0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8, 0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8, 0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA, 0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74, 0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7, 0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B, 0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16, 0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81, 0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A, 0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48, 0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D, 0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA, 0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78, 0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6, 0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29, 0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8, 0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82, 0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6, 0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4, 0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75, 0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2, 0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73 }; static const char *myname; static void usage(void) { (void) fprintf(stderr, "Usage:\n\t%s [-i index] [clientname]\n", myname); exit(1); } int main(int argc, char **argv) { struct t_conf *tc; struct t_confent *tcent, mytce; struct t_pw pwval; char *name; char pname[256]; char *pass1, *pass2; int flags, idx; char *cp; char delimit; char strbuf[MAXB64PARAMLEN]; char saltbuf[MAXB64SALTLEN]; if ((myname = *argv) == NULL) myname = "srp-entry"; else argv++; idx = -1; if (*argv != NULL && strcmp(*argv, "-i") == 0) { if (*++argv == NULL) usage(); idx = atoi(*argv++); } tcent = NULL; if (idx != 0 && (tc = t_openconf(NULL)) != NULL) { if (idx == -1) tcent = t_getconflast(tc); else tcent = t_getconfbyindex(tc, idx); } if (idx <= 0 && tcent == NULL) { mytce.index = 0; mytce.modulus.data = (u_char *)wkmodulus; mytce.modulus.len = sizeof (wkmodulus); mytce.generator.data = (u_char *)"\002"; mytce.generator.len = 1; tcent = &mytce; } if (tcent == NULL) { (void) fprintf(stderr, "SRP modulus/generator %d not found\n", idx); exit(1); } if ((name = *argv) == NULL) { (void) printf("Client name: "); if (fgets(pname, sizeof (pname), stdin) == NULL) exit(1); if ((cp = strchr(pname, '\n')) != NULL) *cp = '\0'; name = pname; } for (;;) { if ((pass1 = getpassphrase("Pass phrase: ")) == NULL) exit(1); pass1 = strdup(pass1); if ((pass2 = getpassphrase("Re-enter phrase: ")) == NULL) exit(1); if (strcmp(pass1, pass2) == 0) break; free(pass1); (void) printf("Phrases don't match; try again.\n"); } memset(&pwval, 0, sizeof (pwval)); t_makepwent(&pwval, name, pass1, NULL, tcent); flags = 0; for (cp = name; *cp != '\0'; cp++) if (isspace(*cp)) flags |= HAS_SPACE; else if (*cp == '"') flags |= HAS_DQUOTE; else if (*cp == '\'') flags |= HAS_SQUOTE; else if (*cp == '\\') flags |= HAS_BACKSLASH; delimit = flags == 0 ? '\0' : (flags & HAS_DQUOTE) ? '\'' : '"'; if (delimit != '\0') (void) putchar(delimit); for (cp = name; *cp != '\0'; cp++) { if (*cp == delimit || *cp == '\\') (void) putchar('\\'); (void) putchar(*cp); } if (delimit != '\0') (void) putchar(delimit); (void) printf(" * %d:%s:%s *\n", pwval.pebuf.index, t_tob64(strbuf, (char *)pwval.pebuf.password.data, pwval.pebuf.password.len), t_tob64(saltbuf, (char *)pwval.pebuf.salt.data, pwval.pebuf.salt.len)); return 0; } ppp-2.4.5/pppd/sys-linux.c000066400000000000000000002135361130035057700154160ustar00rootroot00000000000000/* * sys-linux.c - System-dependent procedures for setting up * PPP interfaces on Linux systems * * Copyright (c) 1994-2004 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Derived from main.c and pppd.h, which are: * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* This is in netdevice.h. However, this compile will fail miserably if you attempt to include netdevice.h because it has so many references to __memcpy functions which it should not attempt to do. So, since I really don't use it, but it must be defined, define it now. */ #ifndef MAX_ADDR_LEN #define MAX_ADDR_LEN 7 #endif #if __GLIBC__ >= 2 #include /* glibc 2 conflicts with linux/types.h */ #include #include #include #include #else #include #include #include #include #include #endif #include #include #include #include #include "pppd.h" #include "fsm.h" #include "ipcp.h" #ifdef IPX_CHANGE #include "ipxcp.h" #if __GLIBC__ >= 2 && \ !(defined(__powerpc__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0) #include #else #include #endif #endif /* IPX_CHANGE */ #ifdef PPP_FILTER #include #include #endif /* PPP_FILTER */ #ifdef LOCKLIB #include #endif #ifdef INET6 #ifndef _LINUX_IN6_H /* * This is in linux/include/net/ipv6.h. */ struct in6_ifreq { struct in6_addr ifr6_addr; __u32 ifr6_prefixlen; unsigned int ifr6_ifindex; }; #endif #define IN6_LLADDR_FROM_EUI64(sin6, eui64) do { \ memset(&sin6.s6_addr, 0, sizeof(struct in6_addr)); \ sin6.s6_addr16[0] = htons(0xfe80); \ eui64_copy(eui64, sin6.s6_addr32[2]); \ } while (0) #endif /* INET6 */ /* We can get an EIO error on an ioctl if the modem has hung up */ #define ok_error(num) ((num)==EIO) static int tty_disc = N_TTY; /* The TTY discipline */ static int ppp_disc = N_PPP; /* The PPP discpline */ static int initfdflags = -1; /* Initial file descriptor flags for fd */ static int ppp_fd = -1; /* fd which is set to PPP discipline */ static int sock_fd = -1; /* socket for doing interface ioctls */ static int slave_fd = -1; /* pty for old-style demand mode, slave */ static int master_fd = -1; /* pty for old-style demand mode, master */ #ifdef INET6 static int sock6_fd = -1; #endif /* INET6 */ /* * For the old-style kernel driver, this is the same as ppp_fd. * For the new-style driver, it is the fd of an instance of /dev/ppp * which is attached to the ppp unit and is used for controlling it. */ int ppp_dev_fd = -1; /* fd for /dev/ppp (new style driver) */ static int chindex; /* channel index (new style driver) */ static fd_set in_fds; /* set of fds that wait_input waits for */ static int max_in_fd; /* highest fd set in in_fds */ static int has_proxy_arp = 0; static int driver_version = 0; static int driver_modification = 0; static int driver_patch = 0; static int driver_is_old = 0; static int restore_term = 0; /* 1 => we've munged the terminal */ static struct termios inittermios; /* Initial TTY termios */ int new_style_driver = 0; static char loop_name[20]; static unsigned char inbuf[512]; /* buffer for chars read from loopback */ static int if_is_up; /* Interface has been marked up */ static int have_default_route; /* Gateway for default route added */ static u_int32_t proxy_arp_addr; /* Addr for proxy arp entry added */ static char proxy_arp_dev[16]; /* Device for proxy arp entry */ static u_int32_t our_old_addr; /* for detecting address changes */ static int dynaddr_set; /* 1 if ip_dynaddr set */ static int looped; /* 1 if using loop */ static int link_mtu; /* mtu for the link (not bundle) */ static struct utsname utsname; /* for the kernel version */ static int kernel_version; #define KVERSION(j,n,p) ((j)*1000000 + (n)*1000 + (p)) #define MAX_IFS 100 #define FLAGS_GOOD (IFF_UP | IFF_BROADCAST) #define FLAGS_MASK (IFF_UP | IFF_BROADCAST | \ IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP) #define SIN_ADDR(x) (((struct sockaddr_in *) (&(x)))->sin_addr.s_addr) /* Prototypes for procedures local to this file. */ static int modify_flags(int fd, int clear_bits, int set_bits); static int translate_speed (int bps); static int baud_rate_of (int speed); static void close_route_table (void); static int open_route_table (void); static int read_route_table (struct rtentry *rt); static int defaultroute_exists (struct rtentry *rt); static int get_ether_addr (u_int32_t ipaddr, struct sockaddr *hwaddr, char *name, int namelen); static void decode_version (char *buf, int *version, int *mod, int *patch); static int set_kdebugflag(int level); static int ppp_registered(void); static int make_ppp_unit(void); extern u_char inpacket_buf[]; /* borrowed from main.c */ /* * 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); /* * Determine if the PPP connection should still be present. */ extern int hungup; /* new_fd is the fd of a tty */ static void set_ppp_fd (int new_fd) { ppp_fd = new_fd; if (!new_style_driver) ppp_dev_fd = new_fd; } static int still_ppp(void) { if (new_style_driver) return !hungup && ppp_fd >= 0; if (!hungup || ppp_fd == slave_fd) return 1; if (slave_fd >= 0) { set_ppp_fd(slave_fd); return 1; } return 0; } /* * modify_flags - set and clear flag bits controlling the kernel * PPP driver. */ static int modify_flags(int fd, int clear_bits, int set_bits) { int flags; if (ioctl(fd, PPPIOCGFLAGS, &flags) == -1) goto err; flags = (flags & ~clear_bits) | set_bits; if (ioctl(fd, PPPIOCSFLAGS, &flags) == -1) goto err; return 0; err: if (errno != EIO) error("Failed to set PPP kernel option flags: %m"); return -1; } /******************************************************************** * * sys_init - System-dependent initialization. */ void sys_init(void) { /* Get an internet socket for doing socket ioctls. */ sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if (sock_fd < 0) fatal("Couldn't create IP socket: %m(%d)", errno); #ifdef INET6 sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0); if (sock6_fd < 0) sock6_fd = -errno; /* save errno for later */ #endif FD_ZERO(&in_fds); max_in_fd = 0; } /******************************************************************** * * sys_cleanup - restore any system state we modified before exiting: * mark the interface down, delete default route and/or proxy arp entry. * This shouldn't call die() because it's called from die(). */ void sys_cleanup(void) { /* * Take down the device */ if (if_is_up) { if_is_up = 0; sifdown(0); } /* * Delete any routes through the device. */ if (have_default_route) cifdefaultroute(0, 0, 0); if (has_proxy_arp) cifproxyarp(0, proxy_arp_addr); } /******************************************************************** * * sys_close - Clean up in a child process before execing. */ void sys_close(void) { if (new_style_driver && ppp_dev_fd >= 0) close(ppp_dev_fd); if (sock_fd >= 0) close(sock_fd); #ifdef INET6 if (sock6_fd >= 0) close(sock6_fd); #endif if (slave_fd >= 0) close(slave_fd); if (master_fd >= 0) close(master_fd); } /******************************************************************** * * set_kdebugflag - Define the debugging level for the kernel */ static int set_kdebugflag (int requested_level) { if (ppp_dev_fd < 0) return 1; if (ioctl(ppp_dev_fd, PPPIOCSDEBUG, &requested_level) < 0) { if ( ! ok_error (errno) ) error("ioctl(PPPIOCSDEBUG): %m (line %d)", __LINE__); return (0); } return (1); } /******************************************************************** * * tty_establish_ppp - Turn the serial port into a ppp interface. */ int tty_establish_ppp (int tty_fd) { int ret_fd; /* * Ensure that the tty device is in exclusive mode. */ if (ioctl(tty_fd, TIOCEXCL, 0) < 0) { if ( ! ok_error ( errno )) warn("Couldn't make tty exclusive: %m"); } /* * Demand mode - prime the old ppp device to relinquish the unit. */ if (!new_style_driver && looped && ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) { error("ioctl(transfer ppp unit): %m, line %d", __LINE__); return -1; } /* * Set the current tty to the PPP discpline */ #ifndef N_SYNC_PPP #define N_SYNC_PPP 14 #endif ppp_disc = (new_style_driver && sync_serial)? N_SYNC_PPP: N_PPP; if (ioctl(tty_fd, TIOCSETD, &ppp_disc) < 0) { if ( ! ok_error (errno) ) { error("Couldn't set tty to PPP discipline: %m"); return -1; } } ret_fd = generic_establish_ppp(tty_fd); #define SC_RCVB (SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP) #define SC_LOGB (SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \ | SC_LOG_FLUSH) if (ret_fd >= 0) { modify_flags(ppp_fd, SC_RCVB | SC_LOGB, (kdebugflag * SC_DEBUG) & SC_LOGB); } else { if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno)) warn("Couldn't reset tty to normal line discipline: %m"); } return ret_fd; } /******************************************************************** * * generic_establish_ppp - Turn the fd into a ppp interface. */ int generic_establish_ppp (int fd) { int x; if (new_style_driver) { int flags; /* Open an instance of /dev/ppp and connect the channel to it */ if (ioctl(fd, PPPIOCGCHAN, &chindex) == -1) { error("Couldn't get channel number: %m"); goto err; } dbglog("using channel %d", chindex); fd = open("/dev/ppp", O_RDWR); if (fd < 0) { error("Couldn't reopen /dev/ppp: %m"); goto err; } (void) fcntl(fd, F_SETFD, FD_CLOEXEC); if (ioctl(fd, PPPIOCATTCHAN, &chindex) < 0) { error("Couldn't attach to channel %d: %m", chindex); goto err_close; } flags = fcntl(fd, F_GETFL); if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) warn("Couldn't set /dev/ppp (channel) to nonblock: %m"); set_ppp_fd(fd); if (!looped) ifunit = -1; if (!looped && !multilink) { /* * Create a new PPP unit. */ if (make_ppp_unit() < 0) goto err_close; } if (looped) modify_flags(ppp_dev_fd, SC_LOOP_TRAFFIC, 0); if (!multilink) { add_fd(ppp_dev_fd); if (ioctl(fd, PPPIOCCONNECT, &ifunit) < 0) { error("Couldn't attach to PPP unit %d: %m", ifunit); goto err_close; } } } else { /* * Old-style driver: find out which interface we were given. */ set_ppp_fd (fd); if (ioctl(fd, PPPIOCGUNIT, &x) < 0) { if (ok_error (errno)) goto err; fatal("ioctl(PPPIOCGUNIT): %m (line %d)", __LINE__); } /* Check that we got the same unit again. */ if (looped && x != ifunit) fatal("transfer_ppp failed: wanted unit %d, got %d", ifunit, x); ifunit = x; /* * Fetch the initial file flags and reset blocking mode on the file. */ initfdflags = fcntl(fd, F_GETFL); if (initfdflags == -1 || fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) { if ( ! ok_error (errno)) warn("Couldn't set device to non-blocking mode: %m"); } } /* * Enable debug in the driver if requested. */ if (!looped) set_kdebugflag (kdebugflag); looped = 0; return ppp_fd; err_close: close(fd); err: return -1; } /******************************************************************** * * tty_disestablish_ppp - Restore the serial port to normal operation. * This shouldn't call die() because it's called from die(). */ void tty_disestablish_ppp(int tty_fd) { if (!hungup) { /* * Flush the tty output buffer so that the TIOCSETD doesn't hang. */ if (tcflush(tty_fd, TCIOFLUSH) < 0) { warn("tcflush failed: %m"); goto flushfailed; } /* * Restore the previous line discipline */ if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0) { if ( ! ok_error (errno)) error("ioctl(TIOCSETD, N_TTY): %m (line %d)", __LINE__); } if (ioctl(tty_fd, TIOCNXCL, 0) < 0) { if ( ! ok_error (errno)) warn("ioctl(TIOCNXCL): %m (line %d)", __LINE__); } /* Reset non-blocking mode on fd. */ if (initfdflags != -1 && fcntl(tty_fd, F_SETFL, initfdflags) < 0) { if ( ! ok_error (errno)) warn("Couldn't restore device fd flags: %m"); } } flushfailed: initfdflags = -1; generic_disestablish_ppp(tty_fd); } /******************************************************************** * * generic_disestablish_ppp - Restore device components to normal * operation, and reconnect the ppp unit to the loopback if in demand * mode. This shouldn't call die() because it's called from die(). */ void generic_disestablish_ppp(int dev_fd) { if (new_style_driver) { close(ppp_fd); ppp_fd = -1; if (demand) { modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC); looped = 1; } else if (!doing_multilink && ppp_dev_fd >= 0) { close(ppp_dev_fd); remove_fd(ppp_dev_fd); ppp_dev_fd = -1; } } else { /* old-style driver */ if (demand) set_ppp_fd(slave_fd); else ppp_dev_fd = -1; } } /* * make_ppp_unit - make a new ppp unit for ppp_dev_fd. * Assumes new_style_driver. */ static int make_ppp_unit() { int x, flags; if (ppp_dev_fd >= 0) { dbglog("in make_ppp_unit, already had /dev/ppp open?"); close(ppp_dev_fd); } ppp_dev_fd = open("/dev/ppp", O_RDWR); if (ppp_dev_fd < 0) fatal("Couldn't open /dev/ppp: %m"); flags = fcntl(ppp_dev_fd, F_GETFL); if (flags == -1 || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1) warn("Couldn't set /dev/ppp to nonblock: %m"); ifunit = req_unit; x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit); if (x < 0 && req_unit >= 0 && errno == EEXIST) { warn("Couldn't allocate PPP unit %d as it is already in use", req_unit); ifunit = -1; x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit); } if (x < 0) error("Couldn't create new ppp unit: %m"); return x; } /* * cfg_bundle - configure the existing bundle. * Used in demand mode. */ void cfg_bundle(int mrru, int mtru, int rssn, int tssn) { if (!new_style_driver) return; /* set the mrru, mtu and flags */ if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0) error("Couldn't set MRRU: %m"); modify_flags(ppp_dev_fd, SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ|SC_MULTILINK, ((rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0) | (mrru? SC_MULTILINK: 0))); /* connect up the channel */ if (ioctl(ppp_fd, PPPIOCCONNECT, &ifunit) < 0) fatal("Couldn't attach to PPP unit %d: %m", ifunit); add_fd(ppp_dev_fd); } /* * make_new_bundle - create a new PPP unit (i.e. a bundle) * and connect our channel to it. This should only get called * if `multilink' was set at the time establish_ppp was called. * In demand mode this uses our existing bundle instead of making * a new one. */ void make_new_bundle(int mrru, int mtru, int rssn, int tssn) { if (!new_style_driver) return; /* make us a ppp unit */ if (make_ppp_unit() < 0) die(1); /* set the mrru and flags */ cfg_bundle(mrru, mtru, rssn, tssn); } /* * bundle_attach - attach our link to a given PPP unit. * We assume the unit is controlled by another pppd. */ int bundle_attach(int ifnum) { int master_fd; if (!new_style_driver) return -1; master_fd = open("/dev/ppp", O_RDWR); if (master_fd < 0) fatal("Couldn't open /dev/ppp: %m"); if (ioctl(master_fd, PPPIOCATTACH, &ifnum) < 0) { if (errno == ENXIO) { close(master_fd); return 0; /* doesn't still exist */ } fatal("Couldn't attach to interface unit %d: %m\n", ifnum); } if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0) fatal("Couldn't connect to interface unit %d: %m", ifnum); modify_flags(master_fd, 0, SC_MULTILINK); close(master_fd); ifunit = ifnum; return 1; } /* * destroy_bundle - tell the driver to destroy our bundle. */ void destroy_bundle(void) { if (ppp_dev_fd >= 0) { close(ppp_dev_fd); remove_fd(ppp_dev_fd); ppp_dev_fd = -1; } } /******************************************************************** * * clean_check - Fetch the flags for the device and generate * appropriate error messages. */ void clean_check(void) { int x; char *s; if (still_ppp()) { if (ioctl(ppp_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 = "all had bit 7 set to 1"; break; case SC_RCV_B7_1: s = "all had bit 7 set to 0"; break; case SC_RCV_EVNP: s = "all had odd parity"; break; case SC_RCV_ODDP: s = "all had even parity"; break; } if (s != NULL) { warn("Receive serial link is not 8-bit clean:"); warn("Problem: %s", s); } } } } /* * 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 B57600 { 57600, B57600 }, #endif #ifdef B76800 { 76800, B76800 }, #endif #ifdef B115200 { 115200, B115200 }, #endif #ifdef EXTA { 19200, EXTA }, #endif #ifdef EXTB { 38400, EXTB }, #endif #ifdef B230400 { 230400, B230400 }, #endif #ifdef B460800 { 460800, B460800 }, #endif #ifdef B921600 { 921600, B921600 }, #endif #ifdef B1000000 { 1000000, B1000000 }, #endif #ifdef B1152000 { 1152000, B1152000 }, #endif #ifdef B1500000 { 1500000, B1500000 }, #endif #ifdef B2000000 { 2000000, B2000000 }, #endif #ifdef B2500000 { 2500000, B2500000 }, #endif #ifdef B3000000 { 3000000, B3000000 }, #endif #ifdef B3500000 { 3500000, B3500000 }, #endif #ifdef B4000000 { 4000000, B4000000 }, #endif { 0, 0 } }; /******************************************************************** * * Translate from bits/second to a speed_t. */ static int translate_speed (int bps) { struct speed *speedp; if (bps != 0) { for (speedp = speeds; speedp->speed_int; speedp++) { if (bps == speedp->speed_int) return speedp->speed_val; } warn("speed %d not supported", bps); } return 0; } /******************************************************************** * * Translate from a speed_t to bits/second. */ static int baud_rate_of (int speed) { struct speed *speedp; if (speed != 0) { for (speedp = speeds; speedp->speed_int; speedp++) { if (speed == speedp->speed_val) return speedp->speed_int; } } return 0; } /******************************************************************** * * 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. */ void set_up_tty(int tty_fd, int local) { int speed; struct termios tios; setdtr(tty_fd, 1); if (tcgetattr(tty_fd, &tios) < 0) { if (!ok_error(errno)) fatal("tcgetattr: %m (line %d)", __LINE__); return; } if (!restore_term) inittermios = tios; tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL); tios.c_cflag |= CS8 | CREAD | HUPCL; tios.c_iflag = IGNBRK | IGNPAR; tios.c_oflag = 0; tios.c_lflag = 0; tios.c_cc[VMIN] = 1; tios.c_cc[VTIME] = 0; if (local || !modem) tios.c_cflag ^= (CLOCAL | HUPCL); switch (crtscts) { case 1: tios.c_cflag |= CRTSCTS; break; case -2: tios.c_iflag |= IXON | IXOFF; tios.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */ tios.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */ break; case -1: tios.c_cflag &= ~CRTSCTS; break; default: break; } speed = translate_speed(inspeed); if (speed) { cfsetospeed (&tios, speed); cfsetispeed (&tios, speed); } /* * We can't proceed if the serial port speed is B0, * since that implies that the serial port is disabled. */ else { speed = cfgetospeed(&tios); if (speed == B0) fatal("Baud rate for %s is 0; need explicit baud rate", devnam); } while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno)) if (errno != EINTR) fatal("tcsetattr: %m (line %d)", __LINE__); baud_rate = baud_rate_of(speed); restore_term = 1; } /******************************************************************** * * setdtr - control the DTR line on the serial port. * This is called from die(), so it shouldn't call die(). */ void setdtr (int tty_fd, int on) { int modembits = TIOCM_DTR; ioctl(tty_fd, (on ? TIOCMBIS : TIOCMBIC), &modembits); } /******************************************************************** * * restore_tty - restore the terminal to the saved settings. */ void restore_tty (int tty_fd) { if (restore_term) { restore_term = 0; /* * Turn off echoing, because otherwise we can get into * a loop with the tty and the modem echoing to each other. * We presume we are the sole user of this tty device, so * when we close it, it will revert to its defaults anyway. */ if (!default_device) inittermios.c_lflag &= ~(ECHO | ECHONL); if (tcsetattr(tty_fd, TCSAFLUSH, &inittermios) < 0) { if (! ok_error (errno)) warn("tcsetattr: %m (line %d)", __LINE__); } } } /******************************************************************** * * output - Output PPP packet. */ void output (int unit, unsigned char *p, int len) { int fd = ppp_fd; int proto; dump_packet("sent", p, len); if (snoop_send_hook) snoop_send_hook(p, len); if (len < PPP_HDRLEN) return; if (new_style_driver) { p += 2; len -= 2; proto = (p[0] << 8) + p[1]; if (ppp_dev_fd >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG)) fd = ppp_dev_fd; } if (write(fd, p, len) < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS || errno == ENXIO || errno == EIO || errno == EINTR) warn("write: warning: %m (%d)", errno); else error("write: %m (%d)", errno); } } /******************************************************************** * * wait_input - wait until there is data available, * for the length of time specified by *timo (indefinite * if timo is NULL). */ void wait_input(struct timeval *timo) { fd_set ready, exc; int n; ready = in_fds; exc = in_fds; n = select(max_in_fd + 1, &ready, NULL, &exc, timo); if (n < 0 && errno != EINTR) fatal("select: %m"); } /* * add_fd - add an fd to the set that wait_input waits for. */ void add_fd(int fd) { if (fd >= FD_SETSIZE) fatal("internal error: file descriptor too large (%d)", fd); FD_SET(fd, &in_fds); if (fd > max_in_fd) max_in_fd = fd; } /* * remove_fd - remove an fd from the set that wait_input waits for. */ void remove_fd(int fd) { FD_CLR(fd, &in_fds); } /******************************************************************** * * read_packet - get a PPP packet from the serial device. */ int read_packet (unsigned char *buf) { int len, nr; len = PPP_MRU + PPP_HDRLEN; if (new_style_driver) { *buf++ = PPP_ALLSTATIONS; *buf++ = PPP_UI; len -= 2; } nr = -1; if (ppp_fd >= 0) { nr = read(ppp_fd, buf, len); if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EIO && errno != EINTR) error("read: %m"); if (nr < 0 && errno == ENXIO) return 0; } if (nr < 0 && new_style_driver && ppp_dev_fd >= 0 && !bundle_eof) { /* N.B. we read ppp_fd first since LCP packets come in there. */ nr = read(ppp_dev_fd, buf, len); if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EIO && errno != EINTR) error("read /dev/ppp: %m"); if (nr < 0 && errno == ENXIO) nr = 0; if (nr == 0 && doing_multilink) { remove_fd(ppp_dev_fd); bundle_eof = 1; } } if (new_style_driver && ppp_fd < 0 && ppp_dev_fd < 0) nr = 0; return (new_style_driver && nr > 0)? nr+2: nr; } /******************************************************************** * * get_loop_output - get outgoing packets from the ppp device, * and detect when we want to bring the real link up. * Return value is 1 if we need to bring up the link, 0 otherwise. */ int get_loop_output(void) { int rv = 0; int n; if (new_style_driver) { while ((n = read_packet(inpacket_buf)) > 0) if (loop_frame(inpacket_buf, n)) rv = 1; return rv; } while ((n = read(master_fd, inbuf, sizeof(inbuf))) > 0) if (loop_chars(inbuf, n)) rv = 1; if (n == 0) fatal("eof on loopback"); if (errno != EWOULDBLOCK && errno != EAGAIN) fatal("read from loopback: %m(%d)", errno); return rv; } /* * netif_set_mtu - set the MTU on the PPP network interface. */ void netif_set_mtu(int unit, int mtu) { struct ifreq ifr; memset (&ifr, '\0', sizeof (ifr)); strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); ifr.ifr_mtu = mtu; if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0) error("ioctl(SIOCSIFMTU): %m (line %d)", __LINE__); } /* * netif_get_mtu - get the MTU on the PPP network interface. */ int netif_get_mtu(int unit) { struct ifreq ifr; memset (&ifr, '\0', sizeof (ifr)); strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ifunit >= 0 && ioctl(sock_fd, SIOCGIFMTU, (caddr_t) &ifr) < 0) { error("ioctl(SIOCGIFMTU): %m (line %d)", __LINE__); return 0; } return ifr.ifr_mtu; } /******************************************************************** * * tty_send_config - configure the transmit characteristics of * the ppp interface. */ void tty_send_config(int mtu, u_int32_t asyncmap, int pcomp, int accomp) { int x; if (!still_ppp()) return; link_mtu = mtu; if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) { if (errno != EIO && errno != ENOTTY) error("Couldn't set transmit async character map: %m"); ++error_count; return; } x = (pcomp? SC_COMP_PROT: 0) | (accomp? SC_COMP_AC: 0) | (sync_serial? SC_SYNC: 0); modify_flags(ppp_fd, SC_COMP_PROT|SC_COMP_AC|SC_SYNC, x); } /******************************************************************** * * tty_set_xaccm - set the extended transmit ACCM for the interface. */ void tty_set_xaccm (ext_accm accm) { if (!still_ppp()) return; if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) { if ( ! ok_error (errno)) warn("ioctl(set extended ACCM): %m (line %d)", __LINE__); } } /******************************************************************** * * tty_recv_config - configure the receive-side characteristics of * the ppp interface. */ void tty_recv_config(int mru, u_int32_t asyncmap, int pcomp, int accomp) { /* * If we were called because the link has gone down then there is nothing * which may be done. Just return without incident. */ if (!still_ppp()) return; /* * Set the receiver parameters */ if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) { if (errno != EIO && errno != ENOTTY) error("Couldn't set channel receive MRU: %m"); } if (new_style_driver && ppp_dev_fd >= 0 && ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) error("Couldn't set MRU in generic PPP layer: %m"); if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) { if (errno != EIO && errno != ENOTTY) error("Couldn't set channel receive asyncmap: %m"); } } /******************************************************************** * * ccp_test - ask kernel whether a given compression method * is acceptable for use. */ int ccp_test(int unit, u_char *opt_ptr, int opt_len, int for_transmit) { struct ppp_option_data data; memset (&data, '\0', sizeof (data)); data.ptr = opt_ptr; data.length = opt_len; data.transmit = for_transmit; if (ioctl(ppp_dev_fd, PPPIOCSCOMPRESS, (caddr_t) &data) >= 0) return 1; return (errno == ENOBUFS)? 0: -1; } /******************************************************************** * * ccp_flags_set - inform kernel about the current state of CCP. */ void ccp_flags_set (int unit, int isopen, int isup) { int x; x = (isopen? SC_CCP_OPEN: 0) | (isup? SC_CCP_UP: 0); if (still_ppp() && ppp_dev_fd >= 0) modify_flags(ppp_dev_fd, SC_CCP_OPEN|SC_CCP_UP, x); } #ifdef PPP_FILTER /* * set_filters - set the active and pass filters in the kernel driver. */ int set_filters(struct bpf_program *pass, struct bpf_program *active) { struct sock_fprog fp; fp.len = pass->bf_len; fp.filter = (struct sock_filter *) pass->bf_insns; if (ioctl(ppp_dev_fd, PPPIOCSPASS, &fp) < 0) { if (errno == ENOTTY) warn("kernel does not support PPP filtering"); else error("Couldn't set pass-filter in kernel: %m"); return 0; } fp.len = active->bf_len; fp.filter = (struct sock_filter *) active->bf_insns; if (ioctl(ppp_dev_fd, PPPIOCSACTIVE, &fp) < 0) { error("Couldn't set active-filter in kernel: %m"); return 0; } return 1; } #endif /* PPP_FILTER */ /******************************************************************** * * get_idle_time - return how long the link has been idle. */ int get_idle_time(u, ip) int u; struct ppp_idle *ip; { return ioctl(ppp_dev_fd, PPPIOCGIDLE, ip) >= 0; } /******************************************************************** * * get_ppp_stats - return statistics for the link. */ int get_ppp_stats(u, stats) int u; struct pppd_stats *stats; { struct ifpppstatsreq req; memset (&req, 0, sizeof (req)); req.stats_ptr = (caddr_t) &req.stats; strlcpy(req.ifr__name, ifname, sizeof(req.ifr__name)); if (ioctl(sock_fd, SIOCGPPPSTATS, &req) < 0) { error("Couldn't get PPP statistics: %m"); return 0; } stats->bytes_in = req.stats.p.ppp_ibytes; stats->bytes_out = req.stats.p.ppp_obytes; stats->pkts_in = req.stats.p.ppp_ipackets; stats->pkts_out = req.stats.p.ppp_opackets; return 1; } /******************************************************************** * * ccp_fatal_error - returns 1 if decompression was disabled as a * result of an error detected after decompression of a packet, * 0 otherwise. This is necessary because of patent nonsense. */ int ccp_fatal_error (int unit) { int flags; if (ioctl(ppp_dev_fd, PPPIOCGFLAGS, &flags) < 0) { error("Couldn't read compression error flags: %m"); flags = 0; } return flags & SC_DC_FERROR; } /******************************************************************** * * path_to_procfs - find the path to the proc file system mount point */ static char proc_path[MAXPATHLEN]; static int proc_path_len; static char *path_to_procfs(const char *tail) { struct mntent *mntent; FILE *fp; if (proc_path_len == 0) { /* Default the mount location of /proc */ strlcpy (proc_path, "/proc", sizeof(proc_path)); proc_path_len = 5; fp = fopen(MOUNTED, "r"); if (fp != NULL) { while ((mntent = getmntent(fp)) != NULL) { if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0) continue; if (strcmp(mntent->mnt_type, "proc") == 0) { strlcpy(proc_path, mntent->mnt_dir, sizeof(proc_path)); proc_path_len = strlen(proc_path); break; } } fclose (fp); } } strlcpy(proc_path + proc_path_len, tail, sizeof(proc_path) - proc_path_len); return proc_path; } /* * /proc/net/route parsing stuff. */ #define ROUTE_MAX_COLS 12 FILE *route_fd = (FILE *) 0; static char route_buffer[512]; static int route_dev_col, route_dest_col, route_gw_col; static int route_flags_col, route_mask_col; static int route_num_cols; static int open_route_table (void); static void close_route_table (void); static int read_route_table (struct rtentry *rt); /******************************************************************** * * 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; } } /******************************************************************** * * open_route_table - open the interface to the route table */ static char route_delims[] = " \t\n"; static int open_route_table (void) { char *path; close_route_table(); path = path_to_procfs("/net/route"); route_fd = fopen (path, "r"); if (route_fd == NULL) { error("can't open routing table %s: %m", path); return 0; } route_dev_col = 0; /* default to usual columns */ route_dest_col = 1; route_gw_col = 2; route_flags_col = 3; route_mask_col = 7; route_num_cols = 8; /* parse header line */ if (fgets(route_buffer, sizeof(route_buffer), route_fd) != 0) { char *p = route_buffer, *q; int col; for (col = 0; col < ROUTE_MAX_COLS; ++col) { int used = 1; if ((q = strtok(p, route_delims)) == 0) break; if (strcasecmp(q, "iface") == 0) route_dev_col = col; else if (strcasecmp(q, "destination") == 0) route_dest_col = col; else if (strcasecmp(q, "gateway") == 0) route_gw_col = col; else if (strcasecmp(q, "flags") == 0) route_flags_col = col; else if (strcasecmp(q, "mask") == 0) route_mask_col = col; else used = 0; if (used && col >= route_num_cols) route_num_cols = col + 1; p = NULL; } } return 1; } /******************************************************************** * * read_route_table - read the next entry from the route table */ static int read_route_table(struct rtentry *rt) { char *cols[ROUTE_MAX_COLS], *p; int col; memset (rt, '\0', sizeof (struct rtentry)); if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0) return 0; p = route_buffer; for (col = 0; col < route_num_cols; ++col) { cols[col] = strtok(p, route_delims); if (cols[col] == NULL) return 0; /* didn't get enough columns */ p = NULL; } SIN_ADDR(rt->rt_dst) = strtoul(cols[route_dest_col], NULL, 16); SIN_ADDR(rt->rt_gateway) = strtoul(cols[route_gw_col], NULL, 16); SIN_ADDR(rt->rt_genmask) = strtoul(cols[route_mask_col], NULL, 16); rt->rt_flags = (short) strtoul(cols[route_flags_col], NULL, 16); rt->rt_dev = cols[route_dev_col]; return 1; } /******************************************************************** * * defaultroute_exists - determine if there is a default route */ static int defaultroute_exists (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 (kernel_version > KVERSION(2,1,0) && SIN_ADDR(rt->rt_genmask) != 0) continue; if (SIN_ADDR(rt->rt_dst) == 0L) { result = 1; break; } } close_route_table(); return result; } /* * have_route_to - determine if the system has any route to * a given IP address. `addr' is in network byte order. * Return value is 1 if yes, 0 if no, -1 if don't know. * For demand mode to work properly, we have to ignore routes * through our own interface. */ int have_route_to(u_int32_t addr) { struct rtentry rt; int result = 0; if (!open_route_table()) return -1; /* don't know */ while (read_route_table(&rt)) { if ((rt.rt_flags & RTF_UP) == 0 || strcmp(rt.rt_dev, ifname) == 0) continue; if ((addr & SIN_ADDR(rt.rt_genmask)) == SIN_ADDR(rt.rt_dst)) { result = 1; break; } } close_route_table(); return result; } /******************************************************************** * * sifdefaultroute - assign a default route through the address given. */ int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway) { struct rtentry rt; if (defaultroute_exists(&rt) && strcmp(rt.rt_dev, ifname) != 0) { if (rt.rt_flags & RTF_GATEWAY) error("not replacing existing default route via %I", SIN_ADDR(rt.rt_gateway)); else error("not replacing existing default route through %s", rt.rt_dev); return 0; } memset (&rt, 0, sizeof (rt)); SET_SA_FAMILY (rt.rt_dst, AF_INET); rt.rt_dev = ifname; if (kernel_version > KVERSION(2,1,0)) { SET_SA_FAMILY (rt.rt_genmask, AF_INET); SIN_ADDR(rt.rt_genmask) = 0L; } rt.rt_flags = RTF_UP; if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) { if ( ! ok_error ( errno )) error("default route ioctl(SIOCADDRT): %m"); return 0; } have_default_route = 1; return 1; } /******************************************************************** * * cifdefaultroute - delete a default route through the address given. */ int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway) { struct rtentry rt; have_default_route = 0; 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; if (kernel_version > KVERSION(2,1,0)) { SET_SA_FAMILY (rt.rt_genmask, AF_INET); SIN_ADDR(rt.rt_genmask) = 0L; } rt.rt_flags = RTF_UP; if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) { if (still_ppp()) { if ( ! ok_error ( errno )) error("default route ioctl(SIOCDELRT): %m"); return 0; } } return 1; } /******************************************************************** * * sifproxyarp - Make a proxy ARP entry for the peer. */ int sifproxyarp (int unit, u_int32_t his_adr) { struct arpreq arpreq; char *forw_path; if (has_proxy_arp == 0) { memset (&arpreq, '\0', sizeof(arpreq)); SET_SA_FAMILY(arpreq.arp_pa, AF_INET); SIN_ADDR(arpreq.arp_pa) = his_adr; arpreq.arp_flags = ATF_PERM | ATF_PUBL; /* * Get the hardware address of an interface on the same subnet * as our local address. */ if (!get_ether_addr(his_adr, &arpreq.arp_ha, proxy_arp_dev, sizeof(proxy_arp_dev))) { error("Cannot determine ethernet address for proxy ARP"); return 0; } strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev)); if (ioctl(sock_fd, SIOCSARP, (caddr_t)&arpreq) < 0) { if ( ! ok_error ( errno )) error("ioctl(SIOCSARP): %m"); return 0; } proxy_arp_addr = his_adr; has_proxy_arp = 1; if (tune_kernel) { forw_path = path_to_procfs("/sys/net/ipv4/ip_forward"); if (forw_path != 0) { int fd = open(forw_path, O_WRONLY); if (fd >= 0) { if (write(fd, "1", 1) != 1) error("Couldn't enable IP forwarding: %m"); close(fd); } } } } return 1; } /******************************************************************** * * cifproxyarp - Delete the proxy ARP entry for the peer. */ int cifproxyarp (int unit, u_int32_t his_adr) { struct arpreq arpreq; if (has_proxy_arp) { has_proxy_arp = 0; memset (&arpreq, '\0', sizeof(arpreq)); SET_SA_FAMILY(arpreq.arp_pa, AF_INET); SIN_ADDR(arpreq.arp_pa) = his_adr; arpreq.arp_flags = ATF_PERM | ATF_PUBL; strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev)); if (ioctl(sock_fd, SIOCDARP, (caddr_t)&arpreq) < 0) { if ( ! ok_error ( errno )) warn("ioctl(SIOCDARP): %m"); return 0; } } return 1; } /******************************************************************** * * get_ether_addr - get the hardware address of an interface on the * the same subnet as ipaddr. */ static int get_ether_addr (u_int32_t ipaddr, struct sockaddr *hwaddr, char *name, int namelen) { struct ifreq *ifr, *ifend; u_int32_t ina, mask; char *aliasp; struct ifreq ifreq, bestifreq; struct ifconf ifc; struct ifreq ifs[MAX_IFS]; u_int32_t bestmask=0; int found_interface = 0; ifc.ifc_len = sizeof(ifs); ifc.ifc_req = ifs; if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) { if ( ! ok_error ( errno )) error("ioctl(SIOCGIFCONF): %m (line %d)", __LINE__); return 0; } /* * 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 = SIN_ADDR(ifr->ifr_addr); strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); /* * Check that the interface is up, and not point-to-point * nor loopback. */ if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0) continue; if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0) continue; /* * Get its netmask and check that it's on the right subnet. */ if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0) continue; mask = SIN_ADDR(ifreq.ifr_addr); if (((ipaddr ^ ina) & mask) != 0) continue; /* no match */ /* matched */ if (mask >= bestmask) { /* Compare using >= instead of > -- it is possible for an interface to have a netmask of 0.0.0.0 */ found_interface = 1; bestifreq = ifreq; bestmask = mask; } } } if (!found_interface) return 0; strlcpy(name, bestifreq.ifr_name, namelen); /* trim off the :1 in eth0:1 */ aliasp = strchr(name, ':'); if (aliasp != 0) *aliasp = 0; info("found interface %s for proxy arp", name); /* * Now get the hardware address. */ memset (&bestifreq.ifr_hwaddr, 0, sizeof (struct sockaddr)); if (ioctl (sock_fd, SIOCGIFHWADDR, &bestifreq) < 0) { error("SIOCGIFHWADDR(%s): %m", bestifreq.ifr_name); return 0; } memcpy (hwaddr, &bestifreq.ifr_hwaddr, sizeof (struct sockaddr)); return 1; } /* * get_if_hwaddr - get the hardware address for the specified * network interface device. */ int get_if_hwaddr(u_char *addr, char *name) { struct ifreq ifreq; int ret, sock_fd; sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if (sock_fd < 0) return 0; memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr)); strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name)); ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq); close(sock_fd); if (ret >= 0) memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6); return ret; } /* * get_first_ethernet - return the name of the first ethernet-style * interface on this system. */ char * get_first_ethernet() { return "eth0"; } /******************************************************************** * * Return user specified netmask, modified by any mask we might determine * for address `addr' (in network byte order). * Here we scan through the system's list of interfaces, looking for * any non-point-to-point interfaces which might appear to be on the same * network as `addr'. If we find any, we OR in their netmask to the * user-specified netmask. */ u_int32_t GetMask (u_int32_t addr) { u_int32_t mask, nmask, ina; struct ifreq *ifr, *ifend, ifreq; struct ifconf ifc; struct ifreq ifs[MAX_IFS]; addr = ntohl(addr); if (IN_CLASSA(addr)) /* determine network mask for address class */ nmask = IN_CLASSA_NET; else if (IN_CLASSB(addr)) nmask = IN_CLASSB_NET; else nmask = IN_CLASSC_NET; /* class D nets are disallowed by bad_ip_adrs */ mask = netmask | htonl(nmask); /* * Scan through the system's network interfaces. */ ifc.ifc_len = sizeof(ifs); ifc.ifc_req = ifs; if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) { if ( ! ok_error ( errno )) warn("ioctl(SIOCGIFCONF): %m (line %d)", __LINE__); return mask; } ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); for (ifr = ifc.ifc_req; ifr < ifend; ifr++) { /* * Check the interface's internet address. */ if (ifr->ifr_addr.sa_family != AF_INET) continue; ina = SIN_ADDR(ifr->ifr_addr); if (((ntohl(ina) ^ addr) & nmask) != 0) continue; /* * Check that the interface is up, and not point-to-point nor loopback. */ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0) continue; if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0) continue; /* * Get its netmask and OR it into our mask. */ if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0) continue; mask |= SIN_ADDR(ifreq.ifr_addr); break; } return mask; } /******************************************************************** * * Internal routine to decode the version.modification.patch level */ static void decode_version (char *buf, int *version, int *modification, int *patch) { char *endp; *version = (int) strtoul (buf, &endp, 10); *modification = 0; *patch = 0; if (endp != buf && *endp == '.') { buf = endp + 1; *modification = (int) strtoul (buf, &endp, 10); if (endp != buf && *endp == '.') { buf = endp + 1; *patch = (int) strtoul (buf, &buf, 10); } } } /******************************************************************** * * Procedure to determine if the PPP line discipline is registered. */ static int ppp_registered(void) { int local_fd; int mfd = -1; int ret = 0; char slave[16]; /* * We used to open the serial device and set it to the ppp line * discipline here, in order to create a ppp unit. But that is * not a good idea - the user might have specified a device that * they can't open (permission, or maybe it doesn't really exist). * So we grab a pty master/slave pair and use that. */ if (!get_pty(&mfd, &local_fd, slave, 0)) { no_ppp_msg = "Couldn't determine if PPP is supported (no free ptys)"; return 0; } /* * Try to put the device into the PPP discipline. */ if (ioctl(local_fd, TIOCSETD, &ppp_disc) < 0) { error("ioctl(TIOCSETD(PPP)): %m (line %d)", __LINE__); } else ret = 1; close(local_fd); close(mfd); return ret; } /******************************************************************** * * 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, fd, err; struct ifreq ifr; int size; int my_version, my_modification, my_patch; int osmaj, osmin, ospatch; /* get the kernel version now, since we are called before sys_init */ uname(&utsname); osmaj = osmin = ospatch = 0; sscanf(utsname.release, "%d.%d.%d", &osmaj, &osmin, &ospatch); kernel_version = KVERSION(osmaj, osmin, ospatch); fd = open("/dev/ppp", O_RDWR); if (fd >= 0) { new_style_driver = 1; /* XXX should get from driver */ driver_version = 2; driver_modification = 4; driver_patch = 0; close(fd); return 1; } err = errno; if (kernel_version >= KVERSION(2,3,13)) { error("Couldn't open the /dev/ppp device: %m"); if (errno == ENOENT) no_ppp_msg = "You need to create the /dev/ppp device node by\n" "executing the following command as root:\n" " mknod /dev/ppp c 108 0\n"; else if (errno == ENODEV || errno == ENXIO) no_ppp_msg = "Please load the ppp_generic kernel module.\n"; return 0; } /* we are running on a really really old kernel */ no_ppp_msg = "This system lacks kernel support for PPP. This could be because\n" "the PPP kernel module could not be loaded, or because PPP was not\n" "included in the kernel configuration. If PPP was included as a\n" "module, try `/sbin/modprobe -v ppp'. If that fails, check that\n" "ppp.o exists in /lib/modules/`uname -r`/net.\n" "See README.linux file in the ppp distribution for more details.\n"; /* * Open a socket for doing the ioctl operations. */ s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) return 0; strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name)); ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0; /* * If the device did not exist then attempt to create one by putting the * current tty into the PPP discipline. If this works then obtain the * flags for the device again. */ if (!ok) { if (ppp_registered()) { strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name)); ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0; } } /* * Ensure that the hardware address is for PPP and not something else */ if (ok) ok = ioctl (s, SIOCGIFHWADDR, (caddr_t) &ifr) >= 0; if (ok && ((ifr.ifr_hwaddr.sa_family & ~0xFF) != ARPHRD_PPP)) ok = 0; /* * This is the PPP device. Validate the version of the driver at this * point to ensure that this program will work with the driver. */ if (ok) { char abBuffer [1024]; ifr.ifr_data = abBuffer; size = ioctl (s, SIOCGPPPVER, (caddr_t) &ifr); if (size < 0) { error("Couldn't read driver version: %m"); ok = 0; no_ppp_msg = "Sorry, couldn't verify kernel driver version\n"; } else { decode_version(abBuffer, &driver_version, &driver_modification, &driver_patch); /* * Validate the version of the driver against the version that we used. */ decode_version(VERSION, &my_version, &my_modification, &my_patch); /* The version numbers must match */ if (driver_version != my_version) ok = 0; /* The modification levels must be legal */ if (driver_modification < 3) { if (driver_modification >= 2) { /* we can cope with 2.2.0 and above */ driver_is_old = 1; } else { ok = 0; } } close (s); if (!ok) { slprintf(route_buffer, sizeof(route_buffer), "Sorry - PPP driver version %d.%d.%d is out of date\n", driver_version, driver_modification, driver_patch); no_ppp_msg = route_buffer; } } } return ok; } /******************************************************************** * * Update the wtmp file with the appropriate user name and tty device. */ void logwtmp (const char *line, const char *name, const char *host) { struct utmp ut, *utp; pid_t mypid = getpid(); #if __GLIBC__ < 2 int wtmp; #endif /* * Update the signon database for users. * Christoph Lameter: Copied from poeigl-1.36 Jan 3, 1996 */ utmpname(_PATH_UTMP); setutent(); while ((utp = getutent()) && (utp->ut_pid != mypid)) /* nothing */; if (utp) memcpy(&ut, utp, sizeof(ut)); else /* some gettys/telnetds don't initialize utmp... */ memset(&ut, 0, sizeof(ut)); if (ut.ut_id[0] == 0) strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); strncpy(ut.ut_user, name, sizeof(ut.ut_user)); strncpy(ut.ut_line, line, sizeof(ut.ut_line)); time(&ut.ut_time); ut.ut_type = USER_PROCESS; ut.ut_pid = mypid; /* Insert the host name if one is supplied */ if (*host) strncpy (ut.ut_host, host, sizeof(ut.ut_host)); /* Insert the IP address of the remote system if IP is enabled */ if (ipcp_protent.enabled_flag && ipcp_hisoptions[0].neg_addr) memcpy(&ut.ut_addr, (char *) &ipcp_hisoptions[0].hisaddr, sizeof(ut.ut_addr)); /* CL: Makes sure that the logout works */ if (*host == 0 && *name==0) ut.ut_host[0]=0; pututline(&ut); endutent(); /* * Update the wtmp file. */ #if __GLIBC__ >= 2 updwtmp(_PATH_WTMP, &ut); #else wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY); if (wtmp >= 0) { flock(wtmp, LOCK_EX); if (write (wtmp, (char *)&ut, sizeof(ut)) != sizeof(ut)) warn("error writing %s: %m", _PATH_WTMP); flock(wtmp, LOCK_UN); close (wtmp); } #endif } /******************************************************************** * * sifvjcomp - config tcp header compression */ int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid) { u_int x; if (vjcomp) { if (ioctl(ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) error("Couldn't set up TCP header compression: %m"); vjcomp = 0; } x = (vjcomp? SC_COMP_TCP: 0) | (cidcomp? 0: SC_NO_TCP_CCID); modify_flags(ppp_dev_fd, SC_COMP_TCP|SC_NO_TCP_CCID, x); return 1; } /******************************************************************** * * sifup - Config the interface up and enable IP packets to pass. */ int sifup(int u) { struct ifreq ifr; memset (&ifr, '\0', sizeof (ifr)); strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { if (! ok_error (errno)) error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__); return 0; } ifr.ifr_flags |= (IFF_UP | IFF_POINTOPOINT); if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { if (! ok_error (errno)) error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__); return 0; } if_is_up++; return 1; } /******************************************************************** * * sifdown - Disable the indicated protocol and config the interface * down if there are no remaining protocols. */ int sifdown (int u) { struct ifreq ifr; if (if_is_up && --if_is_up > 0) return 1; memset (&ifr, '\0', sizeof (ifr)); strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { if (! ok_error (errno)) error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__); return 0; } ifr.ifr_flags &= ~IFF_UP; ifr.ifr_flags |= IFF_POINTOPOINT; if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { if (! ok_error (errno)) error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__); return 0; } return 1; } /******************************************************************** * * sifaddr - Config the interface IP addresses and netmask. */ int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr, u_int32_t net_mask) { struct ifreq ifr; struct rtentry rt; memset (&ifr, '\0', sizeof (ifr)); memset (&rt, '\0', sizeof (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); strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); /* * Set our IP address */ SIN_ADDR(ifr.ifr_addr) = our_adr; if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) { if (errno != EEXIST) { if (! ok_error (errno)) error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__); } else { warn("ioctl(SIOCSIFADDR): Address already exists"); } return (0); } /* * Set the gateway address */ if (his_adr != 0) { SIN_ADDR(ifr.ifr_dstaddr) = his_adr; if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) { if (! ok_error (errno)) error("ioctl(SIOCSIFDSTADDR): %m (line %d)", __LINE__); return (0); } } /* * Set the netmask. * For recent kernels, force the netmask to 255.255.255.255. */ if (kernel_version >= KVERSION(2,1,16)) net_mask = ~0L; if (net_mask != 0) { SIN_ADDR(ifr.ifr_netmask) = net_mask; if (ioctl(sock_fd, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) { if (! ok_error (errno)) error("ioctl(SIOCSIFNETMASK): %m (line %d)", __LINE__); return (0); } } /* * Add the device route */ if (kernel_version < KVERSION(2,1,16)) { SET_SA_FAMILY (rt.rt_dst, AF_INET); SET_SA_FAMILY (rt.rt_gateway, AF_INET); rt.rt_dev = ifname; SIN_ADDR(rt.rt_gateway) = 0L; SIN_ADDR(rt.rt_dst) = his_adr; rt.rt_flags = RTF_UP | RTF_HOST; if (kernel_version > KVERSION(2,1,0)) { SET_SA_FAMILY (rt.rt_genmask, AF_INET); SIN_ADDR(rt.rt_genmask) = -1L; } if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) { if (! ok_error (errno)) error("ioctl(SIOCADDRT) device route: %m (line %d)", __LINE__); return (0); } } /* set ip_dynaddr in demand mode if address changes */ if (demand && tune_kernel && !dynaddr_set && our_old_addr && our_old_addr != our_adr) { /* set ip_dynaddr if possible */ char *path; int fd; path = path_to_procfs("/sys/net/ipv4/ip_dynaddr"); if (path != 0 && (fd = open(path, O_WRONLY)) >= 0) { if (write(fd, "1", 1) != 1) error("Couldn't enable dynamic IP addressing: %m"); close(fd); } dynaddr_set = 1; /* only 1 attempt */ } our_old_addr = 0; return 1; } /******************************************************************** * * cifaddr - Clear the interface IP addresses, and delete routes * through the interface if possible. */ int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr) { struct ifreq ifr; if (kernel_version < KVERSION(2,1,16)) { /* * Delete the route through the device */ struct rtentry rt; 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; SIN_ADDR(rt.rt_gateway) = 0; SIN_ADDR(rt.rt_dst) = his_adr; rt.rt_flags = RTF_UP | RTF_HOST; if (kernel_version > KVERSION(2,1,0)) { SET_SA_FAMILY (rt.rt_genmask, AF_INET); SIN_ADDR(rt.rt_genmask) = -1L; } if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) { if (still_ppp() && ! ok_error (errno)) error("ioctl(SIOCDELRT) device route: %m (line %d)", __LINE__); return (0); } } /* This way it is possible to have an IPX-only or IPv6-only interface */ memset(&ifr, 0, sizeof(ifr)); SET_SA_FAMILY(ifr.ifr_addr, AF_INET); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) { if (! ok_error (errno)) { error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__); return 0; } } our_old_addr = our_adr; return 1; } #ifdef INET6 /******************************************************************** * * sif6addr - Config the interface with an IPv6 link-local address */ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64) { struct in6_ifreq ifr6; struct ifreq ifr; struct in6_rtmsg rt6; if (sock6_fd < 0) { errno = -sock6_fd; error("IPv6 socket creation failed: %m"); return 0; } memset(&ifr, 0, sizeof (ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) { error("sif6addr: ioctl(SIOCGIFINDEX): %m (line %d)", __LINE__); return 0; } /* Local interface */ memset(&ifr6, 0, sizeof(ifr6)); IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64); ifr6.ifr6_ifindex = ifr.ifr_ifindex; ifr6.ifr6_prefixlen = 10; if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6) < 0) { error("sif6addr: ioctl(SIOCSIFADDR): %m (line %d)", __LINE__); return 0; } /* Route to remote host */ memset(&rt6, 0, sizeof(rt6)); IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64); rt6.rtmsg_flags = RTF_UP; rt6.rtmsg_dst_len = 10; rt6.rtmsg_ifindex = ifr.ifr_ifindex; rt6.rtmsg_metric = 1; if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) { error("sif6addr: ioctl(SIOCADDRT): %m (line %d)", __LINE__); return 0; } return 1; } /******************************************************************** * * cif6addr - Remove IPv6 address from interface */ int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64) { struct ifreq ifr; struct in6_ifreq ifr6; if (sock6_fd < 0) { errno = -sock6_fd; error("IPv6 socket creation failed: %m"); return 0; } memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) { error("cif6addr: ioctl(SIOCGIFINDEX): %m (line %d)", __LINE__); return 0; } memset(&ifr6, 0, sizeof(ifr6)); IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64); ifr6.ifr6_ifindex = ifr.ifr_ifindex; ifr6.ifr6_prefixlen = 10; if (ioctl(sock6_fd, SIOCDIFADDR, &ifr6) < 0) { if (errno != EADDRNOTAVAIL) { if (! ok_error (errno)) error("cif6addr: ioctl(SIOCDIFADDR): %m (line %d)", __LINE__); } else { warn("cif6addr: ioctl(SIOCDIFADDR): No such address"); } return (0); } return 1; } #endif /* INET6 */ /* * get_pty - get a pty master/slave pair and chown the slave side * to the uid given. Assumes slave_name points to >= 16 bytes of space. */ int get_pty(master_fdp, slave_fdp, slave_name, uid) int *master_fdp; int *slave_fdp; char *slave_name; int uid; { int i, mfd, sfd = -1; char pty_name[16]; struct termios tios; #ifdef TIOCGPTN /* * Try the unix98 way first. */ mfd = open("/dev/ptmx", O_RDWR); if (mfd >= 0) { int ptn; if (ioctl(mfd, TIOCGPTN, &ptn) >= 0) { slprintf(pty_name, sizeof(pty_name), "/dev/pts/%d", ptn); chmod(pty_name, S_IRUSR | S_IWUSR); #ifdef TIOCSPTLCK ptn = 0; if (ioctl(mfd, TIOCSPTLCK, &ptn) < 0) warn("Couldn't unlock pty slave %s: %m", pty_name); #endif if ((sfd = open(pty_name, O_RDWR | O_NOCTTY)) < 0) warn("Couldn't open pty slave %s: %m", pty_name); } } #endif /* TIOCGPTN */ if (sfd < 0) { /* the old way - scan through the pty name space */ for (i = 0; i < 64; ++i) { slprintf(pty_name, sizeof(pty_name), "/dev/pty%c%x", 'p' + i / 16, i % 16); mfd = open(pty_name, O_RDWR, 0); if (mfd >= 0) { pty_name[5] = 't'; sfd = open(pty_name, O_RDWR | O_NOCTTY, 0); if (sfd >= 0) { fchown(sfd, uid, -1); fchmod(sfd, S_IRUSR | S_IWUSR); break; } close(mfd); } } } if (sfd < 0) return 0; strlcpy(slave_name, pty_name, 16); *master_fdp = mfd; *slave_fdp = sfd; if (tcgetattr(sfd, &tios) == 0) { tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB); tios.c_cflag |= CS8 | CREAD | CLOCAL; tios.c_iflag = IGNPAR; tios.c_oflag = 0; tios.c_lflag = 0; if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0) warn("couldn't set attributes on pty: %m"); } else warn("couldn't get attributes on pty: %m"); return 1; } /******************************************************************** * * open_loopback - open the device we use for getting packets * in demand mode. Under Linux, we use a pty master/slave pair. */ int open_ppp_loopback(void) { int flags; looped = 1; if (new_style_driver) { /* allocate ourselves a ppp unit */ if (make_ppp_unit() < 0) die(1); modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC); set_kdebugflag(kdebugflag); ppp_fd = -1; return ppp_dev_fd; } if (!get_pty(&master_fd, &slave_fd, loop_name, 0)) fatal("No free pty for loopback"); set_ppp_fd(slave_fd); flags = fcntl(master_fd, F_GETFL); if (flags == -1 || fcntl(master_fd, F_SETFL, flags | O_NONBLOCK) == -1) warn("couldn't set master loopback to nonblock: %m"); flags = fcntl(ppp_fd, F_GETFL); if (flags == -1 || fcntl(ppp_fd, F_SETFL, flags | O_NONBLOCK) == -1) warn("couldn't set slave loopback to nonblock: %m"); if (ioctl(ppp_fd, TIOCSETD, &ppp_disc) < 0) fatal("ioctl(TIOCSETD): %m (line %d)", __LINE__); /* * Find out which interface we were given. */ if (ioctl(ppp_fd, PPPIOCGUNIT, &ifunit) < 0) fatal("ioctl(PPPIOCGUNIT): %m (line %d)", __LINE__); /* * Enable debug in the driver if requested. */ set_kdebugflag (kdebugflag); return master_fd; } /******************************************************************** * * sifnpmode - Set the mode for handling packets for a given NP. */ int sifnpmode(u, proto, mode) int u; int proto; enum NPmode mode; { struct npioctl npi; npi.protocol = proto; npi.mode = mode; if (ioctl(ppp_dev_fd, PPPIOCSNPMODE, (caddr_t) &npi) < 0) { if (! ok_error (errno)) error("ioctl(PPPIOCSNPMODE, %d, %d): %m", proto, mode); return 0; } return 1; } /******************************************************************** * * sipxfaddr - Config the interface IPX networknumber */ int sipxfaddr (int unit, unsigned long int network, unsigned char * node ) { int result = 1; #ifdef IPX_CHANGE int skfd; struct ifreq ifr; struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr; skfd = socket (AF_IPX, SOCK_DGRAM, 0); if (skfd < 0) { if (! ok_error (errno)) dbglog("socket(AF_IPX): %m (line %d)", __LINE__); result = 0; } else { memset (&ifr, '\0', sizeof (ifr)); strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); memcpy (sipx->sipx_node, node, IPX_NODE_LEN); sipx->sipx_family = AF_IPX; sipx->sipx_port = 0; sipx->sipx_network = htonl (network); sipx->sipx_type = IPX_FRAME_ETHERII; sipx->sipx_action = IPX_CRTITF; /* * Set the IPX device */ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) { result = 0; if (errno != EEXIST) { if (! ok_error (errno)) dbglog("ioctl(SIOCSIFADDR, CRTITF): %m (line %d)", __LINE__); } else { warn("ioctl(SIOCSIFADDR, CRTITF): Address already exists"); } } close (skfd); } #endif return result; } /******************************************************************** * * cipxfaddr - Clear the information for the IPX network. The IPX routes * are removed and the device is no longer able to pass IPX * frames. */ int cipxfaddr (int unit) { int result = 1; #ifdef IPX_CHANGE int skfd; struct ifreq ifr; struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr; skfd = socket (AF_IPX, SOCK_DGRAM, 0); if (skfd < 0) { if (! ok_error (errno)) dbglog("socket(AF_IPX): %m (line %d)", __LINE__); result = 0; } else { memset (&ifr, '\0', sizeof (ifr)); strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); sipx->sipx_type = IPX_FRAME_ETHERII; sipx->sipx_action = IPX_DLTITF; sipx->sipx_family = AF_IPX; /* * Set the IPX device */ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) { if (! ok_error (errno)) info("ioctl(SIOCSIFADDR, IPX_DLTITF): %m (line %d)", __LINE__); result = 0; } close (skfd); } #endif return result; } /* * Use the hostname as part of the random number seed. */ int get_host_seed() { int h; char *p = hostname; h = 407; for (p = hostname; *p != 0; ++p) h = h * 37 + *p; return h; } /******************************************************************** * * sys_check_options - check the options that the user specified */ int sys_check_options(void) { #ifdef IPX_CHANGE /* * Disable the IPX protocol if the support is not present in the kernel. */ char *path; if (ipxcp_protent.enabled_flag) { struct stat stat_buf; if ( ((path = path_to_procfs("/net/ipx/interface")) == NULL && (path = path_to_procfs("/net/ipx_interface")) == NULL) || lstat(path, &stat_buf) < 0) { error("IPX support is not present in the kernel\n"); ipxcp_protent.enabled_flag = 0; } } #endif if (demand && driver_is_old) { option_error("demand dialling is not supported by kernel driver " "version %d.%d.%d", driver_version, driver_modification, driver_patch); return 0; } if (multilink && !new_style_driver) { warn("Warning: multilink is not supported by the kernel driver"); multilink = 0; } return 1; } #ifdef INET6 /* * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI * * convert the 48-bit MAC address of eth0 into EUI 64. caller also assumes * that the system has a properly configured Ethernet interface for this * function to return non-zero. */ int ether_to_eui64(eui64_t *p_eui64) { struct ifreq ifr; int skfd; const unsigned char *ptr; skfd = socket(PF_INET6, SOCK_DGRAM, 0); if(skfd == -1) { warn("could not open IPv6 socket"); return 0; } strcpy(ifr.ifr_name, "eth0"); if(ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) { close(skfd); warn("could not obtain hardware address for eth0"); return 0; } close(skfd); /* * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1] */ ptr = ifr.ifr_hwaddr.sa_data; p_eui64->e8[0] = ptr[0] | 0x02; p_eui64->e8[1] = ptr[1]; p_eui64->e8[2] = ptr[2]; p_eui64->e8[3] = 0xFF; p_eui64->e8[4] = 0xFE; p_eui64->e8[5] = ptr[3]; p_eui64->e8[6] = ptr[4]; p_eui64->e8[7] = ptr[5]; return 1; } #endif ppp-2.4.5/pppd/sys-solaris.c000066400000000000000000002010061130035057700157200ustar00rootroot00000000000000/* * System-dependent procedures for pppd under Solaris 2. * * Parts re-written by Adi Masputra , based on * the original sys-svr4.c * * Copyright (c) 2000 by Sun Microsystems, Inc. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation is hereby granted, provided that the above copyright * notice appears in all copies. * * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES * * Copyright (c) 1995-2002 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Derived from main.c and pppd.h, which are: * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: sys-solaris.c,v 1.16 2008/01/30 14:26:53 carlsonj Exp $" #include #include #include #include #include #include #include #include #include #include #ifndef CRTSCTS #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SOL2 #include #include #include #include #include #endif #include "pppd.h" #include "fsm.h" #include "lcp.h" #include "ipcp.h" #include "ccp.h" #if !defined(PPP_DRV_NAME) #define PPP_DRV_NAME "ppp" #endif /* !defined(PPP_DRV_NAME) */ #if !defined(PPP_DEV_NAME) #define PPP_DEV_NAME "/dev/" PPP_DRV_NAME #endif /* !defined(PPP_DEV_NAME) */ #if !defined(AHDLC_MOD_NAME) #define AHDLC_MOD_NAME "ppp_ahdl" #endif /* !defined(AHDLC_MOD_NAME) */ #if !defined(COMP_MOD_NAME) #define COMP_MOD_NAME "ppp_comp" #endif /* !defined(COMP_MOD_NAME) */ #if !defined(IP_DEV_NAME) #define IP_DEV_NAME "/dev/ip" #endif /* !defined(IP_DEV_NAME) */ #if !defined(IP_MOD_NAME) #define IP_MOD_NAME "ip" #endif /* !defined(IP_MOD_NAME) */ #if !defined(UDP_DEV_NAME) && defined(SOL2) #define UDP_DEV_NAME "/dev/udp" #endif /* !defined(UDP_DEV_NAME) && defined(SOL2) */ #if !defined(UDP6_DEV_NAME) && defined(SOL2) #define UDP6_DEV_NAME "/dev/udp6" #endif /* !defined(UDP6_DEV_NAME) && defined(SOL2) */ static const char rcsid[] = RCSID; #if defined(SOL2) /* * "/dev/udp" is used as a multiplexor to PLINK the interface stream * under. It is used in place of "/dev/ip" since STREAMS will not let * a driver be PLINK'ed under itself, and "/dev/ip" is typically the * driver at the bottom of the tunneling interfaces stream. */ static char *mux_dev_name = UDP_DEV_NAME; #else static char *mux_dev_name = IP_DEV_NAME; #endif static int pppfd; static int fdmuxid = -1; static int ipfd; static int ipmuxid = -1; #if defined(INET6) && defined(SOL2) static int ip6fd; /* IP file descriptor */ static int ip6muxid = -1; /* Multiplexer file descriptor */ static int if6_is_up = 0; /* IPv6 interface has been marked up */ #define _IN6_LLX_FROM_EUI64(l, s, eui64, as) do { \ s->sin6_addr.s6_addr32[0] = htonl(as); \ eui64_copy(eui64, s->sin6_addr.s6_addr32[2]); \ s->sin6_family = AF_INET6; \ l.lifr_addr.ss_family = AF_INET6; \ l.lifr_addrlen = 64; \ l.lifr_addr = laddr; \ } while (0) #define IN6_LLADDR_FROM_EUI64(l, s, eui64) \ _IN6_LLX_FROM_EUI64(l, s, eui64, 0xfe800000) #define IN6_LLTOKEN_FROM_EUI64(l, s, eui64) \ _IN6_LLX_FROM_EUI64(l, s, eui64, 0) #endif /* defined(INET6) && defined(SOL2) */ #if defined(INET6) && defined(SOL2) static char first_ether_name[LIFNAMSIZ]; /* Solaris 8 and above */ #else static char first_ether_name[IFNAMSIZ]; /* Before Solaris 8 */ #define MAXIFS 256 /* Max # of interfaces */ #endif /* defined(INET6) && defined(SOL2) */ static int restore_term; static struct termios inittermios; #ifndef CRTSCTS static struct termiox inittermiox; static int termiox_ok; #endif static struct winsize wsinfo; /* Initial window size info */ static pid_t tty_sid; /* original session ID for terminal */ extern u_char inpacket_buf[]; /* borrowed from main.c */ #define MAX_POLLFDS 32 static struct pollfd pollfds[MAX_POLLFDS]; static int n_pollfds; static int link_mtu, link_mru; #define NMODULES 32 static int tty_nmodules; static char tty_modules[NMODULES][FMNAMESZ+1]; static int tty_npushed; static int if_is_up; /* Interface has been marked up */ static u_int32_t remote_addr; /* IP address of peer */ static u_int32_t default_route_gateway; /* Gateway for default route added */ static u_int32_t proxy_arp_addr; /* Addr for proxy arp entry added */ /* Prototypes for procedures local to this file. */ static int translate_speed __P((int)); static int baud_rate_of __P((int)); static int get_ether_addr __P((u_int32_t, struct sockaddr *)); static int get_hw_addr __P((char *, u_int32_t, struct sockaddr *)); static int get_hw_addr_dlpi __P((char *, struct sockaddr *)); static int dlpi_attach __P((int, int)); static int dlpi_info_req __P((int)); static int dlpi_get_reply __P((int, union DL_primitives *, int, int)); static int strioctl __P((int, int, void *, int, int)); #ifdef SOL2 /* * sifppa - Sets interface ppa * * without setting the ppa, ip module will return EINVAL upon setting the * interface UP (SIOCSxIFFLAGS). This is because ip module in 2.8 expects * two DLPI_INFO_REQ to be sent down to the driver (below ip) before * IFF_UP can be set. Plumbing the device causes one DLPI_INFO_REQ to * be sent down, and the second DLPI_INFO_REQ is sent upon receiving * IF_UNITSEL (old) or SIOCSLIFNAME (new) ioctls. Such setting of the ppa * is required because the ppp DLPI provider advertises itself as * a DLPI style 2 type, which requires a point of attachment to be * specified. The only way the user can specify a point of attachment * is via SIOCSLIFNAME or IF_UNITSEL. * * Such changes in the behavior of ip module was made to meet new or * evolving standards requirements. * */ static int sifppa(fd, ppa) int fd; int ppa; { return (int)ioctl(fd, IF_UNITSEL, (char *)&ppa); } #endif /* SOL2 */ #if defined(SOL2) && defined(INET6) /* * get_first_ethernet - returns the first Ethernet interface name found in * the system, or NULL if none is found * * NOTE: This is the lifreq version (Solaris 8 and above) */ char * get_first_ethernet() { struct lifnum lifn; struct lifconf lifc; struct lifreq *plifreq; struct lifreq lifr; int fd, num_ifs, i, found; uint_t fl, req_size; char *req; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { return 0; } /* * Find out how many interfaces are running */ lifn.lifn_family = AF_UNSPEC; lifn.lifn_flags = LIFC_NOXMIT; if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0) { close(fd); error("could not determine number of interfaces: %m"); return 0; } num_ifs = lifn.lifn_count; req_size = num_ifs * sizeof(struct lifreq); req = malloc(req_size); if (req == NULL) { close(fd); error("out of memory"); return 0; } /* * Get interface configuration info for all interfaces */ lifc.lifc_family = AF_UNSPEC; lifc.lifc_flags = LIFC_NOXMIT; lifc.lifc_len = req_size; lifc.lifc_buf = req; if (ioctl(fd, SIOCGLIFCONF, &lifc) < 0) { close(fd); free(req); error("SIOCGLIFCONF: %m"); return 0; } /* * And traverse each interface to look specifically for the first * occurence of an Ethernet interface which has been marked up */ plifreq = lifc.lifc_req; found = 0; for (i = lifc.lifc_len / sizeof(struct lifreq); i > 0; i--, plifreq++) { if (strchr(plifreq->lifr_name, ':') != NULL) continue; memset(&lifr, 0, sizeof(lifr)); strncpy(lifr.lifr_name, plifreq->lifr_name, sizeof(lifr.lifr_name)); if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) { close(fd); free(req); error("SIOCGLIFFLAGS: %m"); return 0; } fl = lifr.lifr_flags; if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP | IFF_BROADCAST)) continue; found = 1; break; } free(req); close(fd); if (found) { strncpy(first_ether_name, lifr.lifr_name, sizeof(first_ether_name)); return (char *)first_ether_name; } else return NULL; } #else /* * get_first_ethernet - returns the first Ethernet interface name found in * the system, or NULL if none is found * * NOTE: This is the ifreq version (before Solaris 8). */ char * get_first_ethernet() { struct ifconf ifc; struct ifreq *pifreq; struct ifreq ifr; int fd, num_ifs, i, found; uint_t fl, req_size; char *req; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { return 0; } /* * Find out how many interfaces are running */ if (ioctl(fd, SIOCGIFNUM, (char *)&num_ifs) < 0) { num_ifs = MAXIFS; } req_size = num_ifs * sizeof(struct ifreq); req = malloc(req_size); if (req == NULL) { close(fd); error("out of memory"); return 0; } /* * Get interface configuration info for all interfaces */ ifc.ifc_len = req_size; ifc.ifc_buf = req; if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { close(fd); free(req); error("SIOCGIFCONF: %m"); return 0; } /* * And traverse each interface to look specifically for the first * occurence of an Ethernet interface which has been marked up */ pifreq = ifc.ifc_req; found = 0; for (i = ifc.ifc_len / sizeof(struct ifreq); i > 0; i--, pifreq++) { if (strchr(pifreq->ifr_name, ':') != NULL) continue; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, pifreq->ifr_name, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { close(fd); free(req); error("SIOCGIFFLAGS: %m"); return 0; } fl = ifr.ifr_flags; if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP | IFF_BROADCAST)) continue; found = 1; break; } free(req); close(fd); if (found) { strncpy(first_ether_name, ifr.ifr_name, sizeof(first_ether_name)); return (char *)first_ether_name; } else return NULL; } #endif /* defined(SOL2) && defined(INET6) */ #if defined(SOL2) /* * get_if_hwaddr - get the hardware address for the specified * network interface device. */ int get_if_hwaddr(u_char *addr, char *if_name) { struct sockaddr s_eth_addr; struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data; if (if_name == NULL) return -1; /* * Send DL_INFO_REQ to the driver to solicit its MAC address */ if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) { error("could not obtain hardware address for %s", if_name); return -1; } memcpy(addr, eth_addr->ether_addr_octet, 6); return 1; } #endif /* SOL2 */ #if defined(SOL2) && defined(INET6) /* * slifname - Sets interface ppa and flags * * in addition to the comments stated in sifppa(), IFF_IPV6 bit must * be set in order to declare this as an IPv6 interface */ static int slifname(fd, ppa) int fd; int ppa; { struct lifreq lifr; int ret; memset(&lifr, 0, sizeof(lifr)); ret = ioctl(fd, SIOCGLIFFLAGS, &lifr); if (ret < 0) goto slifname_done; lifr.lifr_flags |= IFF_IPV6; lifr.lifr_flags &= ~(IFF_BROADCAST | IFF_IPV4); lifr.lifr_ppa = ppa; strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); ret = ioctl(fd, SIOCSLIFNAME, &lifr); slifname_done: return ret; } /* * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI * * walks the list of valid ethernet interfaces, and convert the first * found 48-bit MAC address into EUI 64. caller also assumes that * the system has a properly configured Ethernet interface for this * function to return non-zero. */ int ether_to_eui64(eui64_t *p_eui64) { struct sockaddr s_eth_addr; struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data; char *if_name; if ((if_name = get_first_ethernet()) == NULL) { error("no persistent id can be found"); return 0; } /* * Send DL_INFO_REQ to the driver to solicit its MAC address */ if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) { error("could not obtain hardware address for %s", if_name); return 0; } /* * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1] */ p_eui64->e8[0] = (eth_addr->ether_addr_octet[0] & 0xFF) | 0x02; p_eui64->e8[1] = (eth_addr->ether_addr_octet[1] & 0xFF); p_eui64->e8[2] = (eth_addr->ether_addr_octet[2] & 0xFF); p_eui64->e8[3] = 0xFF; p_eui64->e8[4] = 0xFE; p_eui64->e8[5] = (eth_addr->ether_addr_octet[3] & 0xFF); p_eui64->e8[6] = (eth_addr->ether_addr_octet[4] & 0xFF); p_eui64->e8[7] = (eth_addr->ether_addr_octet[5] & 0xFF); return 1; } #endif /* defined(SOL2) && defined(INET6) */ /* * sys_init - System-dependent initialization. */ void sys_init() { int ifd, x; struct ifreq ifr; #if defined(INET6) && defined(SOL2) int i6fd; struct lifreq lifr; #endif /* defined(INET6) && defined(SOL2) */ #if !defined(SOL2) struct { union DL_primitives prim; char space[64]; } reply; #endif /* !defined(SOL2) */ ipfd = open(mux_dev_name, O_RDWR, 0); if (ipfd < 0) fatal("Couldn't open IP device: %m"); #if defined(INET6) && defined(SOL2) ip6fd = open(UDP6_DEV_NAME, O_RDWR, 0); if (ip6fd < 0) fatal("Couldn't open IP device (2): %m"); #endif /* defined(INET6) && defined(SOL2) */ if (default_device && !notty) tty_sid = getsid((pid_t)0); pppfd = open(PPP_DEV_NAME, O_RDWR | O_NONBLOCK, 0); if (pppfd < 0) fatal("Can't open %s: %m", PPP_DEV_NAME); if (kdebugflag & 1) { x = PPPDBG_LOG + PPPDBG_DRIVER; strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0); } /* Assign a new PPA and get its unit number. */ if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0) fatal("Can't create new PPP interface: %m"); #if defined(SOL2) /* * Since sys_init() is called prior to ifname being set in main(), * we need to get the ifname now, otherwise slifname(), and others, * will fail, or maybe, I should move them to a later point ? * */ sprintf(ifname, PPP_DRV_NAME "%d", ifunit); #endif /* defined(SOL2) */ /* * Open the ppp device again and link it under the ip multiplexor. * IP will assign a unit number which hopefully is the same as ifunit. * I don't know any way to be certain they will be the same. :-( */ ifd = open(PPP_DEV_NAME, O_RDWR, 0); if (ifd < 0) fatal("Can't open %s (2): %m", PPP_DEV_NAME); if (kdebugflag & 1) { x = PPPDBG_LOG + PPPDBG_DRIVER; strioctl(ifd, PPPIO_DEBUG, &x, sizeof(int), 0); } #if defined(INET6) && defined(SOL2) i6fd = open(PPP_DEV_NAME, O_RDWR, 0); if (i6fd < 0) { close(ifd); fatal("Can't open %s (3): %m", PPP_DEV_NAME); } if (kdebugflag & 1) { x = PPPDBG_LOG + PPPDBG_DRIVER; strioctl(i6fd, PPPIO_DEBUG, &x, sizeof(int), 0); } #endif /* defined(INET6) && defined(SOL2) */ #if defined(SOL2) if (ioctl(ifd, I_PUSH, IP_MOD_NAME) < 0) { close(ifd); #if defined(INET6) close(i6fd); #endif /* defined(INET6) */ fatal("Can't push IP module: %m"); } /* * Assign ppa according to the unit number returned by ppp device * after plumbing is completed above. */ if (sifppa(ifd, ifunit) < 0) { close (ifd); #if defined(INET6) close(i6fd); #endif /* defined(INET6) */ fatal("Can't set ppa for unit %d: %m", ifunit); } #if defined(INET6) /* * An IPv6 interface is created anyway, even when the user does not * explicitly enable it. Note that the interface will be marked * IPv6 during slifname(). */ if (ioctl(i6fd, I_PUSH, IP_MOD_NAME) < 0) { close(ifd); close(i6fd); fatal("Can't push IP module (2): %m"); } /* * Assign ppa according to the unit number returned by ppp device * after plumbing is completed above. In addition, mark the interface * as an IPv6 interface. */ if (slifname(i6fd, ifunit) < 0) { close(ifd); close(i6fd); fatal("Can't set ifname for unit %d: %m", ifunit); } #endif /* defined(INET6) */ ipmuxid = ioctl(ipfd, I_PLINK, ifd); close(ifd); if (ipmuxid < 0) { #if defined(INET6) close(i6fd); #endif /* defined(INET6) */ fatal("Can't I_PLINK PPP device to IP: %m"); } memset(&ifr, 0, sizeof(ifr)); sprintf(ifr.ifr_name, "%s", ifname); ifr.ifr_ip_muxid = ipmuxid; /* * In Sol 8 and later, STREAMS dynamic module plumbing feature exists. * This is so that an arbitrary module can be inserted, or deleted, * between ip module and the device driver without tearing down the * existing stream. Such feature requires the mux ids, which is set * by SIOCSIFMUXID (or SIOCLSIFMUXID). */ if (ioctl(ipfd, SIOCSIFMUXID, &ifr) < 0) { ioctl(ipfd, I_PUNLINK, ipmuxid); #if defined(INET6) close(i6fd); #endif /* defined(INET6) */ fatal("SIOCSIFMUXID: %m"); } #else /* else if !defined(SOL2) */ if (dlpi_attach(ifd, ifunit) < 0 || dlpi_get_reply(ifd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0) { close(ifd); fatal("Can't attach to ppp%d: %m", ifunit); } ipmuxid = ioctl(ipfd, I_LINK, ifd); close(ifd); if (ipmuxid < 0) fatal("Can't link PPP device to IP: %m"); #endif /* defined(SOL2) */ #if defined(INET6) && defined(SOL2) ip6muxid = ioctl(ip6fd, I_PLINK, i6fd); close(i6fd); if (ip6muxid < 0) { ioctl(ipfd, I_PUNLINK, ipmuxid); fatal("Can't I_PLINK PPP device to IP (2): %m"); } memset(&lifr, 0, sizeof(lifr)); sprintf(lifr.lifr_name, "%s", ifname); lifr.lifr_ip_muxid = ip6muxid; /* * Let IP know of the mux id [see comment for SIOCSIFMUXID above] */ if (ioctl(ip6fd, SIOCSLIFMUXID, &lifr) < 0) { ioctl(ipfd, I_PUNLINK, ipmuxid); ioctl(ip6fd, I_PUNLINK, ip6muxid); fatal("Can't link PPP device to IP (2): %m"); } #endif /* defined(INET6) && defined(SOL2) */ #if !defined(SOL2) /* Set the interface name for the link. */ slprintf(ifr.ifr_name, sizeof(ifr.ifr_name), PPP_DRV_NAME "%d", ifunit); ifr.ifr_metric = ipmuxid; if (strioctl(ipfd, SIOCSIFNAME, (char *)&ifr, sizeof ifr, 0) < 0) fatal("Can't set interface name %s: %m", ifr.ifr_name); #endif /* !defined(SOL2) */ n_pollfds = 0; } /* * sys_cleanup - restore any system state we modified before exiting: * mark the interface down, delete default route and/or proxy arp entry. * This should call die() because it's called from die(). */ void sys_cleanup() { #if defined(SOL2) struct ifreq ifr; #if defined(INET6) struct lifreq lifr; #endif /* defined(INET6) */ #endif /* defined(SOL2) */ #if defined(SOL2) && defined(INET6) if (if6_is_up) sif6down(0); #endif /* defined(SOL2) && defined(INET6) */ if (if_is_up) sifdown(0); if (default_route_gateway) cifdefaultroute(0, default_route_gateway, default_route_gateway); if (proxy_arp_addr) cifproxyarp(0, proxy_arp_addr); #if defined(SOL2) /* * Make sure we ask ip what the muxid, because 'ifconfig modlist' will * unlink and re-link the modules, causing the muxid to change. */ memset(&ifr, 0, sizeof(ifr)); sprintf(ifr.ifr_name, "%s", ifname); if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) { error("SIOCGIFFLAGS: %m"); return; } if (ioctl(ipfd, SIOCGIFMUXID, &ifr) < 0) { error("SIOCGIFMUXID: %m"); return; } ipmuxid = ifr.ifr_ip_muxid; if (ioctl(ipfd, I_PUNLINK, ipmuxid) < 0) { error("Can't I_PUNLINK PPP from IP: %m"); return; } #if defined(INET6) /* * Make sure we ask ip what the muxid, because 'ifconfig modlist' will * unlink and re-link the modules, causing the muxid to change. */ memset(&lifr, 0, sizeof(lifr)); sprintf(lifr.lifr_name, "%s", ifname); if (ioctl(ip6fd, SIOCGLIFFLAGS, &lifr) < 0) { error("SIOCGLIFFLAGS: %m"); return; } if (ioctl(ip6fd, SIOCGLIFMUXID, &lifr) < 0) { error("SIOCGLIFMUXID: %m"); return; } ip6muxid = lifr.lifr_ip_muxid; if (ioctl(ip6fd, I_PUNLINK, ip6muxid) < 0) { error("Can't I_PUNLINK PPP from IP (2): %m"); } #endif /* defined(INET6) */ #endif /* defined(SOL2) */ } /* * sys_close - Clean up in a child process before execing. */ void sys_close() { close(ipfd); #if defined(INET6) && defined(SOL2) close(ip6fd); #endif /* defined(INET6) && defined(SOL2) */ if (pppfd >= 0) close(pppfd); } /* * sys_check_options - check the options that the user specified */ int sys_check_options() { return 1; } #if 0 /* * daemon - Detach us from controlling terminal session. */ int daemon(nochdir, noclose) int nochdir, noclose; { int pid; if ((pid = fork()) < 0) return -1; if (pid != 0) exit(0); /* parent dies */ setsid(); if (!nochdir) chdir("/"); if (!noclose) { fclose(stdin); /* don't need stdin, stdout, stderr */ fclose(stdout); fclose(stderr); } return 0; } #endif /* * ppp_available - check whether the system has any ppp interfaces */ int ppp_available() { struct stat buf; return stat(PPP_DEV_NAME, &buf) >= 0; } /* * any_compressions - see if compression is enabled or not * * In the STREAMS implementation of kernel-portion pppd, * the comp STREAMS module performs the ACFC, PFC, as well * CCP and VJ compressions. However, if the user has explicitly * declare to not enable them from the command line, there is * no point of having the comp module be pushed on the stream. */ static int any_compressions() { if ((!lcp_wantoptions[0].neg_accompression) && (!lcp_wantoptions[0].neg_pcompression) && (!ccp_protent.enabled_flag) && (!ipcp_wantoptions[0].neg_vj)) { return 0; } return 1; } /* * tty_establish_ppp - Turn the serial port into a ppp interface. */ int tty_establish_ppp(fd) int fd; { int i; /* Pop any existing modules off the tty stream. */ for (i = 0;; ++i) if (ioctl(fd, I_LOOK, tty_modules[i]) < 0 || strcmp(tty_modules[i], "ptem") == 0 || ioctl(fd, I_POP, 0) < 0) break; tty_nmodules = i; /* Push the async hdlc module and the compressor module. */ tty_npushed = 0; if(!sync_serial) { if (ioctl(fd, I_PUSH, AHDLC_MOD_NAME) < 0) { error("Couldn't push PPP Async HDLC module: %m"); return -1; } ++tty_npushed; } if (kdebugflag & 4) { i = PPPDBG_LOG + PPPDBG_AHDLC; strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0); } /* * There's no need to push comp module if we don't intend * to compress anything */ if (any_compressions()) { if (ioctl(fd, I_PUSH, COMP_MOD_NAME) < 0) error("Couldn't push PPP compression module: %m"); else ++tty_npushed; } if (kdebugflag & 2) { i = PPPDBG_LOG; if (any_compressions()) i += PPPDBG_COMP; strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0); } /* Link the serial port under the PPP multiplexor. */ if ((fdmuxid = ioctl(pppfd, I_LINK, fd)) < 0) { error("Can't link tty to PPP mux: %m"); return -1; } return pppfd; } /* * tty_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 tty_disestablish_ppp(fd) int fd; { int i; if (fdmuxid >= 0) { if (ioctl(pppfd, I_UNLINK, fdmuxid) < 0) { if (!hungup) error("Can't unlink tty from PPP mux: %m"); } fdmuxid = -1; if (!hungup) { while (tty_npushed > 0 && ioctl(fd, I_POP, 0) >= 0) --tty_npushed; for (i = tty_nmodules - 1; i >= 0; --i) if (ioctl(fd, I_PUSH, tty_modules[i]) < 0) error("Couldn't restore tty module %s: %m", tty_modules[i]); } if (hungup && default_device && tty_sid > 0) { /* * If we have received a hangup, we need to send a SIGHUP * to the terminal's controlling process. The reason is * that the original stream head for the terminal hasn't * seen the M_HANGUP message (it went up through the ppp * driver to the stream head for our fd to /dev/ppp). */ kill(tty_sid, SIGHUP); } } } /* * Check whether the link seems not to be 8-bit clean. */ void clean_check() { int x; char *s; if (strioctl(pppfd, PPPIO_GCLEAN, &x, 0, sizeof(x)) < 0) return; s = NULL; switch (~x) { case RCV_B7_0: s = "bit 7 set to 1"; break; case RCV_B7_1: s = "bit 7 set to 0"; break; case RCV_EVNP: s = "odd parity"; break; case RCV_ODDP: s = "even parity"; break; } if (s != NULL) { warn("Serial link is not 8-bit clean:"); warn("All received characters had %s", s); } } /* * 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 B76800 { 76800, B76800 }, #endif #ifdef B115200 { 115200, B115200 }, #endif #ifdef B153600 { 153600, B153600 }, #endif #ifdef B230400 { 230400, B230400 }, #endif #ifdef B307200 { 307200, B307200 }, #endif #ifdef B460800 { 460800, B460800 }, #endif { 0, 0 } }; /* * Translate from bits/second to a speed_t. */ static 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; warn("speed %d not supported", bps); return 0; } /* * Translate from a speed_t to bits/second. */ static 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; } /* * 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. */ void set_up_tty(fd, local) int fd, local; { int speed; struct termios tios; #if !defined (CRTSCTS) struct termiox tiox; #endif if (!sync_serial && tcgetattr(fd, &tios) < 0) fatal("tcgetattr: %m"); #ifndef CRTSCTS termiox_ok = 1; if (!sync_serial && ioctl (fd, TCGETX, &tiox) < 0) { termiox_ok = 0; if (errno != ENOTTY) error("TCGETX: %m"); } #endif if (!restore_term) { inittermios = tios; #ifndef CRTSCTS inittermiox = tiox; #endif if (!sync_serial) ioctl(fd, TIOCGWINSZ, &wsinfo); } tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL); #ifdef CRTSCTS if (crtscts > 0) tios.c_cflag |= CRTSCTS; else if (crtscts < 0) tios.c_cflag &= ~CRTSCTS; #else if (crtscts != 0 && !termiox_ok) { error("Can't set RTS/CTS flow control"); } else if (crtscts > 0) { tiox.x_hflag |= RTSXOFF|CTSXON; } else if (crtscts < 0) { tiox.x_hflag &= ~(RTSXOFF|CTSXON); } #endif 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 |= IXON | 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); /* * We can't proceed if the serial port speed is 0, * since that implies that the serial port is disabled. */ if ((speed == B0) && !sync_serial) fatal("Baud rate for %s is 0; need explicit baud rate", devnam); } if (!sync_serial && tcsetattr(fd, TCSAFLUSH, &tios) < 0) fatal("tcsetattr: %m"); #ifndef CRTSCTS if (!sync_serial && termiox_ok && ioctl (fd, TCSETXF, &tiox) < 0){ error("TCSETXF: %m"); } #endif baud_rate = inspeed = baud_rate_of(speed); if (!sync_serial) restore_term = 1; } /* * restore_tty - restore the terminal to the saved settings. */ void restore_tty(fd) int fd; { if (restore_term) { if (!default_device) { /* * Turn off echoing, because otherwise we can get into * a loop with the tty and the modem echoing to each other. * We presume we are the sole user of this tty device, so * when we close it, it will revert to its defaults anyway. */ inittermios.c_lflag &= ~(ECHO | ECHONL); } if (!sync_serial && tcsetattr(fd, TCSAFLUSH, &inittermios) < 0) if (!hungup && errno != ENXIO) warn("tcsetattr: %m"); #ifndef CRTSCTS if (!sync_serial && ioctl (fd, TCSETXF, &inittermiox) < 0){ if (!hungup && errno != ENXIO) error("TCSETXF: %m"); } #endif if (!sync_serial) ioctl(fd, TIOCSWINSZ, &wsinfo); restore_term = 0; } } /* * setdtr - control the DTR line on the serial port. * This is called from die(), so it shouldn't call die(). */ void setdtr(fd, on) int fd, on; { int modembits = TIOCM_DTR; ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits); } /* * open_loopback - open the device we use for getting packets * in demand mode. Under Solaris 2, we use our existing fd * to the ppp driver. */ int open_ppp_loopback() { return pppfd; } /* * output - Output PPP packet. */ void output(unit, p, len) int unit; u_char *p; int len; { struct strbuf data; int retries; struct pollfd pfd; dump_packet("sent", p, len); if (snoop_send_hook) snoop_send_hook(p, len); data.len = len; data.buf = (caddr_t) p; retries = 4; while (putmsg(pppfd, NULL, &data, 0) < 0) { if (--retries < 0 || (errno != EWOULDBLOCK && errno != EAGAIN)) { if (errno != ENXIO) error("Couldn't send packet: %m"); break; } pfd.fd = pppfd; pfd.events = POLLOUT; poll(&pfd, 1, 250); /* wait for up to 0.25 seconds */ } } /* * wait_input - wait until there is data available, * for the length of time specified by *timo (indefinite * if timo is NULL). */ void wait_input(timo) struct timeval *timo; { int t; t = timo == NULL? -1: timo->tv_sec * 1000 + timo->tv_usec / 1000; if (poll(pollfds, n_pollfds, t) < 0 && errno != EINTR) fatal("poll: %m"); } /* * add_fd - add an fd to the set that wait_input waits for. */ void add_fd(fd) int fd; { int n; for (n = 0; n < n_pollfds; ++n) if (pollfds[n].fd == fd) return; if (n_pollfds < MAX_POLLFDS) { pollfds[n_pollfds].fd = fd; pollfds[n_pollfds].events = POLLIN | POLLPRI | POLLHUP; ++n_pollfds; } else error("Too many inputs!"); } /* * remove_fd - remove an fd from the set that wait_input waits for. */ void remove_fd(fd) int fd; { int n; for (n = 0; n < n_pollfds; ++n) { if (pollfds[n].fd == fd) { while (++n < n_pollfds) pollfds[n-1] = pollfds[n]; --n_pollfds; break; } } } #if 0 /* * wait_loop_output - wait until there is data available on the * loopback, for the length of time specified by *timo (indefinite * if timo is NULL). */ void wait_loop_output(timo) struct timeval *timo; { wait_input(timo); } /* * wait_time - wait for a given length of time or until a * signal is received. */ void wait_time(timo) struct timeval *timo; { int n; n = select(0, NULL, NULL, NULL, timo); if (n < 0 && errno != EINTR) fatal("select: %m"); } #endif /* * read_packet - get a PPP packet from the serial device. */ int read_packet(buf) u_char *buf; { struct strbuf ctrl, data; int flags, len; unsigned char ctrlbuf[sizeof(union DL_primitives) + 64]; for (;;) { data.maxlen = PPP_MRU + PPP_HDRLEN; data.buf = (caddr_t) buf; ctrl.maxlen = sizeof(ctrlbuf); ctrl.buf = (caddr_t) ctrlbuf; flags = 0; len = getmsg(pppfd, &ctrl, &data, &flags); if (len < 0) { if (errno == EAGAIN || errno == EINTR) return -1; fatal("Error reading packet: %m"); } if (ctrl.len <= 0) return data.len; /* * Got a M_PROTO or M_PCPROTO message. Interpret it * as a DLPI primitive?? */ if (debug) dbglog("got dlpi prim 0x%x, len=%d", ((union DL_primitives *)ctrlbuf)->dl_primitive, ctrl.len); } } /* * get_loop_output - get outgoing packets from the ppp device, * and detect when we want to bring the real link up. * Return value is 1 if we need to bring up the link, 0 otherwise. */ int get_loop_output() { int len; int rv = 0; while ((len = read_packet(inpacket_buf)) > 0) { if (loop_frame(inpacket_buf, len)) rv = 1; } return rv; } /* * netif_set_mtu - set the MTU on the PPP network interface. */ void netif_set_mtu(unit, mtu) int unit, mtu; { struct ifreq ifr; #if defined(INET6) && defined(SOL2) struct lifreq lifr; int fd; #endif /* defined(INET6) && defined(SOL2) */ memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); ifr.ifr_metric = link_mtu; if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) { error("Couldn't set IP MTU (%s): %m", ifr.ifr_name); } #if defined(INET6) && defined(SOL2) fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd < 0) error("Couldn't open IPv6 socket: %m"); memset(&lifr, 0, sizeof(lifr)); strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); lifr.lifr_mtu = link_mtu; if (ioctl(fd, SIOCSLIFMTU, &lifr) < 0) { close(fd); error("Couldn't set IPv6 MTU (%s): %m", ifr.ifr_name); } close(fd); #endif /* defined(INET6) && defined(SOL2) */ } /* * tty_send_config - configure the transmit characteristics of * the ppp interface. */ void tty_send_config(mtu, asyncmap, pcomp, accomp) int mtu; u_int32_t asyncmap; int pcomp, accomp; { int cf[2]; link_mtu = mtu; if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) { if (hungup && errno == ENXIO) { ++error_count; return; } error("Couldn't set MTU: %m"); } if (fdmuxid >= 0) { if (!sync_serial) { if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) error("Couldn't set transmit ACCM: %m"); } cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0); cf[1] = COMP_PROT | COMP_AC; if (any_compressions() && strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) error("Couldn't set prot/AC compression: %m"); } } /* * tty_set_xaccm - set the extended transmit ACCM for the interface. */ void tty_set_xaccm(accm) ext_accm accm; { if (sync_serial) return; if (fdmuxid >= 0 && strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) { if (!hungup || errno != ENXIO) warn("Couldn't set extended ACCM: %m"); } } /* * tty_recv_config - configure the receive-side characteristics of * the ppp interface. */ void tty_recv_config(mru, asyncmap, pcomp, accomp) int mru; u_int32_t asyncmap; int pcomp, accomp; { int cf[2]; link_mru = mru; if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) { if (hungup && errno == ENXIO) { ++error_count; return; } error("Couldn't set MRU: %m"); } if (fdmuxid >= 0) { if (!sync_serial) { if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) error("Couldn't set receive ACCM: %m"); } cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0); cf[1] = DECOMP_PROT | DECOMP_AC; if (any_compressions() && strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) error("Couldn't set prot/AC decompression: %m"); } } /* * ccp_test - ask kernel whether a given compression method * is acceptable for use. */ int ccp_test(unit, opt_ptr, opt_len, for_transmit) int unit, opt_len, for_transmit; u_char *opt_ptr; { if (strioctl(pppfd, (for_transmit? PPPIO_XCOMP: PPPIO_RCOMP), opt_ptr, opt_len, 0) >= 0) return 1; return (errno == ENOSR)? 0: -1; } /* * ccp_flags_set - inform kernel about the current state of CCP. */ void ccp_flags_set(unit, isopen, isup) int unit, isopen, isup; { int cf[2]; cf[0] = (isopen? CCP_ISOPEN: 0) + (isup? CCP_ISUP: 0); cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR; if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) { if (!hungup || errno != ENXIO) error("Couldn't set kernel CCP state: %m"); } } /* * get_idle_time - return how long the link has been idle. */ int get_idle_time(u, ip) int u; struct ppp_idle *ip; { return strioctl(pppfd, PPPIO_GIDLE, ip, 0, sizeof(struct ppp_idle)) >= 0; } /* * get_ppp_stats - return statistics for the link. */ int get_ppp_stats(u, stats) int u; struct pppd_stats *stats; { struct ppp_stats s; if (!sync_serial && strioctl(pppfd, PPPIO_GETSTAT, &s, 0, sizeof(s)) < 0) { error("Couldn't get link statistics: %m"); return 0; } stats->bytes_in = s.p.ppp_ibytes; stats->bytes_out = s.p.ppp_obytes; stats->pkts_in = s.p.ppp_ipackets; stats->pkts_out = s.p.ppp_opackets; return 1; } #if 0 /* * set_filters - transfer the pass and active filters to the kernel. */ int set_filters(pass, active) struct bpf_program *pass, *active; { int ret = 1; if (pass->bf_len > 0) { if (strioctl(pppfd, PPPIO_PASSFILT, pass, sizeof(struct bpf_program), 0) < 0) { error("Couldn't set pass-filter in kernel: %m"); ret = 0; } } if (active->bf_len > 0) { if (strioctl(pppfd, PPPIO_ACTIVEFILT, active, sizeof(struct bpf_program), 0) < 0) { error("Couldn't set active-filter in kernel: %m"); ret = 0; } } return ret; } #endif /* * ccp_fatal_error - returns 1 if decompression was disabled as a * result of an error detected after decompression of a packet, * 0 otherwise. This is necessary because of patent nonsense. */ int ccp_fatal_error(unit) int unit; { int cf[2]; cf[0] = cf[1] = 0; if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) { if (errno != ENXIO && errno != EINVAL) error("Couldn't get compression flags: %m"); return 0; } return cf[0] & CCP_FATALERROR; } /* * sifvjcomp - config tcp header compression */ int sifvjcomp(u, vjcomp, xcidcomp, xmaxcid) int u, vjcomp, xcidcomp, xmaxcid; { int cf[2]; char maxcid[2]; if (vjcomp) { maxcid[0] = xcidcomp; maxcid[1] = 15; /* XXX should be rmaxcid */ if (strioctl(pppfd, PPPIO_VJINIT, maxcid, sizeof(maxcid), 0) < 0) { error("Couldn't initialize VJ compression: %m"); } } cf[0] = (vjcomp? COMP_VJC + DECOMP_VJC: 0) /* XXX this is wrong */ + (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0); cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID; if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) { if (vjcomp) error("Couldn't enable VJ compression: %m"); } return 1; } /* * sifup - Config the interface up and enable IP packets to pass. */ int sifup(u) int u; { struct ifreq ifr; strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) { error("Couldn't mark interface up (get): %m"); return 0; } ifr.ifr_flags |= IFF_UP; if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) { error("Couldn't mark interface up (set): %m"); return 0; } if_is_up = 1; return 1; } /* * sifdown - Config the interface down and disable IP. */ int sifdown(u) int u; { struct ifreq ifr; if (ipmuxid < 0) return 1; strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) { error("Couldn't mark interface down (get): %m"); return 0; } ifr.ifr_flags &= ~IFF_UP; if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) { error("Couldn't mark interface down (set): %m"); return 0; } if_is_up = 0; return 1; } /* * sifnpmode - Set the mode for handling packets for a given NP. */ int sifnpmode(u, proto, mode) int u; int proto; enum NPmode mode; { int npi[2]; npi[0] = proto; npi[1] = (int) mode; if (strioctl(pppfd, PPPIO_NPMODE, &npi, 2 * sizeof(int), 0) < 0) { error("ioctl(set NP %d mode to %d): %m", proto, mode); return 0; } return 1; } #if defined(SOL2) && defined(INET6) /* * sif6up - Config the IPv6 interface up and enable IPv6 packets to pass. */ int sif6up(u) int u; { struct lifreq lifr; int fd; fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd < 0) { return 0; } memset(&lifr, 0, sizeof(lifr)); strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) { close(fd); return 0; } lifr.lifr_flags |= IFF_UP; strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); if (ioctl(fd, SIOCSLIFFLAGS, &lifr) < 0) { close(fd); return 0; } if6_is_up = 1; close(fd); return 1; } /* * sifdown - Config the IPv6 interface down and disable IPv6. */ int sif6down(u) int u; { struct lifreq lifr; int fd; fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd < 0) return 0; memset(&lifr, 0, sizeof(lifr)); strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) { close(fd); return 0; } lifr.lifr_flags &= ~IFF_UP; strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) { close(fd); return 0; } if6_is_up = 0; close(fd); return 1; } /* * sif6addr - Config the interface with an IPv6 link-local address */ int sif6addr(u, o, h) int u; eui64_t o, h; { struct lifreq lifr; struct sockaddr_storage laddr; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&laddr; int fd; fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd < 0) return 0; memset(&lifr, 0, sizeof(lifr)); strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); /* * Do this because /dev/ppp responds to DL_PHYS_ADDR_REQ with * zero values, hence the interface token came to be zero too, * and without this, in.ndpd will complain */ IN6_LLTOKEN_FROM_EUI64(lifr, sin6, o); if (ioctl(fd, SIOCSLIFTOKEN, &lifr) < 0) { close(fd); return 0; } /* * Set the interface address and destination address */ IN6_LLADDR_FROM_EUI64(lifr, sin6, o); if (ioctl(fd, SIOCSLIFADDR, &lifr) < 0) { close(fd); return 0; } memset(&lifr, 0, sizeof(lifr)); strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); IN6_LLADDR_FROM_EUI64(lifr, sin6, h); if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) < 0) { close(fd); return 0; } return 1; } /* * cif6addr - Remove the IPv6 address from interface */ int cif6addr(u, o, h) int u; eui64_t o, h; { return 1; } #endif /* defined(SOL2) && defined(INET6) */ #define INET_ADDR(x) (((struct sockaddr_in *) &(x))->sin_addr.s_addr) /* * sifaddr - Config the interface IP addresses and netmask. */ int sifaddr(u, o, h, m) int u; u_int32_t o, h, m; { struct ifreq ifr; int ret = 1; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); ifr.ifr_addr.sa_family = AF_INET; INET_ADDR(ifr.ifr_addr) = m; if (ioctl(ipfd, SIOCSIFNETMASK, &ifr) < 0) { error("Couldn't set IP netmask: %m"); ret = 0; } ifr.ifr_addr.sa_family = AF_INET; INET_ADDR(ifr.ifr_addr) = o; if (ioctl(ipfd, SIOCSIFADDR, &ifr) < 0) { error("Couldn't set local IP address: %m"); ret = 0; } /* * On some systems, we have to explicitly set the point-to-point * flag bit before we can set a destination address. */ if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) >= 0 && (ifr.ifr_flags & IFF_POINTOPOINT) == 0) { ifr.ifr_flags |= IFF_POINTOPOINT; if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) { error("Couldn't mark interface pt-to-pt: %m"); ret = 0; } } ifr.ifr_dstaddr.sa_family = AF_INET; INET_ADDR(ifr.ifr_dstaddr) = h; if (ioctl(ipfd, SIOCSIFDSTADDR, &ifr) < 0) { error("Couldn't set remote IP address: %m"); ret = 0; } remote_addr = h; return ret; } /* * cifaddr - Clear the interface IP addresses, and delete routes * through the interface if possible. */ int cifaddr(u, o, h) int u; u_int32_t o, h; { #if defined(__USLC__) /* was: #if 0 */ cifroute(unit, ouraddr, hisaddr); if (ipmuxid >= 0) { notice("Removing ppp interface unit"); if (ioctl(ipfd, I_UNLINK, ipmuxid) < 0) { error("Can't remove ppp interface unit: %m"); return 0; } ipmuxid = -1; } #endif remote_addr = 0; return 1; } /* * sifdefaultroute - assign a default route through the address given. */ int sifdefaultroute(u, l, g) int u; u_int32_t l, g; { struct rtentry rt; #if defined(__USLC__) g = l; /* use the local address as gateway */ #endif memset(&rt, 0, sizeof(rt)); rt.rt_dst.sa_family = AF_INET; INET_ADDR(rt.rt_dst) = 0; rt.rt_gateway.sa_family = AF_INET; INET_ADDR(rt.rt_gateway) = g; rt.rt_flags = RTF_GATEWAY; if (ioctl(ipfd, SIOCADDRT, &rt) < 0) { error("Can't add default route: %m"); return 0; } default_route_gateway = g; return 1; } /* * cifdefaultroute - delete a default route through the address given. */ int cifdefaultroute(u, l, g) int u; u_int32_t l, g; { struct rtentry rt; #if defined(__USLC__) g = l; /* use the local address as gateway */ #endif memset(&rt, 0, sizeof(rt)); rt.rt_dst.sa_family = AF_INET; INET_ADDR(rt.rt_dst) = 0; rt.rt_gateway.sa_family = AF_INET; INET_ADDR(rt.rt_gateway) = g; rt.rt_flags = RTF_GATEWAY; if (ioctl(ipfd, SIOCDELRT, &rt) < 0) { error("Can't delete default route: %m"); return 0; } default_route_gateway = 0; return 1; } /* * sifproxyarp - Make a proxy ARP entry for the peer. */ int sifproxyarp(unit, hisaddr) int unit; u_int32_t hisaddr; { struct arpreq arpreq; memset(&arpreq, 0, sizeof(arpreq)); if (!get_ether_addr(hisaddr, &arpreq.arp_ha)) return 0; arpreq.arp_pa.sa_family = AF_INET; INET_ADDR(arpreq.arp_pa) = hisaddr; arpreq.arp_flags = ATF_PERM | ATF_PUBL; if (ioctl(ipfd, SIOCSARP, (caddr_t) &arpreq) < 0) { error("Couldn't set proxy ARP entry: %m"); return 0; } proxy_arp_addr = hisaddr; return 1; } /* * cifproxyarp - Delete the proxy ARP entry for the peer. */ int cifproxyarp(unit, hisaddr) int unit; u_int32_t hisaddr; { struct arpreq arpreq; memset(&arpreq, 0, sizeof(arpreq)); arpreq.arp_pa.sa_family = AF_INET; INET_ADDR(arpreq.arp_pa) = hisaddr; if (ioctl(ipfd, SIOCDARP, (caddr_t)&arpreq) < 0) { error("Couldn't delete proxy ARP entry: %m"); return 0; } proxy_arp_addr = 0; return 1; } /* * get_ether_addr - get the hardware address of an interface on the * the same subnet as ipaddr. */ #define MAX_IFS 32 static int get_ether_addr(ipaddr, hwaddr) u_int32_t ipaddr; struct sockaddr *hwaddr; { struct ifreq *ifr, *ifend, ifreq; int nif; struct ifconf ifc; u_int32_t ina, mask; /* * Scan through the system's network interfaces. */ #ifdef SIOCGIFNUM if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0) #endif nif = MAX_IFS; ifc.ifc_len = nif * sizeof(struct ifreq); ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len); if (ifc.ifc_buf == 0) return 0; if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) { warn("Couldn't get system interface list: %m"); free(ifc.ifc_buf); return 0; } ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) { if (ifr->ifr_addr.sa_family != AF_INET) continue; /* * Check that the interface is up, and not point-to-point or loopback. */ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); if (ioctl(ipfd, 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(ipfd, SIOCGIFNETMASK, &ifreq) < 0) continue; ina = INET_ADDR(ifr->ifr_addr); mask = INET_ADDR(ifreq.ifr_addr); if ((ipaddr & mask) == (ina & mask)) break; } if (ifr >= ifend) { warn("No suitable interface found for proxy ARP"); free(ifc.ifc_buf); return 0; } info("found interface %s for proxy ARP", ifr->ifr_name); if (!get_hw_addr(ifr->ifr_name, ina, hwaddr)) { error("Couldn't get hardware address for %s", ifr->ifr_name); free(ifc.ifc_buf); return 0; } free(ifc.ifc_buf); return 1; } /* * get_hw_addr_dlpi - obtain the hardware address using DLPI */ static int get_hw_addr_dlpi(name, hwaddr) char *name; struct sockaddr *hwaddr; { char *q; int unit, iffd, adrlen; unsigned char *adrp; char ifdev[24]; struct { union DL_primitives prim; char space[64]; } reply; /* * We have to open the device and ask it for its hardware address. * First split apart the device name and unit. */ slprintf(ifdev, sizeof(ifdev), "/dev/%s", name); for (q = ifdev + strlen(ifdev); --q >= ifdev; ) if (!isdigit(*q)) break; unit = atoi(q+1); q[1] = 0; /* * Open the device and do a DLPI attach and phys_addr_req. */ iffd = open(ifdev, O_RDWR); if (iffd < 0) { error("Can't open %s: %m", ifdev); return 0; } if (dlpi_attach(iffd, unit) < 0 || dlpi_get_reply(iffd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0 || dlpi_info_req(iffd) < 0 || dlpi_get_reply(iffd, &reply.prim, DL_INFO_ACK, sizeof(reply)) < 0) { close(iffd); return 0; } adrlen = reply.prim.info_ack.dl_addr_length; adrp = (unsigned char *)&reply + reply.prim.info_ack.dl_addr_offset; #if DL_CURRENT_VERSION >= 2 if (reply.prim.info_ack.dl_sap_length < 0) adrlen += reply.prim.info_ack.dl_sap_length; else adrp += reply.prim.info_ack.dl_sap_length; #endif hwaddr->sa_family = AF_UNSPEC; memcpy(hwaddr->sa_data, adrp, adrlen); return 1; } /* * get_hw_addr - obtain the hardware address for a named interface. */ static int get_hw_addr(name, ina, hwaddr) char *name; u_int32_t ina; struct sockaddr *hwaddr; { /* New way - get the address by doing an arp request. */ int s; struct arpreq req; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) return 0; memset(&req, 0, sizeof(req)); req.arp_pa.sa_family = AF_INET; INET_ADDR(req.arp_pa) = ina; if (ioctl(s, SIOCGARP, &req) < 0) { error("Couldn't get ARP entry for %s: %m", ip_ntoa(ina)); return 0; } *hwaddr = req.arp_ha; hwaddr->sa_family = AF_UNSPEC; return 1; } static int dlpi_attach(fd, ppa) int fd, ppa; { dl_attach_req_t req; struct strbuf buf; req.dl_primitive = DL_ATTACH_REQ; req.dl_ppa = ppa; buf.len = sizeof(req); buf.buf = (void *) &req; return putmsg(fd, &buf, NULL, RS_HIPRI); } static int dlpi_info_req(fd) int fd; { dl_info_req_t req; struct strbuf buf; req.dl_primitive = DL_INFO_REQ; buf.len = sizeof(req); buf.buf = (void *) &req; return putmsg(fd, &buf, NULL, RS_HIPRI); } static int dlpi_get_reply(fd, reply, expected_prim, maxlen) union DL_primitives *reply; int fd, expected_prim, maxlen; { struct strbuf buf; int flags, n; struct pollfd pfd; /* * Use poll to wait for a message with a timeout. */ pfd.fd = fd; pfd.events = POLLIN | POLLPRI; do { n = poll(&pfd, 1, 1000); } while (n == -1 && errno == EINTR); if (n <= 0) return -1; /* * Get the reply. */ buf.maxlen = maxlen; buf.buf = (void *) reply; flags = 0; if (getmsg(fd, &buf, NULL, &flags) < 0) return -1; if (buf.len < sizeof(ulong)) { if (debug) dbglog("dlpi response short (len=%d)\n", buf.len); return -1; } if (reply->dl_primitive == expected_prim) return 0; if (debug) { if (reply->dl_primitive == DL_ERROR_ACK) { dbglog("dlpi error %d (unix errno %d) for prim %x\n", reply->error_ack.dl_errno, reply->error_ack.dl_unix_errno, reply->error_ack.dl_error_primitive); } else { dbglog("dlpi unexpected response prim %x\n", reply->dl_primitive); } } return -1; } /* * Return user specified netmask, modified by any mask we might determine * for address `addr' (in network byte order). * Here we scan through the system's list of interfaces, looking for * any non-point-to-point interfaces which might appear to be on the same * network as `addr'. If we find any, we OR in their netmask to the * user-specified netmask. */ u_int32_t GetMask(addr) u_int32_t addr; { u_int32_t mask, nmask, ina; struct ifreq *ifr, *ifend, ifreq; int nif; struct ifconf ifc; addr = ntohl(addr); if (IN_CLASSA(addr)) /* determine network mask for address class */ nmask = IN_CLASSA_NET; else if (IN_CLASSB(addr)) nmask = IN_CLASSB_NET; else nmask = IN_CLASSC_NET; /* class D nets are disallowed by bad_ip_adrs */ mask = netmask | htonl(nmask); /* * Scan through the system's network interfaces. */ #ifdef SIOCGIFNUM if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0) #endif nif = MAX_IFS; ifc.ifc_len = nif * sizeof(struct ifreq); ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len); if (ifc.ifc_buf == 0) return mask; if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) { warn("Couldn't get system interface list: %m"); free(ifc.ifc_buf); return mask; } ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) { /* * Check the interface's internet address. */ if (ifr->ifr_addr.sa_family != AF_INET) continue; ina = INET_ADDR(ifr->ifr_addr); if ((ntohl(ina) & nmask) != (addr & nmask)) continue; /* * Check that the interface is up, and not point-to-point or loopback. */ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); if (ioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0) continue; if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK)) != IFF_UP) continue; /* * Get its netmask and OR it into our mask. */ if (ioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0) continue; mask |= INET_ADDR(ifreq.ifr_addr); } free(ifc.ifc_buf); return mask; } /* * logwtmp - write an accounting record to the /var/adm/wtmp file. */ void logwtmp(line, name, host) const char *line, *name, *host; { static struct utmpx utmpx; if (name[0] != 0) { /* logging in */ strncpy(utmpx.ut_user, name, sizeof(utmpx.ut_user)); strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line)); strncpy(utmpx.ut_host, host, sizeof(utmpx.ut_host)); if (*host != '\0') { utmpx.ut_syslen = strlen(host) + 1; if (utmpx.ut_syslen > sizeof(utmpx.ut_host)) utmpx.ut_syslen = sizeof(utmpx.ut_host); } utmpx.ut_pid = getpid(); utmpx.ut_type = USER_PROCESS; } else { utmpx.ut_type = DEAD_PROCESS; } gettimeofday(&utmpx.ut_tv, NULL); updwtmpx("/var/adm/wtmpx", &utmpx); } /* * get_host_seed - return the serial number of this machine. */ int get_host_seed() { char buf[32]; if (sysinfo(SI_HW_SERIAL, buf, sizeof(buf)) < 0) { error("sysinfo: %m"); return 0; } return (int) strtoul(buf, NULL, 16); } static int strioctl(fd, cmd, ptr, ilen, olen) int fd, cmd, ilen, olen; void *ptr; { struct strioctl str; str.ic_cmd = cmd; str.ic_timout = 0; str.ic_len = ilen; str.ic_dp = ptr; if (ioctl(fd, I_STR, &str) == -1) return -1; if (str.ic_len != olen) dbglog("strioctl: expected %d bytes, got %d for cmd %x\n", olen, str.ic_len, cmd); return 0; } #if 0 /* * lock - create a lock file for the named lock device */ #define LOCK_PREFIX "/var/spool/locks/LK." static char lock_file[40]; /* name of lock file created */ int lock(dev) char *dev; { int n, fd, pid; struct stat sbuf; char ascii_pid[12]; if (stat(dev, &sbuf) < 0) { error("Can't get device number for %s: %m", dev); return -1; } if ((sbuf.st_mode & S_IFMT) != S_IFCHR) { error("Can't lock %s: not a character device", dev); return -1; } slprintf(lock_file, sizeof(lock_file), "%s%03d.%03d.%03d", LOCK_PREFIX, major(sbuf.st_dev), major(sbuf.st_rdev), minor(sbuf.st_rdev)); 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 */ n = read(fd, ascii_pid, 11); if (n <= 0) { error("Can't read pid from lock file %s", lock_file); close(fd); } else { ascii_pid[n] = 0; pid = atoi(ascii_pid); if (pid > 0 && kill(pid, 0) == -1 && errno == ESRCH) { /* pid no longer exists - remove the lock file */ if (unlink(lock_file) == 0) { close(fd); notice("Removed stale lock on %s (pid %d)", dev, pid); continue; } else warn("Couldn't remove stale lock on %s", dev); } else notice("Device %s is locked by pid %d", dev, pid); } close(fd); } else error("Can't create lock file %s: %m", lock_file); lock_file[0] = 0; return -1; } slprintf(ascii_pid, sizeof(ascii_pid), "%10d\n", getpid()); write(fd, ascii_pid, 11); close(fd); return 1; } /* * unlock - remove our lockfile */ void unlock() { if (lock_file[0]) { unlink(lock_file); lock_file[0] = 0; } } #endif /* * cifroute - delete a route through the addresses given. */ int cifroute(u, our, his) int u; u_int32_t our, his; { struct rtentry rt; memset(&rt, 0, sizeof(rt)); rt.rt_dst.sa_family = AF_INET; INET_ADDR(rt.rt_dst) = his; rt.rt_gateway.sa_family = AF_INET; INET_ADDR(rt.rt_gateway) = our; rt.rt_flags = RTF_HOST; if (ioctl(ipfd, SIOCDELRT, &rt) < 0) { error("Can't delete route: %m"); return 0; } return 1; } /* * have_route_to - determine if the system has a route to the specified * IP address. Returns 0 if not, 1 if so, -1 if we can't tell. * `addr' is in network byte order. * For demand mode to work properly, we have to ignore routes * through our own interface. */ #ifndef T_CURRENT /* needed for Solaris 2.5 */ #define T_CURRENT MI_T_CURRENT #endif int have_route_to(addr) u_int32_t addr; { #ifdef SOL2 int fd, r, flags, i; struct { struct T_optmgmt_req req; struct opthdr hdr; } req; union { struct T_optmgmt_ack ack; unsigned char space[64]; } ack; struct opthdr *rh; struct strbuf cbuf, dbuf; int nroutes; mib2_ipRouteEntry_t routes[8]; mib2_ipRouteEntry_t *rp; fd = open(mux_dev_name, O_RDWR); if (fd < 0) { warn("have_route_to: couldn't open %s: %m", mux_dev_name); return -1; } req.req.PRIM_type = T_OPTMGMT_REQ; req.req.OPT_offset = (char *) &req.hdr - (char *) &req; req.req.OPT_length = sizeof(req.hdr); req.req.MGMT_flags = T_CURRENT; req.hdr.level = MIB2_IP; req.hdr.name = 0; req.hdr.len = 0; cbuf.buf = (char *) &req; cbuf.len = sizeof(req); if (putmsg(fd, &cbuf, NULL, 0) == -1) { warn("have_route_to: putmsg: %m"); close(fd); return -1; } for (;;) { cbuf.buf = (char *) &ack; cbuf.maxlen = sizeof(ack); dbuf.buf = (char *) routes; dbuf.maxlen = sizeof(routes); flags = 0; r = getmsg(fd, &cbuf, &dbuf, &flags); if (r == -1) { warn("have_route_to: getmsg: %m"); close(fd); return -1; } if (cbuf.len < sizeof(struct T_optmgmt_ack) || ack.ack.PRIM_type != T_OPTMGMT_ACK || ack.ack.MGMT_flags != T_SUCCESS || ack.ack.OPT_length < sizeof(struct opthdr)) { dbglog("have_route_to: bad message len=%d prim=%d", cbuf.len, ack.ack.PRIM_type); close(fd); return -1; } rh = (struct opthdr *) ((char *)&ack + ack.ack.OPT_offset); if (rh->level == 0 && rh->name == 0) break; if (rh->level != MIB2_IP || rh->name != MIB2_IP_21) { while (r == MOREDATA) r = getmsg(fd, NULL, &dbuf, &flags); continue; } for (;;) { nroutes = dbuf.len / sizeof(mib2_ipRouteEntry_t); for (rp = routes, i = 0; i < nroutes; ++i, ++rp) { if (rp->ipRouteMask != ~0) { dbglog("have_route_to: dest=%x gw=%x mask=%x\n", rp->ipRouteDest, rp->ipRouteNextHop, rp->ipRouteMask); if (((addr ^ rp->ipRouteDest) & rp->ipRouteMask) == 0 && rp->ipRouteNextHop != remote_addr) return 1; } } if (r == 0) break; r = getmsg(fd, NULL, &dbuf, &flags); } } close(fd); return 0; #else return -1; #endif /* SOL2 */ } /* * get_pty - get a pty master/slave pair and chown the slave side to * the uid given. Assumes slave_name points to MAXPATHLEN bytes of space. */ int get_pty(master_fdp, slave_fdp, slave_name, uid) int *master_fdp; int *slave_fdp; char *slave_name; int uid; { int mfd, sfd; char *pty_name; mfd = open("/dev/ptmx", O_RDWR); if (mfd < 0) { error("Couldn't open pty master: %m"); return 0; } pty_name = ptsname(mfd); if (pty_name == NULL) { error("Couldn't get name of pty slave"); close(mfd); return 0; } if (chown(pty_name, uid, -1) < 0) warn("Couldn't change owner of pty slave: %m"); if (chmod(pty_name, S_IRUSR | S_IWUSR) < 0) warn("Couldn't change permissions on pty slave: %m"); if (unlockpt(mfd) < 0) warn("Couldn't unlock pty slave: %m"); sfd = open(pty_name, O_RDWR); if (sfd < 0) { error("Couldn't open pty slave %s: %m", pty_name); close(mfd); return 0; } if (ioctl(sfd, I_PUSH, "ptem") < 0) warn("Couldn't push ptem module on pty slave: %m"); dbglog("Using %s", pty_name); strlcpy(slave_name, pty_name, MAXPATHLEN); *master_fdp = mfd; *slave_fdp = sfd; return 1; } ppp-2.4.5/pppd/tdb.c000066400000000000000000001525011130035057700142060ustar00rootroot00000000000000 /* Unix SMB/CIFS implementation. trivial database library Copyright (C) Andrew Tridgell 1999-2004 Copyright (C) Paul `Rusty' Russell 2000 Copyright (C) Jeremy Allison 2000-2003 ** NOTE! The following LGPL license applies to the tdb ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* NOTE: If you use tdbs under valgrind, and in particular if you run * tdbtorture, you may get spurious "uninitialized value" warnings. I * think this is because valgrind doesn't understand that the mmap'd * area may be written to by other processes. Memory can, from the * point of view of the grinded process, spontaneously become * initialized. * * I can think of a few solutions. [mbp 20030311] * * 1 - Write suppressions for Valgrind so that it doesn't complain * about this. Probably the most reasonable but people need to * remember to use them. * * 2 - Use IO not mmap when running under valgrind. Not so nice. * * 3 - Use the special valgrind macros to mark memory as valid at the * right time. Probably too hard -- the process just doesn't know. */ #include #include #include #include #include #include #include #include #include #include #include "tdb.h" #include "spinlock.h" #define TDB_MAGIC_FOOD "TDB file\n" #define TDB_VERSION (0x26011967 + 6) #define TDB_MAGIC (0x26011999U) #define TDB_FREE_MAGIC (~TDB_MAGIC) #define TDB_DEAD_MAGIC (0xFEE1DEAD) #define TDB_ALIGNMENT 4 #define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT) #define DEFAULT_HASH_SIZE 131 #define TDB_PAGE_SIZE 0x2000 #define FREELIST_TOP (sizeof(struct tdb_header)) #define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1)) #define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24)) #define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC) #define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r)) #define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off)) #define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + TDB_SPINLOCK_SIZE(hash_size)) /* NB assumes there is a local variable called "tdb" that is the * current context, also takes doubly-parenthesized print-style * argument. */ #define TDB_LOG(x) (tdb->log_fn?((tdb->log_fn x),0) : 0) /* lock offsets */ #define GLOBAL_LOCK 0 #define ACTIVE_LOCK 4 #ifndef MAP_FILE #define MAP_FILE 0 #endif #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif /* free memory if the pointer is valid and zero the pointer */ #ifndef SAFE_FREE #define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0) #endif #define BUCKET(hash) ((hash) % tdb->header.hash_size) TDB_DATA tdb_null; /* all contexts, to ensure no double-opens (fcntl locks don't nest!) */ static TDB_CONTEXT *tdbs = NULL; static int tdb_munmap(TDB_CONTEXT *tdb) { if (tdb->flags & TDB_INTERNAL) return 0; #ifdef HAVE_MMAP if (tdb->map_ptr) { int ret = munmap(tdb->map_ptr, tdb->map_size); if (ret != 0) return ret; } #endif tdb->map_ptr = NULL; return 0; } static void tdb_mmap(TDB_CONTEXT *tdb) { if (tdb->flags & TDB_INTERNAL) return; #ifdef HAVE_MMAP if (!(tdb->flags & TDB_NOMMAP)) { tdb->map_ptr = mmap(NULL, tdb->map_size, PROT_READ|(tdb->read_only? 0:PROT_WRITE), MAP_SHARED|MAP_FILE, tdb->fd, 0); /* * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!! */ if (tdb->map_ptr == MAP_FAILED) { tdb->map_ptr = NULL; TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n", tdb->map_size, strerror(errno))); } } else { tdb->map_ptr = NULL; } #else tdb->map_ptr = NULL; #endif } /* Endian conversion: we only ever deal with 4 byte quantities */ static void *convert(void *buf, u32 size) { u32 i, *p = buf; for (i = 0; i < size / 4; i++) p[i] = TDB_BYTEREV(p[i]); return buf; } #define DOCONV() (tdb->flags & TDB_CONVERT) #define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x) /* the body of the database is made of one list_struct for the free space plus a separate data list for each hash value */ struct list_struct { tdb_off next; /* offset of the next record in the list */ tdb_len rec_len; /* total byte length of record */ tdb_len key_len; /* byte length of key */ tdb_len data_len; /* byte length of data */ u32 full_hash; /* the full 32 bit hash of the key */ u32 magic; /* try to catch errors */ /* the following union is implied: union { char record[rec_len]; struct { char key[key_len]; char data[data_len]; } u32 totalsize; (tailer) } */ }; /*************************************************************** Allow a caller to set a "alarm" flag that tdb can check to abort a blocking lock on SIGALRM. ***************************************************************/ static sig_atomic_t *palarm_fired; void tdb_set_lock_alarm(sig_atomic_t *palarm) { palarm_fired = palarm; } /* a byte range locking function - return 0 on success this functions locks/unlocks 1 byte at the specified offset. On error, errno is also set so that errors are passed back properly through tdb_open(). */ static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, int rw_type, int lck_type, int probe) { struct flock fl; int ret; if (tdb->flags & TDB_NOLOCK) return 0; if ((rw_type == F_WRLCK) && (tdb->read_only)) { errno = EACCES; return -1; } fl.l_type = rw_type; fl.l_whence = SEEK_SET; fl.l_start = offset; fl.l_len = 1; fl.l_pid = 0; do { ret = fcntl(tdb->fd,lck_type,&fl); if (ret == -1 && errno == EINTR && palarm_fired && *palarm_fired) break; } while (ret == -1 && errno == EINTR); if (ret == -1) { if (!probe && lck_type != F_SETLK) { /* Ensure error code is set for log fun to examine. */ if (errno == EINTR && palarm_fired && *palarm_fired) tdb->ecode = TDB_ERR_LOCK_TIMEOUT; else tdb->ecode = TDB_ERR_LOCK; TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n", tdb->fd, offset, rw_type, lck_type)); } /* Was it an alarm timeout ? */ if (errno == EINTR && palarm_fired && *palarm_fired) { TDB_LOG((tdb, 5, "tdb_brlock timed out (fd=%d) at offset %d rw_type=%d lck_type=%d\n", tdb->fd, offset, rw_type, lck_type)); return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1); } /* Otherwise - generic lock error. errno set by fcntl. * EAGAIN is an expected return from non-blocking * locks. */ if (errno != EAGAIN) { TDB_LOG((tdb, 5, "tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d: %s\n", tdb->fd, offset, rw_type, lck_type, strerror(errno))); } return TDB_ERRCODE(TDB_ERR_LOCK, -1); } return 0; } /* lock a list in the database. list -1 is the alloc list */ static int tdb_lock(TDB_CONTEXT *tdb, int list, int ltype) { if (list < -1 || list >= (int)tdb->header.hash_size) { TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n", list, ltype)); return -1; } if (tdb->flags & TDB_NOLOCK) return 0; /* Since fcntl locks don't nest, we do a lock for the first one, and simply bump the count for future ones */ if (tdb->locked[list+1].count == 0) { if (!tdb->read_only && tdb->header.rwlocks) { if (tdb_spinlock(tdb, list, ltype)) { TDB_LOG((tdb, 0, "tdb_lock spinlock failed on list %d ltype=%d\n", list, ltype)); return -1; } } else if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) { TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n", list, ltype, strerror(errno))); return -1; } tdb->locked[list+1].ltype = ltype; } tdb->locked[list+1].count++; return 0; } /* unlock the database: returns void because it's too late for errors. */ /* changed to return int it may be interesting to know there has been an error --simo */ static int tdb_unlock(TDB_CONTEXT *tdb, int list, int ltype) { int ret = -1; if (tdb->flags & TDB_NOLOCK) return 0; /* Sanity checks */ if (list < -1 || list >= (int)tdb->header.hash_size) { TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size)); return ret; } if (tdb->locked[list+1].count==0) { TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n")); return ret; } if (tdb->locked[list+1].count == 1) { /* Down to last nested lock: unlock underneath */ if (!tdb->read_only && tdb->header.rwlocks) { ret = tdb_spinunlock(tdb, list, ltype); } else { ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0); } } else { ret = 0; } tdb->locked[list+1].count--; if (ret) TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n")); return ret; } /* check for an out of bounds access - if it is out of bounds then see if the database has been expanded by someone else and expand if necessary note that "len" is the minimum length needed for the db */ static int tdb_oob(TDB_CONTEXT *tdb, tdb_off len, int probe) { struct stat st; if (len <= tdb->map_size) return 0; if (tdb->flags & TDB_INTERNAL) { if (!probe) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n", (int)len, (int)tdb->map_size)); } return TDB_ERRCODE(TDB_ERR_IO, -1); } if (fstat(tdb->fd, &st) == -1) return TDB_ERRCODE(TDB_ERR_IO, -1); if (st.st_size < (size_t)len) { if (!probe) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n", (int)len, (int)st.st_size)); } return TDB_ERRCODE(TDB_ERR_IO, -1); } /* Unmap, update size, remap */ if (tdb_munmap(tdb) == -1) return TDB_ERRCODE(TDB_ERR_IO, -1); tdb->map_size = st.st_size; tdb_mmap(tdb); return 0; } /* write a lump of data at a specified offset */ static int tdb_write(TDB_CONTEXT *tdb, tdb_off off, void *buf, tdb_len len) { if (tdb_oob(tdb, off + len, 0) != 0) return -1; if (tdb->map_ptr) memcpy(off + (char *)tdb->map_ptr, buf, len); #ifdef HAVE_PWRITE else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) { #else else if (lseek(tdb->fd, off, SEEK_SET) != off || write(tdb->fd, buf, len) != (ssize_t)len) { #endif /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n", off, len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_IO, -1); } return 0; } /* read a lump of data at a specified offset, maybe convert */ static int tdb_read(TDB_CONTEXT *tdb,tdb_off off,void *buf,tdb_len len,int cv) { if (tdb_oob(tdb, off + len, 0) != 0) return -1; if (tdb->map_ptr) memcpy(buf, off + (char *)tdb->map_ptr, len); #ifdef HAVE_PREAD else if (pread(tdb->fd, buf, len, off) != (ssize_t)len) { #else else if (lseek(tdb->fd, off, SEEK_SET) != off || read(tdb->fd, buf, len) != (ssize_t)len) { #endif /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d (%s)\n", off, len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_IO, -1); } if (cv) convert(buf, len); return 0; } /* read a lump of data, allocating the space for it */ static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len) { char *buf; if (!(buf = malloc(len))) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_OOM; TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n", len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_OOM, buf); } if (tdb_read(tdb, offset, buf, len, 0) == -1) { SAFE_FREE(buf); return NULL; } return buf; } /* read/write a tdb_off */ static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d) { return tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV()); } static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d) { tdb_off off = *d; return tdb_write(tdb, offset, CONVERT(off), sizeof(*d)); } /* read/write a record */ static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) { if (tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1) return -1; if (TDB_BAD_MAGIC(rec)) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, 0,"rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } return tdb_oob(tdb, rec->next+sizeof(*rec), 0); } static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) { struct list_struct r = *rec; return tdb_write(tdb, offset, CONVERT(r), sizeof(r)); } /* read a freelist record and check for simple errors */ static int rec_free_read(TDB_CONTEXT *tdb, tdb_off off, struct list_struct *rec) { if (tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1) return -1; if (rec->magic == TDB_MAGIC) { /* this happens when a app is showdown while deleting a record - we should not completely fail when this happens */ TDB_LOG((tdb, 0,"rec_free_read non-free magic 0x%x at offset=%d - fixing\n", rec->magic, off)); rec->magic = TDB_FREE_MAGIC; if (tdb_write(tdb, off, rec, sizeof(*rec)) == -1) return -1; } if (rec->magic != TDB_FREE_MAGIC) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n", rec->magic, off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } if (tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0) return -1; return 0; } /* update a record tailer (must hold allocation lock) */ static int update_tailer(TDB_CONTEXT *tdb, tdb_off offset, const struct list_struct *rec) { tdb_off totalsize; /* Offset of tailer from record header */ totalsize = sizeof(*rec) + rec->rec_len; return ofs_write(tdb, offset + totalsize - sizeof(tdb_off), &totalsize); } static tdb_off tdb_dump_record(TDB_CONTEXT *tdb, tdb_off offset) { struct list_struct rec; tdb_off tailer_ofs, tailer; if (tdb_read(tdb, offset, (char *)&rec, sizeof(rec), DOCONV()) == -1) { printf("ERROR: failed to read record at %u\n", offset); return 0; } printf(" rec: offset=%u next=%d rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n", offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic); tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off); if (ofs_read(tdb, tailer_ofs, &tailer) == -1) { printf("ERROR: failed to read tailer at %u\n", tailer_ofs); return rec.next; } if (tailer != rec.rec_len + sizeof(rec)) { printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n", (unsigned)tailer, (unsigned)(rec.rec_len + sizeof(rec))); } return rec.next; } static int tdb_dump_chain(TDB_CONTEXT *tdb, int i) { tdb_off rec_ptr, top; top = TDB_HASH_TOP(i); if (tdb_lock(tdb, i, F_WRLCK) != 0) return -1; if (ofs_read(tdb, top, &rec_ptr) == -1) return tdb_unlock(tdb, i, F_WRLCK); if (rec_ptr) printf("hash=%d\n", i); while (rec_ptr) { rec_ptr = tdb_dump_record(tdb, rec_ptr); } return tdb_unlock(tdb, i, F_WRLCK); } void tdb_dump_all(TDB_CONTEXT *tdb) { int i; for (i=0;iheader.hash_size;i++) { tdb_dump_chain(tdb, i); } printf("freelist:\n"); tdb_dump_chain(tdb, -1); } int tdb_printfreelist(TDB_CONTEXT *tdb) { int ret; long total_free = 0; tdb_off offset, rec_ptr; struct list_struct rec; if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0) return ret; offset = FREELIST_TOP; /* read in the freelist top */ if (ofs_read(tdb, offset, &rec_ptr) == -1) { tdb_unlock(tdb, -1, F_WRLCK); return 0; } printf("freelist top=[0x%08x]\n", rec_ptr ); while (rec_ptr) { if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec), DOCONV()) == -1) { tdb_unlock(tdb, -1, F_WRLCK); return -1; } if (rec.magic != TDB_FREE_MAGIC) { printf("bad magic 0x%08x in free list\n", rec.magic); tdb_unlock(tdb, -1, F_WRLCK); return -1; } printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)]\n", rec.next, rec.rec_len, rec.rec_len ); total_free += rec.rec_len; /* move to the next record */ rec_ptr = rec.next; } printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, (int)total_free); return tdb_unlock(tdb, -1, F_WRLCK); } /* Remove an element from the freelist. Must have alloc lock. */ static int remove_from_freelist(TDB_CONTEXT *tdb, tdb_off off, tdb_off next) { tdb_off last_ptr, i; /* read in the freelist top */ last_ptr = FREELIST_TOP; while (ofs_read(tdb, last_ptr, &i) != -1 && i != 0) { if (i == off) { /* We've found it! */ return ofs_write(tdb, last_ptr, &next); } /* Follow chain (next offset is at start of record) */ last_ptr = i; } TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } /* Add an element into the freelist. Merge adjacent records if neccessary. */ static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) { tdb_off right, left; /* Allocation and tailer lock */ if (tdb_lock(tdb, -1, F_WRLCK) != 0) return -1; /* set an initial tailer, so if we fail we don't leave a bogus record */ if (update_tailer(tdb, offset, rec) != 0) { TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n")); goto fail; } /* Look right first (I'm an Australian, dammit) */ right = offset + sizeof(*rec) + rec->rec_len; if (right + sizeof(*rec) <= tdb->map_size) { struct list_struct r; if (tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) { TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right)); goto left; } /* If it's free, expand to include it. */ if (r.magic == TDB_FREE_MAGIC) { if (remove_from_freelist(tdb, right, r.next) == -1) { TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right)); goto left; } rec->rec_len += sizeof(r) + r.rec_len; } } left: /* Look left */ left = offset - sizeof(tdb_off); if (left > TDB_DATA_START(tdb->header.hash_size)) { struct list_struct l; tdb_off leftsize; /* Read in tailer and jump back to header */ if (ofs_read(tdb, left, &leftsize) == -1) { TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left)); goto update; } left = offset - leftsize; /* Now read in record */ if (tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) { TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize)); goto update; } /* If it's free, expand to include it. */ if (l.magic == TDB_FREE_MAGIC) { if (remove_from_freelist(tdb, left, l.next) == -1) { TDB_LOG((tdb, 0, "tdb_free: left free failed at %u\n", left)); goto update; } else { offset = left; rec->rec_len += leftsize; } } } update: if (update_tailer(tdb, offset, rec) == -1) { TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset)); goto fail; } /* Now, prepend to free list */ rec->magic = TDB_FREE_MAGIC; if (ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 || rec_write(tdb, offset, rec) == -1 || ofs_write(tdb, FREELIST_TOP, &offset) == -1) { TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset)); goto fail; } /* And we're done. */ tdb_unlock(tdb, -1, F_WRLCK); return 0; fail: tdb_unlock(tdb, -1, F_WRLCK); return -1; } /* expand a file. we prefer to use ftruncate, as that is what posix says to use for mmap expansion */ static int expand_file(TDB_CONTEXT *tdb, tdb_off size, tdb_off addition) { char buf[1024]; #if HAVE_FTRUNCATE_EXTEND if (ftruncate(tdb->fd, size+addition) != 0) { TDB_LOG((tdb, 0, "expand_file ftruncate to %d failed (%s)\n", size+addition, strerror(errno))); return -1; } #else char b = 0; #ifdef HAVE_PWRITE if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) { #else if (lseek(tdb->fd, (size+addition) - 1, SEEK_SET) != (size+addition) - 1 || write(tdb->fd, &b, 1) != 1) { #endif TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n", size+addition, strerror(errno))); return -1; } #endif /* now fill the file with something. This ensures that the file isn't sparse, which would be very bad if we ran out of disk. This must be done with write, not via mmap */ memset(buf, 0x42, sizeof(buf)); while (addition) { int n = addition>sizeof(buf)?sizeof(buf):addition; #ifdef HAVE_PWRITE int ret = pwrite(tdb->fd, buf, n, size); #else int ret; if (lseek(tdb->fd, size, SEEK_SET) != size) return -1; ret = write(tdb->fd, buf, n); #endif if (ret != n) { TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n", n, strerror(errno))); return -1; } addition -= n; size += n; } return 0; } /* expand the database at least size bytes by expanding the underlying file and doing the mmap again if necessary */ static int tdb_expand(TDB_CONTEXT *tdb, tdb_off size) { struct list_struct rec; tdb_off offset; if (tdb_lock(tdb, -1, F_WRLCK) == -1) { TDB_LOG((tdb, 0, "lock failed in tdb_expand\n")); return -1; } /* must know about any previous expansions by another process */ tdb_oob(tdb, tdb->map_size + 1, 1); /* always make room for at least 10 more records, and round the database up to a multiple of TDB_PAGE_SIZE */ size = TDB_ALIGN(tdb->map_size + size*10, TDB_PAGE_SIZE) - tdb->map_size; if (!(tdb->flags & TDB_INTERNAL)) tdb_munmap(tdb); /* * We must ensure the file is unmapped before doing this * to ensure consistency with systems like OpenBSD where * writes and mmaps are not consistent. */ /* expand the file itself */ if (!(tdb->flags & TDB_INTERNAL)) { if (expand_file(tdb, tdb->map_size, size) != 0) goto fail; } tdb->map_size += size; if (tdb->flags & TDB_INTERNAL) tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size); else { /* * We must ensure the file is remapped before adding the space * to ensure consistency with systems like OpenBSD where * writes and mmaps are not consistent. */ /* We're ok if the mmap fails as we'll fallback to read/write */ tdb_mmap(tdb); } /* form a new freelist record */ memset(&rec,'\0',sizeof(rec)); rec.rec_len = size - sizeof(rec); /* link it into the free list */ offset = tdb->map_size - size; if (tdb_free(tdb, offset, &rec) == -1) goto fail; tdb_unlock(tdb, -1, F_WRLCK); return 0; fail: tdb_unlock(tdb, -1, F_WRLCK); return -1; } /* allocate some space from the free list. The offset returned points to a unconnected list_struct within the database with room for at least length bytes of total data 0 is returned if the space could not be allocated */ static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length, struct list_struct *rec) { tdb_off rec_ptr, last_ptr, newrec_ptr; struct list_struct newrec; memset(&newrec, '\0', sizeof(newrec)); if (tdb_lock(tdb, -1, F_WRLCK) == -1) return 0; /* Extra bytes required for tailer */ length += sizeof(tdb_off); again: last_ptr = FREELIST_TOP; /* read in the freelist top */ if (ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) goto fail; /* keep looking until we find a freelist record big enough */ while (rec_ptr) { if (rec_free_read(tdb, rec_ptr, rec) == -1) goto fail; if (rec->rec_len >= length) { /* found it - now possibly split it up */ if (rec->rec_len > length + MIN_REC_SIZE) { /* Length of left piece */ length = TDB_ALIGN(length, TDB_ALIGNMENT); /* Right piece to go on free list */ newrec.rec_len = rec->rec_len - (sizeof(*rec) + length); newrec_ptr = rec_ptr + sizeof(*rec) + length; /* And left record is shortened */ rec->rec_len = length; } else newrec_ptr = 0; /* Remove allocated record from the free list */ if (ofs_write(tdb, last_ptr, &rec->next) == -1) goto fail; /* Update header: do this before we drop alloc lock, otherwise tdb_free() might try to merge with us, thinking we're free. (Thanks Jeremy Allison). */ rec->magic = TDB_MAGIC; if (rec_write(tdb, rec_ptr, rec) == -1) goto fail; /* Did we create new block? */ if (newrec_ptr) { /* Update allocated record tailer (we shortened it). */ if (update_tailer(tdb, rec_ptr, rec) == -1) goto fail; /* Free new record */ if (tdb_free(tdb, newrec_ptr, &newrec) == -1) goto fail; } /* all done - return the new record offset */ tdb_unlock(tdb, -1, F_WRLCK); return rec_ptr; } /* move to the next record */ last_ptr = rec_ptr; rec_ptr = rec->next; } /* we didn't find enough space. See if we can expand the database and if we can then try again */ if (tdb_expand(tdb, length + sizeof(*rec)) == 0) goto again; fail: tdb_unlock(tdb, -1, F_WRLCK); return 0; } /* initialise a new database with a specified hash size */ static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size) { struct tdb_header *newdb; int size, ret = -1; /* We make it up in memory, then write it out if not internal */ size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off); if (!(newdb = calloc(size, 1))) return TDB_ERRCODE(TDB_ERR_OOM, -1); /* Fill in the header */ newdb->version = TDB_VERSION; newdb->hash_size = hash_size; if (tdb->flags & TDB_INTERNAL) { tdb->map_size = size; tdb->map_ptr = (char *)newdb; memcpy(&tdb->header, newdb, sizeof(tdb->header)); /* Convert the `ondisk' version if asked. */ CONVERT(*newdb); return 0; } if (lseek(tdb->fd, 0, SEEK_SET) == -1) goto fail; if (ftruncate(tdb->fd, 0) == -1) goto fail; /* This creates an endian-converted header, as if read from disk */ CONVERT(*newdb); memcpy(&tdb->header, newdb, sizeof(tdb->header)); /* Don't endian-convert the magic food! */ memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1); if (write(tdb->fd, newdb, size) != size) ret = -1; else ret = tdb_create_rwlocks(tdb->fd, hash_size); fail: SAFE_FREE(newdb); return ret; } /* Returns 0 on fail. On success, return offset of record, and fills in rec */ static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, struct list_struct *r) { tdb_off rec_ptr; /* read in the hash top */ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) return 0; /* keep looking until we find the right record */ while (rec_ptr) { if (rec_read(tdb, rec_ptr, r) == -1) return 0; if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) { char *k; /* a very likely hit - read the key */ k = tdb_alloc_read(tdb, rec_ptr + sizeof(*r), r->key_len); if (!k) return 0; if (memcmp(key.dptr, k, key.dsize) == 0) { SAFE_FREE(k); return rec_ptr; } SAFE_FREE(k); } rec_ptr = r->next; } return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); } /* As tdb_find, but if you succeed, keep the lock */ static tdb_off tdb_find_lock_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, int locktype, struct list_struct *rec) { u32 rec_ptr; if (tdb_lock(tdb, BUCKET(hash), locktype) == -1) return 0; if (!(rec_ptr = tdb_find(tdb, key, hash, rec))) tdb_unlock(tdb, BUCKET(hash), locktype); return rec_ptr; } enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb) { return tdb->ecode; } static struct tdb_errname { enum TDB_ERROR ecode; const char *estring; } emap[] = { {TDB_SUCCESS, "Success"}, {TDB_ERR_CORRUPT, "Corrupt database"}, {TDB_ERR_IO, "IO Error"}, {TDB_ERR_LOCK, "Locking error"}, {TDB_ERR_OOM, "Out of memory"}, {TDB_ERR_EXISTS, "Record exists"}, {TDB_ERR_NOLOCK, "Lock exists on other keys"}, {TDB_ERR_NOEXIST, "Record does not exist"} }; /* Error string for the last tdb error */ const char *tdb_errorstr(TDB_CONTEXT *tdb) { u32 i; for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++) if (tdb->ecode == emap[i].ecode) return emap[i].estring; return "Invalid error code"; } /* update an entry in place - this only works if the new data size is <= the old data size and the key exists. on failure return -1. */ static int tdb_update_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf) { struct list_struct rec; tdb_off rec_ptr; /* find entry */ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) return -1; /* must be long enough key, data and tailer */ if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) { tdb->ecode = TDB_SUCCESS; /* Not really an error */ return -1; } if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len, dbuf.dptr, dbuf.dsize) == -1) return -1; if (dbuf.dsize != rec.data_len) { /* update size */ rec.data_len = dbuf.dsize; return rec_write(tdb, rec_ptr, &rec); } return 0; } /* find an entry in the database given a key */ /* If an entry doesn't exist tdb_err will be set to * TDB_ERR_NOEXIST. If a key has no data attached * tdb_err will not be set. Both will return a * zero pptr and zero dsize. */ TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key) { tdb_off rec_ptr; struct list_struct rec; TDB_DATA ret; u32 hash; /* find which hash bucket it is in */ hash = tdb->hash_fn(&key); if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) return tdb_null; if (rec.data_len) ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, rec.data_len); else ret.dptr = NULL; ret.dsize = rec.data_len; tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); return ret; } /* check if an entry in the database exists note that 1 is returned if the key is found and 0 is returned if not found this doesn't match the conventions in the rest of this module, but is compatible with gdbm */ static int tdb_exists_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash) { struct list_struct rec; if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0) return 0; tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); return 1; } int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key) { u32 hash = tdb->hash_fn(&key); return tdb_exists_hash(tdb, key, hash); } /* record lock stops delete underneath */ static int lock_record(TDB_CONTEXT *tdb, tdb_off off) { return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0; } /* Write locks override our own fcntl readlocks, so check it here. Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not an error to fail to get the lock here. */ static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off) { struct tdb_traverse_lock *i; for (i = &tdb->travlocks; i; i = i->next) if (i->off == off) return -1; return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1); } /* Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not an error to fail to get the lock here. */ static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off) { return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0); } /* fcntl locks don't stack: avoid unlocking someone else's */ static int unlock_record(TDB_CONTEXT *tdb, tdb_off off) { struct tdb_traverse_lock *i; u32 count = 0; if (off == 0) return 0; for (i = &tdb->travlocks; i; i = i->next) if (i->off == off) count++; return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0); } /* actually delete an entry in the database given the offset */ static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec) { tdb_off last_ptr, i; struct list_struct lastrec; if (tdb->read_only) return -1; if (write_lock_record(tdb, rec_ptr) == -1) { /* Someone traversing here: mark it as dead */ rec->magic = TDB_DEAD_MAGIC; return rec_write(tdb, rec_ptr, rec); } if (write_unlock_record(tdb, rec_ptr) != 0) return -1; /* find previous record in hash chain */ if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1) return -1; for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next) if (rec_read(tdb, i, &lastrec) == -1) return -1; /* unlink it: next ptr is at start of record. */ if (last_ptr == 0) last_ptr = TDB_HASH_TOP(rec->full_hash); if (ofs_write(tdb, last_ptr, &rec->next) == -1) return -1; /* recover the space */ if (tdb_free(tdb, rec_ptr, rec) == -1) return -1; return 0; } /* Uses traverse lock: 0 = finish, -1 = error, other = record offset */ static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock, struct list_struct *rec) { int want_next = (tlock->off != 0); /* Lock each chain from the start one. */ for (; tlock->hash < tdb->header.hash_size; tlock->hash++) { if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1) return -1; /* No previous record? Start at top of chain. */ if (!tlock->off) { if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash), &tlock->off) == -1) goto fail; } else { /* Otherwise unlock the previous record. */ if (unlock_record(tdb, tlock->off) != 0) goto fail; } if (want_next) { /* We have offset of old record: grab next */ if (rec_read(tdb, tlock->off, rec) == -1) goto fail; tlock->off = rec->next; } /* Iterate through chain */ while( tlock->off) { tdb_off current; if (rec_read(tdb, tlock->off, rec) == -1) goto fail; if (!TDB_DEAD(rec)) { /* Woohoo: we found one! */ if (lock_record(tdb, tlock->off) != 0) goto fail; return tlock->off; } /* Try to clean dead ones from old traverses */ current = tlock->off; tlock->off = rec->next; if (!tdb->read_only && do_delete(tdb, current, rec) != 0) goto fail; } tdb_unlock(tdb, tlock->hash, F_WRLCK); want_next = 0; } /* We finished iteration without finding anything */ return TDB_ERRCODE(TDB_SUCCESS, 0); fail: tlock->off = 0; if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n")); return -1; } /* traverse the entire database - calling fn(tdb, key, data) on each element. return -1 on error or the record count traversed if fn is NULL then it is not called a non-zero return value from fn() indicates that the traversal should stop */ int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *private) { TDB_DATA key, dbuf; struct list_struct rec; struct tdb_traverse_lock tl = { NULL, 0, 0 }; int ret, count = 0; /* This was in the initializaton, above, but the IRIX compiler * did not like it. crh */ tl.next = tdb->travlocks.next; /* fcntl locks don't stack: beware traverse inside traverse */ tdb->travlocks.next = &tl; /* tdb_next_lock places locks on the record returned, and its chain */ while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) { count++; /* now read the full record */ key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec), rec.key_len + rec.data_len); if (!key.dptr) { ret = -1; if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) goto out; if (unlock_record(tdb, tl.off) != 0) TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); goto out; } key.dsize = rec.key_len; dbuf.dptr = key.dptr + rec.key_len; dbuf.dsize = rec.data_len; /* Drop chain lock, call out */ if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) { ret = -1; goto out; } if (fn && fn(tdb, key, dbuf, private)) { /* They want us to terminate traversal */ ret = count; if (unlock_record(tdb, tl.off) != 0) { TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));; ret = -1; } tdb->travlocks.next = tl.next; SAFE_FREE(key.dptr); return count; } SAFE_FREE(key.dptr); } out: tdb->travlocks.next = tl.next; if (ret < 0) return -1; else return count; } /* find the first entry in the database and return its key */ TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb) { TDB_DATA key; struct list_struct rec; /* release any old lock */ if (unlock_record(tdb, tdb->travlocks.off) != 0) return tdb_null; tdb->travlocks.off = tdb->travlocks.hash = 0; if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0) return tdb_null; /* now read the key */ key.dsize = rec.key_len; key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize); if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n")); return key; } /* find the next entry in the database, returning its key */ TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey) { u32 oldhash; TDB_DATA key = tdb_null; struct list_struct rec; char *k = NULL; /* Is locked key the old key? If so, traverse will be reliable. */ if (tdb->travlocks.off) { if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK)) return tdb_null; if (rec_read(tdb, tdb->travlocks.off, &rec) == -1 || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec), rec.key_len)) || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) { /* No, it wasn't: unlock it and start from scratch */ if (unlock_record(tdb, tdb->travlocks.off) != 0) return tdb_null; if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0) return tdb_null; tdb->travlocks.off = 0; } SAFE_FREE(k); } if (!tdb->travlocks.off) { /* No previous element: do normal find, and lock record */ tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), F_WRLCK, &rec); if (!tdb->travlocks.off) return tdb_null; tdb->travlocks.hash = BUCKET(rec.full_hash); if (lock_record(tdb, tdb->travlocks.off) != 0) { TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno))); return tdb_null; } } oldhash = tdb->travlocks.hash; /* Grab next record: locks chain and returned record, unlocks old record */ if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) { key.dsize = rec.key_len; key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec), key.dsize); /* Unlock the chain of this new record */ if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n")); } /* Unlock the chain of old record */ if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n")); return key; } /* delete an entry in the database given a key */ static int tdb_delete_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash) { tdb_off rec_ptr; struct list_struct rec; int ret; if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec))) return -1; ret = do_delete(tdb, rec_ptr, &rec); if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n")); return ret; } int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key) { u32 hash = tdb->hash_fn(&key); return tdb_delete_hash(tdb, key, hash); } /* store an element in the database, replacing any existing element with the same key return 0 on success, -1 on failure */ int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) { struct list_struct rec; u32 hash; tdb_off rec_ptr; char *p = NULL; int ret = 0; /* find which hash bucket it is in */ hash = tdb->hash_fn(&key); if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) return -1; /* check for it existing, on insert. */ if (flag == TDB_INSERT) { if (tdb_exists_hash(tdb, key, hash)) { tdb->ecode = TDB_ERR_EXISTS; goto fail; } } else { /* first try in-place update, on modify or replace. */ if (tdb_update_hash(tdb, key, hash, dbuf) == 0) goto out; if (tdb->ecode == TDB_ERR_NOEXIST && flag == TDB_MODIFY) { /* if the record doesn't exist and we are in TDB_MODIFY mode then we should fail the store */ goto fail; } } /* reset the error code potentially set by the tdb_update() */ tdb->ecode = TDB_SUCCESS; /* delete any existing record - if it doesn't exist we don't care. Doing this first reduces fragmentation, and avoids coalescing with `allocated' block before it's updated. */ if (flag != TDB_INSERT) tdb_delete_hash(tdb, key, hash); /* Copy key+value *before* allocating free space in case malloc fails and we are left with a dead spot in the tdb. */ if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) { tdb->ecode = TDB_ERR_OOM; goto fail; } memcpy(p, key.dptr, key.dsize); if (dbuf.dsize) memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize); /* we have to allocate some space */ if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec))) goto fail; /* Read hash top into next ptr */ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1) goto fail; rec.key_len = key.dsize; rec.data_len = dbuf.dsize; rec.full_hash = hash; rec.magic = TDB_MAGIC; /* write out and point the top of the hash chain at it */ if (rec_write(tdb, rec_ptr, &rec) == -1 || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1 || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) { /* Need to tdb_unallocate() here */ goto fail; } out: SAFE_FREE(p); tdb_unlock(tdb, BUCKET(hash), F_WRLCK); return ret; fail: ret = -1; goto out; } /* Attempt to append data to an entry in place - this only works if the new data size is <= the old data size and the key exists. on failure return -1. Record must be locked before calling. */ static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA new_dbuf) { struct list_struct rec; tdb_off rec_ptr; /* find entry */ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) return -1; /* Append of 0 is always ok. */ if (new_dbuf.dsize == 0) return 0; /* must be long enough for key, old data + new data and tailer */ if (rec.rec_len < key.dsize + rec.data_len + new_dbuf.dsize + sizeof(tdb_off)) { /* No room. */ tdb->ecode = TDB_SUCCESS; /* Not really an error */ return -1; } if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len + rec.data_len, new_dbuf.dptr, new_dbuf.dsize) == -1) return -1; /* update size */ rec.data_len += new_dbuf.dsize; return rec_write(tdb, rec_ptr, &rec); } /* Append to an entry. Create if not exist. */ int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf) { struct list_struct rec; u32 hash; tdb_off rec_ptr; char *p = NULL; int ret = 0; size_t new_data_size = 0; /* find which hash bucket it is in */ hash = tdb->hash_fn(&key); if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) return -1; /* first try in-place. */ if (tdb_append_inplace(tdb, key, hash, new_dbuf) == 0) goto out; /* reset the error code potentially set by the tdb_append_inplace() */ tdb->ecode = TDB_SUCCESS; /* find entry */ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) { if (tdb->ecode != TDB_ERR_NOEXIST) goto fail; /* Not found - create. */ ret = tdb_store(tdb, key, new_dbuf, TDB_INSERT); goto out; } new_data_size = rec.data_len + new_dbuf.dsize; /* Copy key+old_value+value *before* allocating free space in case malloc fails and we are left with a dead spot in the tdb. */ if (!(p = (char *)malloc(key.dsize + new_data_size))) { tdb->ecode = TDB_ERR_OOM; goto fail; } /* Copy the key in place. */ memcpy(p, key.dptr, key.dsize); /* Now read the old data into place. */ if (rec.data_len && tdb_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, p + key.dsize, rec.data_len, 0) == -1) goto fail; /* Finally append the new data. */ if (new_dbuf.dsize) memcpy(p+key.dsize+rec.data_len, new_dbuf.dptr, new_dbuf.dsize); /* delete any existing record - if it doesn't exist we don't care. Doing this first reduces fragmentation, and avoids coalescing with `allocated' block before it's updated. */ tdb_delete_hash(tdb, key, hash); if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec))) goto fail; /* Read hash top into next ptr */ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1) goto fail; rec.key_len = key.dsize; rec.data_len = new_data_size; rec.full_hash = hash; rec.magic = TDB_MAGIC; /* write out and point the top of the hash chain at it */ if (rec_write(tdb, rec_ptr, &rec) == -1 || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+new_data_size)==-1 || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) { /* Need to tdb_unallocate() here */ goto fail; } out: SAFE_FREE(p); tdb_unlock(tdb, BUCKET(hash), F_WRLCK); return ret; fail: ret = -1; goto out; } static int tdb_already_open(dev_t device, ino_t ino) { TDB_CONTEXT *i; for (i = tdbs; i; i = i->next) { if (i->device == device && i->inode == ino) { return 1; } } return 0; } /* This is based on the hash algorithm from gdbm */ static u32 default_tdb_hash(TDB_DATA *key) { u32 value; /* Used to compute the hash value. */ u32 i; /* Used to cycle through random values. */ /* Set the initial value from the key size. */ for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++) value = (value + (key->dptr[i] << (i*5 % 24))); return (1103515243 * value + 12345); } /* open the database, creating it if necessary The open_flags and mode are passed straight to the open call on the database file. A flags value of O_WRONLY is invalid. The hash size is advisory, use zero for a default value. Return is NULL on error, in which case errno is also set. Don't try to call tdb_error or tdb_errname, just do strerror(errno). @param name may be NULL for internal databases. */ TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode) { return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL); } TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, tdb_log_func log_fn, tdb_hash_func hash_fn) { TDB_CONTEXT *tdb; struct stat st; int rev = 0, locked = 0; unsigned char *vp; u32 vertest; if (!(tdb = calloc(1, sizeof *tdb))) { /* Can't log this */ errno = ENOMEM; goto fail; } tdb->fd = -1; tdb->name = NULL; tdb->map_ptr = NULL; tdb->flags = tdb_flags; tdb->open_flags = open_flags; tdb->log_fn = log_fn; tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash; if ((open_flags & O_ACCMODE) == O_WRONLY) { TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n", name)); errno = EINVAL; goto fail; } if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE; if ((open_flags & O_ACCMODE) == O_RDONLY) { tdb->read_only = 1; /* read only databases don't do locking or clear if first */ tdb->flags |= TDB_NOLOCK; tdb->flags &= ~TDB_CLEAR_IF_FIRST; } /* internal databases don't mmap or lock, and start off cleared */ if (tdb->flags & TDB_INTERNAL) { tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP); tdb->flags &= ~TDB_CLEAR_IF_FIRST; if (tdb_new_database(tdb, hash_size) != 0) { TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!")); goto fail; } goto internal; } if ((tdb->fd = open(name, open_flags, mode)) == -1) { TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n", name, strerror(errno))); goto fail; /* errno set by open(2) */ } /* ensure there is only one process initialising at once */ if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) { TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n", name, strerror(errno))); goto fail; /* errno set by tdb_brlock */ } /* we need to zero database if we are the only one with it open */ if ((tdb_flags & TDB_CLEAR_IF_FIRST) && (locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0))) { open_flags |= O_CREAT; if (ftruncate(tdb->fd, 0) == -1) { TDB_LOG((tdb, 0, "tdb_open_ex: " "failed to truncate %s: %s\n", name, strerror(errno))); goto fail; /* errno set by ftruncate */ } } if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header) || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0 || (tdb->header.version != TDB_VERSION && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) { /* its not a valid database - possibly initialise it */ if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) { errno = EIO; /* ie bad format or something */ goto fail; } rev = (tdb->flags & TDB_CONVERT); } vp = (unsigned char *)&tdb->header.version; vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) | (((u32)vp[2]) << 8) | (u32)vp[3]; tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0; if (!rev) tdb->flags &= ~TDB_CONVERT; else { tdb->flags |= TDB_CONVERT; convert(&tdb->header, sizeof(tdb->header)); } if (fstat(tdb->fd, &st) == -1) goto fail; /* Is it already in the open list? If so, fail. */ if (tdb_already_open(st.st_dev, st.st_ino)) { TDB_LOG((tdb, 2, "tdb_open_ex: " "%s (%d,%d) is already open in this process\n", name, (int)st.st_dev, (int)st.st_ino)); errno = EBUSY; goto fail; } if (!(tdb->name = (char *)strdup(name))) { errno = ENOMEM; goto fail; } tdb->map_size = st.st_size; tdb->device = st.st_dev; tdb->inode = st.st_ino; tdb->locked = calloc(tdb->header.hash_size+1, sizeof(tdb->locked[0])); if (!tdb->locked) { TDB_LOG((tdb, 2, "tdb_open_ex: " "failed to allocate lock structure for %s\n", name)); errno = ENOMEM; goto fail; } tdb_mmap(tdb); if (locked) { if (!tdb->read_only) if (tdb_clear_spinlocks(tdb) != 0) { TDB_LOG((tdb, 0, "tdb_open_ex: " "failed to clear spinlock\n")); goto fail; } if (tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) { TDB_LOG((tdb, 0, "tdb_open_ex: " "failed to take ACTIVE_LOCK on %s: %s\n", name, strerror(errno))); goto fail; } } /* We always need to do this if the CLEAR_IF_FIRST flag is set, even if we didn't get the initial exclusive lock as we need to let all other users know we're using it. */ if (tdb_flags & TDB_CLEAR_IF_FIRST) { /* leave this lock in place to indicate it's in use */ if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1) goto fail; } internal: /* Internal (memory-only) databases skip all the code above to * do with disk files, and resume here by releasing their * global lock and hooking into the active list. */ if (tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1) goto fail; tdb->next = tdbs; tdbs = tdb; return tdb; fail: { int save_errno = errno; if (!tdb) return NULL; if (tdb->map_ptr) { if (tdb->flags & TDB_INTERNAL) SAFE_FREE(tdb->map_ptr); else tdb_munmap(tdb); } SAFE_FREE(tdb->name); if (tdb->fd != -1) if (close(tdb->fd) != 0) TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n")); SAFE_FREE(tdb->locked); SAFE_FREE(tdb); errno = save_errno; return NULL; } } /** * Close a database. * * @returns -1 for error; 0 for success. **/ int tdb_close(TDB_CONTEXT *tdb) { TDB_CONTEXT **i; int ret = 0; if (tdb->map_ptr) { if (tdb->flags & TDB_INTERNAL) SAFE_FREE(tdb->map_ptr); else tdb_munmap(tdb); } SAFE_FREE(tdb->name); if (tdb->fd != -1) ret = close(tdb->fd); SAFE_FREE(tdb->locked); /* Remove from contexts list */ for (i = &tdbs; *i; i = &(*i)->next) { if (*i == tdb) { *i = tdb->next; break; } } memset(tdb, 0, sizeof(*tdb)); SAFE_FREE(tdb); return ret; } /* lock/unlock entire database */ int tdb_lockall(TDB_CONTEXT *tdb) { u32 i; /* There are no locks on read-only dbs */ if (tdb->read_only) return TDB_ERRCODE(TDB_ERR_LOCK, -1); for (i = 0; i < tdb->header.hash_size; i++) if (tdb_lock(tdb, i, F_WRLCK)) break; /* If error, release locks we have... */ if (i < tdb->header.hash_size) { u32 j; for ( j = 0; j < i; j++) tdb_unlock(tdb, j, F_WRLCK); return TDB_ERRCODE(TDB_ERR_NOLOCK, -1); } return 0; } void tdb_unlockall(TDB_CONTEXT *tdb) { u32 i; for (i=0; i < tdb->header.hash_size; i++) tdb_unlock(tdb, i, F_WRLCK); } /* lock/unlock one hash chain. This is meant to be used to reduce contention - it cannot guarantee how many records will be locked */ int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key) { return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); } int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key) { return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); } int tdb_chainlock_read(TDB_CONTEXT *tdb, TDB_DATA key) { return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); } int tdb_chainunlock_read(TDB_CONTEXT *tdb, TDB_DATA key) { return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); } /* register a loging function */ void tdb_logging_function(TDB_CONTEXT *tdb, void (*fn)(TDB_CONTEXT *, int , const char *, ...)) { tdb->log_fn = fn; } /* reopen a tdb - this can be used after a fork to ensure that we have an independent seek pointer from our parent and to re-establish locks */ int tdb_reopen(TDB_CONTEXT *tdb) { struct stat st; if (tdb->flags & TDB_INTERNAL) return 0; /* Nothing to do. */ if (tdb_munmap(tdb) != 0) { TDB_LOG((tdb, 0, "tdb_reopen: munmap failed (%s)\n", strerror(errno))); goto fail; } if (close(tdb->fd) != 0) TDB_LOG((tdb, 0, "tdb_reopen: WARNING closing tdb->fd failed!\n")); tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0); if (tdb->fd == -1) { TDB_LOG((tdb, 0, "tdb_reopen: open failed (%s)\n", strerror(errno))); goto fail; } if (fstat(tdb->fd, &st) != 0) { TDB_LOG((tdb, 0, "tdb_reopen: fstat failed (%s)\n", strerror(errno))); goto fail; } if (st.st_ino != tdb->inode || st.st_dev != tdb->device) { TDB_LOG((tdb, 0, "tdb_reopen: file dev/inode has changed!\n")); goto fail; } tdb_mmap(tdb); if ((tdb->flags & TDB_CLEAR_IF_FIRST) && (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)) { TDB_LOG((tdb, 0, "tdb_reopen: failed to obtain active lock\n")); goto fail; } return 0; fail: tdb_close(tdb); return -1; } /* reopen all tdb's */ int tdb_reopen_all(void) { TDB_CONTEXT *tdb; for (tdb=tdbs; tdb; tdb = tdb->next) { /* Ensure no clear-if-first. */ tdb->flags &= ~TDB_CLEAR_IF_FIRST; if (tdb_reopen(tdb) != 0) return -1; } return 0; } ppp-2.4.5/pppd/tdb.h000066400000000000000000000125611130035057700142140ustar00rootroot00000000000000#ifndef __TDB_H__ #define __TDB_H__ /* Unix SMB/CIFS implementation. trivial database library Copyright (C) Andrew Tridgell 1999-2004 ** NOTE! The following LGPL license applies to the tdb ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef __cplusplus extern "C" { #endif #ifndef PRINTF_ATTRIBUTE /** Use gcc attribute to check printf fns. a1 is the 1-based index of * the parameter containing the format, and a2 the index of the first * argument. Note that some gcc 2.x versions don't handle this * properly **/ #if (__GNUC__ >= 3) #define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) #else #define PRINTF_ATTRIBUTE(a1, a2) #endif #endif /* flags to tdb_store() */ #define TDB_REPLACE 1 #define TDB_INSERT 2 #define TDB_MODIFY 3 /* flags for tdb_open() */ #define TDB_DEFAULT 0 /* just a readability place holder */ #define TDB_CLEAR_IF_FIRST 1 #define TDB_INTERNAL 2 /* don't store on disk */ #define TDB_NOLOCK 4 /* don't do any locking */ #define TDB_NOMMAP 8 /* don't use mmap */ #define TDB_CONVERT 16 /* convert endian (internal use) */ #define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */ #define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret) /* error codes */ enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT, TDB_ERR_NOEXIST}; #ifndef u32 #define u32 unsigned #endif typedef struct { char *dptr; size_t dsize; } TDB_DATA; typedef u32 tdb_len; typedef u32 tdb_off; /* this is stored at the front of every database */ struct tdb_header { char magic_food[32]; /* for /etc/magic */ u32 version; /* version of the code */ u32 hash_size; /* number of hash entries */ tdb_off rwlocks; tdb_off reserved[31]; }; struct tdb_lock_type { u32 count; u32 ltype; }; struct tdb_traverse_lock { struct tdb_traverse_lock *next; u32 off; u32 hash; }; /* this is the context structure that is returned from a db open */ typedef struct tdb_context { char *name; /* the name of the database */ void *map_ptr; /* where it is currently mapped */ int fd; /* open file descriptor for the database */ tdb_len map_size; /* how much space has been mapped */ int read_only; /* opened read-only */ struct tdb_lock_type *locked; /* array of chain locks */ enum TDB_ERROR ecode; /* error code for last tdb error */ struct tdb_header header; /* a cached copy of the header */ u32 flags; /* the flags passed to tdb_open */ struct tdb_traverse_lock travlocks; /* current traversal locks */ struct tdb_context *next; /* all tdbs to avoid multiple opens */ dev_t device; /* uniquely identifies this tdb */ ino_t inode; /* uniquely identifies this tdb */ void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...) PRINTF_ATTRIBUTE(3,4); /* logging function */ u32 (*hash_fn)(TDB_DATA *key); int open_flags; /* flags used in the open - needed by reopen */ } TDB_CONTEXT; typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *); typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...); typedef u32 (*tdb_hash_func)(TDB_DATA *key); TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode); TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, tdb_log_func log_fn, tdb_hash_func hash_fn); int tdb_reopen(TDB_CONTEXT *tdb); int tdb_reopen_all(void); void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func); enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb); const char *tdb_errorstr(TDB_CONTEXT *tdb); TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key); int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key); int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag); int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf); int tdb_close(TDB_CONTEXT *tdb); TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb); TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key); int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *); int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key); int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]); void tdb_unlockkeys(TDB_CONTEXT *tdb); int tdb_lockall(TDB_CONTEXT *tdb); void tdb_unlockall(TDB_CONTEXT *tdb); /* Low level locking functions: use with care */ void tdb_set_lock_alarm(sig_atomic_t *palarm); int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key); int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key); /* Debug functions. Not used in production. */ void tdb_dump_all(TDB_CONTEXT *tdb); int tdb_printfreelist(TDB_CONTEXT *tdb); extern TDB_DATA tdb_null; #ifdef __cplusplus } #endif #endif /* tdb.h */ ppp-2.4.5/pppd/tty.c000066400000000000000000001003201130035057700142450ustar00rootroot00000000000000/* * tty.c - code for handling serial ports in pppd. * * Copyright (C) 2000-2004 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Portions derived from main.c, which is: * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: tty.c,v 1.27 2008/07/01 12:27:56 paulus Exp $" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pppd.h" #include "fsm.h" #include "lcp.h" void tty_process_extra_options __P((void)); void tty_check_options __P((void)); int connect_tty __P((void)); void disconnect_tty __P((void)); void tty_close_fds __P((void)); void cleanup_tty __P((void)); void tty_do_send_config __P((int, u_int32_t, int, int)); static int setdevname __P((char *, char **, int)); static int setspeed __P((char *, char **, int)); static int setxonxoff __P((char **)); static int setescape __P((char **)); static void printescape __P((option_t *, void (*)(void *, char *,...),void *)); static void finish_tty __P((void)); static int start_charshunt __P((int, int)); static void stop_charshunt __P((void *, int)); static void charshunt_done __P((void *)); static void charshunt __P((int, int, char *)); static int record_write __P((FILE *, int code, u_char *buf, int nb, struct timeval *)); static int open_socket __P((char *)); static void maybe_relock __P((void *, int)); static int pty_master; /* fd for master side of pty */ static int pty_slave; /* fd for slave side of pty */ static int real_ttyfd; /* fd for actual serial port (not pty) */ static int ttyfd; /* Serial port file descriptor */ static char speed_str[16]; /* Serial port speed as string */ mode_t tty_mode = (mode_t)-1; /* Original access permissions to tty */ int baud_rate; /* Actual bits/second for serial device */ char *callback_script; /* script for doing callback */ int charshunt_pid; /* Process ID for charshunt */ int locked; /* lock() has succeeded */ struct stat devstat; /* result of stat() on devnam */ /* option variables */ int crtscts = 0; /* Use hardware flow control */ bool modem = 1; /* Use modem control lines */ int inspeed = 0; /* Input/Output speed requested */ bool lockflag = 0; /* Create lock file to lock the serial dev */ char *initializer = NULL; /* Script to initialize physical link */ char *connect_script = NULL; /* Script to establish physical link */ char *disconnect_script = NULL; /* Script to disestablish physical link */ char *welcomer = NULL; /* Script to run after phys link estab. */ char *ptycommand = NULL; /* Command to run on other side of pty */ bool notty = 0; /* Stdin/out is not a tty */ char *record_file = NULL; /* File to record chars sent/received */ int max_data_rate; /* max bytes/sec through charshunt */ bool sync_serial = 0; /* Device is synchronous serial device */ char *pty_socket = NULL; /* Socket to connect to pty */ int using_pty = 0; /* we're allocating a pty as the device */ extern uid_t uid; extern int kill_link; extern int asked_to_quit; extern int got_sigterm; /* XXX */ extern int privopen; /* don't lock, open device as root */ u_int32_t xmit_accm[8]; /* extended transmit ACCM */ /* option descriptors */ option_t tty_options[] = { /* device name must be first, or change connect_tty() below! */ { "device name", o_wild, (void *) &setdevname, "Serial port device name", OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, devnam}, { "tty speed", o_wild, (void *) &setspeed, "Baud rate for serial port", OPT_PRIO | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, speed_str }, { "lock", o_bool, &lockflag, "Lock serial device with UUCP-style lock file", OPT_PRIO | 1 }, { "nolock", o_bool, &lockflag, "Don't lock serial device", OPT_PRIOSUB | OPT_PRIV }, { "init", o_string, &initializer, "A program to initialize the device", OPT_PRIO | OPT_PRIVFIX }, { "connect", o_string, &connect_script, "A program to set up a connection", OPT_PRIO | OPT_PRIVFIX }, { "disconnect", o_string, &disconnect_script, "Program to disconnect serial device", OPT_PRIO | OPT_PRIVFIX }, { "welcome", o_string, &welcomer, "Script to welcome client", OPT_PRIO | OPT_PRIVFIX }, { "pty", o_string, &ptycommand, "Script to run on pseudo-tty master side", OPT_PRIO | OPT_PRIVFIX | OPT_DEVNAM }, { "notty", o_bool, ¬ty, "Input/output is not a tty", OPT_DEVNAM | 1 }, { "socket", o_string, &pty_socket, "Send and receive over socket, arg is host:port", OPT_PRIO | OPT_DEVNAM }, { "record", o_string, &record_file, "Record characters sent/received to file", OPT_PRIO }, { "crtscts", o_int, &crtscts, "Set hardware (RTS/CTS) flow control", OPT_PRIO | OPT_NOARG | OPT_VAL(1) }, { "cdtrcts", o_int, &crtscts, "Set alternate hardware (DTR/CTS) flow control", OPT_PRIOSUB | OPT_NOARG | OPT_VAL(2) }, { "nocrtscts", o_int, &crtscts, "Disable hardware flow control", OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) }, { "-crtscts", o_int, &crtscts, "Disable hardware flow control", OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) }, { "nocdtrcts", o_int, &crtscts, "Disable hardware flow control", OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) }, { "xonxoff", o_special_noarg, (void *)setxonxoff, "Set software (XON/XOFF) flow control", OPT_PRIOSUB }, { "modem", o_bool, &modem, "Use modem control lines", OPT_PRIO | 1 }, { "local", o_bool, &modem, "Don't use modem control lines", OPT_PRIOSUB | 0 }, { "sync", o_bool, &sync_serial, "Use synchronous HDLC serial encoding", 1 }, { "datarate", o_int, &max_data_rate, "Maximum data rate in bytes/sec (with pty, notty or record option)", OPT_PRIO }, { "escape", o_special, (void *)setescape, "List of character codes to escape on transmission", OPT_A2PRINTER, (void *)printescape }, { NULL } }; struct channel tty_channel = { tty_options, &tty_process_extra_options, &tty_check_options, &connect_tty, &disconnect_tty, &tty_establish_ppp, &tty_disestablish_ppp, &tty_do_send_config, &tty_recv_config, &cleanup_tty, &tty_close_fds }; /* * setspeed - Set the serial port baud rate. * If doit is 0, the call is to check whether this option is * potentially a speed value. */ static int setspeed(arg, argv, doit) char *arg; char **argv; int doit; { char *ptr; int spd; spd = strtol(arg, &ptr, 0); if (ptr == arg || *ptr != 0 || spd == 0) return 0; if (doit) { inspeed = spd; slprintf(speed_str, sizeof(speed_str), "%d", spd); } return 1; } /* * setdevname - Set the device name. * If doit is 0, the call is to check whether this option is * potentially a device name. */ static int setdevname(cp, argv, doit) char *cp; char **argv; int doit; { struct stat statbuf; char dev[MAXPATHLEN]; if (*cp == 0) return 0; if (*cp != '/') { strlcpy(dev, "/dev/", sizeof(dev)); strlcat(dev, cp, sizeof(dev)); cp = dev; } /* * Check if there is a character device by this name. */ if (stat(cp, &statbuf) < 0) { if (!doit) return errno != ENOENT; option_error("Couldn't stat %s: %m", cp); return 0; } if (!S_ISCHR(statbuf.st_mode)) { if (doit) option_error("%s is not a character device", cp); return 0; } if (doit) { strlcpy(devnam, cp, sizeof(devnam)); devstat = statbuf; default_device = 0; } return 1; } static int setxonxoff(argv) char **argv; { lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */ lcp_wantoptions[0].neg_asyncmap = 1; crtscts = -2; 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) { option_error("escape parameter contains invalid hex number '%s'", p); return 0; } p = endp; if (n < 0 || n == 0x5E || n > 0xFF) { option_error("can't escape character 0x%x", n); ret = 0; } else xmit_accm[n >> 5] |= 1 << (n & 0x1F); while (*p == ',' || *p == ' ') ++p; } lcp_allowoptions[0].asyncmap = xmit_accm[0]; return ret; } static void printescape(opt, printer, arg) option_t *opt; void (*printer) __P((void *, char *, ...)); void *arg; { int n; int first = 1; for (n = 0; n < 256; ++n) { if (n == 0x7d) n += 2; /* skip 7d, 7e */ if (xmit_accm[n >> 5] & (1 << (n & 0x1f))) { if (!first) printer(arg, ","); else first = 0; printer(arg, "%x", n); } } if (first) printer(arg, "oops # nothing escaped"); } /* * tty_init - do various tty-related initializations. */ void tty_init() { add_notifier(&pidchange, maybe_relock, 0); the_channel = &tty_channel; xmit_accm[3] = 0x60000000; } /* * tty_process_extra_options - work out which tty device we are using * and read its options file. */ void tty_process_extra_options() { using_pty = notty || ptycommand != NULL || pty_socket != NULL; if (using_pty) return; if (default_device) { char *p; if (!isatty(0) || (p = ttyname(0)) == NULL) { option_error("no device specified and stdin is not a tty"); exit(EXIT_OPTION_ERROR); } strlcpy(devnam, p, sizeof(devnam)); if (stat(devnam, &devstat) < 0) fatal("Couldn't stat default device %s: %m", devnam); } /* * Parse the tty options file. * The per-tty options file should not change * ptycommand, pty_socket, notty or devnam. * options_for_tty doesn't override options set on the command line, * except for some privileged options. */ if (!options_for_tty()) exit(EXIT_OPTION_ERROR); } /* * tty_check_options - do consistency checks on the options we were given. */ void tty_check_options() { struct stat statbuf; int fdflags; if (demand && notty) { option_error("demand-dialling is incompatible with notty"); exit(EXIT_OPTION_ERROR); } if (demand && connect_script == 0 && ptycommand == NULL && pty_socket == NULL) { option_error("connect script is required for demand-dialling\n"); exit(EXIT_OPTION_ERROR); } /* default holdoff to 0 if no connect script has been given */ if (connect_script == 0 && !holdoff_specified) holdoff = 0; if (using_pty) { if (!default_device) { option_error("%s option precludes specifying device name", pty_socket? "socket": notty? "notty": "pty"); exit(EXIT_OPTION_ERROR); } if (ptycommand != NULL && notty) { option_error("pty option is incompatible with notty option"); exit(EXIT_OPTION_ERROR); } if (pty_socket != NULL && (ptycommand != NULL || notty)) { option_error("socket option is incompatible with pty and notty"); exit(EXIT_OPTION_ERROR); } default_device = notty; lockflag = 0; modem = 0; if (notty && log_to_fd <= 1) log_to_fd = -1; } else { /* * If the user has specified a device which is the same as * the one on stdin, pretend they didn't specify any. * If the device is already open read/write on stdin, * we assume we don't need to lock it, and we can open it * as root. */ if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev) { default_device = 1; fdflags = fcntl(0, F_GETFL); if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR) privopen = 1; } } if (default_device) nodetach = 1; /* * Don't send log messages to the serial port, it tends to * confuse the peer. :-) */ if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev) log_to_fd = -1; } /* * connect_tty - get the serial port ready to start doing PPP. * That is, open the serial port, set its speed and mode, and run * the connector and/or welcomer. */ int connect_tty() { char *connector; int fdflags; #ifndef __linux__ struct stat statbuf; #endif char numbuf[16]; /* * Get a pty master/slave pair if the pty, notty, socket, * or record options were specified. */ strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); pty_master = -1; pty_slave = -1; real_ttyfd = -1; if (using_pty || record_file != NULL) { if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) { error("Couldn't allocate pseudo-tty"); status = EXIT_FATAL_ERROR; return -1; } set_up_tty(pty_slave, 1); } /* * Lock the device if we've been asked to. */ status = EXIT_LOCK_FAILED; if (lockflag && !privopen) { if (lock(devnam) < 0) goto errret; locked = 1; } /* * Open the serial device and set it up to be the ppp interface. * First we open it in non-blocking mode so we can set the * various termios flags appropriately. If we aren't dialling * out and we want to use the modem lines, we reopen it later * in order to wait for the carrier detect signal from the modem. */ got_sigterm = 0; connector = doing_callback? callback_script: connect_script; if (devnam[0] != 0) { for (;;) { /* If the user specified the device name, become the user before opening it. */ int err, prio; prio = privopen? OPRIO_ROOT: tty_options[0].priority; if (prio < OPRIO_ROOT && seteuid(uid) == -1) { error("Unable to drop privileges before opening %s: %m\n", devnam); status = EXIT_OPEN_FAILED; goto errret; } real_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); err = errno; if (prio < OPRIO_ROOT && seteuid(0) == -1) fatal("Unable to regain privileges"); if (real_ttyfd >= 0) break; errno = err; if (err != EINTR) { error("Failed to open %s: %m", devnam); status = EXIT_OPEN_FAILED; } if (!persist || err != EINTR) goto errret; } ttyfd = real_ttyfd; if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1 || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) warn("Couldn't reset non-blocking mode on device: %m"); #ifndef __linux__ /* * Linux 2.4 and above blocks normal writes to the tty * when it is in PPP line discipline, so this isn't needed. */ /* * Do the equivalent of `mesg n' to stop broadcast messages. */ if (fstat(ttyfd, &statbuf) < 0 || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) { warn("Couldn't restrict write permissions to %s: %m", devnam); } else tty_mode = statbuf.st_mode; #endif /* __linux__ */ /* * Set line speed, flow control, etc. * If we have a non-null connection or initializer script, * on most systems we set CLOCAL for now so that we can talk * to the modem before carrier comes up. But this has the * side effect that we might miss it if CD drops before we * get to clear CLOCAL below. On systems where we can talk * successfully to the modem with CLOCAL clear and CD down, * we could clear CLOCAL at this point. */ set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0) || initializer != NULL)); } /* * If the pty, socket, notty and/or record option was specified, * start up the character shunt now. */ status = EXIT_PTYCMD_FAILED; if (ptycommand != NULL) { if (record_file != NULL) { int ipipe[2], opipe[2], ok; if (pipe(ipipe) < 0 || pipe(opipe) < 0) fatal("Couldn't create pipes for record option: %m"); /* don't leak these to the ptycommand */ (void) fcntl(ipipe[0], F_SETFD, FD_CLOEXEC); (void) fcntl(opipe[1], F_SETFD, FD_CLOEXEC); ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0 && start_charshunt(ipipe[0], opipe[1]); close(ipipe[0]); close(ipipe[1]); close(opipe[0]); close(opipe[1]); if (!ok) goto errret; } else { if (device_script(ptycommand, pty_master, pty_master, 1) < 0) goto errret; } } else if (pty_socket != NULL) { int fd = open_socket(pty_socket); if (fd < 0) goto errret; if (!start_charshunt(fd, fd)) goto errret; close(fd); } else if (notty) { if (!start_charshunt(0, 1)) goto errret; dup2(fd_devnull, 0); dup2(fd_devnull, 1); if (log_to_fd == 1) log_to_fd = -1; if (log_to_fd != 2) dup2(fd_devnull, 2); } else if (record_file != NULL) { int fd = dup(ttyfd); if (!start_charshunt(fd, fd)) goto errret; } if (using_pty || record_file != NULL) { ttyfd = pty_slave; close(pty_master); pty_master = -1; } /* run connection script */ if ((connector && connector[0]) || initializer) { if (real_ttyfd != -1) { /* XXX do this if doing_callback == CALLBACK_DIALIN? */ if (!default_device && modem) { setdtr(real_ttyfd, 0); /* in case modem is off hook */ sleep(1); setdtr(real_ttyfd, 1); } } if (initializer && initializer[0]) { if (device_script(initializer, ttyfd, ttyfd, 0) < 0) { error("Initializer script failed"); status = EXIT_INIT_FAILED; goto errretf; } if (got_sigterm) { disconnect_tty(); goto errretf; } info("Serial port initialized."); } if (connector && connector[0]) { if (device_script(connector, ttyfd, ttyfd, 0) < 0) { error("Connect script failed"); status = EXIT_CONNECT_FAILED; goto errretf; } if (got_sigterm) { disconnect_tty(); goto errretf; } info("Serial connection established."); } /* set line speed, flow control, etc.; clear CLOCAL if modem option */ if (real_ttyfd != -1) set_up_tty(real_ttyfd, 0); if (doing_callback == CALLBACK_DIALIN) connector = NULL; } /* reopen tty if necessary to wait for carrier */ if (connector == NULL && modem && devnam[0] != 0) { int i; for (;;) { if ((i = open(devnam, O_RDWR)) >= 0) break; if (errno != EINTR) { error("Failed to reopen %s: %m", devnam); status = EXIT_OPEN_FAILED; } if (!persist || errno != EINTR || hungup || got_sigterm) goto errret; } close(i); } slprintf(numbuf, sizeof(numbuf), "%d", baud_rate); script_setenv("SPEED", numbuf, 0); /* run welcome script, if any */ if (welcomer && welcomer[0]) { if (device_script(welcomer, ttyfd, ttyfd, 0) < 0) warn("Welcome script failed"); } /* * If we are initiating this connection, wait for a short * time for something from the peer. This can avoid bouncing * our packets off his tty before he has it set up. */ if (connector != NULL || ptycommand != NULL || pty_socket != NULL) listen_time = connect_delay; return ttyfd; errretf: if (real_ttyfd >= 0) tcflush(real_ttyfd, TCIOFLUSH); errret: if (pty_master >= 0) { close(pty_master); pty_master = -1; } ttyfd = -1; if (got_sigterm) asked_to_quit = 1; return -1; } void disconnect_tty() { if (disconnect_script == NULL || hungup) return; if (real_ttyfd >= 0) set_up_tty(real_ttyfd, 1); if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) { warn("disconnect script failed"); } else { info("Serial link disconnected."); } stop_charshunt(NULL, 0); } void tty_close_fds() { if (pty_slave >= 0) close(pty_slave); if (real_ttyfd >= 0) { close(real_ttyfd); real_ttyfd = -1; } /* N.B. ttyfd will == either pty_slave or real_ttyfd */ } void cleanup_tty() { if (real_ttyfd >= 0) finish_tty(); tty_close_fds(); if (locked) { unlock(); locked = 0; } } /* * tty_do_send_config - set transmit-side PPP configuration. * We set the extended transmit ACCM here as well. */ void tty_do_send_config(mtu, accm, pcomp, accomp) int mtu; u_int32_t accm; int pcomp, accomp; { tty_set_xaccm(xmit_accm); tty_send_config(mtu, accm, pcomp, accomp); } /* * finish_tty - restore the terminal device to its original settings */ static void finish_tty() { /* drop dtr to hang up */ if (!default_device && modem) { setdtr(real_ttyfd, 0); /* * This sleep is in case the serial port has CLOCAL set by default, * and consequently will reassert DTR when we close the device. */ sleep(1); } restore_tty(real_ttyfd); #ifndef __linux__ if (tty_mode != (mode_t) -1) { if (fchmod(real_ttyfd, tty_mode) != 0) error("Couldn't restore tty permissions"); } #endif /* __linux__ */ close(real_ttyfd); real_ttyfd = -1; } /* * maybe_relock - our PID has changed, maybe update the lock file. */ static void maybe_relock(arg, pid) void *arg; int pid; { if (locked) relock(pid); } /* * open_socket - establish a stream socket connection to the nominated * host and port. */ static int open_socket(dest) char *dest; { char *sep, *endp = NULL; int sock, port = -1; u_int32_t host; struct hostent *hent; struct sockaddr_in sad; /* parse host:port and resolve host to an IP address */ sep = strchr(dest, ':'); if (sep != NULL) port = strtol(sep+1, &endp, 10); if (port < 0 || endp == sep+1 || sep == dest) { error("Can't parse host:port for socket destination"); return -1; } *sep = 0; host = inet_addr(dest); if (host == (u_int32_t) -1) { hent = gethostbyname(dest); if (hent == NULL) { error("%s: unknown host in socket option", dest); *sep = ':'; return -1; } host = *(u_int32_t *)(hent->h_addr_list[0]); } *sep = ':'; /* get a socket and connect it to the other end */ sock = socket(PF_INET, SOCK_STREAM, 0); if (sock < 0) { error("Can't create socket: %m"); return -1; } memset(&sad, 0, sizeof(sad)); sad.sin_family = AF_INET; sad.sin_port = htons(port); sad.sin_addr.s_addr = host; if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) { error("Can't connect to %s: %m", dest); close(sock); return -1; } return sock; } /* * start_charshunt - create a child process to run the character shunt. */ static int start_charshunt(ifd, ofd) int ifd, ofd; { int cpid; cpid = safe_fork(ifd, ofd, (log_to_fd >= 0? log_to_fd: 2)); if (cpid == -1) { error("Can't fork process for character shunt: %m"); return 0; } if (cpid == 0) { /* child */ reopen_log(); if (!nodetach) log_to_fd = -1; else if (log_to_fd >= 0) log_to_fd = 2; setgid(getgid()); setuid(uid); if (getuid() != uid) fatal("setuid failed"); charshunt(0, 1, record_file); exit(0); } charshunt_pid = cpid; record_child(cpid, "pppd (charshunt)", charshunt_done, NULL, 1); return 1; } static void charshunt_done(arg) void *arg; { charshunt_pid = 0; } static void stop_charshunt(arg, sig) void *arg; int sig; { if (charshunt_pid) kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM)); } /* * charshunt - the character shunt, which passes characters between * the pty master side and the serial port (or stdin/stdout). * This runs as the user (not as root). * (We assume ofd >= ifd which is true the way this gets called. :-). */ static void charshunt(ifd, ofd, record_file) int ifd, ofd; char *record_file; { int n, nfds; fd_set ready, writey; u_char *ibufp, *obufp; int nibuf, nobuf; int flags; int pty_readable, stdin_readable; struct timeval lasttime; FILE *recordf = NULL; int ilevel, olevel, max_level; struct timeval levelt, tout, *top; extern u_char inpacket_buf[]; /* * Reset signal handlers. */ signal(SIGHUP, SIG_IGN); /* Hangup */ signal(SIGINT, SIG_DFL); /* Interrupt */ signal(SIGTERM, SIG_DFL); /* Terminate */ signal(SIGCHLD, SIG_DFL); signal(SIGUSR1, SIG_DFL); signal(SIGUSR2, SIG_DFL); signal(SIGABRT, SIG_DFL); signal(SIGALRM, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGILL, SIG_DFL); signal(SIGPIPE, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGSEGV, SIG_DFL); #ifdef SIGBUS signal(SIGBUS, SIG_DFL); #endif #ifdef SIGEMT signal(SIGEMT, SIG_DFL); #endif #ifdef SIGPOLL signal(SIGPOLL, SIG_DFL); #endif #ifdef SIGPROF signal(SIGPROF, SIG_DFL); #endif #ifdef SIGSYS signal(SIGSYS, SIG_DFL); #endif #ifdef SIGTRAP signal(SIGTRAP, SIG_DFL); #endif #ifdef SIGVTALRM signal(SIGVTALRM, SIG_DFL); #endif #ifdef SIGXCPU signal(SIGXCPU, SIG_DFL); #endif #ifdef SIGXFSZ signal(SIGXFSZ, SIG_DFL); #endif /* * Check that the fds won't overrun the fd_sets */ if (ifd >= FD_SETSIZE || ofd >= FD_SETSIZE || pty_master >= FD_SETSIZE) fatal("internal error: file descriptor too large (%d, %d, %d)", ifd, ofd, pty_master); /* * Open the record file if required. */ if (record_file != NULL) { recordf = fopen(record_file, "a"); if (recordf == NULL) error("Couldn't create record file %s: %m", record_file); } /* set all the fds to non-blocking mode */ flags = fcntl(pty_master, F_GETFL); if (flags == -1 || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1) warn("couldn't set pty master to nonblock: %m"); flags = fcntl(ifd, F_GETFL); if (flags == -1 || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1) warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty")); if (ofd != ifd) { flags = fcntl(ofd, F_GETFL); if (flags == -1 || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1) warn("couldn't set stdout to nonblock: %m"); } nibuf = nobuf = 0; ibufp = obufp = NULL; pty_readable = stdin_readable = 1; ilevel = olevel = 0; gettimeofday(&levelt, NULL); if (max_data_rate) { max_level = max_data_rate / 10; if (max_level < 100) max_level = 100; } else max_level = PPP_MRU + PPP_HDRLEN + 1; nfds = (ofd > pty_master? ofd: pty_master) + 1; if (recordf != NULL) { gettimeofday(&lasttime, NULL); putc(7, recordf); /* put start marker */ putc(lasttime.tv_sec >> 24, recordf); putc(lasttime.tv_sec >> 16, recordf); putc(lasttime.tv_sec >> 8, recordf); putc(lasttime.tv_sec, recordf); lasttime.tv_usec = 0; } while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) { top = 0; tout.tv_sec = 0; tout.tv_usec = 10000; FD_ZERO(&ready); FD_ZERO(&writey); if (nibuf != 0) { if (ilevel >= max_level) top = &tout; else FD_SET(pty_master, &writey); } else if (stdin_readable) FD_SET(ifd, &ready); if (nobuf != 0) { if (olevel >= max_level) top = &tout; else FD_SET(ofd, &writey); } else if (pty_readable) FD_SET(pty_master, &ready); if (select(nfds, &ready, &writey, NULL, top) < 0) { if (errno != EINTR) fatal("select"); continue; } if (max_data_rate) { double dt; int nbt; struct timeval now; gettimeofday(&now, NULL); dt = (now.tv_sec - levelt.tv_sec + (now.tv_usec - levelt.tv_usec) / 1e6); nbt = (int)(dt * max_data_rate); ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt; olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt; levelt = now; } else ilevel = olevel = 0; if (FD_ISSET(ifd, &ready)) { ibufp = inpacket_buf; nibuf = read(ifd, ibufp, PPP_MRU + PPP_HDRLEN); if (nibuf < 0 && errno == EIO) nibuf = 0; if (nibuf < 0) { if (!(errno == EINTR || errno == EAGAIN)) { error("Error reading standard input: %m"); break; } nibuf = 0; } else if (nibuf == 0) { /* end of file from stdin */ stdin_readable = 0; if (recordf) if (!record_write(recordf, 4, NULL, 0, &lasttime)) recordf = NULL; } else { FD_SET(pty_master, &writey); if (recordf) if (!record_write(recordf, 2, ibufp, nibuf, &lasttime)) recordf = NULL; } } if (FD_ISSET(pty_master, &ready)) { obufp = outpacket_buf; nobuf = read(pty_master, obufp, PPP_MRU + PPP_HDRLEN); if (nobuf < 0 && errno == EIO) nobuf = 0; if (nobuf < 0) { if (!(errno == EINTR || errno == EAGAIN)) { error("Error reading pseudo-tty master: %m"); break; } nobuf = 0; } else if (nobuf == 0) { /* end of file from the pty - slave side has closed */ pty_readable = 0; stdin_readable = 0; /* pty is not writable now */ nibuf = 0; close(ofd); if (recordf) if (!record_write(recordf, 3, NULL, 0, &lasttime)) recordf = NULL; } else { FD_SET(ofd, &writey); if (recordf) if (!record_write(recordf, 1, obufp, nobuf, &lasttime)) recordf = NULL; } } else if (!stdin_readable) pty_readable = 0; if (FD_ISSET(ofd, &writey)) { n = nobuf; if (olevel + n > max_level) n = max_level - olevel; n = write(ofd, obufp, n); if (n < 0) { if (errno == EIO) { pty_readable = 0; nobuf = 0; } else if (errno != EAGAIN && errno != EINTR) { error("Error writing standard output: %m"); break; } } else { obufp += n; nobuf -= n; olevel += n; } } if (FD_ISSET(pty_master, &writey)) { n = nibuf; if (ilevel + n > max_level) n = max_level - ilevel; n = write(pty_master, ibufp, n); if (n < 0) { if (errno == EIO) { stdin_readable = 0; nibuf = 0; } else if (errno != EAGAIN && errno != EINTR) { error("Error writing pseudo-tty master: %m"); break; } } else { ibufp += n; nibuf -= n; ilevel += n; } } } exit(0); } static int record_write(f, code, buf, nb, tp) FILE *f; int code; u_char *buf; int nb; struct timeval *tp; { struct timeval now; int diff; gettimeofday(&now, NULL); now.tv_usec /= 100000; /* actually 1/10 s, not usec now */ diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec); if (diff > 0) { if (diff > 255) { putc(5, f); putc(diff >> 24, f); putc(diff >> 16, f); putc(diff >> 8, f); putc(diff, f); } else { putc(6, f); putc(diff, f); } *tp = now; } putc(code, f); if (buf != NULL) { putc(nb >> 8, f); putc(nb, f); fwrite(buf, nb, 1, f); } fflush(f); if (ferror(f)) { error("Error writing record file: %m"); return 0; } return 1; } ppp-2.4.5/pppd/upap.c000066400000000000000000000366151130035057700144110ustar00rootroot00000000000000/* * upap.c - User/Password Authentication Protocol. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: upap.c,v 1.30 2005/07/13 10:41:58 paulus Exp $" /* * TODO: */ #include #include #include "pppd.h" #include "upap.h" static const char rcsid[] = RCSID; static bool hide_password = 1; /* * Command-line options. */ static option_t pap_option_list[] = { { "hide-password", o_bool, &hide_password, "Don't output passwords to log", OPT_PRIO | 1 }, { "show-password", o_bool, &hide_password, "Show password string in debug log messages", OPT_PRIOSUB | 0 }, { "pap-restart", o_int, &upap[0].us_timeouttime, "Set retransmit timeout for PAP", OPT_PRIO }, { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, "Set max number of transmissions for auth-reqs", OPT_PRIO }, { "pap-timeout", o_int, &upap[0].us_reqtimeout, "Set time limit for peer PAP authentication", OPT_PRIO }, { NULL } }; /* * Protocol entry points. */ static void upap_init __P((int)); static void upap_lowerup __P((int)); static void upap_lowerdown __P((int)); static void upap_input __P((int, u_char *, int)); static void upap_protrej __P((int)); static int upap_printpkt __P((u_char *, int, void (*) __P((void *, char *, ...)), void *)); struct protent pap_protent = { PPP_PAP, upap_init, upap_input, upap_protrej, upap_lowerup, upap_lowerdown, NULL, NULL, upap_printpkt, NULL, 1, "PAP", NULL, pap_option_list, NULL, NULL, NULL }; upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ static void upap_timeout __P((void *)); static void upap_reqtimeout __P((void *)); static void upap_rauthreq __P((upap_state *, u_char *, int, int)); static void upap_rauthack __P((upap_state *, u_char *, int, int)); static void upap_rauthnak __P((upap_state *, u_char *, int, int)); static void upap_sauthreq __P((upap_state *)); static void upap_sresp __P((upap_state *, int, int, char *, int)); /* * upap_init - Initialize a UPAP unit. */ static 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; u->us_reqtimeout = UPAP_DEFREQTIME; } /* * 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; if (u->us_reqtimeout > 0) TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); } /* * upap_timeout - Retransmission timer for sending auth-reqs expired. */ static void upap_timeout(arg) void *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 */ error("No response to PAP authenticate-requests"); u->us_clientstate = UPAPCS_BADAUTH; auth_withpeer_fail(u->us_unit, PPP_PAP); return; } upap_sauthreq(u); /* Send Authenticate-Request */ } /* * upap_reqtimeout - Give up waiting for the peer to send an auth-req. */ static void upap_reqtimeout(arg) void *arg; { upap_state *u = (upap_state *) arg; if (u->us_serverstate != UPAPSS_LISTEN) return; /* huh?? */ auth_peer_fail(u->us_unit, PPP_PAP); u->us_serverstate = UPAPSS_BADAUTH; } /* * upap_lowerup - The lower layer is up. * * Start authenticating if pending. */ static 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; if (u->us_reqtimeout > 0) TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); } } /* * upap_lowerdown - The lower layer is down. * * Cancel all timeouts. */ static void upap_lowerdown(unit) int unit; { upap_state *u = &upap[unit]; if (u->us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) UNTIMEOUT(upap_reqtimeout, u); 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. */ static void upap_protrej(unit) int unit; { upap_state *u = &upap[unit]; if (u->us_clientstate == UPAPCS_AUTHREQ) { error("PAP authentication failed due to protocol-reject"); auth_withpeer_fail(unit, PPP_PAP); } if (u->us_serverstate == UPAPSS_LISTEN) { error("PAP authentication of peer failed (protocol-reject)"); auth_peer_fail(unit, PPP_PAP); } upap_lowerdown(unit); } /* * upap_input - Input UPAP packet. */ static 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(("pap_input: rcvd short header.")); return; } GETCHAR(code, inp); GETCHAR(id, inp); GETSHORT(len, inp); if (len < UPAP_HEADERLEN) { UPAPDEBUG(("pap_input: rcvd illegal length.")); return; } if (len > l) { UPAPDEBUG(("pap_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; char rhostname[256]; int retcode; char *msg; int msglen; 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 < 1) { UPAPDEBUG(("pap_rauth: rcvd short packet.")); return; } GETCHAR(ruserlen, inp); len -= sizeof (u_char) + ruserlen + sizeof (u_char); if (len < 0) { UPAPDEBUG(("pap_rauth: rcvd short packet.")); return; } ruser = (char *) inp; INCPTR(ruserlen, inp); GETCHAR(rpasswdlen, inp); if (len < rpasswdlen) { UPAPDEBUG(("pap_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); BZERO(rpasswd, rpasswdlen); /* * Check remote number authorization. A plugin may have filled in * the remote number or added an allowed number, and rather than * return an authenticate failure, is leaving it for us to verify. */ if (retcode == UPAP_AUTHACK) { if (!auth_number()) { /* We do not want to leak info about the pap result. */ retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */ warn("calling number %q is not authorized", remote_number); } } msglen = strlen(msg); if (msglen > 255) msglen = 255; upap_sresp(u, retcode, id, msg, msglen); /* Null terminate and clean remote name. */ slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser); if (retcode == UPAP_AUTHACK) { u->us_serverstate = UPAPSS_OPEN; notice("PAP peer authentication succeeded for %q", rhostname); auth_peer_success(u->us_unit, PPP_PAP, 0, ruser, ruserlen); } else { u->us_serverstate = UPAPSS_BADAUTH; warn("PAP peer authentication failed for %q", rhostname); auth_peer_fail(u->us_unit, PPP_PAP); } if (u->us_reqtimeout > 0) UNTIMEOUT(upap_reqtimeout, u); } /* * 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; if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ return; /* * Parse message. */ if (len < 1) { UPAPDEBUG(("pap_rauthack: ignoring missing msg-length.")); } else { GETCHAR(msglen, inp); if (msglen > 0) { len -= sizeof (u_char); if (len < msglen) { UPAPDEBUG(("pap_rauthack: rcvd short packet.")); return; } msg = (char *) inp; PRINTMSG(msg, msglen); } } u->us_clientstate = UPAPCS_OPEN; auth_withpeer_success(u->us_unit, PPP_PAP, 0); } /* * upap_rauthnak - Receive Authenticate-Nak. */ static void upap_rauthnak(u, inp, id, len) upap_state *u; u_char *inp; int id; int len; { u_char msglen; char *msg; if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ return; /* * Parse message. */ if (len < 1) { UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length.")); } else { GETCHAR(msglen, inp); if (msglen > 0) { len -= sizeof (u_char); if (len < msglen) { UPAPDEBUG(("pap_rauthnak: rcvd short packet.")); return; } msg = (char *) inp; PRINTMSG(msg, msglen); } } u->us_clientstate = UPAPCS_BADAUTH; error("PAP authentication failed"); auth_withpeer_fail(u->us_unit, PPP_PAP); } /* * 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, PPP_PAP); 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 + PPP_HDRLEN); TIMEOUT(upap_timeout, 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, PPP_PAP); PUTCHAR(code, outp); PUTCHAR(id, outp); PUTSHORT(outlen, outp); PUTCHAR(msglen, outp); BCOPY(msg, outp, msglen); output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN); } /* * upap_printpkt - print the contents of a PAP packet. */ static char *upap_codenames[] = { "AuthReq", "AuthAck", "AuthNak" }; static int upap_printpkt(p, plen, printer, arg) u_char *p; int plen; void (*printer) __P((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="); if (!hide_password) print_string(pwd, wlen, printer, arg); else 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, " "); 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; } ppp-2.4.5/pppd/upap.h000066400000000000000000000074431130035057700144130ustar00rootroot00000000000000/* * upap.h - User/Password Authentication Protocol definitions. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: upap.h,v 1.8 2002/12/04 23:03:33 paulus Exp $ */ /* * Packet header = Code, id, length. */ #define UPAP_HEADERLEN 4 /* * 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 (seconds) for auth-req retrans. */ int us_transmits; /* Number of auth-reqs sent */ int us_maxtransmits; /* Maximum number of auth-reqs to send */ int us_reqtimeout; /* Time to wait for auth-req from peer */ } 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 (seconds) for retransmitting req */ #define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ extern upap_state upap[]; void upap_authwithpeer __P((int, char *, char *)); void upap_authpeer __P((int)); extern struct protent pap_protent; ppp-2.4.5/pppd/utils.c000066400000000000000000000510731130035057700145770ustar00rootroot00000000000000/* * utils.c - various utility functions used in pppd. * * Copyright (c) 1999-2002 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 3. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RCSID "$Id: utils.c,v 1.25 2008/06/03 12:06:37 paulus Exp $" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SVR4 #include #endif #include "pppd.h" #include "fsm.h" #include "lcp.h" static const char rcsid[] = RCSID; #if defined(SUNOS4) extern char *strerror(); #endif static void logit __P((int, char *, va_list)); static void log_write __P((int, char *)); static void vslp_printer __P((void *, char *, ...)); static void format_packet __P((u_char *, int, void (*) (void *, char *, ...), void *)); struct buffer_info { char *ptr; int len; }; /* * strlcpy - like strcpy/strncpy, doesn't overflow destination buffer, * always leaves destination null-terminated (for len > 0). */ size_t strlcpy(dest, src, len) char *dest; const char *src; size_t len; { size_t ret = strlen(src); if (len != 0) { if (ret < len) strcpy(dest, src); else { strncpy(dest, src, len - 1); dest[len-1] = 0; } } return ret; } /* * strlcat - like strcat/strncat, doesn't overflow destination buffer, * always leaves destination null-terminated (for len > 0). */ size_t strlcat(dest, src, len) char *dest; const char *src; size_t len; { size_t dlen = strlen(dest); return dlen + strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0)); } /* * slprintf - format a message into a buffer. Like sprintf except we * also specify the length of the output buffer, and we handle * %m (error message), %v (visible string), * %q (quoted string), %t (current time) and %I (IP address) formats. * Doesn't do floating-point formats. * Returns the number of chars put into buf. */ int slprintf __V((char *buf, int buflen, char *fmt, ...)) { va_list args; int n; #if defined(__STDC__) va_start(args, fmt); #else char *buf; int buflen; char *fmt; va_start(args); buf = va_arg(args, char *); buflen = va_arg(args, int); fmt = va_arg(args, char *); #endif n = vslprintf(buf, buflen, fmt, args); va_end(args); return n; } /* * vslprintf - like slprintf, takes a va_list instead of a list of args. */ #define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) int vslprintf(buf, buflen, fmt, args) char *buf; int buflen; char *fmt; va_list args; { int c, i, n; int width, prec, fillch; int base, len, neg, quoted; unsigned long val = 0; char *str, *f, *buf0; unsigned char *p; char num[32]; time_t t; u_int32_t ip; static char hexchars[] = "0123456789abcdef"; struct buffer_info bufinfo; buf0 = buf; --buflen; while (buflen > 0) { for (f = fmt; *f != '%' && *f != 0; ++f) ; if (f > fmt) { len = f - fmt; if (len > buflen) len = buflen; memcpy(buf, fmt, len); buf += len; buflen -= len; fmt = f; } if (*fmt == 0) break; c = *++fmt; width = 0; prec = -1; fillch = ' '; if (c == '0') { fillch = '0'; c = *++fmt; } if (c == '*') { width = va_arg(args, int); c = *++fmt; } else { while (isdigit(c)) { width = width * 10 + c - '0'; c = *++fmt; } } if (c == '.') { c = *++fmt; if (c == '*') { prec = va_arg(args, int); c = *++fmt; } else { prec = 0; while (isdigit(c)) { prec = prec * 10 + c - '0'; c = *++fmt; } } } str = 0; base = 0; neg = 0; ++fmt; switch (c) { case 'l': c = *fmt++; switch (c) { case 'd': val = va_arg(args, long); if (val < 0) { neg = 1; val = -val; } base = 10; break; case 'u': val = va_arg(args, unsigned long); base = 10; break; default: OUTCHAR('%'); OUTCHAR('l'); --fmt; /* so %lz outputs %lz etc. */ continue; } break; case 'd': i = va_arg(args, int); if (i < 0) { neg = 1; val = -i; } else val = i; base = 10; break; case 'u': val = va_arg(args, unsigned int); base = 10; break; case 'o': val = va_arg(args, unsigned int); base = 8; break; case 'x': case 'X': val = va_arg(args, unsigned int); base = 16; break; case 'p': val = (unsigned long) va_arg(args, void *); base = 16; neg = 2; break; case 's': str = va_arg(args, char *); break; case 'c': num[0] = va_arg(args, int); num[1] = 0; str = num; break; case 'm': str = strerror(errno); break; case 'I': ip = va_arg(args, u_int32_t); ip = ntohl(ip); slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); str = num; break; #if 0 /* not used, and breaks on S/390, apparently */ case 'r': f = va_arg(args, char *); #ifndef __powerpc__ n = vslprintf(buf, buflen + 1, f, va_arg(args, va_list)); #else /* On the powerpc, a va_list is an array of 1 structure */ n = vslprintf(buf, buflen + 1, f, va_arg(args, void *)); #endif buf += n; buflen -= n; continue; #endif case 't': time(&t); str = ctime(&t); str += 4; /* chop off the day name */ str[15] = 0; /* chop off year and newline */ break; case 'v': /* "visible" string */ case 'q': /* quoted string */ quoted = c == 'q'; p = va_arg(args, unsigned char *); if (fillch == '0' && prec >= 0) { n = prec; } else { n = strlen((char *)p); if (prec >= 0 && n > prec) n = prec; } while (n > 0 && buflen > 0) { c = *p++; --n; if (!quoted && c >= 0x80) { OUTCHAR('M'); OUTCHAR('-'); c -= 0x80; } if (quoted && (c == '"' || c == '\\')) OUTCHAR('\\'); if (c < 0x20 || (0x7f <= c && c < 0xa0)) { if (quoted) { OUTCHAR('\\'); switch (c) { case '\t': OUTCHAR('t'); break; case '\n': OUTCHAR('n'); break; case '\b': OUTCHAR('b'); break; case '\f': OUTCHAR('f'); break; default: OUTCHAR('x'); OUTCHAR(hexchars[c >> 4]); OUTCHAR(hexchars[c & 0xf]); } } else { if (c == '\t') OUTCHAR(c); else { OUTCHAR('^'); OUTCHAR(c ^ 0x40); } } } else OUTCHAR(c); } continue; case 'P': /* print PPP packet */ bufinfo.ptr = buf; bufinfo.len = buflen + 1; p = va_arg(args, unsigned char *); n = va_arg(args, int); format_packet(p, n, vslp_printer, &bufinfo); buf = bufinfo.ptr; buflen = bufinfo.len - 1; continue; case 'B': p = va_arg(args, unsigned char *); for (n = prec; n > 0; --n) { c = *p++; if (fillch == ' ') OUTCHAR(' '); OUTCHAR(hexchars[(c >> 4) & 0xf]); OUTCHAR(hexchars[c & 0xf]); } continue; default: *buf++ = '%'; if (c != '%') --fmt; /* so %z outputs %z etc. */ --buflen; continue; } if (base != 0) { str = num + sizeof(num); *--str = 0; while (str > num + neg) { *--str = hexchars[val % base]; val = val / base; if (--prec <= 0 && val == 0) break; } switch (neg) { case 1: *--str = '-'; break; case 2: *--str = 'x'; *--str = '0'; break; } len = num + sizeof(num) - 1 - str; } else { len = strlen(str); if (prec >= 0 && len > prec) len = prec; } if (width > 0) { if (width > buflen) width = buflen; if ((n = width - len) > 0) { buflen -= n; for (; n > 0; --n) *buf++ = fillch; } } if (len > buflen) len = buflen; memcpy(buf, str, len); buf += len; buflen -= len; } *buf = 0; return buf - buf0; } /* * vslp_printer - used in processing a %P format */ static void vslp_printer __V((void *arg, char *fmt, ...)) { int n; va_list pvar; struct buffer_info *bi; #if defined(__STDC__) va_start(pvar, fmt); #else void *arg; char *fmt; va_start(pvar); arg = va_arg(pvar, void *); fmt = va_arg(pvar, char *); #endif bi = (struct buffer_info *) arg; n = vslprintf(bi->ptr, bi->len, fmt, pvar); va_end(pvar); bi->ptr += n; bi->len -= n; } #ifdef unused /* * log_packet - format a packet and log it. */ void log_packet(p, len, prefix, level) u_char *p; int len; char *prefix; int level; { init_pr_log(prefix, level); format_packet(p, len, pr_log, &level); end_pr_log(); } #endif /* unused */ /* * format_packet - make a readable representation of a packet, * calling `printer(arg, format, ...)' to output it. */ static void format_packet(p, len, printer, arg) u_char *p; int len; void (*printer) __P((void *, char *, ...)); void *arg; { int i, n; u_short proto; struct protent *protp; if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) { p += 2; GETSHORT(proto, p); len -= PPP_HDRLEN; for (i = 0; (protp = protocols[i]) != NULL; ++i) if (proto == protp->protocol) break; if (protp != NULL) { printer(arg, "[%s", protp->name); n = (*protp->printpkt)(p, len, printer, arg); printer(arg, "]"); p += n; len -= n; } else { for (i = 0; (protp = protocols[i]) != NULL; ++i) if (proto == (protp->protocol & ~0x8000)) break; if (protp != 0 && protp->data_name != 0) { printer(arg, "[%s data]", protp->data_name); if (len > 8) printer(arg, "%.8B ...", p); else printer(arg, "%.*B", len, p); len = 0; } else printer(arg, "[proto=0x%x]", proto); } } if (len > 32) printer(arg, "%.32B ...", p); else printer(arg, "%.*B", len, p); } /* * init_pr_log, end_pr_log - initialize and finish use of pr_log. */ static char line[256]; /* line to be logged accumulated here */ static char *linep; /* current pointer within line */ static int llevel; /* level for logging */ void init_pr_log(prefix, level) const char *prefix; int level; { linep = line; if (prefix != NULL) { strlcpy(line, prefix, sizeof(line)); linep = line + strlen(line); } llevel = level; } void end_pr_log() { if (linep != line) { *linep = 0; log_write(llevel, line); } } /* * pr_log - printer routine for outputting to syslog */ void pr_log __V((void *arg, char *fmt, ...)) { int l, n; va_list pvar; char *p, *eol; char buf[256]; #if defined(__STDC__) va_start(pvar, fmt); #else void *arg; char *fmt; va_start(pvar); arg = va_arg(pvar, void *); fmt = va_arg(pvar, char *); #endif n = vslprintf(buf, sizeof(buf), fmt, pvar); va_end(pvar); p = buf; eol = strchr(buf, '\n'); if (linep != line) { l = (eol == NULL)? n: eol - buf; if (linep + l < line + sizeof(line)) { if (l > 0) { memcpy(linep, buf, l); linep += l; } if (eol == NULL) return; p = eol + 1; eol = strchr(p, '\n'); } *linep = 0; log_write(llevel, line); linep = line; } while (eol != NULL) { *eol = 0; log_write(llevel, p); p = eol + 1; eol = strchr(p, '\n'); } /* assumes sizeof(buf) <= sizeof(line) */ l = buf + n - p; if (l > 0) { memcpy(line, p, n); linep = line + l; } } /* * print_string - print a readable representation of a string using * printer. */ void print_string(p, len, printer, arg) char *p; int len; void (*printer) __P((void *, char *, ...)); void *arg; { int c; printer(arg, "\""); for (; len > 0; --len) { c = *p++; if (' ' <= c && c <= '~') { if (c == '\\' || c == '"') printer(arg, "\\"); printer(arg, "%c", c); } else { switch (c) { case '\n': printer(arg, "\\n"); break; case '\r': printer(arg, "\\r"); break; case '\t': printer(arg, "\\t"); break; default: printer(arg, "\\%.3o", c); } } } printer(arg, "\""); } /* * logit - does the hard work for fatal et al. */ static void logit(level, fmt, args) int level; char *fmt; va_list args; { int n; char buf[1024]; n = vslprintf(buf, sizeof(buf), fmt, args); log_write(level, buf); } static void log_write(level, buf) int level; char *buf; { syslog(level, "%s", buf); if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) { int n = strlen(buf); if (n > 0 && buf[n-1] == '\n') --n; if (write(log_to_fd, buf, n) != n || write(log_to_fd, "\n", 1) != 1) log_to_fd = -1; } } /* * fatal - log an error message and die horribly. */ void fatal __V((char *fmt, ...)) { va_list pvar; #if defined(__STDC__) va_start(pvar, fmt); #else char *fmt; va_start(pvar); fmt = va_arg(pvar, char *); #endif logit(LOG_ERR, fmt, pvar); va_end(pvar); die(1); /* as promised */ } /* * error - log an error message. */ void error __V((char *fmt, ...)) { va_list pvar; #if defined(__STDC__) va_start(pvar, fmt); #else char *fmt; va_start(pvar); fmt = va_arg(pvar, char *); #endif logit(LOG_ERR, fmt, pvar); va_end(pvar); ++error_count; } /* * warn - log a warning message. */ void warn __V((char *fmt, ...)) { va_list pvar; #if defined(__STDC__) va_start(pvar, fmt); #else char *fmt; va_start(pvar); fmt = va_arg(pvar, char *); #endif logit(LOG_WARNING, fmt, pvar); va_end(pvar); } /* * notice - log a notice-level message. */ void notice __V((char *fmt, ...)) { va_list pvar; #if defined(__STDC__) va_start(pvar, fmt); #else char *fmt; va_start(pvar); fmt = va_arg(pvar, char *); #endif logit(LOG_NOTICE, fmt, pvar); va_end(pvar); } /* * info - log an informational message. */ void info __V((char *fmt, ...)) { va_list pvar; #if defined(__STDC__) va_start(pvar, fmt); #else char *fmt; va_start(pvar); fmt = va_arg(pvar, char *); #endif logit(LOG_INFO, fmt, pvar); va_end(pvar); } /* * dbglog - log a debug message. */ void dbglog __V((char *fmt, ...)) { va_list pvar; #if defined(__STDC__) va_start(pvar, fmt); #else char *fmt; va_start(pvar); fmt = va_arg(pvar, char *); #endif logit(LOG_DEBUG, fmt, pvar); va_end(pvar); } /* * dump_packet - print out a packet in readable form if it is interesting. * Assumes len >= PPP_HDRLEN. */ void dump_packet(const char *tag, unsigned char *p, int len) { int proto; if (!debug) return; /* * don't print LCP echo request/reply packets if debug <= 1 * and the link is up. */ proto = (p[2] << 8) + p[3]; if (debug <= 1 && unsuccess == 0 && proto == PPP_LCP && len >= PPP_HDRLEN + HEADERLEN) { unsigned char *lcp = p + PPP_HDRLEN; int l = (lcp[2] << 8) + lcp[3]; if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP) && l >= HEADERLEN && l <= len - PPP_HDRLEN) return; } dbglog("%s %P", tag, p, len); } /* * complete_read - read a full `count' bytes from fd, * unless end-of-file or an error other than EINTR is encountered. */ ssize_t complete_read(int fd, void *buf, size_t count) { size_t done; ssize_t nb; char *ptr = buf; for (done = 0; done < count; ) { nb = read(fd, ptr, count - done); if (nb < 0) { if (errno == EINTR) continue; return -1; } if (nb == 0) break; done += nb; ptr += nb; } return done; } /* Procedures for locking the serial device using a lock file. */ #ifndef LOCK_DIR #ifdef __linux__ #define LOCK_DIR "/var/lock" #else #ifdef SVR4 #define LOCK_DIR "/var/spool/locks" #else #define LOCK_DIR "/var/spool/lock" #endif #endif #endif /* LOCK_DIR */ static char lock_file[MAXPATHLEN]; /* * lock - create a lock file for the named device */ int lock(dev) char *dev; { #ifdef LOCKLIB int result; result = mklock (dev, (void *) 0); if (result == 0) { strlcpy(lock_file, dev, sizeof(lock_file)); return 0; } if (result > 0) notice("Device %s is locked by pid %d", dev, result); else error("Can't create lock file %s", lock_file); return -1; #else /* LOCKLIB */ char lock_buffer[12]; int fd, pid, n; #ifdef SVR4 struct stat sbuf; if (stat(dev, &sbuf) < 0) { error("Can't get device number for %s: %m", dev); return -1; } if ((sbuf.st_mode & S_IFMT) != S_IFCHR) { error("Can't lock %s: not a character device", dev); return -1; } slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d", LOCK_DIR, major(sbuf.st_dev), major(sbuf.st_rdev), minor(sbuf.st_rdev)); #else char *p; char lockdev[MAXPATHLEN]; if ((p = strstr(dev, "dev/")) != NULL) { dev = p + 4; strncpy(lockdev, dev, MAXPATHLEN-1); lockdev[MAXPATHLEN-1] = 0; while ((p = strrchr(lockdev, '/')) != NULL) { *p = '_'; } dev = lockdev; } else if ((p = strrchr(dev, '/')) != NULL) dev = p + 1; slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev); #endif while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) { if (errno != EEXIST) { error("Can't create lock file %s: %m", lock_file); break; } /* Read the lock file to find out who has the device locked. */ fd = open(lock_file, O_RDONLY, 0); if (fd < 0) { if (errno == ENOENT) /* This is just a timing problem. */ continue; error("Can't open existing lock file %s: %m", lock_file); break; } #ifndef LOCK_BINARY n = read(fd, lock_buffer, 11); #else n = read(fd, &pid, sizeof(pid)); #endif /* LOCK_BINARY */ close(fd); fd = -1; if (n <= 0) { error("Can't read pid from lock file %s", lock_file); break; } /* See if the process still exists. */ #ifndef LOCK_BINARY lock_buffer[n] = 0; pid = atoi(lock_buffer); #endif /* LOCK_BINARY */ if (pid == getpid()) return 1; /* somebody else locked it for us */ if (pid == 0 || (kill(pid, 0) == -1 && errno == ESRCH)) { if (unlink (lock_file) == 0) { notice("Removed stale lock on %s (pid %d)", dev, pid); continue; } warn("Couldn't remove stale lock on %s", dev); } else notice("Device %s is locked by pid %d", dev, pid); break; } if (fd < 0) { lock_file[0] = 0; return -1; } pid = getpid(); #ifndef LOCK_BINARY slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid); write (fd, lock_buffer, 11); #else write(fd, &pid, sizeof (pid)); #endif close(fd); return 0; #endif } /* * relock - called to update our lockfile when we are about to detach, * thus changing our pid (we fork, the child carries on, and the parent dies). * Note that this is called by the parent, with pid equal to the pid * of the child. This avoids a potential race which would exist if * we had the child rewrite the lockfile (the parent might die first, * and another process could think the lock was stale if it checked * between when the parent died and the child rewrote the lockfile). */ int relock(pid) int pid; { #ifdef LOCKLIB /* XXX is there a way to do this? */ return -1; #else /* LOCKLIB */ int fd; char lock_buffer[12]; if (lock_file[0] == 0) return -1; fd = open(lock_file, O_WRONLY, 0); if (fd < 0) { error("Couldn't reopen lock file %s: %m", lock_file); lock_file[0] = 0; return -1; } #ifndef LOCK_BINARY slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid); write (fd, lock_buffer, 11); #else write(fd, &pid, sizeof(pid)); #endif /* LOCK_BINARY */ close(fd); return 0; #endif /* LOCKLIB */ } /* * unlock - remove our lockfile */ void unlock() { if (lock_file[0]) { #ifdef LOCKLIB (void) rmlock(lock_file, (void *) 0); #else unlink(lock_file); #endif lock_file[0] = 0; } } ppp-2.4.5/pppdump/000077500000000000000000000000001130035057700140075ustar00rootroot00000000000000ppp-2.4.5/pppdump/.gitignore000066400000000000000000000000101130035057700157660ustar00rootroot00000000000000pppdump ppp-2.4.5/pppdump/Makefile.linux000066400000000000000000000006111130035057700166030ustar00rootroot00000000000000DESTDIR = $(INSTROOT)@DESTDIR@ BINDIR = $(DESTDIR)/sbin MANDIR = $(DESTDIR)/share/man/man8 CFLAGS= -O -I../include/net OBJS = pppdump.o bsd-comp.o deflate.o zlib.o INSTALL= install all: pppdump pppdump: $(OBJS) $(CC) -o pppdump $(OBJS) clean: rm -f pppdump $(OBJS) *~ install: mkdir -p $(BINDIR) $(MANDIR) $(INSTALL) -s -c pppdump $(BINDIR) $(INSTALL) -c -m 444 pppdump.8 $(MANDIR) ppp-2.4.5/pppdump/Makefile.sol2000066400000000000000000000006051130035057700163260ustar00rootroot00000000000000# # pppdump Makefile for SVR4 systems # $Id: Makefile.sol2,v 1.4 2002/09/07 05:15:25 carlsonj Exp $ # include ../Makedefs.com CFLAGS= $(COPTS) -I../include/net OBJS = pppdump.o bsd-comp.o deflate.o zlib.o all: pppdump pppdump: $(OBJS) $(CC) -o pppdump $(OBJS) clean: rm -f $(OBJS) pppdump *~ install: $(INSTALL) -f $(BINDIR) pppdump $(INSTALL) -m 444 -f $(MANDIR)/man8 pppdump.8 ppp-2.4.5/pppdump/bsd-comp.c000066400000000000000000000502401130035057700156600ustar00rootroot00000000000000/* Because this code is derived from the 4.3BSD compress source: * * * Copyright (c) 1985, 1986 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * James A. Woods, derived from original work by Spencer Thomas * and Joseph Orost. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: bsd-comp.c,v 1.4 2004/01/17 05:47:55 carlsonj Exp $ */ #include #include #include #include #include #include "ppp_defs.h" #include "ppp-comp.h" #if DO_BSD_COMPRESS /* * PPP "BSD compress" compression * The differences between this compression and the classic BSD LZW * source are obvious from the requirement that the classic code worked * with files while this handles arbitrarily long streams that * are broken into packets. They are: * * When the code size expands, a block of junk is not emitted by * the compressor and not expected by the decompressor. * * New codes are not necessarily assigned every time an old * code is output by the compressor. This is because a packet * end forces a code to be emitted, but does not imply that a * new sequence has been seen. * * The compression ratio is checked at the first end of a packet * after the appropriate gap. Besides simplifying and speeding * things up, this makes it more likely that the transmitter * and receiver will agree when the dictionary is cleared when * compression is not going well. */ /* * A dictionary for doing BSD compress. */ struct bsd_db { int totlen; /* length of this structure */ u_int hsize; /* size of the hash table */ u_char hshift; /* used in hash function */ u_char n_bits; /* current bits/code */ u_char maxbits; u_char debug; u_char unit; u_short seqno; /* sequence number of next packet */ u_int hdrlen; /* header length to preallocate */ u_int mru; u_int maxmaxcode; /* largest valid code */ u_int max_ent; /* largest code in use */ u_int in_count; /* uncompressed bytes, aged */ u_int bytes_out; /* compressed bytes, aged */ u_int ratio; /* recent compression ratio */ u_int checkpoint; /* when to next check the ratio */ u_int clear_count; /* times dictionary cleared */ u_int incomp_count; /* incompressible packets */ u_int incomp_bytes; /* incompressible bytes */ u_int uncomp_count; /* uncompressed packets */ u_int uncomp_bytes; /* uncompressed bytes */ u_int comp_count; /* compressed packets */ u_int comp_bytes; /* compressed bytes */ u_short *lens; /* array of lengths of codes */ struct bsd_dict { union { /* hash value */ u_int32_t fcode; struct { #ifdef BSD_LITTLE_ENDIAN u_short prefix; /* preceding code */ u_char suffix; /* last character of new code */ u_char pad; #else u_char pad; u_char suffix; /* last character of new code */ u_short prefix; /* preceding code */ #endif } hs; } f; u_short codem1; /* output of hash table -1 */ u_short cptr; /* map code to hash table entry */ } dict[1]; }; #define BSD_OVHD 2 /* BSD compress overhead/packet */ #define BSD_INIT_BITS BSD_MIN_BITS static void *bsd_decomp_alloc __P((u_char *options, int opt_len)); static void bsd_free __P((void *state)); static int bsd_decomp_init __P((void *state, u_char *options, int opt_len, int unit, int hdrlen, int mru, int debug)); static void bsd_incomp __P((void *state, u_char *dmsg, int len)); static int bsd_decompress __P((void *state, u_char *cmp, int inlen, u_char *dmp, int *outlen)); static void bsd_reset __P((void *state)); static void bsd_comp_stats __P((void *state, struct compstat *stats)); /* * Exported procedures. */ struct compressor ppp_bsd_compress = { CI_BSD_COMPRESS, /* compress_proto */ bsd_decomp_alloc, /* decomp_alloc */ bsd_free, /* decomp_free */ bsd_decomp_init, /* decomp_init */ bsd_reset, /* decomp_reset */ bsd_decompress, /* decompress */ bsd_incomp, /* incomp */ bsd_comp_stats, /* decomp_stat */ }; /* * the next two codes should not be changed lightly, as they must not * lie within the contiguous general code space. */ #define CLEAR 256 /* table clear output code */ #define FIRST 257 /* first free entry */ #define LAST 255 #define MAXCODE(b) ((1 << (b)) - 1) #define BADCODEM1 MAXCODE(BSD_MAX_BITS) #define BSD_HASH(prefix,suffix,hshift) ((((u_int32_t)(suffix)) << (hshift)) \ ^ (u_int32_t)(prefix)) #define BSD_KEY(prefix,suffix) ((((u_int32_t)(suffix)) << 16) \ + (u_int32_t)(prefix)) #define CHECK_GAP 10000 /* Ratio check interval */ #define RATIO_SCALE_LOG 8 #define RATIO_SCALE (1<>RATIO_SCALE_LOG) /* * clear the dictionary */ static void bsd_clear(db) struct bsd_db *db; { db->clear_count++; db->max_ent = FIRST-1; db->n_bits = BSD_INIT_BITS; db->ratio = 0; db->bytes_out = 0; db->in_count = 0; db->checkpoint = CHECK_GAP; } /* * If the dictionary is full, then see if it is time to reset it. * * Compute the compression ratio using fixed-point arithmetic * with 8 fractional bits. * * Since we have an infinite stream instead of a single file, * watch only the local compression ratio. * * Since both peers must reset the dictionary at the same time even in * the absence of CLEAR codes (while packets are incompressible), they * must compute the same ratio. */ static int /* 1=output CLEAR */ bsd_check(db) struct bsd_db *db; { u_int new_ratio; if (db->in_count >= db->checkpoint) { /* age the ratio by limiting the size of the counts */ if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX) { db->in_count -= db->in_count/4; db->bytes_out -= db->bytes_out/4; } db->checkpoint = db->in_count + CHECK_GAP; if (db->max_ent >= db->maxmaxcode) { /* Reset the dictionary only if the ratio is worse, * or if it looks as if it has been poisoned * by incompressible data. * * This does not overflow, because * db->in_count <= RATIO_MAX. */ new_ratio = db->in_count << RATIO_SCALE_LOG; if (db->bytes_out != 0) new_ratio /= db->bytes_out; if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE) { bsd_clear(db); return 1; } db->ratio = new_ratio; } } return 0; } /* * Return statistics. */ static void bsd_comp_stats(state, stats) void *state; struct compstat *stats; { struct bsd_db *db = (struct bsd_db *) state; u_int out; stats->unc_bytes = db->uncomp_bytes; stats->unc_packets = db->uncomp_count; stats->comp_bytes = db->comp_bytes; stats->comp_packets = db->comp_count; stats->inc_bytes = db->incomp_bytes; stats->inc_packets = db->incomp_count; stats->ratio = db->in_count; out = db->bytes_out; if (stats->ratio <= 0x7fffff) stats->ratio <<= 8; else out >>= 8; if (out != 0) stats->ratio /= out; } /* * Reset state, as on a CCP ResetReq. */ static void bsd_reset(state) void *state; { struct bsd_db *db = (struct bsd_db *) state; db->seqno = 0; bsd_clear(db); db->clear_count = 0; } /* * Allocate space for a (de) compressor. */ static void * bsd_alloc(options, opt_len, decomp) u_char *options; int opt_len, decomp; { int bits; u_int newlen, hsize, hshift, maxmaxcode; struct bsd_db *db; if (opt_len != 3 || options[0] != CI_BSD_COMPRESS || options[1] != 3 || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION) return NULL; bits = BSD_NBITS(options[2]); switch (bits) { case 9: /* needs 82152 for both directions */ case 10: /* needs 84144 */ case 11: /* needs 88240 */ case 12: /* needs 96432 */ hsize = 5003; hshift = 4; break; case 13: /* needs 176784 */ hsize = 9001; hshift = 5; break; case 14: /* needs 353744 */ hsize = 18013; hshift = 6; break; case 15: /* needs 691440 */ hsize = 35023; hshift = 7; break; case 16: /* needs 1366160--far too much, */ /* hsize = 69001; */ /* and 69001 is too big for cptr */ /* hshift = 8; */ /* in struct bsd_db */ /* break; */ default: return NULL; } maxmaxcode = MAXCODE(bits); newlen = sizeof(*db) + (hsize-1) * (sizeof(db->dict[0])); db = (struct bsd_db *) malloc(newlen); if (!db) return NULL; memset(db, 0, sizeof(*db) - sizeof(db->dict)); if (!decomp) { db->lens = NULL; } else { db->lens = (u_short *) malloc((maxmaxcode+1) * sizeof(db->lens[0])); if (!db->lens) { free(db); return NULL; } } db->totlen = newlen; db->hsize = hsize; db->hshift = hshift; db->maxmaxcode = maxmaxcode; db->maxbits = bits; return (void *) db; } static void bsd_free(state) void *state; { struct bsd_db *db = (struct bsd_db *) state; if (db->lens) free(db->lens); free(db); } static void * bsd_decomp_alloc(options, opt_len) u_char *options; int opt_len; { return bsd_alloc(options, opt_len, 1); } /* * Initialize the database. */ static int bsd_init(db, options, opt_len, unit, hdrlen, mru, debug, decomp) struct bsd_db *db; u_char *options; int opt_len, unit, hdrlen, mru, debug, decomp; { int i; if (opt_len < CILEN_BSD_COMPRESS || options[0] != CI_BSD_COMPRESS || options[1] != CILEN_BSD_COMPRESS || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION || BSD_NBITS(options[2]) != db->maxbits || decomp && db->lens == NULL) return 0; if (decomp) { i = LAST+1; while (i != 0) db->lens[--i] = 1; } i = db->hsize; while (i != 0) { db->dict[--i].codem1 = BADCODEM1; db->dict[i].cptr = 0; } db->unit = unit; db->hdrlen = hdrlen; db->mru = mru; if (debug) db->debug = 1; bsd_reset(db); return 1; } static int bsd_decomp_init(state, options, opt_len, unit, hdrlen, mru, debug) void *state; u_char *options; int opt_len, unit, hdrlen, mru, debug; { return bsd_init((struct bsd_db *) state, options, opt_len, unit, hdrlen, mru, debug, 1); } /* * Update the "BSD Compress" dictionary on the receiver for * incompressible data by pretending to compress the incoming data. */ static void bsd_incomp(state, dmsg, mlen) void *state; u_char *dmsg; int mlen; { struct bsd_db *db = (struct bsd_db *) state; u_int hshift = db->hshift; u_int max_ent = db->max_ent; u_int n_bits = db->n_bits; struct bsd_dict *dictp; u_int32_t fcode; u_char c; long hval, disp; int slen, ilen; u_int bitno = 7; u_char *rptr; u_int ent; rptr = dmsg; ent = rptr[0]; /* get the protocol */ if (ent == 0) { ++rptr; --mlen; ent = rptr[0]; } if ((ent & 1) == 0 || ent < 0x21 || ent > 0xf9) return; db->seqno++; ilen = 1; /* count the protocol as 1 byte */ ++rptr; slen = dmsg + mlen - rptr; ilen += slen; for (; slen > 0; --slen) { c = *rptr++; fcode = BSD_KEY(ent, c); hval = BSD_HASH(ent, c, hshift); dictp = &db->dict[hval]; /* validate and then check the entry */ if (dictp->codem1 >= max_ent) goto nomatch; if (dictp->f.fcode == fcode) { ent = dictp->codem1+1; continue; /* found (prefix,suffix) */ } /* continue probing until a match or invalid entry */ disp = (hval == 0) ? 1 : hval; do { hval += disp; if (hval >= db->hsize) hval -= db->hsize; dictp = &db->dict[hval]; if (dictp->codem1 >= max_ent) goto nomatch; } while (dictp->f.fcode != fcode); ent = dictp->codem1+1; continue; /* finally found (prefix,suffix) */ nomatch: /* output (count) the prefix */ bitno += n_bits; /* code -> hashtable */ if (max_ent < db->maxmaxcode) { struct bsd_dict *dictp2; /* expand code size if needed */ if (max_ent >= MAXCODE(n_bits)) db->n_bits = ++n_bits; /* Invalidate previous hash table entry * assigned this code, and then take it over. */ dictp2 = &db->dict[max_ent+1]; if (db->dict[dictp2->cptr].codem1 == max_ent) db->dict[dictp2->cptr].codem1 = BADCODEM1; dictp2->cptr = hval; dictp->codem1 = max_ent; dictp->f.fcode = fcode; db->max_ent = ++max_ent; db->lens[max_ent] = db->lens[ent]+1; } ent = c; } bitno += n_bits; /* output (count) the last code */ db->bytes_out += bitno/8; db->in_count += ilen; (void)bsd_check(db); ++db->incomp_count; db->incomp_bytes += ilen; ++db->uncomp_count; db->uncomp_bytes += ilen; /* Increase code size if we would have without the packet * boundary and as the decompressor will. */ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) db->n_bits++; } /* * Decompress "BSD Compress" * * Because of patent problems, we return DECOMP_ERROR for errors * found by inspecting the input data and for system problems, but * DECOMP_FATALERROR for any errors which could possibly be said to * be being detected "after" decompression. For DECOMP_ERROR, * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be * infringing a patent of Motorola's if we do, so we take CCP down * instead. * * Given that the frame has the correct sequence number and a good FCS, * errors such as invalid codes in the input most likely indicate a * bug, so we return DECOMP_FATALERROR for them in order to turn off * compression, even though they are detected by inspecting the input. */ static int bsd_decompress(state, cmsg, inlen, dmp, outlenp) void *state; u_char *cmsg, *dmp; int inlen, *outlenp; { struct bsd_db *db = (struct bsd_db *) state; u_int max_ent = db->max_ent; u_int32_t accm = 0; u_int bitno = 32; /* 1st valid bit in accm */ u_int n_bits = db->n_bits; u_int tgtbitno = 32-n_bits; /* bitno when we have a code */ struct bsd_dict *dictp; int explen, i, seq, len; u_int incode, oldcode, finchar; u_char *p, *rptr, *wptr; int ilen; int dlen, space, codelen, extra; rptr = cmsg; if (*rptr == 0) ++rptr; ++rptr; /* skip protocol (assumed 0xfd) */ seq = (rptr[0] << 8) + rptr[1]; rptr += BSD_OVHD; ilen = len = cmsg + inlen - rptr; /* * Check the sequence number and give up if it is not what we expect. */ if (seq != db->seqno++) { if (db->debug) printf("bsd_decomp%d: bad sequence # %d, expected %d\n", db->unit, seq, db->seqno - 1); return DECOMP_ERROR; } wptr = dmp + db->hdrlen; oldcode = CLEAR; explen = 0; while (len > 0) { /* * Accumulate bytes until we have a complete code. * Then get the next code, relying on the 32-bit, * unsigned accm to mask the result. */ bitno -= 8; accm |= *rptr++ << bitno; --len; if (tgtbitno < bitno) continue; incode = accm >> tgtbitno; accm <<= n_bits; bitno += n_bits; if (incode == CLEAR) { /* * The dictionary must only be cleared at * the end of a packet. But there could be an * empty message block at the end. */ if (len > 0) { if (db->debug) printf("bsd_decomp%d: bad CLEAR\n", db->unit); return DECOMP_FATALERROR; } bsd_clear(db); explen = ilen = 0; break; } if (incode > max_ent + 2 || incode > db->maxmaxcode || incode > max_ent && oldcode == CLEAR) { if (db->debug) { printf("bsd_decomp%d: bad code 0x%x oldcode=0x%x ", db->unit, incode, oldcode); printf("max_ent=0x%x dlen=%d seqno=%d\n", max_ent, dlen, db->seqno); } return DECOMP_FATALERROR; /* probably a bug */ } /* Special case for KwKwK string. */ if (incode > max_ent) { finchar = oldcode; extra = 1; } else { finchar = incode; extra = 0; } codelen = db->lens[finchar]; explen += codelen + extra; if (explen > db->mru + 1) { if (db->debug) printf("bsd_decomp%d: ran out of mru\n", db->unit); return DECOMP_FATALERROR; } /* * Decode this code and install it in the decompressed buffer. */ p = (wptr += codelen); while (finchar > LAST) { dictp = &db->dict[db->dict[finchar].cptr]; #ifdef DEBUG --codelen; if (codelen <= 0) { printf("bsd_decomp%d: fell off end of chain ", db->unit); printf("0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, db->dict[finchar].cptr, max_ent); return DECOMP_FATALERROR; } if (dictp->codem1 != finchar-1) { printf("bsd_decomp%d: bad code chain 0x%x finchar=0x%x ", db->unit, incode, finchar); printf("oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, db->dict[finchar].cptr, dictp->codem1); return DECOMP_FATALERROR; } #endif *--p = dictp->f.hs.suffix; finchar = dictp->f.hs.prefix; } *--p = finchar; #ifdef DEBUG if (--codelen != 0) printf("bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent); #endif if (extra) /* the KwKwK case again */ *wptr++ = finchar; /* * If not first code in a packet, and * if not out of code space, then allocate a new code. * * Keep the hash table correct so it can be used * with uncompressed packets. */ if (oldcode != CLEAR && max_ent < db->maxmaxcode) { struct bsd_dict *dictp2; u_int32_t fcode; int hval, disp; fcode = BSD_KEY(oldcode,finchar); hval = BSD_HASH(oldcode,finchar,db->hshift); dictp = &db->dict[hval]; /* look for a free hash table entry */ if (dictp->codem1 < max_ent) { disp = (hval == 0) ? 1 : hval; do { hval += disp; if (hval >= db->hsize) hval -= db->hsize; dictp = &db->dict[hval]; } while (dictp->codem1 < max_ent); } /* * Invalidate previous hash table entry * assigned this code, and then take it over */ dictp2 = &db->dict[max_ent+1]; if (db->dict[dictp2->cptr].codem1 == max_ent) { db->dict[dictp2->cptr].codem1 = BADCODEM1; } dictp2->cptr = hval; dictp->codem1 = max_ent; dictp->f.fcode = fcode; db->max_ent = ++max_ent; db->lens[max_ent] = db->lens[oldcode]+1; /* Expand code size if needed. */ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) { db->n_bits = ++n_bits; tgtbitno = 32-n_bits; } } oldcode = incode; } *outlenp = wptr - (dmp + db->hdrlen); /* * Keep the checkpoint right so that incompressible packets * clear the dictionary at the right times. */ db->bytes_out += ilen; db->in_count += explen; if (bsd_check(db) && db->debug) { printf("bsd_decomp%d: peer should have cleared dictionary\n", db->unit); } ++db->comp_count; db->comp_bytes += ilen + BSD_OVHD; ++db->uncomp_count; db->uncomp_bytes += explen; return DECOMP_OK; } #endif /* DO_BSD_COMPRESS */ ppp-2.4.5/pppdump/deflate.c000066400000000000000000000221421130035057700155600ustar00rootroot00000000000000/* * ppp_deflate.c - interface the zlib procedures for Deflate compression * and decompression (as used by gzip) to the PPP code. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: deflate.c,v 1.5 2004/01/17 05:47:55 carlsonj Exp $ */ #include #include #include #include #include #include "ppp_defs.h" #include "ppp-comp.h" #include "zlib.h" #if DO_DEFLATE #define DEFLATE_DEBUG 1 /* * State for a Deflate (de)compressor. */ struct deflate_state { int seqno; int w_size; int unit; int hdrlen; int mru; int debug; z_stream strm; struct compstat stats; }; #define DEFLATE_OVHD 2 /* Deflate overhead/packet */ static void *z_alloc __P((void *, u_int items, u_int size)); static void z_free __P((void *, void *ptr, u_int nb)); static void *z_decomp_alloc __P((u_char *options, int opt_len)); static void z_decomp_free __P((void *state)); static int z_decomp_init __P((void *state, u_char *options, int opt_len, int unit, int hdrlen, int mru, int debug)); static void z_incomp __P((void *state, u_char *dmsg, int len)); static int z_decompress __P((void *state, u_char *cmp, int inlen, u_char *dmp, int *outlenp)); static void z_decomp_reset __P((void *state)); static void z_comp_stats __P((void *state, struct compstat *stats)); /* * Procedures exported to if_ppp.c. */ struct compressor ppp_deflate = { CI_DEFLATE, /* compress_proto */ z_decomp_alloc, /* decomp_alloc */ z_decomp_free, /* decomp_free */ z_decomp_init, /* decomp_init */ z_decomp_reset, /* decomp_reset */ z_decompress, /* decompress */ z_incomp, /* incomp */ z_comp_stats, /* decomp_stat */ }; /* * Space allocation and freeing routines for use by zlib routines. */ static void * z_alloc(notused, items, size) void *notused; u_int items, size; { return malloc(items * size); } static void z_free(notused, ptr, nbytes) void *notused; void *ptr; u_int nbytes; { free(ptr); } static void z_comp_stats(arg, stats) void *arg; struct compstat *stats; { struct deflate_state *state = (struct deflate_state *) arg; u_int out; *stats = state->stats; stats->ratio = stats->unc_bytes; out = stats->comp_bytes + stats->unc_bytes; if (stats->ratio <= 0x7ffffff) stats->ratio <<= 8; else out >>= 8; if (out != 0) stats->ratio /= out; } /* * Allocate space for a decompressor. */ static void * z_decomp_alloc(options, opt_len) u_char *options; int opt_len; { struct deflate_state *state; int w_size; if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE || options[1] != CILEN_DEFLATE || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || options[3] != DEFLATE_CHK_SEQUENCE) return NULL; w_size = DEFLATE_SIZE(options[2]); if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) return NULL; state = (struct deflate_state *) malloc(sizeof(*state)); if (state == NULL) return NULL; state->strm.next_out = NULL; state->strm.zalloc = (alloc_func) z_alloc; state->strm.zfree = (free_func) z_free; if (inflateInit2(&state->strm, -w_size) != Z_OK) { free(state); return NULL; } state->w_size = w_size; memset(&state->stats, 0, sizeof(state->stats)); return (void *) state; } static void z_decomp_free(arg) void *arg; { struct deflate_state *state = (struct deflate_state *) arg; inflateEnd(&state->strm); free(state); } static int z_decomp_init(arg, options, opt_len, unit, hdrlen, mru, debug) void *arg; u_char *options; int opt_len, unit, hdrlen, mru, debug; { struct deflate_state *state = (struct deflate_state *) arg; if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE || options[1] != CILEN_DEFLATE || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || DEFLATE_SIZE(options[2]) != state->w_size || options[3] != DEFLATE_CHK_SEQUENCE) return 0; state->seqno = 0; state->unit = unit; state->hdrlen = hdrlen; state->debug = debug; state->mru = mru; inflateReset(&state->strm); return 1; } static void z_decomp_reset(arg) void *arg; { struct deflate_state *state = (struct deflate_state *) arg; state->seqno = 0; inflateReset(&state->strm); } /* * Decompress a Deflate-compressed packet. * * Because of patent problems, we return DECOMP_ERROR for errors * found by inspecting the input data and for system problems, but * DECOMP_FATALERROR for any errors which could possibly be said to * be being detected "after" decompression. For DECOMP_ERROR, * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be * infringing a patent of Motorola's if we do, so we take CCP down * instead. * * Given that the frame has the correct sequence number and a good FCS, * errors such as invalid codes in the input most likely indicate a * bug, so we return DECOMP_FATALERROR for them in order to turn off * compression, even though they are detected by inspecting the input. */ static int z_decompress(arg, mi, inlen, mo, outlenp) void *arg; u_char *mi, *mo; int inlen, *outlenp; { struct deflate_state *state = (struct deflate_state *) arg; u_char *rptr, *wptr; int rlen, olen, ospace; int seq, i, flush, r, decode_proto; rptr = mi; if (*rptr == 0) ++rptr; ++rptr; /* Check the sequence number. */ seq = (rptr[0] << 8) + rptr[1]; rptr += 2; if (seq != state->seqno) { #if !DEFLATE_DEBUG if (state->debug) #endif printf("z_decompress%d: bad seq # %d, expected %d\n", state->unit, seq, state->seqno); return DECOMP_ERROR; } ++state->seqno; /* * Set up to call inflate. */ wptr = mo; state->strm.next_in = rptr; state->strm.avail_in = mi + inlen - rptr; rlen = state->strm.avail_in + PPP_HDRLEN + DEFLATE_OVHD; state->strm.next_out = wptr; state->strm.avail_out = state->mru + 2; r = inflate(&state->strm, Z_PACKET_FLUSH); if (r != Z_OK) { #if !DEFLATE_DEBUG if (state->debug) #endif printf("z_decompress%d: inflate returned %d (%s)\n", state->unit, r, (state->strm.msg? state->strm.msg: "")); return DECOMP_FATALERROR; } olen = state->mru + 2 - state->strm.avail_out; *outlenp = olen; if ((wptr[0] & 1) != 0) ++olen; /* for suppressed protocol high byte */ olen += 2; /* for address, control */ #if DEFLATE_DEBUG if (olen > state->mru + PPP_HDRLEN) printf("ppp_deflate%d: exceeded mru (%d > %d)\n", state->unit, olen, state->mru + PPP_HDRLEN); #endif state->stats.unc_bytes += olen; state->stats.unc_packets++; state->stats.comp_bytes += rlen; state->stats.comp_packets++; return DECOMP_OK; } /* * Incompressible data has arrived - add it to the history. */ static void z_incomp(arg, mi, mlen) void *arg; u_char *mi; int mlen; { struct deflate_state *state = (struct deflate_state *) arg; u_char *rptr; int rlen, proto, r; /* * Check that the protocol is one we handle. */ rptr = mi; proto = rptr[0]; if ((proto & 1) == 0) proto = (proto << 8) + rptr[1]; if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) return; ++state->seqno; if (rptr[0] == 0) ++rptr; rlen = mi + mlen - rptr; state->strm.next_in = rptr; state->strm.avail_in = rlen; r = inflateIncomp(&state->strm); if (r != Z_OK) { /* gak! */ #if !DEFLATE_DEBUG if (state->debug) #endif printf("z_incomp%d: inflateIncomp returned %d (%s)\n", state->unit, r, (state->strm.msg? state->strm.msg: "")); return; } /* * Update stats. */ if (proto <= 0xff) ++rlen; rlen += 2; state->stats.inc_bytes += rlen; state->stats.inc_packets++; state->stats.unc_bytes += rlen; state->stats.unc_packets++; } #endif /* DO_DEFLATE */ ppp-2.4.5/pppdump/ppp-comp.h000066400000000000000000000123751130035057700157230ustar00rootroot00000000000000/* * ppp-comp.h - Definitions for doing PPP packet compression. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp-comp.h,v 1.2 2002/12/06 09:49:16 paulus Exp $ */ #ifndef _NET_PPP_COMP_H #define _NET_PPP_COMP_H /* * The following symbols control whether we include code for * various compression methods. */ #ifndef DO_BSD_COMPRESS #define DO_BSD_COMPRESS 1 /* by default, include BSD-Compress */ #endif #ifndef DO_DEFLATE #define DO_DEFLATE 1 /* by default, include Deflate */ #endif #define DO_PREDICTOR_1 0 #define DO_PREDICTOR_2 0 /* * Structure giving methods for compression/decompression. */ struct compressor { int compress_proto; /* CCP compression protocol number */ /* Allocate space for a decompressor (receive side) */ void *(*decomp_alloc) __P((u_char *options, int opt_len)); /* Free space used by a decompressor */ void (*decomp_free) __P((void *state)); /* Initialize a decompressor */ int (*decomp_init) __P((void *state, u_char *options, int opt_len, int unit, int hdrlen, int mru, int debug)); /* Reset a decompressor */ void (*decomp_reset) __P((void *state)); /* Decompress a packet. */ int (*decompress) __P((void *state, u_char *mp, int inlen, u_char *dmp, int *outlen)); /* Update state for an incompressible packet received */ void (*incomp) __P((void *state, u_char *mp, int len)); /* Return decompression statistics */ void (*decomp_stat) __P((void *state, struct compstat *stats)); }; /* * Return values for decompress routine. * We need to make these distinctions so that we can disable certain * useful functionality, namely sending a CCP reset-request as a result * of an error detected after decompression. This is to avoid infringing * a patent held by Motorola. * Don't you just lurve software patents. */ #define DECOMP_OK 0 /* everything went OK */ #define DECOMP_ERROR 1 /* error detected before decomp. */ #define DECOMP_FATALERROR 2 /* error detected after decomp. */ /* * CCP codes. */ #define CCP_CONFREQ 1 #define CCP_CONFACK 2 #define CCP_CONFNAK 3 #define CCP_CONFREJ 4 #define CCP_TERMREQ 5 #define CCP_TERMACK 6 #define CCP_RESETREQ 14 #define CCP_RESETACK 15 /* * Max # bytes for a CCP option */ #define CCP_MAX_OPTION_LENGTH 32 /* * Parts of a CCP packet. */ #define CCP_CODE(dp) ((dp)[0]) #define CCP_ID(dp) ((dp)[1]) #define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3]) #define CCP_HDRLEN 4 #define CCP_OPT_CODE(dp) ((dp)[0]) #define CCP_OPT_LENGTH(dp) ((dp)[1]) #define CCP_OPT_MINLEN 2 /* * Definitions for BSD-Compress. */ #define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */ #define CILEN_BSD_COMPRESS 3 /* length of config. option */ /* Macros for handling the 3rd byte of the BSD-Compress config option. */ #define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */ #define BSD_VERSION(x) ((x) >> 5) /* version of option format */ #define BSD_CURRENT_VERSION 1 /* current version number */ #define BSD_MAKE_OPT(v, n) (((v) << 5) | (n)) #define BSD_MIN_BITS 9 /* smallest code size supported */ #define BSD_MAX_BITS 15 /* largest code size supported */ /* * Definitions for Deflate. */ #define CI_DEFLATE 26 /* config option for Deflate */ #define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */ #define CILEN_DEFLATE 4 /* length of its config option */ #define DEFLATE_MIN_SIZE 8 #define DEFLATE_MAX_SIZE 15 #define DEFLATE_METHOD_VAL 8 #define DEFLATE_SIZE(x) (((x) >> 4) + DEFLATE_MIN_SIZE) #define DEFLATE_METHOD(x) ((x) & 0x0F) #define DEFLATE_MAKE_OPT(w) ((((w) - DEFLATE_MIN_SIZE) << 4) \ + DEFLATE_METHOD_VAL) #define DEFLATE_CHK_SEQUENCE 0 /* * Definitions for other, as yet unsupported, compression methods. */ #define CI_PREDICTOR_1 1 /* config option for Predictor-1 */ #define CILEN_PREDICTOR_1 2 /* length of its config option */ #define CI_PREDICTOR_2 2 /* config option for Predictor-2 */ #define CILEN_PREDICTOR_2 2 /* length of its config option */ #endif /* _NET_PPP_COMP_H */ ppp-2.4.5/pppdump/pppdump.8000066400000000000000000000031561130035057700155720ustar00rootroot00000000000000.\" @(#) $Id: pppdump.8,v 1.2 2004/11/13 12:22:49 paulus Exp $ .TH PPPDUMP 8 "1 April 1999" .SH NAME pppdump \- convert PPP record file to readable format .SH SYNOPSIS .B pppdump [ .B \-h | .B \-p [ .B \-d ]] [ .B \-r ] [ .B \-m \fImru ] [ .I file \fR... ] .ti 12 .SH DESCRIPTION The .B pppdump utility converts the files written using the \fIrecord\fR option of .B pppd into a human-readable format. If one or more filenames are specified, .B pppdump will read each in turn; otherwise it will read its standard input. In each case the result is written to standard output. .PP The options are as follows: .TP .B \-h Prints the bytes sent and received in hexadecimal. If neither this option nor the \fB\-p\fR option is specified, the bytes are printed as the characters themselves, with non-printing and non-ASCII characters printed as escape sequences. .TP .B \-p Collects the bytes sent and received into PPP packets, interpreting the async HDLC framing and escape characters and checking the FCS (frame check sequence) of each packet. The packets are printed as hex values and as characters (non-printable characters are printed as `.'). .TP .B \-d With the \fB\-p\fR option, this option causes .B pppdump to decompress packets which have been compressed with the BSD-Compress or Deflate methods. .TP .B \-r Reverses the direction indicators, so that `sent' is printed for bytes or packets received, and `rcvd' is printed for bytes or packets sent. .TP .B \-m \fImru Use \fImru\fR as the MRU (maximum receive unit) for both directions of the link when checking for over-length PPP packets (with the \fB\-p\fR option). .SH SEE ALSO pppd(8) ppp-2.4.5/pppdump/pppdump.c000066400000000000000000000310011130035057700156330ustar00rootroot00000000000000/* * pppdump - print out the contents of a record file generated by * pppd in readable form. * * Copyright (c) 1999 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "ppp_defs.h" #include "ppp-comp.h" int hexmode; int pppmode; int reverse; int decompress; int mru = 1500; int abs_times; time_t start_time; int start_time_tenths; int tot_sent, tot_rcvd; extern int optind; extern char *optarg; void dumplog(); void dumpppp(); void show_time(); void handle_ccp(); int main(ac, av) int ac; char **av; { int i; char *p; FILE *f; while ((i = getopt(ac, av, "hprdm:a")) != -1) { switch (i) { case 'h': hexmode = 1; break; case 'p': pppmode = 1; break; case 'r': reverse = 1; break; case 'd': decompress = 1; break; case 'm': mru = atoi(optarg); break; case 'a': abs_times = 1; break; default: fprintf(stderr, "Usage: %s [-h | -p[d]] [-r] [-m mru] [-a] [file ...]\n", av[0]); exit(1); } } if (optind >= ac) dumplog(stdin); else { for (i = optind; i < ac; ++i) { p = av[i]; if ((f = fopen(p, "r")) == NULL) { perror(p); exit(1); } if (pppmode) dumpppp(f); else dumplog(f); fclose(f); } } exit(0); } void dumplog(f) FILE *f; { int c, n, k, col; int nb, c2; unsigned char buf[16]; while ((c = getc(f)) != EOF) { switch (c) { case 1: case 2: if (reverse) c = 3 - c; printf("%s %c", c==1? "sent": "rcvd", hexmode? ' ': '"'); col = 6; n = getc(f); n = (n << 8) + getc(f); *(c==1? &tot_sent: &tot_rcvd) += n; nb = 0; for (; n > 0; --n) { c = getc(f); if (c == EOF) { printf("\nEOF\n"); exit(0); } if (hexmode) { if (nb >= 16) { printf(" "); for (k = 0; k < nb; ++k) { c2 = buf[k]; putchar((' ' <= c2 && c2 <= '~')? c2: '.'); } printf("\n "); nb = 0; } buf[nb++] = c; printf(" %.2x", c); } else { k = (' ' <= c && c <= '~')? (c != '\\' && c != '"')? 1: 2: 3; if ((col += k) >= 78) { printf("\n "); col = 6 + k; } switch (k) { case 1: putchar(c); break; case 2: printf("\\%c", c); break; case 3: printf("\\%.2x", c); break; } } } if (hexmode) { for (k = nb; k < 16; ++k) printf(" "); printf(" "); for (k = 0; k < nb; ++k) { c2 = buf[k]; putchar((' ' <= c2 && c2 <= '~')? c2: '.'); } } else putchar('"'); printf("\n"); break; case 3: case 4: printf("end %s\n", c==3? "send": "recv"); break; case 5: case 6: case 7: show_time(f, c); break; default: printf("?%.2x\n"); } } } /* * FCS lookup table as calculated by genfcstab. */ static u_short fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; struct pkt { int cnt; int esc; int flags; struct compressor *comp; void *state; unsigned char buf[8192]; } spkt, rpkt; /* Values for flags */ #define CCP_ISUP 1 #define CCP_ERROR 2 #define CCP_FATALERROR 4 #define CCP_ERR (CCP_ERROR | CCP_FATALERROR) #define CCP_DECOMP_RUN 8 unsigned char dbuf[8192]; void dumpppp(f) FILE *f; { int c, n, k; int nb, nl, dn, proto, rv; char *dir, *q; unsigned char *p, *r, *endp; unsigned char *d; unsigned short fcs; struct pkt *pkt; spkt.cnt = rpkt.cnt = 0; spkt.esc = rpkt.esc = 0; while ((c = getc(f)) != EOF) { switch (c) { case 1: case 2: if (reverse) c = 3 - c; dir = c==1? "sent": "rcvd"; pkt = c==1? &spkt: &rpkt; n = getc(f); n = (n << 8) + getc(f); *(c==1? &tot_sent: &tot_rcvd) += n; for (; n > 0; --n) { c = getc(f); switch (c) { case EOF: printf("\nEOF\n"); if (spkt.cnt > 0) printf("[%d bytes in incomplete send packet]\n", spkt.cnt); if (rpkt.cnt > 0) printf("[%d bytes in incomplete recv packet]\n", rpkt.cnt); exit(0); case '~': if (pkt->cnt > 0) { q = dir; if (pkt->esc) { printf("%s aborted packet:\n ", dir); q = " "; } nb = pkt->cnt; p = pkt->buf; pkt->cnt = 0; pkt->esc = 0; if (nb <= 2) { printf("%s short packet [%d bytes]:", q, nb); for (k = 0; k < nb; ++k) printf(" %.2x", p[k]); printf("\n"); break; } fcs = PPP_INITFCS; for (k = 0; k < nb; ++k) fcs = PPP_FCS(fcs, p[k]); fcs &= 0xFFFF; nb -= 2; endp = p + nb; r = p; if (r[0] == 0xff && r[1] == 3) r += 2; if ((r[0] & 1) == 0) ++r; ++r; if (endp - r > mru) printf(" ERROR: length (%d) > MRU (%d)\n", endp - r, mru); if (decompress && fcs == PPP_GOODFCS) { /* See if this is a CCP or compressed packet */ d = dbuf; r = p; if (r[0] == 0xff && r[1] == 3) { *d++ = *r++; *d++ = *r++; } proto = r[0]; if ((proto & 1) == 0) proto = (proto << 8) + r[1]; if (proto == PPP_CCP) { handle_ccp(pkt, r + 2, endp - r - 2); } else if (proto == PPP_COMP) { if ((pkt->flags & CCP_ISUP) && (pkt->flags & CCP_DECOMP_RUN) && pkt->state && (pkt->flags & CCP_ERR) == 0) { rv = pkt->comp->decompress(pkt->state, r, endp - r, d, &dn); switch (rv) { case DECOMP_OK: p = dbuf; nb = d + dn - p; if ((d[0] & 1) == 0) --dn; --dn; if (dn > mru) printf(" ERROR: decompressed length (%d) > MRU (%d)\n", dn, mru); break; case DECOMP_ERROR: printf(" DECOMPRESSION ERROR\n"); pkt->flags |= CCP_ERROR; break; case DECOMP_FATALERROR: printf(" FATAL DECOMPRESSION ERROR\n"); pkt->flags |= CCP_FATALERROR; break; } } } else if (pkt->state && (pkt->flags & CCP_DECOMP_RUN)) { pkt->comp->incomp(pkt->state, r, endp - r); } } do { nl = nb < 16? nb: 16; printf("%s ", q); for (k = 0; k < nl; ++k) printf(" %.2x", p[k]); for (; k < 16; ++k) printf(" "); printf(" "); for (k = 0; k < nl; ++k) { c = p[k]; putchar((' ' <= c && c <= '~')? c: '.'); } printf("\n"); q = " "; p += nl; nb -= nl; } while (nb > 0); if (fcs != PPP_GOODFCS) printf(" BAD FCS: (residue = %x)\n", fcs); } break; case '}': if (!pkt->esc) { pkt->esc = 1; break; } /* else fall through */ default: if (pkt->esc) { c ^= 0x20; pkt->esc = 0; } pkt->buf[pkt->cnt++] = c; break; } } break; case 3: case 4: if (reverse) c = 7 - c; dir = c==3? "send": "recv"; pkt = c==3? &spkt: &rpkt; printf("end %s", dir); if (pkt->cnt > 0) printf(" [%d bytes in incomplete packet]", pkt->cnt); printf("\n"); break; case 5: case 6: case 7: show_time(f, c); break; default: printf("?%.2x\n"); } } } extern struct compressor ppp_bsd_compress, ppp_deflate; struct compressor *compressors[] = { #if DO_BSD_COMPRESS &ppp_bsd_compress, #endif #if DO_DEFLATE &ppp_deflate, #endif NULL }; void handle_ccp(cp, dp, len) struct pkt *cp; u_char *dp; int len; { int clen; struct compressor **comp; if (len < CCP_HDRLEN) return; clen = CCP_LENGTH(dp); if (clen > len) return; switch (CCP_CODE(dp)) { case CCP_CONFACK: cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP); if (clen < CCP_HDRLEN + CCP_OPT_MINLEN || clen < CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) break; dp += CCP_HDRLEN; clen -= CCP_HDRLEN; for (comp = compressors; *comp != NULL; ++comp) { if ((*comp)->compress_proto == dp[0]) { if (cp->state != NULL) { (*cp->comp->decomp_free)(cp->state); cp->state = NULL; } cp->comp = *comp; cp->state = (*comp)->decomp_alloc(dp, CCP_OPT_LENGTH(dp)); cp->flags |= CCP_ISUP; if (cp->state != NULL && (*cp->comp->decomp_init) (cp->state, dp, clen, 0, 0, 8192, 1)) cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN; break; } } break; case CCP_CONFNAK: case CCP_CONFREJ: cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP); break; case CCP_RESETACK: if (cp->flags & CCP_ISUP) { if (cp->state && (cp->flags & CCP_DECOMP_RUN)) { (*cp->comp->decomp_reset)(cp->state); cp->flags &= ~CCP_ERROR; } } break; } } void show_time(f, c) FILE *f; int c; { time_t t; int n; struct tm *tm; if (c == 7) { t = getc(f); t = (t << 8) + getc(f); t = (t << 8) + getc(f); t = (t << 8) + getc(f); printf("start %s", ctime(&t)); start_time = t; start_time_tenths = 0; tot_sent = tot_rcvd = 0; } else { n = getc(f); if (c == 5) { for (c = 3; c > 0; --c) n = (n << 8) + getc(f); } if (abs_times) { n += start_time_tenths; start_time += n / 10; start_time_tenths = n % 10; tm = localtime(&start_time); printf("time %.2d:%.2d:%.2d.%d", tm->tm_hour, tm->tm_min, tm->tm_sec, start_time_tenths); printf(" (sent %d, rcvd %d)\n", tot_sent, tot_rcvd); } else printf("time %.1fs\n", (double) n / 10); } } ppp-2.4.5/pppdump/zlib.c000066400000000000000000004613221130035057700151230ustar00rootroot00000000000000/* * This file is derived from various .h and .c files from the zlib-0.95 * distribution by Jean-loup Gailly and Mark Adler, with some additions * by Paul Mackerras to aid in implementing Deflate compression and * decompression for PPP packets. See zlib.h for conditions of * distribution and use. * * Changes that have been made include: * - changed functions not used outside this file to "local" * - added minCompression parameter to deflateInit2 * - added Z_PACKET_FLUSH (see zlib.h for details) * - added inflateIncomp * * $Id: zlib.c,v 1.2 1999/04/01 07:26:30 paulus Exp $ */ /*+++++*/ /* zutil.h -- internal interface and configuration of the compression library * Copyright (C) 1995 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */ #define _Z_UTIL_H #include "zlib.h" #ifdef STDC # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #define FAR typedef unsigned char uch; typedef uch FAR uchf; typedef unsigned short ush; typedef ush FAR ushf; typedef unsigned long ulg; extern char *z_errmsg[]; /* indexed by 1-zlib_error */ #define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err) /* To be used only when the state is known to be valid */ #ifndef NULL #define NULL ((void *) 0) #endif /* common constants */ #define DEFLATED 8 #ifndef DEF_WBITS # define DEF_WBITS MAX_WBITS #endif /* default windowBits for decompression. MAX_WBITS is for compression only */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default memLevel */ #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 /* The three kinds of block type */ #define MIN_MATCH 3 #define MAX_MATCH 258 /* The minimum and maximum match lengths */ /* functions */ #if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) # define HAVE_MEMCPY #endif #ifdef HAVE_MEMCPY # define zmemcpy memcpy # define zmemzero(dest, len) memset(dest, 0, len) #else # define zmemcpy(d, s, n) bcopy((s), (d), (n)) # define zmemzero bzero #endif /* Diagnostic functions */ #ifdef DEBUG_ZLIB # include # ifndef verbose # define verbose 0 # endif # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) fprintf x # define Tracev(x) {if (verbose) fprintf x ;} # define Tracevv(x) {if (verbose>1) fprintf x ;} # define Tracec(c,x) {if (verbose && (c)) fprintf x ;} # define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} #else # define Assert(cond,msg) # define Trace(x) # define Tracev(x) # define Tracevv(x) # define Tracec(c,x) # define Tracecv(c,x) #endif typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len)); /* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */ /* void zcfree OF((voidpf opaque, voidpf ptr)); */ #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr, size) \ (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size)) #define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);} /* deflate.h -- internal compression state * Copyright (C) 1995 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /*+++++*/ /* From: deflate.h,v 1.5 1995/05/03 17:27:09 jloup Exp */ /* =========================================================================== * Internal compression state. */ /* Data type */ #define BINARY 0 #define ASCII 1 #define UNKNOWN 2 #define LENGTH_CODES 29 /* number of length codes, not counting the special END_BLOCK code */ #define LITERALS 256 /* number of literal bytes 0..255 */ #define L_CODES (LITERALS+1+LENGTH_CODES) /* number of Literal or Length codes, including the END_BLOCK code */ #define D_CODES 30 /* number of distance codes */ #define BL_CODES 19 /* number of codes used to transfer the bit lengths */ #define HEAP_SIZE (2*L_CODES+1) /* maximum heap size */ #define MAX_BITS 15 /* All codes must not exceed MAX_BITS bits */ #define INIT_STATE 42 #define BUSY_STATE 113 #define FLUSH_STATE 124 #define FINISH_STATE 666 /* Stream status */ /* Data structure describing a single value and its code string. */ typedef struct ct_data_s { union { ush freq; /* frequency count */ ush code; /* bit string */ } fc; union { ush dad; /* father node in Huffman tree */ ush len; /* length of bit string */ } dl; } FAR ct_data; #define Freq fc.freq #define Code fc.code #define Dad dl.dad #define Len dl.len typedef struct static_tree_desc_s static_tree_desc; typedef struct tree_desc_s { ct_data *dyn_tree; /* the dynamic tree */ int max_code; /* largest code with non zero frequency */ static_tree_desc *stat_desc; /* the corresponding static tree */ } FAR tree_desc; typedef ush Pos; typedef Pos FAR Posf; typedef unsigned IPos; /* A Pos is an index in the character window. We use short instead of int to * save space in the various tables. IPos is used only for parameter passing. */ typedef struct deflate_state { z_stream *strm; /* pointer back to this zlib stream */ int status; /* as the name implies */ Bytef *pending_buf; /* output still pending */ Bytef *pending_out; /* next pending byte to output to the stream */ int pending; /* nb of bytes in the pending buffer */ uLong adler; /* adler32 of uncompressed data */ int noheader; /* suppress zlib header and adler32 */ Byte data_type; /* UNKNOWN, BINARY or ASCII */ Byte method; /* STORED (for zip only) or DEFLATED */ int minCompr; /* min size decrease for Z_FLUSH_NOSTORE */ /* used by deflate.c: */ uInt w_size; /* LZ77 window size (32K by default) */ uInt w_bits; /* log2(w_size) (8..16) */ uInt w_mask; /* w_size - 1 */ Bytef *window; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. Also, it limits * the window size to 64K, which is quite useful on MSDOS. * To do: use the user input buffer as sliding window. */ ulg window_size; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ Posf *prev; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ Posf *head; /* Heads of the hash chains or NIL. */ uInt ins_h; /* hash index of string to be inserted */ uInt hash_size; /* number of elements in hash table */ uInt hash_bits; /* log2(hash_size) */ uInt hash_mask; /* hash_size-1 */ uInt hash_shift; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ long block_start; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ uInt match_length; /* length of best match */ IPos prev_match; /* previous match */ int match_available; /* set if previous match exists */ uInt strstart; /* start of string to insert */ uInt match_start; /* start of matching string */ uInt lookahead; /* number of valid bytes ahead in window */ uInt prev_length; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ uInt max_chain_length; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ uInt max_lazy_match; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ # define max_insert_length max_lazy_match /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ int level; /* compression level (1..9) */ int strategy; /* favor or force Huffman coding*/ uInt good_match; /* Use a faster search when the previous match is longer than this */ int nice_match; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn't use ct_data typedef below to supress compiler warning */ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ struct tree_desc_s l_desc; /* desc. for literal tree */ struct tree_desc_s d_desc; /* desc. for distance tree */ struct tree_desc_s bl_desc; /* desc. for bit length tree */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ int heap_len; /* number of elements in the heap */ int heap_max; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ uch depth[2*L_CODES+1]; /* Depth of each subtree used as tie breaker for trees of equal frequency */ uchf *l_buf; /* buffer for literals or lengths */ uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can't count above 4 */ uInt last_lit; /* running index in l_buf */ ushf *d_buf; /* Buffer for distances. To simplify the code, d_buf and l_buf have * the same number of elements. To use different lengths, an extra flag * array would be necessary. */ ulg opt_len; /* bit length of current block with optimal trees */ ulg static_len; /* bit length of current block with static trees */ ulg compressed_len; /* total bit length of compressed file */ uInt matches; /* number of string matches in current block */ int last_eob_len; /* bit length of EOB code for last block */ #ifdef DEBUG_ZLIB ulg bits_sent; /* bit length of the compressed data */ #endif ush bi_buf; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ int bi_valid; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ uInt blocks_in_packet; /* Number of blocks produced since the last time Z_PACKET_FLUSH * was used. */ } FAR deflate_state; /* Output a byte on the stream. * IN assertion: there is enough room in pending_buf. */ #define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* Minimum amount of lookahead, except at the end of the input file. * See deflate.c for comments about the MIN_MATCH+1. */ #define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) /* In order to simplify the code, particularly on 16 bit machines, match * distances are limited to MAX_DIST instead of WSIZE. */ /* in trees.c */ local void ct_init OF((deflate_state *s)); local int ct_tally OF((deflate_state *s, int dist, int lc)); local ulg ct_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, int flush)); local void ct_align OF((deflate_state *s)); local void ct_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, int eof)); local void ct_stored_type_only OF((deflate_state *s)); /*+++++*/ /* deflate.c -- compress data using the deflation algorithm * Copyright (C) 1995 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process depends on being able to identify portions * of the input text which are identical to earlier input (within a * sliding window trailing behind the input currently being processed). * * The most straightforward technique turns out to be the fastest for * most input files: try all possible matches and select the longest. * The key feature of this algorithm is that insertions into the string * dictionary are very simple and thus fast, and deletions are avoided * completely. Insertions are performed at each input character, whereas * string matches are performed only when the previous match ends. So it * is preferable to spend more time in matches to allow very fast string * insertions and avoid deletions. The matching algorithm for small * strings is inspired from that of Rabin & Karp. A brute force approach * is used to find longer strings when a small match has been found. * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze * (by Leonid Broukhis). * A previous version of this file used a more sophisticated algorithm * (by Fiala and Greene) which is guaranteed to run in linear amortized * time, but has a larger average cost, uses more memory and is patented. * However the F&G algorithm may be faster for some highly redundant * files if the parameter max_chain_length (described below) is too large. * * ACKNOWLEDGEMENTS * * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and * I found it in 'freeze' written by Leonid Broukhis. * Thanks to many people for bug reports and testing. * * REFERENCES * * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc * * A description of the Rabin and Karp algorithm is given in the book * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. * * Fiala,E.R., and Greene,D.H. * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 * */ /* From: deflate.c,v 1.8 1995/05/03 17:27:08 jloup Exp */ local char zlib_copyright[] = " deflate Copyright 1995 Jean-loup Gailly "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ #define NIL 0 /* Tail of hash chains */ #ifndef TOO_FAR # define TOO_FAR 4096 #endif /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* Minimum amount of lookahead, except at the end of the input file. * See deflate.c for comments about the MIN_MATCH+1. */ /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ typedef struct config_s { ush good_length; /* reduce lazy search above this match length */ ush max_lazy; /* do not perform lazy search above this match length */ ush nice_length; /* quit search above this match length */ ush max_chain; } config; local config configuration_table[10] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0}, /* store only */ /* 1 */ {4, 4, 8, 4}, /* maximum speed, no lazy matches */ /* 2 */ {4, 5, 16, 8}, /* 3 */ {4, 6, 32, 32}, /* 4 */ {4, 4, 16, 16}, /* lazy matches */ /* 5 */ {8, 16, 32, 32}, /* 6 */ {8, 16, 128, 128}, /* 7 */ {8, 32, 128, 256}, /* 8 */ {32, 128, 258, 1024}, /* 9 */ {32, 258, 258, 4096}}; /* maximum compression */ /* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different * meaning. */ #define EQUAL 0 /* result of memcmp for equal strings */ /* =========================================================================== * Prototypes for local functions. */ local void fill_window OF((deflate_state *s)); local int deflate_fast OF((deflate_state *s, int flush)); local int deflate_slow OF((deflate_state *s, int flush)); local void lm_init OF((deflate_state *s)); local int longest_match OF((deflate_state *s, IPos cur_match)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_stream *strm)); local int read_buf OF((z_stream *strm, charf *buf, unsigned size)); #ifdef ASMV void match_init OF((void)); /* asm code initialization */ #endif #ifdef DEBUG_ZLIB local void check_match OF((deflate_state *s, IPos start, IPos match, int length)); #endif /* =========================================================================== * Update a hash value with the given input byte * IN assertion: all calls to to UPDATE_HASH are made with consecutive * input characters, so that a running hash key can be computed from the * previous key instead of complete recalculation each time. */ #define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) /* =========================================================================== * Insert string str in the dictionary and set match_head to the previous head * of the hash chain (the most recent string with same hash key). Return * the previous length of the hash chain. * IN assertion: all calls to to INSERT_STRING are made with consecutive * input characters and the first MIN_MATCH bytes of str are valid * (except for the last MIN_MATCH-1 bytes of the input file). */ #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \ s->head[s->ins_h] = (str)) /* =========================================================================== * Initialize the hash table (avoiding 64K overflow for 16 bit systems). * prev[] will be initialized on the fly. */ #define CLEAR_HASH(s) \ s->head[s->hash_size-1] = NIL; \ zmemzero((charf *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); /* ========================================================================= */ int deflateInit (strm, level) z_stream *strm; int level; { return deflateInit2 (strm, level, DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, 0, 0); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ int deflateInit2 (strm, level, method, windowBits, memLevel, strategy, minCompression) z_stream *strm; int level; int method; int windowBits; int memLevel; int strategy; int minCompression; { deflate_state *s; int noheader = 0; if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* if (strm->zalloc == Z_NULL) strm->zalloc = zcalloc; */ /* if (strm->zfree == Z_NULL) strm->zfree = zcfree; */ if (level == Z_DEFAULT_COMPRESSION) level = 6; if (windowBits < 0) { /* undocumented feature: suppress zlib header */ noheader = 1; windowBits = -windowBits; } if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != DEFLATED || windowBits < 8 || windowBits > 15 || level < 1 || level > 9) { return Z_STREAM_ERROR; } s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; s->strm = strm; s->noheader = noheader; s->w_bits = windowBits; s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; s->hash_bits = memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, 2*sizeof(ush)); if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { strm->msg = z_errmsg[1-Z_MEM_ERROR]; deflateEnd (strm); return Z_MEM_ERROR; } s->d_buf = (ushf *) &(s->pending_buf[s->lit_bufsize]); s->l_buf = (uchf *) &(s->pending_buf[3*s->lit_bufsize]); /* We overlay pending_buf and d_buf+l_buf. This works since the average * output size for (length,distance) codes is <= 32 bits (worst case * is 15+15+13=33). */ s->level = level; s->strategy = strategy; s->method = (Byte)method; s->minCompr = minCompression; s->blocks_in_packet = 0; return deflateReset(strm); } /* ========================================================================= */ int deflateReset (strm) z_stream *strm; { deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL || strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR; strm->total_in = strm->total_out = 0; strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ strm->data_type = Z_UNKNOWN; s = (deflate_state *)strm->state; s->pending = 0; s->pending_out = s->pending_buf; if (s->noheader < 0) { s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */ } s->status = s->noheader ? BUSY_STATE : INIT_STATE; s->adler = 1; ct_init(s); lm_init(s); return Z_OK; } /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ local void putShortMSB (s, b) deflate_state *s; uInt b; { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } /* ========================================================================= * Flush as much pending output as possible. */ local void flush_pending(strm) z_stream *strm; { deflate_state *state = (deflate_state *) strm->state; unsigned len = state->pending; if (len > strm->avail_out) len = strm->avail_out; if (len == 0) return; if (strm->next_out != NULL) { zmemcpy(strm->next_out, state->pending_out, len); strm->next_out += len; } state->pending_out += len; strm->total_out += len; strm->avail_out -= len; state->pending -= len; if (state->pending == 0) { state->pending_out = state->pending_buf; } } /* ========================================================================= */ int deflate (strm, flush) z_stream *strm; int flush; { deflate_state *state = (deflate_state *) strm->state; if (strm == Z_NULL || state == Z_NULL) return Z_STREAM_ERROR; if (strm->next_in == Z_NULL && strm->avail_in != 0) { ERR_RETURN(strm, Z_STREAM_ERROR); } if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); state->strm = strm; /* just in case */ /* Write the zlib header */ if (state->status == INIT_STATE) { uInt header = (DEFLATED + ((state->w_bits-8)<<4)) << 8; uInt level_flags = (state->level-1) >> 1; if (level_flags > 3) level_flags = 3; header |= (level_flags << 6); header += 31 - (header % 31); state->status = BUSY_STATE; putShortMSB(state, header); } /* Flush as much pending output as possible */ if (state->pending != 0) { flush_pending(strm); if (strm->avail_out == 0) return Z_OK; } /* If we came back in here to get the last output from * a previous flush, we're done for now. */ if (state->status == FLUSH_STATE) { state->status = BUSY_STATE; if (flush != Z_NO_FLUSH && flush != Z_FINISH) return Z_OK; } /* User must not provide more input after the first FINISH: */ if (state->status == FINISH_STATE && strm->avail_in != 0) { ERR_RETURN(strm, Z_BUF_ERROR); } /* Start a new block or continue the current one. */ if (strm->avail_in != 0 || state->lookahead != 0 || (flush == Z_FINISH && state->status != FINISH_STATE)) { int quit; if (flush == Z_FINISH) { state->status = FINISH_STATE; } if (state->level <= 3) { quit = deflate_fast(state, flush); } else { quit = deflate_slow(state, flush); } if (quit || strm->avail_out == 0) return Z_OK; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don't have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } /* If a flush was requested, we have a little more to output now. */ if (flush != Z_NO_FLUSH && flush != Z_FINISH && state->status != FINISH_STATE) { switch (flush) { case Z_PARTIAL_FLUSH: ct_align(state); break; case Z_PACKET_FLUSH: /* Output just the 3-bit `stored' block type value, but not a zero length. */ ct_stored_type_only(state); break; default: ct_stored_block(state, (char*)0, 0L, 0); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush == Z_FULL_FLUSH) { CLEAR_HASH(state); /* forget history */ } } flush_pending(strm); if (strm->avail_out == 0) { /* We'll have to come back to get the rest of the output; * this ensures we don't output a second zero-length stored * block (or whatever). */ state->status = FLUSH_STATE; return Z_OK; } } Assert(strm->avail_out > 0, "bug2"); if (flush != Z_FINISH) return Z_OK; if (state->noheader) return Z_STREAM_END; /* Write the zlib trailer (adler32) */ putShortMSB(state, (uInt)(state->adler >> 16)); putShortMSB(state, (uInt)(state->adler & 0xffff)); flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ state->noheader = -1; /* write the trailer only once! */ return state->pending != 0 ? Z_OK : Z_STREAM_END; } /* ========================================================================= */ int deflateEnd (strm) z_stream *strm; { deflate_state *state = (deflate_state *) strm->state; if (strm == Z_NULL || state == Z_NULL) return Z_STREAM_ERROR; TRY_FREE(strm, state->window, state->w_size * 2 * sizeof(Byte)); TRY_FREE(strm, state->prev, state->w_size * sizeof(Pos)); TRY_FREE(strm, state->head, state->hash_size * sizeof(Pos)); TRY_FREE(strm, state->pending_buf, state->lit_bufsize * 2 * sizeof(ush)); ZFREE(strm, state, sizeof(deflate_state)); strm->state = Z_NULL; return Z_OK; } /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. */ local int read_buf(strm, buf, size) z_stream *strm; charf *buf; unsigned size; { unsigned len = strm->avail_in; deflate_state *state = (deflate_state *) strm->state; if (len > size) len = size; if (len == 0) return 0; strm->avail_in -= len; if (!state->noheader) { state->adler = adler32(state->adler, strm->next_in, len); } zmemcpy(buf, strm->next_in, len); strm->next_in += len; strm->total_in += len; return (int)len; } /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ local void lm_init (s) deflate_state *s; { s->window_size = (ulg)2L*s->w_size; CLEAR_HASH(s); /* Set the default configuration parameters: */ s->max_lazy_match = configuration_table[s->level].max_lazy; s->good_match = configuration_table[s->level].good_length; s->nice_match = configuration_table[s->level].nice_length; s->max_chain_length = configuration_table[s->level].max_chain; s->strstart = 0; s->block_start = 0L; s->lookahead = 0; s->match_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; #ifdef ASMV match_init(); /* initialize the asm code */ #endif } /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 */ #ifndef ASMV /* For 80x86 and 680x0, an optimized version will be provided in match.asm or * match.S. The code will be functionally equivalent. */ local int longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ int best_len = s->prev_length; /* best match length so far */ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? s->strstart - (IPos)MAX_DIST(s) : NIL; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ Posf *prev = s->prev; uInt wmask = s->w_mask; #ifdef UNALIGNED_OK /* Compare two bytes at a time. Note: this is not always beneficial. * Try with and without -DUNALIGNED_OK to check. */ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; register ush scan_start = *(ushf*)scan; register ush scan_end = *(ushf*)(scan+best_len-1); #else register Bytef *strend = s->window + s->strstart + MAX_MATCH; register Byte scan_end1 = scan[best_len-1]; register Byte scan_end = scan[best_len]; #endif /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s->prev_length >= s->good_match) { chain_length >>= 2; } Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); do { Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2: */ #if (defined(UNALIGNED_OK) && MAX_MATCH == 258) /* This code assumes sizeof(unsigned short) == 2. Do not use * UNALIGNED_OK if your compiler uses a different size. */ if (*(ushf*)(match+best_len-1) != scan_end || *(ushf*)match != scan_start) continue; /* It is not necessary to compare scan[2] and match[2] since they are * always equal when the other bytes match, given that the hash keys * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at * strstart+3, +5, ... up to strstart+257. We check for insufficient * lookahead only every 4th comparison; the 128th check will be made * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ Assert(scan[2] == match[2], "scan[2]?"); scan++, match++; do { } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && scan < strend); /* The funny "do {}" generates better code on most compilers */ /* Here, scan <= window+strstart+257 */ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); if (*scan == *match) scan++; len = (MAX_MATCH - 1) - (int)(strend-scan); scan = strend - (MAX_MATCH-1); #else /* UNALIGNED_OK */ if (match[best_len] != scan_end || match[best_len-1] != scan_end1 || *match != *scan || *++match != scan[1]) continue; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match++; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); scan = strend - MAX_MATCH; #endif /* UNALIGNED_OK */ if (len > best_len) { s->match_start = cur_match; best_len = len; if (len >= s->nice_match) break; #ifdef UNALIGNED_OK scan_end = *(ushf*)(scan+best_len-1); #else scan_end1 = scan[best_len-1]; scan_end = scan[best_len]; #endif } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length != 0); return best_len; } #endif /* ASMV */ #ifdef DEBUG_ZLIB /* =========================================================================== * Check that the match at match_start is indeed a match. */ local void check_match(s, start, match, length) deflate_state *s; IPos start, match; int length; { /* check that the match is indeed a match */ if (memcmp((charf *)s->window + match, (charf *)s->window + start, length) != EQUAL) { fprintf(stderr, " start %u, match %u, length %d\n", start, match, length); do { fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); } while (--length != 0); z_error("invalid match"); } if (verbose > 1) { fprintf(stderr,"\\[%d,%d]", start-match, length); do { putc(s->window[start++], stderr); } while (--length != 0); } } #else # define check_match(s, start, match, length) #endif /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ local void fill_window(s) deflate_state *s; { register unsigned n, m; register Posf *p; unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; do { more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); /* Deal with !@#$% 64K limit: */ if (more == 0 && s->strstart == 0 && s->lookahead == 0) { more = wsize; } else if (more == (unsigned)(-1)) { /* Very unlikely, but possible on 16 bit machine if strstart == 0 * and lookahead == 1 (input done one byte at time) */ more--; /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ } else if (s->strstart >= wsize+MAX_DIST(s)) { /* By the IN assertion, the window is not empty so we can't confuse * more == 0 with more == 64K on a 16 bit machine. */ zmemcpy((charf *)s->window, (charf *)s->window+wsize, (unsigned)wsize); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; /* Slide the hash table (could be avoided with 32 bit values at the expense of memory usage): */ n = s->hash_size; p = &s->head[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m-wsize : NIL); } while (--n); n = wsize; p = &s->prev[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m-wsize : NIL); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); more += wsize; } if (s->strm->avail_in == 0) return; /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ Assert(more >= 2, "more < 2"); n = read_buf(s->strm, (charf *)s->window + s->strstart + s->lookahead, more); s->lookahead += n; /* Initialize the hash value now that we have some input: */ if (s->lookahead >= MIN_MATCH) { s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); } /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. */ #define FLUSH_BLOCK_ONLY(s, flush) { \ ct_flush_block(s, (s->block_start >= 0L ? \ (charf *)&s->window[(unsigned)s->block_start] : \ (charf *)Z_NULL), (long)s->strstart - s->block_start, (flush)); \ s->block_start = s->strstart; \ flush_pending(s->strm); \ Tracev((stderr,"[FLUSH]")); \ } /* Same but force premature exit if necessary. */ #define FLUSH_BLOCK(s, flush) { \ FLUSH_BLOCK_ONLY(s, flush); \ if (s->strm->avail_out == 0) return 1; \ } /* =========================================================================== * Compress as much as possible from the input stream, return true if * processing was terminated prematurely (no more input or output space). * This function does not perform lazy evaluationof matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ local int deflate_fast(s, flush) deflate_state *s; int flush; { IPos hash_head = NIL; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ s->prev_length = MIN_MATCH-1; for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1; if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ if (s->strategy != Z_HUFFMAN_ONLY) { s->match_length = longest_match (s, hash_head); } /* longest_match() sets match_start */ if (s->match_length > s->lookahead) s->match_length = s->lookahead; } if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->match_start, s->match_length); bflush = ct_tally(s, s->strstart - s->match_start, s->match_length - MIN_MATCH); s->lookahead -= s->match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ if (s->match_length <= s->max_insert_length && s->lookahead >= MIN_MATCH) { s->match_length--; /* string at strstart already in hash table */ do { s->strstart++; INSERT_STRING(s, s->strstart, hash_head); /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s->match_length != 0); s->strstart++; } else { s->strstart += s->match_length; s->match_length = 0; s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); bflush = ct_tally (s, 0, s->window[s->strstart]); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, Z_NO_FLUSH); } FLUSH_BLOCK(s, flush); return 0; /* normal exit */ } /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ local int deflate_slow(s, flush) deflate_state *s; int flush; { IPos hash_head = NIL; /* head of hash chain */ int bflush; /* set if current block must be flushed */ /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1; if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. */ s->prev_length = s->match_length, s->prev_match = s->match_start; s->match_length = MIN_MATCH-1; if (hash_head != NIL && s->prev_length < s->max_lazy_match && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ if (s->strategy != Z_HUFFMAN_ONLY) { s->match_length = longest_match (s, hash_head); } /* longest_match() sets match_start */ if (s->match_length > s->lookahead) s->match_length = s->lookahead; if (s->match_length <= 5 && (s->strategy == Z_FILTERED || (s->match_length == MIN_MATCH && s->strstart - s->match_start > TOO_FAR))) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s->match_length = MIN_MATCH-1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ check_match(s, s->strstart-1, s->prev_match, s->prev_length); bflush = ct_tally(s, s->strstart -1 - s->prev_match, s->prev_length - MIN_MATCH); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s->lookahead -= s->prev_length-1; s->prev_length -= 2; do { if (++s->strstart <= max_insert) { INSERT_STRING(s, s->strstart, hash_head); } } while (--s->prev_length != 0); s->match_available = 0; s->match_length = MIN_MATCH-1; s->strstart++; if (bflush) FLUSH_BLOCK(s, Z_NO_FLUSH); } else if (s->match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ Tracevv((stderr,"%c", s->window[s->strstart-1])); if (ct_tally (s, 0, s->window[s->strstart-1])) { FLUSH_BLOCK_ONLY(s, Z_NO_FLUSH); } s->strstart++; s->lookahead--; if (s->strm->avail_out == 0) return 1; } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s->match_available = 1; s->strstart++; s->lookahead--; } } Assert (flush != Z_NO_FLUSH, "no flush?"); if (s->match_available) { Tracevv((stderr,"%c", s->window[s->strstart-1])); ct_tally (s, 0, s->window[s->strstart-1]); s->match_available = 0; } FLUSH_BLOCK(s, flush); return 0; } /*+++++*/ /* trees.c -- output deflated data using Huffman coding * Copyright (C) 1995 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process uses several Huffman trees. The more * common source values are represented by shorter bit sequences. * * Each code tree is stored in a compressed form which is itself * a Huffman encoding of the lengths of all the code strings (in * ascending order by source values). The actual code strings are * reconstructed from the lengths in the inflate process, as described * in the deflate specification. * * REFERENCES * * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc * * Storer, James A. * Data Compression: Methods and Theory, pp. 49-50. * Computer Science Press, 1988. ISBN 0-7167-8156-5. * * Sedgewick, R. * Algorithms, p290. * Addison-Wesley, 1983. ISBN 0-201-06672-6. */ /* From: trees.c,v 1.5 1995/05/03 17:27:12 jloup Exp */ #ifdef DEBUG_ZLIB # include #endif /* =========================================================================== * Constants */ #define MAX_BL_BITS 7 /* Bit length codes must not exceed MAX_BL_BITS bits */ #define END_BLOCK 256 /* end of block literal code */ #define REP_3_6 16 /* repeat previous bit length 3-6 times (2 bits of repeat count) */ #define REPZ_3_10 17 /* repeat a zero length 3-10 times (3 bits of repeat count) */ #define REPZ_11_138 18 /* repeat a zero length 11-138 times (7 bits of repeat count) */ local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; local int extra_dbits[D_CODES] /* extra bits for each distance code */ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; local int extra_blbits[BL_CODES]/* extra bits for each bit length code */ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; local uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ #define Buf_size (8 * 2*sizeof(char)) /* Number of bits used within bi_buf. (bi_buf might be implemented on * more than 16 bits on some systems.) */ /* =========================================================================== * Local data. These are initialized only once. * To do: initialize at compile time to be completely reentrant. ??? */ local ct_data static_ltree[L_CODES+2]; /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see ct_init * below). */ local ct_data static_dtree[D_CODES]; /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ local uch dist_code[512]; /* distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ local uch length_code[MAX_MATCH-MIN_MATCH+1]; /* length code for each normalized match length (0 == MIN_MATCH) */ local int base_length[LENGTH_CODES]; /* First normalized length for each code (0 = MIN_MATCH) */ local int base_dist[D_CODES]; /* First normalized distance for each code (0 = distance of 1) */ struct static_tree_desc_s { ct_data *static_tree; /* static tree or NULL */ intf *extra_bits; /* extra bits for each code or NULL */ int extra_base; /* base index for extra_bits */ int elems; /* max number of elements in the tree */ int max_length; /* max bit length for the codes */ }; local static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; local static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; local static_tree_desc static_bl_desc = {(ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== * Local (static) routines in this file. */ local void ct_static_init OF((void)); local void init_block OF((deflate_state *s)); local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); local void build_tree OF((deflate_state *s, tree_desc *desc)); local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); local int build_bl_tree OF((deflate_state *s)); local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, int blcodes)); local void compress_block OF((deflate_state *s, ct_data *ltree, ct_data *dtree)); local void set_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); local void copy_block OF((deflate_state *s, charf *buf, unsigned len, int header)); #ifndef DEBUG_ZLIB # define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) /* Send a code of the given tree. c and tree must not have side effects */ #else /* DEBUG_ZLIB */ # define send_code(s, c, tree) \ { if (verbose>1) fprintf(stderr,"\ncd %3d ",(c)); \ send_bits(s, tree[c].Code, tree[c].Len); } #endif #define d_code(dist) \ ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) /* Mapping from a distance to a distance code. dist is the distance - 1 and * must not have side effects. dist_code[256] and dist_code[257] are never * used. */ /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ #define put_short(s, w) { \ put_byte(s, (uch)((w) & 0xff)); \ put_byte(s, (uch)((ush)(w) >> 8)); \ } /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef DEBUG_ZLIB local void send_bits OF((deflate_state *s, int value, int length)); local void send_bits(s, value, length) deflate_state *s; int value; /* value to send */ int length; /* number of bits */ { Tracev((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; /* If not enough room in bi_buf, use (valid) bits from bi_buf and * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { s->bi_buf |= (value << s->bi_valid); put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); s->bi_valid += length - Buf_size; } else { s->bi_buf |= value << s->bi_valid; s->bi_valid += length; } } #else /* !DEBUG_ZLIB */ #define send_bits(s, value, length) \ { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ int val = value;\ s->bi_buf |= (val << s->bi_valid);\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ s->bi_valid += len - Buf_size;\ } else {\ s->bi_buf |= (value) << s->bi_valid;\ s->bi_valid += len;\ }\ } #endif /* DEBUG_ZLIB */ #define MAX(a,b) (a >= b ? a : b) /* the arguments must not have side effects */ /* =========================================================================== * Initialize the various 'constant' tables. * To do: do this at compile time. */ local void ct_static_init() { int n; /* iterates over tree elements */ int bits; /* bit counter */ int length; /* length value */ int code; /* code value */ int dist; /* distance index */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES-1; code++) { base_length[code] = length; for (n = 0; n < (1< dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ for ( ; code < D_CODES; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { dist_code[256 + dist++] = (uch)code; } } Assert (dist == 256, "ct_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; n = 0; while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES; n++) { static_dtree[n].Len = 5; static_dtree[n].Code = bi_reverse(n, 5); } } /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ local void ct_init(s) deflate_state *s; { if (static_dtree[0].Len == 0) { ct_static_init(); /* To do: at compile time */ } s->compressed_len = 0L; s->l_desc.dyn_tree = s->dyn_ltree; s->l_desc.stat_desc = &static_l_desc; s->d_desc.dyn_tree = s->dyn_dtree; s->d_desc.stat_desc = &static_d_desc; s->bl_desc.dyn_tree = s->bl_tree; s->bl_desc.stat_desc = &static_bl_desc; s->bi_buf = 0; s->bi_valid = 0; s->last_eob_len = 8; /* enough lookahead for inflate */ #ifdef DEBUG_ZLIB s->bits_sent = 0L; #endif s->blocks_in_packet = 0; /* Initialize the first block of the first file: */ init_block(s); } /* =========================================================================== * Initialize a new block. */ local void init_block(s) deflate_state *s; { int n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; s->dyn_ltree[END_BLOCK].Freq = 1; s->opt_len = s->static_len = 0L; s->last_lit = s->matches = 0; } #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ /* =========================================================================== * Remove the smallest element from the heap and recreate the heap with * one less element. Updates heap and heap_len. */ #define pqremove(s, tree, top) \ {\ top = s->heap[SMALLEST]; \ s->heap[SMALLEST] = s->heap[s->heap_len--]; \ pqdownheap(s, tree, SMALLEST); \ } /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ #define smaller(tree, n, m, depth) \ (tree[n].Freq < tree[m].Freq || \ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ local void pqdownheap(s, tree, k) deflate_state *s; ct_data *tree; /* the tree to restore */ int k; /* node to move down */ { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { /* Set j to the smallest of the two sons: */ if (j < s->heap_len && smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s->heap[j], s->depth)) break; /* Exchange v with the smallest son */ s->heap[k] = s->heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s->heap[k] = v; } /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ local void gen_bitlen(s, desc) deflate_state *s; tree_desc *desc; /* the tree descriptor */ { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; ct_data *stree = desc->stat_desc->static_tree; intf *extra = desc->stat_desc->extra_bits; int base = desc->stat_desc->extra_base; int max_length = desc->stat_desc->max_length; int h; /* heap index */ int n, m; /* iterate over the tree elements */ int bits; /* bit length */ int xbits; /* extra bits */ ush f; /* frequency */ int overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ for (h = s->heap_max+1; h < HEAP_SIZE; h++) { n = s->heap[h]; bits = tree[tree[n].Dad].Len + 1; if (bits > max_length) bits = max_length, overflow++; tree[n].Len = (ush)bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) continue; /* not a leaf node */ s->bl_count[bits]++; xbits = 0; if (n >= base) xbits = extra[n-base]; f = tree[n].Freq; s->opt_len += (ulg)f * (bits + xbits); if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); } if (overflow == 0) return; Trace((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length-1; while (s->bl_count[bits] == 0) bits--; s->bl_count[bits]--; /* move one leaf down the tree */ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ s->bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from 'ar' written by Haruhiko Okumura.) */ for (bits = max_length; bits != 0; bits--) { n = s->bl_count[bits]; while (n != 0) { m = s->heap[--h]; if (m > max_code) continue; if (tree[m].Len != (unsigned) bits) { Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); s->opt_len += ((long)bits - (long)tree[m].Len) *(long)tree[m].Freq; tree[m].Len = (ush)bits; } n--; } } } /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ local void gen_codes (tree, max_code, bl_count) ct_data *tree; /* the tree to decorate */ int max_code; /* largest code with non zero frequency */ ushf *bl_count; /* number of codes at each bit length */ { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ ush code = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { next_code[bits] = code = (code + bl_count[bits-1]) << 1; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; int n, m; /* iterate over heap elements */ int max_code = -1; /* largest code with non zero frequency */ int node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. * heap[0] is not used. */ s->heap_len = 0, s->heap_max = HEAP_SIZE; for (n = 0; n < elems; n++) { if (tree[n].Freq != 0) { s->heap[++(s->heap_len)] = max_code = n; s->depth[n] = 0; } else { tree[n].Len = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s->heap_len < 2) { node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); tree[node].Freq = 1; s->depth[node] = 0; s->opt_len--; if (stree) s->static_len -= stree[node].Len; /* node is 0 or 1 so it does not have extra bits */ } desc->max_code = max_code; /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { pqremove(s, tree, n); /* n = node of least frequency */ m = s->heap[SMALLEST]; /* m = node of next least frequency */ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ s->heap[--(s->heap_max)] = m; /* Create a new node father of n and m */ tree[node].Freq = tree[n].Freq + tree[m].Freq; s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1); tree[n].Dad = tree[m].Dad = (ush)node; #ifdef DUMP_BL_TREE if (tree == s->bl_tree) { fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); } #endif /* and insert the new node in the heap */ s->heap[SMALLEST] = node++; pqdownheap(s, tree, SMALLEST); } while (s->heap_len >= 2); s->heap[--(s->heap_max)] = s->heap[SMALLEST]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, (tree_desc *)desc); /* The field len is now set, we can generate the bit codes */ gen_codes ((ct_data *)tree, max_code, s->bl_count); } /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ local void scan_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ if (nextlen == 0) max_count = 138, min_count = 3; tree[max_code+1].Len = (ush)0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { s->bl_tree[curlen].Freq += count; } else if (curlen != 0) { if (curlen != prevlen) s->bl_tree[curlen].Freq++; s->bl_tree[REP_3_6].Freq++; } else if (count <= 10) { s->bl_tree[REPZ_3_10].Freq++; } else { s->bl_tree[REPZ_11_138].Freq++; } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ local void send_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ /* tree[max_code+1].Len = -1; */ /* guard already set */ if (nextlen == 0) max_count = 138, min_count = 3; for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s->bl_tree); } while (--count != 0); } else if (curlen != 0) { if (curlen != prevlen) { send_code(s, curlen, s->bl_tree); count--; } Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); } else { send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ local int build_bl_tree(s) deflate_state *s; { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); /* Build the bit length tree: */ build_tree(s, (tree_desc *)(&(s->bl_desc))); /* opt_len now includes the length of the tree representations, except * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ s->opt_len += 3*(max_blindex+1) + 5+5+4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); return max_blindex; } /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ local void send_all_trees(s, lcodes, dcodes, blcodes) deflate_state *s; int lcodes, dcodes, blcodes; /* number of codes for each tree */ { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, "too many codes"); Tracev((stderr, "\nbl counts: ")); send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes-1, 5); send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { Tracev((stderr, "\nbl code %2d ", bl_order[rank])); send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); } Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); } /* =========================================================================== * Send a stored block */ local void ct_stored_block(s, buf, stored_len, eof) deflate_state *s; charf *buf; /* input block */ ulg stored_len; /* length of input block */ int eof; /* true if this is the last block for a file */ { send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ s->compressed_len = (s->compressed_len + 3 + 7) & ~7L; s->compressed_len += (stored_len + 4) << 3; copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ } /* Send just the `stored block' type code without any length bytes or data. */ local void ct_stored_type_only(s) deflate_state *s; { send_bits(s, (STORED_BLOCK << 1), 3); bi_windup(s); s->compressed_len = (s->compressed_len + 3) & ~7L; } /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. * The current inflate code requires 9 bits of lookahead. If the EOB * code for the previous block was coded on 5 bits or less, inflate * may have only 5+3 bits of lookahead to decode this EOB. * (There are no problems if the previous block is stored or fixed.) */ local void ct_align(s) deflate_state *s; { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ bi_flush(s); /* Of the 10 bits for the empty block, we have already sent * (10 - bi_valid) bits. The lookahead for the EOB of the previous * block was thus its length plus what we have just sent. */ if (s->last_eob_len + 10 - s->bi_valid < 9) { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); s->compressed_len += 10L; bi_flush(s); } s->last_eob_len = 7; } /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and output the encoded block to the zip file. This function * returns the total compressed length for the file so far. */ local ulg ct_flush_block(s, buf, stored_len, flush) deflate_state *s; charf *buf; /* input block, or NULL if too old */ ulg stored_len; /* length of input block */ int flush; /* Z_FINISH if this is the last block for a file */ { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex; /* index of last bit length code of non zero freq */ int eof = flush == Z_FINISH; ++s->blocks_in_packet; /* Check if the file is ascii or binary */ if (s->data_type == UNKNOWN) set_data_type(s); /* Construct the literal and distance trees */ build_tree(s, (tree_desc *)(&(s->l_desc))); Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, s->static_len)); build_tree(s, (tree_desc *)(&(s->d_desc))); Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute first the block length in bytes */ opt_lenb = (s->opt_len+3+7)>>3; static_lenb = (s->static_len+3+7)>>3; Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, s->last_lit)); if (static_lenb <= opt_lenb) opt_lenb = static_lenb; /* If compression failed and this is the first and last block, * and if the .zip file can be seeked (to rewrite the local header), * the whole file is transformed into a stored file: */ #ifdef STORED_FILE_OK # ifdef FORCE_STORED_FILE if (eof && compressed_len == 0L) /* force stored file */ # else if (stored_len <= opt_lenb && eof && s->compressed_len==0L && seekable()) # endif { /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ if (buf == (charf*)0) error ("block vanished"); copy_block(buf, (unsigned)stored_len, 0); /* without header */ s->compressed_len = stored_len << 3; s->method = STORED; } else #endif /* STORED_FILE_OK */ /* For Z_PACKET_FLUSH, if we don't achieve the required minimum * compression, and this block contains all the data since the last * time we used Z_PACKET_FLUSH, then just omit this block completely * from the output. */ if (flush == Z_PACKET_FLUSH && s->blocks_in_packet == 1 && opt_lenb > stored_len - s->minCompr) { s->blocks_in_packet = 0; /* output nothing */ } else #ifdef FORCE_STORED if (buf != (char*)0) /* force stored block */ #else if (stored_len+4 <= opt_lenb && buf != (char*)0) /* 4: two words for the lengths */ #endif { /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can't have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ ct_stored_block(s, buf, stored_len, eof); } else #ifdef FORCE_STATIC if (static_lenb >= 0) /* force static trees */ #else if (static_lenb == opt_lenb) #endif { send_bits(s, (STATIC_TREES<<1)+eof, 3); compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); s->compressed_len += 3 + s->static_len; } else { send_bits(s, (DYN_TREES<<1)+eof, 3); send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, max_blindex+1); compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); s->compressed_len += 3 + s->opt_len; } Assert (s->compressed_len == s->bits_sent, "bad compressed size"); init_block(s); if (eof) { bi_windup(s); s->compressed_len += 7; /* align on byte boundary */ } Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, s->compressed_len-7*eof)); return s->compressed_len >> 3; } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ local int ct_tally (s, dist, lc) deflate_state *s; int dist; /* distance of matched string */ int lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ { s->d_buf[s->last_lit] = (ush)dist; s->l_buf[s->last_lit++] = (uch)lc; if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; } else { s->matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ Assert((ush)dist < (ush)MAX_DIST(s) && (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++; s->dyn_dtree[d_code(dist)].Freq++; } /* Try to guess if it is profitable to stop the current block here */ if (s->level > 2 && (s->last_lit & 0xfff) == 0) { /* Compute an upper bound for the compressed length */ ulg out_length = (ulg)s->last_lit*8L; ulg in_length = (ulg)s->strstart - s->block_start; int dcode; for (dcode = 0; dcode < D_CODES; dcode++) { out_length += (ulg)s->dyn_dtree[dcode].Freq * (5L+extra_dbits[dcode]); } out_length >>= 3; Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", s->last_lit, in_length, out_length, 100L - out_length*100L/in_length)); if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; } return (s->last_lit == s->lit_bufsize-1); /* We avoid equality with lit_bufsize because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ } /* =========================================================================== * Send the block data compressed using the given Huffman trees */ local void compress_block(s, ltree, dtree) deflate_state *s; ct_data *ltree; /* literal tree */ ct_data *dtree; /* distance tree */ { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ unsigned lx = 0; /* running index in l_buf */ unsigned code; /* the code to send */ int extra; /* number of extra bits to send */ if (s->last_lit != 0) do { dist = s->d_buf[lx]; lc = s->l_buf[lx++]; if (dist == 0) { send_code(s, lc, ltree); /* send a literal byte */ Tracecv(isgraph(lc), (stderr," '%c' ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = length_code[lc]; send_code(s, code+LITERALS+1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra != 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra != 0) { dist -= base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow"); } while (lx < s->last_lit); send_code(s, END_BLOCK, ltree); s->last_eob_len = ltree[END_BLOCK].Len; } /* =========================================================================== * Set the data type to ASCII or BINARY, using a crude approximation: * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. * IN assertion: the fields freq of dyn_ltree are set and the total of all * frequencies does not exceed 64K (to fit in an int on 16 bit machines). */ local void set_data_type(s) deflate_state *s; { int n = 0; unsigned ascii_freq = 0; unsigned bin_freq = 0; while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII); } /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ local unsigned bi_reverse(code, len) unsigned code; /* the value to invert */ int len; /* its bit length */ { register unsigned res = 0; do { res |= code & 1; code >>= 1, res <<= 1; } while (--len > 0); return res >> 1; } /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ local void bi_flush(s) deflate_state *s; { if (s->bi_valid == 16) { put_short(s, s->bi_buf); s->bi_buf = 0; s->bi_valid = 0; } else if (s->bi_valid >= 8) { put_byte(s, (Byte)s->bi_buf); s->bi_buf >>= 8; s->bi_valid -= 8; } } /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ local void bi_windup(s) deflate_state *s; { if (s->bi_valid > 8) { put_short(s, s->bi_buf); } else if (s->bi_valid > 0) { put_byte(s, (Byte)s->bi_buf); } s->bi_buf = 0; s->bi_valid = 0; #ifdef DEBUG_ZLIB s->bits_sent = (s->bits_sent+7) & ~7; #endif } /* =========================================================================== * Copy a stored block, storing first the length and its * one's complement if requested. */ local void copy_block(s, buf, len, header) deflate_state *s; charf *buf; /* the input data */ unsigned len; /* its length */ int header; /* true if block header must be written */ { bi_windup(s); /* align on byte boundary */ s->last_eob_len = 8; /* enough lookahead for inflate */ if (header) { put_short(s, (ush)len); put_short(s, (ush)~len); #ifdef DEBUG_ZLIB s->bits_sent += 2*16; #endif } #ifdef DEBUG_ZLIB s->bits_sent += (ulg)len<<3; #endif while (len--) { put_byte(s, *buf++); } } /*+++++*/ /* infblock.h -- header to use infblock.c * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ struct inflate_blocks_state; typedef struct inflate_blocks_state FAR inflate_blocks_statef; local inflate_blocks_statef * inflate_blocks_new OF(( z_stream *z, check_func c, /* check function */ uInt w)); /* window size */ local int inflate_blocks OF(( inflate_blocks_statef *, z_stream *, int)); /* initial return code */ local void inflate_blocks_reset OF(( inflate_blocks_statef *, z_stream *, uLongf *)); /* check value on output */ local int inflate_blocks_free OF(( inflate_blocks_statef *, z_stream *, uLongf *)); /* check value on output */ local int inflate_addhistory OF(( inflate_blocks_statef *, z_stream *)); local int inflate_packet_flush OF(( inflate_blocks_statef *)); /*+++++*/ /* inftrees.h -- header to use inftrees.c * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Huffman code lookup table entry--this entry is four bytes for machines that have 16-bit pointers (e.g. PC's in the small or medium model). */ typedef struct inflate_huft_s FAR inflate_huft; struct inflate_huft_s { union { struct { Byte Exop; /* number of extra bits or operation */ Byte Bits; /* number of bits in this code or subcode */ } what; uInt Nalloc; /* number of these allocated here */ Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ } word; /* 16-bit, 8 bytes for 32-bit machines) */ union { uInt Base; /* literal, length base, or distance base */ inflate_huft *Next; /* pointer to next level of table */ } more; }; #ifdef DEBUG_ZLIB local uInt inflate_hufts; #endif local int inflate_trees_bits OF(( uIntf *, /* 19 code lengths */ uIntf *, /* bits tree desired/actual depth */ inflate_huft * FAR *, /* bits tree result */ z_stream *)); /* for zalloc, zfree functions */ local int inflate_trees_dynamic OF(( uInt, /* number of literal/length codes */ uInt, /* number of distance codes */ uIntf *, /* that many (total) code lengths */ uIntf *, /* literal desired/actual bit depth */ uIntf *, /* distance desired/actual bit depth */ inflate_huft * FAR *, /* literal/length tree result */ inflate_huft * FAR *, /* distance tree result */ z_stream *)); /* for zalloc, zfree functions */ local int inflate_trees_fixed OF(( uIntf *, /* literal desired/actual bit depth */ uIntf *, /* distance desired/actual bit depth */ inflate_huft * FAR *, /* literal/length tree result */ inflate_huft * FAR *)); /* distance tree result */ local int inflate_trees_free OF(( inflate_huft *, /* tables to free */ z_stream *)); /* for zfree function */ /*+++++*/ /* infcodes.h -- header to use infcodes.c * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ struct inflate_codes_state; typedef struct inflate_codes_state FAR inflate_codes_statef; local inflate_codes_statef *inflate_codes_new OF(( uInt, uInt, inflate_huft *, inflate_huft *, z_stream *)); local int inflate_codes OF(( inflate_blocks_statef *, z_stream *, int)); local void inflate_codes_free OF(( inflate_codes_statef *, z_stream *)); /*+++++*/ /* inflate.c -- zlib interface to inflate modules * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* inflate private state */ struct internal_state { /* mode */ enum { METHOD, /* waiting for method byte */ FLAG, /* waiting for flag byte */ BLOCKS, /* decompressing blocks */ CHECK4, /* four check bytes to go */ CHECK3, /* three check bytes to go */ CHECK2, /* two check bytes to go */ CHECK1, /* one check byte to go */ DONE, /* finished check, done */ BAD} /* got an error--stay here */ mode; /* current inflate mode */ /* mode dependent information */ union { uInt method; /* if FLAGS, method byte */ struct { uLong was; /* computed check value */ uLong need; /* stream check value */ } check; /* if CHECK, check values to compare */ uInt marker; /* if BAD, inflateSync's marker bytes count */ } sub; /* submode */ /* mode independent information */ int nowrap; /* flag for no wrapper */ uInt wbits; /* log2(window size) (8..15, defaults to 15) */ inflate_blocks_statef *blocks; /* current inflate_blocks state */ }; int inflateReset(z) z_stream *z; { uLong c; if (z == Z_NULL || z->state == Z_NULL) return Z_STREAM_ERROR; z->total_in = z->total_out = 0; z->msg = Z_NULL; z->state->mode = z->state->nowrap ? BLOCKS : METHOD; inflate_blocks_reset(z->state->blocks, z, &c); Trace((stderr, "inflate: reset\n")); return Z_OK; } int inflateEnd(z) z_stream *z; { uLong c; if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) return Z_STREAM_ERROR; if (z->state->blocks != Z_NULL) inflate_blocks_free(z->state->blocks, z, &c); ZFREE(z, z->state, sizeof(struct internal_state)); z->state = Z_NULL; Trace((stderr, "inflate: end\n")); return Z_OK; } int inflateInit2(z, w) z_stream *z; int w; { /* initialize state */ if (z == Z_NULL) return Z_STREAM_ERROR; /* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */ /* if (z->zfree == Z_NULL) z->zfree = zcfree; */ if ((z->state = (struct internal_state FAR *) ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) return Z_MEM_ERROR; z->state->blocks = Z_NULL; /* handle undocumented nowrap option (no zlib header or check) */ z->state->nowrap = 0; if (w < 0) { w = - w; z->state->nowrap = 1; } /* set window size */ if (w < 8 || w > 15) { inflateEnd(z); return Z_STREAM_ERROR; } z->state->wbits = (uInt)w; /* create inflate_blocks state */ if ((z->state->blocks = inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w)) == Z_NULL) { inflateEnd(z); return Z_MEM_ERROR; } Trace((stderr, "inflate: allocated\n")); /* reset state */ inflateReset(z); return Z_OK; } int inflateInit(z) z_stream *z; { return inflateInit2(z, DEF_WBITS); } #define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} #define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) int inflate(z, f) z_stream *z; int f; { int r; uInt b; if (z == Z_NULL || z->next_in == Z_NULL) return Z_STREAM_ERROR; r = Z_BUF_ERROR; while (1) switch (z->state->mode) { case METHOD: NEEDBYTE if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED) { z->state->mode = BAD; z->msg = "unknown compression method"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } if ((z->state->sub.method >> 4) + 8 > z->state->wbits) { z->state->mode = BAD; z->msg = "invalid window size"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } z->state->mode = FLAG; case FLAG: NEEDBYTE if ((b = NEXTBYTE) & 0x20) { z->state->mode = BAD; z->msg = "invalid reserved bit"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } if (((z->state->sub.method << 8) + b) % 31) { z->state->mode = BAD; z->msg = "incorrect header check"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } Trace((stderr, "inflate: zlib header ok\n")); z->state->mode = BLOCKS; case BLOCKS: r = inflate_blocks(z->state->blocks, z, r); if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) r = inflate_packet_flush(z->state->blocks); if (r == Z_DATA_ERROR) { z->state->mode = BAD; z->state->sub.marker = 0; /* can try inflateSync */ break; } if (r != Z_STREAM_END) return r; r = Z_OK; inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); if (z->state->nowrap) { z->state->mode = DONE; break; } z->state->mode = CHECK4; case CHECK4: NEEDBYTE z->state->sub.check.need = (uLong)NEXTBYTE << 24; z->state->mode = CHECK3; case CHECK3: NEEDBYTE z->state->sub.check.need += (uLong)NEXTBYTE << 16; z->state->mode = CHECK2; case CHECK2: NEEDBYTE z->state->sub.check.need += (uLong)NEXTBYTE << 8; z->state->mode = CHECK1; case CHECK1: NEEDBYTE z->state->sub.check.need += (uLong)NEXTBYTE; if (z->state->sub.check.was != z->state->sub.check.need) { z->state->mode = BAD; z->msg = "incorrect data check"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } Trace((stderr, "inflate: zlib check ok\n")); z->state->mode = DONE; case DONE: return Z_STREAM_END; case BAD: return Z_DATA_ERROR; default: return Z_STREAM_ERROR; } empty: if (f != Z_PACKET_FLUSH) return r; z->state->mode = BAD; z->state->sub.marker = 0; /* can try inflateSync */ return Z_DATA_ERROR; } /* * This subroutine adds the data at next_in/avail_in to the output history * without performing any output. The output buffer must be "caught up"; * i.e. no pending output (hence s->read equals s->write), and the state must * be BLOCKS (i.e. we should be willing to see the start of a series of * BLOCKS). On exit, the output will also be caught up, and the checksum * will have been updated if need be. */ int inflateIncomp(z) z_stream *z; { if (z->state->mode != BLOCKS) return Z_DATA_ERROR; return inflate_addhistory(z->state->blocks, z); } int inflateSync(z) z_stream *z; { uInt n; /* number of bytes to look at */ Bytef *p; /* pointer to bytes */ uInt m; /* number of marker bytes found in a row */ uLong r, w; /* temporaries to save total_in and total_out */ /* set up */ if (z == Z_NULL || z->state == Z_NULL) return Z_STREAM_ERROR; if (z->state->mode != BAD) { z->state->mode = BAD; z->state->sub.marker = 0; } if ((n = z->avail_in) == 0) return Z_BUF_ERROR; p = z->next_in; m = z->state->sub.marker; /* search */ while (n && m < 4) { if (*p == (Byte)(m < 2 ? 0 : 0xff)) m++; else if (*p) m = 0; else m = 4 - m; p++, n--; } /* restore */ z->total_in += p - z->next_in; z->next_in = p; z->avail_in = n; z->state->sub.marker = m; /* return no joy or set up to restart on a new block */ if (m != 4) return Z_DATA_ERROR; r = z->total_in; w = z->total_out; inflateReset(z); z->total_in = r; z->total_out = w; z->state->mode = BLOCKS; return Z_OK; } #undef NEEDBYTE #undef NEXTBYTE /*+++++*/ /* infutil.h -- types and macros common to blocks and codes * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* inflate blocks semi-private state */ struct inflate_blocks_state { /* mode */ enum { TYPE, /* get type bits (3, including end bit) */ LENS, /* get lengths for stored */ STORED, /* processing stored block */ TABLE, /* get table lengths */ BTREE, /* get bit lengths tree for a dynamic block */ DTREE, /* get length, distance trees for a dynamic block */ CODES, /* processing fixed or dynamic block */ DRY, /* output remaining window bytes */ DONEB, /* finished last block, done */ BADB} /* got a data error--stuck here */ mode; /* current inflate_block mode */ /* mode dependent information */ union { uInt left; /* if STORED, bytes left to copy */ struct { uInt table; /* table lengths (14 bits) */ uInt index; /* index into blens (or border) */ uIntf *blens; /* bit lengths of codes */ uInt bb; /* bit length tree depth */ inflate_huft *tb; /* bit length decoding tree */ int nblens; /* # elements allocated at blens */ } trees; /* if DTREE, decoding info for trees */ struct { inflate_huft *tl, *td; /* trees to free */ inflate_codes_statef *codes; } decode; /* if CODES, current state */ } sub; /* submode */ uInt last; /* true if this block is the last block */ /* mode independent information */ uInt bitk; /* bits in bit buffer */ uLong bitb; /* bit buffer */ Bytef *window; /* sliding window */ Bytef *end; /* one byte after sliding window */ Bytef *read; /* window read pointer */ Bytef *write; /* window write pointer */ check_func checkfn; /* check function */ uLong check; /* check on output */ }; /* defines for inflate input/output */ /* update pointers and return */ #define UPDBITS {s->bitb=b;s->bitk=k;} #define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} #define UPDOUT {s->write=q;} #define UPDATE {UPDBITS UPDIN UPDOUT} #define LEAVE {UPDATE return inflate_flush(s,z,r);} /* get bytes and bits */ #define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} #define NEEDBYTE {if(n)r=Z_OK;else LEAVE} #define NEXTBYTE (n--,*p++) #define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} /* output bytes */ #define WAVAIL (qread?s->read-q-1:s->end-q) #define LOADOUT {q=s->write;m=WAVAIL;} #define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}} #define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} #define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} #define OUTBYTE(a) {*q++=(Byte)(a);m--;} /* load local pointers */ #define LOAD {LOADIN LOADOUT} /* And'ing with mask[n] masks the lower n bits */ local uInt inflate_mask[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; /* copy as much as possible from the sliding window to the output area */ local int inflate_flush OF(( inflate_blocks_statef *, z_stream *, int)); /*+++++*/ /* inffast.h -- header to use inffast.c * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ local int inflate_fast OF(( uInt, uInt, inflate_huft *, inflate_huft *, inflate_blocks_statef *, z_stream *)); /*+++++*/ /* infblock.c -- interpret and process block types to last block * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* Table for deflate from PKZIP's appnote.txt. */ local uInt border[] = { /* Order of the bit length code lengths */ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* Notes beyond the 1.93a appnote.txt: 1. Distance pointers never point before the beginning of the output stream. 2. Distance pointers can point back across blocks, up to 32k away. 3. There is an implied maximum of 7 bits for the bit length table and 15 bits for the actual data. 4. If only one code exists, then it is encoded using one bit. (Zero would be more efficient, but perhaps a little confusing.) If two codes exist, they are coded using one bit each (0 and 1). 5. There is no way of sending zero distance codes--a dummy must be sent if there are none. (History: a pre 2.0 version of PKZIP would store blocks with no distance codes, but this was discovered to be too harsh a criterion.) Valid only for 1.93a. 2.04c does allow zero distance codes, which is sent as one code of zero bits in length. 6. There are up to 286 literal/length codes. Code 256 represents the end-of-block. Note however that the static length tree defines 288 codes just to fill out the Huffman codes. Codes 286 and 287 cannot be used though, since there is no length base or extra bits defined for them. Similarily, there are up to 30 distance codes. However, static trees define 32 codes (all 5 bits) to fill out the Huffman codes, but the last two had better not show up in the data. 7. Unzip can check dynamic Huffman blocks for complete code sets. The exception is that a single code would not be complete (see #4). 8. The five bits following the block type is really the number of literal codes sent minus 257. 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits (1+6+6). Therefore, to output three times the length, you output three codes (1+1+1), whereas to output four times the same length, you only need two codes (1+3). Hmm. 10. In the tree reconstruction algorithm, Code = Code + Increment only if BitLength(i) is not zero. (Pretty obvious.) 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) 12. Note: length code 284 can represent 227-258, but length code 285 really is 258. The last length deserves its own, short code since it gets used a lot in very redundant files. The length 258 is special since 258 - 3 (the min match length) is 255. 13. The literal/length and distance code bit lengths are read as a single stream of lengths. It is possible (and advantageous) for a repeat code (16, 17, or 18) to go across the boundary between the two sets of lengths. */ local void inflate_blocks_reset(s, z, c) inflate_blocks_statef *s; z_stream *z; uLongf *c; { if (s->checkfn != Z_NULL) *c = s->check; if (s->mode == BTREE || s->mode == DTREE) ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); if (s->mode == CODES) { inflate_codes_free(s->sub.decode.codes, z); inflate_trees_free(s->sub.decode.td, z); inflate_trees_free(s->sub.decode.tl, z); } s->mode = TYPE; s->bitk = 0; s->bitb = 0; s->read = s->write = s->window; if (s->checkfn != Z_NULL) s->check = (*s->checkfn)(0L, Z_NULL, 0); Trace((stderr, "inflate: blocks reset\n")); } local inflate_blocks_statef *inflate_blocks_new(z, c, w) z_stream *z; check_func c; uInt w; { inflate_blocks_statef *s; if ((s = (inflate_blocks_statef *)ZALLOC (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) return s; if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) { ZFREE(z, s, sizeof(struct inflate_blocks_state)); return Z_NULL; } s->end = s->window + w; s->checkfn = c; s->mode = TYPE; Trace((stderr, "inflate: blocks allocated\n")); inflate_blocks_reset(s, z, &s->check); return s; } local int inflate_blocks(s, z, r) inflate_blocks_statef *s; z_stream *z; int r; { uInt t; /* temporary storage */ uLong b; /* bit buffer */ uInt k; /* bits in bit buffer */ Bytef *p; /* input data pointer */ uInt n; /* bytes available there */ Bytef *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ /* copy input/output information to locals (UPDATE macro restores) */ LOAD /* process input based on current state */ while (1) switch (s->mode) { case TYPE: NEEDBITS(3) t = (uInt)b & 7; s->last = t & 1; switch (t >> 1) { case 0: /* stored */ Trace((stderr, "inflate: stored block%s\n", s->last ? " (last)" : "")); DUMPBITS(3) t = k & 7; /* go to byte boundary */ DUMPBITS(t) s->mode = LENS; /* get length of stored block */ break; case 1: /* fixed */ Trace((stderr, "inflate: fixed codes block%s\n", s->last ? " (last)" : "")); { uInt bl, bd; inflate_huft *tl, *td; inflate_trees_fixed(&bl, &bd, &tl, &td); s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); if (s->sub.decode.codes == Z_NULL) { r = Z_MEM_ERROR; LEAVE } s->sub.decode.tl = Z_NULL; /* don't try to free these */ s->sub.decode.td = Z_NULL; } DUMPBITS(3) s->mode = CODES; break; case 2: /* dynamic */ Trace((stderr, "inflate: dynamic codes block%s\n", s->last ? " (last)" : "")); DUMPBITS(3) s->mode = TABLE; break; case 3: /* illegal */ DUMPBITS(3) s->mode = BADB; z->msg = "invalid block type"; r = Z_DATA_ERROR; LEAVE } break; case LENS: NEEDBITS(32) if (((~b) >> 16) != (b & 0xffff)) { s->mode = BADB; z->msg = "invalid stored block lengths"; r = Z_DATA_ERROR; LEAVE } s->sub.left = (uInt)b & 0xffff; b = k = 0; /* dump bits */ Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); s->mode = s->sub.left ? STORED : TYPE; break; case STORED: if (n == 0) LEAVE NEEDOUT t = s->sub.left; if (t > n) t = n; if (t > m) t = m; zmemcpy(q, p, t); p += t; n -= t; q += t; m -= t; if ((s->sub.left -= t) != 0) break; Tracev((stderr, "inflate: stored end, %lu total out\n", z->total_out + (q >= s->read ? q - s->read : (s->end - s->read) + (q - s->window)))); s->mode = s->last ? DRY : TYPE; break; case TABLE: NEEDBITS(14) s->sub.trees.table = t = (uInt)b & 0x3fff; #ifndef PKZIP_BUG_WORKAROUND if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) { s->mode = BADB; z->msg = "too many length or distance symbols"; r = Z_DATA_ERROR; LEAVE } #endif t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); if (t < 19) t = 19; if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) { r = Z_MEM_ERROR; LEAVE } s->sub.trees.nblens = t; DUMPBITS(14) s->sub.trees.index = 0; Tracev((stderr, "inflate: table sizes ok\n")); s->mode = BTREE; case BTREE: while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) { NEEDBITS(3) s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; DUMPBITS(3) } while (s->sub.trees.index < 19) s->sub.trees.blens[border[s->sub.trees.index++]] = 0; s->sub.trees.bb = 7; t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, &s->sub.trees.tb, z); if (t != Z_OK) { r = t; if (r == Z_DATA_ERROR) s->mode = BADB; LEAVE } s->sub.trees.index = 0; Tracev((stderr, "inflate: bits tree ok\n")); s->mode = DTREE; case DTREE: while (t = s->sub.trees.table, s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) { inflate_huft *h; uInt i, j, c; t = s->sub.trees.bb; NEEDBITS(t) h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); t = h->word.what.Bits; c = h->more.Base; if (c < 16) { DUMPBITS(t) s->sub.trees.blens[s->sub.trees.index++] = c; } else /* c == 16..18 */ { i = c == 18 ? 7 : c - 14; j = c == 18 ? 11 : 3; NEEDBITS(t + i) DUMPBITS(t) j += (uInt)b & inflate_mask[i]; DUMPBITS(i) i = s->sub.trees.index; t = s->sub.trees.table; if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) { s->mode = BADB; z->msg = "invalid bit length repeat"; r = Z_DATA_ERROR; LEAVE } c = c == 16 ? s->sub.trees.blens[i - 1] : 0; do { s->sub.trees.blens[i++] = c; } while (--j); s->sub.trees.index = i; } } inflate_trees_free(s->sub.trees.tb, z); s->sub.trees.tb = Z_NULL; { uInt bl, bd; inflate_huft *tl, *td; inflate_codes_statef *c; bl = 9; /* must be <= 9 for lookahead assumptions */ bd = 6; /* must be <= 9 for lookahead assumptions */ t = s->sub.trees.table; t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), s->sub.trees.blens, &bl, &bd, &tl, &td, z); if (t != Z_OK) { if (t == (uInt)Z_DATA_ERROR) s->mode = BADB; r = t; LEAVE } Tracev((stderr, "inflate: trees ok\n")); if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) { inflate_trees_free(td, z); inflate_trees_free(tl, z); r = Z_MEM_ERROR; LEAVE } ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); s->sub.decode.codes = c; s->sub.decode.tl = tl; s->sub.decode.td = td; } s->mode = CODES; case CODES: UPDATE if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) return inflate_flush(s, z, r); r = Z_OK; inflate_codes_free(s->sub.decode.codes, z); inflate_trees_free(s->sub.decode.td, z); inflate_trees_free(s->sub.decode.tl, z); LOAD Tracev((stderr, "inflate: codes end, %lu total out\n", z->total_out + (q >= s->read ? q - s->read : (s->end - s->read) + (q - s->window)))); if (!s->last) { s->mode = TYPE; break; } if (k > 7) /* return unused byte, if any */ { Assert(k < 16, "inflate_codes grabbed too many bytes") k -= 8; n++; p--; /* can always return one */ } s->mode = DRY; case DRY: FLUSH if (s->read != s->write) LEAVE s->mode = DONEB; case DONEB: r = Z_STREAM_END; LEAVE case BADB: r = Z_DATA_ERROR; LEAVE default: r = Z_STREAM_ERROR; LEAVE } } local int inflate_blocks_free(s, z, c) inflate_blocks_statef *s; z_stream *z; uLongf *c; { inflate_blocks_reset(s, z, c); ZFREE(z, s->window, s->end - s->window); ZFREE(z, s, sizeof(struct inflate_blocks_state)); Trace((stderr, "inflate: blocks freed\n")); return Z_OK; } /* * This subroutine adds the data at next_in/avail_in to the output history * without performing any output. The output buffer must be "caught up"; * i.e. no pending output (hence s->read equals s->write), and the state must * be BLOCKS (i.e. we should be willing to see the start of a series of * BLOCKS). On exit, the output will also be caught up, and the checksum * will have been updated if need be. */ local int inflate_addhistory(s, z) inflate_blocks_statef *s; z_stream *z; { uLong b; /* bit buffer */ /* NOT USED HERE */ uInt k; /* bits in bit buffer */ /* NOT USED HERE */ uInt t; /* temporary storage */ Bytef *p; /* input data pointer */ uInt n; /* bytes available there */ Bytef *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ if (s->read != s->write) return Z_STREAM_ERROR; if (s->mode != TYPE) return Z_DATA_ERROR; /* we're ready to rock */ LOAD /* while there is input ready, copy to output buffer, moving * pointers as needed. */ while (n) { t = n; /* how many to do */ /* is there room until end of buffer? */ if (t > m) t = m; /* update check information */ if (s->checkfn != Z_NULL) s->check = (*s->checkfn)(s->check, q, t); zmemcpy(q, p, t); q += t; p += t; n -= t; z->total_out += t; s->read = q; /* drag read pointer forward */ /* WRAP */ /* expand WRAP macro by hand to handle s->read */ if (q == s->end) { s->read = q = s->window; m = WAVAIL; } } UPDATE return Z_OK; } /* * At the end of a Deflate-compressed PPP packet, we expect to have seen * a `stored' block type value but not the (zero) length bytes. */ local int inflate_packet_flush(s) inflate_blocks_statef *s; { if (s->mode != LENS) return Z_DATA_ERROR; s->mode = TYPE; return Z_OK; } /*+++++*/ /* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* simplify the use of the inflate_huft type with some defines */ #define base more.Base #define next more.Next #define exop word.what.Exop #define bits word.what.Bits local int huft_build OF(( uIntf *, /* code lengths in bits */ uInt, /* number of codes */ uInt, /* number of "simple" codes */ uIntf *, /* list of base values for non-simple codes */ uIntf *, /* list of extra bits for non-simple codes */ inflate_huft * FAR*,/* result: starting table */ uIntf *, /* maximum lookup bits (returns actual) */ z_stream *)); /* for zalloc function */ local voidpf falloc OF(( voidpf, /* opaque pointer (not used) */ uInt, /* number of items */ uInt)); /* size of item */ local void ffree OF(( voidpf q, /* opaque pointer (not used) */ voidpf p, /* what to free (not used) */ uInt n)); /* number of bytes (not used) */ /* Tables for deflate from PKZIP's appnote.txt. */ local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; /* actually lengths - 2; also see note #13 above about 258 */ local uInt cplext[] = { /* Extra bits for literal codes 257..285 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */ local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; local uInt cpdext[] = { /* Extra bits for distance codes */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /* Huffman code decoding is performed using a multi-level table lookup. The fastest way to decode is to simply build a lookup table whose size is determined by the longest code. However, the time it takes to build this table can also be a factor if the data being decoded is not very long. The most common codes are necessarily the shortest codes, so those codes dominate the decoding time, and hence the speed. The idea is you can have a shorter table that decodes the shorter, more probable codes, and then point to subsidiary tables for the longer codes. The time it costs to decode the longer codes is then traded against the time it takes to make longer tables. This results of this trade are in the variables lbits and dbits below. lbits is the number of bits the first level table for literal/ length codes can decode in one step, and dbits is the same thing for the distance codes. Subsequent tables are also less than or equal to those sizes. These values may be adjusted either when all of the codes are shorter than that, in which case the longest code length in bits is used, or when the shortest code is *longer* than the requested table size, in which case the length of the shortest code in bits is used. There are two different values for the two tables, since they code a different number of possibilities each. The literal/length table codes 286 possible values, or in a flat code, a little over eight bits. The distance table codes 30 possible values, or a little less than five bits, flat. The optimum values for speed end up being about one bit more than those, so lbits is 8+1 and dbits is 5+1. The optimum values may differ though from machine to machine, and possibly even between compilers. Your mileage may vary. */ /* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ #define BMAX 15 /* maximum bit length of any code */ #define N_MAX 288 /* maximum number of codes in any set */ #ifdef DEBUG_ZLIB uInt inflate_hufts; #endif local int huft_build(b, n, s, d, e, t, m, zs) uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ uInt n; /* number of codes (assumed <= N_MAX) */ uInt s; /* number of simple-valued codes (0..s-1) */ uIntf *d; /* list of base values for non-simple codes */ uIntf *e; /* list of extra bits for non-simple codes */ inflate_huft * FAR *t; /* result: starting table */ uIntf *m; /* maximum lookup bits, returns actual */ z_stream *zs; /* for zalloc function */ /* Given a list of code lengths and a maximum table size, make a set of tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR if the given code set is incomplete (the tables are still built in this case), Z_DATA_ERROR if the input is invalid (all zero length codes or an over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */ { uInt a; /* counter for codes of length k */ uInt c[BMAX+1]; /* bit length count table */ uInt f; /* i repeats in table every f entries */ int g; /* maximum code length */ int h; /* table level */ register uInt i; /* counter, current code */ register uInt j; /* counter */ register int k; /* number of bits in current code */ int l; /* bits per table (returned in m) */ register uIntf *p; /* pointer into c[], b[], or v[] */ inflate_huft *q; /* points to current table */ struct inflate_huft_s r; /* table entry for structure assignment */ inflate_huft *u[BMAX]; /* table stack */ uInt v[N_MAX]; /* values in order of bit length */ register int w; /* bits before this table == (l * h) */ uInt x[BMAX+1]; /* bit offsets, then code stack */ uIntf *xp; /* pointer into x */ int y; /* number of dummy codes added */ uInt z; /* number of entries in current table */ /* Generate counts for each bit length */ p = c; #define C0 *p++ = 0; #define C2 C0 C0 C0 C0 #define C4 C2 C2 C2 C2 C4 /* clear c[]--assume BMAX+1 is 16 */ p = b; i = n; do { c[*p++]++; /* assume all entries <= BMAX */ } while (--i); if (c[0] == n) /* null input--all zero length codes */ { *t = (inflate_huft *)Z_NULL; *m = 0; return Z_OK; } /* Find minimum and maximum length, bound *m by those */ l = *m; for (j = 1; j <= BMAX; j++) if (c[j]) break; k = j; /* minimum code length */ if ((uInt)l < j) l = j; for (i = BMAX; i; i--) if (c[i]) break; g = i; /* maximum code length */ if ((uInt)l > i) l = i; *m = l; /* Adjust last length count to fill out codes, if needed */ for (y = 1 << j; j < i; j++, y <<= 1) if ((y -= c[j]) < 0) return Z_DATA_ERROR; if ((y -= c[i]) < 0) return Z_DATA_ERROR; c[i] += y; /* Generate starting offsets into the value table for each length */ x[1] = j = 0; p = c + 1; xp = x + 2; while (--i) { /* note that i == g from above */ *xp++ = (j += *p++); } /* Make a table of values in order of bit lengths */ p = b; i = 0; do { if ((j = *p++) != 0) v[x[j]++] = i; } while (++i < n); /* Generate the Huffman codes and for each, make the table entries */ x[0] = i = 0; /* first Huffman code is zero */ p = v; /* grab values in bit order */ h = -1; /* no tables yet--level -1 */ w = -l; /* bits decoded == (l * h) */ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ q = (inflate_huft *)Z_NULL; /* ditto */ z = 0; /* ditto */ /* go through the bit lengths (k already is bits in shortest code) */ for (; k <= g; k++) { a = c[k]; while (a--) { /* here i is the Huffman code of length k bits for value *p */ /* make tables up to required level */ while (k > w + l) { h++; w += l; /* previous table always l bits */ /* compute minimum size table less than or equal to l bits */ z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ { /* too few codes for k-w bit table */ f -= a + 1; /* deduct codes from patterns left */ xp = c + k; if (j < z) while (++j < z) /* try smaller tables up to z bits */ { if ((f <<= 1) <= *++xp) break; /* enough codes to use up j bits */ f -= *xp; /* else deduct codes from patterns */ } } z = 1 << j; /* table entries for j-bit table */ /* allocate and link in new table */ if ((q = (inflate_huft *)ZALLOC (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) { if (h) inflate_trees_free(u[0], zs); return Z_MEM_ERROR; /* not enough memory */ } q->word.Nalloc = z + 1; #ifdef DEBUG_ZLIB inflate_hufts += z + 1; #endif *t = q + 1; /* link to list for huft_free() */ *(t = &(q->next)) = Z_NULL; u[h] = ++q; /* table starts after link */ /* connect to last table, if there is one */ if (h) { x[h] = i; /* save pattern for backing up */ r.bits = (Byte)l; /* bits to dump before this table */ r.exop = (Byte)j; /* bits in this table */ r.next = q; /* pointer to this table */ j = i >> (w - l); /* (get around Turbo C bug) */ u[h-1][j] = r; /* connect to last table */ } } /* set up table entry in r */ r.bits = (Byte)(k - w); if (p >= v + n) r.exop = 128 + 64; /* out of values--invalid code */ else if (*p < s) { r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ r.base = *p++; /* simple code is just the value */ } else { r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */ r.base = d[*p++ - s]; } /* fill code-like entries with r */ f = 1 << (k - w); for (j = i >> w; j < z; j += f) q[j] = r; /* backwards increment the k-bit code i */ for (j = 1 << (k - 1); i & j; j >>= 1) i ^= j; i ^= j; /* backup over finished tables */ while ((i & ((1 << w) - 1)) != x[h]) { h--; /* don't need to update q */ w -= l; } } } /* Return Z_BUF_ERROR if we were given an incomplete table */ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; } local int inflate_trees_bits(c, bb, tb, z) uIntf *c; /* 19 code lengths */ uIntf *bb; /* bits tree desired/actual depth */ inflate_huft * FAR *tb; /* bits tree result */ z_stream *z; /* for zfree function */ { int r; r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); if (r == Z_DATA_ERROR) z->msg = "oversubscribed dynamic bit lengths tree"; else if (r == Z_BUF_ERROR) { inflate_trees_free(*tb, z); z->msg = "incomplete dynamic bit lengths tree"; r = Z_DATA_ERROR; } return r; } local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) uInt nl; /* number of literal/length codes */ uInt nd; /* number of distance codes */ uIntf *c; /* that many (total) code lengths */ uIntf *bl; /* literal desired/actual bit depth */ uIntf *bd; /* distance desired/actual bit depth */ inflate_huft * FAR *tl; /* literal/length tree result */ inflate_huft * FAR *td; /* distance tree result */ z_stream *z; /* for zfree function */ { int r; /* build literal/length tree */ if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK) { if (r == Z_DATA_ERROR) z->msg = "oversubscribed literal/length tree"; else if (r == Z_BUF_ERROR) { inflate_trees_free(*tl, z); z->msg = "incomplete literal/length tree"; r = Z_DATA_ERROR; } return r; } /* build distance tree */ if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK) { if (r == Z_DATA_ERROR) z->msg = "oversubscribed literal/length tree"; else if (r == Z_BUF_ERROR) { #ifdef PKZIP_BUG_WORKAROUND r = Z_OK; } #else inflate_trees_free(*td, z); z->msg = "incomplete literal/length tree"; r = Z_DATA_ERROR; } inflate_trees_free(*tl, z); return r; #endif } /* done */ return Z_OK; } /* build fixed tables only once--keep them here */ local int fixed_lock = 0; local int fixed_built = 0; #define FIXEDH 530 /* number of hufts used by fixed tables */ local uInt fixed_left = FIXEDH; local inflate_huft fixed_mem[FIXEDH]; local uInt fixed_bl; local uInt fixed_bd; local inflate_huft *fixed_tl; local inflate_huft *fixed_td; local voidpf falloc(q, n, s) voidpf q; /* opaque pointer (not used) */ uInt n; /* number of items */ uInt s; /* size of item */ { Assert(s == sizeof(inflate_huft) && n <= fixed_left, "inflate_trees falloc overflow"); if (q) s++; /* to make some compilers happy */ fixed_left -= n; return (voidpf)(fixed_mem + fixed_left); } local void ffree(q, p, n) voidpf q; voidpf p; uInt n; { Assert(0, "inflate_trees ffree called!"); if (q) q = p; /* to make some compilers happy */ } local int inflate_trees_fixed(bl, bd, tl, td) uIntf *bl; /* literal desired/actual bit depth */ uIntf *bd; /* distance desired/actual bit depth */ inflate_huft * FAR *tl; /* literal/length tree result */ inflate_huft * FAR *td; /* distance tree result */ { /* build fixed tables if not built already--lock out other instances */ while (++fixed_lock > 1) fixed_lock--; if (!fixed_built) { int k; /* temporary variable */ unsigned c[288]; /* length list for huft_build */ z_stream z; /* for falloc function */ /* set up fake z_stream for memory routines */ z.zalloc = falloc; z.zfree = ffree; z.opaque = Z_NULL; /* literal table */ for (k = 0; k < 144; k++) c[k] = 8; for (; k < 256; k++) c[k] = 9; for (; k < 280; k++) c[k] = 7; for (; k < 288; k++) c[k] = 8; fixed_bl = 7; huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); /* distance table */ for (k = 0; k < 30; k++) c[k] = 5; fixed_bd = 5; huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); /* done */ fixed_built = 1; } fixed_lock--; *bl = fixed_bl; *bd = fixed_bd; *tl = fixed_tl; *td = fixed_td; return Z_OK; } local int inflate_trees_free(t, z) inflate_huft *t; /* table to free */ z_stream *z; /* for zfree function */ /* Free the malloc'ed tables built by huft_build(), which makes a linked list of the tables it made, with the links in a dummy first entry of each table. */ { register inflate_huft *p, *q; /* Go through linked list, freeing from the malloced (t[-1]) address. */ p = t; while (p != Z_NULL) { q = (--p)->next; ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft)); p = q; } return Z_OK; } /*+++++*/ /* infcodes.c -- process literals and length/distance pairs * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* simplify the use of the inflate_huft type with some defines */ #define base more.Base #define next more.Next #define exop word.what.Exop #define bits word.what.Bits /* inflate codes private state */ struct inflate_codes_state { /* mode */ enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ START, /* x: set up for LEN */ LEN, /* i: get length/literal/eob next */ LENEXT, /* i: getting length extra (have base) */ DIST, /* i: get distance next */ DISTEXT, /* i: getting distance extra */ COPY, /* o: copying bytes in window, waiting for space */ LIT, /* o: got literal, waiting for output space */ WASH, /* o: got eob, possibly still output waiting */ END, /* x: got eob and all data flushed */ BADCODE} /* x: got error */ mode; /* current inflate_codes mode */ /* mode dependent information */ uInt len; union { struct { inflate_huft *tree; /* pointer into tree */ uInt need; /* bits needed */ } code; /* if LEN or DIST, where in tree */ uInt lit; /* if LIT, literal */ struct { uInt get; /* bits to get for extra */ uInt dist; /* distance back to copy from */ } copy; /* if EXT or COPY, where and how much */ } sub; /* submode */ /* mode independent information */ Byte lbits; /* ltree bits decoded per branch */ Byte dbits; /* dtree bits decoder per branch */ inflate_huft *ltree; /* literal/length/eob tree */ inflate_huft *dtree; /* distance tree */ }; local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) uInt bl, bd; inflate_huft *tl, *td; z_stream *z; { inflate_codes_statef *c; if ((c = (inflate_codes_statef *) ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) { c->mode = START; c->lbits = (Byte)bl; c->dbits = (Byte)bd; c->ltree = tl; c->dtree = td; Tracev((stderr, "inflate: codes new\n")); } return c; } local int inflate_codes(s, z, r) inflate_blocks_statef *s; z_stream *z; int r; { uInt j; /* temporary storage */ inflate_huft *t; /* temporary pointer */ uInt e; /* extra bits or operation */ uLong b; /* bit buffer */ uInt k; /* bits in bit buffer */ Bytef *p; /* input data pointer */ uInt n; /* bytes available there */ Bytef *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ Bytef *f; /* pointer to copy strings from */ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ /* copy input/output information to locals (UPDATE macro restores) */ LOAD /* process input and output based on current state */ while (1) switch (c->mode) { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ case START: /* x: set up for LEN */ #ifndef SLOW if (m >= 258 && n >= 10) { UPDATE r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); LOAD if (r != Z_OK) { c->mode = r == Z_STREAM_END ? WASH : BADCODE; break; } } #endif /* !SLOW */ c->sub.code.need = c->lbits; c->sub.code.tree = c->ltree; c->mode = LEN; case LEN: /* i: get length/literal/eob next */ j = c->sub.code.need; NEEDBITS(j) t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); DUMPBITS(t->bits) e = (uInt)(t->exop); if (e == 0) /* literal */ { c->sub.lit = t->base; Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", t->base)); c->mode = LIT; break; } if (e & 16) /* length */ { c->sub.copy.get = e & 15; c->len = t->base; c->mode = LENEXT; break; } if ((e & 64) == 0) /* next table */ { c->sub.code.need = e; c->sub.code.tree = t->next; break; } if (e & 32) /* end of block */ { Tracevv((stderr, "inflate: end of block\n")); c->mode = WASH; break; } c->mode = BADCODE; /* invalid code */ z->msg = "invalid literal/length code"; r = Z_DATA_ERROR; LEAVE case LENEXT: /* i: getting length extra (have base) */ j = c->sub.copy.get; NEEDBITS(j) c->len += (uInt)b & inflate_mask[j]; DUMPBITS(j) c->sub.code.need = c->dbits; c->sub.code.tree = c->dtree; Tracevv((stderr, "inflate: length %u\n", c->len)); c->mode = DIST; case DIST: /* i: get distance next */ j = c->sub.code.need; NEEDBITS(j) t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); DUMPBITS(t->bits) e = (uInt)(t->exop); if (e & 16) /* distance */ { c->sub.copy.get = e & 15; c->sub.copy.dist = t->base; c->mode = DISTEXT; break; } if ((e & 64) == 0) /* next table */ { c->sub.code.need = e; c->sub.code.tree = t->next; break; } c->mode = BADCODE; /* invalid code */ z->msg = "invalid distance code"; r = Z_DATA_ERROR; LEAVE case DISTEXT: /* i: getting distance extra */ j = c->sub.copy.get; NEEDBITS(j) c->sub.copy.dist += (uInt)b & inflate_mask[j]; DUMPBITS(j) Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); c->mode = COPY; case COPY: /* o: copying bytes in window, waiting for space */ #ifndef __TURBOC__ /* Turbo C bug for following expression */ f = (uInt)(q - s->window) < c->sub.copy.dist ? s->end - (c->sub.copy.dist - (q - s->window)) : q - c->sub.copy.dist; #else f = q - c->sub.copy.dist; if ((uInt)(q - s->window) < c->sub.copy.dist) f = s->end - (c->sub.copy.dist - (q - s->window)); #endif while (c->len) { NEEDOUT OUTBYTE(*f++) if (f == s->end) f = s->window; c->len--; } c->mode = START; break; case LIT: /* o: got literal, waiting for output space */ NEEDOUT OUTBYTE(c->sub.lit) c->mode = START; break; case WASH: /* o: got eob, possibly more output */ FLUSH if (s->read != s->write) LEAVE c->mode = END; case END: r = Z_STREAM_END; LEAVE case BADCODE: /* x: got error */ r = Z_DATA_ERROR; LEAVE default: r = Z_STREAM_ERROR; LEAVE } } local void inflate_codes_free(c, z) inflate_codes_statef *c; z_stream *z; { ZFREE(z, c, sizeof(struct inflate_codes_state)); Tracev((stderr, "inflate: codes free\n")); } /*+++++*/ /* inflate_util.c -- data and routines common to blocks and codes * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* copy as much as possible from the sliding window to the output area */ local int inflate_flush(s, z, r) inflate_blocks_statef *s; z_stream *z; int r; { uInt n; Bytef *p, *q; /* local copies of source and destination pointers */ p = z->next_out; q = s->read; /* compute number of bytes to copy as far as end of window */ n = (uInt)((q <= s->write ? s->write : s->end) - q); if (n > z->avail_out) n = z->avail_out; if (n && r == Z_BUF_ERROR) r = Z_OK; /* update counters */ z->avail_out -= n; z->total_out += n; /* update check information */ if (s->checkfn != Z_NULL) s->check = (*s->checkfn)(s->check, q, n); /* copy as far as end of window */ if (p != NULL) { zmemcpy(p, q, n); p += n; } q += n; /* see if more to copy at beginning of window */ if (q == s->end) { /* wrap pointers */ q = s->window; if (s->write == s->end) s->write = s->window; /* compute bytes to copy */ n = (uInt)(s->write - q); if (n > z->avail_out) n = z->avail_out; if (n && r == Z_BUF_ERROR) r = Z_OK; /* update counters */ z->avail_out -= n; z->total_out += n; /* update check information */ if (s->checkfn != Z_NULL) s->check = (*s->checkfn)(s->check, q, n); /* copy */ if (p != NULL) { zmemcpy(p, q, n); p += n; } q += n; } /* update pointers */ z->next_out = p; s->read = q; /* done */ return r; } /*+++++*/ /* inffast.c -- process literals and length/distance pairs fast * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* simplify the use of the inflate_huft type with some defines */ #define base more.Base #define next more.Next #define exop word.what.Exop #define bits word.what.Bits /* macros for bit input with no checking and for returning unused bytes */ #define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<>3);p-=c;k&=7;} /* Called with number of bytes left to write in window at least 258 (the maximum string length) and number of input bytes available at least ten. The ten bytes are six bytes for the longest length/ distance pair plus four bytes for overloading the bit buffer. */ local int inflate_fast(bl, bd, tl, td, s, z) uInt bl, bd; inflate_huft *tl, *td; inflate_blocks_statef *s; z_stream *z; { inflate_huft *t; /* temporary pointer */ uInt e; /* extra bits or operation */ uLong b; /* bit buffer */ uInt k; /* bits in bit buffer */ Bytef *p; /* input data pointer */ uInt n; /* bytes available there */ Bytef *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ uInt ml; /* mask for literal/length tree */ uInt md; /* mask for distance tree */ uInt c; /* bytes to copy */ uInt d; /* distance back to copy from */ Bytef *r; /* copy source pointer */ /* load input, output, bit values */ LOAD /* initialize masks */ ml = inflate_mask[bl]; md = inflate_mask[bd]; /* do until not enough input or output space for fast loop */ do { /* assume called with m >= 258 && n >= 10 */ /* get literal/length code */ GRABBITS(20) /* max bits for literal/length code */ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) { DUMPBITS(t->bits) Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? "inflate: * literal '%c'\n" : "inflate: * literal 0x%02x\n", t->base)); *q++ = (Byte)t->base; m--; continue; } do { DUMPBITS(t->bits) if (e & 16) { /* get extra bits for length */ e &= 15; c = t->base + ((uInt)b & inflate_mask[e]); DUMPBITS(e) Tracevv((stderr, "inflate: * length %u\n", c)); /* decode distance base of block to copy */ GRABBITS(15); /* max bits for distance code */ e = (t = td + ((uInt)b & md))->exop; do { DUMPBITS(t->bits) if (e & 16) { /* get extra bits to add to distance base */ e &= 15; GRABBITS(e) /* get extra bits (up to 13) */ d = t->base + ((uInt)b & inflate_mask[e]); DUMPBITS(e) Tracevv((stderr, "inflate: * distance %u\n", d)); /* do the copy */ m -= c; if ((uInt)(q - s->window) >= d) /* offset before dest */ { /* just copy */ r = q - d; *q++ = *r++; c--; /* minimum count is three, */ *q++ = *r++; c--; /* so unroll loop a little */ } else /* else offset after destination */ { e = d - (q - s->window); /* bytes from offset to end */ r = s->end - e; /* pointer to offset */ if (c > e) /* if source crosses, */ { c -= e; /* copy to end of window */ do { *q++ = *r++; } while (--e); r = s->window; /* copy rest from start of window */ } } do { /* copy all or what's left */ *q++ = *r++; } while (--c); break; } else if ((e & 64) == 0) e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; else { z->msg = "invalid distance code"; UNGRAB UPDATE return Z_DATA_ERROR; } } while (1); break; } if ((e & 64) == 0) { if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) { DUMPBITS(t->bits) Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? "inflate: * literal '%c'\n" : "inflate: * literal 0x%02x\n", t->base)); *q++ = (Byte)t->base; m--; break; } } else if (e & 32) { Tracevv((stderr, "inflate: * end of block\n")); UNGRAB UPDATE return Z_STREAM_END; } else { z->msg = "invalid literal/length code"; UNGRAB UPDATE return Z_DATA_ERROR; } } while (1); } while (m >= 258 && n >= 10); /* not enough input or output--restore pointers and return */ UNGRAB UPDATE return Z_OK; } /*+++++*/ /* zutil.c -- target dependent utility functions for the compression library * Copyright (C) 1995 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */ char *zlib_version = ZLIB_VERSION; char *z_errmsg[] = { "stream end", /* Z_STREAM_END 1 */ "", /* Z_OK 0 */ "file error", /* Z_ERRNO (-1) */ "stream error", /* Z_STREAM_ERROR (-2) */ "data error", /* Z_DATA_ERROR (-3) */ "insufficient memory", /* Z_MEM_ERROR (-4) */ "buffer error", /* Z_BUF_ERROR (-5) */ ""}; /*+++++*/ /* adler32.c -- compute the Adler-32 checksum of a data stream * Copyright (C) 1995 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */ #define BASE 65521L /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #define DO1(buf) {s1 += *buf++; s2 += s1;} #define DO2(buf) DO1(buf); DO1(buf); #define DO4(buf) DO2(buf); DO2(buf); #define DO8(buf) DO4(buf); DO4(buf); #define DO16(buf) DO8(buf); DO8(buf); /* ========================================================================= */ uLong adler32(adler, buf, len) uLong adler; Bytef *buf; uInt len; { unsigned long s1 = adler & 0xffff; unsigned long s2 = (adler >> 16) & 0xffff; int k; if (buf == Z_NULL) return 1L; while (len > 0) { k = len < NMAX ? len : NMAX; len -= k; while (k >= 16) { DO16(buf); k -= 16; } if (k != 0) do { DO1(buf); } while (--k); s1 %= BASE; s2 %= BASE; } return (s2 << 16) | s1; } ppp-2.4.5/pppdump/zlib.h000066400000000000000000000660711130035057700151320ustar00rootroot00000000000000/* $Id: zlib.h,v 1.1 1999/03/23 03:21:58 paulus Exp $ */ /* * This file is derived from zlib.h and zconf.h from the zlib-0.95 * distribution by Jean-loup Gailly and Mark Adler, with some additions * by Paul Mackerras to aid in implementing Deflate compression and * decompression for PPP packets. */ /* zlib.h -- interface of the 'zlib' general purpose compression library version 0.95, Aug 16th, 1995. Copyright (C) 1995 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler gzip@prep.ai.mit.edu madler@alumni.caltech.edu */ #ifndef _ZLIB_H #define _ZLIB_H /* #include "zconf.h" */ /* included directly here */ /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */ /* The library does not install any signal handler. It is recommended to add at least a handler for SIGSEGV when decompressing; the library checks the consistency of the input data whenever possible but may go nuts for some forms of corrupted input. */ /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints * at addresses which are not a multiple of their size. * Under DOS, -DFAR=far or -DFAR=__far may be needed. */ #ifndef STDC # if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus) # define STDC # endif #endif #ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */ # include #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif #ifndef FAR # define FAR #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2 */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): 1 << (windowBits+2) + 1 << (memLevel+9) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus a few kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif typedef unsigned char Byte; /* 8 bits */ typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ typedef Byte FAR Bytef; typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte FAR *voidpf; typedef Byte *voidp; #endif /* end of original zconf.h */ #define ZLIB_VERSION "0.95P" /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms may be added later and will have the same stream interface. For compression the application must provide the output buffer and may optionally provide the input buffer for optimization. For decompression, the application must provide the input buffer and may optionally provide the output buffer for optimization. Compression can be done in a single step if the buffers are large enough (for example if an input file is mmap'ed), or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes)); struct internal_state; typedef struct z_stream_s { Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total nb of input bytes read so far */ Bytef *next_out; /* next output byte should be put there */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total nb of bytes output so far */ char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ voidp opaque; /* private data object passed to zalloc and zfree */ Byte data_type; /* best guess about the data type: ascii or binary */ } z_stream; /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return Z_NULL if there is not enough memory for the object. On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use in the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 #define Z_FULL_FLUSH 2 #define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */ #define Z_FINISH 4 #define Z_PACKET_FLUSH 5 /* See deflate() below for the usage of these constants */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) /* error codes for the compression/decompression functions */ #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_DEFAULT_STRATEGY 0 #define Z_BINARY 0 #define Z_ASCII 1 #define Z_UNKNOWN 2 /* Used to set the data_type field */ #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ extern char *zlib_version; /* The application can compare zlib_version and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. */ /* basic functions */ extern int deflateInit OF((z_stream *strm, int level)); /* Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 1 and 9: 1 gives best speed, 9 gives best compression. Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level. msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ extern int deflate OF((z_stream *strm, int flush)); /* Performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary (in interactive applications). Some output may be provided even if flush is not set. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If the parameter flush is set to Z_PARTIAL_FLUSH, the current compression block is terminated and flushed to the output buffer so that the decompressor can get all input data available so far. For method 9, a future variant on method 8, the current block will be flushed but not terminated. If flush is set to Z_FULL_FLUSH, the compression block is terminated, a special marker is output and the compression dictionary is discarded; this is useful to allow the decompressor to synchronize if one compressed block has been damaged (see inflateSync below). Flushing degrades compression and so should be used only when necessary. Using Z_FULL_FLUSH too often can seriously degrade the compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). If the parameter flush is set to Z_PACKET_FLUSH, the compression block is terminated, and a zero-length stored block is output, omitting the length bytes (the effect of this is that the 3-bit type code 000 for a stored block is output, and the output is then byte-aligned). This is designed for use at the end of a PPP packet. In addition, if the current compression block contains all the data since the last Z_PACKET_FLUSH, it is never output as a stored block. If the current compression block output as a static or dynamic block would not be at least `minCompression' bytes smaller than the original data, then nothing is output for that block. (The type code for the zero-length stored block is still output, resulting in a single zero byte being output for the whole packet.) `MinCompression' is a parameter to deflateInit2, or 0 if deflateInit is used. If the parameter flush is set to Z_FINISH, all pending input is processed, all pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space; if deflate returns with Z_OK, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used immediately after deflateInit if all the compression is to be done in a single step. In this case, avail_out must be at least 0.1% larger than avail_in plus 12 bytes. If deflate does not return Z_STREAM_END, then it must be called again as described above. deflate() may update data_type if it can make a good guess about the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible. */ extern int deflateEnd OF((z_stream *strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent. In the error case, msg may be set but then points to a static string (which must not be deallocated). */ extern int inflateInit OF((z_stream *strm)); /* Initializes the internal stream state for decompression. The fields zalloc and zfree must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, inflateInit updates them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory. msg is set to null if there is no error message. inflateInit does not perform any decompression: this will be done by inflate(). */ extern int inflate OF((z_stream *strm, int flush)); /* Performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in is updated and processing will resume at this point for the next call of inflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. inflate() always provides as much output as possible (until there is no more input data or no more space in the output buffer). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH, inflate flushes as much output as possible to the output buffer. The flushing behavior of inflate is not specified for values of the flush parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the current implementation actually flushes as much output as possible anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data has been consumed, it is expecting to see the length field of a stored block; if not, it returns Z_DATA_ERROR. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all the uncompressed data. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The next operation on this stream must be inflateEnd to deallocate the decompression state. The use of Z_FINISH is never required, but can be used to inform inflate that a faster routine may be used for the single inflate() call. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if the stream structure was inconsistent (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress is possible or if there was not enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then call inflateSync to look for a good compression block. */ extern int inflateEnd OF((z_stream *strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent. In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* advanced functions */ /* The following functions are needed only in some special applications. */ extern int deflateInit2 OF((z_stream *strm, int level, int method, int windowBits, int memLevel, int strategy, int minCompression)); /* This is another version of deflateInit with more compression options. The fields next_in, zalloc and zfree must be initialized before by the caller. The method parameter is the compression method. It must be 8 in this version of the library. (Method 9 will allow a 64K history buffer and partial block flushes.) The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library (the value 16 will be allowed for method 9). Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no string match). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. The minCompression parameter specifies the minimum reduction in size required for a compressed block to be output when Z_PACKET_FLUSH is used (see the description of deflate above). If next_in is not null, the library will use this buffer to hold also some history information; the buffer must either hold the entire input data, or have at least 1<<(windowBits+1) bytes and be writable. If next_in is null, the library will allocate its own history buffer (and leave next_in null). next_out need not be provided here but must be provided by the application for the next call of deflate(). If the history buffer is provided by the application, next_in must must never be changed by the application since the compressor maintains information inside this buffer from call to call; the application must provide more input only by increasing avail_in. next_in is always reset by the library in this case. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid method). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ extern int deflateCopy OF((z_stream *dest, z_stream *source)); /* Sets the destination stream as a complete copy of the source stream. If the source stream is using an application-supplied history buffer, a new buffer is allocated for the destination stream. The compressed output buffer is always application-supplied. It's the responsibility of the application to provide the correct values of next_out and avail_out for the next call of deflate. This function is useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal compression state which can be quite large, so this strategy is slow and can consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being NULL). msg is left unchanged in both source and destination. */ extern int deflateReset OF((z_stream *strm)); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate all the internal compression state. The stream will keep the same compression level and any other attributes that may have been set by deflateInit2. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being NULL). */ extern int inflateInit2 OF((z_stream *strm, int windowBits)); /* This is another version of inflateInit with more compression options. The fields next_out, zalloc and zfree must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library (the value 16 will be allowed soon). The default value is 15 if inflateInit is used instead. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. If next_out is not null, the library will use this buffer for the history buffer; the buffer must either be large enough to hold the entire output data, or have at least 1<.depend # makedepend $(CFLAGS) $(PPPSTATSRCS) ppp-2.4.5/pppstats/Makefile.sol2000066400000000000000000000005751130035057700165250ustar00rootroot00000000000000# # pppstats Makefile for SVR4 systems # $Id: Makefile.sol2,v 1.10 2002/09/07 05:15:25 carlsonj Exp $ # include ../Makedefs.com CFLAGS = -DSTREAMS -I../include $(COPTS) all: pppstats pppstats: pppstats.c $(CC) $(CFLAGS) -o pppstats pppstats.c install: pppstats $(INSTALL) -f $(BINDIR) pppstats $(INSTALL) -m 444 -f $(MANDIR)/man8 pppstats.8 clean: rm -f pppstats *~ core ppp-2.4.5/pppstats/pppstats.8000066400000000000000000000126271130035057700161570ustar00rootroot00000000000000.\" @(#) $Id: pppstats.8,v 1.4 2004/11/13 12:22:49 paulus Exp $ .TH PPPSTATS 8 "26 June 1995" .SH NAME pppstats \- print PPP statistics .SH SYNOPSIS .B pppstats [ .B \-a ] [ .B \-v ] [ .B \-r ] [ .B \-z ] [ .B \-c .I ] [ .B \-w .I ] [ .I interface ] .ti 12 .SH DESCRIPTION The .B pppstats utility reports PPP\-related statistics at regular intervals for the specified PPP interface. If the interface is unspecified, it will default to ppp0. The display is split horizontally into input and output sections containing columns of statistics describing the properties and volume of packets received and transmitted by the interface. .PP The options are as follows: .TP .B \-a Display absolute values rather than deltas. With this option, all reports show statistics for the time since the link was initiated. Without this option, the second and subsequent reports show statistics for the time since the last report. .TP .B \-c \fIcount Repeat the display .I count times. If this option is not specified, the default repeat count is 1 if the .B \-w option is not specified, otherwise infinity. .TP .B \-r Display additional statistics summarizing the compression ratio achieved by the packet compression algorithm in use. .TP .B \-v Display additional statistics relating to the performance of the Van Jacobson TCP header compression algorithm. .TP .B \-w \fIwait Pause .I wait seconds between each display. If this option is not specified, the default interval is 5 seconds. .TP .B \-z Instead of the standard display, show statistics indicating the performance of the packet compression algorithm in use. .PP The following fields are printed on the input side when the .B \-z option is not used: .TP .B IN The total number of bytes received by this interface. .TP .B PACK The total number of packets received by this interface. .TP .B VJCOMP The number of header-compressed TCP packets received by this interface. .TP .B VJUNC The number of header-uncompressed TCP packets received by this interface. Not reported when the .B \-r option is specified. .TP .B VJERR The number of corrupted or bogus header-compressed TCP packets received by this interface. Not reported when the .B \-r option is specified. .TP .B VJTOSS The number of VJ header-compressed TCP packets dropped on reception by this interface because of preceding errors. Only reported when the .B \-v option is specified. .TP .B NON-VJ The total number of non-TCP packets received by this interface. Only reported when the .B \-v option is specified. .TP .B RATIO The compression ratio achieved for received packets by the packet compression scheme in use, defined as the uncompressed size divided by the compressed size. Only reported when the .B \-r option is specified. .TP .B UBYTE The total number of bytes received, after decompression of compressed packets. Only reported when the .B \-r option is specified. .PP The following fields are printed on the output side: .TP .B OUT The total number of bytes transmitted from this interface. .TP .B PACK The total number of packets transmitted from this interface. .TP .B VJCOMP The number of TCP packets transmitted from this interface with VJ-compressed TCP headers. .TP .B VJUNC The number of TCP packets transmitted from this interface with VJ-uncompressed TCP headers. Not reported when the .B \-r option is specified. .TP .B NON-VJ The total number of non-TCP packets transmitted from this interface. Not reported when the .B \-r option is specified. .TP .B VJSRCH The number of searches for the cached header entry for a VJ header compressed TCP packet. Only reported when the .B \-v option is specified. .TP .B VJMISS The number of failed searches for the cached header entry for a VJ header compressed TCP packet. Only reported when the .B \-v option is specified. .TP .B RATIO The compression ratio achieved for transmitted packets by the packet compression scheme in use, defined as the size before compression divided by the compressed size. Only reported when the .B \-r option is specified. .TP .B UBYTE The total number of bytes to be transmitted, before packet compression is applied. Only reported when the .B \-r option is specified. .PP When the .B \-z option is specified, .Nm pppstats instead displays the following fields, relating to the packet compression algorithm currently in use. If packet compression is not in use, these fields will all display zeroes. The fields displayed on the input side are: .TP .B COMPRESSED BYTE The number of bytes of compressed packets received. .TP .B COMPRESSED PACK The number of compressed packets received. .TP .B INCOMPRESSIBLE BYTE The number of bytes of incompressible packets (that is, those which were transmitted in uncompressed form) received. .TP .B INCOMPRESSIBLE PACK The number of incompressible packets received. .TP .B COMP RATIO The recent compression ratio for incoming packets, defined as the uncompressed size divided by the compressed size (including both compressible and incompressible packets). .PP The fields displayed on the output side are: .TP .B COMPRESSED BYTE The number of bytes of compressed packets transmitted. .TP .B COMPRESSED PACK The number of compressed packets transmitted. .TP .B INCOMPRESSIBLE BYTE The number of bytes of incompressible packets transmitted (that is, those which were transmitted in uncompressed form). .TP .B INCOMPRESSIBLE PACK The number of incompressible packets transmitted. .TP .B COMP RATIO The recent compression ratio for outgoing packets. .SH SEE ALSO pppd(8) ppp-2.4.5/pppstats/pppstats.c000066400000000000000000000312221130035057700162220ustar00rootroot00000000000000/* * print PPP statistics: * pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface] * * -a Show absolute values rather than deltas * -d Show data rate (kB/s) rather than bytes * -v Show more stats for VJ TCP header compression * -r Show compression ratio * -z Show compression statistics instead of default display * * History: * perkins@cps.msu.edu: Added compression statistics and alternate * display. 11/94 * Brad Parker (brad@cayman.com) 6/92 * * from the original "slstats" by Van Jacobson * * Copyright (c) 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef __STDC__ #define const #endif #ifndef lint static const char rcsid[] = "$Id: pppstats.c,v 1.29 2002/10/27 12:56:26 fcusack Exp $"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifndef STREAMS #if defined(__linux__) && defined(__powerpc__) \ && (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0) /* kludge alert! */ #undef __GLIBC__ #endif #include /* *BSD, Linux, NeXT, Ultrix etc. */ #ifndef __linux__ #include #include #include #else /* Linux */ #if __GLIBC__ >= 2 #include /* glibc 2 conflicts with linux/types.h */ #include #else #include #include #endif #include #include #endif /* __linux__ */ #else /* STREAMS */ #include /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */ #include #include #endif /* STREAMS */ int vflag, rflag, zflag; /* select type of display */ int aflag; /* print absolute values, not deltas */ int dflag; /* print data rates, not bytes */ int interval, count; int infinite; int unit; int s; /* socket or /dev/ppp file descriptor */ int signalled; /* set if alarm goes off "early" */ char *progname; char *interface; #if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT) extern int optind; extern char *optarg; #endif /* * If PPP_DRV_NAME is not defined, use the legacy "ppp" as the * device name. */ #if !defined(PPP_DRV_NAME) #define PPP_DRV_NAME "ppp" #endif /* !defined(PPP_DRV_NAME) */ static void usage __P((void)); static void catchalarm __P((int)); static void get_ppp_stats __P((struct ppp_stats *)); static void get_ppp_cstats __P((struct ppp_comp_stats *)); static void intpr __P((void)); int main __P((int, char *argv[])); static void usage() { fprintf(stderr, "Usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n", progname); exit(1); } /* * Called if an interval expires before intpr has completed a loop. * Sets a flag to not wait for the alarm. */ static void catchalarm(arg) int arg; { signalled = 1; } #ifndef STREAMS static void get_ppp_stats(curp) struct ppp_stats *curp; { struct ifpppstatsreq req; memset (&req, 0, sizeof (req)); #ifdef __linux__ req.stats_ptr = (caddr_t) &req.stats; #undef ifr_name #define ifr_name ifr__name #endif strncpy(req.ifr_name, interface, sizeof(req.ifr_name)); if (ioctl(s, SIOCGPPPSTATS, &req) < 0) { fprintf(stderr, "%s: ", progname); if (errno == ENOTTY) fprintf(stderr, "kernel support missing\n"); else perror("couldn't get PPP statistics"); exit(1); } *curp = req.stats; } static void get_ppp_cstats(csp) struct ppp_comp_stats *csp; { struct ifpppcstatsreq creq; memset (&creq, 0, sizeof (creq)); #ifdef __linux__ creq.stats_ptr = (caddr_t) &creq.stats; #undef ifr_name #define ifr_name ifr__name #endif strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name)); if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) { fprintf(stderr, "%s: ", progname); if (errno == ENOTTY) { fprintf(stderr, "no kernel compression support\n"); if (zflag) exit(1); rflag = 0; } else { perror("couldn't get PPP compression stats"); exit(1); } } #ifdef __linux__ if (creq.stats.c.bytes_out == 0) { creq.stats.c.bytes_out = creq.stats.c.comp_bytes + creq.stats.c.inc_bytes; creq.stats.c.in_count = creq.stats.c.unc_bytes; } if (creq.stats.c.bytes_out == 0) creq.stats.c.ratio = 0.0; else creq.stats.c.ratio = 256.0 * creq.stats.c.in_count / creq.stats.c.bytes_out; if (creq.stats.d.bytes_out == 0) { creq.stats.d.bytes_out = creq.stats.d.comp_bytes + creq.stats.d.inc_bytes; creq.stats.d.in_count = creq.stats.d.unc_bytes; } if (creq.stats.d.bytes_out == 0) creq.stats.d.ratio = 0.0; else creq.stats.d.ratio = 256.0 * creq.stats.d.in_count / creq.stats.d.bytes_out; #endif *csp = creq.stats; } #else /* STREAMS */ int strioctl(fd, cmd, ptr, ilen, olen) int fd, cmd, ilen, olen; char *ptr; { struct strioctl str; str.ic_cmd = cmd; str.ic_timout = 0; str.ic_len = ilen; str.ic_dp = ptr; if (ioctl(fd, I_STR, &str) == -1) return -1; if (str.ic_len != olen) fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n", olen, str.ic_len, cmd); return 0; } static void get_ppp_stats(curp) struct ppp_stats *curp; { if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) { fprintf(stderr, "%s: ", progname); if (errno == EINVAL) fprintf(stderr, "kernel support missing\n"); else perror("couldn't get PPP statistics"); exit(1); } } static void get_ppp_cstats(csp) struct ppp_comp_stats *csp; { if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) { fprintf(stderr, "%s: ", progname); if (errno == ENOTTY) { fprintf(stderr, "no kernel compression support\n"); if (zflag) exit(1); rflag = 0; } else { perror("couldn't get PPP compression statistics"); exit(1); } } } #endif /* STREAMS */ #define MAX0(a) ((int)(a) > 0? (a): 0) #define V(offset) MAX0(cur.offset - old.offset) #define W(offset) MAX0(ccs.offset - ocs.offset) #define RATIO(c, i, u) ((c) == 0? 1.0: (u) / ((double)(c) + (i))) #define CRATE(x) RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes)) #define KBPS(n) ((n) / (interval * 1000.0)) /* * Print a running summary of interface statistics. * Repeat display every interval seconds, showing statistics * collected over that interval. Assumes that interval is non-zero. * First line printed is cumulative. */ static void intpr() { register int line = 0; sigset_t oldmask, mask; char *bunit; int ratef = 0; struct ppp_stats cur, old; struct ppp_comp_stats ccs, ocs; memset(&old, 0, sizeof(old)); memset(&ocs, 0, sizeof(ocs)); while (1) { get_ppp_stats(&cur); if (zflag || rflag) get_ppp_cstats(&ccs); (void)signal(SIGALRM, catchalarm); signalled = 0; (void)alarm(interval); if ((line % 20) == 0) { if (zflag) { printf("IN: COMPRESSED INCOMPRESSIBLE COMP | "); printf("OUT: COMPRESSED INCOMPRESSIBLE COMP\n"); bunit = dflag? "KB/S": "BYTE"; printf(" %s PACK %s PACK RATIO | ", bunit, bunit); printf(" %s PACK %s PACK RATIO", bunit, bunit); } else { printf("%8.8s %6.6s %6.6s", "IN", "PACK", "VJCOMP"); if (!rflag) printf(" %6.6s %6.6s", "VJUNC", "VJERR"); if (vflag) printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ"); if (rflag) printf(" %6.6s %6.6s", "RATIO", "UBYTE"); printf(" | %8.8s %6.6s %6.6s", "OUT", "PACK", "VJCOMP"); if (!rflag) printf(" %6.6s %6.6s", "VJUNC", "NON-VJ"); if (vflag) printf(" %6.6s %6.6s", "VJSRCH", "VJMISS"); if (rflag) printf(" %6.6s %6.6s", "RATIO", "UBYTE"); } putchar('\n'); } if (zflag) { if (ratef) { printf("%8.3f %6u %8.3f %6u %6.2f", KBPS(W(d.comp_bytes)), W(d.comp_packets), KBPS(W(d.inc_bytes)), W(d.inc_packets), ccs.d.ratio / 256.0); printf(" | %8.3f %6u %8.3f %6u %6.2f", KBPS(W(c.comp_bytes)), W(c.comp_packets), KBPS(W(c.inc_bytes)), W(c.inc_packets), ccs.c.ratio / 256.0); } else { printf("%8u %6u %8u %6u %6.2f", W(d.comp_bytes), W(d.comp_packets), W(d.inc_bytes), W(d.inc_packets), ccs.d.ratio / 256.0); printf(" | %8u %6u %8u %6u %6.2f", W(c.comp_bytes), W(c.comp_packets), W(c.inc_bytes), W(c.inc_packets), ccs.c.ratio / 256.0); } } else { if (ratef) printf("%8.3f", KBPS(V(p.ppp_ibytes))); else printf("%8u", V(p.ppp_ibytes)); printf(" %6u %6u", V(p.ppp_ipackets), V(vj.vjs_compressedin)); if (!rflag) printf(" %6u %6u", V(vj.vjs_uncompressedin), V(vj.vjs_errorin)); if (vflag) printf(" %6u %6u", V(vj.vjs_tossed), V(p.ppp_ipackets) - V(vj.vjs_compressedin) - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin)); if (rflag) { printf(" %6.2f ", CRATE(d)); if (ratef) printf("%6.2f", KBPS(W(d.unc_bytes))); else printf("%6u", W(d.unc_bytes)); } if (ratef) printf(" | %8.3f", KBPS(V(p.ppp_obytes))); else printf(" | %8u", V(p.ppp_obytes)); printf(" %6u %6u", V(p.ppp_opackets), V(vj.vjs_compressed)); if (!rflag) printf(" %6u %6u", V(vj.vjs_packets) - V(vj.vjs_compressed), V(p.ppp_opackets) - V(vj.vjs_packets)); if (vflag) printf(" %6u %6u", V(vj.vjs_searches), V(vj.vjs_misses)); if (rflag) { printf(" %6.2f ", CRATE(c)); if (ratef) printf("%6.2f", KBPS(W(c.unc_bytes))); else printf("%6u", W(c.unc_bytes)); } } putchar('\n'); fflush(stdout); line++; count--; if (!infinite && !count) break; sigemptyset(&mask); sigaddset(&mask, SIGALRM); sigprocmask(SIG_BLOCK, &mask, &oldmask); if (!signalled) { sigemptyset(&mask); sigsuspend(&mask); } sigprocmask(SIG_SETMASK, &oldmask, NULL); signalled = 0; (void)alarm(interval); if (!aflag) { old = cur; ocs = ccs; ratef = dflag; } } } int main(argc, argv) int argc; char *argv[]; { int c; #ifdef STREAMS char *dev; #endif interface = PPP_DRV_NAME "0"; if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else ++progname; while ((c = getopt(argc, argv, "advrzc:w:")) != -1) { switch (c) { case 'a': ++aflag; break; case 'd': ++dflag; break; case 'v': ++vflag; break; case 'r': ++rflag; break; case 'z': ++zflag; break; case 'c': count = atoi(optarg); if (count <= 0) usage(); break; case 'w': interval = atoi(optarg); if (interval <= 0) usage(); break; default: usage(); } } argc -= optind; argv += optind; if (!interval && count) interval = 5; if (interval && !count) infinite = 1; if (!interval && !count) count = 1; if (aflag) dflag = 0; if (argc > 1) usage(); if (argc > 0) interface = argv[0]; if (sscanf(interface, PPP_DRV_NAME "%d", &unit) != 1) { fprintf(stderr, "%s: invalid interface '%s' specified\n", progname, interface); } #ifndef STREAMS { struct ifreq ifr; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { fprintf(stderr, "%s: ", progname); perror("couldn't create IP socket"); exit(1); } #ifdef __linux__ #undef ifr_name #define ifr_name ifr_ifrn.ifrn_name #endif strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { fprintf(stderr, "%s: nonexistent interface '%s' specified\n", progname, interface); exit(1); } } #else /* STREAMS */ #ifdef __osf__ dev = "/dev/streams/ppp"; #else dev = "/dev/" PPP_DRV_NAME; #endif if ((s = open(dev, O_RDONLY)) < 0) { fprintf(stderr, "%s: couldn't open ", progname); perror(dev); exit(1); } if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) { fprintf(stderr, "%s: ppp%d is not available\n", progname, unit); exit(1); } #endif /* STREAMS */ intpr(); exit(0); } ppp-2.4.5/scripts/000077500000000000000000000000001130035057700140115ustar00rootroot00000000000000ppp-2.4.5/scripts/README000066400000000000000000000140561130035057700146770ustar00rootroot00000000000000This directory contains a set of scripts which have been used on Linux as well as Solaris 2.x systems to initiate or maintain a connection with PPP. The files in this directory were contributed by Al Longyear (longyear@netcom.com) and Adi Masputra (adi.masputra@sun.com) ------------------------------------------------------------------------ 1. README This file. You are reading it. It is just documentation. ------------------------------------------------------------------------ 2. ppp-on This script will initiate a connection to the PPP system. It will run the chat program with the connection script as a parameter. This is a possible security hole. However, it is simple. It is meant to replace the previous version of ppp-on which was not very functional. The ppp-on script has entries for the account name, password, IP addresses, and telephone numbers. The parameters are passed to the pppd process and, then in turn, to the second part of the connect script, as a set of environment variables. Please make sure that you put the full path name to the ppp-on-dialer script in the reference to it in ppp-on. ------------------------------------------------------------------------ 3. ppp-on-dialer This is the second part to the simple calling script, ppp-on. It executes the chat program to connect the user with a standard UNIX style getty/login connection sequence. ------------------------------------------------------------------------ 4. callback This script may be used in lieu of the ppp-on-dialer to permit the common modem callback sequence. You may need to make changes to the expected prompt string for the modem. The script works by disabling the system's detection of the DCD condition and working on the modem status message "NO CARRIER" which is generated when the modem disconnects. It is crude. It does work for my modem connection. Use as you see fit. ------------------------------------------------------------------------ 5. redialer The redialer script is a replacement for the ppp-on-dialer script. It will do 'attack dialing' or 'demon dialing' of one or more telephone numbers. The first number which responds will be used for a connection. There is a limit of ten attempts and a 15 second delay between dialing attempts. Both values are set in the script. ------------------------------------------------------------------------ 6. ppp-off This is a script which will terminate the active ppp connection. Use as either "ppp-off" to terminate ppp0, or "ppp-off " to terminate the connection on . For example, "ppp-off ppp2" will terminate the ppp2 connection. ------------------------------------------------------------------------ 7. secure-card This script was written by Jim Isaacson . It is a script for the 'expect' programming language used with Tcl. You need to have expect and Tcl installed before this script may be used. This script will operate with a device marketed under the name "SecureCARD". This little device is mated with its controller. On the credit card size device, there is a sequence number which changes on a random basis. In order for you to connect you need to enter a fixed portion of your account name and the number which is displayed on this card device. The number must match the value at the controller in order for the account name to be used. The problem is that chat uses fixed response strings. In addition, the timing for running the script may prevent the use of a script that reads the value before it starts the dial sequence. What was needed was a script which asked the user at the user's console at the time that it is needed. This led to the use of expect. ------------------------------------------------------------------------ 8. ppp-on-rsh This script will initiate a PPP connection to a remote machine using rsh. This is implemented by creating a master/slave pseudo-tty with the slave pointing to rsh, specifically with the 'pty' and 'notty' options of pppd. It is assumed that the remote machine contains some sort of trust mechanisms (such as ~/.rhosts, et al) to allow the local machine to connect via rsh as root. ------------------------------------------------------------------------ 9. ppp-on-ssh This script will initiate a PPP connection to a remote machine using the secure shell, or ssh. I've only tested this on ssh 1.x, so those of you who are running ssh 2.x mahy need to modify the ssh options slightly. This is implemented by creating a master/slave pseudo-ttyt with the slave pointing to ssh, specifically with the 'pty' and 'notty' options of pppd. It is assumed that the remote machine can accept the ssh connection from the local host, in the sense that all ssh authentication mechanisms have been properly configured, so that a remote root user can open a ssh connection. ------------------------------------------------------------------------ 10. options-rsh-loc & options-rsh-rem These options files accompany the ppp-on-rsh script mentioned above. In theory, you'd want to copy the options-rsh-rem to the remote machine where in.rshd is running. The only extra option required on the remote machine options file is the 'notty' option. In addition, all ASCII control characters [0x00 to 0x1f], plus 0xff, are escaped. This may need to be modified depending on the rsh (or pseudo-tty) implementation which may differ across platforms, for further optimizations. ------------------------------------------------------------------------ 11. options-ssh-loc & options-ssh-rem These options files accompany the ppp-on-ssh script mentioned above. I've only tested this on ssh 1.x, so those of you who are running ssh 2.x need to modify the ssh options slightly. In theory, you'd want to copy the options-ssh-rem to the remote machine where sshd daemon is running. The only extra options required on the remote machine options file is the 'notty' option. In addition, all ASCII control characters [0x00 to 0x1f], plus 0xff, are escaped. This may need to be modified depending on the ssh (or pseudo-tty) implementation which may differ across platforms, for further optimizations. ppp-2.4.5/scripts/autopppd000077500000000000000000000127161130035057700156020ustar00rootroot00000000000000#!/usr/bin/perl -w # Auto dial script by Brian May use Proc::Daemon; use strict; use Sys::Syslog qw(:DEFAULT setlogsock); # default set, plus setlogsock use Proc::WaitStat qw(:DEFAULT waitstat); Proc::Daemon::Init; open(PIDFILE,">/var/run/autopppd.pid"); print(PIDFILE "$$"); close(PIDFILE); sub toseconds($) { my ($hours,$minutes,$seconds) = split(/:/,shift); return ($hours*60+$minutes)*60+$seconds; } sub dseconds($) { my ($total) = @_; my $seconds = $total % 60; $total = ($total - $seconds)/60; my $minutes = $total % 60; $total = ($total - $minutes)/60; my $hours = $total % 24; $total = ($total - $hours)/24; my $days = $total; if ($days > 0) { return(sprintf("%d-%02d:%02d:%02d",$days,$hours,$minutes,$seconds)); } else { return(sprintf("%02d:%02d:%02d",$hours,$minutes,$seconds)); } } my $program="autopppd"; setlogsock('unix'); openlog($program, 'cons,pid', 'daemon'); my $pppd_start_time; my $pppd_end_time; my $pppd_run_time; my $pppd_fail; my $delay=0; my $idelay=0; my @delays = ( toseconds("00:01:00"), # 1 minute toseconds("00:07:00"), # 8 minutes toseconds("00:07:00"), # 15 minutes toseconds("00:15:00"), # 30 minutes toseconds("00:30:00"), # 1 hour toseconds("01:00:00"), # 2 hours toseconds("01:00:00"), # 3 hours toseconds("03:00:00"), # 6 hours toseconds("06:00:00"), # 12 hours toseconds("12:00:00"), # 24 hours toseconds("24:00:00") # 48 hours ); # action == 0 => immediate retry (!FIXME! needs to have some delay) # action == 1 => delayed retry # action == 2 => abort my $code = { 0 => { message=>"pppd detached", action=> 2 }, 1 => { message=>"fatal error", action=> 2 }, 2 => { message=>"options error", action=> 2 }, 3 => { message=>"not setuid-root error", action=> 2 }, 4 => { message=>"no kernel support for PPP", action=> 2 }, 5 => { message=>"SIGINT or SIGTERM or SIGHUP", action=> 1 }, 6 => { message=>"Serial port locked", action=> 1 }, # should be 0 7 => { message=>"Serial port open error", action=> 1 }, 8 => { message=>"Connect failed", action=> 1 }, 9 => { message=>"Could not execute pty command", action=> 1 }, 10 => { message=>"PPP negotiation failed", action=> 1 }, 11 => { message=>"Peer failed to authenticate", action=> 1 }, 12 => { message=>"Link was idle", action=> 1 }, 13 => { message=>"Time limit exceeded", action=> 1 }, 14 => { message=>"call back not implemented", action=> 2 }, 15 => { message=>"peer not responding", action=> 1 }, 16 => { message=>"modem hang up", action=> 1 }, 17 => { message=>"Serial loopback detected", action=> 1 }, 18 => { message=>"Init script failed", action=> 1 }, 19 => { message=>"We failed to authenticate", action=> 1 }, }; while (1) { $pppd_start_time=time; syslog('info', 'restarting pppd'); # logging sometimes stopped working after ppp was running for # some time. lets see if closing and reopening the log file helps... closelog(); # run ppp my $rc=system("pppd","-detach",@ARGV); # reopon log file openlog($program, 'cons,pid', 'daemon'); # calculate run time $pppd_end_time=time; $pppd_run_time=$pppd_end_time-$pppd_start_time; my $pppd_code = ($? >> 8); my $pppd_signal = $? & 127; my $pppd_coredump = $? & 128; $pppd_fail = 1; if ($pppd_signal != 0) { if ($pppd_coredump) { syslog('err',"pppd died with signal $pppd_signal, coredump"); } else { syslog('err',"pppd died with signal $pppd_signal"); } } elsif ($pppd_coredump) { syslog('err',"pppd died with coredump"); } elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} == 0) { syslog('err', "pppd returned: ".$code->{$pppd_code}{"message"}." ($pppd_code), immediate retry"); $pppd_fail = 0; } elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} == 1) { syslog('err', "pppd returned: ".$code->{$pppd_code}{"message"}." ($pppd_code), delayed retry"); $pppd_fail = 1; } elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} >= 2) { syslog('err', "pppd returned: ".$code->{$pppd_code}{"message"}." ($pppd_code), aborting"); exit(255); } elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} >= 2) { syslog('err', "pppd returned: unknown error ($pppd_code), delayed retry"); $pppd_fail = 1; } # if it hasn't ran for at least an hour, then somthing went wrong elsif ($pppd_run_time < toseconds("01:00:00")) { syslog('err',"pppd session didn't last 1 hour, delayed retry"); $pppd_fail = 1; } else { $pppd_fail = 0; } # if not failed, then reset delay. if (!$pppd_fail) { $idelay = 0; } # get next delay. $delay = $delays[$idelay]; # log statistics. syslog('info',"rc=".waitstat($rc)." runtime=".dseconds($pppd_run_time)." delay[$idelay]=".dseconds($delay).""); # delay for desired time. sleep($delay); # increment delay for next time. if (defined($delays[$idelay+1])) { $idelay++; } } closelog(); ppp-2.4.5/scripts/callback000077500000000000000000000043751130035057700155040ustar00rootroot00000000000000#!/bin/sh ################################################################### # # Script to dial the remote system, negotiate the connection, and send # it the id. Then wait for the modem to disconnect. Reset the modem # to answer mode and wait for the system to call back. # # The telephone number and modempass are used when establishing the # connection to the modem. # PHONE=555-1212 MODEMPASS=modem_identifier # # Once the modem calls back, the account name and password are used for # a UNIX style login operation. # ACCOUNT=my_account_name PASSWORD=my_password ################################################################### # # Step 1. Dial the modem and negotiate the initial dialog. # note: the modem is configured to ignore loss of DCD at this point. # it is important that this be performed because the loss of DCD # will normally prevent system from working since 'modem' is used # for pppd. # # The script is terminated normally when the carrier is lost. # chat -v \ TIMEOUT 3 \ ABORT '\nBUSY\r' \ ABORT '\nNO ANSWER\r' \ ABORT '\nRINGING\r\n\r\nRINGING\r' \ '' AT \ 'OK-+++\c-OK' 'AT&C0&D2S0=0H0' \ TIMEOUT 30 \ OK ATDT$TELEPHONE \ CONNECT '' \ assword: $MODEMPASS \ "\nNO CARRIER\r" if [ "$?" = "0" ]; then ################################################################### # # Step 2. Wait for the call back from the remote. This will wait for at most # 30 seconds for the call back should the first attempt fail or # something happen with the callback logic at the remote. # # note: when the callback occurs, the DCD setting is re-enabled. # # If some voice call should happen during this period, the system will # answer the telephone and then hang up on them. I realize that this is # rude, but there is little that this script can do. # chat -v \ TIMEOUT 30 \ ABORT '\nVOICE\r' \ '\nRING\r' 'AT&C1A' \ CONNECT '' \ TIMEOUT 10 \ ogin:--ogin: $ACCOUNT \ TIMEOUT 45 \ assword: $PASSWORD if [ "$?" = "0" ]; then exit 0 fi fi ################################################################### # # The script has failed. Terminate the connection mode. # chat -v TIMEOUT 3 "" AT 'OK-+++\c-OK' 'AT&C1&D2S0=0H0' OK exit 1 ppp-2.4.5/scripts/chat-callback000066400000000000000000000053531130035057700164130ustar00rootroot00000000000000# ===================================================================================== # Chat script to dial our Company PPP account. # They uses a call-back system to identify us and to reverse # charge the call cost. # ===================================================================================== # ECHO OFF # All the usual abort strings ABORT "NO CARRIER" ABORT "VOICE" ABORT "BUSY" ABORT "NO DIALTONE" ABORT "NO ANSWER" # # If calling outside allowed time we get this: # ABORT "Access denied" # # Modem initialisation stuff # TIMEOUT 5 SAY "Initialising modem ...\n" '' ATE1 'OK\r\n' ATS0=1S11=60X4&K4S42.1=1 # # Now dial our ISP and wait for connection # SAY "Dialling our ISP ...\n" 'OK\r\n' ATDT09834657 TIMEOUT 60 CONNECT \c SAY "Connected ...\n" # # This is the first stage login, we identify ourself so that the remote # system will agree to call us back. # TIMEOUT 30 SAY "Sending Callback login ID ...\n" name:-BREAK-name: callme # # From now on, we must assume no carrier is normal as well # as receiving a HANGUP signal because it will be the # case if our ISP clears the call to call us back. # CLR_ABORT "NO CARRIER" HANGUP OFF # ABORT "Invalid" # # Now send password and wait to see what happens # SAY "Sending Callback password ...\n" word:--word: xvsgsgs "You will be" \c # # What can happen now is: # either: we get "You will be called back..." which is the successful case # or: we get "Invalid login" and we abort (bad login ID or password) # or: we get "NO CARRIER" because of an error, this will not abort # and we will time out after 30 seconds # or: we get nothing and we will time out after 30 seconds # # # We reach here if we got "You will be called back..." # CLR_ABORT "Invalid" SAY "Now waiting for Call back ...\n" # # The remote system will now hangup and we will get both "NO CARRIER" # and a hangup signal which are ignored. We now wait for a connection # for up to 120 seconds. What happens here if somebody else calls before # the remote system is a bit dangerous: # # If a malicious user connects and says 'name:', he will see 'PPPuser' # If he then says 'word:' he will see the passowrd 'blipblop'. I may not # know to which systems these belong to, though. It is up to you to consider # that case and decide wether the risk is too big or not .... # TIMEOUT 120 "CONNECT" \c # # We have been called, re-arm ABORT on NO CARRIER and normal hangup signal # behaviour # HANGUP ON ABORT "NO CARRIER" # # Second stage login in order to start PPP # SAY "Remote system called back, logging in ...\n" SAY "Sending login ID ...\n" name:-BREAK-name: PPPuser SAY "Sending password ...\n" word:--word: blipblop SAY "Asking to start PPP ...\n" 'CnetSrv' "ppp default" "Entering PPP mode" \c SAY "ISP PPP started ...\n" ppp-2.4.5/scripts/chatchat/000077500000000000000000000000001130035057700155705ustar00rootroot00000000000000ppp-2.4.5/scripts/chatchat/README000066400000000000000000000111151130035057700164470ustar00rootroot00000000000000v 0.1 gpk@onramp.net 3/27/99 I Intro This document covers the use of the modified "chat" program and its adjunct "chatchat" to login using the Security Dynamics SecurID card on a linux system. This set of files comprises a modified version of the chat program (the one distributed with ppp-2.3.5) and a new program called chatchat that allows you to supply data from the keyboard to the chat program. The SecurID card generates passwords that have a lifetime of one minute and are used as a first layer in dial up security. The only software I know of for this card is for windows, so I wrote my own. This software allows you to type in the time-sensitive password right when your chat script is asked to supply the passcode by the remote system. II How It Works This version of chat his an additional command that can be put into its options that says "Don't reply with this string. Open this pipe, read the contents, and reply with that instead." Chatchat creates a pipe and lets you type your passcode into it, then chat picks that up and sends it out just as though the passcode was hardcoded into the options. III Installation I've provided intel binaries and source code the the modified chat program and the chatchat program. I'll recommend that you copy the chat.c program into your ppp-2.3.5/chat directory (save your original chat.c program first!) and re-make it using the Makefile that comes with chat. Copy the new chat somewhere into your path. (On my system chat lives in /usr/sbin/chat, so I've copied the modified one into /usr/sbin/chat.new and changed my dial in script to call chat.new instead of chat. Second, compile chatchat.c and install it somewhere in your path: gcc -g -o chatchat chatchat.c cp chatchat /usr/sbin Third, modify your chat script to use the chatchat program. Mine looks something like this: -------------------- #!/bin/sh # # This is part 2 of the ppp-on script. It will perform the connection # protocol for the desired connection. # use atm0 to turn down the speaker volume on my sportster x2 voice modem # gpk 11/2/97 exec /usr/sbin/chat.new -V -v \ ABORT "BUSY" \ ABORT "NO DIAL TONE" \ ABORT "NO ANSWER" \ TIMEOUT 50 \ "" "atm0" \ OK ATDT$TELEPHONE \ CONNECT '' \ name: \\da0xxxxxx \ word: @/var/tmp/p \ compress. '' ----------------------- This is a standard chat script: * abort if the modem is busy, you don't get a dial tone, no one answers, or 50 seconds elapses. * use atm0 to mute the modem * dial the modem, when it connects, wait to be asked for account name * when we see "name:" prompt, delay briefly then respond with your account name (fill in your account name) Now we get to the new stuff: * when we see "word:" in the password prompt, instead of responding with "@/var/tmp/p", the modified chat program will open the pipe /var/tmp/p, read the passcode out of there, and send it * when we see "compress." (the last word before ppp starts), reply with nothing. The script ends and we start ppp. Note: * Make sure there is some whitespace between the filename and the \. IV Usage To use this install the modified chat and chatchat programs, and modify your chat script similar to the above. Before you dial in, start that chatchat program giving it the same pipe as in your config file. In the above case: chatchat /var/tmp/p Wait until you have one or two tick marks left on your card's current number, then start your dial up process that eventually calls chat. When chat goes to open and read the pipe, chatchat will prompt: type PIN into SecurID card and enter resulting passcode: At that point, type your PIN number into your Securid card, press the diamond, and type the resulting numbers in as your passcode. If you've left the -V -v options on your chat command you'll see everything so out, otherwise it works silently. If you type the number wrong or run out of time, the server will respond with an authentication failure. In that case you will have to hang up and start again. I don't know how to build a conditional script that says either expect "compress" next, but if you see "name:" again, do this instead. V Additional Information You can obtain additional information about chat and ppp from the man pages for chat and pppd, as well as the PPP-HOWTO. ppp-2.4.5/scripts/chatchat/chatchat.c000066400000000000000000000205741130035057700175230ustar00rootroot00000000000000/* ************************************************************************* * NAME: chatchat.c * * DESCRIPTION: * * This program creates a pipe for the chat process to read. The user * can supply information (like a password) that will be picked up * by chat and sent just like the regular contents of a chat script. * * Usage is: * * chatchat * * where matches the option given in the chat script. * * for instance the chat script fragment: * * ... * name: \\dmyname \ * word: @/var/tmp/p \ * ... * ^ * (note: leave some whitespace after the filename) * * expect "name:", reply with a delay followed by "myname" * expect "word:", reply with the data read from the pipe /var/tmp/p * * the matching usage of chatchat would be: * * chatchat /var/tmp/p * * eg: * * $chatchat /var/tmp/p * ... * some other process eventually starts: * chat ... * chat parses the "@/var/tmp/p" option and opens * /var/tmp/p * (chatchat prompts:) * * type PIN into SecurID card * enter resulting passcode: [user inputs something] * * chat reads /var/tmp/p & gets what the * user typed at chatchat's "enter string" prompt * chat removes the pipe file * chat sends the user's input as a response in * place of "@/var/tmp/p" * * PROCESS: * * gcc -g -o chatchat chatchat.c * * * GLOBALS: none * * REFERENCES: * * see the man pages and documentation that come with the 'chat' program * (part of the ppp package). you will need to use the modified chat * program that accepts the '@' operator. * * LIMITATIONS: * * REVISION HISTORY: * * STR Description Author * * 23-Mar-99 initial coding gpk * 12-May-99 unlink the pipe after closing paulus * * TARGET: ANSI C * This program is in the public domain. * * * ************************************************************************* */ #include #include #include #include #include #include #include /* MAXINPUT - the data typed into chatchat must be fewer */ /* characters than this. */ #define MAXINPUT 80 /* ************************************************************************* NAME: main USAGE: int argc; char * argv[]; main(argc, argv[]); returns: int DESCRIPTION: if the pipe file name is given on the command line, create the pipe, prompt the user and put whatever is typed into the pipe. returns -1 on error else # characters entered REFERENCES: LIMITATIONS: GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 25-Mar-99 initial coding gpk ************************************************************************* */ int main(int argc, char * argv[]) { int retval; int create_and_write_pipe(char * pipename); if (argc != 2) { fprintf(stderr, "usage: %s pipename\n", argv[0]); retval = -1; } else { retval = create_and_write_pipe(argv[1]); } return (retval); } /* ************************************************************************* NAME: create_and_write_pipe USAGE: int some_int; char * pipename; some_int = create_and_write_pipe(pipename); returns: int DESCRIPTION: given the pipename, create the pipe, open it, prompt the user for a string to put into the pipe, write the string, and close the pipe on error, print out an error message and return -1 returns -1 on error else #bytes written into the pipe REFERENCES: LIMITATIONS: GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 25-Mar-99 initial coding gpk 12-May-99 remove pipe after closing paulus ************************************************************************* */ int create_and_write_pipe(char * pipename) { int retval, created, pipefd, nread, nwritten; char input[MAXINPUT]; char errstring[180]; int create_pipe(char * pipename); int write_to_pipe(int pipefd, char * input, int nchar); created = create_pipe(pipename); if (-1 == created) { sprintf(errstring, "unable to create pipe '%s'", pipename); perror(errstring); retval = -1; } else { /* note: this open won't succeed until chat has the pipe */ /* open and ready to read. this makes for nice timing. */ pipefd = open(pipename, O_WRONLY); if (-1 == pipefd) { sprintf(errstring, "unable to open pipe '%s'", pipename); perror(errstring); retval = -1; } else { fprintf(stderr, "%s \n %s", "type PIN into SecurID card and", "enter resulting passcode:"); nread = read(STDIN_FILENO, (void *)input, MAXINPUT); if (0 >= nread) { perror("unable to read from stdin"); retval = -1; } else { /* munch off the newline character, chat supplies */ /* a return when it sends the string out. */ input[nread -1] = 0; nread--; nwritten = write_to_pipe(pipefd, input, nread); /* printf("wrote [%d]: '%s'\n", nwritten, input); */ retval = nwritten; } close(pipefd); /* Now make the pipe go away. It won't actually go away completely until chat closes it. */ if (unlink(pipename) < 0) perror("Warning: couldn't remove pipe"); } } return(retval); } /* ************************************************************************* NAME: create_pipe USAGE: int some_int; char * pipename; some_int = create_pipe(pipename); returns: int DESCRIPTION: create a pipe of the given name if there is an error (like the pipe already exists) print an error message and return return -1 on failure else success REFERENCES: LIMITATIONS: GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 25-Mar-99 initial coding gpk ************************************************************************* */ int create_pipe(char * pipename) { mode_t old_umask; int created; /* hijack the umask temporarily to get the mode I want */ /* on the pipe. */ old_umask = umask(000); created = mknod(pipename, S_IFIFO | S_IRWXU | S_IWGRP | S_IWOTH, (dev_t)NULL); /* now restore umask. */ (void)umask(old_umask); if (-1 == created) { perror("unable to create pipe"); } return(created); } /* ************************************************************************* NAME: write_to_pipe USAGE: int some_int; int pipefd; char * input; int nchar; some_int = write_to_pipe(pipefd, input, nchar); returns: int DESCRIPTION: write nchars of data from input to pipefd on error print a message to stderr return -1 on error, else # bytes written REFERENCES: LIMITATIONS: GLOBAL VARIABLES: accessed: none modified: none FUNCTIONS CALLED: REVISION HISTORY: STR Description of Revision Author 25-Mar-99 initial coding gpk 12-May-99 don't write count word first paulus ************************************************************************* */ int write_to_pipe(int pipefd, char * input, int nchar) { int nwritten; /* nwritten = write(pipefd, (void *)&nchar, sizeof(nchar)); */ nwritten = write(pipefd, (void *)input, nchar); if (-1 == nwritten) { perror("unable to write to pipe"); } return(nwritten); } ppp-2.4.5/scripts/ip-down.local.add000066400000000000000000000010441130035057700171300ustar00rootroot00000000000000 # # This sample code shows you one way to modify your setup to allow automatic # configuration of your resolv.conf for peer supplied DNS addresses when using # the `usepeerdns' option. # # In my case I just added this to my /etc/ppp/ip-down.local script. You may need to # create an executable script if one does not exist. # # Nick Walker (nickwalker@email.com) # if [ -n "$USEPEERDNS" -a -f /etc/ppp/resolv.conf ]; then if [ -f /etc/ppp/resolv.prev ]; then cp -f /etc/ppp/resolv.prev /etc/resolv.conf else rm -f /etc/resolv.conf fi fi ppp-2.4.5/scripts/ip-up.local.add000066400000000000000000000013311130035057700166040ustar00rootroot00000000000000 # # This sample code shows you one way to modify your setup to allow automatic # configuration of your resolv.conf for peer supplied DNS addresses when using # the `usepeerdns' option. # # In my case I just added this to my /etc/ppp/ip-up.local script. You may need to # create an executable script if one does not exist. # # Nick Walker (nickwalker@email.com) # if [ -n "$USEPEERDNS" -a -f /etc/ppp/resolv.conf ]; then rm -f /etc/ppp/resolv.prev if [ -f /etc/resolv.conf ]; then cp /etc/resolv.conf /etc/ppp/resolv.prev grep domain /etc/ppp/resolv.prev > /etc/resolv.conf grep search /etc/ppp/resolv.prev >> /etc/resolv.conf cat /etc/ppp/resolv.conf >> /etc/resolv.conf else cp /etc/ppp/resolv.conf /etc fi fi ppp-2.4.5/scripts/ipv6-down.sample000066400000000000000000000011411130035057700170420ustar00rootroot00000000000000#!/bin/sh # # This script is called with the following parameters: # interface tty speed local-address remote-address ipparam # # Kill the router advertisement daemon on this interface. # The killing procedure is copied from RedHat 6.0 initscripts. DEVICE="$1" PIDFILE="/var/run/radvd-$DEVICE.pid" [ -f "$PIDFILE" ] || exit 0 PID="$(cat "$PIDFILE")" if [ "$PID" != "" ]; then if ps h "$PID" >/dev/null 2>&1; then kill -TERM "$PID" usleep 10000 if ps h "$PID" >/dev/null 2>&1; then sleep 1 if ps h "$PID" >/dev/null 2>&1; then kill -KILL "$PID" fi fi fi fi rm -f "$PIDFILE" ppp-2.4.5/scripts/ipv6-up.sample000066400000000000000000000014571130035057700165310ustar00rootroot00000000000000#!/bin/sh # # This script is called with the following parameters: # interface tty speed local-address remote-address ipparam # # Start router advertisements on this link. # Based on radvd 0.5.0 behaviour DEVICE="$1" CFGFILE="/etc/radvd.conf-$DEVICE" PIDFILE="/var/run/radvd-$DEVICE.pid" EXEFILE="/usr/sbin/radvd" if [ -x "$EXEFILE" -a -f "$CFGFILE" ]; then touch "$PIDFILE" if [ ! -f "$PIDFILE" ]; then echo "error: $PIDFILE is not a regular file. Aborting" exit 0 fi PID="$(cat "$PIDFILE")" if [ -n "$PID" ]; then ps h "$PID" >/dev/null 2>&1 && exit 0 fi # radvd 0.5.0 doesn't write a pid-file so we do it here # enabling debugging keeps radvd in foreground, putting it # on background gives us the PID. "$EXEFILE" -d 1 -C "$CFGFILE" & echo $! >"$PIDFILE" fi ppp-2.4.5/scripts/options-rsh-loc000066400000000000000000000001261130035057700167730ustar00rootroot00000000000000debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1460 ppp-2.4.5/scripts/options-rsh-rem000066400000000000000000000001341130035057700170000ustar00rootroot00000000000000notty debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1460 ppp-2.4.5/scripts/options-ssh-loc000066400000000000000000000001261130035057700167740ustar00rootroot00000000000000debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1400 ppp-2.4.5/scripts/options-ssh-rem000066400000000000000000000001341130035057700170010ustar00rootroot00000000000000notty debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1400 ppp-2.4.5/scripts/plog000066400000000000000000000002221130035057700146710ustar00rootroot00000000000000#!/bin/sh if [ -s /var/log/ppp.log ]; then exec tail "$@" /var/log/ppp.log else exec tail "$@" /var/log/syslog | grep ' \(pppd\|chat\)\[' fi ppp-2.4.5/scripts/poff000066400000000000000000000053241130035057700146720ustar00rootroot00000000000000#!/bin/sh # $Id: poff,v 1.1 2002/11/24 23:30:44 etbe Exp $ # Written by John Hasler and based on work # by Phil Hands . Distributed under the GNU GPL if [ -x /usr/bin/kill ]; then KILL="/usr/bin/kill" else KILL="/bin/kill" fi SIG=TERM DONE="stopped" MODE="" usage () { cat < pon.txt .\" .TH PON 1 "July 2000" "Debian Project" "Debian PPPD" .SH NAME pon, poff, plog \- starts up, shuts down or lists the log of PPP connections .SH SYNOPSIS .B pon [ isp\-name [ options ] ] .br .B poff [ \-r ] [ \-d ] [ \-c ] [ \-a ] [ \-h ] [ isp\-name ] .br .B plog [ arguments ] .SH DESCRIPTION This manual page describes the \fBpon\fP, \fBplog\fP and \fBpoff\fP scripts, which allow users to control PPP connections. .. .SS pon \fBpon\fP, invoked without arguments, runs the \fI/etc/ppp/ppp_on_boot\fP file, if it exists and is executable. Otherwise, a PPP connection will be started using configuration from \fI/etc/ppp/peers/provider\fP. This is the default behaviour unless an \fBisp\-name\fP argument is given. .PP For instance, to use ISP configuration "myisp" run: .IP pon myisp .PP \fBpon\fP will then use the options file \fI/etc/ppp/peers/myisp\fP. You can pass additional \fBoptions\fP after the ISP name, too. \fBpon\fP can be used to run multiple, simultaneous PPP connections. .. .SS poff \fBpoff\fP closes a PPP connection. If more than one PPP connection exists, the one named in the argument to \fBpoff\fP will be killed, e.g. .IP poff myprovider2 .PP will terminate the connection to myprovider2, and leave the PPP connections to e.g. "myprovider1" or "myprovider3" up and running. .PP \fBpoff\fP takes the following command line options: .RS .TP .B "\-r" causes the connection to be redialed after it is dropped. .TP .B "\-d" toggles the state of pppd's debug option. .TP .B "\-c" causes .BR pppd (8) to renegotiate compression. .TP .B "\-a" stops all running ppp connections. If the argument \fBisp\-name\fP is given it will be ignored. .TP .B "\-h" displays help information. .TP .B "\-v" prints the version and exits. .PP If no argument is given, \fBpoff\fP will stop or signal pppd if and only if there is exactly one running. If more than one connection is active, it will exit with an error code of 1. .. .SS plog \fBplog\fP shows you the last few lines of \fI/var/log/ppp.log\fP. If that file doesn't exist, it shows you the last few lines of your \fI/var/log/syslog\fP file, but excluding the lines not generated by pppd. This script makes use of the .BR tail (1) command, so arguments that can be passed to .BR tail (1) can also be passed to \fBplog\fP. .PP Note: the \fBplog\fP script can only be used by root or another system administrator in group "adm", due to security reasons. Also, to have all pppd-generated information in one logfile, that plog can show, you need the following line in your \fI/etc/syslog.conf\fP file: .PP local2.* \-/var/log/ppp.log .RE .SH FILES .TP .I /etc/ppp/options PPPd system options file. .TP .I /etc/ppp/pap\-secrets System PAP passwords file. .TP .I /etc/ppp/chap\-secrets System CHAP passwords file. .TP .I /etc/ppp/peers/ Directory holding the peer options files. The default file is called \fIprovider\fP. .TP .I /etc/chatscripts/provider The chat script invoked from the default \fI/etc/ppp/peers/provider\fP. .TP .I /var/log/ppp.log The default PPP log file. .SH AUTHORS The p-commands were written by Christoph Lameter . Updated and revised by Philip Hands . .br This manual was written by Othmar Pasteka . Modified by Rob Levin , with some extensions taken from the old p-commands manual written by John Hasler . .SH "SEE ALSO" .BR pppd (8), .BR chat (8), .BR tail (1). ppp-2.4.5/scripts/ppp-off000077500000000000000000000017071130035057700153130ustar00rootroot00000000000000#!/bin/sh ###################################################################### # # Determine the device to be terminated. # if [ "$1" = "" ]; then DEVICE=ppp0 else DEVICE=$1 fi ###################################################################### # # If the ppp0 pid file is present then the program is running. Stop it. if [ -r /var/run/$DEVICE.pid ]; then kill -INT `cat /var/run/$DEVICE.pid` # # If the kill did not work then there is no process running for this # pid. It may also mean that the lock file will be left. You may wish # to delete the lock file at the same time. if [ ! "$?" = "0" ]; then rm -f /var/run/$DEVICE.pid echo "ERROR: Removed stale pid file" exit 1 fi # # Success. Let pppd clean up its own junk. echo "PPP link to $DEVICE terminated." exit 0 fi # # The ppp process is not running for ppp0 echo "ERROR: PPP link is not active on $DEVICE" exit 1 ppp-2.4.5/scripts/ppp-on000077500000000000000000000031511130035057700151500ustar00rootroot00000000000000#!/bin/sh # # Script to initiate a ppp connection. This is the first part of the # pair of scripts. This is not a secure pair of scripts as the codes # are visible with the 'ps' command. However, it is simple. # # These are the parameters. Change as needed. TELEPHONE=555-1212 # The telephone number for the connection ACCOUNT=george # The account name for logon (as in 'George Burns') PASSWORD=gracie # The password for this account (and 'Gracie Allen') LOCAL_IP=0.0.0.0 # Local IP address if known. Dynamic = 0.0.0.0 REMOTE_IP=0.0.0.0 # Remote IP address if desired. Normally 0.0.0.0 NETMASK=255.255.255.0 # The proper netmask if needed # # Export them so that they will be available at 'ppp-on-dialer' time. export TELEPHONE ACCOUNT PASSWORD # # This is the location of the script which dials the phone and logs # in. Please use the absolute file name as the $PATH variable is not # used on the connect option. (To do so on a 'root' account would be # a security hole so don't ask.) # DIALER_SCRIPT=/etc/ppp/ppp-on-dialer # # Initiate the connection # # I put most of the common options on this command. Please, don't # forget the 'lock' option or some programs such as mgetty will not # work. The asyncmap and escape will permit the PPP link to work with # a telnet or rlogin connection. You are welcome to make any changes # as desired. Don't use the 'defaultroute' option if you currently # have a default route to an ethernet gateway. # exec /usr/sbin/pppd debug lock modem crtscts /dev/ttyS0 38400 \ asyncmap 20A0000 escape FF kdebug 0 $LOCAL_IP:$REMOTE_IP \ noipdefault netmask $NETMASK defaultroute connect $DIALER_SCRIPT ppp-2.4.5/scripts/ppp-on-dialer000077500000000000000000000006151130035057700164100ustar00rootroot00000000000000#!/bin/sh # # This is part 2 of the ppp-on script. It will perform the connection # protocol for the desired connection. # exec chat -v \ TIMEOUT 3 \ ABORT '\nBUSY\r' \ ABORT '\nNO ANSWER\r' \ ABORT '\nRINGING\r\n\r\nRINGING\r' \ '' \rAT \ 'OK-+++\c-OK' ATH0 \ TIMEOUT 30 \ OK ATDT$TELEPHONE \ CONNECT '' \ ogin:--ogin: $ACCOUNT \ assword: $PASSWORD ppp-2.4.5/scripts/ppp-on-rsh000077500000000000000000000037471130035057700157550ustar00rootroot00000000000000#!/bin/sh # # A sample script to establish PPP session(s) via rsh # # Adi Masputra # Jan 24, 2000 # # # You'd definitely want to change the following addresses to suit # your network configuration # LOC_IP=10.0.0.1 REM_IP=10.0.0.2 NETMASK=255.255.0.0 export LOC_IP REM_IP # # This is the remote peer where in.rshd is running, either # its hostname or IP address # PPPD_RHOST=myremotehost # # For this example, we assume that pppd on both local and remote # machines reside in the same place, /usr/local/bin/pppd # PPPD_LOC=/usr/local/bin/pppd # # The location of local options file (where rsh client is running). # Note that the sample options file included in the distribution # may need further customizations, depending on your needs. The 'noauth' # option specified in the file is there to simplify the example. In # reality, you'd probably want to remove such option. # PPPD_LOC_OPT=/etc/ppp/options-rsh-loc # # The location of remote options file (where in.rshd daemon is running). # Note that the sample options file included in the distribution # may need further customizations, depending on your needs. The 'noauth' # option specified in the file is there to simplify the example. In # reality, you'd probably want to remove such option. Also note that # the remote options file need to include the 'notty' option for this # to work # PPPD_REM_OPT=/etc/ppp/options-rsh-rem # # The location of rsh client on the local machine # RSH_LOC=/bin/rsh export PPPD_LOC PPPD_LOC_OPT PPPD_REM_OPT PPPD_RHOST RSH_LOC # # Uncomment the following to enable IPv6, note that the IPv6 support # needs to be enabled during compilation # # PPPD_IPV6='+ipv6 ipv6cp-use-ipaddr' export PPPD_IPV6 # # And execute pppd with the pty option, specifying rsh client as the # slave side of the pseduo-tty master/slave pair. # exec $PPPD_LOC \ pty '$RSH_LOC $PPPD_RHOST $PPPD_LOC $REM_IP:$LOC_IP $PPPD_IPV6 file $PPPD_REM_OPT' \ $LOC_IP:$REM_IP netmask $NETMASK $PPPD_IPV6 file $PPPD_LOC_OPT ppp-2.4.5/scripts/ppp-on-ssh000077500000000000000000000044161130035057700157500ustar00rootroot00000000000000#!/bin/sh # # A sample script to establish PPP session(s) via SSH 1.x # # Adi Masputra # Jan 24, 2000 # # # You'd definitely want to change the following addresses to suit # your network configuration # LOC_IP=10.0.0.1 REM_IP=10.0.0.2 NETMASK=255.255.0.0 export LOC_IP REM_IP # # This is the remote peer where sshd is running, either # its hostname or IP address # PPPD_RHOST=myremotehost # # For this example, we assume that pppd on both local and remote # machines reside in the same place, /usr/local/bin/pppd # PPPD_LOC=/usr/local/bin/pppd # # The location of local options file (where ssh client is running). # Note that the sample options file included in the distribution # may need further customizations, depending on your needs. The 'noauth' # option specified in the file is there to simplify the example, although # some may choose to have it there and rely on ssh authentication # instead. # PPPD_LOC_OPT=/etc/ppp/options-ssh-loc # # The location of remote options file (where sshd daemon is running) # Note that the sample options file included in the distribution # may need further customizations, depending on your needs. The 'noauth' # option specified in the file is there to simplify the example, although # some may choose to have it there and rely on ssh authentication # instead. Also note that the remote options file need to include the 'notty' # options for this to work. # PPPD_REM_OPT=/etc/ppp/options-ssh-rem # # The location of ssh client on the local machine # SSH_LOC=/usr/local/bin/ssh export PPPD_LOC PPPD_LOC_OPT PPPD_REM_OPT PPPD_RHOST SSH_LOC # # Uncomment the following to enable IPv6, note that the IPv6 support # needs to be enabled during compilation # # PPPD_IPV6='+ipv6 ipv6cp-use-ipaddr' export PPPD_IPV6 # # And execute pppd with the pty option, specifying ssh client as the # slave side of the pseudo-tty master/slave pair. Note that on this example, # ssh has been compiled to allow NULL encryption (thus the '-c none' option), # but in reality, you'd probably want to specify the encryption algorithm. # See the man page of ssh(1) for details. # exec $PPPD_LOC \ pty '$SSH_LOC -c none $PPPD_RHOST $PPPD_LOC $REM_IP:$LOC_IP $PPPD_IPV6 file $PPPD_REM_OPT' \ $LOC_IP:$REM_IP netmask $NETMASK $PPPD_IPV6 file $PPPD_LOC_OPT ppp-2.4.5/scripts/redialer000077500000000000000000000042751130035057700155360ustar00rootroot00000000000000#!/bin/sh ################################################################### # # These parameters control the attack dialing sequence. # # Maximum number of attempts to reach the telephone number(s) MAX_ATTEMPTS=10 # Delay between each of the attempts. This is a parameter to sleep # so use "15s" for 15 seconds, "1m" for 1 minute, etc. SLEEP_DELAY=15s ################################################################### # # This is a list of telephone numbers. Add new numbers if you wish # and see the function 'callall' below for the dial process. PHONE1=555-1212 PHONE2=411 ################################################################### # # If you use the ppp-on script, then these are passed to this routine # automatically. There is no need to define them here. If not, then # you will need to set the values. # ACCOUNT=my_account_name PASSWORD=my_password ################################################################### # # Function to initialize the modem and ensure that it is in command # state. This may not be needed, but it doesn't hurt. # function initialize { chat -v TIMEOUT 3 '' AT 'OK-+++\c-OK' return } ################################################################### # # Script to dial a telephone # function callnumber { chat -v \ ABORT '\nBUSY\r' \ ABORT '\nNO ANSWER\r' \ ABORT '\nRINGING\r\n\r\nRINGING\r' \ '' ATDT$1 \ CONNECT '' \ ogin:--ogin: $ACCOUNT \ assword: $PASSWORD # # If the connection was successful then end the whole script with a # success. # if [ "$?" = "0" ]; then exit 0 fi return } ################################################################### # # Script to dial any telephone number # function callall { # echo "dialing attempt number: $1" >/dev/console callnumber $PHONE1 # callnumber $PHONE2 } ################################################################### # # Initialize the modem to ensure that it is in the command state # initialize if [ ! "$?" = "0" ]; then exit 1 fi # # Dial telephone numbers until one answers # attempt=0 while : ; do attempt=`expr $attempt + 1` callall $attempt if [ "$attempt" = "$MAX_ATTEMPTS" ]; then exit 1 fi sleep "$SLEEP_DELAY" done ppp-2.4.5/scripts/secure-card000077500000000000000000000045141130035057700161400ustar00rootroot00000000000000#!/usr/local/bin/expect -f # # This script was written by Jim Isaacson . It is # designed to work as a script to use the SecureCARD(tm) device. This # little device is mated with a central controller. The number displayed # on this card changes every so often and you need to enter the number # along with your user account name in order to gain access. Since chat # is based upon fixed strings this procedure will not work with chat. # # It is included by permission. An excellent reference for the expect # program used by this script is in the book: # # "Exploring Expect" # by Don Libes # Published by O'Rielly and Associates # send_user "hello, starting ppp\n" system "stty 19200 -echoe -echo raw < /dev/ttyS3 > /dev/ttyS3" # # These are the parameters for the program. # set user Pxxxxxx set password xxxxxxx set modem /dev/ttyS3 set dialup set timeout 60 spawn -noecho -open [open $modem "r+"] send "AT&F\r" expect "OK" send "ATe0v1x4&c1q0&d2&c1s2=128s0=0DT $dialup\r" set timeout 15 set counter 0 set still_connecting 1 expect { -re ".*CONNECT.*\n" { set timeout 5 set still_connecting 0 continue -expect } -re ".*CONNECT.*\r" { set timeout 5 set still_connecting 0 continue -expect } -re ".*NO.*CARRIER" { send_user "Failed to Connect, exiting...\n" exit } -re ".*NO.*DIAL.*TONE" { send_user "Failed to Connect, exiting...\n" exit } -re ".*VOICE" { send_user "Failed to Connect, exiting...\n" exit } -re ".*sscode:.*\n" { continue -expect } -re ".*sscode:" { set timeout -1 expect_user -re "(.*)\n" send "$expect_out(1,string)\r" set timeout 30 continue -expect } -re ".*Next.*:" { set timeout -1 expect_user -re "(.*)\n" send "$expect_out(1,string)\r" set timeout 30 continue -expect } -re "Your.*" { send "\r" continue -expect } -re ".*in:" { send "$user\r" continue -expect } -re ".*word:" { send "$password\r" } timeout { if { $still_connecting > 0 } { continue -expect } set timeout 15 send "\r" incr counter if { $counter > 8 } { send_user "Cannot Connect\n" exit } else { continue -expect } } } overlay -0 $spawn_id -1 $spawn_id pppd /dev/ttyS3 19200 192.111.187.215: \ crtscts modem defaultroute debug ppp-2.4.5/solaris/000077500000000000000000000000001130035057700137765ustar00rootroot00000000000000ppp-2.4.5/solaris/Makedefs000066400000000000000000000003141130035057700154360ustar00rootroot00000000000000# # defines common to several Makefiles # INSTALL= /usr/sbin/install BINDIR = @DESTDIR@/bin MANDIR = @DESTDIR@/man ETCDIR = @SYSCONF@/ppp CC = /opt/SUNWspro/bin/cc COPTS = -O -Xa LD = /usr/ccs/bin/ld ppp-2.4.5/solaris/Makedefs.gcc000066400000000000000000000002701130035057700161720ustar00rootroot00000000000000# # defines common to several Makefiles # INSTALL= /usr/sbin/install BINDIR = @DESTDIR@/bin MANDIR = @DESTDIR@/man ETCDIR = @SYSCONF@/ppp CC = gcc COPTS = -O2 LD = /usr/ccs/bin/ld ppp-2.4.5/solaris/Makedefs.sol2000066400000000000000000000040231130035057700163150ustar00rootroot00000000000000# # Generic make definitions for Solaris 2 # # $Id: Makedefs.sol2,v 1.2 2002/09/07 05:15:25 carlsonj Exp $ # include ../Makedefs.com CPPFLAGS = -D_KERNEL -DSVR4 -DSOL2 -DPRIOQ -DDEBUG -I../include CFLAGS = $(CPPFLAGS) $(COPTS) # lint-specific variables LINT = lint LINT_OPT_32 = LINT_OPT_64 = -Xarch=v9 -errchk=longptr64 LINT_32 = LINT_32 += -erroff=E_BAD_PTR_CAST_ALIGN LINT_32 += -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED LINT_32 += -erroff=E_SUSPICIOUS_COMPARISON LINT_32 += -erroff=E_CAST_UINT_TO_SIGNED_INT LINT_32 += -erroff=E_PASS_UINT_TO_SIGNED_INT LINT_32 += -erroff=E_INVALID_ANNOTATION_NAME LINT_32 += -erroff=E_FUNC_ARG_UNUSED # This might be needed, but zlib.c and vjcompress.c will squawk # when not ignored LINT_32 += -erroff=E_CASE_FALLTHRU LINT_32 += -erroff=E_RET_INT_IMPLICITLY LINT_32 += -erroff=E_FUNC_NO_RET_VAL # Some STREAMS macros will be noisy too when this isn't ignored LINT_32 += -erroff=E_CONSTANT_CONDITION LINT_32 += -erroff=E_CONST_EXPR # Extra noise suppressant for 64-bit EXTRA_OFF = EXTRA_OFF += -erroff=E_CAST_INT_TO_SMALL_INT EXTRA_OFF += -erroff=E_CAST_INT_CONST_TO_SMALL_INT EXTRA_OFF += -erroff=E_CAST_TO_PTR_FROM_INT EXTRA_OFF += -erroff=E_ASSIGN_INT_TO_SMALL_INT EXTRA_OFF += -erroff=E_ASSIGN_INT_FROM_BIG_CONST EXTRA_OFF += -erroff=E_CONST_PROMOTED_UNSIGNED_LL EXTRA_OFF += -erroff=E_CONST_PROMOTED_LONG_LONG EXTRA_OFF += -erroff=E_CONST_TRUNCATED_BY_ASSIGN EXTRA_OFF += -erroff=E_PASS_INT_FROM_BIG_CONST EXTRA_OFF += -erroff=E_COMP_INT_WITH_LARGE_INT EXTRA_OFF += -erroff=E_ASSIGN_UINT_TO_SIGNED_INT EXTRA_OFF += -erroff=E_ASSIGN_NARROW_CONV EXTRA_OFF += -erroff=E_PASS_INT_TO_SMALL_INT EXTRA_OFF += -erroff=E_PTR_CONV_LOSES_BITS LINT_64 = $(LINT_32) LINT_64 += $(EXTRA_OFF) LINTFLAGS64 = -Xa -nsxmuF -errtags=yes $(LINT_OPT_64) $(LINT_64) LINT64 = $(LINT) -c $(LINTFLAGS64) $(CPPFLAGS) LINTFLAGS32 = -Xa -nsxmuF -errtags=yes $(LINT_OPT_32) $(LINT_32) LINT32 = $(LINT) -c $(LINTFLAGS32) $(CPPFLAGS) ppp-2.4.5/solaris/Makefile.sol2000066400000000000000000000030401130035057700163110ustar00rootroot00000000000000# # Makefile for STREAMS modules for Solaris 2. # # $Id: Makefile.sol2,v 1.3 2004/11/15 00:57:54 carlsonj Exp $ # include Makedefs.sol2 COPTS += -xO2 -xspace -W0,-Lt COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \ ppp_comp_mod.o all: ppp ppp_ahdl ppp_comp ppp: ppp.o ppp_mod.o $(LD) -r -o $@ ppp.o ppp_mod.o chmod +x $@ ppp_ahdl: ppp_ahdlc.o ppp_ahdlc_mod.o $(LD) -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o chmod +x $@ ppp_comp: $(COMP_OBJS) $(LD) -r -o $@ $(COMP_OBJS) chmod +x $@ bsd-comp.o: ../modules/bsd-comp.c $(CC) $(CFLAGS) -c $? deflate.o: ../modules/deflate.c $(CC) $(CFLAGS) -c $? ppp.o: ppp.c $(CC) $(CFLAGS) -c $? ppp_mod.o: ppp_mod.c $(CC) $(CFLAGS) -c $? ppp_ahdlc_mod.o: ppp_ahdlc_mod.c $(CC) $(CFLAGS) -c $? ppp_ahdlc.o: ppp_ahdlc.c $(CC) $(CFLAGS) -c $? ppp_comp.o: ppp_comp.c $(CC) $(CFLAGS) -c $? ppp_comp_mod.o: ppp_comp_mod.c $(CC) $(CFLAGS) -c $? vjcompress.o: ../modules/vjcompress.c $(CC) $(CFLAGS) -c $? zlib.o: ../common/zlib.c $(CC) $(CFLAGS) -c $? install: /usr/sbin/modunload -i 0 cp ppp ppp.conf /kernel/drv cp ppp_comp ppp_ahdl /kernel/strmod if grep clone:ppp /etc/minor_perm; then :; else \ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi /usr/sbin/rem_drv ppp 2>/dev/null || true /usr/sbin/modunload -i 0 /usr/sbin/add_drv ppp SRCS = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \ ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c lint: $(LINT32) $(SRCS) clean: rm -f ppp ppp_comp ppp_ahdl *.o *~ core rm -f *.ln ppp-2.4.5/solaris/Makefile.sol2-64000066400000000000000000000050021130035057700165400ustar00rootroot00000000000000# # Makefile for 64-bit STREAMS modules for Solaris 2. # # $Id: Makefile.sol2-64,v 1.3 2004/11/15 00:57:54 carlsonj Exp $ # include Makedefs.sol2 # Sun's cc flag for LP64 compilation / linkage COPTS += -xchip=ultra -xarch=v9 -Wc,-xcode=abs32 -Wc,-Qiselect-regsym=0 -xO3 -xspace -W0,-Lt # subdirectory where 64-bit objects / binaries will be placed LP64DIR = sparcv9 # Name of legacy Makefile (for 32-bit binaries) STD_MAKE = Makefile.sol2 COMP_OBJS = $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \ $(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \ $(LP64DIR)/ppp_comp_mod.o all: std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp std_objs: $(MAKE) -f $(STD_MAKE) all ppp: $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o $(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o chmod +x $(LP64DIR)/$@ ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o $(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \ $(LP64DIR)/ppp_ahdlc_mod.o chmod +x $(LP64DIR)/$@ ppp_comp: $(COMP_OBJS) $(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS) chmod +x $(LP64DIR)/$@ $(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/deflate.o: ../modules/deflate.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp.o: ppp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_mod.o: ppp_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_comp.o: ppp_comp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/vjcompress.o: ../modules/vjcompress.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/zlib.o: ../common/zlib.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR): mkdir -m 755 -p $@ install: /usr/sbin/modunload -i 0 cp ppp ppp.conf /kernel/drv cp ppp_comp ppp_ahdl /kernel/strmod cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR) cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR) if grep clone:ppp /etc/minor_perm; then :; else \ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi /usr/sbin/rem_drv ppp 2>/dev/null || true /usr/sbin/modunload -i 0 /usr/sbin/add_drv ppp SRCS = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \ ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c lint: $(LINT64) $(SRCS) lint-32: $(LINT32) $(SRCS) clean: $(MAKE) -f $(STD_MAKE) clean rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core ppp-2.4.5/solaris/Makefile.sol2-64x000066400000000000000000000050171130035057700167360ustar00rootroot00000000000000# # Makefile for 64-bit STREAMS modules for Solaris 2 on x64 # # $Id: Makefile.sol2-64x,v 1.1 2005/06/26 23:53:17 carlsonj Exp $ # include Makedefs.sol2 # Sun's cc flag for LP64 compilation / linkage COPTS += -errwarn -xtarget=opteron -xarch=amd64 -xmodel=kernel \ -Ui386 -U__i386 -D__amd64 -xO2 # subdirectory where 64-bit objects / binaries will be placed LP64DIR = amd64 # Name of legacy Makefile (for 32-bit binaries) STD_MAKE = Makefile.sol2 COMP_OBJS = $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \ $(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \ $(LP64DIR)/ppp_comp_mod.o all: std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp std_objs: $(MAKE) -f $(STD_MAKE) all ppp: $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o $(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o chmod +x $(LP64DIR)/$@ ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o $(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \ $(LP64DIR)/ppp_ahdlc_mod.o chmod +x $(LP64DIR)/$@ ppp_comp: $(COMP_OBJS) $(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS) chmod +x $(LP64DIR)/$@ $(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/deflate.o: ../modules/deflate.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp.o: ppp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_mod.o: ppp_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_comp.o: ppp_comp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/vjcompress.o: ../modules/vjcompress.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/zlib.o: ../common/zlib.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR): mkdir -m 755 -p $@ install: /usr/sbin/modunload -i 0 cp ppp ppp.conf /kernel/drv cp ppp_comp ppp_ahdl /kernel/strmod cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR) cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR) if grep clone:ppp /etc/minor_perm; then :; else \ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi /usr/sbin/rem_drv ppp 2>/dev/null || true /usr/sbin/modunload -i 0 /usr/sbin/add_drv ppp SRCS = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \ ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c lint: $(LINT64) $(SRCS) lint-32: $(LINT32) $(SRCS) clean: $(MAKE) -f $(STD_MAKE) clean rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core ppp-2.4.5/solaris/Makefile.sol2gcc000066400000000000000000000030331130035057700167700ustar00rootroot00000000000000# # Makefile for STREAMS modules for Solaris 2. # # $Id: Makefile.sol2gcc,v 1.4 2004/11/15 00:57:54 carlsonj Exp $ # include Makedefs.sol2 COPTS += -fno-builtin COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \ ppp_comp_mod.o all: ppp ppp_ahdl ppp_comp ppp: ppp.o ppp_mod.o $(LD) -r -o $@ ppp.o ppp_mod.o chmod +x $@ ppp_ahdl: ppp_ahdlc.o ppp_ahdlc_mod.o $(LD) -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o chmod +x $@ ppp_comp: $(COMP_OBJS) $(LD) -r -o $@ $(COMP_OBJS) chmod +x $@ bsd-comp.o: ../modules/bsd-comp.c $(CC) $(CFLAGS) -c $? deflate.o: ../modules/deflate.c $(CC) $(CFLAGS) -c $? ppp.o: ppp.c $(CC) $(CFLAGS) -c $? ppp_mod.o: ppp_mod.c $(CC) $(CFLAGS) -c $? ppp_ahdlc_mod.o: ppp_ahdlc_mod.c $(CC) $(CFLAGS) -c $? ppp_ahdlc.o: ppp_ahdlc.c $(CC) $(CFLAGS) -c $? ppp_comp.o: ppp_comp.c $(CC) $(CFLAGS) -c $? ppp_comp_mod.o: ppp_comp_mod.c $(CC) $(CFLAGS) -c $? vjcompress.o: ../modules/vjcompress.c $(CC) $(CFLAGS) -c $? zlib.o: ../common/zlib.c $(CC) $(CFLAGS) -c $? install: /usr/sbin/modunload -i 0 cp ppp ppp.conf /kernel/drv cp ppp_comp ppp_ahdl /kernel/strmod if grep clone:ppp /etc/minor_perm; then :; else \ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi /usr/sbin/rem_drv ppp 2>/dev/null || true /usr/sbin/modunload -i 0 /usr/sbin/add_drv ppp SRCS = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \ ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c lint: $(LINT32) $(SRCS) clean: rm -f ppp ppp_comp ppp_ahdl *.o *~ core rm -f *.ln ppp-2.4.5/solaris/Makefile.sol2gcc-64000066400000000000000000000047601130035057700172270ustar00rootroot00000000000000# # Makefile for 64-bit STREAMS modules for Solaris 2. # # $Id: Makefile.sol2gcc-64,v 1.3 2004/11/15 00:57:54 carlsonj Exp $ # include Makedefs.sol2 # gcc flags for LP64 compilation / linkage COPTS += -mcpu=v9 -m64 -mcmodel=medlow -mstack-bias -fno-builtin # subdirectory where 64-bit objects / binaries will be placed LP64DIR = sparcv9 # Name of legacy Makefile (for 32-bit binaries) STD_MAKE = Makefile.sol2gcc COMP_OBJS = $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \ $(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \ $(LP64DIR)/ppp_comp_mod.o all: std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp std_objs: $(MAKE) -f $(STD_MAKE) all ppp: $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o $(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o chmod +x $(LP64DIR)/$@ ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o $(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \ $(LP64DIR)/ppp_ahdlc_mod.o chmod +x $(LP64DIR)/$@ ppp_comp: $(COMP_OBJS) $(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS) chmod +x $(LP64DIR)/$@ $(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/deflate.o: ../modules/deflate.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp.o: ppp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_mod.o: ppp_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_comp.o: ppp_comp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/vjcompress.o: ../modules/vjcompress.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/zlib.o: ../common/zlib.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR): mkdir -m 755 -p $@ install: /usr/sbin/modunload -i 0 cp ppp ppp.conf /kernel/drv cp ppp_comp ppp_ahdl /kernel/strmod cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR) cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR) if grep clone:ppp /etc/minor_perm; then :; else \ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi /usr/sbin/rem_drv ppp 2>/dev/null || true /usr/sbin/modunload -i 0 /usr/sbin/add_drv ppp SRCS = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \ ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c lint: $(LINT64) $(SRCS) lint-32: $(LINT32) $(SRCS) clean: $(MAKE) -f $(STD_MAKE) clean rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core ppp-2.4.5/solaris/Makefile.sol2gcc-64x000066400000000000000000000055041130035057700174140ustar00rootroot00000000000000# # Makefile for 64-bit STREAMS modules for Solaris 2 on x64 with gcc. # # $Id: Makefile.sol2gcc-64x,v 1.1 2005/06/26 23:53:17 carlsonj Exp $ # include Makedefs.sol2 # gcc flags for LP64 compilation / linkage COPTS += -finline -fno-inline-functions -fno-builtin -fno-asm \ -nodefaultlibs -D__sun -m64 -mtune=opteron -Ui386 \ -U__i386 -fno-strict-aliasing -fno-unit-at-a-time \ -fno-optimize-sibling-calls -O2 -D_ASM_INLINES \ -ffreestanding -mcmodel=kernel -mno-red-zone -gdwarf-2 \ -std=gnu89 -D_KERNEL -D_SYSCALL32 -D_SYSCALL32_IMPL \ -D_ELF64 -Dsun -D__sun -D__SVR4 # subdirectory where 64-bit objects / binaries will be placed LP64DIR = amd64 # Name of legacy Makefile (for 32-bit binaries) STD_MAKE = Makefile.sol2gcc COMP_OBJS = $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \ $(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \ $(LP64DIR)/ppp_comp_mod.o all: std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp std_objs: $(MAKE) -f $(STD_MAKE) all ppp: $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o $(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o chmod +x $(LP64DIR)/$@ ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o $(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \ $(LP64DIR)/ppp_ahdlc_mod.o chmod +x $(LP64DIR)/$@ ppp_comp: $(COMP_OBJS) $(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS) chmod +x $(LP64DIR)/$@ $(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/deflate.o: ../modules/deflate.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp.o: ppp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_mod.o: ppp_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_comp.o: ppp_comp.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/vjcompress.o: ../modules/vjcompress.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR)/zlib.o: ../common/zlib.c $(CC) $(CFLAGS) -c $? -o $@ $(LP64DIR): mkdir -m 755 -p $@ install: /usr/sbin/modunload -i 0 cp ppp ppp.conf /kernel/drv cp ppp_comp ppp_ahdl /kernel/strmod cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR) cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR) if grep clone:ppp /etc/minor_perm; then :; else \ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi /usr/sbin/rem_drv ppp 2>/dev/null || true /usr/sbin/modunload -i 0 /usr/sbin/add_drv ppp SRCS = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \ ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c lint: $(LINT64) $(SRCS) lint-32: $(LINT32) $(SRCS) clean: $(MAKE) -f $(STD_MAKE) clean rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core ppp-2.4.5/solaris/Makefile.top000066400000000000000000000020721130035057700162400ustar00rootroot00000000000000# # ppp top level makefile for SVR4 and Solaris 2 # # $Id: Makefile.top,v 1.3 2004/11/01 09:31:07 paulus Exp $ # include Makedefs.com all: cd chat; $(MAKE) all cd pppd; $(MAKE) all cd pppstats; $(MAKE) all cd pppdump; $(MAKE) all cd solaris; $(MAKE) all install: $(BINDIR) $(MANDIR)/man8 install-progs install-progs: cd chat; $(MAKE) install cd pppd; $(MAKE) install cd pppstats; $(MAKE) install cd pppdump; $(MAKE) install install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \ $(ETCDIR)/chap-secrets install-modules: cd solaris; $(MAKE) install $(ETCDIR)/options: cp etc.ppp/options $@ chmod go-w $@ $(ETCDIR)/pap-secrets: $(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/pap-secrets $(ETCDIR)/chap-secrets: $(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/chap-secrets $(BINDIR): mkdir -m 755 -p $@ $(MANDIR)/man8: mkdir -m 755 -p $@ $(ETCDIR): mkdir -m 755 -p $@ clean: rm -f *~ cd chat; $(MAKE) clean cd pppd; $(MAKE) clean cd pppstats; $(MAKE) clean cd pppdump; $(MAKE) clean cd solaris; $(MAKE) clean # no tests yet, one day... installcheck: true ppp-2.4.5/solaris/ppp.c000066400000000000000000001726041130035057700147530ustar00rootroot00000000000000/* * ppp.c - STREAMS multiplexing pseudo-device driver for PPP. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp.c,v 1.4 2005/06/27 00:59:57 carlsonj Exp $ */ /* * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX. */ #include #include #include #include #include #include #ifdef __osf__ #include #include #define queclass(mp) ((mp)->b_band & QPCTL) #else #include #endif #include #ifdef SVR4 #include #include #include #include #ifdef SOL2 #include #include #include #include #else #include #include #include #include #endif /* SOL2 */ #else /* not SVR4 */ #include #endif /* SVR4 */ #include #include #include "ppp_mod.h" /* * Modifications marked with #ifdef PRIOQ are for priority queueing of * interactive traffic, and are due to Marko Zec . */ #ifdef PRIOQ #endif /* PRIOQ */ #include /* leave this outside of PRIOQ for htons */ #ifdef __STDC__ #define __P(x) x #else #define __P(x) () #endif /* * The IP module may use this SAP value for IP packets. */ #ifndef ETHERTYPE_IP #define ETHERTYPE_IP 0x800 #endif #if !defined(ETHERTYPE_IPV6) #define ETHERTYPE_IPV6 0x86dd #endif /* !defined(ETHERTYPE_IPV6) */ #if !defined(ETHERTYPE_ALLSAP) && defined(SOL2) #define ETHERTYPE_ALLSAP 0 #endif /* !defined(ETHERTYPE_ALLSAP) && defined(SOL2) */ #if !defined(PPP_ALLSAP) && defined(SOL2) #define PPP_ALLSAP PPP_ALLSTATIONS #endif /* !defined(PPP_ALLSAP) && defined(SOL2) */ extern time_t time; #ifdef SOL2 /* * We use this reader-writer lock to ensure that the lower streams * stay connected to the upper streams while the lower-side put and * service procedures are running. Essentially it is an existence * lock for the upper stream associated with each lower stream. */ krwlock_t ppp_lower_lock; #define LOCK_LOWER_W rw_enter(&ppp_lower_lock, RW_WRITER) #define LOCK_LOWER_R rw_enter(&ppp_lower_lock, RW_READER) #define TRYLOCK_LOWER_R rw_tryenter(&ppp_lower_lock, RW_READER) #define UNLOCK_LOWER rw_exit(&ppp_lower_lock) #define MT_ENTER(x) mutex_enter(x) #define MT_EXIT(x) mutex_exit(x) /* * Notes on multithreaded implementation for Solaris 2: * * We use an inner perimeter around each queue pair and an outer * perimeter around the whole driver. The inner perimeter is * entered exclusively for all entry points (open, close, put, * service). The outer perimeter is entered exclusively for open * and close and shared for put and service. This is all done for * us by the streams framework. * * I used to think that the perimeters were entered for the lower * streams' put and service routines as well as for the upper streams'. * Because of problems experienced by people, and after reading the * documentation more closely, I now don't think that is true. So we * now use ppp_lower_lock to give us an existence guarantee on the * upper stream controlling each lower stream. * * Shared entry to the outer perimeter protects the existence of all * the upper streams and their upperstr_t structures, and guarantees * that the following fields of any upperstr_t won't change: * nextmn, next, nextppa. It guarantees that the lowerq field of an * upperstr_t won't go from non-zero to zero, that the global `ppas' * won't change and that the no lower stream will get unlinked. * * Shared (reader) access to ppa_lower_lock guarantees that no lower * stream will be unlinked and that the lowerq field of all upperstr_t * structures won't change. */ #else /* SOL2 */ #define LOCK_LOWER_W 0 #define LOCK_LOWER_R 0 #define TRYLOCK_LOWER_R 1 #define UNLOCK_LOWER 0 #define MT_ENTER(x) 0 #define MT_EXIT(x) 0 #endif /* SOL2 */ /* * Private information; one per upper stream. */ typedef struct upperstr { minor_t mn; /* minor device number */ struct upperstr *nextmn; /* next minor device */ queue_t *q; /* read q associated with this upper stream */ int flags; /* flag bits, see below */ int state; /* current DLPI state */ int sap; /* service access point */ int req_sap; /* which SAP the DLPI client requested */ struct upperstr *ppa; /* control stream for our ppa */ struct upperstr *next; /* next stream for this ppa */ uint ioc_id; /* last ioctl ID for this stream */ enum NPmode npmode; /* what to do with packets on this SAP */ unsigned char rblocked; /* flow control has blocked upper read strm */ /* N.B. rblocked is only changed by control stream's put/srv procs */ /* * There is exactly one control stream for each PPA. * The following fields are only used for control streams. */ int ppa_id; queue_t *lowerq; /* write queue attached below this PPA */ struct upperstr *nextppa; /* next control stream */ int mru; int mtu; struct pppstat stats; /* statistics */ time_t last_sent; /* time last NP packet sent */ time_t last_recv; /* time last NP packet rcvd */ #ifdef SOL2 kmutex_t stats_lock; /* lock for stats updates */ kstat_t *kstats; /* stats for netstat */ #endif /* SOL2 */ #ifdef LACHTCP int ifflags; char ifname[IFNAMSIZ]; struct ifstats ifstats; #endif /* LACHTCP */ } upperstr_t; /* Values for flags */ #define US_PRIV 1 /* stream was opened by superuser */ #define US_CONTROL 2 /* stream is a control stream */ #define US_BLOCKED 4 /* flow ctrl has blocked lower write stream */ #define US_LASTMOD 8 /* no PPP modules below us */ #define US_DBGLOG 0x10 /* log various occurrences */ #define US_RBLOCKED 0x20 /* flow ctrl has blocked upper read stream */ #if defined(SOL2) #if DL_CURRENT_VERSION >= 2 #define US_PROMISC 0x40 /* stream is promiscuous */ #endif /* DL_CURRENT_VERSION >= 2 */ #define US_RAWDATA 0x80 /* raw M_DATA, no DLPI header */ #endif /* defined(SOL2) */ #ifdef PRIOQ static u_char max_band=0; static u_char def_band=0; #define IPPORT_DEFAULT 65535 /* * Port priority table * Highest priority ports are listed first, lowest are listed last. * ICMP & packets using unlisted ports will be treated as "default". * If IPPORT_DEFAULT is not listed here, "default" packets will be * assigned lowest priority. * Each line should be terminated with "0". * Line containing only "0" marks the end of the list. */ static u_short prioq_table[]= { 113, 53, 0, 22, 23, 513, 517, 518, 0, 514, 21, 79, 111, 0, 25, 109, 110, 0, IPPORT_DEFAULT, 0, 20, 70, 80, 8001, 8008, 8080, 0, /* 8001,8008,8080 - common proxy ports */ 0 }; #endif /* PRIOQ */ static upperstr_t *minor_devs = NULL; static upperstr_t *ppas = NULL; #ifdef SVR4 static int pppopen __P((queue_t *, dev_t *, int, int, cred_t *)); static int pppclose __P((queue_t *, int, cred_t *)); #else static int pppopen __P((queue_t *, int, int, int)); static int pppclose __P((queue_t *, int)); #endif /* SVR4 */ static int pppurput __P((queue_t *, mblk_t *)); static int pppuwput __P((queue_t *, mblk_t *)); static int pppursrv __P((queue_t *)); static int pppuwsrv __P((queue_t *)); static int ppplrput __P((queue_t *, mblk_t *)); static int ppplwput __P((queue_t *, mblk_t *)); static int ppplrsrv __P((queue_t *)); static int ppplwsrv __P((queue_t *)); #ifndef NO_DLPI static void dlpi_request __P((queue_t *, mblk_t *, upperstr_t *)); static void dlpi_error __P((queue_t *, upperstr_t *, int, int, int)); static void dlpi_ok __P((queue_t *, int)); #endif static int send_data __P((mblk_t *, upperstr_t *)); static void new_ppa __P((queue_t *, mblk_t *)); static void attach_ppa __P((queue_t *, mblk_t *)); #ifndef NO_DLPI static void detach_ppa __P((queue_t *, mblk_t *)); #endif static void detach_lower __P((queue_t *, mblk_t *)); static void debug_dump __P((queue_t *, mblk_t *)); static upperstr_t *find_dest __P((upperstr_t *, int)); #if defined(SOL2) static upperstr_t *find_promisc __P((upperstr_t *, int)); static mblk_t *prepend_ether __P((upperstr_t *, mblk_t *, int)); static mblk_t *prepend_udind __P((upperstr_t *, mblk_t *, int)); static void promisc_sendup __P((upperstr_t *, mblk_t *, int, int)); #endif /* defined(SOL2) */ static int putctl2 __P((queue_t *, int, int, int)); static int putctl4 __P((queue_t *, int, int, int)); static int pass_packet __P((upperstr_t *ppa, mblk_t *mp, int outbound)); #ifdef FILTER_PACKETS static int ip_hard_filter __P((upperstr_t *ppa, mblk_t *mp, int outbound)); #endif /* FILTER_PACKETS */ #define PPP_ID 0xb1a6 static struct module_info ppp_info = { #ifdef PRIOQ PPP_ID, "ppp", 0, 512, 512, 384 #else PPP_ID, "ppp", 0, 512, 512, 128 #endif /* PRIOQ */ }; static struct qinit pppurint = { pppurput, pppursrv, pppopen, pppclose, NULL, &ppp_info, NULL }; static struct qinit pppuwint = { pppuwput, pppuwsrv, NULL, NULL, NULL, &ppp_info, NULL }; static struct qinit ppplrint = { ppplrput, ppplrsrv, NULL, NULL, NULL, &ppp_info, NULL }; static struct qinit ppplwint = { ppplwput, ppplwsrv, NULL, NULL, NULL, &ppp_info, NULL }; #ifdef LACHTCP extern struct ifstats *ifstats; int pppdevflag = 0; #endif struct streamtab pppinfo = { &pppurint, &pppuwint, &ppplrint, &ppplwint }; int ppp_count; /* * How we maintain statistics. */ #ifdef SOL2 #define INCR_IPACKETS(ppa) \ if (ppa->kstats != 0) { \ KSTAT_NAMED_PTR(ppa->kstats)[0].value.ul++; \ } #define INCR_IERRORS(ppa) \ if (ppa->kstats != 0) { \ KSTAT_NAMED_PTR(ppa->kstats)[1].value.ul++; \ } #define INCR_OPACKETS(ppa) \ if (ppa->kstats != 0) { \ KSTAT_NAMED_PTR(ppa->kstats)[2].value.ul++; \ } #define INCR_OERRORS(ppa) \ if (ppa->kstats != 0) { \ KSTAT_NAMED_PTR(ppa->kstats)[3].value.ul++; \ } #endif #ifdef LACHTCP #define INCR_IPACKETS(ppa) ppa->ifstats.ifs_ipackets++; #define INCR_IERRORS(ppa) ppa->ifstats.ifs_ierrors++; #define INCR_OPACKETS(ppa) ppa->ifstats.ifs_opackets++; #define INCR_OERRORS(ppa) ppa->ifstats.ifs_oerrors++; #endif /* * STREAMS driver entry points. */ static int #ifdef SVR4 pppopen(q, devp, oflag, sflag, credp) queue_t *q; dev_t *devp; int oflag, sflag; cred_t *credp; #else pppopen(q, dev, oflag, sflag) queue_t *q; int dev; /* really dev_t */ int oflag, sflag; #endif { upperstr_t *up; upperstr_t **prevp; minor_t mn; #ifdef PRIOQ u_short *ptr; u_char new_band; #endif /* PRIOQ */ if (q->q_ptr) DRV_OPEN_OK(dev); /* device is already open */ #ifdef PRIOQ /* Calculate max_bband & def_band from definitions in prioq.h This colud be done at some more approtiate time (less often) but this way it works well so I'll just leave it here */ max_band = 1; def_band = 0; ptr = prioq_table; while (*ptr) { new_band = 1; while (*ptr) if (*ptr++ == IPPORT_DEFAULT) { new_band = 0; def_band = max_band; } max_band += new_band; ptr++; } if (def_band) def_band = max_band - def_band; --max_band; #endif /* PRIOQ */ if (sflag == CLONEOPEN) { mn = 0; for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) { if (up->mn != mn) break; ++mn; } } else { #ifdef SVR4 mn = getminor(*devp); #else mn = minor(dev); #endif for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) { if (up->mn >= mn) break; } if (up->mn == mn) { /* this can't happen */ q->q_ptr = WR(q)->q_ptr = (caddr_t) up; DRV_OPEN_OK(dev); } } /* * Construct a new minor node. */ up = (upperstr_t *) ALLOC_SLEEP(sizeof(upperstr_t)); bzero((caddr_t) up, sizeof(upperstr_t)); if (up == 0) { DPRINT("pppopen: out of kernel memory\n"); OPEN_ERROR(ENXIO); } up->nextmn = *prevp; *prevp = up; up->mn = mn; #ifdef SVR4 *devp = makedevice(getmajor(*devp), mn); #endif up->q = q; if (NOTSUSER() == 0) up->flags |= US_PRIV; #ifndef NO_DLPI up->state = DL_UNATTACHED; #endif #ifdef LACHTCP up->ifflags = IFF_UP | IFF_POINTOPOINT; #endif up->sap = -1; up->last_sent = up->last_recv = time; up->npmode = NPMODE_DROP; q->q_ptr = (caddr_t) up; WR(q)->q_ptr = (caddr_t) up; noenable(WR(q)); #ifdef SOL2 mutex_init(&up->stats_lock, NULL, MUTEX_DRIVER, NULL); #endif ++ppp_count; qprocson(q); DRV_OPEN_OK(makedev(major(dev), mn)); } static int #ifdef SVR4 pppclose(q, flag, credp) queue_t *q; int flag; cred_t *credp; #else pppclose(q, flag) queue_t *q; int flag; #endif { upperstr_t *up, **upp; upperstr_t *as, *asnext; upperstr_t **prevp; qprocsoff(q); up = (upperstr_t *) q->q_ptr; if (up == 0) { DPRINT("pppclose: q_ptr = 0\n"); return 0; } if (up->flags & US_DBGLOG) DPRINT2("ppp/%d: close, flags=%x\n", up->mn, up->flags); if (up->flags & US_CONTROL) { #ifdef LACHTCP struct ifstats *ifp, *pifp; #endif if (up->lowerq != 0) { /* Gack! the lower stream should have be unlinked earlier! */ DPRINT1("ppp%d: lower stream still connected on close?\n", up->mn); LOCK_LOWER_W; up->lowerq->q_ptr = 0; RD(up->lowerq)->q_ptr = 0; up->lowerq = 0; UNLOCK_LOWER; } /* * This stream represents a PPA: * For all streams attached to the PPA, clear their * references to this PPA. * Then remove this PPA from the list of PPAs. */ for (as = up->next; as != 0; as = asnext) { asnext = as->next; as->next = 0; as->ppa = 0; if (as->flags & US_BLOCKED) { as->flags &= ~US_BLOCKED; flushq(WR(as->q), FLUSHDATA); } } for (upp = &ppas; *upp != 0; upp = &(*upp)->nextppa) if (*upp == up) { *upp = up->nextppa; break; } #ifdef LACHTCP /* Remove the statistics from the active list. */ for (ifp = ifstats, pifp = 0; ifp; ifp = ifp->ifs_next) { if (ifp == &up->ifstats) { if (pifp) pifp->ifs_next = ifp->ifs_next; else ifstats = ifp->ifs_next; break; } pifp = ifp; } #endif } else { /* * If this stream is attached to a PPA, * remove it from the PPA's list. */ if ((as = up->ppa) != 0) { for (; as->next != 0; as = as->next) if (as->next == up) { as->next = up->next; break; } } } #ifdef SOL2 if (up->kstats) kstat_delete(up->kstats); mutex_destroy(&up->stats_lock); #endif q->q_ptr = NULL; WR(q)->q_ptr = NULL; for (prevp = &minor_devs; *prevp != 0; prevp = &(*prevp)->nextmn) { if (*prevp == up) { *prevp = up->nextmn; break; } } FREE(up, sizeof(upperstr_t)); --ppp_count; return 0; } /* * A message from on high. We do one of three things: * - qreply() * - put the message on the lower write stream * - queue it for our service routine */ static int pppuwput(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us, *ppa, *nps; struct iocblk *iop; struct linkblk *lb; #ifdef LACHTCP struct ifreq *ifr; int i; #endif queue_t *lq; int error, n, sap; mblk_t *mq; struct ppp_idle *pip; #ifdef PRIOQ queue_t *tlq; #endif /* PRIOQ */ #ifdef NO_DLPI upperstr_t *os; #endif us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("pppuwput: q_ptr = 0!\n"); return 0; } if (mp == 0) { DPRINT1("pppuwput/%d: mp = 0!\n", us->mn); return 0; } if (mp->b_datap == 0) { DPRINT1("pppuwput/%d: mp->b_datap = 0!\n", us->mn); return 0; } switch (mp->b_datap->db_type) { #ifndef NO_DLPI case M_PCPROTO: case M_PROTO: dlpi_request(q, mp, us); break; #endif /* NO_DLPI */ case M_DATA: if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: uwput M_DATA len=%d flags=%x\n", us->mn, msgdsize(mp), us->flags); if (us->ppa == 0 || msgdsize(mp) > us->ppa->mtu + PPP_HDRLEN #ifndef NO_DLPI || (us->flags & US_CONTROL) == 0 #endif /* NO_DLPI */ ) { DPRINT1("pppuwput: junk data len=%d\n", msgdsize(mp)); freemsg(mp); break; } #ifdef NO_DLPI /* pass_packet frees the packet on returning 0 */ if ((us->flags & US_CONTROL) == 0 && !pass_packet(us, mp, 1)) break; #endif if (!send_data(mp, us) && !putq(q, mp)) freemsg(mp); break; case M_IOCTL: iop = (struct iocblk *) mp->b_rptr; error = EINVAL; if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: ioctl %x count=%d\n", us->mn, iop->ioc_cmd, iop->ioc_count); switch (iop->ioc_cmd) { #if defined(SOL2) case DLIOCRAW: /* raw M_DATA mode */ us->flags |= US_RAWDATA; error = 0; break; #endif /* defined(SOL2) */ case I_LINK: if ((us->flags & US_CONTROL) == 0 || us->lowerq != 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl I_LINK b_cont = 0!\n", us->mn); break; } lb = (struct linkblk *) mp->b_cont->b_rptr; lq = lb->l_qbot; if (lq == 0) { DPRINT1("pppuwput/%d: ioctl I_LINK l_qbot = 0!\n", us->mn); break; } LOCK_LOWER_W; us->lowerq = lq; lq->q_ptr = (caddr_t) q; RD(lq)->q_ptr = (caddr_t) us->q; UNLOCK_LOWER; iop->ioc_count = 0; error = 0; us->flags &= ~US_LASTMOD; /* Unblock upper streams which now feed this lower stream. */ qenable(q); /* Send useful information down to the modules which are now linked below us. */ putctl2(lq, M_CTL, PPPCTL_UNIT, us->ppa_id); putctl4(lq, M_CTL, PPPCTL_MRU, us->mru); putctl4(lq, M_CTL, PPPCTL_MTU, us->mtu); #ifdef PRIOQ /* Lower tty driver's queue hiwat/lowat from default 4096/128 to 256/128 since we don't want queueing of data on output to physical device */ freezestr(lq); for (tlq = lq; tlq->q_next != NULL; tlq = tlq->q_next) ; strqset(tlq, QHIWAT, 0, 256); strqset(tlq, QLOWAT, 0, 128); unfreezestr(lq); #endif /* PRIOQ */ break; case I_UNLINK: if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl I_UNLINK b_cont = 0!\n", us->mn); break; } lb = (struct linkblk *) mp->b_cont->b_rptr; #if DEBUG if (us->lowerq != lb->l_qbot) { DPRINT2("ppp unlink: lowerq=%x qbot=%x\n", us->lowerq, lb->l_qbot); break; } #endif iop->ioc_count = 0; qwriter(q, mp, detach_lower, PERIM_OUTER); /* mp is now gone */ error = -1; break; case PPPIO_NEWPPA: if (us->flags & US_CONTROL) break; if ((us->flags & US_PRIV) == 0) { error = EPERM; break; } /* Arrange to return an int */ if ((mq = mp->b_cont) == 0 || mq->b_datap->db_lim - mq->b_rptr < sizeof(int)) { mq = allocb(sizeof(int), BPRI_HI); if (mq == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = mq; mq->b_cont = 0; } iop->ioc_count = sizeof(int); mq->b_wptr = mq->b_rptr + sizeof(int); qwriter(q, mp, new_ppa, PERIM_OUTER); /* mp is now gone */ error = -1; break; case PPPIO_ATTACH: /* like dlpi_attach, for programs which can't write to the stream (like pppstats) */ if (iop->ioc_count != sizeof(int) || us->ppa != 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_ATTACH b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; for (ppa = ppas; ppa != 0; ppa = ppa->nextppa) if (ppa->ppa_id == n) break; if (ppa == 0) break; us->ppa = ppa; iop->ioc_count = 0; qwriter(q, mp, attach_ppa, PERIM_OUTER); /* mp is now gone */ error = -1; break; #ifdef NO_DLPI case PPPIO_BIND: /* Attach to a given SAP. */ if (iop->ioc_count != sizeof(int) || us->ppa == 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_BIND b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; /* n must be a valid PPP network protocol number. */ if (n < 0x21 || n > 0x3fff || (n & 0x101) != 1) break; /* check that no other stream is bound to this sap already. */ for (os = us->ppa; os != 0; os = os->next) if (os->sap == n) break; if (os != 0) break; us->sap = n; iop->ioc_count = 0; error = 0; break; #endif /* NO_DLPI */ case PPPIO_MRU: if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_MRU b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; if (n <= 0 || n > PPP_MAXMRU) break; if (n < PPP_MRU) n = PPP_MRU; us->mru = n; if (us->lowerq) putctl4(us->lowerq, M_CTL, PPPCTL_MRU, n); error = 0; iop->ioc_count = 0; break; case PPPIO_MTU: if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_MTU b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; if (n <= 0 || n > PPP_MAXMTU) break; us->mtu = n; #ifdef LACHTCP /* The MTU reported in netstat, not used as IP max packet size! */ us->ifstats.ifs_mtu = n; #endif if (us->lowerq) putctl4(us->lowerq, M_CTL, PPPCTL_MTU, n); error = 0; iop->ioc_count = 0; break; case PPPIO_LASTMOD: us->flags |= US_LASTMOD; error = 0; break; case PPPIO_DEBUG: if (iop->ioc_count != sizeof(int)) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_DEBUG b_cont = 0!\n", us->mn); break; } n = *(int *)mp->b_cont->b_rptr; if (n == PPPDBG_DUMP + PPPDBG_DRIVER) { qwriter(q, mp, debug_dump, PERIM_OUTER); /* mp is now gone */ error = -1; } else if (n == PPPDBG_LOG + PPPDBG_DRIVER) { DPRINT1("ppp/%d: debug log enabled\n", us->mn); us->flags |= US_DBGLOG; iop->ioc_count = 0; error = 0; } else { if (us->ppa == 0 || us->ppa->lowerq == 0) break; putnext(us->ppa->lowerq, mp); /* mp is now gone */ error = -1; } break; case PPPIO_NPMODE: if (iop->ioc_count != 2 * sizeof(int)) break; if ((us->flags & US_CONTROL) == 0) break; if (mp->b_cont == 0) { DPRINT1("pppuwput/%d: ioctl PPPIO_NPMODE b_cont = 0!\n", us->mn); break; } sap = ((int *)mp->b_cont->b_rptr)[0]; for (nps = us->next; nps != 0; nps = nps->next) { if (us->flags & US_DBGLOG) DPRINT2("us = 0x%x, us->next->sap = 0x%x\n", nps, nps->sap); if (nps->sap == sap) break; } if (nps == 0) { if (us->flags & US_DBGLOG) DPRINT2("ppp/%d: no stream for sap %x\n", us->mn, sap); break; } /* XXX possibly should use qwriter here */ nps->npmode = (enum NPmode) ((int *)mp->b_cont->b_rptr)[1]; if (nps->npmode != NPMODE_QUEUE && (nps->flags & US_BLOCKED) != 0) qenable(WR(nps->q)); iop->ioc_count = 0; error = 0; break; case PPPIO_GIDLE: if ((ppa = us->ppa) == 0) break; mq = allocb(sizeof(struct ppp_idle), BPRI_HI); if (mq == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = mq; mq->b_cont = 0; pip = (struct ppp_idle *) mq->b_wptr; pip->xmit_idle = time - ppa->last_sent; pip->recv_idle = time - ppa->last_recv; mq->b_wptr += sizeof(struct ppp_idle); iop->ioc_count = sizeof(struct ppp_idle); error = 0; break; #ifdef LACHTCP case SIOCSIFNAME: /* Sent from IP down to us. Attach the ifstats structure. */ if (iop->ioc_count != sizeof(struct ifreq) || us->ppa == 0) break; ifr = (struct ifreq *)mp->b_cont->b_rptr; /* Find the unit number in the interface name. */ for (i = 0; i < IFNAMSIZ; i++) { if (ifr->ifr_name[i] == 0 || (ifr->ifr_name[i] >= '0' && ifr->ifr_name[i] <= '9')) break; else us->ifname[i] = ifr->ifr_name[i]; } us->ifname[i] = 0; /* Convert the unit number to binary. */ for (n = 0; i < IFNAMSIZ; i++) { if (ifr->ifr_name[i] == 0) { break; } else { n = n * 10 + ifr->ifr_name[i] - '0'; } } /* Verify the ppa. */ if (us->ppa->ppa_id != n) break; ppa = us->ppa; /* Set up the netstat block. */ strncpy (ppa->ifname, us->ifname, IFNAMSIZ); ppa->ifstats.ifs_name = ppa->ifname; ppa->ifstats.ifs_unit = n; ppa->ifstats.ifs_active = us->state != DL_UNBOUND; ppa->ifstats.ifs_mtu = ppa->mtu; /* Link in statistics used by netstat. */ ppa->ifstats.ifs_next = ifstats; ifstats = &ppa->ifstats; iop->ioc_count = 0; error = 0; break; case SIOCGIFFLAGS: if (!(us->flags & US_CONTROL)) { if (us->ppa) us = us->ppa; else break; } ((struct iocblk_in *)iop)->ioc_ifflags = us->ifflags; error = 0; break; case SIOCSIFFLAGS: if (!(us->flags & US_CONTROL)) { if (us->ppa) us = us->ppa; else break; } us->ifflags = ((struct iocblk_in *)iop)->ioc_ifflags; error = 0; break; case SIOCSIFADDR: if (!(us->flags & US_CONTROL)) { if (us->ppa) us = us->ppa; else break; } us->ifflags |= IFF_RUNNING; ((struct iocblk_in *)iop)->ioc_ifflags |= IFF_RUNNING; error = 0; break; case SIOCSIFMTU: /* * Vanilla SVR4 systems don't handle SIOCSIFMTU, rather * they take the MTU from the DL_INFO_ACK we sent in response * to their DL_INFO_REQ. Fortunately, they will update the * MTU if we send an unsolicited DL_INFO_ACK up. */ if ((mq = allocb(sizeof(dl_info_req_t), BPRI_HI)) == 0) break; /* should do bufcall */ ((union DL_primitives *)mq->b_rptr)->dl_primitive = DL_INFO_REQ; mq->b_wptr = mq->b_rptr + sizeof(dl_info_req_t); dlpi_request(q, mq, us); /* mp is now gone */ error = -1; break; case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFADDR: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCGIFMETRIC: error = 0; break; #endif /* LACHTCP */ default: if (us->ppa == 0 || us->ppa->lowerq == 0) break; us->ioc_id = iop->ioc_id; error = -1; switch (iop->ioc_cmd) { case PPPIO_GETSTAT: case PPPIO_GETCSTAT: if (us->flags & US_LASTMOD) { error = EINVAL; break; } putnext(us->ppa->lowerq, mp); break; default: if (us->flags & US_PRIV) putnext(us->ppa->lowerq, mp); else { DPRINT1("ppp ioctl %x rejected\n", iop->ioc_cmd); error = EPERM; } break; } break; } if (error > 0) { iop->ioc_error = error; mp->b_datap->db_type = M_IOCNAK; qreply(q, mp); } else if (error == 0) { mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } break; case M_FLUSH: if (us->flags & US_DBGLOG) DPRINT2("ppp/%d: flush %x\n", us->mn, *mp->b_rptr); if (*mp->b_rptr & FLUSHW) flushq(q, FLUSHDATA); if (*mp->b_rptr & FLUSHR) { *mp->b_rptr &= ~FLUSHW; qreply(q, mp); } else freemsg(mp); break; default: freemsg(mp); break; } return 0; } #ifndef NO_DLPI static void dlpi_request(q, mp, us) queue_t *q; mblk_t *mp; upperstr_t *us; { union DL_primitives *d = (union DL_primitives *) mp->b_rptr; int size = mp->b_wptr - mp->b_rptr; mblk_t *reply, *np; upperstr_t *ppa, *os; int sap, len; dl_info_ack_t *info; dl_bind_ack_t *ackp; #if DL_CURRENT_VERSION >= 2 dl_phys_addr_ack_t *paddrack; static struct ether_addr eaddr = {0}; #endif if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: dlpi prim %x len=%d\n", us->mn, d->dl_primitive, size); switch (d->dl_primitive) { case DL_INFO_REQ: if (size < sizeof(dl_info_req_t)) goto badprim; if ((reply = allocb(sizeof(dl_info_ack_t), BPRI_HI)) == 0) break; /* should do bufcall */ reply->b_datap->db_type = M_PCPROTO; info = (dl_info_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_info_ack_t); bzero((caddr_t) info, sizeof(dl_info_ack_t)); info->dl_primitive = DL_INFO_ACK; info->dl_max_sdu = us->ppa? us->ppa->mtu: PPP_MAXMTU; info->dl_min_sdu = 1; info->dl_addr_length = sizeof(uint); info->dl_mac_type = DL_ETHER; /* a bigger lie */ info->dl_current_state = us->state; info->dl_service_mode = DL_CLDLS; info->dl_provider_style = DL_STYLE2; #if DL_CURRENT_VERSION >= 2 info->dl_sap_length = sizeof(uint); info->dl_version = DL_CURRENT_VERSION; #endif qreply(q, reply); break; case DL_ATTACH_REQ: if (size < sizeof(dl_attach_req_t)) goto badprim; if (us->state != DL_UNATTACHED || us->ppa != 0) { dlpi_error(q, us, DL_ATTACH_REQ, DL_OUTSTATE, 0); break; } for (ppa = ppas; ppa != 0; ppa = ppa->nextppa) if (ppa->ppa_id == d->attach_req.dl_ppa) break; if (ppa == 0) { dlpi_error(q, us, DL_ATTACH_REQ, DL_BADPPA, 0); break; } us->ppa = ppa; qwriter(q, mp, attach_ppa, PERIM_OUTER); return; case DL_DETACH_REQ: if (size < sizeof(dl_detach_req_t)) goto badprim; if (us->state != DL_UNBOUND || us->ppa == 0) { dlpi_error(q, us, DL_DETACH_REQ, DL_OUTSTATE, 0); break; } qwriter(q, mp, detach_ppa, PERIM_OUTER); return; case DL_BIND_REQ: if (size < sizeof(dl_bind_req_t)) goto badprim; if (us->state != DL_UNBOUND || us->ppa == 0) { dlpi_error(q, us, DL_BIND_REQ, DL_OUTSTATE, 0); break; } #if 0 /* apparently this test fails (unnecessarily?) on some systems */ if (d->bind_req.dl_service_mode != DL_CLDLS) { dlpi_error(q, us, DL_BIND_REQ, DL_UNSUPPORTED, 0); break; } #endif /* saps must be valid PPP network protocol numbers, except that we accept ETHERTYPE_IP in place of PPP_IP. */ sap = d->bind_req.dl_sap; us->req_sap = sap; #if defined(SOL2) if (us->flags & US_DBGLOG) DPRINT2("DL_BIND_REQ: ip gives sap = 0x%x, us = 0x%x", sap, us); if (sap == ETHERTYPE_IP) /* normal IFF_IPV4 */ sap = PPP_IP; else if (sap == ETHERTYPE_IPV6) /* when IFF_IPV6 is set */ sap = PPP_IPV6; else if (sap == ETHERTYPE_ALLSAP) /* snoop gives sap of 0 */ sap = PPP_ALLSAP; else { DPRINT2("DL_BIND_REQ: unrecognized sap = 0x%x, us = 0x%x", sap, us); dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0); break; } #else if (sap == ETHERTYPE_IP) sap = PPP_IP; if (sap < 0x21 || sap > 0x3fff || (sap & 0x101) != 1) { dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0); break; } #endif /* defined(SOL2) */ /* check that no other stream is bound to this sap already. */ for (os = us->ppa; os != 0; os = os->next) if (os->sap == sap) break; if (os != 0) { dlpi_error(q, us, DL_BIND_REQ, DL_NOADDR, 0); break; } us->sap = sap; us->state = DL_IDLE; if ((reply = allocb(sizeof(dl_bind_ack_t) + sizeof(uint), BPRI_HI)) == 0) break; /* should do bufcall */ ackp = (dl_bind_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_bind_ack_t) + sizeof(uint); reply->b_datap->db_type = M_PCPROTO; bzero((caddr_t) ackp, sizeof(dl_bind_ack_t)); ackp->dl_primitive = DL_BIND_ACK; ackp->dl_sap = sap; ackp->dl_addr_length = sizeof(uint); ackp->dl_addr_offset = sizeof(dl_bind_ack_t); *(uint *)(ackp+1) = sap; qreply(q, reply); break; case DL_UNBIND_REQ: if (size < sizeof(dl_unbind_req_t)) goto badprim; if (us->state != DL_IDLE) { dlpi_error(q, us, DL_UNBIND_REQ, DL_OUTSTATE, 0); break; } us->sap = -1; us->state = DL_UNBOUND; #ifdef LACHTCP us->ppa->ifstats.ifs_active = 0; #endif dlpi_ok(q, DL_UNBIND_REQ); break; case DL_UNITDATA_REQ: if (size < sizeof(dl_unitdata_req_t)) goto badprim; if (us->state != DL_IDLE) { dlpi_error(q, us, DL_UNITDATA_REQ, DL_OUTSTATE, 0); break; } if ((ppa = us->ppa) == 0) { cmn_err(CE_CONT, "ppp: in state dl_idle but ppa == 0?\n"); break; } len = mp->b_cont == 0? 0: msgdsize(mp->b_cont); if (len > ppa->mtu) { DPRINT2("dlpi data too large (%d > %d)\n", len, ppa->mtu); break; } #if defined(SOL2) /* * Should there be any promiscuous stream(s), send the data * up for each promiscuous stream that we recognize. */ if (mp->b_cont) promisc_sendup(ppa, mp->b_cont, us->sap, 0); #endif /* defined(SOL2) */ mp->b_band = 0; #ifdef PRIOQ /* Extract s_port & d_port from IP-packet, the code is a bit dirty here, but so am I, too... */ if (mp->b_datap->db_type == M_PROTO && us->sap == PPP_IP && mp->b_cont != 0) { u_char *bb, *tlh; int iphlen, len; u_short *ptr; u_char band_unset, cur_band, syn; u_short s_port, d_port; bb = mp->b_cont->b_rptr; /* bb points to IP-header*/ len = mp->b_cont->b_wptr - mp->b_cont->b_rptr; syn = 0; s_port = IPPORT_DEFAULT; d_port = IPPORT_DEFAULT; if (len >= 20) { /* 20 = minimum length of IP header */ iphlen = (bb[0] & 0x0f) * 4; tlh = bb + iphlen; len -= iphlen; switch (bb[9]) { case IPPROTO_TCP: if (len >= 20) { /* min length of TCP header */ s_port = (tlh[0] << 8) + tlh[1]; d_port = (tlh[2] << 8) + tlh[3]; syn = tlh[13] & 0x02; } break; case IPPROTO_UDP: if (len >= 8) { /* min length of UDP header */ s_port = (tlh[0] << 8) + tlh[1]; d_port = (tlh[2] << 8) + tlh[3]; } break; } } /* * Now calculate b_band for this packet from the * port-priority table. */ ptr = prioq_table; cur_band = max_band; band_unset = 1; while (*ptr) { while (*ptr && band_unset) if (s_port == *ptr || d_port == *ptr++) { mp->b_band = cur_band; band_unset = 0; break; } ptr++; cur_band--; } if (band_unset) mp->b_band = def_band; /* It may be usable to urge SYN packets a bit */ if (syn) mp->b_band++; } #endif /* PRIOQ */ /* this assumes PPP_HDRLEN <= sizeof(dl_unitdata_req_t) */ if (mp->b_datap->db_ref > 1) { np = allocb(PPP_HDRLEN, BPRI_HI); if (np == 0) break; /* gak! */ np->b_cont = mp->b_cont; mp->b_cont = 0; freeb(mp); mp = np; } else mp->b_datap->db_type = M_DATA; /* XXX should use dl_dest_addr_offset/length here, but we would have to translate ETHERTYPE_IP -> PPP_IP */ mp->b_wptr = mp->b_rptr + PPP_HDRLEN; mp->b_rptr[0] = PPP_ALLSTATIONS; mp->b_rptr[1] = PPP_UI; mp->b_rptr[2] = us->sap >> 8; mp->b_rptr[3] = us->sap; /* pass_packet frees the packet on returning 0 */ if (pass_packet(us, mp, 1)) { if (!send_data(mp, us) && !putq(q, mp)) freemsg(mp); } return; #if DL_CURRENT_VERSION >= 2 case DL_PHYS_ADDR_REQ: if (size < sizeof(dl_phys_addr_req_t)) goto badprim; /* * Don't check state because ifconfig sends this one down too */ if ((reply = allocb(sizeof(dl_phys_addr_ack_t)+ETHERADDRL, BPRI_HI)) == 0) break; /* should do bufcall */ reply->b_datap->db_type = M_PCPROTO; paddrack = (dl_phys_addr_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_phys_addr_ack_t); bzero((caddr_t) paddrack, sizeof(dl_phys_addr_ack_t)+ETHERADDRL); paddrack->dl_primitive = DL_PHYS_ADDR_ACK; paddrack->dl_addr_length = ETHERADDRL; paddrack->dl_addr_offset = sizeof(dl_phys_addr_ack_t); bcopy(&eaddr, reply->b_wptr, ETHERADDRL); reply->b_wptr += ETHERADDRL; qreply(q, reply); break; #if defined(SOL2) case DL_PROMISCON_REQ: if (size < sizeof(dl_promiscon_req_t)) goto badprim; us->flags |= US_PROMISC; dlpi_ok(q, DL_PROMISCON_REQ); break; case DL_PROMISCOFF_REQ: if (size < sizeof(dl_promiscoff_req_t)) goto badprim; us->flags &= ~US_PROMISC; dlpi_ok(q, DL_PROMISCOFF_REQ); break; #else case DL_PROMISCON_REQ: /* fall thru */ case DL_PROMISCOFF_REQ: /* fall thru */ #endif /* defined(SOL2) */ #endif /* DL_CURRENT_VERSION >= 2 */ #if DL_CURRENT_VERSION >= 2 case DL_SET_PHYS_ADDR_REQ: case DL_SUBS_BIND_REQ: case DL_SUBS_UNBIND_REQ: case DL_ENABMULTI_REQ: case DL_DISABMULTI_REQ: case DL_XID_REQ: case DL_TEST_REQ: case DL_REPLY_UPDATE_REQ: case DL_REPLY_REQ: case DL_DATA_ACK_REQ: #endif case DL_CONNECT_REQ: case DL_TOKEN_REQ: dlpi_error(q, us, d->dl_primitive, DL_NOTSUPPORTED, 0); break; case DL_CONNECT_RES: case DL_DISCONNECT_REQ: case DL_RESET_REQ: case DL_RESET_RES: dlpi_error(q, us, d->dl_primitive, DL_OUTSTATE, 0); break; case DL_UDQOS_REQ: dlpi_error(q, us, d->dl_primitive, DL_BADQOSTYPE, 0); break; #if DL_CURRENT_VERSION >= 2 case DL_TEST_RES: case DL_XID_RES: break; #endif default: if (us->flags & US_DBGLOG) DPRINT1("ppp: unknown dlpi prim 0x%x\n", d->dl_primitive); /* fall through */ badprim: dlpi_error(q, us, d->dl_primitive, DL_BADPRIM, 0); break; } freemsg(mp); } static void dlpi_error(q, us, prim, err, uerr) queue_t *q; upperstr_t *us; int prim, err, uerr; { mblk_t *reply; dl_error_ack_t *errp; if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: dlpi error, prim=%x, err=%x\n", us->mn, prim, err); reply = allocb(sizeof(dl_error_ack_t), BPRI_HI); if (reply == 0) return; /* XXX should do bufcall */ reply->b_datap->db_type = M_PCPROTO; errp = (dl_error_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_error_ack_t); errp->dl_primitive = DL_ERROR_ACK; errp->dl_error_primitive = prim; errp->dl_errno = err; errp->dl_unix_errno = uerr; qreply(q, reply); } static void dlpi_ok(q, prim) queue_t *q; int prim; { mblk_t *reply; dl_ok_ack_t *okp; reply = allocb(sizeof(dl_ok_ack_t), BPRI_HI); if (reply == 0) return; /* XXX should do bufcall */ reply->b_datap->db_type = M_PCPROTO; okp = (dl_ok_ack_t *) reply->b_wptr; reply->b_wptr += sizeof(dl_ok_ack_t); okp->dl_primitive = DL_OK_ACK; okp->dl_correct_primitive = prim; qreply(q, reply); } #endif /* NO_DLPI */ /* * If return value is 0, then the packet has already been freed. */ static int pass_packet(us, mp, outbound) upperstr_t *us; mblk_t *mp; int outbound; { int pass; upperstr_t *ppa; if ((ppa = us->ppa) == 0) { freemsg(mp); return 0; } #ifdef FILTER_PACKETS pass = ip_hard_filter(us, mp, outbound); #else /* * Here is where we might, in future, decide whether to pass * or drop the packet, and whether it counts as link activity. */ pass = 1; #endif /* FILTER_PACKETS */ if (pass < 0) { /* pass only if link already up, and don't update time */ if (ppa->lowerq == 0) { freemsg(mp); return 0; } pass = 1; } else if (pass) { if (outbound) ppa->last_sent = time; else ppa->last_recv = time; } return pass; } /* * We have some data to send down to the lower stream (or up the * control stream, if we don't have a lower stream attached). * Returns 1 if the message was dealt with, 0 if it wasn't able * to be sent on and should therefore be queued up. */ static int send_data(mp, us) mblk_t *mp; upperstr_t *us; { upperstr_t *ppa; if ((us->flags & US_BLOCKED) || us->npmode == NPMODE_QUEUE) return 0; ppa = us->ppa; if (ppa == 0 || us->npmode == NPMODE_DROP || us->npmode == NPMODE_ERROR) { if (us->flags & US_DBGLOG) DPRINT2("ppp/%d: dropping pkt (npmode=%d)\n", us->mn, us->npmode); freemsg(mp); return 1; } if (ppa->lowerq == 0) { /* try to send it up the control stream */ if (bcanputnext(ppa->q, mp->b_band)) { /* * The message seems to get corrupted for some reason if * we just send the message up as it is, so we send a copy. */ mblk_t *np = copymsg(mp); freemsg(mp); if (np != 0) putnext(ppa->q, np); return 1; } } else { if (bcanputnext(ppa->lowerq, mp->b_band)) { MT_ENTER(&ppa->stats_lock); ppa->stats.ppp_opackets++; ppa->stats.ppp_obytes += msgdsize(mp); #ifdef INCR_OPACKETS INCR_OPACKETS(ppa); #endif MT_EXIT(&ppa->stats_lock); /* * The lower queue is only ever detached while holding an * exclusive lock on the whole driver. So we can be confident * that the lower queue is still there. */ putnext(ppa->lowerq, mp); return 1; } } us->flags |= US_BLOCKED; return 0; } /* * Allocate a new PPA id and link this stream into the list of PPAs. * This procedure is called with an exclusive lock on all queues in * this driver. */ static void new_ppa(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us, *up, **usp; int ppa_id; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("new_ppa: q_ptr = 0!\n"); return; } usp = &ppas; ppa_id = 0; while ((up = *usp) != 0 && ppa_id == up->ppa_id) { ++ppa_id; usp = &up->nextppa; } us->ppa_id = ppa_id; us->ppa = us; us->next = 0; us->nextppa = *usp; *usp = us; us->flags |= US_CONTROL; us->npmode = NPMODE_PASS; us->mtu = PPP_MTU; us->mru = PPP_MRU; #ifdef SOL2 /* * Create a kstats record for our statistics, so netstat -i works. */ if (us->kstats == 0) { char unit[32]; sprintf(unit, "ppp%d", us->ppa->ppa_id); us->kstats = kstat_create("ppp", us->ppa->ppa_id, unit, "net", KSTAT_TYPE_NAMED, 4, 0); if (us->kstats != 0) { kstat_named_t *kn = KSTAT_NAMED_PTR(us->kstats); strcpy(kn[0].name, "ipackets"); kn[0].data_type = KSTAT_DATA_ULONG; strcpy(kn[1].name, "ierrors"); kn[1].data_type = KSTAT_DATA_ULONG; strcpy(kn[2].name, "opackets"); kn[2].data_type = KSTAT_DATA_ULONG; strcpy(kn[3].name, "oerrors"); kn[3].data_type = KSTAT_DATA_ULONG; kstat_install(us->kstats); } } #endif /* SOL2 */ *(int *)mp->b_cont->b_rptr = ppa_id; mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } static void attach_ppa(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us, *t; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("attach_ppa: q_ptr = 0!\n"); return; } #ifndef NO_DLPI us->state = DL_UNBOUND; #endif for (t = us->ppa; t->next != 0; t = t->next) ; t->next = us; us->next = 0; if (mp->b_datap->db_type == M_IOCTL) { mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } else { #ifndef NO_DLPI dlpi_ok(q, DL_ATTACH_REQ); #endif freemsg(mp); } } #ifndef NO_DLPI static void detach_ppa(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us, *t; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("detach_ppa: q_ptr = 0!\n"); return; } for (t = us->ppa; t->next != 0; t = t->next) if (t->next == us) { t->next = us->next; break; } us->next = 0; us->ppa = 0; us->state = DL_UNATTACHED; dlpi_ok(q, DL_DETACH_REQ); freemsg(mp); } #endif /* * We call this with qwriter in order to give the upper queue procedures * the guarantee that the lower queue is not going to go away while * they are executing. */ static void detach_lower(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("detach_lower: q_ptr = 0!\n"); return; } LOCK_LOWER_W; us->lowerq->q_ptr = 0; RD(us->lowerq)->q_ptr = 0; us->lowerq = 0; UNLOCK_LOWER; /* Unblock streams which now feed back up the control stream. */ qenable(us->q); mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } static int pppuwsrv(q) queue_t *q; { upperstr_t *us, *as; mblk_t *mp; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("pppuwsrv: q_ptr = 0!\n"); return 0; } /* * If this is a control stream, then this service procedure * probably got enabled because of flow control in the lower * stream being enabled (or because of the lower stream going * away). Therefore we enable the service procedure of all * attached upper streams. */ if (us->flags & US_CONTROL) { for (as = us->next; as != 0; as = as->next) qenable(WR(as->q)); } /* Try to send on any data queued here. */ us->flags &= ~US_BLOCKED; while ((mp = getq(q)) != 0) { if (!send_data(mp, us)) { putbq(q, mp); break; } } return 0; } /* should never get called... */ static int ppplwput(q, mp) queue_t *q; mblk_t *mp; { putnext(q, mp); return 0; } static int ppplwsrv(q) queue_t *q; { queue_t *uq; /* * Flow control has back-enabled this stream: * enable the upper write service procedure for * the upper control stream for this lower stream. */ LOCK_LOWER_R; uq = (queue_t *) q->q_ptr; if (uq != 0) qenable(uq); UNLOCK_LOWER; return 0; } /* * This should only get called for control streams. */ static int pppurput(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *ppa, *us; int proto, len; struct iocblk *iop; ppa = (upperstr_t *) q->q_ptr; if (ppa == 0) { DPRINT("pppurput: q_ptr = 0!\n"); return 0; } switch (mp->b_datap->db_type) { case M_CTL: MT_ENTER(&ppa->stats_lock); switch (*mp->b_rptr) { case PPPCTL_IERROR: #ifdef INCR_IERRORS INCR_IERRORS(ppa); #endif ppa->stats.ppp_ierrors++; break; case PPPCTL_OERROR: #ifdef INCR_OERRORS INCR_OERRORS(ppa); #endif ppa->stats.ppp_oerrors++; break; } MT_EXIT(&ppa->stats_lock); freemsg(mp); break; case M_IOCACK: case M_IOCNAK: /* * Attempt to match up the response with the stream * that the request came from. */ iop = (struct iocblk *) mp->b_rptr; for (us = ppa; us != 0; us = us->next) if (us->ioc_id == iop->ioc_id) break; if (us == 0) freemsg(mp); else putnext(us->q, mp); break; case M_HANGUP: /* * The serial device has hung up. We don't want to send * the M_HANGUP message up to pppd because that will stop * us from using the control stream any more. Instead we * send a zero-length message as an end-of-file indication. */ freemsg(mp); mp = allocb(1, BPRI_HI); if (mp == 0) { DPRINT1("ppp/%d: couldn't allocate eof message!\n", ppa->mn); break; } putnext(ppa->q, mp); break; case M_DATA: len = msgdsize(mp); if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN) { PULLUP(mp, PPP_HDRLEN); if (mp == 0) { DPRINT1("ppp_urput: msgpullup failed (len=%d)\n", len); break; } } MT_ENTER(&ppa->stats_lock); ppa->stats.ppp_ipackets++; ppa->stats.ppp_ibytes += len; #ifdef INCR_IPACKETS INCR_IPACKETS(ppa); #endif MT_EXIT(&ppa->stats_lock); proto = PPP_PROTOCOL(mp->b_rptr); #if defined(SOL2) /* * Should there be any promiscuous stream(s), send the data * up for each promiscuous stream that we recognize. */ promisc_sendup(ppa, mp, proto, 1); #endif /* defined(SOL2) */ if (proto < 0x8000 && (us = find_dest(ppa, proto)) != 0) { /* * A data packet for some network protocol. * Queue it on the upper stream for that protocol. * XXX could we just putnext it? (would require thought) * The rblocked flag is there to ensure that we keep * messages in order for each network protocol. */ /* pass_packet frees the packet on returning 0 */ if (!pass_packet(us, mp, 0)) break; if (!us->rblocked && !canput(us->q)) us->rblocked = 1; if (!putq(us->rblocked ? q : us->q, mp)) freemsg(mp); break; } /* FALLTHROUGH */ default: /* * A control frame, a frame for an unknown protocol, * or some other message type. * Send it up to pppd via the control stream. */ if (queclass(mp) == QPCTL || canputnext(ppa->q)) putnext(ppa->q, mp); else if (!putq(q, mp)) freemsg(mp); break; } return 0; } static int pppursrv(q) queue_t *q; { upperstr_t *us, *as; mblk_t *mp, *hdr; #ifndef NO_DLPI dl_unitdata_ind_t *ud; #endif int proto; us = (upperstr_t *) q->q_ptr; if (us == 0) { DPRINT("pppursrv: q_ptr = 0!\n"); return 0; } if (us->flags & US_CONTROL) { /* * A control stream. * If there is no lower queue attached, run the write service * routines of other upper streams attached to this PPA. */ if (us->lowerq == 0) { as = us; do { if (as->flags & US_BLOCKED) qenable(WR(as->q)); as = as->next; } while (as != 0); } /* * Messages get queued on this stream's read queue if they * can't be queued on the read queue of the attached stream * that they are destined for. This is for flow control - * when this queue fills up, the lower read put procedure will * queue messages there and the flow control will propagate * down from there. */ while ((mp = getq(q)) != 0) { proto = PPP_PROTOCOL(mp->b_rptr); if (proto < 0x8000 && (as = find_dest(us, proto)) != 0) { if (!canput(as->q)) break; if (!putq(as->q, mp)) freemsg(mp); } else { if (!canputnext(q)) break; putnext(q, mp); } } if (mp) { putbq(q, mp); } else { /* can now put stuff directly on network protocol streams again */ for (as = us->next; as != 0; as = as->next) as->rblocked = 0; } /* * If this stream has a lower stream attached, * enable the read queue's service routine. * XXX we should really only do this if the queue length * has dropped below the low-water mark. */ if (us->lowerq != 0) qenable(RD(us->lowerq)); } else { /* * A network protocol stream. Put a DLPI header on each * packet and send it on. * (Actually, it seems that the IP module will happily * accept M_DATA messages without the DL_UNITDATA_IND header.) */ while ((mp = getq(q)) != 0) { if (!canputnext(q)) { putbq(q, mp); break; } #ifndef NO_DLPI proto = PPP_PROTOCOL(mp->b_rptr); mp->b_rptr += PPP_HDRLEN; hdr = allocb(sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint), BPRI_MED); if (hdr == 0) { /* XXX should put it back and use bufcall */ freemsg(mp); continue; } hdr->b_datap->db_type = M_PROTO; ud = (dl_unitdata_ind_t *) hdr->b_wptr; hdr->b_wptr += sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint); hdr->b_cont = mp; ud->dl_primitive = DL_UNITDATA_IND; ud->dl_dest_addr_length = sizeof(uint); ud->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t); ud->dl_src_addr_length = sizeof(uint); ud->dl_src_addr_offset = ud->dl_dest_addr_offset + sizeof(uint); #if DL_CURRENT_VERSION >= 2 ud->dl_group_address = 0; #endif /* Send the DLPI client the data with the SAP they requested, (e.g. ETHERTYPE_IP) rather than the PPP protocol number (e.g. PPP_IP) */ ((uint *)(ud + 1))[0] = us->req_sap; /* dest SAP */ ((uint *)(ud + 1))[1] = us->req_sap; /* src SAP */ putnext(q, hdr); #else /* NO_DLPI */ putnext(q, mp); #endif /* NO_DLPI */ } /* * Now that we have consumed some packets from this queue, * enable the control stream's read service routine so that we * can process any packets for us that might have got queued * there for flow control reasons. */ if (us->ppa) qenable(us->ppa->q); } return 0; } static upperstr_t * find_dest(ppa, proto) upperstr_t *ppa; int proto; { upperstr_t *us; for (us = ppa->next; us != 0; us = us->next) if (proto == us->sap) break; return us; } #if defined (SOL2) /* * Test upstream promiscuous conditions. As of now, only pass IPv4 and * Ipv6 packets upstream (let PPP packets be decoded elsewhere). */ static upperstr_t * find_promisc(us, proto) upperstr_t *us; int proto; { if ((proto != PPP_IP) && (proto != PPP_IPV6)) return (upperstr_t *)0; for ( ; us; us = us->next) { if ((us->flags & US_PROMISC) && (us->state == DL_IDLE)) return us; } return (upperstr_t *)0; } /* * Prepend an empty Ethernet header to msg for snoop, et al. */ static mblk_t * prepend_ether(us, mp, proto) upperstr_t *us; mblk_t *mp; int proto; { mblk_t *eh; int type; if ((eh = allocb(sizeof(struct ether_header), BPRI_HI)) == 0) { freemsg(mp); return (mblk_t *)0; } if (proto == PPP_IP) type = ETHERTYPE_IP; else if (proto == PPP_IPV6) type = ETHERTYPE_IPV6; else type = proto; /* What else? Let decoder decide */ eh->b_wptr += sizeof(struct ether_header); bzero((caddr_t)eh->b_rptr, sizeof(struct ether_header)); ((struct ether_header *)eh->b_rptr)->ether_type = htons((short)type); eh->b_cont = mp; return (eh); } /* * Prepend DL_UNITDATA_IND mblk to msg */ static mblk_t * prepend_udind(us, mp, proto) upperstr_t *us; mblk_t *mp; int proto; { dl_unitdata_ind_t *dlu; mblk_t *dh; size_t size; size = sizeof(dl_unitdata_ind_t); if ((dh = allocb(size, BPRI_MED)) == 0) { freemsg(mp); return (mblk_t *)0; } dh->b_datap->db_type = M_PROTO; dh->b_wptr = dh->b_datap->db_lim; dh->b_rptr = dh->b_wptr - size; dlu = (dl_unitdata_ind_t *)dh->b_rptr; dlu->dl_primitive = DL_UNITDATA_IND; dlu->dl_dest_addr_length = 0; dlu->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t); dlu->dl_src_addr_length = 0; dlu->dl_src_addr_offset = sizeof(dl_unitdata_ind_t); dlu->dl_group_address = 0; dh->b_cont = mp; return (dh); } /* * For any recognized promiscuous streams, send data upstream */ static void promisc_sendup(ppa, mp, proto, skip) upperstr_t *ppa; mblk_t *mp; int proto, skip; { mblk_t *dup_mp, *dup_dup_mp; upperstr_t *prus, *nprus; if ((prus = find_promisc(ppa, proto)) != 0) { if (dup_mp = dupmsg(mp)) { if (skip) dup_mp->b_rptr += PPP_HDRLEN; for ( ; nprus = find_promisc(prus->next, proto); prus = nprus) { if (dup_dup_mp = dupmsg(dup_mp)) { if (canputnext(prus->q)) { if (prus->flags & US_RAWDATA) { dup_dup_mp = prepend_ether(prus, dup_dup_mp, proto); } else { dup_dup_mp = prepend_udind(prus, dup_dup_mp, proto); } if (dup_dup_mp == 0) continue; putnext(prus->q, dup_dup_mp); } else { DPRINT("ppp_urput: data to promisc q dropped\n"); freemsg(dup_dup_mp); } } } if (canputnext(prus->q)) { if (prus->flags & US_RAWDATA) { dup_mp = prepend_ether(prus, dup_mp, proto); } else { dup_mp = prepend_udind(prus, dup_mp, proto); } if (dup_mp != 0) putnext(prus->q, dup_mp); } else { DPRINT("ppp_urput: data to promisc q dropped\n"); freemsg(dup_mp); } } } } #endif /* defined(SOL2) */ /* * We simply put the message on to the associated upper control stream * (either here or in ppplrsrv). That way we enter the perimeters * before looking through the list of attached streams to decide which * stream it should go up. */ static int ppplrput(q, mp) queue_t *q; mblk_t *mp; { queue_t *uq; struct iocblk *iop; switch (mp->b_datap->db_type) { case M_IOCTL: iop = (struct iocblk *) mp->b_rptr; iop->ioc_error = EINVAL; mp->b_datap->db_type = M_IOCNAK; qreply(q, mp); return 0; case M_FLUSH: if (*mp->b_rptr & FLUSHR) flushq(q, FLUSHDATA); if (*mp->b_rptr & FLUSHW) { *mp->b_rptr &= ~FLUSHR; qreply(q, mp); } else freemsg(mp); return 0; } /* * If we can't get the lower lock straight away, queue this one * rather than blocking, to avoid the possibility of deadlock. */ if (!TRYLOCK_LOWER_R) { if (!putq(q, mp)) freemsg(mp); return 0; } /* * Check that we're still connected to the driver. */ uq = (queue_t *) q->q_ptr; if (uq == 0) { UNLOCK_LOWER; DPRINT1("ppplrput: q = %x, uq = 0??\n", q); freemsg(mp); return 0; } /* * Try to forward the message to the put routine for the upper * control stream for this lower stream. * If there are already messages queued here, queue this one so * they don't get out of order. */ if (queclass(mp) == QPCTL || (qsize(q) == 0 && canput(uq))) put(uq, mp); else if (!putq(q, mp)) freemsg(mp); UNLOCK_LOWER; return 0; } static int ppplrsrv(q) queue_t *q; { mblk_t *mp; queue_t *uq; /* * Packets get queued here for flow control reasons * or if the lrput routine couldn't get the lower lock * without blocking. */ LOCK_LOWER_R; uq = (queue_t *) q->q_ptr; if (uq == 0) { UNLOCK_LOWER; flushq(q, FLUSHALL); DPRINT1("ppplrsrv: q = %x, uq = 0??\n", q); return 0; } while ((mp = getq(q)) != 0) { if (queclass(mp) == QPCTL || canput(uq)) put(uq, mp); else { putbq(q, mp); break; } } UNLOCK_LOWER; return 0; } static int putctl2(q, type, code, val) queue_t *q; int type, code, val; { mblk_t *mp; mp = allocb(2, BPRI_HI); if (mp == 0) return 0; mp->b_datap->db_type = type; mp->b_wptr[0] = code; mp->b_wptr[1] = val; mp->b_wptr += 2; putnext(q, mp); return 1; } static int putctl4(q, type, code, val) queue_t *q; int type, code, val; { mblk_t *mp; mp = allocb(4, BPRI_HI); if (mp == 0) return 0; mp->b_datap->db_type = type; mp->b_wptr[0] = code; ((short *)mp->b_wptr)[1] = val; mp->b_wptr += 4; putnext(q, mp); return 1; } static void debug_dump(q, mp) queue_t *q; mblk_t *mp; { upperstr_t *us; queue_t *uq, *lq; DPRINT("ppp upper streams:\n"); for (us = minor_devs; us != 0; us = us->nextmn) { uq = us->q; DPRINT3(" %d: q=%x rlev=%d", us->mn, uq, (uq? qsize(uq): 0)); DPRINT3(" wlev=%d flags=0x%b", (uq? qsize(WR(uq)): 0), us->flags, "\020\1priv\2control\3blocked\4last"); DPRINT3(" state=%x sap=%x req_sap=%x", us->state, us->sap, us->req_sap); if (us->ppa == 0) DPRINT(" ppa=?\n"); else DPRINT1(" ppa=%d\n", us->ppa->ppa_id); if (us->flags & US_CONTROL) { lq = us->lowerq; DPRINT3(" control for %d lq=%x rlev=%d", us->ppa_id, lq, (lq? qsize(RD(lq)): 0)); DPRINT3(" wlev=%d mru=%d mtu=%d\n", (lq? qsize(lq): 0), us->mru, us->mtu); } } mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } #ifdef FILTER_PACKETS #include #include #include #include #define MAX_IPHDR 128 /* max TCP/IP header size */ /* The following table contains a hard-coded list of protocol/port pairs. * Any matching packets are either discarded unconditionally, or, * if ok_if_link_up is non-zero when a connection does not currently exist * (i.e., they go through if the connection is present, but never initiate * a dial-out). * This idea came from a post by dm@garage.uun.org (David Mazieres) */ static struct pktfilt_tab { int proto; u_short port; u_short ok_if_link_up; } pktfilt_tab[] = { { IPPROTO_UDP, 520, 1 }, /* RIP, ok to pass if link is up */ { IPPROTO_UDP, 123, 1 }, /* NTP, don't keep up the link for it */ { -1, 0, 0 } /* terminator entry has port == -1 */ }; /* * Packet has already been freed if return value is 0. */ static int ip_hard_filter(us, mp, outbound) upperstr_t *us; mblk_t *mp; int outbound; { struct ip *ip; struct pktfilt_tab *pft; mblk_t *temp_mp; int proto; int len, hlen; /* Note, the PPP header has already been pulled up in all cases */ proto = PPP_PROTOCOL(mp->b_rptr); if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: filter, proto=0x%x, out=%d\n", us->mn, proto, outbound); switch (proto) { case PPP_IP: if ((mp->b_wptr - mp->b_rptr) == PPP_HDRLEN && mp->b_cont != 0) { temp_mp = mp->b_cont; len = msgdsize(temp_mp); hlen = (len < MAX_IPHDR) ? len : MAX_IPHDR; PULLUP(temp_mp, hlen); if (temp_mp == 0) { DPRINT2("ppp/%d: filter, pullup next failed, len=%d\n", us->mn, hlen); mp->b_cont = 0; /* PULLUP() freed the rest */ freemsg(mp); return 0; } ip = (struct ip *)mp->b_cont->b_rptr; } else { len = msgdsize(mp); hlen = (len < (PPP_HDRLEN+MAX_IPHDR)) ? len : (PPP_HDRLEN+MAX_IPHDR); PULLUP(mp, hlen); if (mp == 0) { DPRINT2("ppp/%d: filter, pullup failed, len=%d\n", us->mn, hlen); return 0; } ip = (struct ip *)(mp->b_rptr + PPP_HDRLEN); } /* For IP traffic, certain packets (e.g., RIP) may be either * 1. ignored - dropped completely * 2. will not initiate a connection, but * will be passed if a connection is currently up. */ for (pft=pktfilt_tab; pft->proto != -1; pft++) { if (ip->ip_p == pft->proto) { switch(pft->proto) { case IPPROTO_UDP: if (((struct udphdr *) &((int *)ip)[ip->ip_hl])->uh_dport == htons(pft->port)) goto endfor; break; case IPPROTO_TCP: if (((struct tcphdr *) &((int *)ip)[ip->ip_hl])->th_dport == htons(pft->port)) goto endfor; break; } } } endfor: if (pft->proto != -1) { if (us->flags & US_DBGLOG) DPRINT3("ppp/%d: found IP pkt, proto=0x%x (%d)\n", us->mn, pft->proto, pft->port); /* Discard if not connected, or if not pass_with_link_up */ /* else, if link is up let go by, but don't update time */ if (pft->ok_if_link_up) return -1; freemsg(mp); return 0; } break; } /* end switch (proto) */ return 1; } #endif /* FILTER_PACKETS */ ppp-2.4.5/solaris/ppp.conf000066400000000000000000000000471130035057700154450ustar00rootroot00000000000000name="ppp" parent="pseudo" instance=0; ppp-2.4.5/solaris/ppp_ahdlc.c000066400000000000000000000523721130035057700161050ustar00rootroot00000000000000/* * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC. * * Re-written by Adi Masputra , based on * the original ppp_ahdlc.c * * Copyright (c) 2000 by Sun Microsystems, Inc. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation is hereby granted, provided that the above copyright * notice appears in all copies. * * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp_ahdlc.c,v 1.5 2005/06/27 00:59:57 carlsonj Exp $ */ /* * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX. */ #include #include #include #include #include #ifdef SVR4 #include #include #include #include #else #include #ifdef __osf__ #include #endif #endif /* SVR4 */ #include #include #include "ppp_mod.h" /* * Right now, mutex is only enabled for Solaris 2.x */ #if defined(SOL2) #define USE_MUTEX #endif /* SOL2 */ #ifdef USE_MUTEX #define MUTEX_ENTER(x) mutex_enter(x) #define MUTEX_EXIT(x) mutex_exit(x) #else #define MUTEX_ENTER(x) #define MUTEX_EXIT(x) #endif /* * intpointer_t and uintpointer_t are signed and unsigned integer types * large enough to hold any data pointer; that is, data pointers can be * assigned into or from these integer types without losing precision. * On recent Solaris releases, these types are defined in sys/int_types.h, * but not on SunOS 4.x or the earlier Solaris versions. */ #if defined(_LP64) || defined(_I32LPx) typedef long intpointer_t; typedef unsigned long uintpointer_t; #else typedef int intpointer_t; typedef unsigned int uintpointer_t; #endif MOD_OPEN_DECL(ahdlc_open); MOD_CLOSE_DECL(ahdlc_close); static int ahdlc_wput __P((queue_t *, mblk_t *)); static int ahdlc_rput __P((queue_t *, mblk_t *)); static void ahdlc_encode __P((queue_t *, mblk_t *)); static void ahdlc_decode __P((queue_t *, mblk_t *)); static int msg_byte __P((mblk_t *, unsigned int)); #if defined(SOL2) /* * Don't send HDLC start flag is last transmit is within 1.5 seconds - * FLAG_TIME is defined is microseconds */ #define FLAG_TIME 1500 #define ABS(x) (x >= 0 ? x : (-x)) #endif /* SOL2 */ /* * Extract byte i of message mp */ #define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \ msg_byte((mp), (i))) /* * Is this LCP packet one we have to transmit using LCP defaults? */ #define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7) /* * Standard STREAMS declarations */ static struct module_info minfo = { 0x7d23, "ppp_ahdl", 0, INFPSZ, 32768, 512 }; static struct qinit rinit = { ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL }; static struct qinit winit = { ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL }; #if defined(SVR4) && !defined(SOL2) int phdldevflag = 0; #define ppp_ahdlcinfo phdlinfo #endif /* defined(SVR4) && !defined(SOL2) */ struct streamtab ppp_ahdlcinfo = { &rinit, /* ptr to st_rdinit */ &winit, /* ptr to st_wrinit */ NULL, /* ptr to st_muxrinit */ NULL, /* ptr to st_muxwinit */ #if defined(SUNOS4) NULL /* ptr to ptr to st_modlist */ #endif /* SUNOS4 */ }; #if defined(SUNOS4) int ppp_ahdlc_count = 0; /* open counter */ #endif /* SUNOS4 */ /* * Per-stream state structure */ typedef struct ahdlc_state { #if defined(USE_MUTEX) kmutex_t lock; /* lock for this structure */ #endif /* USE_MUTEX */ int flags; /* link flags */ mblk_t *rx_buf; /* ptr to receive buffer */ int rx_buf_size; /* receive buffer size */ ushort_t infcs; /* calculated rx HDLC FCS */ u_int32_t xaccm[8]; /* 256-bit xmit ACCM */ u_int32_t raccm; /* 32-bit rcv ACCM */ int mtu; /* interface MTU */ int mru; /* link MRU */ int unit; /* current PPP unit number */ struct pppstat stats; /* statistic structure */ #if defined(SOL2) clock_t flag_time; /* time in usec between flags */ clock_t lbolt; /* last updated lbolt */ #endif /* SOL2 */ } ahdlc_state_t; /* * Values for flags */ #define ESCAPED 0x100 /* last saw escape char on input */ #define IFLUSH 0x200 /* flushing input due to error */ /* * RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. */ #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP) /* * FCS lookup table as calculated by genfcstab. */ static u_short fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; static u_int32_t paritytab[8] = { 0x96696996, 0x69969669, 0x69969669, 0x96696996, 0x69969669, 0x96696996, 0x96696996, 0x69969669 }; /* * STREAMS module open (entry) point */ MOD_OPEN(ahdlc_open) { ahdlc_state_t *state; mblk_t *mp; /* * Return if it's already opened */ if (q->q_ptr) { return 0; } /* * This can only be opened as a module */ if (sflag != MODOPEN) { OPEN_ERROR(EINVAL); } state = (ahdlc_state_t *) ALLOC_NOSLEEP(sizeof(ahdlc_state_t)); if (state == 0) OPEN_ERROR(ENOSR); bzero((caddr_t) state, sizeof(ahdlc_state_t)); q->q_ptr = (caddr_t) state; WR(q)->q_ptr = (caddr_t) state; #if defined(USE_MUTEX) mutex_init(&state->lock, NULL, MUTEX_DEFAULT, NULL); #endif /* USE_MUTEX */ state->xaccm[0] = ~0; /* escape 0x00 through 0x1f */ state->xaccm[3] = 0x60000000; /* escape 0x7d and 0x7e */ state->mru = PPP_MRU; /* default of 1500 bytes */ #if defined(SOL2) state->flag_time = drv_usectohz(FLAG_TIME); #endif /* SOL2 */ #if defined(SUNOS4) ppp_ahdlc_count++; #endif /* SUNOS4 */ qprocson(q); if ((mp = allocb(1, BPRI_HI)) != NULL) { mp->b_datap->db_type = M_FLUSH; *mp->b_wptr++ = FLUSHR; putnext(q, mp); } return 0; } /* * STREAMS module close (exit) point */ MOD_CLOSE(ahdlc_close) { ahdlc_state_t *state; qprocsoff(q); state = (ahdlc_state_t *) q->q_ptr; if (state == 0) { DPRINT("state == 0 in ahdlc_close\n"); return 0; } if (state->rx_buf != 0) { freemsg(state->rx_buf); state->rx_buf = 0; } #if defined(USE_MUTEX) mutex_destroy(&state->lock); #endif /* USE_MUTEX */ FREE(q->q_ptr, sizeof(ahdlc_state_t)); q->q_ptr = NULL; OTHERQ(q)->q_ptr = NULL; #if defined(SUNOS4) if (ppp_ahdlc_count) ppp_ahdlc_count--; #endif /* SUNOS4 */ return 0; } /* * Write side put routine */ static int ahdlc_wput(q, mp) queue_t *q; mblk_t *mp; { ahdlc_state_t *state; struct iocblk *iop; int error; mblk_t *np; struct ppp_stats *psp; state = (ahdlc_state_t *) q->q_ptr; if (state == 0) { DPRINT("state == 0 in ahdlc_wput\n"); freemsg(mp); return 0; } switch (mp->b_datap->db_type) { case M_DATA: /* * A data packet - do character-stuffing and FCS, and * send it onwards. */ ahdlc_encode(q, mp); freemsg(mp); break; case M_IOCTL: iop = (struct iocblk *) mp->b_rptr; error = EINVAL; switch (iop->ioc_cmd) { case PPPIO_XACCM: if ((iop->ioc_count < sizeof(u_int32_t)) || (iop->ioc_count > sizeof(ext_accm))) { break; } if (mp->b_cont == 0) { DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit); break; } MUTEX_ENTER(&state->lock); bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm, iop->ioc_count); state->xaccm[2] &= ~0x40000000; /* don't escape 0x5e */ state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */ MUTEX_EXIT(&state->lock); iop->ioc_count = 0; error = 0; break; case PPPIO_RACCM: if (iop->ioc_count != sizeof(u_int32_t)) break; if (mp->b_cont == 0) { DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit); break; } MUTEX_ENTER(&state->lock); bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm, sizeof(u_int32_t)); MUTEX_EXIT(&state->lock); iop->ioc_count = 0; error = 0; break; case PPPIO_GCLEAN: np = allocb(sizeof(int), BPRI_HI); if (np == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = np; MUTEX_ENTER(&state->lock); *(int *)np->b_wptr = state->flags & RCV_FLAGS; MUTEX_EXIT(&state->lock); np->b_wptr += sizeof(int); iop->ioc_count = sizeof(int); error = 0; break; case PPPIO_GETSTAT: np = allocb(sizeof(struct ppp_stats), BPRI_HI); if (np == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = np; psp = (struct ppp_stats *) np->b_wptr; np->b_wptr += sizeof(struct ppp_stats); bzero((caddr_t)psp, sizeof(struct ppp_stats)); psp->p = state->stats; iop->ioc_count = sizeof(struct ppp_stats); error = 0; break; case PPPIO_LASTMOD: /* we knew this anyway */ error = 0; break; default: error = -1; break; } if (error < 0) putnext(q, mp); else if (error == 0) { mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } else { mp->b_datap->db_type = M_IOCNAK; iop->ioc_count = 0; iop->ioc_error = error; qreply(q, mp); } break; case M_CTL: switch (*mp->b_rptr) { case PPPCTL_MTU: MUTEX_ENTER(&state->lock); state->mtu = ((unsigned short *)mp->b_rptr)[1]; MUTEX_EXIT(&state->lock); break; case PPPCTL_MRU: MUTEX_ENTER(&state->lock); state->mru = ((unsigned short *)mp->b_rptr)[1]; MUTEX_EXIT(&state->lock); break; case PPPCTL_UNIT: MUTEX_ENTER(&state->lock); state->unit = mp->b_rptr[1]; MUTEX_EXIT(&state->lock); break; default: putnext(q, mp); return 0; } freemsg(mp); break; default: putnext(q, mp); } return 0; } /* * Read side put routine */ static int ahdlc_rput(q, mp) queue_t *q; mblk_t *mp; { ahdlc_state_t *state; state = (ahdlc_state_t *) q->q_ptr; if (state == 0) { DPRINT("state == 0 in ahdlc_rput\n"); freemsg(mp); return 0; } switch (mp->b_datap->db_type) { case M_DATA: ahdlc_decode(q, mp); break; case M_HANGUP: MUTEX_ENTER(&state->lock); if (state->rx_buf != 0) { /* XXX would like to send this up for debugging */ freemsg(state->rx_buf); state->rx_buf = 0; } state->flags = IFLUSH; MUTEX_EXIT(&state->lock); putnext(q, mp); break; default: putnext(q, mp); } return 0; } /* * Extract bit c from map m, to determine if c needs to be escaped */ #define IN_TX_MAP(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f))) static void ahdlc_encode(q, mp) queue_t *q; mblk_t *mp; { ahdlc_state_t *state; u_int32_t *xaccm, loc_xaccm[8]; ushort_t fcs; size_t outmp_len; mblk_t *outmp, *tmp; uchar_t *dp, fcs_val; int is_lcp, code; #if defined(SOL2) clock_t lbolt; #endif /* SOL2 */ if (msgdsize(mp) < 4) { return; } state = (ahdlc_state_t *)q->q_ptr; MUTEX_ENTER(&state->lock); /* * Allocate an output buffer large enough to handle a case where all * characters need to be escaped */ outmp_len = (msgdsize(mp) << 1) + /* input block x 2 */ (sizeof(fcs) << 2) + /* HDLC FCS x 4 */ (sizeof(uchar_t) << 1); /* HDLC flags x 2 */ outmp = allocb(outmp_len, BPRI_MED); if (outmp == NULL) { state->stats.ppp_oerrors++; MUTEX_EXIT(&state->lock); putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR); return; } #if defined(SOL2) /* * Check if our last transmit happenned within flag_time, using * the system's LBOLT value in clock ticks */ if (drv_getparm(LBOLT, &lbolt) != -1) { if (ABS((clock_t)lbolt - state->lbolt) > state->flag_time) { *outmp->b_wptr++ = PPP_FLAG; } state->lbolt = lbolt; } else { *outmp->b_wptr++ = PPP_FLAG; } #else /* * If the driver below still has a message to process, skip the * HDLC flag, otherwise, put one in the beginning */ if (qsize(q->q_next) == 0) { *outmp->b_wptr++ = PPP_FLAG; } #endif /* * All control characters must be escaped for LCP packets with code * values between 1 (Conf-Req) and 7 (Code-Rej). */ is_lcp = ((MSG_BYTE(mp, 0) == PPP_ALLSTATIONS) && (MSG_BYTE(mp, 1) == PPP_UI) && (MSG_BYTE(mp, 2) == (PPP_LCP >> 8)) && (MSG_BYTE(mp, 3) == (PPP_LCP & 0xff)) && LCP_USE_DFLT(mp)); xaccm = state->xaccm; if (is_lcp) { bcopy((caddr_t)state->xaccm, (caddr_t)loc_xaccm, sizeof(loc_xaccm)); loc_xaccm[0] = ~0; /* force escape on 0x00 through 0x1f */ xaccm = loc_xaccm; } fcs = PPP_INITFCS; /* Initial FCS is 0xffff */ /* * Process this block and the rest (if any) attached to the this one */ for (tmp = mp; tmp; tmp = tmp->b_cont) { if (tmp->b_datap->db_type == M_DATA) { for (dp = tmp->b_rptr; dp < tmp->b_wptr; dp++) { fcs = PPP_FCS(fcs, *dp); if (IN_TX_MAP(*dp, xaccm)) { *outmp->b_wptr++ = PPP_ESCAPE; *outmp->b_wptr++ = *dp ^ PPP_TRANS; } else { *outmp->b_wptr++ = *dp; } } } else { continue; /* skip if db_type is something other than M_DATA */ } } /* * Append the HDLC FCS, making sure that escaping is done on any * necessary bytes */ fcs_val = (fcs ^ 0xffff) & 0xff; if (IN_TX_MAP(fcs_val, xaccm)) { *outmp->b_wptr++ = PPP_ESCAPE; *outmp->b_wptr++ = fcs_val ^ PPP_TRANS; } else { *outmp->b_wptr++ = fcs_val; } fcs_val = ((fcs ^ 0xffff) >> 8) & 0xff; if (IN_TX_MAP(fcs_val, xaccm)) { *outmp->b_wptr++ = PPP_ESCAPE; *outmp->b_wptr++ = fcs_val ^ PPP_TRANS; } else { *outmp->b_wptr++ = fcs_val; } /* * And finally, append the HDLC flag, and send it away */ *outmp->b_wptr++ = PPP_FLAG; state->stats.ppp_obytes += msgdsize(outmp); state->stats.ppp_opackets++; MUTEX_EXIT(&state->lock); putnext(q, outmp); } /* * Checks the 32-bit receive ACCM to see if the byte needs un-escaping */ #define IN_RX_MAP(c, m) ((((unsigned int) (uchar_t) (c)) < 0x20) && \ (m) & (1 << (c))) /* * Process received characters. */ static void ahdlc_decode(q, mp) queue_t *q; mblk_t *mp; { ahdlc_state_t *state; mblk_t *om; uchar_t *dp; state = (ahdlc_state_t *) q->q_ptr; MUTEX_ENTER(&state->lock); state->stats.ppp_ibytes += msgdsize(mp); for (; mp != 0; om = mp->b_cont, freeb(mp), mp = om) for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) { /* * This should detect the lack of 8-bit communication channel * which is necessary for PPP to work. In addition, it also * checks on the parity. */ if (*dp & 0x80) state->flags |= RCV_B7_1; else state->flags |= RCV_B7_0; if (paritytab[*dp >> 5] & (1 << (*dp & 0x1f))) state->flags |= RCV_ODDP; else state->flags |= RCV_EVNP; /* * So we have a HDLC flag ... */ if (*dp == PPP_FLAG) { /* * If we think that it marks the beginning of the frame, * then continue to process the next octects */ if ((state->flags & IFLUSH) || (state->rx_buf == 0) || (msgdsize(state->rx_buf) == 0)) { state->flags &= ~IFLUSH; continue; } /* * We get here because the above condition isn't true, * in which case the HDLC flag was there to mark the end * of the frame (or so we think) */ om = state->rx_buf; if (state->infcs == PPP_GOODFCS) { state->stats.ppp_ipackets++; adjmsg(om, -PPP_FCSLEN); putnext(q, om); } else { DPRINT2("ppp%d: bad fcs (len=%d)\n", state->unit, msgdsize(state->rx_buf)); freemsg(state->rx_buf); state->flags &= ~(IFLUSH | ESCAPED); state->stats.ppp_ierrors++; putctl1(q->q_next, M_CTL, PPPCTL_IERROR); } state->rx_buf = 0; continue; } if (state->flags & IFLUSH) { continue; } /* * Allocate a receive buffer, large enough to store a frame (after * un-escaping) of at least 1500 octets. If MRU is negotiated to * be more than the default, then allocate that much. In addition, * we add an extra 32-bytes for a fudge factor */ if (state->rx_buf == 0) { state->rx_buf_size = (state->mru < PPP_MRU ? PPP_MRU : state->mru); state->rx_buf_size += (sizeof(u_int32_t) << 3); state->rx_buf = allocb(state->rx_buf_size, BPRI_MED); /* * If allocation fails, try again on the next frame */ if (state->rx_buf == 0) { state->flags |= IFLUSH; continue; } state->flags &= ~(IFLUSH | ESCAPED); state->infcs = PPP_INITFCS; } if (*dp == PPP_ESCAPE) { state->flags |= ESCAPED; continue; } /* * Make sure we un-escape the necessary characters, as well as the * ones in our receive async control character map */ if (state->flags & ESCAPED) { *dp ^= PPP_TRANS; state->flags &= ~ESCAPED; } else if (IN_RX_MAP(*dp, state->raccm)) continue; /* * Unless the peer lied to us about the negotiated MRU, we should * never get a frame which is too long. If it happens, toss it away * and grab the next incoming one */ if (msgdsize(state->rx_buf) < state->rx_buf_size) { state->infcs = PPP_FCS(state->infcs, *dp); *state->rx_buf->b_wptr++ = *dp; } else { DPRINT2("ppp%d: frame too long (%d)\n", state->unit, msgdsize(state->rx_buf)); freemsg(state->rx_buf); state->rx_buf = 0; state->flags |= IFLUSH; } } MUTEX_EXIT(&state->lock); } static int msg_byte(mp, i) mblk_t *mp; unsigned int i; { while (mp != 0 && i >= mp->b_wptr - mp->b_rptr) mp = mp->b_cont; if (mp == 0) return -1; return mp->b_rptr[i]; } ppp-2.4.5/solaris/ppp_ahdlc_mod.c000066400000000000000000000013251130035057700167340ustar00rootroot00000000000000#include #include #include #include #include extern struct streamtab ppp_ahdlcinfo; static struct fmodsw fsw = { "ppp_ahdl", &ppp_ahdlcinfo, D_NEW | D_MP | D_MTQPAIR }; extern struct mod_ops mod_strmodops; static struct modlstrmod modlstrmod = { &mod_strmodops, "PPP async HDLC module", &fsw }; static struct modlinkage modlinkage = { MODREV_1, (void *) &modlstrmod, NULL }; /* * Entry points for modloading. */ int _init(void) { return mod_install(&modlinkage); } int _fini(void) { return mod_remove(&modlinkage); } int _info(mip) struct modinfo *mip; { return mod_info(&modlinkage, mip); } ppp-2.4.5/solaris/ppp_comp.c000066400000000000000000000671161130035057700157720ustar00rootroot00000000000000/* * ppp_comp.c - STREAMS module for kernel-level compression and CCP support. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp_comp.c,v 1.3 2004/01/17 05:47:55 carlsonj Exp $ */ /* * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX. */ #include #include #include #include #ifdef SVR4 #include #include #include #else #include #ifdef __osf__ #include #endif #endif /* SVR4 */ #include #include #include "ppp_mod.h" #ifdef __osf__ #include #include #endif #include #include #include #include #define PACKETPTR mblk_t * #include MOD_OPEN_DECL(ppp_comp_open); MOD_CLOSE_DECL(ppp_comp_close); static int ppp_comp_rput __P((queue_t *, mblk_t *)); static int ppp_comp_rsrv __P((queue_t *)); static int ppp_comp_wput __P((queue_t *, mblk_t *)); static int ppp_comp_wsrv __P((queue_t *)); static void ppp_comp_ccp __P((queue_t *, mblk_t *, int)); static int msg_byte __P((mblk_t *, unsigned int)); /* Extract byte i of message mp. */ #define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \ msg_byte((mp), (i))) /* Is this LCP packet one we have to transmit using LCP defaults? */ #define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7) #define PPP_COMP_ID 0xbadf static struct module_info minfo = { #ifdef PRIOQ PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16512, 16384, #else PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16384, 4096, #endif }; static struct qinit r_init = { ppp_comp_rput, ppp_comp_rsrv, ppp_comp_open, ppp_comp_close, NULL, &minfo, NULL }; static struct qinit w_init = { ppp_comp_wput, ppp_comp_wsrv, NULL, NULL, NULL, &minfo, NULL }; #if defined(SVR4) && !defined(SOL2) int pcmpdevflag = 0; #define ppp_compinfo pcmpinfo #endif struct streamtab ppp_compinfo = { &r_init, &w_init, NULL, NULL }; int ppp_comp_count; /* number of module instances in use */ #ifdef __osf__ static void ppp_comp_alloc __P((comp_state_t *)); typedef struct memreq { unsigned char comp_opts[20]; int cmd; int thread_status; char *returned_mem; } memreq_t; #endif typedef struct comp_state { int flags; int mru; int mtu; int unit; struct compressor *xcomp; void *xstate; struct compressor *rcomp; void *rstate; struct vjcompress vj_comp; int vj_last_ierrors; struct pppstat stats; #ifdef __osf__ memreq_t memreq; thread_t thread; #endif } comp_state_t; #ifdef __osf__ extern task_t first_task; #endif /* Bits in flags are as defined in pppio.h. */ #define CCP_ERR (CCP_ERROR | CCP_FATALERROR) #define LAST_MOD 0x1000000 /* no ppp modules below us */ #define DBGLOG 0x2000000 /* log debugging stuff */ #define MAX_IPHDR 128 /* max TCP/IP header size */ #define MAX_VJHDR 20 /* max VJ compressed header size (?) */ #undef MIN /* just in case */ #define MIN(a, b) ((a) < (b)? (a): (b)) /* * List of compressors we know about. */ #if DO_BSD_COMPRESS extern struct compressor ppp_bsd_compress; #endif #if DO_DEFLATE extern struct compressor ppp_deflate, ppp_deflate_draft; #endif struct compressor *ppp_compressors[] = { #if DO_BSD_COMPRESS &ppp_bsd_compress, #endif #if DO_DEFLATE &ppp_deflate, &ppp_deflate_draft, #endif NULL }; /* * STREAMS module entry points. */ MOD_OPEN(ppp_comp_open) { comp_state_t *cp; #ifdef __osf__ thread_t thread; #endif if (q->q_ptr == NULL) { cp = (comp_state_t *) ALLOC_SLEEP(sizeof(comp_state_t)); if (cp == NULL) OPEN_ERROR(ENOSR); bzero((caddr_t)cp, sizeof(comp_state_t)); WR(q)->q_ptr = q->q_ptr = (caddr_t) cp; cp->mru = PPP_MRU; cp->mtu = PPP_MTU; cp->xstate = NULL; cp->rstate = NULL; vj_compress_init(&cp->vj_comp, -1); #ifdef __osf__ if (!(thread = kernel_thread_w_arg(first_task, ppp_comp_alloc, (void *)cp))) OPEN_ERROR(ENOSR); cp->thread = thread; #endif ++ppp_comp_count; qprocson(q); } return 0; } MOD_CLOSE(ppp_comp_close) { comp_state_t *cp; qprocsoff(q); cp = (comp_state_t *) q->q_ptr; if (cp != NULL) { if (cp->xstate != NULL) (*cp->xcomp->comp_free)(cp->xstate); if (cp->rstate != NULL) (*cp->rcomp->decomp_free)(cp->rstate); #ifdef __osf__ if (!cp->thread) printf("ppp_comp_close: NULL thread!\n"); else thread_terminate(cp->thread); #endif FREE(cp, sizeof(comp_state_t)); q->q_ptr = NULL; OTHERQ(q)->q_ptr = NULL; --ppp_comp_count; } return 0; } #ifdef __osf__ /* thread for calling back to a compressor's memory allocator * Needed for Digital UNIX since it's VM can't handle requests * for large amounts of memory without blocking. The thread * provides a context in which we can call a memory allocator * that may block. */ static void ppp_comp_alloc(comp_state_t *cp) { int len, cmd; unsigned char *compressor_options; thread_t thread; void *(*comp_allocator)(); #if defined(MAJOR_VERSION) && (MAJOR_VERSION <= 2) /* In 2.x and earlier the argument gets passed * in the thread structure itself. Yuck. */ thread = current_thread(); cp = thread->reply_port; thread->reply_port = PORT_NULL; #endif for (;;) { assert_wait((vm_offset_t)&cp->memreq.thread_status, TRUE); thread_block(); if (thread_should_halt(current_thread())) thread_halt_self(); cmd = cp->memreq.cmd; compressor_options = &cp->memreq.comp_opts[0]; len = compressor_options[1]; if (cmd == PPPIO_XCOMP) { cp->memreq.returned_mem = cp->xcomp->comp_alloc(compressor_options, len); if (!cp->memreq.returned_mem) { cp->memreq.thread_status = ENOSR; } else { cp->memreq.thread_status = 0; } } else { cp->memreq.returned_mem = cp->rcomp->decomp_alloc(compressor_options, len); if (!cp->memreq.returned_mem) { cp->memreq.thread_status = ENOSR; } else { cp->memreq.thread_status = 0; } } } } #endif /* __osf__ */ /* here's the deal with memory allocation under Digital UNIX. * Some other may also benefit from this... * We can't ask for huge chunks of memory in a context where * the caller can't be put to sleep (like, here.) The alloc * is likely to fail. Instead we do this: the first time we * get called, kick off a thread to do the allocation. Return * immediately to the caller with EAGAIN, as an indication that * they should send down the ioctl again. By the time the * second call comes in it's likely that the memory allocation * thread will have returned with the requested memory. We will * continue to return EAGAIN however until the thread has completed. * When it has, we return zero (and the memory) if the allocator * was successful and ENOSR otherwise. * * Callers of the RCOMP and XCOMP ioctls are encouraged (but not * required) to loop for some number of iterations with a small * delay in the loop body (for instance a 1/10-th second "sleep" * via select.) */ static int ppp_comp_wput(q, mp) queue_t *q; mblk_t *mp; { struct iocblk *iop; comp_state_t *cp; int error, len, n; int flags, mask; mblk_t *np; struct compressor **comp; struct ppp_stats *psp; struct ppp_comp_stats *csp; unsigned char *opt_data; int nxslots, nrslots; cp = (comp_state_t *) q->q_ptr; if (cp == 0) { DPRINT("cp == 0 in ppp_comp_wput\n"); freemsg(mp); return 0; } switch (mp->b_datap->db_type) { case M_DATA: putq(q, mp); break; case M_IOCTL: iop = (struct iocblk *) mp->b_rptr; error = EINVAL; switch (iop->ioc_cmd) { case PPPIO_CFLAGS: /* set/get CCP state */ if (iop->ioc_count != 2 * sizeof(int)) break; if (mp->b_cont == 0) { DPRINT1("ppp_comp_wput/%d: PPPIO_CFLAGS b_cont = 0!\n", cp->unit); break; } flags = ((int *) mp->b_cont->b_rptr)[0]; mask = ((int *) mp->b_cont->b_rptr)[1]; cp->flags = (cp->flags & ~mask) | (flags & mask); if ((mask & CCP_ISOPEN) && (flags & CCP_ISOPEN) == 0) { if (cp->xstate != NULL) { (*cp->xcomp->comp_free)(cp->xstate); cp->xstate = NULL; } if (cp->rstate != NULL) { (*cp->rcomp->decomp_free)(cp->rstate); cp->rstate = NULL; } cp->flags &= ~CCP_ISUP; } error = 0; iop->ioc_count = sizeof(int); ((int *) mp->b_cont->b_rptr)[0] = cp->flags; mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int); break; case PPPIO_VJINIT: /* * Initialize VJ compressor/decompressor */ if (iop->ioc_count != 2) break; if (mp->b_cont == 0) { DPRINT1("ppp_comp_wput/%d: PPPIO_VJINIT b_cont = 0!\n", cp->unit); break; } nxslots = mp->b_cont->b_rptr[0] + 1; nrslots = mp->b_cont->b_rptr[1] + 1; if (nxslots > MAX_STATES || nrslots > MAX_STATES) break; vj_compress_init(&cp->vj_comp, nxslots); cp->vj_last_ierrors = cp->stats.ppp_ierrors; error = 0; iop->ioc_count = 0; break; case PPPIO_XCOMP: case PPPIO_RCOMP: if (iop->ioc_count <= 0) break; if (mp->b_cont == 0) { DPRINT1("ppp_comp_wput/%d: PPPIO_[XR]COMP b_cont = 0!\n", cp->unit); break; } opt_data = mp->b_cont->b_rptr; len = mp->b_cont->b_wptr - opt_data; if (len > iop->ioc_count) len = iop->ioc_count; if (opt_data[1] < 2 || opt_data[1] > len) break; for (comp = ppp_compressors; *comp != NULL; ++comp) if ((*comp)->compress_proto == opt_data[0]) { /* here's the handler! */ error = 0; #ifndef __osf__ if (iop->ioc_cmd == PPPIO_XCOMP) { /* A previous call may have fetched memory for a compressor * that's now being retired or reset. Free it using it's * mechanism for freeing stuff. */ if (cp->xstate != NULL) { (*cp->xcomp->comp_free)(cp->xstate); cp->xstate = NULL; } cp->xcomp = *comp; cp->xstate = (*comp)->comp_alloc(opt_data, len); if (cp->xstate == NULL) error = ENOSR; } else { if (cp->rstate != NULL) { (*cp->rcomp->decomp_free)(cp->rstate); cp->rstate = NULL; } cp->rcomp = *comp; cp->rstate = (*comp)->decomp_alloc(opt_data, len); if (cp->rstate == NULL) error = ENOSR; } #else if ((error = cp->memreq.thread_status) != EAGAIN) if (iop->ioc_cmd == PPPIO_XCOMP) { if (cp->xstate) { (*cp->xcomp->comp_free)(cp->xstate); cp->xstate = 0; } /* sanity check for compressor options */ if (sizeof (cp->memreq.comp_opts) < len) { printf("can't handle options for compressor %d (%d)\n", opt_data[0], opt_data[1]); cp->memreq.thread_status = ENOSR; cp->memreq.returned_mem = 0; } /* fill in request for the thread and kick it off */ if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) { bcopy(opt_data, cp->memreq.comp_opts, len); cp->memreq.cmd = PPPIO_XCOMP; cp->xcomp = *comp; error = cp->memreq.thread_status = EAGAIN; thread_wakeup((vm_offset_t)&cp->memreq.thread_status); } else { cp->xstate = cp->memreq.returned_mem; cp->memreq.returned_mem = 0; cp->memreq.thread_status = 0; } } else { if (cp->rstate) { (*cp->rcomp->decomp_free)(cp->rstate); cp->rstate = NULL; } if (sizeof (cp->memreq.comp_opts) < len) { printf("can't handle options for compressor %d (%d)\n", opt_data[0], opt_data[1]); cp->memreq.thread_status = ENOSR; cp->memreq.returned_mem = 0; } if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) { bcopy(opt_data, cp->memreq.comp_opts, len); cp->memreq.cmd = PPPIO_RCOMP; cp->rcomp = *comp; error = cp->memreq.thread_status = EAGAIN; thread_wakeup((vm_offset_t)&cp->memreq.thread_status); } else { cp->rstate = cp->memreq.returned_mem; cp->memreq.returned_mem = 0; cp->memreq.thread_status = 0; } } #endif break; } iop->ioc_count = 0; break; case PPPIO_GETSTAT: if ((cp->flags & LAST_MOD) == 0) { error = -1; /* let the ppp_ahdl module handle it */ break; } np = allocb(sizeof(struct ppp_stats), BPRI_HI); if (np == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = np; psp = (struct ppp_stats *) np->b_wptr; np->b_wptr += sizeof(struct ppp_stats); iop->ioc_count = sizeof(struct ppp_stats); psp->p = cp->stats; psp->vj = cp->vj_comp.stats; error = 0; break; case PPPIO_GETCSTAT: np = allocb(sizeof(struct ppp_comp_stats), BPRI_HI); if (np == 0) { error = ENOSR; break; } if (mp->b_cont != 0) freemsg(mp->b_cont); mp->b_cont = np; csp = (struct ppp_comp_stats *) np->b_wptr; np->b_wptr += sizeof(struct ppp_comp_stats); iop->ioc_count = sizeof(struct ppp_comp_stats); bzero((caddr_t)csp, sizeof(struct ppp_comp_stats)); if (cp->xstate != 0) (*cp->xcomp->comp_stat)(cp->xstate, &csp->c); if (cp->rstate != 0) (*cp->rcomp->decomp_stat)(cp->rstate, &csp->d); error = 0; break; case PPPIO_DEBUG: if (iop->ioc_count != sizeof(int)) break; if (mp->b_cont == 0) { DPRINT1("ppp_comp_wput/%d: PPPIO_DEBUG b_cont = 0!\n", cp->unit); break; } n = *(int *)mp->b_cont->b_rptr; if (n == PPPDBG_LOG + PPPDBG_COMP) { DPRINT1("ppp_comp%d: debug log enabled\n", cp->unit); cp->flags |= DBGLOG; error = 0; iop->ioc_count = 0; } else { error = -1; } break; case PPPIO_LASTMOD: cp->flags |= LAST_MOD; error = 0; break; default: error = -1; break; } if (error < 0) putnext(q, mp); else if (error == 0) { mp->b_datap->db_type = M_IOCACK; qreply(q, mp); } else { mp->b_datap->db_type = M_IOCNAK; iop->ioc_error = error; iop->ioc_count = 0; qreply(q, mp); } break; case M_CTL: switch (*mp->b_rptr) { case PPPCTL_MTU: cp->mtu = ((unsigned short *)mp->b_rptr)[1]; break; case PPPCTL_MRU: cp->mru = ((unsigned short *)mp->b_rptr)[1]; break; case PPPCTL_UNIT: cp->unit = mp->b_rptr[1]; break; } putnext(q, mp); break; default: putnext(q, mp); } return 0; } static int ppp_comp_wsrv(q) queue_t *q; { mblk_t *mp, *cmp = NULL; comp_state_t *cp; int len, proto, type, hlen, code; struct ip *ip; unsigned char *vjhdr, *dp; cp = (comp_state_t *) q->q_ptr; if (cp == 0) { DPRINT("cp == 0 in ppp_comp_wsrv\n"); return 0; } while ((mp = getq(q)) != 0) { /* assert(mp->b_datap->db_type == M_DATA) */ #ifdef PRIOQ if (!bcanputnext(q,mp->b_band)) #else if (!canputnext(q)) #endif /* PRIOQ */ { putbq(q, mp); break; } /* * First check the packet length and work out what the protocol is. */ len = msgdsize(mp); if (len < PPP_HDRLEN) { DPRINT1("ppp_comp_wsrv: bogus short packet (%d)\n", len); freemsg(mp); cp->stats.ppp_oerrors++; putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR); continue; } proto = (MSG_BYTE(mp, 2) << 8) + MSG_BYTE(mp, 3); /* * Make sure we've got enough data in the first mblk * and that we are its only user. */ if (proto == PPP_CCP) hlen = len; else if (proto == PPP_IP) hlen = PPP_HDRLEN + MAX_IPHDR; else hlen = PPP_HDRLEN; if (hlen > len) hlen = len; if (mp->b_wptr < mp->b_rptr + hlen || mp->b_datap->db_ref > 1) { PULLUP(mp, hlen); if (mp == 0) { DPRINT1("ppp_comp_wsrv: pullup failed (%d)\n", hlen); cp->stats.ppp_oerrors++; putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR); continue; } } /* * Do VJ compression if requested. */ if (proto == PPP_IP && (cp->flags & COMP_VJC)) { ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN); if (ip->ip_p == IPPROTO_TCP) { type = vj_compress_tcp(ip, len - PPP_HDRLEN, &cp->vj_comp, (cp->flags & COMP_VJCCID), &vjhdr); switch (type) { case TYPE_UNCOMPRESSED_TCP: mp->b_rptr[3] = proto = PPP_VJC_UNCOMP; break; case TYPE_COMPRESSED_TCP: dp = vjhdr - PPP_HDRLEN; dp[1] = mp->b_rptr[1]; /* copy control field */ dp[0] = mp->b_rptr[0]; /* copy address field */ dp[2] = 0; /* set protocol field */ dp[3] = proto = PPP_VJC_COMP; mp->b_rptr = dp; break; } } } /* * Do packet compression if enabled. */ if (proto == PPP_CCP) ppp_comp_ccp(q, mp, 0); else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN) && cp->xstate != NULL) { len = msgdsize(mp); (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len, (cp->flags & CCP_ISUP? cp->mtu + PPP_HDRLEN: 0)); if (cmp != NULL) { #ifdef PRIOQ cmp->b_band=mp->b_band; #endif /* PRIOQ */ freemsg(mp); mp = cmp; } } /* * Do address/control and protocol compression if enabled. */ if ((cp->flags & COMP_AC) && !(proto == PPP_LCP && LCP_USE_DFLT(mp))) { mp->b_rptr += 2; /* drop the address & ctrl fields */ if (proto < 0x100 && (cp->flags & COMP_PROT)) ++mp->b_rptr; /* drop the high protocol byte */ } else if (proto < 0x100 && (cp->flags & COMP_PROT)) { /* shuffle up the address & ctrl fields */ mp->b_rptr[2] = mp->b_rptr[1]; mp->b_rptr[1] = mp->b_rptr[0]; ++mp->b_rptr; } cp->stats.ppp_opackets++; cp->stats.ppp_obytes += msgdsize(mp); putnext(q, mp); } return 0; } static int ppp_comp_rput(q, mp) queue_t *q; mblk_t *mp; { comp_state_t *cp; struct iocblk *iop; struct ppp_stats *psp; cp = (comp_state_t *) q->q_ptr; if (cp == 0) { DPRINT("cp == 0 in ppp_comp_rput\n"); freemsg(mp); return 0; } switch (mp->b_datap->db_type) { case M_DATA: putq(q, mp); break; case M_IOCACK: iop = (struct iocblk *) mp->b_rptr; switch (iop->ioc_cmd) { case PPPIO_GETSTAT: /* * Catch this on the way back from the ppp_ahdl module * so we can fill in the VJ stats. */ if (mp->b_cont == 0 || iop->ioc_count != sizeof(struct ppp_stats)) break; psp = (struct ppp_stats *) mp->b_cont->b_rptr; psp->vj = cp->vj_comp.stats; break; } putnext(q, mp); break; case M_CTL: switch (mp->b_rptr[0]) { case PPPCTL_IERROR: ++cp->stats.ppp_ierrors; break; case PPPCTL_OERROR: ++cp->stats.ppp_oerrors; break; } putnext(q, mp); break; default: putnext(q, mp); } return 0; } static int ppp_comp_rsrv(q) queue_t *q; { int proto, rv, i; mblk_t *mp, *dmp = NULL, *np; uchar_t *dp, *iphdr; comp_state_t *cp; int len, hlen, vjlen; u_int iphlen; cp = (comp_state_t *) q->q_ptr; if (cp == 0) { DPRINT("cp == 0 in ppp_comp_rsrv\n"); return 0; } while ((mp = getq(q)) != 0) { /* assert(mp->b_datap->db_type == M_DATA) */ if (!canputnext(q)) { putbq(q, mp); break; } len = msgdsize(mp); cp->stats.ppp_ibytes += len; cp->stats.ppp_ipackets++; /* * First work out the protocol and where the PPP header ends. */ i = 0; proto = MSG_BYTE(mp, 0); if (proto == PPP_ALLSTATIONS) { i = 2; proto = MSG_BYTE(mp, 2); } if ((proto & 1) == 0) { ++i; proto = (proto << 8) + MSG_BYTE(mp, i); } hlen = i + 1; /* * Now reconstruct a complete, contiguous PPP header at the * start of the packet. */ if (hlen < ((cp->flags & DECOMP_AC)? 0: 2) + ((cp->flags & DECOMP_PROT)? 1: 2)) { /* count these? */ goto bad; } if (mp->b_rptr + hlen > mp->b_wptr) { adjmsg(mp, hlen); /* XXX check this call */ hlen = 0; } if (hlen != PPP_HDRLEN) { /* * We need to put some bytes on the front of the packet * to make a full-length PPP header. * If we can put them in *mp, we do, otherwise we * tack another mblk on the front. * XXX we really shouldn't need to carry around * the address and control at this stage. */ dp = mp->b_rptr + hlen - PPP_HDRLEN; if (dp < mp->b_datap->db_base || mp->b_datap->db_ref > 1) { np = allocb(PPP_HDRLEN, BPRI_MED); if (np == 0) goto bad; np->b_cont = mp; mp->b_rptr += hlen; mp = np; dp = mp->b_wptr; mp->b_wptr += PPP_HDRLEN; } else mp->b_rptr = dp; dp[0] = PPP_ALLSTATIONS; dp[1] = PPP_UI; dp[2] = proto >> 8; dp[3] = proto; } /* * Now see if we have a compressed packet to decompress, * or a CCP packet to take notice of. */ proto = PPP_PROTOCOL(mp->b_rptr); if (proto == PPP_CCP) { len = msgdsize(mp); if (mp->b_wptr < mp->b_rptr + len) { PULLUP(mp, len); if (mp == 0) goto bad; } ppp_comp_ccp(q, mp, 1); } else if (proto == PPP_COMP) { if ((cp->flags & CCP_ISUP) && (cp->flags & CCP_DECOMP_RUN) && cp->rstate && (cp->flags & CCP_ERR) == 0) { rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp); switch (rv) { case DECOMP_OK: freemsg(mp); mp = dmp; if (mp == NULL) { /* no error, but no packet returned either. */ continue; } break; case DECOMP_ERROR: cp->flags |= CCP_ERROR; ++cp->stats.ppp_ierrors; putctl1(q->q_next, M_CTL, PPPCTL_IERROR); break; case DECOMP_FATALERROR: cp->flags |= CCP_FATALERROR; ++cp->stats.ppp_ierrors; putctl1(q->q_next, M_CTL, PPPCTL_IERROR); break; } } } else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) { (*cp->rcomp->incomp)(cp->rstate, mp); } /* * Now do VJ decompression. */ proto = PPP_PROTOCOL(mp->b_rptr); if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) { len = msgdsize(mp) - PPP_HDRLEN; if ((cp->flags & DECOMP_VJC) == 0 || len <= 0) goto bad; /* * Advance past the ppp header. * Here we assume that the whole PPP header is in the first mblk. */ np = mp; dp = np->b_rptr + PPP_HDRLEN; if (dp >= mp->b_wptr) { np = np->b_cont; dp = np->b_rptr; } /* * Make sure we have sufficient contiguous data at this point. */ hlen = (proto == PPP_VJC_COMP)? MAX_VJHDR: MAX_IPHDR; if (hlen > len) hlen = len; if (np->b_wptr < dp + hlen || np->b_datap->db_ref > 1) { PULLUP(mp, hlen + PPP_HDRLEN); if (mp == 0) goto bad; np = mp; dp = np->b_rptr + PPP_HDRLEN; } if (proto == PPP_VJC_COMP) { /* * Decompress VJ-compressed packet. * First reset compressor if an input error has occurred. */ if (cp->stats.ppp_ierrors != cp->vj_last_ierrors) { if (cp->flags & DBGLOG) DPRINT1("ppp%d: resetting VJ\n", cp->unit); vj_uncompress_err(&cp->vj_comp); cp->vj_last_ierrors = cp->stats.ppp_ierrors; } vjlen = vj_uncompress_tcp(dp, np->b_wptr - dp, len, &cp->vj_comp, &iphdr, &iphlen); if (vjlen < 0) { if (cp->flags & DBGLOG) DPRINT2("ppp%d: vj_uncomp_tcp failed, pkt len %d\n", cp->unit, len); ++cp->vj_last_ierrors; /* so we don't reset next time */ goto bad; } /* drop ppp and vj headers off */ if (mp != np) { freeb(mp); mp = np; } mp->b_rptr = dp + vjlen; /* allocate a new mblk for the ppp and ip headers */ if ((np = allocb(iphlen + PPP_HDRLEN + 4, BPRI_MED)) == 0) goto bad; dp = np->b_rptr; /* prepend mblk with TCP/IP hdr */ dp[0] = PPP_ALLSTATIONS; /* reconstruct PPP header */ dp[1] = PPP_UI; dp[2] = PPP_IP >> 8; dp[3] = PPP_IP; bcopy((caddr_t)iphdr, (caddr_t)dp + PPP_HDRLEN, iphlen); np->b_wptr = dp + iphlen + PPP_HDRLEN; np->b_cont = mp; /* XXX there seems to be a bug which causes panics in strread if we make an mbuf with only the IP header in it :-( */ if (mp->b_wptr - mp->b_rptr > 4) { bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, 4); mp->b_rptr += 4; np->b_wptr += 4; } else { bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, mp->b_wptr - mp->b_rptr); np->b_wptr += mp->b_wptr - mp->b_rptr; np->b_cont = mp->b_cont; freeb(mp); } mp = np; } else { /* * "Decompress" a VJ-uncompressed packet. */ cp->vj_last_ierrors = cp->stats.ppp_ierrors; if (!vj_uncompress_uncomp(dp, hlen, &cp->vj_comp)) { if (cp->flags & DBGLOG) DPRINT2("ppp%d: vj_uncomp_uncomp failed, pkt len %d\n", cp->unit, len); ++cp->vj_last_ierrors; /* don't need to reset next time */ goto bad; } mp->b_rptr[3] = PPP_IP; /* fix up the PPP protocol field */ } } putnext(q, mp); continue; bad: if (mp != 0) freemsg(mp); cp->stats.ppp_ierrors++; putctl1(q->q_next, M_CTL, PPPCTL_IERROR); } return 0; } /* * Handle a CCP packet being sent or received. * Here all the data in the packet is in a single mbuf. */ static void ppp_comp_ccp(q, mp, rcvd) queue_t *q; mblk_t *mp; int rcvd; { int len, clen; comp_state_t *cp; unsigned char *dp; len = msgdsize(mp); if (len < PPP_HDRLEN + CCP_HDRLEN) return; cp = (comp_state_t *) q->q_ptr; dp = mp->b_rptr + PPP_HDRLEN; len -= PPP_HDRLEN; clen = CCP_LENGTH(dp); if (clen > len) return; switch (CCP_CODE(dp)) { case CCP_CONFREQ: case CCP_TERMREQ: case CCP_TERMACK: cp->flags &= ~CCP_ISUP; break; case CCP_CONFACK: if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN && clen >= CCP_HDRLEN + CCP_OPT_MINLEN && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) { if (!rcvd) { if (cp->xstate != NULL && (*cp->xcomp->comp_init) (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN, cp->unit, 0, ((cp->flags & DBGLOG) != 0))) cp->flags |= CCP_COMP_RUN; } else { if (cp->rstate != NULL && (*cp->rcomp->decomp_init) (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN, cp->unit, 0, cp->mru, ((cp->flags & DBGLOG) != 0))) cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN; } } break; case CCP_RESETACK: if (cp->flags & CCP_ISUP) { if (!rcvd) { if (cp->xstate && (cp->flags & CCP_COMP_RUN)) (*cp->xcomp->comp_reset)(cp->xstate); } else { if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) { (*cp->rcomp->decomp_reset)(cp->rstate); cp->flags &= ~CCP_ERROR; } } } break; } } #if 0 dump_msg(mp) mblk_t *mp; { dblk_t *db; while (mp != 0) { db = mp->b_datap; DPRINT2("mp=%x cont=%x ", mp, mp->b_cont); DPRINT3("rptr=%x wptr=%x datap=%x\n", mp->b_rptr, mp->b_wptr, db); DPRINT2(" base=%x lim=%x", db->db_base, db->db_lim); DPRINT2(" ref=%d type=%d\n", db->db_ref, db->db_type); mp = mp->b_cont; } } #endif static int msg_byte(mp, i) mblk_t *mp; unsigned int i; { while (mp != 0 && i >= mp->b_wptr - mp->b_rptr) mp = mp->b_cont; if (mp == 0) return -1; return mp->b_rptr[i]; } ppp-2.4.5/solaris/ppp_comp_mod.c000066400000000000000000000044541130035057700166250ustar00rootroot00000000000000/* * ppp_comp_mod.c - modload support for PPP compression STREAMS module. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp_comp_mod.c,v 1.2 2002/12/06 09:49:16 paulus Exp $ */ /* * This file is used under Solaris 2. */ #include #include #include #include #include extern struct streamtab ppp_compinfo; static struct fmodsw fsw = { "ppp_comp", &ppp_compinfo, D_NEW | D_MP | D_MTQPAIR }; extern struct mod_ops mod_strmodops; static struct modlstrmod modlstrmod = { &mod_strmodops, "PPP compression module", &fsw }; static struct modlinkage modlinkage = { MODREV_1, (void *) &modlstrmod, NULL }; /* * Entry points for modloading. */ int _init(void) { return mod_install(&modlinkage); } int _fini(void) { return mod_remove(&modlinkage); } int _info(mip) struct modinfo *mip; { return mod_info(&modlinkage, mip); } ppp-2.4.5/solaris/ppp_mod.c000066400000000000000000000110071130035057700155770ustar00rootroot00000000000000/* * ppp_mod.c - modload support for PPP pseudo-device driver. * * Copyright (c) 1994 Paul Mackerras. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Paul Mackerras * ". * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: ppp_mod.c,v 1.3 2004/01/17 05:47:55 carlsonj Exp $ */ /* * This file is used under Solaris 2. */ #include #include #include #include #include #include #include #ifdef __STDC__ #define __P(x) x #else #define __P(x) () #endif static int ppp_identify __P((dev_info_t *)); static int ppp_attach __P((dev_info_t *, ddi_attach_cmd_t)); static int ppp_detach __P((dev_info_t *, ddi_detach_cmd_t)); static int ppp_devinfo __P((dev_info_t *, ddi_info_cmd_t, void *, void **)); extern struct streamtab pppinfo; extern krwlock_t ppp_lower_lock; static dev_info_t *ppp_dip; static struct cb_ops cb_ppp_ops = { nulldev, nulldev, nodev, nodev, /* cb_open, ... */ nodev, nodev, nodev, nodev, /* cb_dump, ... */ nodev, nodev, nodev, nochpoll, /* cb_devmap, ... */ ddi_prop_op, /* cb_prop_op */ &pppinfo, /* cb_stream */ D_NEW|D_MP|D_MTQPAIR|D_MTOUTPERIM|D_MTOCEXCL /* cb_flag */ }; static struct dev_ops ppp_ops = { DEVO_REV, /* devo_rev */ 0, /* devo_refcnt */ ppp_devinfo, /* devo_getinfo */ ppp_identify, /* devo_identify */ nulldev, /* devo_probe */ ppp_attach, /* devo_attach */ ppp_detach, /* devo_detach */ nodev, /* devo_reset */ &cb_ppp_ops, /* devo_cb_ops */ NULL /* devo_bus_ops */ }; /* * Module linkage information */ static struct modldrv modldrv = { &mod_driverops, /* says this is a pseudo driver */ "PPP-2.3 multiplexing driver", &ppp_ops /* driver ops */ }; static struct modlinkage modlinkage = { MODREV_1, (void *) &modldrv, NULL }; int _init(void) { return mod_install(&modlinkage); } int _fini(void) { return mod_remove(&modlinkage); } int _info(mip) struct modinfo *mip; { return mod_info(&modlinkage, mip); } static int ppp_identify(dip) dev_info_t *dip; { /* This entry point is not used as of Solaris 10 */ #ifdef DDI_IDENTIFIED return strcmp(ddi_get_name(dip), "ppp") == 0? DDI_IDENTIFIED: DDI_NOT_IDENTIFIED; #else return 0; #endif } static int ppp_attach(dip, cmd) dev_info_t *dip; ddi_attach_cmd_t cmd; { if (cmd != DDI_ATTACH) return DDI_FAILURE; if (ddi_create_minor_node(dip, "ppp", S_IFCHR, 0, DDI_PSEUDO, CLONE_DEV) == DDI_FAILURE) { ddi_remove_minor_node(dip, NULL); return DDI_FAILURE; } rw_init(&ppp_lower_lock, NULL, RW_DRIVER, NULL); return DDI_SUCCESS; } static int ppp_detach(dip, cmd) dev_info_t *dip; ddi_detach_cmd_t cmd; { rw_destroy(&ppp_lower_lock); ddi_remove_minor_node(dip, NULL); return DDI_SUCCESS; } static int ppp_devinfo(dip, cmd, arg, result) dev_info_t *dip; ddi_info_cmd_t cmd; void *arg; void **result; { int error; error = DDI_SUCCESS; switch (cmd) { case DDI_INFO_DEVT2DEVINFO: if (ppp_dip == NULL) error = DDI_FAILURE; else *result = (void *) ppp_dip; break; case DDI_INFO_DEVT2INSTANCE: *result = NULL; break; default: error = DDI_FAILURE; } return error; } ppp-2.4.5/solaris/ppp_mod.h000066400000000000000000000116331130035057700156110ustar00rootroot00000000000000/* * Miscellaneous definitions for PPP STREAMS modules. */ /* * Macros for allocating and freeing kernel memory. */ #ifdef SVR4 /* SVR4, including Solaris 2 */ #include #define ALLOC_SLEEP(n) kmem_alloc((n), KM_SLEEP) #define ALLOC_NOSLEEP(n) kmem_alloc((n), KM_NOSLEEP) #define FREE(p, n) kmem_free((p), (n)) #endif #ifdef SUNOS4 #include /* SunOS 4.x */ #define ALLOC_SLEEP(n) kmem_alloc((n), KMEM_SLEEP) #define ALLOC_NOSLEEP(n) kmem_alloc((n), KMEM_NOSLEEP) #define FREE(p, n) kmem_free((p), (n)) #define NOTSUSER() (suser()? 0: EPERM) #define bcanputnext(q, band) canputnext((q)) #endif /* SunOS 4 */ #ifdef __osf__ #include /* caution: this mirrors macros in sys/malloc.h, and uses interfaces * which are subject to change. * The problems are that: * - the official MALLOC macro wants the lhs of the assignment as an argument, * and it takes care of the assignment itself (yuck.) * - PPP insists on using "FREE" which conflicts with a macro of the same name. * */ #ifdef BUCKETINDX /* V2.0 */ #define ALLOC_SLEEP(n) (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_WAITOK) #define ALLOC_NOSLEEP(n) (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_NOWAIT) #else #define ALLOC_SLEEP(n) (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_WAITOK) #define ALLOC_NOSLEEP(n) (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_NOWAIT) #endif #define bcanputnext(q, band) canputnext((q)) #ifdef FREE #undef FREE #endif #define FREE(p, n) free((void *)(p), M_DEVBUF) #define NO_DLPI 1 #ifndef IFT_PPP #define IFT_PPP 0x17 #endif #include #define NOTSUSER() (suser(u.u_procp->p_rcred, &u.u_acflag) ? EPERM : 0) /* #include "ppp_osf.h" */ #endif /* __osf__ */ #ifdef AIX4 #define ALLOC_SLEEP(n) xmalloc((n), 0, pinned_heap) /* AIX V4.x */ #define ALLOC_NOSLEEP(n) xmalloc((n), 0, pinned_heap) /* AIX V4.x */ #define FREE(p, n) xmfree((p), pinned_heap) #define NOTSUSER() (suser()? 0: EPERM) #endif /* AIX */ /* * Macros for printing debugging stuff. */ #ifdef DEBUG #if defined(SVR4) || defined(__osf__) #if defined(SNI) #include #define STRLOG_ID 4712 #define DPRINT(f) strlog(STRLOG_ID, 0, 0, SL_TRACE, f) #define DPRINT1(f, a1) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1) #define DPRINT2(f, a1, a2) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2) #define DPRINT3(f, a1, a2, a3) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2, a3) #else #define DPRINT(f) cmn_err(CE_CONT, f) #define DPRINT1(f, a1) cmn_err(CE_CONT, f, a1) #define DPRINT2(f, a1, a2) cmn_err(CE_CONT, f, a1, a2) #define DPRINT3(f, a1, a2, a3) cmn_err(CE_CONT, f, a1, a2, a3) #endif /* SNI */ #else #define DPRINT(f) printf(f) #define DPRINT1(f, a1) printf(f, a1) #define DPRINT2(f, a1, a2) printf(f, a1, a2) #define DPRINT3(f, a1, a2, a3) printf(f, a1, a2, a3) #endif /* SVR4 or OSF */ #else #define DPRINT(f) 0 #define DPRINT1(f, a1) 0 #define DPRINT2(f, a1, a2) 0 #define DPRINT3(f, a1, a2, a3) 0 #endif /* DEBUG */ #ifndef SVR4 typedef unsigned char uchar_t; typedef unsigned short ushort_t; #ifndef __osf__ typedef int minor_t; #endif #endif /* * If we don't have multithreading support, define substitutes. */ #ifndef D_MP # define qprocson(q) # define qprocsoff(q) # define put(q, mp) ((*(q)->q_qinfo->qi_putp)((q), (mp))) # define canputnext(q) canput((q)->q_next) # define qwriter(q, mp, func, scope) (func)((q), (mp)) #endif #ifdef D_MP /* Use msgpullup if we have other multithreading support. */ #define PULLUP(mp, len) \ do { \ mblk_t *np = msgpullup((mp), (len)); \ freemsg((mp)); \ mp = np; \ } while (0) #else /* Use pullupmsg if we don't have any multithreading support. */ #define PULLUP(mp, len) \ do { \ if (!pullupmsg((mp), (len))) { \ freemsg((mp)); \ mp = 0; \ } \ } while (0) #endif /* * How to declare the open and close procedures for a module. */ #ifdef SVR4 #define MOD_OPEN_DECL(name) \ static int name __P((queue_t *, dev_t *, int, int, cred_t *)) #define MOD_CLOSE_DECL(name) \ static int name __P((queue_t *, int, cred_t *)) #define MOD_OPEN(name) \ static int name(q, devp, flag, sflag, credp) \ queue_t *q; \ dev_t *devp; \ int flag, sflag; \ cred_t *credp; #define MOD_CLOSE(name) \ static int name(q, flag, credp) \ queue_t *q; \ int flag; \ cred_t *credp; #define OPEN_ERROR(x) return (x) #define DRV_OPEN_OK(dev) return 0 #define NOTSUSER() (drv_priv(credp)) #else /* not SVR4 */ #define MOD_OPEN_DECL(name) \ static int name __P((queue_t *, int, int, int)) #define MOD_CLOSE_DECL(name) \ static int name __P((queue_t *, int)) #define MOD_OPEN(name) \ static int name(q, dev, flag, sflag) \ queue_t *q; \ int dev; \ int flag, sflag; #define MOD_CLOSE(name) \ static int name(q, flag) \ queue_t *q; \ int flag; #define OPEN_ERROR(x) { u.u_error = (x); return OPENFAIL; } #define DRV_OPEN_OK(dev) return (dev) #endif /* SVR4 */