diff options
author | Guillaume Cottenceau <gc@mandriva.com> | 2001-06-11 11:44:34 +0000 |
---|---|---|
committer | Guillaume Cottenceau <gc@mandriva.com> | 2001-06-11 11:44:34 +0000 |
commit | ab5559aaabd1167a18ac882e64d97c5adc0e7d03 (patch) | |
tree | d22adafe4701e0abbccc7456fc58ae60ce75d5fb | |
parent | f35f2383eed07ff16aa76f30975817117eea6cbb (diff) | |
download | drakx-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.tar drakx-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.tar.gz drakx-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.tar.bz2 drakx-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.tar.xz drakx-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.zip |
Initial revision
184 files changed, 74040 insertions, 0 deletions
diff --git a/mdk-stage1/ppp/Changes-2.3 b/mdk-stage1/ppp/Changes-2.3 new file mode 100644 index 000000000..f5c954b4b --- /dev/null +++ b/mdk-stage1/ppp/Changes-2.3 @@ -0,0 +1,441 @@ +What 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-<linkname>.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 <n> will send log messages to file + descriptor <n> 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 + <nickwalker@email.com>. 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. diff --git a/mdk-stage1/ppp/FAQ b/mdk-stage1/ppp/FAQ new file mode 100644 index 000000000..96bc5c708 --- /dev/null +++ b/mdk-stage1/ppp/FAQ @@ -0,0 +1,634 @@ +This 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 implementation +of Microsoft's CHAP in ppp-2.3; see README.MSCHAP80. + + +------------------------------------------------------------------------ + +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" diff --git a/mdk-stage1/ppp/PLUGINS b/mdk-stage1/ppp/PLUGINS new file mode 100644 index 000000000..0eeabe249 --- /dev/null +++ b/mdk-stage1/ppp/PLUGINS @@ -0,0 +1,131 @@ +Starting 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. My aim is that +plugins will be able to be used with successive versions of pppd +without needing to recompile the plugins. + +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. I suggest that you give the full path name of +the shared object file; 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. + +Plugins can affect the behaviour of pppd in at least three 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. + +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, int userlen, + char *passwd, int passlen, + char **msgp, int *msglenp, + struct wordlist **paddrs, + struct wordlist **popts); + +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. + + +## $Id$ ## diff --git a/mdk-stage1/ppp/README b/mdk-stage1/ppp/README new file mode 100644 index 000000000..aa1e5f9c2 --- /dev/null +++ b/mdk-stage1/ppp/README @@ -0,0 +1,168 @@ +This 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 the protocols layered above IP, such as TCP and UDP. +The Linux and Solaris ports of this package have optional support for +IPV6; the Linux port of this package also has support for IPX. + +This software 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 +SunOS 4.x and Solaris, and as a line discipline under Linux and FreeBSD. + +- 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 primary platforms supported by this package are Linux and Solaris. +Code for SunOS 4.x is included here but is largely untested. I have +code for NeXTStep, FreeBSD, 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. + + +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 2 README.sol2 + SunOS 4.x README.sunos4 + +In each case you start by running the ./configure script. This works +out which operating system you are using and creates symbolic links to +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.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/<pppd-version> (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 + Linux-2.3.99pre5 or later 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 is also patent-encumbered (its inclusion in this package +can be considered a historical anomaly :-) but it doesn't ever expand +packets. Neither does Deflate, which uses the same algorithm as gzip. + + +Patents. +******** + +The BSD-Compress algorithm used for packet compression is the same as +that used in the Unix "compress" command. It is apparently covered by +U.S. patents 4,814,746 (owned by IBM) and 4,558,302 (owned by Unisys), +and corresponding patents in various other countries (but not +Australia). If this is of concern, you can build the package without +including BSD-Compress. To do this, edit net/ppp-comp.h to change the +definition of DO_BSD_COMPRESS to 0. The bsd-comp.c files are then no +longer needed, so the references to bsd-comp.o may optionally be +removed from the Makefiles. + + +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 <paulus@linuxcare.com> +Solaris 2 James Carlson <james.d.carlson@east.sun.com> +SunOS 4.x Adi Masputra <adi.masputra@sun.com> + + +Copyrights: +*********** + +All of the code can be freely used and redistributed. The individual +source files each have their own copyright and permission notice; some +have a BSD-style notice and some are under the GPL. + + +Distribution: +************* + +The primary site for releases of this software is: + + ftp://linuxcare.com.au/pub/ppp/ + + +($Id$) diff --git a/mdk-stage1/ppp/README.MSCHAP80 b/mdk-stage1/ppp/README.MSCHAP80 new file mode 100644 index 000000000..d3ed291b7 --- /dev/null +++ b/mdk-stage1/ppp/README.MSCHAP80 @@ -0,0 +1,284 @@ +PPP Client Support for Microsoft's CHAP-80 +========================================== + +Eric Rosenquist rosenqui@strataware.com +(updated by Paul Mackerras) +(updated by Al Longyear) +(updated by Farrell Woods) + +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: + + <ftp://ftp.microsoft.com/developr/rfc/chapexts.txt> + +In short, MS-CHAP is identified as <auth chap 80> 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 <asyncmap 0x0> <auth chap 80> <magic 0x46a3>] + ^^^^^^^^^^^^ + +The standard pppd implementation will indicate its lack of support for +MS-CHAP by NAKing it: + + sent [LCP ConfNak id=0x2 <auth chap 05>] + +Windows NT Server systems are often configured to "Accept only +Microsoft Authentication" (this is intended to enhance security). Up +until now, that meant that you couldn't use this version of PPPD to +connect to such a system. I've managed to get a client-only +implementation of MS-CHAP working; it will authenticate itself to +another system using MS-CHAP, but if you're using PPPD as a dial-in +server, you won't be able to use MS-CHAP to authenticate the clients. +This would not be a lot of extra work given that the framework is in +place, but I didn't need it myself so I didn't implement it. + + +BUILDING THE PPPD + +MS-CHAP uses a combination of MD4 hashing and DES encryption for +authentication. You may need to get Eric Young's libdes library in +order to use my MS-CHAP extensions. A lot of UNIX systems already +have DES encryption available via the crypt(3), encrypt(3) and +setkey(3) interfaces. Some may (such as that on Digital UNIX) +provide only the encryption mechanism and will not perform +decryption. This is okay. We only need to encrypt to perform +MS-CHAP authentication. + +If you have encrypt/setkey available, then hopefully you need only +define these two things in your Makefile: -DUSE_CRYPT and -DCHAPMS. +Skip the paragraphs below about obtaining and building libdes. Do +the "make clean" and "make" as described below. Linux users +should not need to modify their Makefiles. Instead, +just do "make CHAPMS=1 USE_CRYPT=1". + +If you don't have encrypt and setkey, you will need Eric Young's +libdes library. You can find it in: + +ftp://ftp.funet.fi/pub/crypt/mirrors/ftp.psy.uq.oz.au/DES/libdes-3.06.tar.gz + +Australian residents can get libdes from Eric Young's site: + +ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/libdes-3.06.tar.gz + +It is also available on many other sites (ask Archie). + +I used libdes-3.06, but hopefully anything newer than that will work +also. Get the library, build and test it on your system, and install +it somewhere (typically /usr/local/lib and /usr/local/include). + + + +You should now be ready to (re)compile the PPPD. Go to the pppd +subdirectory and make sure the Makefile contains "-DCHAPMS" in the +CFLAGS or COMPILE_FLAGS macro, and that the LIBS macro (or LDADD for +BSD systems) contains "-ldes". Depending on your system and where the +DES library was installed, you may also need to alter the include and +library paths used by your compiler. + +Do a "make clean" and then a "make" to rebuild pppd. Assuming all +goes well, install the new pppd and move on to the CONFIGURATION +section. + + +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 <other options> + +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 <other options> + +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 +Microsoft's "chapexts.txt" file referenced above: + + 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. + +If everything compiles cleanly, but fails at authentication time, then +it might be a case of the MD4 or DES code screwing up. The following +small program can be used to test the MS-CHAP code to see if it +produces a known response: + +----------------- +#include <stdio.h> + +#include "pppd.h" +#include "chap.h" +#include "chap_ms.h" + +int main(argc, argv) + int argc; + char *argv[]; +{ + u_char challenge[8]; + int challengeInt[sizeof(challenge)]; + chap_state cstate; + int i; + + if (argc != 3) { + fprintf(stderr, "Usage: %s <16-hexchar challenge> <password>\n", + argv[0]); exit(1); + } + + sscanf(argv[1], "%2x%2x%2x%2x%2x%2x%2x%2x", + challengeInt + 0, challengeInt + 1, challengeInt + 2, + challengeInt + 3, challengeInt + 4, challengeInt + 5, + challengeInt + 6, challengeInt + 7); + + for (i = 0; i < sizeof(challenge); i++) + challenge[i] = (u_char)challengeInt[i]; + + ChapMS(&cstate, challenge, sizeof(challenge), argv[2], strlen(argv[2])); + printf("Response length is %d, response is:", cstate.resp_length); + + for (i = 0; i < cstate.resp_length; i++) { + if (i % 8 == 0) + putchar('\n'); + printf("%02X ", (unsigned int)cstate.response[i]); + } + + putchar('\n'); + + exit(0); +} +------------- + +This needs to link against chap_ms.o, md4.o, and the DES library. When +you run it with the command line: + + $ testchap 00000000000000000000000000000000 hello + +it should output the following: + + Response length is 49, response is: + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + F4 D9 9D AF 82 64 DC 3C + 53 F9 BC 92 14 B5 5D 9E + 78 C4 21 48 9D B7 A8 B4 + 01 + +if not, then either the DES library is not working, the MD4 code isn't +working, or there are some problems with the port of the code in +chap_ms.c. + + +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. + +A server implementation would allow MS-CHAP to be used with Windows NT and +Windows 95 clients for enhanced security. Some new command-line options +would be required, as would code to generate the Challenge packet and +verify the response. Most of the helper functions are in place, so this +shouldn't be too hard for someone to add. diff --git a/mdk-stage1/ppp/README.cbcp b/mdk-stage1/ppp/README.cbcp new file mode 100644 index 000000000..6f7b7685e --- /dev/null +++ b/mdk-stage1/ppp/README.cbcp @@ -0,0 +1,97 @@ + 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 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 change the +appropriate Makefile in the pppd subdirectory to add "-DCBCP_SUPPORT" +to the CFLAGS definition and add cbcp.o to the list of object files, +and then recompile pppd. The patch below does this for Makefile.bsd +and Makefile.linux. + + +--------------------------------cut here------------------------------- +diff -r -c ppp-2.3.orig/pppd/Makefile.bsd ppp-2.3/pppd/Makefile.bsd +*** ppp-2.3.orig/pppd/Makefile.bsd Tue Oct 8 13:33:33 1996 +--- ppp-2.3/pppd/Makefile.bsd Fri Apr 11 23:59:15 1997 +*************** +*** 4,14 **** + # -D_BITYPES is for FreeBSD, which doesn't define anything to + # tell us that u_int32_t gets defined if <sys/types.h> is included. + # Remove for older *BSD systems for which this isn't true. +! CFLAGS+= -g -I.. -DHAVE_PATHS_H -D_BITYPES + + PROG= pppd + SRCS= main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \ +! demand.c auth.c options.c sys-bsd.c + MAN= pppd.cat8 + MAN8= pppd.8 + BINMODE=4555 +--- 4,14 ---- + # -D_BITYPES is for FreeBSD, which doesn't define anything to + # tell us that u_int32_t gets defined if <sys/types.h> is included. + # Remove for older *BSD systems for which this isn't true. +! CFLAGS+= -I.. -DHAVE_PATHS_H -D_BITYPES -DCBCP_SUPPORT + + PROG= pppd + SRCS= main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \ +! demand.c auth.c options.c sys-bsd.c cbcp.c + MAN= pppd.cat8 + MAN8= pppd.8 + BINMODE=4555 +diff -r -c ppp-2.3.orig/pppd/Makefile.linux ppp-2.3/pppd/Makefile.linux +*** ppp-2.3.orig/pppd/Makefile.linux Tue Oct 8 15:42:41 1996 +--- ppp-2.3/pppd/Makefile.linux Sat Apr 12 00:02:28 1997 +*************** +*** 14,20 **** + ipxcp.h cbcp.h + MANPAGES = pppd.8 + PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \ +! auth.o options.o demand.o sys-linux.o ipxcp.o + + all: pppd + +--- 14,20 ---- + ipxcp.h cbcp.h + MANPAGES = pppd.8 + PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \ +! auth.o options.o demand.o sys-linux.o ipxcp.o cbcp.o + + all: pppd + +*************** +*** 36,42 **** + #INCLUDE_DIRS= -I/usr/include -I.. + INCLUDE_DIRS= + +! COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE + + CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) + +--- 36,42 ---- + #INCLUDE_DIRS= -I/usr/include -I.. + INCLUDE_DIRS= + +! COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DCBCP_SUPPORT + + CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) + diff --git a/mdk-stage1/ppp/README.linux b/mdk-stage1/ppp/README.linux new file mode 100644 index 000000000..62ed9ca7f --- /dev/null +++ b/mdk-stage1/ppp/README.linux @@ -0,0 +1,297 @@ + PPP for Linux + ------------- + + Paul Mackerras + 8 March 2001 + + for ppp-2.4.1 + +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 2.2 and 2.4 kernel sources contain +up-to-date drivers. + +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.2 or 2.4 (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 the latest 2.4 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.1b1 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 devfs (the device +filesystem), the /dev/ppp node will automagically appear when the +ppp_generic module is loaded, or at startup if ppp_generic is compiled +in. + +If you have ppp_generic as a module, and you are using devfsd (the +devfs daemon), you will need to add a line like this to your +/etc/devfsd.conf: + +LOOKUP ppp MODLOAD + +Otherwise you will need to create a /dev/ppp device node with the +commands: + +# mknod /dev/ppp c 108 0 +# chmod 600 /dev/ppp + +If you use module autoloading and have PPP as a module, you will need +to add the following to your /etc/modules.conf or /etc/conf.modules: + +alias /dev/ppp ppp_generic +alias char-major-108 ppp_generic +alias tty-ldisc-3 ppp_async +alias tty-ldisc-14 ppp_synctty +alias ppp-compress-21 bsd_comp +alias ppp-compress-24 ppp_deflate +alias ppp-compress-26 ppp_deflate + + +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@linuxcare.com.au) 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 + diff --git a/mdk-stage1/ppp/README.sol2 b/mdk-stage1/ppp/README.sol2 new file mode 100644 index 000000000..4c862208f --- /dev/null +++ b/mdk-stage1/ppp/README.sol2 @@ -0,0 +1,220 @@ +This file describes the installation process for ppp-2.3 on systems +running Solaris 2. The Solaris 2 and SVR4 ports share a lot of code +but are not identical. The STREAMS kernel modules and driver for +Solaris 2 are in the svr4 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 + +If you wish to use gcc (or another compiler) instead of Sun's cc, edit +the svr4/Makedefs file and uncomment the definition of CC. You can +also change the options passed to the C compiler by editing the COPTS +definition. + +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 svr4/Makedefs. + +If your system normally has only one network interface, the default +Solaris 2 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. + +Dynamic STREAMS Re-Plumbing Support. +************************************ + +Solaris 8 includes dynamic re-plumbing support. With this, modules +below ip can be inserted, or removed, without having the ip stream be +unplumbed, and re-plumbed again. All states in ip for an interface +will therefore now be preserved. Users can install (or upgrade) +modules like firewall, bandwidth manager, cache manager, tunneling, +etc., without shutting the machine 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 has been done +on both Solaris 7 and 8. Only IPv4 and IPv6 packets will be sent up to +stream(s) marked as promiscuous, e.g, snoop et al. + +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 running IPv6. As of now, +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 works 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<UP,LOOPBACK,RUNNING,MULTICAST,IPv4> mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +hme0: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> 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<UP,LOOPBACK,RUNNING,MULTICAST,IPv6> mtu 8252 index 1 + inet6 ::1/128 +hme0: flags=2000841<UP,RUNNING,MULTICAST,IPv6> mtu 1500 index 2 + ether 8:0:20:8d:38:c1 + inet6 fe80::a00:20ff:fe8d:38c1/10 +hme0:1: flags=2080841<UP,RUNNING,MULTICAST,ADDRCONF,IPv6> mtu 1500 index 2 + inet6 fec0::56:a00:20ff:fe8d:38c1/64 +hme0:2: flags=2080841<UP,RUNNING,MULTICAST,ADDRCONF,IPv6> mtu 1500 index 2 + inet6 2000::56:a00:20ff:fe8d:38c1/64 +hme0:3: flags=2080841<UP,RUNNING,MULTICAST,ADDRCONF,IPv6> mtu 1500 index 2 + inet6 2::56:a00:20ff:fe8d:38c1/64 +ppp0: flags=10008d1<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST,IPv4> mtu 1500 index 12 + inet 172.16.1.1 --> 172.16.1.2 netmask ffffff00 +ppp0: flags=2202851<UP,POINTOPOINT,RUNNING,MULTICAST,UNNUMBERED,NONUD,IPv6> 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 (and Solaris 8 ) in both +32- and 64-bits 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. + +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 +and /dev/zsh drivers. Synchronous mode was tested with a Cisco +router. + +There ppp daemon does not directly support controlling the serial +interface. It relies on the /usr/sbin/syncinit command to initialize +HDLC mode and clocking. + +Some bugs remain: large sized frames are not sent/received properly, +and may be related to the IP mtu. This may be due to bugs in pppd +itself, bugs in Solaris or the serial drivers. The /dev/zsh driver +seems more larger and can send/receive larger frames than the +/dev/se_hdlc driver. 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 +! + diff --git a/mdk-stage1/ppp/README.sunos4 b/mdk-stage1/ppp/README.sunos4 new file mode 100644 index 000000000..b48aec377 --- /dev/null +++ b/mdk-stage1/ppp/README.sunos4 @@ -0,0 +1,62 @@ +This file describes the installation process for ppp-2.3 on systems +running SunOS 4.1.x (or the equivalent). + +The STREAMS modules in the sunos4 directory provide kernel support for +PPP on SunOS 4.1.x systems. They have been tested under SunOS 4.1.3 +on a SparcStation 1+. They should work under earlier SunOS 4.1.x +systems, but no guarantees are given. + +These modules are designed to be loaded into the running kernel using +the `modload' command. + + +Installation. +************* + +1. Run the configure script and make the user-level programs and the +kernel modules. + + ./configure + make + +If you wish to compile using gcc instead of cc, edit the +sunos4/Makedefs file and uncomment the line saying "CC = gcc". You +can also change the C compiler options by editing the COPTS +definition. + +2. Install the pppd, pppstats and chat programs and the loadable +module object files (you need to be root to do this): + + make install + +By default, the programs and the loadable module object files go into +/usr/local/etc. Doing `make install' also copies a script called +ppp.INSTALL into /dev, and makes ppp.LOAD, ppp.UNLOAD, ppp.MKDEV and +ppp.RMDEV links to it. You can change the installation directories by +editing sunos4/Makedefs. + +3. Load the ppp modules (you need to be root for this too): + + /dev/ppp.LOAD + +You will want to do this in your /etc/rc.local file once you have +everything installed. I suggest you put something like the following +in /etc/rc.local (or /etc/loadable, if you have one): + + if [ -f /dev/ppp.AUTOLOAD ]; then + /dev/ppp.LOAD + fi + +and then create a /dev/ppp.AUTOLOAD file with the command + + touch /dev/ppp.AUTOLOAD + +It is not advisable to unload the "if_ppp" module, because it creates +network interface units, and SunOS does not provide any way to destroy +network interfaces once created. If you do unload it, the system will +probably crash at some later stage. + +If you have previously had ppp-2.2 installed, you may have statements +in your /etc/rc.local to load the ppp module. You will need to remove +those. Having done this, you will need to reboot to remove the old +ppp module and load the new modules. diff --git a/mdk-stage1/ppp/SETUP b/mdk-stage1/ppp/SETUP new file mode 100644 index 000000000..fb28a2138 --- /dev/null +++ b/mdk-stage1/ppp/SETUP @@ -0,0 +1,111 @@ + 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: + +cua0 # modem is connected to /dev/cua0 +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<n>.pid in /var/run (or /etc/ppp on older systems such as SunOS or +Ultrix). Here <n> 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` diff --git a/mdk-stage1/ppp/chat/Makefile.linux b/mdk-stage1/ppp/chat/Makefile.linux new file mode 100644 index 000000000..586cbd876 --- /dev/null +++ b/mdk-stage1/ppp/chat/Makefile.linux @@ -0,0 +1,27 @@ +# $Id$ + +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) + +CFLAGS= $(RPM_OPT_FLAGS) $(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) + $(INSTALL) -s -c chat $(BINDIR) + $(INSTALL) -c -m 644 chat.8 $(MANDIR)/man8 + +clean: + rm -f chat.o chat *~ diff --git a/mdk-stage1/ppp/chat/Makefile.linux.makeopt b/mdk-stage1/ppp/chat/Makefile.linux.makeopt new file mode 100644 index 000000000..974680a93 --- /dev/null +++ b/mdk-stage1/ppp/chat/Makefile.linux.makeopt @@ -0,0 +1,27 @@ +# $Id$ + +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) + +CFLAGS= -O2 -g -pipe $(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) + $(INSTALL) -s -c chat $(BINDIR) + $(INSTALL) -c -m 644 chat.8 $(MANDIR)/man8 + +clean: + rm -f chat.o chat *~ diff --git a/mdk-stage1/ppp/chat/Makefile.sol2 b/mdk-stage1/ppp/chat/Makefile.sol2 new file mode 100644 index 000000000..f566cc68b --- /dev/null +++ b/mdk-stage1/ppp/chat/Makefile.sol2 @@ -0,0 +1,19 @@ +# +# Makefile for chat on Solaris 2 +# + +include ../solaris/Makedefs + +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 diff --git a/mdk-stage1/ppp/chat/Makefile.sunos4 b/mdk-stage1/ppp/chat/Makefile.sunos4 new file mode 100644 index 000000000..b3507360e --- /dev/null +++ b/mdk-stage1/ppp/chat/Makefile.sunos4 @@ -0,0 +1,19 @@ +# +# Makefile for chat on suns +# + +include ../sunos4/Makedefs + +CFLAGS = -DSUNOS $(COPTS) + +all: chat + +chat: chat.o + $(CC) -o chat chat.o + +install: chat + $(INSTALL) -c chat $(BINDIR)/chat + $(INSTALL) -c -m 444 chat.8 $(MANDIR)/man8/chat.8 + +clean: + rm -f *~ *.o chat diff --git a/mdk-stage1/ppp/chat/chat.8 b/mdk-stage1/ppp/chat/chat.8 new file mode 100644 index 000000000..8d2029609 --- /dev/null +++ b/mdk-stage1/ppp/chat/chat.8 @@ -0,0 +1,515 @@ +.\" -*- nroff -*- +.\" manual page [] for chat 1.8 +.\" $Id$ +.\" 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<chat file> +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<timeout> +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<report file> +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<phone number> +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<phone number 2> +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. diff --git a/mdk-stage1/ppp/chat/chat.c b/mdk-stage1/ppp/chat/chat.c new file mode 100644 index 000000000..1b22907a8 --- /dev/null +++ b/mdk-stage1/ppp/chat/chat.c @@ -0,0 +1,1756 @@ +/* + * 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 <andras@cityweb.de>. + * + * 12-May-99 added a feature to read data to be sent from a file, + * if the send string starts with @. Idea from gpk <gpk@onramp.net>. + * + * 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 <kdart@cisco.com> + * + * + * 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 <Francis@SwissMail.Com> + * Thu May 15 17:15:40 MET DST 1997 + * + * + * Added -r "report file" switch & REPORT keyword. + * Robert Geer <bgeer@xmission.com> + * + * Added -s "use stderr" and -S "don't use syslog" switches. + * June 18, 1997 + * Karl O. Pinc <kop@meme.com> + * + * + * Added -e "echo" switch & ECHO keyword + * Dick Streefland <dicks@tasking.nl> + * + * + * Considerable updates and modifications by + * Al Longyear <longyear@pobox.com> + * Paul Mackerras <paulus@cs.anu.edu.au> + * + * + * The original author is: + * + * Karl Fox <karl@MorningStar.Com> + * 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$"; +#endif + +#include <stdio.h> +#include <ctype.h> +#include <time.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <syslog.h> + +#ifndef TERMIO +#undef TERMIOS +#define TERMIOS +#endif + +#ifdef TERMIO +#include <termio.h> +#endif +#ifdef TERMIOS +#include <termios.h> +#endif + +#define STR_LEN 1024 + +#ifndef SIGTYPE +#define SIGTYPE void +#endif + +#undef __P +#undef __V + +#ifdef __STDC__ +#include <stdarg.h> +#define __V(x) x +#define __P(x) x +#else +#include <varargs.h> +#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[50] ; +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)); +void usage __P((void)); +void logf __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); +} + +/* + * 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 logf __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) + logf("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 temp[STR_LEN], env_str[STR_LEN], cur_chr; + register char *s1, *phchar; + int add_return = sending; +#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) +#define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \ + || (((chr) >= 'a') && ((chr) <= 'z')) \ + || (((chr) >= 'A') && ((chr) <= 'Z')) \ + || (chr) == '_') + + s1 = temp; + while (*s) { + cur_chr = *s++; + if (cur_chr == '^') { + cur_chr = *s++; + if (cur_chr == '\0') { + *s1++ = '^'; + break; + } + cur_chr &= 0x1F; + if (cur_chr != 0) { + *s1++ = cur_chr; + } + continue; + } + + if (use_env && cur_chr == '$') { /* ARI */ + phchar = env_str; + while (isalnumx(*s)) + *phchar++ = *s++; + *phchar = '\0'; + phchar = getenv(env_str); + if (phchar) + while (*phchar) + *s1++ = *phchar++; + continue; + } + + if (cur_chr != '\\') { + *s1++ = cur_chr; + continue; + } + + cur_chr = *s++; + if (cur_chr == '\0') { + if (sending) { + *s1++ = '\\'; + *s1++ = '\\'; + } + break; + } + + switch (cur_chr) { + case 'b': + *s1++ = '\b'; + break; + + case 'c': + if (sending && *s == '\0') + add_return = 0; + else + *s1++ = cur_chr; + break; + + case '\\': + case 'K': + case 'p': + case 'd': + if (sending) + *s1++ = '\\'; + *s1++ = cur_chr; + break; + + case 'T': + if (sending && phone_num) { + for (phchar = phone_num; *phchar != '\0'; phchar++) + *s1++ = *phchar; + } + else { + *s1++ = '\\'; + *s1++ = 'T'; + } + break; + + case 'U': + if (sending && phone_num2) { + for (phchar = phone_num2; *phchar != '\0'; phchar++) + *s1++ = *phchar; + } + else { + *s1++ = '\\'; + *s1++ = 'U'; + } + break; + + case 'q': + quiet = 1; + break; + + case 'r': + *s1++ = '\r'; + break; + + case 'n': + *s1++ = '\n'; + break; + + case 's': + *s1++ = ' '; + break; + + case 't': + *s1++ = '\t'; + break; + + case 'N': + if (sending) { + *s1++ = '\\'; + *s1++ = '\0'; + } + else + *s1++ = 'N'; + break; + + case '$': /* ARI */ + if (use_env) { + *s1++ = 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)) + *s1++ = '\\'; + *s1++ = cur_chr; + } + break; + } + + if (sending) + *s1++ = '\\'; + *s1++ = cur_chr; + break; + } + } + + if (add_return) + *s1++ = '\r'; + + *s1++ = '\0'; /* guarantee closure */ + *s1++ = '\0'; /* terminate the string */ + return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ +} + +/* + * 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) + logf("Failed (%s)", fail_reason); + else + logf("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) + logf("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) + logf("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) > sizeof fail_buffer - 1) + fatal(1, "Illegal or too-long REPORT string ('%v')", s); + + report_string[n_reports++] = s1; + + if (verbose) + logf("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) > sizeof fail_buffer - 1) + 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) + logf("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) + logf("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: + logf("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: + logf("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) + logf(" -- write timed out"); + else + logf(" -- write failed: %m"); + } + return (0); + } + return (1); +} + +int put_string (s) +register char *s; +{ + quiet = 0; + s = clean(s, 1); + + if (verbose) { + if (quiet) + logf("send (??????)"); + else + logf("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) + logf("expect (%v)", string); + + if (len > STR_LEN) { + logf("expect string is too long"); + exit_code = 1; + return 0; + } + + if (len == 0) { + if (verbose) + logf("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) + logf(""); /* blank line */ + else + logf("%0.*v", s - logged, logged); + logged = s + 1; + } + + *s++ = c; + + if (verbose && s >= logged + 80) { + logf("%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) + logf("%0.*v", s - logged, logged); + logf(" -- 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) + logf("%0.*v", s - logged, logged); + logf(" -- 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) + logf("%0.*v", s - logged, logged); + logged = s; + } + s -= minlen; + memmove(temp, s, minlen); + logged = temp + (logged - s); + s = temp + minlen; + } + + if (alarmed && verbose) + logf("warning: alarm synchronization problem"); + } + + alarm(0); + + if (verbose && printed) { + if (alarmed) + logf(" -- read timed out"); + else + logf(" -- read failed: %m"); + } + + exit_code = 3; + alarmed = 0; + return (0); +} + +/* + * Gross kludge to handle Solaris versions >= 2.6 having usleep. + */ +#ifdef SOL2 +#include <sys/param.h> +#if MAXUID > 65536 /* then this is Solaris 2.6 or later */ +#undef NO_USLEEP +#endif +#endif /* SOL2 */ + +#ifdef NO_USLEEP +#include <sys/types.h> +#include <sys/time.h> + +/* + 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; +} diff --git a/mdk-stage1/ppp/common/zlib.c b/mdk-stage1/ppp/common/zlib.c new file mode 100644 index 000000000..503076886 --- /dev/null +++ b/mdk-stage1/ppp/common/zlib.c @@ -0,0 +1,5376 @@ +/* + * 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$ + */ + +/* + * ==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 <sys/types.h> +#include <sys/time.h> +#include <sys/systm.h> +#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 <linux/string.h> +#define HAVE_MEMCPY + +#else /* not kernel */ + +#if defined(MSDOS)||defined(VMS)||defined(CRAY)||defined(WIN32)||defined(RISCOS) +# include <stddef.h> +# include <errno.h> +#else + extern int errno; +#endif +#ifdef STDC +# include <string.h> +# include <stdlib.h> +#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 <alloc.h> +# else /* MSC or DJGPP */ +# include <malloc.h> +# 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 <stdio.h> +# 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)<<s->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 <ctype.h> +#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<<extra_lbits[code]); n++) { + length_code[length++] = (uch)code; + } + } + Assert (length == 256, "tr_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + length_code[length-1] = (uch)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<<extra_dbits[code]); n++) { + dist_code[dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: dist != 256"); + dist >>= 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<<MAX_BITS)-1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +local void build_tree(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->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<<z->state->wbits)) + { + length = (1<<z->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)<<k;k+=8;}} +#define DUMPBITS(j) {b>>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (uInt)(q<s->read?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) + { + ZFREE(z, s->sub.trees.blens); + 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)) + { + 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); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = BADB; + r = t; + LEAVE + } + 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)<<k;k+=8;}} +#define UNGRAB {n+=(c=k>>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 <stdio.h> +#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 */ diff --git a/mdk-stage1/ppp/common/zlib.h b/mdk-stage1/ppp/common/zlib.h new file mode 100644 index 000000000..188ddaff9 --- /dev/null +++ b/mdk-stage1/ppp/common/zlib.h @@ -0,0 +1,1010 @@ +/* $Id$ */ + +/* + * 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 <windows.h> +# 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<<windowBits bytes. If next_out is null, the + library will allocate its own buffer (and leave next_out null). next_in + need not be provided here but must be provided by the application for the + next call of inflate(). + + If the history buffer is provided by the application, next_out must + never be changed by the application since the decompressor maintains + history information inside this buffer from call to call; the application + can only reset next_out to the beginning of the history buffer when + avail_out is zero and all output has been consumed. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was + not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as + windowBits < 8). msg is set to null if there is no error message. + inflateInit2 does not perform any decompression: this will be done by + inflate(). +*/ + +extern int EXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary (history buffer) from the given + uncompressed byte sequence. This function must be called immediately after + a call of inflate if this call returned Z_NEED_DICT. The dictionary chosen + by the compressor can be determined from the Adler32 value returned by this + call of inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +extern int EXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until the special marker (see deflate() + above) can be found, or until all available input is skipped. No output + is provided. + + inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no marker has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +extern int EXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset 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 inflateIncomp OF((z_stream *strm)); +/* + This function adds the data at next_in (avail_in bytes) to the output + history without performing any output. There must be no pending output, + and the decompressor must be expecting to see the start of a block. + Calling this function is equivalent to decompressing a stored block + containing the data at next_in (except that the data is not output). +*/ + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level, window size, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +extern int EXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least 0.1% larger than + sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +extern int EXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ + + +typedef voidp gzFile; + +extern gzFile EXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9"). gzopen can be used to read a file which is not in gzip format; + in this case gzread will directly read from the file without decompression. + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ + +extern gzFile EXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +extern int EXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +extern int EXPORT gzwrite OF((gzFile file, const voidp buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +extern int EXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +extern int EXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +extern const char * EXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +extern uLong EXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +extern uLong EXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running crc with the bytes buf[0..len-1] and return the updated + crc. If buf is NULL, this function returns the required initial value + for the crc. Pre- and post-conditioning (one's complement) is performed + within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +extern int EXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +extern int EXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +extern int EXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, int strategy, + const char *version, int stream_size)); +extern int EXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + +#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +uLongf *get_crc_table OF((void)); /* can be used by asm versions of crc32() */ + +#ifdef __cplusplus +} +#endif + +#endif /* _ZLIB_H */ +/* --- zlib.h */ diff --git a/mdk-stage1/ppp/configure b/mdk-stage1/ppp/configure new file mode 100755 index 000000000..544fb0d71 --- /dev/null +++ b/mdk-stage1/ppp/configure @@ -0,0 +1,141 @@ +#!/bin/sh +# $Id$ + +# if [ -d /NextApps ]; then +# system="NeXTStep" +# else + system=`uname -s` + release=`uname -r` + machine=`uname -p` + arch=`uname -m` +# fi +state="unknown" + +case $system in + Linux) + makext="linux"; + ksrc="linux"; + state="known";; + SunOS) + case $release in +# [0-3]*) state="ancient";; +# 4*) state="known"; ksrc="sunos4"; makext="sunos4";; + 5.[1-6]*) state="known"; ksrc="solaris"; makext="sol2";; + 5.[7-9]*) state="known"; ksrc="solaris"; makext="sol2"; + case $arch in + sun4u) lp64='y';; + *) ;; + esac;; + esac;; + 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 this system. Sorry.";; + notincluded) + echo "Support for this system has not been included" + echo "in this distribution. Sorry.";; +esac + +orig_makext=$makext + +if [ -d "$ksrc" ]; then + echo "Creating links to Makefiles." + rm -f Makefile + ln -s $ksrc/Makefile.top Makefile + echo " Makefile -> $ksrc/Makefile.top" + if [ "$ksrc" = solaris ]; then + # Point to 64-bit Makefile extension + if [ "$lp64" = y ]; then + makext=$makext-64 + fi + rm -f $ksrc/Makefile + ln -s Makefile.$makext $ksrc/Makefile + echo " $ksrc/Makefile -> Makefile.$makext" + # Restore extension + if [ "$lp64" = y ]; then + makext=$orig_makext + fi + fi + for dir in pppd pppstats chat pppdump; do + rm -f $dir/Makefile + if [ -f $dir/Makefile.$makext ]; then + ln -s Makefile.$makext $dir/Makefile + echo " $dir/Makefile -> Makefile.$makext" + fi + done +fi diff --git a/mdk-stage1/ppp/contrib/pppgetpass/Makefile.linux b/mdk-stage1/ppp/contrib/pppgetpass/Makefile.linux new file mode 100644 index 000000000..7eb217dac --- /dev/null +++ b/mdk-stage1/ppp/contrib/pppgetpass/Makefile.linux @@ -0,0 +1,16 @@ +all: 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 diff --git a/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.8 b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.8 new file mode 100644 index 000000000..ade576970 --- /dev/null +++ b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.8 @@ -0,0 +1,18 @@ +.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) diff --git a/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.gtk.c b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.gtk.c new file mode 100644 index 000000000..48ca04202 --- /dev/null +++ b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.gtk.c @@ -0,0 +1,92 @@ +#include <glib.h> +#include <gdk/gdk.h> +#include <gtk/gtkwindow.h> +#include <gtk/gtkmain.h> +#include <gtk/gtkbutton.h> +#include <gtk/gtkvbox.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkentry.h> +#include <gtk/gtksignal.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> + +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; +} diff --git a/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.sh b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.sh new file mode 100644 index 000000000..09c480519 --- /dev/null +++ b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ -z "$DISPLAY" ]; then + exec pppgetpass.vt "$@" +else + exec pppgetpass.gtk "$@" +fi diff --git a/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.vt.c b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.vt.c new file mode 100644 index 000000000..a1520883c --- /dev/null +++ b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.vt.c @@ -0,0 +1,218 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <syslog.h> +#include <termios.h> +#include <sys/vt.h> + +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; +} diff --git a/mdk-stage1/ppp/etc.ppp/chap-secrets b/mdk-stage1/ppp/etc.ppp/chap-secrets new file mode 100644 index 000000000..7d1c3cd7c --- /dev/null +++ b/mdk-stage1/ppp/etc.ppp/chap-secrets @@ -0,0 +1,2 @@ +# Secrets for authentication using CHAP +# client server secret IP addresses diff --git a/mdk-stage1/ppp/etc.ppp/options b/mdk-stage1/ppp/etc.ppp/options new file mode 100644 index 000000000..0f77a9aa0 --- /dev/null +++ b/mdk-stage1/ppp/etc.ppp/options @@ -0,0 +1,5 @@ +lock +noauth +noipdefault +usepeerdns + diff --git a/mdk-stage1/ppp/etc.ppp/options.options b/mdk-stage1/ppp/etc.ppp/options.options new file mode 100644 index 000000000..4b67b6a0d --- /dev/null +++ b/mdk-stage1/ppp/etc.ppp/options.options @@ -0,0 +1 @@ +lock diff --git a/mdk-stage1/ppp/etc.ppp/pap-secrets b/mdk-stage1/ppp/etc.ppp/pap-secrets new file mode 100644 index 000000000..f8b7dce3c --- /dev/null +++ b/mdk-stage1/ppp/etc.ppp/pap-secrets @@ -0,0 +1,2 @@ +# Secrets for authentication using PAP +# client server secret IP addresses diff --git a/mdk-stage1/ppp/include/linux/if_ppp.h b/mdk-stage1/ppp/include/linux/if_ppp.h new file mode 100644 index 000000000..1febf0931 --- /dev/null +++ b/mdk-stage1/ppp/include/linux/if_ppp.h @@ -0,0 +1,155 @@ +/* $Id$ */ + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +/* + * ==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_ */ diff --git a/mdk-stage1/ppp/include/linux/if_pppvar.h b/mdk-stage1/ppp/include/linux/if_pppvar.h new file mode 100644 index 000000000..b2485cd9d --- /dev/null +++ b/mdk-stage1/ppp/include/linux/if_pppvar.h @@ -0,0 +1,138 @@ +/* From: if_pppvar.h,v 1.2 1995/06/12 11:36:51 paulus Exp */ +/* + * if_pppvar.h - private structures and declarations for PPP. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * ==FILEVERSION 990911== + * + * NOTE TO MAINTAINERS: + * If you modify this file at all, please set the above date. + * if_pppvar.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_pppvar.h + * file. Don't change the format of that line otherwise, so the + * installation script can recognize it. + */ + +/* + * Supported network protocols. These values are used for + * indexing sc_npmode. + */ + +#define NP_IP 0 /* Internet Protocol */ +#define NP_IPX 1 /* IPX protocol */ +#define NP_AT 2 /* Appletalk protocol */ +#define NP_IPV6 3 /* Internet Protocol */ +#define NUM_NP 4 /* Number of NPs. */ + +#define OBUFSIZE 256 /* # chars of output buffering */ + +/* + * Structure describing each ppp unit. + */ + +struct ppp { + int magic; /* magic value for structure */ + struct ppp *next; /* unit with next index */ + unsigned long inuse; /* are we allocated? */ + int line; /* network interface unit # */ + __u32 flags; /* miscellaneous control flags */ + int mtu; /* maximum xmit frame size */ + int mru; /* maximum receive frame size */ + struct slcompress *slcomp; /* for TCP header compression */ + struct sk_buff_head xmt_q; /* frames to send from pppd */ + struct sk_buff_head rcv_q; /* frames for pppd to read */ + unsigned long xmit_busy; /* bit 0 set when xmitter busy */ + + /* Information specific to using ppp on async serial lines. */ + struct tty_struct *tty; /* ptr to TTY structure */ + struct tty_struct *backup_tty; /* TTY to use if tty gets closed */ + __u8 escape; /* 0x20 if prev char was PPP_ESC */ + __u8 toss; /* toss this frame */ + volatile __u8 tty_pushing; /* internal state flag */ + volatile __u8 woke_up; /* internal state flag */ + __u32 xmit_async_map[8]; /* 1 bit means that given control + character is quoted on output*/ + __u32 recv_async_map; /* 1 bit means that given control + character is ignored on input*/ + __u32 bytes_sent; /* Bytes sent on frame */ + __u32 bytes_rcvd; /* Bytes recvd on frame */ + + /* Async transmission information */ + struct sk_buff *tpkt; /* frame currently being sent */ + int tpkt_pos; /* how much of it we've done */ + __u16 tfcs; /* FCS so far for it */ + unsigned char *optr; /* where we're up to in sending */ + unsigned char *olim; /* points past last valid char */ + + /* Async reception information */ + struct sk_buff *rpkt; /* frame currently being rcvd */ + __u16 rfcs; /* FCS so far of rpkt */ + + /* Queues for select() functionality */ + struct wait_queue *read_wait; /* queue for reading processes */ + + /* info for detecting idle channels */ + unsigned long last_xmit; /* time of last transmission */ + unsigned long last_recv; /* time last packet received */ + + /* Statistic information */ + struct pppstat stats; /* statistic information */ + + /* PPP compression protocol information */ + struct compressor *sc_xcomp; /* transmit compressor */ + void *sc_xc_state; /* transmit compressor state */ + struct compressor *sc_rcomp; /* receive decompressor */ + void *sc_rc_state; /* receive decompressor state */ + + enum NPmode sc_npmode[NUM_NP]; /* what to do with each NP */ + int sc_xfer; /* PID of reserved PPP table */ + char name[8]; /* space for unit name */ + struct device dev; /* net device structure */ + struct enet_statistics estats; /* more detailed stats */ + + /* tty output buffer */ + unsigned char obuf[OBUFSIZE]; /* buffer for characters to send */ +}; + +#define PPP_MAGIC 0x5002 +#define PPP_VERSION "2.3.11" diff --git a/mdk-stage1/ppp/include/linux/ppp-comp.h b/mdk-stage1/ppp/include/linux/ppp-comp.h new file mode 100644 index 000000000..3184d0731 --- /dev/null +++ b/mdk-stage1/ppp/include/linux/ppp-comp.h @@ -0,0 +1,203 @@ +/* + * ppp-comp.h - Definitions for doing PPP packet compression. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * ==FILEVERSION 980319== + * + * 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 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 */ + +#ifdef __KERNEL__ +extern int ppp_register_compressor(struct compressor *); +extern void ppp_unregister_compressor(struct compressor *); +#endif /* __KERNEL__ */ + +#endif /* _NET_PPP_COMP_H */ diff --git a/mdk-stage1/ppp/include/linux/ppp_defs.h b/mdk-stage1/ppp/include/linux/ppp_defs.h new file mode 100644 index 000000000..46946fbdc --- /dev/null +++ b/mdk-stage1/ppp/include/linux/ppp_defs.h @@ -0,0 +1,185 @@ +/* $Id$ */ + +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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. + */ + +/* + * ==FILEVERSION 20000114== + * + * 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_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_ */ diff --git a/mdk-stage1/ppp/include/net/if_ppp.h b/mdk-stage1/ppp/include/net/if_ppp.h new file mode 100644 index 000000000..1527ecf3f --- /dev/null +++ b/mdk-stage1/ppp/include/net/if_ppp.h @@ -0,0 +1,133 @@ +/* $Id$ */ + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#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_ */ diff --git a/mdk-stage1/ppp/include/net/ppp-comp.h b/mdk-stage1/ppp/include/net/ppp-comp.h new file mode 100644 index 000000000..0e6a9c672 --- /dev/null +++ b/mdk-stage1/ppp/include/net/ppp-comp.h @@ -0,0 +1,165 @@ +/* + * ppp-comp.h - Definitions for doing PPP packet compression. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +#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 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 */ diff --git a/mdk-stage1/ppp/include/net/ppp_defs.h b/mdk-stage1/ppp/include/net/ppp_defs.h new file mode 100644 index 000000000..c35020eab --- /dev/null +++ b/mdk-stage1/ppp/include/net/ppp_defs.h @@ -0,0 +1,184 @@ +/* $Id$ */ + +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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. + */ + +#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_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]) + +/* + * 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_ */ diff --git a/mdk-stage1/ppp/include/net/pppio.h b/mdk-stage1/ppp/include/net/pppio.h new file mode 100644 index 000000000..9db1ca9ab --- /dev/null +++ b/mdk-stage1/ppp/include/net/pppio.h @@ -0,0 +1,99 @@ +/* + * pppio.h - ioctl and other misc. definitions for STREAMS modules. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +#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 */ diff --git a/mdk-stage1/ppp/include/net/slcompress.h b/mdk-stage1/ppp/include/net/slcompress.h new file mode 100644 index 000000000..9e19bc0ed --- /dev/null +++ b/mdk-stage1/ppp/include/net/slcompress.h @@ -0,0 +1,148 @@ +/* + * Definitions for tcp compression routines. + * + * $Id$ + * + * 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_ */ diff --git a/mdk-stage1/ppp/include/net/vjcompress.h b/mdk-stage1/ppp/include/net/vjcompress.h new file mode 100644 index 000000000..c64596926 --- /dev/null +++ b/mdk-stage1/ppp/include/net/vjcompress.h @@ -0,0 +1,144 @@ +/* + * Definitions for tcp compression routines. + * + * $Id$ + * + * 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_ */ diff --git a/mdk-stage1/ppp/include/pcap-int.h b/mdk-stage1/ppp/include/pcap-int.h new file mode 100644 index 000000000..323994929 --- /dev/null +++ b/mdk-stage1/ppp/include/pcap-int.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1994, 1995, 1996 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that 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 Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory 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. + * + * @(#) $Header$ (LBL) + */ + +#ifndef pcap_int_h +#define pcap_int_h + +#include <pcap.h> + +/* + * Savefile + */ +struct pcap_sf { + FILE *rfile; + int swapped; + int version_major; + int version_minor; + u_char *base; +}; + +struct pcap_md { + struct pcap_stat stat; + /*XXX*/ + int use_bpf; + u_long TotPkts; /* can't oflow for 79 hrs on ether */ + u_long TotAccepted; /* count accepted by filter */ + u_long TotDrops; /* count of dropped packets */ + long TotMissed; /* missed by i/f during this run */ + long OrigMissed; /* missed by i/f before this run */ +#ifdef linux + int pad; + int skip; + char *device; +#endif +}; + +struct pcap { + int fd; + int snapshot; + int linktype; + int tzoff; /* timezone offset */ + int offset; /* offset for proper alignment */ + + struct pcap_sf sf; + struct pcap_md md; + + /* + * Read buffer. + */ + int bufsize; + u_char *buffer; + u_char *bp; + int cc; + + /* + * Place holder for pcap_next(). + */ + u_char *pkt; + + + /* + * Placeholder for filter code if bpf not in kernel. + */ + struct bpf_program fcode; + + char errbuf[PCAP_ERRBUF_SIZE]; +}; + +int yylex(void); + +#ifndef min +#define min(a, b) ((a) > (b) ? (b) : (a)) +#endif + +/* XXX should these be in pcap.h? */ +int pcap_offline_read(pcap_t *, int, pcap_handler, u_char *); +int pcap_read(pcap_t *, int cnt, pcap_handler, u_char *); + +/* Ultrix pads to make everything line up on a nice boundary */ +#if defined(ultrix) || defined(__alpha) +#define PCAP_FDDIPAD 3 +#endif + +/* XXX */ +extern int pcap_fddipad; +#endif diff --git a/mdk-stage1/ppp/linux/Makefile.top b/mdk-stage1/ppp/linux/Makefile.top new file mode 100644 index 000000000..fa34ce59f --- /dev/null +++ b/mdk-stage1/ppp/linux/Makefile.top @@ -0,0 +1,55 @@ +# PPP top-level Makefile for Linux. + + +BINDIR = $(DESTDIR)/usr/sbin +MANDIR = $(DESTDIR)/usr/man +ETCDIR = $(DESTDIR)/etc/ppp + +# uid 0 = root +INSTALL= install + +all: + cd chat; $(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-etcppp + +install-progs: + cd chat; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install + cd pppd; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install + cd pppstats; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install + cd pppdump; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install + +install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \ + $(ETCDIR)/chap-secrets + +$(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; $(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 diff --git a/mdk-stage1/ppp/modules/bsd-comp.c b/mdk-stage1/ppp/modules/bsd-comp.c new file mode 100644 index 000000000..b1b9325c7 --- /dev/null +++ b/mdk-stage1/ppp/modules/bsd-comp.c @@ -0,0 +1,1116 @@ +/* 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$ + */ + +#ifdef AIX4 +#include <net/net_globals.h> +#endif +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stream.h> +#include <net/ppp_defs.h> +#include "ppp_mod.h" + +#ifdef SVR4 +#include <sys/byteorder.h> +#ifndef _BIG_ENDIAN +#define BSD_LITTLE_ENDIAN +#endif +#endif + +#ifdef __osf__ +#undef FIRST +#undef LAST +#define BSD_LITTLE_ENDIAN +#endif + +#define PACKETPTR mblk_t * +#include <net/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_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 RATIO_MAX (0x7fffffff>>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 */ diff --git a/mdk-stage1/ppp/modules/deflate.c b/mdk-stage1/ppp/modules/deflate.c new file mode 100644 index 000000000..1b9054412 --- /dev/null +++ b/mdk-stage1/ppp/modules/deflate.c @@ -0,0 +1,760 @@ +/* + * 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 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +#ifdef AIX4 +#include <net/net_globals.h> +#endif +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stream.h> +#include <net/ppp_defs.h> +#include "ppp_mod.h" + +#define PACKETPTR mblk_t * +#include <net/ppp-comp.h> + +#ifdef __osf__ +#include "zlib.h" +#else +#include "../common/zlib.h" +#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 */ diff --git a/mdk-stage1/ppp/modules/if_ppp.c b/mdk-stage1/ppp/modules/if_ppp.c new file mode 100644 index 000000000..14e89eb4a --- /dev/null +++ b/mdk-stage1/ppp/modules/if_ppp.c @@ -0,0 +1,865 @@ +/* + * if_ppp.c - a network interface connected to a STREAMS module. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under SunOS 4 and Digital UNIX. + * + * This file provides the glue between PPP and IP. + */ + +#define INET 1 + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/netisr.h> +#include <net/ppp_defs.h> +#include <net/pppio.h> +#include <netinet/in.h> +#include <netinet/in_var.h> +#ifdef __osf__ +#include <sys/ioctl.h> +#include <net/if_types.h> +#else +#include <sys/sockio.h> +#endif +#include "ppp_mod.h" + +#include <sys/stream.h> + +#ifdef SNIT_SUPPORT +#include <sys/time.h> +#include <net/nit_if.h> +#include <netinet/if_ether.h> +#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 <net/route.h> +#include <netinet/in_pcb.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/tcp_timer.h> +#include <netinet/tcp_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> + +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__ */ diff --git a/mdk-stage1/ppp/modules/ppp.c b/mdk-stage1/ppp/modules/ppp.c new file mode 100644 index 000000000..44bf08dff --- /dev/null +++ b/mdk-stage1/ppp/modules/ppp.c @@ -0,0 +1,2486 @@ +/* + * ppp.c - STREAMS multiplexing pseudo-device driver for PPP. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/errno.h> +#ifdef __osf__ +#include <sys/ioctl.h> +#include <sys/cmn_err.h> +#define queclass(mp) ((mp)->b_band & QPCTL) +#else +#include <sys/ioccom.h> +#endif +#include <sys/time.h> +#ifdef SVR4 +#include <sys/cmn_err.h> +#include <sys/conf.h> +#include <sys/dlpi.h> +#include <sys/ddi.h> +#ifdef SOL2 +#include <sys/ksynch.h> +#include <sys/kstat.h> +#include <sys/sunddi.h> +#include <sys/ethernet.h> +#else +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <netinet/in.h> +#endif /* SOL2 */ +#else /* not SVR4 */ +#include <sys/user.h> +#endif /* SVR4 */ +#include <net/ppp_defs.h> +#include <net/pppio.h> +#include "ppp_mod.h" + +/* + * Modifications marked with #ifdef PRIOQ are for priority queueing of + * interactive traffic, and are due to Marko Zec <zec@japa.tel.fer.hr>. + */ +#ifdef PRIOQ +#endif /* PRIOQ */ + +#include <netinet/in.h> /* 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 <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> + +#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 */ + diff --git a/mdk-stage1/ppp/modules/ppp_ahdlc.c b/mdk-stage1/ppp/modules/ppp_ahdlc.c new file mode 100644 index 000000000..d0b961258 --- /dev/null +++ b/mdk-stage1/ppp/modules/ppp_ahdlc.c @@ -0,0 +1,878 @@ +/* + * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC. + * + * Re-written by Adi Masputra <adi.masputra@sun.com>, 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 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX. + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stream.h> +#include <sys/errno.h> + +#ifdef SVR4 +#include <sys/conf.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> +#include <sys/ddi.h> +#else +#include <sys/user.h> +#ifdef __osf__ +#include <sys/cmn_err.h> +#endif +#endif /* SVR4 */ + +#include <net/ppp_defs.h> +#include <net/pppio.h> +#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); + freemsg(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; + ushort_t fcs; +#if defined(SOL2) + mblk_t *zmp; +#endif /* SOL2 */ + +#if defined(SOL2) + /* + * In case the driver (or something below) doesn't send + * data upstream in one message block, concatenate everything + */ + if (!((mp->b_wptr - mp->b_rptr == msgdsize(mp)) && + ((intpointer_t)mp->b_rptr % sizeof(intpointer_t) == 0))) { + + zmp = msgpullup(mp, -1); + freemsg(mp); + mp = zmp; + if (mp == 0) + return; + } +#endif /* SOL2 */ + + 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 (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]; +} diff --git a/mdk-stage1/ppp/modules/ppp_comp.c b/mdk-stage1/ppp/modules/ppp_comp.c new file mode 100644 index 000000000..f6eef5ab1 --- /dev/null +++ b/mdk-stage1/ppp/modules/ppp_comp.c @@ -0,0 +1,1126 @@ +/* + * ppp_comp.c - STREAMS module for kernel-level compression and CCP support. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/stream.h> + +#ifdef SVR4 +#include <sys/conf.h> +#include <sys/cmn_err.h> +#include <sys/ddi.h> +#else +#include <sys/user.h> +#ifdef __osf__ +#include <sys/cmn_err.h> +#endif +#endif /* SVR4 */ + +#include <net/ppp_defs.h> +#include <net/pppio.h> +#include "ppp_mod.h" + +#ifdef __osf__ +#include <sys/mbuf.h> +#include <sys/protosw.h> +#endif + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <net/vjcompress.h> + +#define PACKETPTR mblk_t * +#include <net/ppp-comp.h> + +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]; +} diff --git a/mdk-stage1/ppp/modules/ppp_mod.h b/mdk-stage1/ppp/modules/ppp_mod.h new file mode 100644 index 000000000..f0af00886 --- /dev/null +++ b/mdk-stage1/ppp/modules/ppp_mod.h @@ -0,0 +1,190 @@ +/* + * Miscellaneous definitions for PPP STREAMS modules. + */ + +/* + * Macros for allocating and freeing kernel memory. + */ +#ifdef SVR4 /* SVR4, including Solaris 2 */ +#include <sys/kmem.h> +#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 <sys/kmem_alloc.h> /* 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 <sys/malloc.h> + +/* 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 <sys/proc.h> +#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 <sys/strlog.h> +#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 */ diff --git a/mdk-stage1/ppp/modules/vjcompress.c b/mdk-stage1/ppp/modules/vjcompress.c new file mode 100644 index 000000000..809b87231 --- /dev/null +++ b/mdk-stage1/ppp/modules/vjcompress.c @@ -0,0 +1,587 @@ +/* + * 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$ + */ + +#include <sys/types.h> +#include <sys/param.h> + +#ifdef SVR4 +#ifndef __GNUC__ +#include <sys/byteorder.h> /* 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 <net/net_globals.h> +#endif +#include <netinet/in.h> + +#ifdef AIX4 +#define _NETINET_IN_SYSTM_H_ +typedef u_long n_long; +#else +#include <netinet/in_systm.h> +#endif + +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include <net/ppp_defs.h> +#include <net/vjcompress.h> + +#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); +} diff --git a/mdk-stage1/ppp/pppd/Makefile.linux b/mdk-stage1/ppp/pppd/Makefile.linux new file mode 100644 index 000000000..47d6ba01e --- /dev/null +++ b/mdk-stage1/ppp/pppd/Makefile.linux @@ -0,0 +1,129 @@ +# +# pppd makefile for Linux +# $Id$ +# + +# Default installation locations +BINDIR = $(DESTDIR)/usr/sbin +MANDIR = $(DESTDIR)/usr/man + +PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \ + ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \ + demand.c utils.c multilink.c tdb.c tty.c +HEADERS = callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \ + ipxcp.h cbcp.h tdb.h +MANPAGES = pppd.8 +PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \ + auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \ + tdb.o tty.o + +all: pppd + +# +# include dependancies if present and backup if as a header file +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +CC = gcc +# +COPTS = -Wall $(RPM_OPT_FLAGS) +LIBS = -lutil + +ifneq ($(wildcard /usr/lib/libcrypt.*),) +LIBS += -lcrypt +endif + +# Uncomment the next 2 lines to include support for Microsoft's +# MS-CHAP authentication protocol. +CHAPMS=y +USE_CRYPT=y +ifneq ($(wildcard /usr/lib/libcrypt.*),) +HAVE_CRYPT_H=y +endif + +# 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, which it +# doesn't yet. +#FILTER=y + +HAS_SHADOW=y +USE_PAM=y +#HAVE_INET6=y + +PLUGIN=y + +INCLUDE_DIRS= -I../include + +COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK -DHAVE_MMAP + +CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) + +ifdef CHAPMS +CFLAGS += -DCHAPMS=1 +ifndef USE_CRYPT +LIBS := -ldes $(LIBS) +else +CFLAGS += -DUSE_CRYPT=1 +ifneq ($(wildcard /usr/include/crypt.h),) +CFLAGS += -DHAVE_CRYPT_H=1 +endif +endif +PPPDOBJS += md4.o chap_ms.o +ifdef MSLANMAN +CFLAGS += -DMSLANMAN=1 +endif +endif + +ifdef HAS_SHADOW +CFLAGS += -DHAS_SHADOW +#LIBS := -lshadow $(LIBS) +endif + +# For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/. +ifdef USE_PAM +CFLAGS += -DUSE_PAM +LIBS := -lpam -ldl $(LIBS) +endif + +# Lock library binary for Linux is included in 'linux' subdirectory. +ifdef LOCKLIB +LIBS := -llock $(LIBS) +CFLAGS += -DLOCKLIB=1 +endif + +ifdef PLUGIN +CFLAGS += -DPLUGIN +LDFLAGS += -Wl,-E +LIBS += -ldl +endif + +ifdef FILTER +LIBS += -lpcap +CFLAGS += -DPPP_FILTER -I/usr/include/pcap +endif + +ifdef HAVE_INET6 + PPPDSRCS += ipv6cp.c eui64.c + HEADERS += ipv6cp.h eui64.h + PPPDOBJS += ipv6cp.o eui64.o + CFLAGS += -DINET6=1 +endif + + +INSTALL= install + +install: pppd + mkdir -p $(BINDIR) $(MANDIR) + $(INSTALL) -m 555 pppd $(BINDIR)/pppd + $(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8 + +pppd: $(PPPDOBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS) + +clean: + rm -f $(PPPDOBJS) pppd *~ #* core + +depend: + $(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend diff --git a/mdk-stage1/ppp/pppd/Makefile.linux.make b/mdk-stage1/ppp/pppd/Makefile.linux.make new file mode 100644 index 000000000..d98a32562 --- /dev/null +++ b/mdk-stage1/ppp/pppd/Makefile.linux.make @@ -0,0 +1,131 @@ +# +# pppd makefile for Linux +# $Id$ +# + +# Default installation locations +BINDIR = /usr/sbin +MANDIR = /usr/man + +PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \ + ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \ + demand.c utils.c multilink.c tdb.c tty.c +HEADERS = callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \ + ipxcp.h cbcp.h tdb.h +MANPAGES = pppd.8 +PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \ + auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \ + tdb.o tty.o + +all: pppd + +# +# include dependancies if present and backup if as a header file +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +# CC = gcc +# +COPTS = -O2 -pipe -Wall -g +LIBS = + +ifneq ($(wildcard /usr/lib/libcrypt.*),) +LIBS += -lcrypt +endif + +# Uncomment the next 2 lines to include support for Microsoft's +# MS-CHAP authentication protocol. +CHAPMS=y +USE_CRYPT=y +ifneq ($(wildcard /usr/lib/libcrypt.*),) +HAVE_CRYPT_H=y +endif + +# 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, which it +# doesn't yet. +#FILTER=y + +HAS_SHADOW=y +#USE_PAM=y +#HAVE_INET6=y + +PLUGIN=y + +INCLUDE_DIRS= -I../include + +COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK -DHAVE_MMAP + +CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) + +ifdef CHAPMS +CFLAGS += -DCHAPMS=1 +ifndef USE_CRYPT +LIBS := -ldes $(LIBS) +else +CFLAGS += -DUSE_CRYPT=1 +ifneq ($(wildcard /usr/include/crypt.h),) +CFLAGS += -DHAVE_CRYPT_H=1 +endif +endif +PPPDOBJS += md4.o chap_ms.o +ifdef MSLANMAN +CFLAGS += -DMSLANMAN=1 +endif +endif + +ifdef HAS_SHADOW +CFLAGS += -DHAS_SHADOW +#LIBS := -lshadow $(LIBS) +endif + +# For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/. +ifdef USE_PAM +CFLAGS += -DUSE_PAM +LIBS := -lpam -ldl $(LIBS) +endif + +# Lock library binary for Linux is included in 'linux' subdirectory. +ifdef LOCKLIB +LIBS := -llock $(LIBS) +CFLAGS += -DLOCKLIB=1 +endif + +ifdef PLUGIN +CFLAGS += -DPLUGIN +LDFLAGS += -Wl,-E +LIBS += -ldl +endif + +ifdef FILTER +LIBS += -lpcap +CFLAGS += -DPPP_FILTER -I/usr/include/pcap +endif + +ifdef HAVE_INET6 + PPPDSRCS += ipv6cp.c eui64.c + HEADERS += ipv6cp.h eui64.h + PPPDOBJS += ipv6cp.o eui64.o + CFLAGS += -DINET6=1 +endif + + +INSTALL= install -o root + +install: pppd + mkdir -p $(BINDIR) $(MANDIR) + $(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)/man8 + +pppd: $(PPPDOBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS) + +clean: + rm -f $(PPPDOBJS) pppd *~ #* core + +depend: + $(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend diff --git a/mdk-stage1/ppp/pppd/Makefile.linux.makeopt b/mdk-stage1/ppp/pppd/Makefile.linux.makeopt new file mode 100644 index 000000000..3094c941c --- /dev/null +++ b/mdk-stage1/ppp/pppd/Makefile.linux.makeopt @@ -0,0 +1,129 @@ +# +# pppd makefile for Linux +# $Id$ +# + +# Default installation locations +BINDIR = $(DESTDIR)/usr/sbin +MANDIR = $(DESTDIR)/usr/man + +PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \ + ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \ + demand.c utils.c multilink.c tdb.c tty.c +HEADERS = callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \ + ipxcp.h cbcp.h tdb.h +MANPAGES = pppd.8 +PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \ + auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \ + tdb.o tty.o + +all: pppd + +# +# include dependancies if present and backup if as a header file +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +CC = gcc +# +COPTS = -O2 -pipe -Wall -g +LIBS = -lutil + +ifneq ($(wildcard /usr/lib/libcrypt.*),) +LIBS += -lcrypt +endif + +# Uncomment the next 2 lines to include support for Microsoft's +# MS-CHAP authentication protocol. +CHAPMS=y +USE_CRYPT=y +ifneq ($(wildcard /usr/lib/libcrypt.*),) +HAVE_CRYPT_H=y +endif + +# 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, which it +# doesn't yet. +#FILTER=y + +HAS_SHADOW=y +USE_PAM=y +#HAVE_INET6=y + +PLUGIN=y + +INCLUDE_DIRS= -I../include + +COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK -DHAVE_MMAP + +CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) + +ifdef CHAPMS +CFLAGS += -DCHAPMS=1 +ifndef USE_CRYPT +LIBS := -ldes $(LIBS) +else +CFLAGS += -DUSE_CRYPT=1 +ifneq ($(wildcard /usr/include/crypt.h),) +CFLAGS += -DHAVE_CRYPT_H=1 +endif +endif +PPPDOBJS += md4.o chap_ms.o +ifdef MSLANMAN +CFLAGS += -DMSLANMAN=1 +endif +endif + +ifdef HAS_SHADOW +CFLAGS += -DHAS_SHADOW +#LIBS := -lshadow $(LIBS) +endif + +# For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/. +ifdef USE_PAM +CFLAGS += -DUSE_PAM +LIBS := -lpam -ldl $(LIBS) +endif + +# Lock library binary for Linux is included in 'linux' subdirectory. +ifdef LOCKLIB +LIBS := -llock $(LIBS) +CFLAGS += -DLOCKLIB=1 +endif + +ifdef PLUGIN +CFLAGS += -DPLUGIN +LDFLAGS += -Wl,-E +LIBS += -ldl +endif + +ifdef FILTER +LIBS += -lpcap +CFLAGS += -DPPP_FILTER -I/usr/include/pcap +endif + +ifdef HAVE_INET6 + PPPDSRCS += ipv6cp.c eui64.c + HEADERS += ipv6cp.h eui64.h + PPPDOBJS += ipv6cp.o eui64.o + CFLAGS += -DINET6=1 +endif + + +INSTALL= install + +install: pppd + mkdir -p $(BINDIR) $(MANDIR) + $(INSTALL) -m 555 pppd $(BINDIR)/pppd + $(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8 + +pppd: $(PPPDOBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS) + +clean: + rm -f $(PPPDOBJS) pppd *~ #* core + +depend: + $(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend diff --git a/mdk-stage1/ppp/pppd/Makefile.sol2 b/mdk-stage1/ppp/pppd/Makefile.sol2 new file mode 100644 index 000000000..dfdcddd97 --- /dev/null +++ b/mdk-stage1/ppp/pppd/Makefile.sol2 @@ -0,0 +1,48 @@ +# +# Makefile for pppd under Solaris 2. +# $Id$ +# + +include ../solaris/Makedefs + +COPTS += -xO2 -xspace -W0,-Lt +CFLAGS = -I../include -DSVR4 -DSOL2 $(COPTS) +LIBS = -lsocket -lnsl + +OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o tty.o \ + ccp.o auth.o options.o demand.o utils.o sys-solaris.o tdb.o + +# +# uncomment the following to enable 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 + +# +# 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 diff --git a/mdk-stage1/ppp/pppd/Makefile.sunos4 b/mdk-stage1/ppp/pppd/Makefile.sunos4 new file mode 100644 index 000000000..694ac341f --- /dev/null +++ b/mdk-stage1/ppp/pppd/Makefile.sunos4 @@ -0,0 +1,26 @@ +# +# Makefile for pppd under SunOS 4. +# $Id$ +# + +include ../sunos4/Makedefs + +LIBS = + +CFLAGS = $(COPTS) -I../include -DSUNOS4 -DGIDSET_TYPE=int \ + -DLOCK_DIR=\"/usr/spool/locks\" + +all: pppd + +OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \ + auth.o options.o demand.o utils.o sys-sunos4.o tty.o + +pppd: $(OBJS) + $(CC) -o pppd $(OBJS) $(LIBS) + +install: + $(INSTALL) -c -m 4555 pppd $(BINDIR)/pppd + $(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8/pppd.8 + +clean: + rm -f $(OBJS) pppd *~ core diff --git a/mdk-stage1/ppp/pppd/auth.c b/mdk-stage1/ppp/pppd/auth.c new file mode 100644 index 000000000..c1912c252 --- /dev/null +++ b/mdk-stage1/ppp/pppd/auth.c @@ -0,0 +1,1952 @@ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <utmp.h> +#include <fcntl.h> +#if defined(_PATH_LASTLOG) && defined(_linux_) +#include <lastlog.h> +#endif + +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#ifdef USE_PAM +#include <security/pam_appl.h> +#endif + +#ifdef HAS_SHADOW +#include <shadow.h> +#ifndef PW_PPP +#define PW_PPP PW_LOGIN +#endif +#endif + +#include "pppd.h" +#include "fsm.h" +#include "lcp.h" +#include "ipcp.h" +#include "upap.h" +#include "chap.h" +#ifdef CBCP_SUPPORT +#include "cbcp.h" +#endif +#include "pathnames.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]; + +/* Set if we have successfully called plogin() */ +static int logged_in; + +/* 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; + +/* 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; + +/* + * 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; + +static int used_login; /* peer authenticated against login database */ + +/* + * Option variables. + */ +bool uselogin = 0; /* Use /etc/passwd for checking PAP */ +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 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 */ +char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ + +static char *uafname; /* name of most recent +ua file */ + +/* Bits in auth_pending[] */ +#define PAP_WITHPEER 1 +#define PAP_PEER 2 +#define CHAP_WITHPEER 4 +#define CHAP_PEER 8 + +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 plogin __P((char *, char *, char **)); +static void plogout __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 ip_addr_check __P((u_int32_t, struct permitted_ip *)); +static int scan_authfile __P((FILE *, char *, char *, char *, + struct wordlist **, struct wordlist **, + char *)); +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 void check_access __P((FILE *, char *)); +static int wordlist_count __P((struct wordlist *)); + +/* + * 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, &lcp_wantoptions[0].neg_chap, + "Require CHAP authentication from peer", + OPT_PRIOSUB | 1, &auth_required }, + { "+chap", o_bool, &lcp_wantoptions[0].neg_chap, + "Require CHAP authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required }, + + { "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", 1 }, + { "-chap", o_bool, &refuse_chap, + "Don't allow CHAP authentication with peer", OPT_ALIAS | 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, NULL, MAXNAMELEN }, + + { "password", o_string, passwd, + "Password for authenticating us to the peer", + OPT_PRIO | OPT_STATIC | OPT_HIDE, NULL, 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", 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 }, + + { NULL } +}; + +/* + * setupapfile - specifies UPAP info for authenticating with peer. + */ +static int +setupapfile(argv) + char **argv; +{ + FILE *ufile; + int l; + 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"); + seteuid(getuid()); + ufile = fopen(fname, "r"); + seteuid(0); + 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){ + 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)); + if (override_value("passwd", option_priority, fname)) + strlcpy(passwd, p, sizeof(passwd)); + + 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; +} + + +/* + * An Open on LCP has requested a change from Dead to Establish phase. + * Do what's necessary to bring the physical layer up. + */ +void +link_required(unit) + int unit; +{ +} + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void +link_terminated(unit) + int unit; +{ + if (phase == PHASE_DEAD) + return; + if (pap_logout_hook) { + pap_logout_hook(); + } else { + if (logged_in) + plogout(); + } + new_phase(PHASE_DEAD); + notice("Connection terminated."); +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void +link_down(unit) + int unit; +{ + int i; + struct protent *protp; + + 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); + } + 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; + if (phase != PHASE_DEAD) + new_phase(PHASE_TERMINATE); +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void +link_established(unit) + int unit; +{ + int auth; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ho = &lcp_hisoptions[unit]; + int i; + struct protent *protp; + + /* + * Tell higher-level protocols that LCP is up. + */ + 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 && !(go->neg_chap || go->neg_upap)) { + /* + * 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"); + lcp_close(unit, "peer refused to authenticate"); + status = EXIT_PEER_AUTH_FAILED; + return; + } + } + + new_phase(PHASE_AUTHENTICATE); + used_login = 0; + auth = 0; + if (go->neg_chap) { + ChapAuthPeer(unit, our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } else if (go->neg_upap) { + upap_authpeer(unit); + auth |= PAP_PEER; + } + if (ho->neg_chap) { + ChapAuthWithPeer(unit, user, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } else if (ho->neg_upap) { + if (passwd[0] == 0) { + 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; + + if (!auth) + network_phase(unit); +} + +/* + * Proceed to the network phase. + */ +static void +network_phase(unit) + int unit; +{ + lcp_options *go = &lcp_gotoptions[unit]; + + /* + * If the peer had to authenticate, run the auth-up script now. + */ + if (go->neg_chap || go->neg_upap) { + 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(); +} + +void +start_networks() +{ + int i; + struct protent *protp; + + new_phase(PHASE_NETWORK); + +#ifdef HAVE_MULTILINK + if (multilink) { + if (mp_join_bundle()) { + if (updetach && !nodetach) + detach(); + return; + } + } +#endif /* HAVE_MULTILINK */ + +#ifdef PPP_FILTER + if (!demand) + set_filters(&pass_filter, &active_filter); +#endif + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol < 0xC000 && protp->enabled_flag + && protp->open != NULL) { + (*protp->open)(0); + if (protp->protocol != PPP_CCP) + ++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 + */ + lcp_close(unit, "Authentication failed"); + status = EXIT_PEER_AUTH_FAILED; +} + +/* + * The peer has been successfully authenticated using `protocol'. + */ +void +auth_peer_success(unit, protocol, name, namelen) + int unit, protocol; + char *name; + int namelen; +{ + int bit; + + switch (protocol) { + case PPP_CHAP: + bit = CHAP_PEER; + break; + case PPP_PAP: + bit = PAP_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); + + /* + * 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. + */ + lcp_close(unit, "Failed to authenticate ourselves to peer"); + status = EXIT_AUTH_TOPEER_FAILED; +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void +auth_withpeer_success(unit, protocol) + int unit, protocol; +{ + int bit; + + switch (protocol) { + case PPP_CHAP: + bit = CHAP_WITHPEER; + break; + case PPP_PAP: + if (passwd_from_file) + BZERO(passwd, MAXSECRETLEN); + bit = PAP_WITHPEER; + break; + default: + warn("auth_withpeer_success: unknown protocol %x", protocol); + bit = 0; + } + + /* + * 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); + + /* + * 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); + 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"); + } +} + +/* + * 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."); + lcp_close(0, "Link inactive"); + need_holdoff = 0; + status = EXIT_IDLE_TIMEOUT; + } 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"); + lcp_close(0, "Connect time expired"); /* Close connection */ + status = EXIT_CONNECT_TIME; +} + +/* + * 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 (user[0] == 0) + 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 authentication is required, ask peer for CHAP or PAP. */ + if (auth_required) { + allow_any_ip = 0; + if (!wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + } else { + wo->neg_chap = 0; + wo->neg_upap = 0; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. + */ + lacks_ip = 0; + can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip)); + if (!can_auth && wo->neg_chap) { + can_auth = have_chap_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); + } +} + +/* + * 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[0]; + + ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL)); + ao->neg_chap = !refuse_chap + && (passwd[0] != 0 + || have_chap_secret(user, (explicit_remote? remote_name: NULL), + 0, NULL)); + + if (go->neg_upap && !uselogin && !have_pap_secret(NULL)) + go->neg_upap = 0; + if (go->neg_chap) { + if (!have_chap_secret((explicit_remote? remote_name: NULL), + our_name, 1, NULL)) + go->neg_chap = 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) { + if (ret) + set_allowed_addrs(unit, addrs, opts); + BZERO(passwd, sizeof(passwd)); + if (addrs != 0) + free_wordlist(addrs); + 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) { + 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 */ + ret = plogin(user, passwd, msg); + if (ret == UPAP_AUTHNAK) + warn("PAP login failure for %s", user); + else + used_login = 1; + } + 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; + warn("PAP authentication failure for %s", user); + } + } + } + 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; +} + +/* + * This function is needed for PAM. + */ + +#ifdef USE_PAM +/* Static variables used to communicate between the conversation function + * and the server_login function + */ +static char *PAM_username; +static char *PAM_password; +static int PAM_error = 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 PAM_conv (int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + int replies = 0; + struct pam_response *reply = NULL; + +#define COPY_STRING(s) (s) ? strdup(s) : 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); + PAM_error = 1; + return PAM_CONV_ERR; + } + } + *resp = reply; + return PAM_SUCCESS; +} + +static struct pam_conv PAM_conversation = { + &PAM_conv, + NULL +}; +#endif /* USE_PAM */ + +/* + * plogin - Check the user name and password against the system + * password database, and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Login failed. + * UPAP_AUTHACK: Login succeeded. + * In either case, msg points to an appropriate message. + */ + +static int +plogin(user, passwd, msg) + char *user; + char *passwd; + char **msg; +{ + char *tty; + +#ifdef USE_PAM + int pam_error; + + pam_error = pam_start ("ppp", user, &PAM_conversation, &pamh); + if (pam_error != PAM_SUCCESS) { + *msg = (char *) pam_strerror (pamh, pam_error); + reopen_log(); + return UPAP_AUTHNAK; + } + /* + * Define the fields for the credential validation + */ + + PAM_username = user; + PAM_password = passwd; + PAM_error = 0; + pam_set_item (pamh, PAM_TTY, devnam); /* this might be useful to some modules */ + + /* + * Validate the user + */ + pam_error = pam_authenticate (pamh, PAM_SILENT); + if (pam_error == PAM_SUCCESS && !PAM_error) { + pam_error = pam_acct_mgmt (pamh, PAM_SILENT); + if (pam_error == PAM_SUCCESS) + pam_error = pam_open_session (pamh, PAM_SILENT); + } + + *msg = (char *) pam_strerror (pamh, pam_error); + + /* + * Clean up the mess + */ + reopen_log(); /* apparently the PAM stuff does closelog() */ + PAM_username = NULL; + PAM_password = NULL; + if (pam_error != PAM_SUCCESS) + return UPAP_AUTHNAK; +#else /* #ifdef USE_PAM */ + +/* + * Use the non-PAM methods directly + */ + +#ifdef HAS_SHADOW + struct spwd *spwd; + struct spwd *getspnam(); +#endif + struct passwd *pw = getpwnam(user); + + endpwent(); + if (pw == NULL) + return (UPAP_AUTHNAK); + +#ifdef HAS_SHADOW + spwd = getspnam(user); + endspent(); + if (spwd) { + /* check the age of the password entry */ + long 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 (UPAP_AUTHNAK); + } + pw->pw_passwd = spwd->sp_pwdp; + } +#endif + + /* + * If no passwd, don't let them login. + */ + if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2 + || strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0) + return (UPAP_AUTHNAK); + +#endif /* #ifdef USE_PAM */ + + /* + * Write a wtmp entry for this user. + */ + + tty = devnam; + if (strncmp(tty, "/dev/", 5) == 0) + tty += 5; + logwtmp(tty, user, remote_name); /* Add wtmp login entry */ + +#if defined(_PATH_LASTLOG) && !defined(USE_PAM) + if (pw != (struct passwd *)NULL) { + struct lastlog ll; + int fd; + + 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(&ll.ll_time); + (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); + (void)write(fd, (char *)&ll, sizeof(ll)); + (void)close(fd); + } + } +#endif /* _PATH_LASTLOG and not USE_PAM */ + + info("user %s logged in", user); + logged_in = 1; + + return (UPAP_AUTHACK); +} + +/* + * plogout - Logout the user. + */ +static void +plogout() +{ +#ifdef USE_PAM + int pam_error; + + if (pamh != NULL) { + pam_error = pam_close_session (pamh, PAM_SILENT); + pam_end (pamh, pam_error); + pamh = NULL; + } + /* Apparently the pam stuff does closelog(). */ + reopen_log(); +#else /* ! USE_PAM */ + char *tty; + + tty = devnam; + if (strncmp(tty, "/dev/", 5) == 0) + tty += 5; + logwtmp(tty, "", ""); /* Wipe out utmp logout entry */ +#endif /* ! USE_PAM */ + logged_in = 0; +} + + +/* + * 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]; + + /* + * Open the file of pap secrets and scan for a suitable secret. + */ + 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); + ret = i >= 0 && secret[0] == 0; + BZERO(secret, sizeof(secret)); + + if (ret) + set_allowed_addrs(unit, addrs, opts); + else if (opts != 0) + free_wordlist(opts); + if (addrs != 0) + free_wordlist(addrs); + + fclose(f); + 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); + 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); + 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; + + 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); + 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 { + 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); + 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; +} + +/* + * 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 (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; +} + +/* + * 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. + */ +static int +scan_authfile(f, client, server, secret, addrs, opts, filename) + FILE *f; + char *client; + char *server; + char *secret; + struct wordlist **addrs; + struct wordlist **opts; + char *filename; +{ + 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]; + + 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; + + 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); +} diff --git a/mdk-stage1/ppp/pppd/cbcp.c b/mdk-stage1/ppp/pppd/cbcp.c new file mode 100644 index 000000000..8c57b3cde --- /dev/null +++ b/mdk-stage1/ppp/pppd/cbcp.c @@ -0,0 +1,456 @@ +/* + * cbcp - Call Back Configuration Protocol. + * + * Copyright (c) 1995 Pedro Roque Marques + * 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 Pedro Roque Marques. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> + +#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, 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, 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, char *pckt, int len)); +static void cbcp_send __P((cbcp_state *us, u_char 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) { + error("CBCP packet is too small"); + return; + } + + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + +#if 0 + if (len > pktlen) { + error("CBCP packet: invalid length"); + return; + } +#endif + + len -= CBCP_MINLEN; + + switch(code) { + case CBCP_REQ: + us->us_id = id; + cbcp_recvreq(us, inp, len); + break; + + case CBCP_RESP: + dbglog("CBCP_RESP received"); + break; + + case CBCP_ACK: + if (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; + char *pckt; + int pcktlen; +{ + u_char type, opt_len, delay, addr_type; + char address[256]; + int len = pcktlen; + + address[0] = 0; + + while (len) { + dbglog("length: %d", len); + + GETCHAR(type, pckt); + GETCHAR(opt_len, pckt); + + 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; + } + + 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; + + 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"); + PUTCHAR(CB_CONF_USER, bufp); + len = 3 + 1 + strlen(us->us_number) + 1; + PUTCHAR(len , bufp); + PUTCHAR(5, bufp); /* delay */ + PUTCHAR(1, bufp); + BCOPY(us->us_number, bufp, strlen(us->us_number) + 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 = 3; + PUTCHAR(len , bufp); + PUTCHAR(0, bufp); + cbcp_send(us, CBCP_RESP, buf, len); + start_networks(); + return; + } +} + +static void +cbcp_send(us, code, buf, len) + cbcp_state *us; + u_char 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; + char *pckt; + int len; +{ + u_char type, delay, addr_type; + int opt_len; + char address[256]; + + if (len) { + GETCHAR(type, pckt); + GETCHAR(opt_len, pckt); + + 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); +} + +/* ok peer will do callback */ +static void +cbcp_up(us) + cbcp_state *us; +{ + persist = 0; + lcp_close(0, "Call me back, please"); + status = EXIT_CALLBACK; +} diff --git a/mdk-stage1/ppp/pppd/cbcp.h b/mdk-stage1/ppp/pppd/cbcp.h new file mode 100644 index 000000000..c2ab3f689 --- /dev/null +++ b/mdk-stage1/ppp/pppd/cbcp.h @@ -0,0 +1,26 @@ +#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 diff --git a/mdk-stage1/ppp/pppd/ccp.c b/mdk-stage1/ppp/pppd/ccp.c new file mode 100644 index 000000000..7e36e384e --- /dev/null +++ b/mdk-stage1/ppp/pppd/ccp.c @@ -0,0 +1,1257 @@ +/* + * ccp.c - PPP Compression Control Protocol. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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. + */ + +#define RCSID "$Id$" + +#include <stdlib.h> +#include <string.h> + +#include "pppd.h" +#include "fsm.h" +#include "ccp.h" +#include <net/ppp-comp.h> + +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]; + +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", 1, &ccp_allowoptions[0].predictor_1, OPT_PRIO }, + { "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 }, + + { 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)); +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) + +/* + * 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."); + + /* + * 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]); +} + +/* + * 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[16]; + + *go = ccp_wantoptions[f->unit]; + all_rejected[f->unit] = 0; + + /* + * Check whether the kernel knows about the various + * compression methods we might request. + */ + 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); +} + +/* + * 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. + */ + 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; + for (;;) { + res = ccp_test(f->unit, p, CILEN_DEFLATE, 0); + if (res > 0) { + p += CILEN_DEFLATE; + break; + } + if (res < 0 || go->deflate_size <= DEFLATE_MIN_WORKS) { + 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 (;;) { + res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0); + if (res > 0) { + p += CILEN_BSD_COMPRESS; + break; + } + if (res < 0 || go->bsd_bits <= BSD_MIN_BITS) { + 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; + + 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) + fsm *f; + u_char *p; + int len; +{ + 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; + + 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; + + if (go->deflate && len >= CILEN_DEFLATE + && p[0] == (go->deflate_correct? CI_DEFLATE: 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 */ + if (go->deflate_correct) + try.deflate_correct = 0; + else + try.deflate_draft = 0; + 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) { + 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]; + + 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) { + 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; + } + 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) { + 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)); +} + +/* + * 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); +} + +/* + * 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) { + 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"); + } 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; +} + diff --git a/mdk-stage1/ppp/pppd/ccp.h b/mdk-stage1/ppp/pppd/ccp.h new file mode 100644 index 000000000..609d858c5 --- /dev/null +++ b/mdk-stage1/ppp/pppd/ccp.h @@ -0,0 +1,48 @@ +/* + * ccp.h - Definitions for PPP Compression Control Protocol. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +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? */ + 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; diff --git a/mdk-stage1/ppp/pppd/chap.c b/mdk-stage1/ppp/pppd/chap.c new file mode 100644 index 000000000..54c0e0095 --- /dev/null +++ b/mdk-stage1/ppp/pppd/chap.c @@ -0,0 +1,860 @@ +/* + * chap.c - Challenge Handshake Authentication Protocol. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +/* + * TODO: + */ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> + +#include "pppd.h" +#include "chap.h" +#include "md5.h" +#ifdef CHAPMS +#include "chap_ms.h" +#endif + +static const char rcsid[] = RCSID; + +/* + * Command-line options. + */ +static option_t chap_option_list[] = { + { "chap-restart", o_int, &chap[0].timeouttime, + "Set timeout for CHAP", OPT_PRIO }, + { "chap-max-challenge", o_int, &chap[0].max_transmits, + "Set max #xmits for challenge", OPT_PRIO }, + { "chap-interval", o_int, &chap[0].chal_interval, + "Set interval for rechallenge", OPT_PRIO }, +#ifdef MSLANMAN + { "ms-lanman", o_bool, &ms_lanman, + "Use LanMan passwd when using MS-CHAP", 1 }, +#endif + { NULL } +}; + +/* + * Protocol entry points. + */ +static void ChapInit __P((int)); +static void ChapLowerUp __P((int)); +static void ChapLowerDown __P((int)); +static void ChapInput __P((int, u_char *, int)); +static void ChapProtocolReject __P((int)); +static int ChapPrintPkt __P((u_char *, int, + void (*) __P((void *, char *, ...)), void *)); + +struct protent chap_protent = { + PPP_CHAP, + ChapInit, + ChapInput, + ChapProtocolReject, + ChapLowerUp, + ChapLowerDown, + NULL, + NULL, + ChapPrintPkt, + NULL, + 1, + "CHAP", + NULL, + chap_option_list, + NULL, + NULL, + NULL +}; + +chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ + +static void ChapChallengeTimeout __P((void *)); +static void ChapResponseTimeout __P((void *)); +static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int)); +static void ChapRechallenge __P((void *)); +static void ChapReceiveResponse __P((chap_state *, u_char *, int, int)); +static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int)); +static void ChapReceiveFailure __P((chap_state *, u_char *, int, int)); +static void ChapSendStatus __P((chap_state *, int)); +static void ChapSendChallenge __P((chap_state *)); +static void ChapSendResponse __P((chap_state *)); +static void ChapGenChallenge __P((chap_state *)); + +extern double drand48 __P((void)); +extern void srand48 __P((long)); + +/* + * ChapInit - Initialize a CHAP unit. + */ +static void +ChapInit(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + /* random number generator is initialized in magic_init */ +} + + +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void +ChapAuthWithPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void +ChapAuthPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; +} + + +/* + * ChapChallengeTimeout - Timeout expired on sending challenge. + */ +static void +ChapChallengeTimeout(arg) + void *arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) + return; + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + error("Peer failed to respond to CHAP challenge"); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + return; + } + + ChapSendChallenge(cstate); /* Re-send challenge */ +} + + +/* + * ChapResponseTimeout - Timeout expired on sending response. + */ +static void +ChapResponseTimeout(arg) + void *arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) + return; + + ChapSendResponse(cstate); /* re-send response */ +} + + +/* + * ChapRechallenge - Time to challenge the peer again. + */ +static void +ChapRechallenge(arg) + void *arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) + return; + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +static void +ChapLowerUp(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) + cstate->clientstate = CHAPCS_CLOSED; + else if (cstate->clientstate == CHAPCS_PENDING) + cstate->clientstate = CHAPCS_LISTEN; + + if (cstate->serverstate == CHAPSS_INITIAL) + cstate->serverstate = CHAPSS_CLOSED; + else if (cstate->serverstate == CHAPSS_PENDING) { + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +ChapLowerDown(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) + UNTIMEOUT(ChapChallengeTimeout, cstate); + else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) + UNTIMEOUT(ChapRechallenge, cstate); + if (cstate->clientstate == CHAPCS_RESPONSE) + UNTIMEOUT(ChapResponseTimeout, cstate); + + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +static void +ChapProtocolReject(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) + auth_peer_fail(unit, PPP_CHAP); + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) + auth_withpeer_fail(unit, PPP_CHAP); + ChapLowerDown(unit); /* shutdown chap */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +static void +ChapInput(unit, inpacket, packet_len) + int unit; + u_char *inpacket; + int packet_len; +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG(("ChapInput: rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG(("ChapInput: rcvd illegal length.")); + return; + } + if (len > packet_len) { + CHAPDEBUG(("ChapInput: rcvd short packet.")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + warn("Unknown CHAP code (%d) received.", code); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge and send Response. + */ +static void +ChapReceiveChallenge(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + int id; + int len; +{ + int rchallenge_len; + u_char *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + MD5_CTX mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; + + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet.")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet.")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= sizeof(rhostname)) + len = sizeof(rhostname) - 1; + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + /* Microsoft doesn't send their name back in the PPP packet */ + if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) { + strlcpy(rhostname, remote_name, sizeof(rhostname)); + CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name", + rhostname)); + } + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, + secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + warn("No CHAP secret found for authenticating us to %q", rhostname); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) + UNTIMEOUT(ChapResponseTimeout, cstate); + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->resp_id, 1); + MD5Update(&mdContext, secret, secret_len); + MD5Update(&mdContext, rchallenge, rchallenge_len); + MD5Final(hash, &mdContext); + BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + +#ifdef CHAPMS + case CHAP_MICROSOFT: + ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); + break; +#endif + + default: + CHAPDEBUG(("unknown digest type %d", cstate->resp_type)); + return; + } + + BZERO(secret, sizeof(secret)); + ChapSendResponse(cstate); +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void +ChapReceiveResponse(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + int id; + int len; +{ + u_char *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + MD5_CTX mdContext; + char secret[MAXSECRETLEN]; + u_char hash[MD5_SIGNATURE_SIZE]; + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) + return; /* doesn't match ID of last challenge */ + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + ChapSendStatus(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + ChapSendStatus(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG(("ChapReceiveResponse: rcvd short packet.")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u_char) + remmd_len; + if (len < 0) { + CHAPDEBUG(("ChapReceiveResponse: rcvd short packet.")); + return; + } + + UNTIMEOUT(ChapChallengeTimeout, cstate); + + if (len >= sizeof(rhostname)) + len = sizeof(rhostname) - 1; + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname), + cstate->chal_name, secret, &secret_len, 1)) { + warn("No CHAP secret found for authenticating %q", rhostname); + } else { + + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) + break; /* it's not even the right length */ + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->chal_id, 1); + MD5Update(&mdContext, secret, secret_len); + MD5Update(&mdContext, cstate->challenge, cstate->chal_len); + MD5Final(hash, &mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) + code = CHAP_SUCCESS; /* they are the same! */ + break; + + default: + CHAPDEBUG(("unknown digest type %d", cstate->chal_type)); + } + } + + BZERO(secret, sizeof(secret)); + ChapSendStatus(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + } + if (cstate->chal_interval != 0) + TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); + notice("CHAP peer authentication succeeded for %q", rhostname); + + } else { + error("CHAP peer authentication failed for remote host %q", rhostname); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + } +} + +/* + * ChapReceiveSuccess - Receive Success + */ +static void +ChapReceiveSuccess(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + u_char id; + int len; +{ + + if (cstate->clientstate == CHAPCS_OPEN) + /* presumably an answer to a duplicate response */ + return; + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) + PRINTMSG(inp, len); + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, PPP_CHAP); +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +static void +ChapReceiveFailure(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + u_char id; + int len; +{ + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) + PRINTMSG(inp, len); + + error("CHAP authentication failed"); + auth_withpeer_fail(cstate->unit, PPP_CHAP); +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void +ChapSendChallenge(cstate) + chap_state *cstate; +{ + u_char *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); + + TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); + ++cstate->chal_transmits; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void +ChapSendStatus(cstate, code) + chap_state *cstate; + int code; +{ + u_char *outp; + int outlen, msglen; + char msg[256]; + + if (code == CHAP_SUCCESS) + slprintf(msg, sizeof(msg), "Welcome to %s.", hostname); + else + slprintf(msg, sizeof(msg), "I don't like you. Go 'way."); + msglen = strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len. The challenge + * string and its length are stored in *cstate, and various other fields of + * *cstate are initialized. + */ + +static void +ChapGenChallenge(cstate) + chap_state *cstate; +{ + int chal_len; + u_char *ptr = cstate->challenge; + int i; + + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) ((drand48() * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) + + MIN_CHALLENGE_LENGTH); + cstate->chal_len = chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++) + *ptr++ = (char) (drand48() * 0xff); +} + +/* + * ChapSendResponse - send a response packet with values as specified + * in *cstate. + */ +/* ARGSUSED */ +static void +ChapSendResponse(cstate) + chap_state *cstate; +{ + u_char *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); + ++cstate->resp_transmits; +} + +/* + * ChapPrintPkt - print the contents of a CHAP packet. + */ +static char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; + +static int +ChapPrintPkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, id, len; + int clen, nlen; + u_char x; + + if (plen < CHAP_HEADERLEN) + return 0; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) + printer(arg, " %s", ChapCodenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) + break; + clen = p[0]; + if (len < clen + 1) + break; + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = "); + print_string((char *)p, nlen, printer, arg); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " "); + print_string((char *)p, len, printer, arg); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + + return len + CHAP_HEADERLEN; +} diff --git a/mdk-stage1/ppp/pppd/chap.h b/mdk-stage1/ppp/pppd/chap.h new file mode 100644 index 000000000..945d051d0 --- /dev/null +++ b/mdk-stage1/ppp/pppd/chap.h @@ -0,0 +1,124 @@ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +#ifndef __CHAP_INCLUDE__ + +/* Code + ID + length */ +#define CHAP_HEADERLEN 4 + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ +#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths (for challenges we send) and other limits. + */ +#define MIN_CHALLENGE_LENGTH 16 +#define MAX_CHALLENGE_LENGTH 24 +#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ + +/* + * Each interface is described by a chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u_char chal_len; /* challenge length */ + u_char chal_id; /* ID of last challenge */ + u_char chal_type; /* hash algorithm for challenges */ + u_char id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u_char resp_length; /* length of response */ + u_char resp_id; /* ID for response messages */ + u_char resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ +} chap_state; + + +/* + * Client (peer) states. + */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ + +/* + * Server (authenticator) states. + */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ + +/* + * Timeouts. + */ +#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */ +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ + +extern chap_state chap[]; + +void ChapAuthWithPeer __P((int, char *, int)); +void ChapAuthPeer __P((int, char *, int)); + +extern struct protent chap_protent; + +#define __CHAP_INCLUDE__ +#endif /* __CHAP_INCLUDE__ */ diff --git a/mdk-stage1/ppp/pppd/chap_ms.c b/mdk-stage1/ppp/pppd/chap_ms.c new file mode 100644 index 000000000..81a57bb5a --- /dev/null +++ b/mdk-stage1/ppp/pppd/chap_ms.c @@ -0,0 +1,338 @@ +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * 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 Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * 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 + */ + +#define RCSID "$Id$" + +#ifdef CHAPMS + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#ifdef HAVE_CRYPT_H +#include <crypt.h> +#endif + +#include "pppd.h" +#include "chap.h" +#include "chap_ms.h" +#include "md4.h" + +#ifndef USE_CRYPT +#include <des.h> +#endif + +static const char rcsid[] = RCSID; + +typedef struct { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +} MS_ChapResponse; +/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), + in case this struct gets padded. */ + + +static void ChallengeResponse __P((u_char *, u_char *, u_char *)); +static void DesEncrypt __P((u_char *, u_char *, u_char *)); +static void MakeKey __P((u_char *, u_char *)); +static u_char Get7Bits __P((u_char *, int)); +static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *)); +#ifdef MSLANMAN +static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *)); +#endif + +#ifdef USE_CRYPT +static void Expand __P((u_char *, u_char *)); +static void Collapse __P((u_char *, u_char *)); +#endif + +#ifdef MSLANMAN +bool ms_lanman = 0; /* Use LanMan password instead of NT */ + /* Has meaning only with MS-CHAP challenges */ +#endif + +static void +ChallengeResponse(challenge, pwHash, response) + u_char *challenge; /* IN 8 octets */ + u_char *pwHash; /* IN 16 octets */ + u_char *response; /* OUT 24 octets */ +{ + char ZPasswordHash[21]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE); + +#if 0 + dbglog("ChallengeResponse - ZPasswordHash %.*B", + sizeof(ZPasswordHash), ZPasswordHash); +#endif + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + +#if 0 + dbglog("ChallengeResponse - response %.24B", response); +#endif +} + + +#ifdef USE_CRYPT +static void +DesEncrypt(clear, key, cipher) + u_char *clear; /* IN 8 octets */ + u_char *key; /* IN 7 octets */ + u_char *cipher; /* OUT 8 octets */ +{ + u_char des_key[8]; + u_char crypt_key[66]; + u_char des_input[66]; + + MakeKey(key, des_key); + + Expand(des_key, crypt_key); + setkey(crypt_key); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear)); +#endif + + Expand(clear, des_input); + encrypt(des_input, 0); + Collapse(des_input, cipher); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher)); +#endif +} + +#else /* USE_CRYPT */ + +static void +DesEncrypt(clear, key, cipher) + u_char *clear; /* IN 8 octets */ + u_char *key; /* IN 7 octets */ + u_char *cipher; /* OUT 8 octets */ +{ + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + + des_set_key(&des_key, key_schedule); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear)); +#endif + + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher)); +#endif +} + +#endif /* USE_CRYPT */ + + +static u_char Get7Bits(input, startBit) + u_char *input; + int startBit; +{ + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +#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; + } +} +#endif + +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 + +#if 0 + CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key)); + CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key)); +#endif +} + +static void +ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response) + char *rchallenge; + int rchallenge_len; + char *secret; + int secret_len; + MS_ChapResponse *response; +{ + int i; +#ifdef __NetBSD__ + /* NetBSD uses the libc md4 routines which take bytes instead of bits */ + int mdlen = secret_len * 2; +#else + int mdlen = secret_len * 2 * 8; +#endif + MD4_CTX md4Context; + u_char hash[MD4_SIGNATURE_SIZE]; + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + + /* Initialize the Unicode version of the secret (== password). */ + /* This implicitly supports 8-bit ISO8859/1 characters. */ + BZERO(unicodePassword, sizeof(unicodePassword)); + for (i = 0; i < secret_len; i++) + unicodePassword[i * 2] = (u_char)secret[i]; + + MD4Init(&md4Context); + MD4Update(&md4Context, unicodePassword, mdlen); + + MD4Final(hash, &md4Context); /* Tell MD4 we're done */ + + ChallengeResponse(rchallenge, hash, response->NTResp); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void +ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response) + char *rchallenge; + int rchallenge_len; + char *secret; + int secret_len; + MS_ChapResponse *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]); + DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); + DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); +} +#endif + +void +ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len) + chap_state *cstate; + char *rchallenge; + int rchallenge_len; + char *secret; + int secret_len; +{ + MS_ChapResponse response; + +#if 0 + CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret)); +#endif + BZERO(&response, sizeof(response)); + + /* Calculate both always */ + ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + + /* prefered method is set by option */ + response.UseNT = !ms_lanman; +#else + response.UseNT = 1; +#endif + + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; +} + +#endif /* CHAPMS */ diff --git a/mdk-stage1/ppp/pppd/chap_ms.h b/mdk-stage1/ppp/pppd/chap_ms.h new file mode 100644 index 000000000..fad2755c8 --- /dev/null +++ b/mdk-stage1/ppp/pppd/chap_ms.h @@ -0,0 +1,33 @@ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * 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 Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +#ifndef __CHAPMS_INCLUDE__ + +#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */ +#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ + +void ChapMS __P((chap_state *, char *, int, char *, int)); + +#define __CHAPMS_INCLUDE__ +#endif /* __CHAPMS_INCLUDE__ */ diff --git a/mdk-stage1/ppp/pppd/demand.c b/mdk-stage1/ppp/pppd/demand.c new file mode 100644 index 000000000..9a740be8d --- /dev/null +++ b/mdk-stage1/ppp/pppd/demand.c @@ -0,0 +1,351 @@ +/* + * demand.c - Support routines for demand-dialling. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/socket.h> +#ifdef PPP_FILTER +#include <net/if.h> +#include <net/bpf.h> +#include <pcap.h> +#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)); + ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0); + ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0); + +#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 + if (pass_filter.bf_len != 0 + && bpf_filter(pass_filter.bf_insns, p, len, len) == 0) + return 0; + if (active_filter.bf_len != 0 + && bpf_filter(active_filter.bf_insns, p, len, len) == 0) + return 0; +#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 !!?? */ +} diff --git a/mdk-stage1/ppp/pppd/eui64.c b/mdk-stage1/ppp/pppd/eui64.c new file mode 100644 index 000000000..7f3176035 --- /dev/null +++ b/mdk-stage1/ppp/pppd/eui64.c @@ -0,0 +1,40 @@ +/* + eui64.c - EUI64 routines for IPv6CP. + Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi> + + 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 Tommi Komulainen. The name of the author may not be used + to endorse or promote products derived from this software without + specific prior written permission. + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + + $Id$ +*/ + +#define RCSID "$Id$" + +#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; +} diff --git a/mdk-stage1/ppp/pppd/eui64.h b/mdk-stage1/ppp/pppd/eui64.h new file mode 100644 index 000000000..ca4445345 --- /dev/null +++ b/mdk-stage1/ppp/pppd/eui64.h @@ -0,0 +1,97 @@ +/* + eui64.h - EUI64 routines for IPv6CP. + Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi> + + 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 Tommi Komulainen. The name of the author may not be used + to endorse or promote products derived from this software without + specific prior written permission. + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + + $Id$ +*/ + +#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 <netinet/in.h> + +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__ */ + diff --git a/mdk-stage1/ppp/pppd/fsm.c b/mdk-stage1/ppp/pppd/fsm.c new file mode 100644 index 000000000..07a8c11f1 --- /dev/null +++ b/mdk-stage1/ppp/pppd/fsm.c @@ -0,0 +1,762 @@ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> + +#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; + } +} + + +/* + * 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: + 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, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + 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; + + f->state = 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 */ + 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; + + 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 (*proc) __P((fsm *, u_char *, int)); + int ret; + + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !(ret = proc(f, inp, len))) { + /* Nak/reject is bad - ignore it */ + error("Received bad configure-nak/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)); + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->retransmits = 0; + f->state = STOPPING; + 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); + 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: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = 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; + } + + 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); +} diff --git a/mdk-stage1/ppp/pppd/fsm.h b/mdk-stage1/ppp/pppd/fsm.h new file mode 100644 index 000000000..c94a68e6c --- /dev/null +++ b/mdk-stage1/ppp/pppd/fsm.h @@ -0,0 +1,144 @@ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * 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 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 (*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) */ diff --git a/mdk-stage1/ppp/pppd/ipcp.c b/mdk-stage1/ppp/pppd/ipcp.c new file mode 100644 index 000000000..ac5bd39fa --- /dev/null +++ b/mdk-stage1/ppp/pppd/ipcp.c @@ -0,0 +1,2054 @@ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +/* + * TODO: + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <netdb.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#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 */ + +/* 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; + +/* 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 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)); /* 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 **)); +static 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 }, + + { "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 *)); /* 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. + */ +static 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]); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = 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 = 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_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]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + if (wo->ouraddr == 0) + wo->accept_local = 1; + if (wo->hisaddr == 0) + wo->accept_remote = 1; + 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); +} + + +/* + * 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 LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) +#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + /* 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 (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj) + + LENCIDNS(go->req_dns1) + + LENCIDNS(go->req_dns2)) ; +} + + +/* + * 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 ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else \ + neg = 0; \ + } + +#define ADDCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u_int32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else \ + neg = 0; \ + } + +#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; \ + } + + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[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 ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) \ + goto bad; \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) \ + goto bad; \ + } \ + } + +#define ACKCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u_int32_t l; \ + if ((len -= addrlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) \ + goto bad; \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) \ + goto bad; \ + } \ + } + +#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; \ + } + + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + 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) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u_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 NAKCIADDR(opt, neg, old, code) \ + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else \ + ciaddr2 = 0; \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + +#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. + */ + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + 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 (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; + } + } + ); + + NAKCIDNS(CI_MS_DNS1, req_dns1, + try.dnsaddr[0] = cidnsaddr; + ); + + NAKCIDNS(CI_MS_DNS2, req_dns2, + 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. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) + goto bad; + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) + goto bad; + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) + try.ouraddr = ciaddr1; + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) + try.hisaddr = ciaddr2; + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) + goto bad; + try.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; + } + 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 REJCIADDR(opt, neg, old, val1, val2) \ + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == 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; \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) \ + goto bad; \ + } \ + try.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) \ + goto bad; \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) \ + goto bad; \ + } \ + try.neg = 0; \ + } + +#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; \ + } + + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); + + REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[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]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u_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->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 { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; + + case CI_ADDR: + if (!ao->neg_addr || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + 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 && + wo->req_addr && !reject_if_disagree) { + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = ucp - inp; /* Compute output length */ + IPCPDEBUG(("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) { + /* 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; + 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); + 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->hisaddr = wo->hisaddr; + + 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) { + 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); + script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1); + + if (usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { + script_setenv("USEPEERDNS", "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); + create_resolv(go->dnsaddr[0], go->dnsaddr[1]); + } + + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (!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) { + 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 (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 + + /* 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 (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); + 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]); + } + + np_up(f->unit, PPP_IP); + ipcp_is_up = 1; + + 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); + } +} + + +/* + * 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. */ + update_link_stats(f->unit); + 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); + + /* + * 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); + } +} + + +/* + * 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; +{ + 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); + } + break; + case s_down: + if (ipcp_fsm[0].state == OPENED) { + ipcp_script_state = s_up; + ipcp_script(_PATH_IPUP); + } + break; + } +} + + +/* + * ipcp_script - Execute a script with arguments + * interface-name tty-name speed local-IP remote-IP. + */ +static void +ipcp_script(script) + char *script; +{ + 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; + ipcp_script_pid = run_program(script, argv, 0, ipcp_script_done, NULL); +} + +/* + * 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, + 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 +#define IPPROTO_TCP 6 +#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; +} diff --git a/mdk-stage1/ppp/pppd/ipcp.h b/mdk-stage1/ppp/pppd/ipcp.h new file mode 100644 index 000000000..8c5aca861 --- /dev/null +++ b/mdk-stage1/ppp/pppd/ipcp.h @@ -0,0 +1,73 @@ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * 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; diff --git a/mdk-stage1/ppp/pppd/ipv6cp.c b/mdk-stage1/ppp/pppd/ipv6cp.c new file mode 100644 index 000000000..54ff7d7d8 --- /dev/null +++ b/mdk-stage1/ppp/pppd/ipv6cp.c @@ -0,0 +1,1512 @@ +/* + ipv6cp.c - PPP IPV6 Control Protocol. + Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi> + + 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. The name of the author may not be + used to endorse or promote products derived from this software + without specific prior written permission. + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +*/ + +/* 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) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +#define RCSID "$Id$" + +/* + * 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 <stdio.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#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)); /* 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) + { "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; +} + +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) + fsm *f; + u_char *p; + int len; +{ + 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 (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) { + 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( (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) + /* + * 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); +} + +/* + * 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 IPPROTO_TCP 6 +#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; +} diff --git a/mdk-stage1/ppp/pppd/ipv6cp.h b/mdk-stage1/ppp/pppd/ipv6cp.h new file mode 100644 index 000000000..60d366eb3 --- /dev/null +++ b/mdk-stage1/ppp/pppd/ipv6cp.h @@ -0,0 +1,126 @@ +/* + ipv6cp.h - PPP IPV6 Control Protocol. + Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi> + + 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. The name of the author may not be + used to endorse or promote products derived from this software + without specific prior written permission. + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +*/ + +/* 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) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * 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) + 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; diff --git a/mdk-stage1/ppp/pppd/ipxcp.c b/mdk-stage1/ppp/pppd/ipxcp.c new file mode 100644 index 000000000..f9a12b934 --- /dev/null +++ b/mdk-stage1/ppp/pppd/ipxcp.c @@ -0,0 +1,1570 @@ +/* + * ipxcp.c - PPP IPX Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifdef IPX_CHANGE + +#define RCSID "$Id$" + +/* + * TODO: + */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#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)); /* 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; +{ + 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; +{ + 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 (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 (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(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) + fsm *f; + u_char *p; + int len; +{ + 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 (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 (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 (!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(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] = go->name; + argv[10] = ho->name; + argv[11] = ipparam; + argv[12] = strpid; + argv[13] = NULL; + run_program(script, argv, 0, NULL, NULL); +} + +/* + * 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(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 */ diff --git a/mdk-stage1/ppp/pppd/ipxcp.h b/mdk-stage1/ppp/pppd/ipxcp.h new file mode 100644 index 000000000..47f680d70 --- /dev/null +++ b/mdk-stage1/ppp/pppd/ipxcp.h @@ -0,0 +1,71 @@ +/* + * ipxcp.h - IPX Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * 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; diff --git a/mdk-stage1/ppp/pppd/lcp.c b/mdk-stage1/ppp/pppd/lcp.c new file mode 100644 index 000000000..41c58cad5 --- /dev/null +++ b/mdk-stage1/ppp/pppd/lcp.c @@ -0,0 +1,2224 @@ +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +/* + * TODO: + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "pppd.h" +#include "fsm.h" +#include "lcp.h" +#include "chap.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)); /* 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->chap_mdtype = CHAP_DIGEST_MD5; + 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_DIGEST_MD5; + ao->neg_upap = 1; + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; +#ifdef CBCP_SUPPORT + ao->neg_cbcp = 1; +#endif + 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) + 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(&lcp_fsm[unit], 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. + */ + ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0); + ppp_recv_config(unit, PPP_MRU, (lax_recv? 0: 0xffffffff), + wo->neg_pcompression, wo->neg_accompression); + 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: + 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; + + 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; + } + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol == prot && protp->enabled_flag) { + (*protp->protrej)(f->unit); + return; + } + + warn("Protocol-Reject for unsupported protocol 0x%x", 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 and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!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, digest) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(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); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !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, digest) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || \ + citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != 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); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !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) + fsm *f; + u_char *p; + int len; +{ + 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; \ + } + + /* + * 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) + && 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; + INCPTR(2, p); + GETSHORT(cishort, p); + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { + /* + * If we were asking for CHAP, they obviously don't want to do it. + * If we weren't asking for CHAP, then we were asking for PAP, + * in which case this Nak is bad. + */ + if (!go->neg_chap) + goto bad; + try.neg_chap = 0; + + } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); + if (go->neg_chap) { + /* + * We were asking for CHAP/MD5; they must want a different + * algorithm. If they can't do MD5, we can ask for M$-CHAP + * if we support it, otherwise we'll have to stop + * asking for CHAP. + */ + if (cichar != go->chap_mdtype) { +#ifdef CHAPMS + if (cichar == CHAP_MICROSOFT) + go->chap_mdtype = CHAP_MICROSOFT; + else +#endif /* CHAPMS */ + try.neg_chap = 0; + } + } else { + /* + * Stop asking for PAP if we were asking for it. + */ + try.neg_upap = 0; + } + + } else { + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + 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 (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) + 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."); + lcp_close(f->unit, "Loopback detected"); + status = EXIT_LOOPBACK; + } + } 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, digest) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) \ + goto bad; \ + try.neg = 0; \ + 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); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_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)) { + /* + * Reject the option if we're not willing to authenticate. + */ + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + /* + * Authtype must be PAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == PPP_PAP) { + if (ho->neg_chap || /* we've already accepted CHAP */ + 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 */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + /* XXX if we can do CHAP_MICROSOFT as well, we should + probably put in another option saying so */ + break; + } + ho->neg_upap = 1; + break; + } + if (cishort == PPP_CHAP) { + if (ho->neg_upap || /* we've already accepted PAP */ + 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 PAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != CHAP_DIGEST_MD5 +#ifdef CHAPMS + && cichar != CHAP_MICROSOFT +#endif + ) { + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 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.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + if (ao->neg_chap) { + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(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; + + 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 lower of that and the + * MTU we want to use. + */ + mtu = ho->neg_mru? ho->mru: PPP_MRU; +#ifdef HAVE_MULTILINK + if (!(multilink && go->neg_mrru && ho->neg_mrru)) +#endif /* HAVE_MULTILINK */ + netif_set_mtu(f->unit, MIN(mtu, 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, (go->neg_mru? MAX(wo->mru, go->mru): PPP_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" +}; + +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_DIGEST_MD5: + printer(arg, " MD5"); + ++p; + break; +#ifdef CHAPMS + case CHAP_MICROSOFT: + printer(arg, " m$oft"); + ++p; + break; +#endif + } + } + 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); + p += 4; + len -= 4; + } + 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."); + lcp_close(f->unit, "Peer not responding"); + status = EXIT_PEER_DEAD; + } +} + +/* + * 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; + } +} diff --git a/mdk-stage1/ppp/pppd/lcp.h b/mdk-stage1/ppp/pppd/lcp.h new file mode 100644 index 000000000..b87a9295e --- /dev/null +++ b/mdk-stage1/ppp/pppd/lcp.h @@ -0,0 +1,95 @@ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#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 */ + +/* + * LCP-specific packet types. + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#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_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 type (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 diff --git a/mdk-stage1/ppp/pppd/magic.c b/mdk-stage1/ppp/pppd/magic.c new file mode 100644 index 000000000..764692a16 --- /dev/null +++ b/mdk-stage1/ppp/pppd/magic.c @@ -0,0 +1,87 @@ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> + +#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(); +} + +#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 diff --git a/mdk-stage1/ppp/pppd/magic.h b/mdk-stage1/ppp/pppd/magic.h new file mode 100644 index 000000000..1344626a3 --- /dev/null +++ b/mdk-stage1/ppp/pppd/magic.h @@ -0,0 +1,23 @@ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +void magic_init __P((void)); /* Initialize the magic number generator */ +u_int32_t magic __P((void)); /* Returns the next magic number */ diff --git a/mdk-stage1/ppp/pppd/main.c b/mdk-stage1/ppp/pppd/main.c new file mode 100644 index 000000000..8789d3b1d --- /dev/null +++ b/mdk-stage1/ppp/pppd/main.c @@ -0,0 +1,1831 @@ +/* + * main.c - Point-to-Point Protocol main module + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <syslog.h> +#include <netdb.h> +#include <utmp.h> +#include <pwd.h> +#include <setjmp.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#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.h" +#include "ccp.h" +#include "pathnames.h" +#include "tdb.h" + +#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; + +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 */ +TDB_CONTEXT *pppdb; /* database for storing status etc. */ +char db_key[32]; + +int (*holdoff_hook) __P((void)) = NULL; +int (*new_phase_hook) __P((int)) = NULL; + +static int conn_running; /* we have a [dis]connector running */ +static int devfd; /* fd of underlying device */ +static int fd_ppp = -1; /* fd for talking PPP */ +static int fd_loop; /* fd for getting demand-dial packets */ + +int phase; /* where the link is at */ +int kill_link; +int open_ccp_flag; +int listen_time; +int got_sigusr2; +int got_sigterm; +int got_sighup; + +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. */ + +struct pppd_stats link_stats; +int link_connect_time; +int link_stats_valid; + +/* + * 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; + 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((void)); +static void create_linkpidfile __P((void)); +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 int reap_kids __P((int waitfor)); +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)); +static void handle_events __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, +#ifdef IPX_CHANGE + &ipxcp_protent, +#endif +#ifdef AT_CHANGE + &atcp_protent, +#endif + 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]; + + new_phase(PHASE_INITIALIZE); + + /* + * Ensure that fds 0, 1, 2 are open, to /dev/null if nowhere else. + * This way we can close 0, 1, 2 in detach() without clobbering + * a fd that we are using. + */ + if ((i = open("/dev/null", O_RDWR)) >= 0) { + while (0 <= i && i <= 2) + i = dup(i); + if (i >= 0) + close(i); + } + + 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); + } + + /* + * Initialize system-dependent stuff. + */ + sys_init(); + + 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; + } + } + + /* + * 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(); + + waiting = 0; + + create_linkpidfile(); + + /* + * 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. + */ + tdb_writelock(pppdb); + fd_loop = open_ppp_loopback(); + set_ifunit(1); + tdb_writeunlock(pppdb); + + /* + * Configure the interface and mark it up, etc. + */ + demand_conf(); + } + + do_callback = 0; + for (;;) { + + 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 (kill_link && !persist) + break; + if (get_loop_output()) + break; + } + remove_fd(fd_loop); + if (kill_link && !persist) + break; + + /* + * Now we want to bring up the link. + */ + demand_block(); + info("Starting link"); + } + + new_phase(PHASE_SERIALCONN); + + devfd = the_channel->connect(); + if (devfd < 0) + goto fail; + + /* set up the serial device as a ppp interface */ + tdb_writelock(pppdb); + fd_ppp = the_channel->establish_ppp(devfd); + if (fd_ppp < 0) { + tdb_writeunlock(pppdb); + status = EXIT_FATAL_ERROR; + goto disconnect; + } + + if (!demand && ifunit >= 0) + set_ifunit(1); + tdb_writeunlock(pppdb); + + /* + * Start opening the connection and wait for + * incoming events (reply, timeout, etc.). + */ + notice("Connect: %s <--> %s", ifname, ppp_devnam); + gettimeofday(&start_time, NULL); + link_stats_valid = 0; + script_unsetenv("CONNECT_TIME"); + script_unsetenv("BYTES_SENT"); + script_unsetenv("BYTES_RCVD"); + lcp_lowerup(0); + + add_fd(fd_ppp); + lcp_open(0); /* Start protocol */ + status = EXIT_NEGOTIATION_FAILED; + new_phase(PHASE_ESTABLISH); + while (phase != PHASE_DEAD) { + handle_events(); + get_input(); + if (kill_link) + lcp_close(0, "User request"); + if (open_ccp_flag) { + if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) { + ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */ + (*ccp_protent.open)(0); + } + } + } + + /* + * 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); + } + + /* + * Delete pid file before disestablishing ppp. Otherwise it + * can happen that another pppd gets the same unit and then + * we delete its pid file. + */ + if (!demand) { + if (pidfilename[0] != 0 + && unlink(pidfilename) < 0 && errno != ENOENT) + warn("unable to delete pid file %s: %m", pidfilename); + pidfilename[0] = 0; + } + + /* + * 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. + */ + remove_fd(fd_ppp); + clean_check(); + the_channel->disestablish_ppp(devfd); + fd_ppp = -1; + if (!hungup) + lcp_lowerdown(0); + if (!demand) + script_unsetenv("IFNAME"); + + /* + * Run disconnector script, if requested. + * XXX we may not be able to do this if the line has hung up! + */ + disconnect: + new_phase(PHASE_DISCONNECT); + the_channel->disconnect(); + + fail: + if (the_channel->cleanup) + (*the_channel->cleanup)(); + + if (!demand) { + if (pidfilename[0] != 0 + && unlink(pidfilename) < 0 && errno != ENOENT) + warn("unable to delete pid file %s: %m", pidfilename); + pidfilename[0] = 0; + } + + if (!persist || (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 */ + /* XXX should have a timeout here */ + while (n_children > 0) { + 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); + } + if (reap_kids(1) < 0) + break; + } + + die(status); + return 0; +} + +/* + * handle_events - wait for something to happen and respond to it. + */ +static void +handle_events() +{ + struct timeval timo; + sigset_t mask; + + kill_link = open_ccp_flag = 0; + if (sigsetjmp(sigjmp, 1) == 0) { + sigprocmask(SIG_BLOCK, &mask, NULL); + if (got_sighup || got_sigterm || got_sigusr2 || got_sigchld) { + sigprocmask(SIG_UNBLOCK, &mask, NULL); + } else { + waiting = 1; + sigprocmask(SIG_UNBLOCK, &mask, NULL); + wait_input(timeleft(&timo)); + } + } + waiting = 0; + calltimeout(); + if (got_sighup) { + kill_link = 1; + got_sighup = 0; + if (status != EXIT_HANGUP) + status = EXIT_USER_REQUEST; + } + if (got_sigterm) { + kill_link = 1; + persist = 0; + status = EXIT_USER_REQUEST; + got_sigterm = 0; + } + if (got_sigchld) { + reap_kids(0); /* Don't leave dead kids lying around */ + got_sigchld = 0; + } + if (got_sigusr2) { + open_ccp_flag = 1; + got_sigusr2 = 0; + } +} + +/* + * setup_signals - initialize signal handling. + */ +static void +setup_signals() +{ + struct sigaction sa; + sigset_t mask; + + /* + * Compute mask of all interesting signals and install signal handlers + * for each. Only one signal handler may be active at a time. Therefore, + * all other signals should be masked when any handler is executing. + */ + sigemptyset(&mask); + sigaddset(&mask, SIGHUP); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGCHLD); + sigaddset(&mask, 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 = mask; + 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(); /* write pid to file */ + create_linkpidfile(); + } +} + +/* + * detach - detach us from the controlling terminal. + */ +void +detach() +{ + int pid; + char numbuf[16]; + + if (detached) + return; + if ((pid = fork()) < 0) { + error("Couldn't detach (fork failed: %m)"); + die(1); /* or just return? */ + } + if (pid != 0) { + /* parent */ + notify(pidchange, pid); + exit(0); /* parent dies */ + } + setsid(); + chdir("/"); + close(0); + close(1); + close(2); + detached = 1; + if (log_default) + log_to_fd = -1; + /* update pid files if they have been written already */ + if (pidfilename[0]) + create_pidfile(); + if (linkpidfile[0]) + create_linkpidfile(); + slprintf(numbuf, sizeof(numbuf), "%d", getpid()); + script_setenv("PPPD_PID", numbuf, 1); +} + +/* + * reopen_log - (re)open our connection to syslog. + */ +void +reopen_log() +{ +#ifdef ULTRIX + openlog("pppd", LOG_PID); +#else + openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); + setlogmask(LOG_UPTO(LOG_INFO)); +#endif +} + +/* + * Create a file containing our process ID. + */ +static void +create_pidfile() +{ + FILE *pidfile; + + slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid", + _PATH_VARRUN, ifname); + if ((pidfile = fopen(pidfilename, "w")) != NULL) { + fprintf(pidfile, "%d\n", getpid()); + (void) fclose(pidfile); + } else { + error("Failed to create pid file %s: %m", pidfilename); + pidfilename[0] = 0; + } +} + +static void +create_linkpidfile() +{ + 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", getpid()); + if (ifname[0]) + fprintf(pidfile, "%s\n", ifname); + (void) fclose(pidfile); + } else { + error("Failed to create 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" }, + { 0x6f, "Stampede Bridging" }, + { 0xfb, "single-link compression" }, + { 0xfd, "1st choice compression" }, + { 0x0201, "802.1d Hello Packets" }, + { 0x0203, "IBM Source Routing BPDU" }, + { 0x0205, "DEC LANBridge100 Spanning Tree" }, + { 0x0231, "Luxcom" }, + { 0x0233, "Sigma Network Systems" }, + { 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" }, + { 0x006f, "Stampede Bridging Control Protocol" }, + { 0x80fb, "Single Link Compression Control Protocol" }, + { 0x80fd, "Compression Control Protocol" }, + { 0xc021, "Link Control Protocol" }, + { 0xc023, "Password Authentication Protocol" }, + { 0xc025, "Link Quality Report" }, + { 0xc027, "Shiva Password Authentication Protocol" }, + { 0xc029, "CallBack Control Protocol (CBCP)" }, + { 0xc081, "Container Control Protocol" }, + { 0xc223, "Challenge Handshake Authentication Protocol" }, + { 0xc281, "Proprietary 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) { + notice("Modem hangup"); + hungup = 1; + status = EXIT_HANGUP; + lcp_lowerdown(0); /* serial link is no longer available */ + link_terminated(0); + return; + } + + if (debug /*&& (debugflags & DBG_INPACKET)*/) + dbglog("rcvd %P", p, len); + + if (len < PPP_HDRLEN) { + MAINDEBUG(("io(): Received short packet.")); + return; + } + + 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) { + MAINDEBUG(("get_input: Received 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)) { + MAINDEBUG(("get_input: 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); +} + +/* + * 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; +{ + 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)(); + + 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; + + if (pppdb != NULL) + cleanup_db(); +} + +/* + * 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; + + slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time); + script_setenv("CONNECT_TIME", numbuf, 0); + slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_out); + script_setenv("BYTES_SENT", numbuf, 0); + slprintf(numbuf, sizeof(numbuf), "%d", 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. + * + * Note that this timeout takes the number of milliseconds, NOT hz (as in + * the kernel). + */ +void +timeout(func, arg, secs, usecs) + void (*func) __P((void *)); + void *arg; + int secs, usecs; +{ + struct callout *newp, *p, **pp; + + MAINDEBUG(("Timeout %p:%p in %d.%03d seconds.", func, arg, + time / 1000, time % 1000)); + + /* + * 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; + + MAINDEBUG(("Untimeout %p:%p.", func, arg)); + + /* + * 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. + */ +static void +kill_my_pg(sig) + int sig; +{ + struct sigaction act, oldact; + + act.sa_handler = SIG_IGN; + act.sa_flags = 0; + kill(0, sig); + 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; +{ + info("Hangup (SIGHUP)"); + 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; +{ + info("Terminating on signal %d.", sig); + got_sigterm = 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); +} + + +/* + * 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); +} + + +/* + * 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, fd; + int status = -1; + int errfd; + + ++conn_running; + pid = fork(); + + if (pid < 0) { + --conn_running; + error("Failed to create child process: %m"); + return -1; + } + + if (pid != 0) { + if (dont_wait) { + record_child(pid, program, NULL, NULL); + status = 0; + } else { + while (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) + continue; + fatal("error waiting for (dis)connection process: %m"); + } + --conn_running; + } + return (status == 0 ? 0 : -1); + } + + /* here we are executing in the child */ + /* make sure fds 0, 1, 2 are occupied */ + while ((fd = dup(in)) >= 0) { + if (fd > 2) { + close(fd); + break; + } + } + + /* dup in and out to fds > 2 */ + in = dup(in); + out = dup(out); + if (log_to_fd >= 0) { + errfd = dup(log_to_fd); + } else { + errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600); + } + + /* close fds 0 - 2 and any others we can think of */ + close(0); + close(1); + close(2); + sys_close(); + if (the_channel->close) + (*the_channel->close)(); + closelog(); + + /* dup the in, out, err fds to 0, 1, 2 */ + dup2(in, 0); + close(in); + dup2(out, 1); + close(out); + if (errfd >= 0) { + dup2(errfd, 2); + close(errfd); + } + + setuid(uid); + if (getuid() != uid) { + error("setuid failed"); + exit(1); + } + setgid(getgid()); + execl("/bin/sh", "sh", "-c", program, (char *)0); + error("could not exec /bin/sh: %m"); + exit(99); + /* NOTREACHED */ +} + + +/* + * run-program - execute a program with given arguments, + * but don't wait for it. + * If the program can't be executed, logs an error unless + * must_exist is 0 and the program file doesn't exist. + * 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) + char *prog; + char **args; + int must_exist; + void (*done) __P((void *)); + void *arg; +{ + int pid; + 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 = fork(); + if (pid == -1) { + error("Failed to create child process for %s: %m", prog); + return -1; + } + if (pid == 0) { + int new_fd; + + /* 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()); + + /* Ensure that nothing of our device environment is inherited. */ + sys_close(); + closelog(); + close (0); + close (1); + close (2); + if (the_channel->close) + (*the_channel->close)(); + + /* Don't pass handles to the PPP device, even by accident. */ + new_fd = open (_PATH_DEVNULL, O_RDWR); + if (new_fd >= 0) { + if (new_fd != 0) { + dup2 (new_fd, 0); /* stdin <- /dev/null */ + close (new_fd); + } + dup2 (0, 1); /* stdout -> /dev/null */ + dup2 (0, 2); /* stderr -> /dev/null */ + } + +#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 + + /* SysV recommends a second fork at this point. */ + + /* 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); + } + + if (debug) + dbglog("Script %s started (pid %d)", prog, pid); + record_child(pid, prog, done, arg); + + return pid; +} + + +/* + * record_child - add a child process to the list for reap_kids + * to use. + */ +void +record_child(pid, prog, done, arg) + int pid; + char *prog; + void (*done) __P((void *)); + void *arg; +{ + 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; + children = chp; + } +} + + +/* + * reap_kids - get status from any dead child processes, + * and log a message for abnormal terminations. + */ +static int +reap_kids(waitfor) + int waitfor; +{ + int pid, status; + struct subprocess *chp, **prevp; + + if (n_children == 0) + return 0; + while ((pid = waitpid(-1, &status, (waitfor? 0: WNOHANG))) != -1 + && pid != 0) { + 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, status); + if (chp && chp->done) + (*chp->done)(chp->arg); + if (chp) + free(chp); + } + 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_notify. + */ +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] == '=') { + if (p[-1] && pppdb != NULL) + delete_db_key(p); + free(p-1); + script_env[i] = newstring; + if (iskey && pppdb != NULL) + add_db_key(newstring); + update_db_entry(); + 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; + + if (pppdb != NULL) { + if (iskey) + add_db_key(newstring); + update_db_entry(); + } +} + +/* + * 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] == '=') { + if (p[-1] && pppdb != NULL) + delete_db_key(p); + free(p-1); + while ((script_env[i] = script_env[i+1]) != 0) + ++i; + break; + } + } + if (pppdb != NULL) + update_db_entry(); +} + +/* + * 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); + 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_error(pppdb)); + +} + +/* + * 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_error(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); +} diff --git a/mdk-stage1/ppp/pppd/md4.c b/mdk-stage1/ppp/pppd/md4.c new file mode 100644 index 000000000..cda9f943d --- /dev/null +++ b/mdk-stage1/ppp/pppd/md4.c @@ -0,0 +1,298 @@ +/* +** ******************************************************************** +** 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 <stdio.h> +#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<<S) | (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 */ + 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)***********************************/ diff --git a/mdk-stage1/ppp/pppd/md4.h b/mdk-stage1/ppp/pppd/md4.h new file mode 100644 index 000000000..80e8f9a2a --- /dev/null +++ b/mdk-stage1/ppp/pppd/md4.h @@ -0,0 +1,64 @@ + +/* +** ******************************************************************** +** 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)***********************************/ diff --git a/mdk-stage1/ppp/pppd/md5.c b/mdk-stage1/ppp/pppd/md5.c new file mode 100644 index 000000000..db48023c5 --- /dev/null +++ b/mdk-stage1/ppp/pppd/md5.c @@ -0,0 +1,306 @@ + + +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "md5.h" + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform (); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#ifdef __STDC__ +#define UL(x) x##U +#else +#define UL(x) x +#endif + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void MD5Init (mdContext) +MD5_CTX *mdContext; +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void MD5Update (mdContext, inBuf, inLen) +MD5_CTX *mdContext; +unsigned char *inBuf; +unsigned int inLen; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void MD5Final (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); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } + 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) ******************************** + */ diff --git a/mdk-stage1/ppp/pppd/md5.h b/mdk-stage1/ppp/pppd/md5.h new file mode 100644 index 000000000..7492b2228 --- /dev/null +++ b/mdk-stage1/ppp/pppd/md5.h @@ -0,0 +1,58 @@ +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef __MD5_INCLUDE__ + +/* typedef a 32-bit type */ +typedef unsigned int UINT4; + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (); +void MD5Update (); +void MD5Final (); + +#define __MD5_INCLUDE__ +#endif /* __MD5_INCLUDE__ */ diff --git a/mdk-stage1/ppp/pppd/multilink.c b/mdk-stage1/ppp/pppd/multilink.c new file mode 100644 index 000000000..e5f2ac40d --- /dev/null +++ b/mdk-stage1/ppp/pppd/multilink.c @@ -0,0 +1,396 @@ +/* + * multilink.c - support routines for multilink. + * + * Copyright (c) 2000 Paul Mackerras. + * 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. The name of the author may not be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <netdb.h> +#include <errno.h> +#include <signal.h> +#include <netinet/in.h> + +#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 */ + +extern TDB_CONTEXT *pppdb; +extern char db_key[]; + +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]; + + 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 (!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; + } + + /* + * 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); + + /* + * 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; + tdb_writelock(pppdb); + 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) { + /* it is, 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); + tdb_writeunlock(pppdb); + 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); + tdb_writeunlock(pppdb); + info("New bundle %s created", ifname); + return 0; +} + +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; +} + diff --git a/mdk-stage1/ppp/pppd/options.c b/mdk-stage1/ppp/pppd/options.c new file mode 100644 index 000000000..9ebac372e --- /dev/null +++ b/mdk-stage1/ppp/pppd/options.c @@ -0,0 +1,1513 @@ +/* + * options.c - handles option processing for PPP. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +#include <ctype.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <syslog.h> +#include <string.h> +#include <pwd.h> +#ifdef PLUGIN +#include <dlfcn.h> +#endif +#ifdef PPP_FILTER +#include <pcap.h> +#include <pcap-int.h> /* XXX: To get struct pcap */ +#endif + +#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. + */ +#ifdef PPP_FILTER +int dflag = 0; /* Tell libpcap we want debugging */ +#endif +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 */ + +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 */ +pcap_t pc; /* Fake struct pcap so we can compile expr */ +#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 + +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 }, + + { "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 }, + +#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 + { "pdebug", o_int, &dflag, + "libpcap debugging", OPT_PRIO }, + + { "pass-filter", 1, setpassfilter, + "set filter for packets to pass", OPT_PRIO }, + + { "active-filter", 1, setactivefilter, + "set filter for active pkts", OPT_PRIO }, +#endif + + { NULL } +}; + +#ifndef IMPLEMENTATION +#define IMPLEMENTATION "" +#endif + +static char *usage_string = "\ +pppd version %s\n\ +Usage: %s [ options ], where options are:\n\ + <device> Communicate over the named device\n\ + <speed> Set the baud rate to <speed>\n\ + <loc>:<rem> Set the local and/or remote interface IP\n\ + addresses. Either one may be omitted.\n\ + asyncmap <n> Set the desired async map to hex <n>\n\ + auth Require authentication from peer\n\ + connect <p> Invoke shell command <p> to set up the serial line\n\ + crtscts Use hardware RTS/CTS flow control\n\ + defaultroute Add default route through interface\n\ + file <f> Take options from file <f>\n\ + modem Use modem control lines\n\ + mru <n> Set MRU value to <n> for negotiation\n\ +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; + char *argv[MAXARGS]; + char args[MAXARGS][MAXWORDLEN]; + char cmd[MAXWORDLEN]; + + if (check_prot) + seteuid(getuid()); + f = fopen(filename, "r"); + err = errno; + if (check_prot) + seteuid(0); + 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 (strncmp(dev, "/dev/", 5) == 0) + dev += 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; + + 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; + 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, 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; + pp = (struct option_value **) &opt->addr2; + while (*pp != 0) + pp = &(*pp)->next; + *pp = ovp; + } + } + break; + + case o_wild: + wildp = (int (*) __P((char *, char **, int))) opt->addr; + if (!(*wildp)(cmd, argv, 1)) + return 0; + break; + } + + if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE + |OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST)) == 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 = 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 \. + * \<newline> 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<hex_string> 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>. + * 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; +{ + pc.linktype = DLT_PPP; + pc.snapshot = PPP_HDRLEN; + + if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0) + return 1; + option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc)); + return 0; +} + +/* + * setactivefilter - Set the active filter for packets + */ +static int +setactivefilter(argv) + char **argv; +{ + pc.linktype = DLT_PPP; + pc.snapshot = PPP_HDRLEN; + + if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0) + return 1; + option_error("error in active-filter expression: %s\n", pcap_geterr(&pc)); + return 0; +} +#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; + + if (!privileged_option) + seteuid(getuid()); + 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(0); + 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 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", + 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 */ diff --git a/mdk-stage1/ppp/pppd/patchlevel.h b/mdk-stage1/ppp/pppd/patchlevel.h new file mode 100644 index 000000000..2a2d816f1 --- /dev/null +++ b/mdk-stage1/ppp/pppd/patchlevel.h @@ -0,0 +1,4 @@ +/* $Id$ */ + +#define VERSION "2.4.1" +#define DATE "25 March 2001" diff --git a/mdk-stage1/ppp/pppd/pathnames.h b/mdk-stage1/ppp/pppd/pathnames.h new file mode 100644 index 000000000..d8eee70de --- /dev/null +++ b/mdk-stage1/ppp/pppd/pathnames.h @@ -0,0 +1,57 @@ +/* + * define path names + * + * $Id$ + */ + +#ifdef HAVE_PATHS_H +#include <paths.h> + +#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_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_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" + +#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 "pppd.tdb" +#else /* __STDC__ */ +#ifdef HAVE_PATHS_H +#define _PATH_PPPDB "/var/run/pppd.tdb" +#else +#define _PATH_PPPDB "/etc/ppp/pppd.tdb" +#endif +#endif /* __STDC__ */ + +#ifdef PLUGIN +#define _PATH_PLUGIN "/usr/lib/pppd/" VERSION +#endif /* PLUGIN */ diff --git a/mdk-stage1/ppp/pppd/plugins/Makefile.linux b/mdk-stage1/ppp/pppd/plugins/Makefile.linux new file mode 100644 index 000000000..a64256461 --- /dev/null +++ b/mdk-stage1/ppp/pppd/plugins/Makefile.linux @@ -0,0 +1,19 @@ +CC = gcc +CFLAGS = -g -O2 -I.. -I../../include -fPIC +LDFLAGS = -shared +INSTALL = install + +all: minconn.so passprompt.so + +minconn.so: minconn.c + $(CC) -o $@ $(LDFLAGS) $(CFLAGS) minconn.c + +passprompt.so: passprompt.c + $(CC) -o $@ $(LDFLAGS) $(CFLAGS) passprompt.c + +LIBDIR = /usr/lib/pppd + +install: minconn.so passprompt.so + version=`awk -F '"' '/VERSION/ { print $$2; }' ../patchlevel.h`; \ + $(INSTALL) -d $(LIBDIR)/$$version; \ + $(INSTALL) $? $(LIBDIR)/$$version
\ No newline at end of file diff --git a/mdk-stage1/ppp/pppd/plugins/Makefile.sol2 b/mdk-stage1/ppp/pppd/plugins/Makefile.sol2 new file mode 100644 index 000000000..8f4398258 --- /dev/null +++ b/mdk-stage1/ppp/pppd/plugins/Makefile.sol2 @@ -0,0 +1,27 @@ +# +# Makefile for plugins on Solaris 2 +# +# $Id$ +# + +include ../../svr4/Makedefs + +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 diff --git a/mdk-stage1/ppp/pppd/plugins/minconn.c b/mdk-stage1/ppp/pppd/plugins/minconn.c new file mode 100644 index 000000000..02ea34bf6 --- /dev/null +++ b/mdk-stage1/ppp/pppd/plugins/minconn.c @@ -0,0 +1,46 @@ +/* + * minconn.c - pppd plugin to implement a `minconnect' option. + * + * Copyright 1999 Paul Mackerras. + * + * 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. The name of the author + * may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include <stddef.h> +#include <time.h> +#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; +} diff --git a/mdk-stage1/ppp/pppd/plugins/passprompt.c b/mdk-stage1/ppp/pppd/plugins/passprompt.c new file mode 100644 index 000000000..5e6a7f90b --- /dev/null +++ b/mdk-stage1/ppp/pppd/plugins/passprompt.c @@ -0,0 +1,108 @@ +/* + * 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 <errno.h> +#include <unistd.h> +#include <sys/wait.h> +#include <syslog.h> +#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; + size_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[4], 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) { + 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; +} diff --git a/mdk-stage1/ppp/pppd/ppp.pam b/mdk-stage1/ppp/pppd/ppp.pam new file mode 100644 index 000000000..475a4bc88 --- /dev/null +++ b/mdk-stage1/ppp/pppd/ppp.pam @@ -0,0 +1,6 @@ +#%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
\ No newline at end of file diff --git a/mdk-stage1/ppp/pppd/pppd.8 b/mdk-stage1/ppp/pppd/pppd.8 new file mode 100644 index 000000000..ab091cd83 --- /dev/null +++ b/mdk-stage1/ppp/pppd/pppd.8 @@ -0,0 +1,1591 @@ +.\" manual page [] for pppd 2.4 +.\" $Id$ +.\" SH section heading +.\" SS subsection heading +.\" LP paragraph +.\" IP indented paragraph +.\" TP hanging label +.TH PPPD 8 +.SH NAME +pppd \- Point to Point Protocol daemon +.SH SYNOPSIS +.B pppd +[ +.I tty_name +] [ +.I speed +] [ +.I options +] +.SH DESCRIPTION +.LP +The Point-to-Point Protocol (PPP) provides a method for transmitting +datagrams over serial point-to-point links. PPP +is composed of three parts: a method for encapsulating datagrams over +serial links, an extensible Link Control Protocol (LCP), and +a family of Network Control Protocols (NCP) for establishing +and configuring different network-layer protocols. +.LP +The encapsulation scheme is provided by driver code in the kernel. +Pppd provides the basic LCP, authentication support, and an NCP for +establishing and configuring the Internet Protocol (IP) (called the IP +Control Protocol, IPCP). +.SH FREQUENTLY USED OPTIONS +.TP +.I <tty_name> +Communicate over the named device. The string "/dev/" is prepended if +necessary. 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> +Set the baud rate to <speed> (a decimal number). On systems such as +4.4BSD and NetBSD, any speed can be specified. Other systems +(e.g. SunOS) allow only a limited set of speeds. +.TP +.B asyncmap \fI<map> +Set the async character map to <map>. This map describes which +control characters cannot be successfully received over the serial +line. Pppd will ask the peer to send these characters as a 2-byte +escape sequence. The argument is a 32 bit hex number with each bit +representing a character to escape. Bit 0 (00000001) represents the +character 0x00; bit 31 (80000000) represents the character 0x1f or ^_. +If multiple \fIasyncmap\fR options are given, the values are ORed +together. If no \fIasyncmap\fR option is given, no async character +map will be negotiated for the receive direction; the peer should then +escape \fIall\fR 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 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 +Use the executable or shell command specified by \fIscript\fR to set +up the serial line. This script would typically use the chat(8) +program to dial the modem and start the remote ppp session. A value +for this option from a privileged source cannot be overridden by a +non-privileged user. +.TP +.B crtscts +Use hardware flow control (i.e. RTS/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 +unidirectional flow control. The serial port will +suspend transmission when requested by the modem (via CTS) +but will be unable to request the modem 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 +Run the executable or shell command specified by \fIscript\fR after +pppd has terminated the link. This script could, for example, issue +commands to the modem to cause it to hang up if hardware modem control +signals were not available. 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 +Run the executable or shell command specified by \fIscript\fR 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. +.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 +minimum MRU value is 128. The default MRU value is 1500. A value of +296 is recommended for slow links (40 bytes for TCP/IP header + 256 +bytes of data). (Note that for IPv6 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 +IPv6 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 <local_IP_address>\fB:\fI<remote_IP_address> +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<local_interface_identifier>\fR,\fI<remote_interface_identifier> +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 <local>,<remote>\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 NetBSD, and then only +if both the kernel and pppd were compiled with PPP_FILTER defined. +.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 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 connect-delay \fIn +Wait for up \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 endpoint \fI<epdisc> +Sets the endpoint discriminator sent by the local machine to the peer +during multilink negotiation to \fI<epdisc>\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 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 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<string> +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. +.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. +.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. +.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 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<addr> +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<addr> +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 netmask \fIn +Set the interface netmask to \fIn\fR, a 32 bit netmask in "decimal dot" +notation (e.g. 255.255.255.0). If this option is given, the value +specified is ORed with the default netmask. The default netmask is +chosen based on the negotiated remote IP address; it is the +appropriate network mask for the class of the remote IP address, ORed +with the netmasks for any non point-to-point network interfaces in the +system which are on the same network. (Note: on some platforms, pppd +will always use 255.255.255.255 for the netmask, if that is the only +appropriate value for a point-to-point interface.) +.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 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 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 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 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 NetBSD, and then only if both +the kernel and pppd were compiled with PPP_FILTER defined. +.TP +.B persist +Do not exit after a connection is terminated; instead try to reopen +the connection. +.TP +.B plugin \fIfilename +Load the shared library object file \fIfilename\fR as a plugin. This +is a privileged option. +.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 refuse-chap +With this option, pppd will not agree to authenticate itself to the +peer using CHAP. +.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-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 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 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. 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 two authentication protocols: the Password +Authentication Protocol (PAP) and the Challenge Handshake +Authentication Protocol (CHAP). 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. +.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). +Both 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. +.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 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 circumstances the peer may send no endpoint discriminator or a +non-unique value. The optional 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/pppd.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. Currently, if the first pppd terminates (for example, because of +a hangup or a received signal) the bundle is destroyed. +.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 or /etc/ppp/chap-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" which 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 +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 overriden by recompiling pppd with the macro +LOG_PPP defined as the desired facility.) In order to see the error +and debug messages, you will need to edit your /etc/syslog.conf file +to direct the messages to the desired output device or file. +.LP +The \fIdebug\fR option causes the contents of all control packets sent +or received to be logged, that is, all LCP, PAP, CHAP or IPCP packets. +This can be useful if the PPP negotiation does not succeed 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. 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. +.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-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 script. 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 /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 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/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 +.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 RFC2472 +Haskin, D. +.I IP Version 6 over PPP +December 1998. +.SH NOTES +The following signals have the specified effect when sent to pppd. +.TP +.B SIGINT, SIGTERM +These signals cause pppd to terminate the link (by closing LCP), +restore the serial device settings, and exit. +.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. +.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 (Paul.Mackerras@cs.anu.edu.au), based on earlier work by +Drew Perkins, +Brad Clements, +Karl Fox, +Greg Christy, +and +Brad Parker. diff --git a/mdk-stage1/ppp/pppd/pppd.h b/mdk-stage1/ppp/pppd/pppd.h new file mode 100644 index 000000000..02f6dfcef --- /dev/null +++ b/mdk-stage1/ppp/pppd/pppd.h @@ -0,0 +1,787 @@ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * TODO: + */ + +#ifndef __PPPD_H__ +#define __PPPD_H__ + +#include <stdio.h> /* for FILE */ +#include <limits.h> /* for NGROUPS_MAX */ +#include <sys/param.h> /* for MAXPATHLEN and BSD4_4, if defined */ +#include <sys/types.h> /* for u_int32_t, if defined */ +#include <sys/time.h> /* for struct timeval */ +#include <net/ppp_defs.h> +#include "patchlevel.h" + +#if defined(__STDC__) +#include <stdarg.h> +#define __V(x) x +#else +#include <varargs.h> +#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; + 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 /* OR in argument to value */ +#define OPT_INC 0x800 /* increment value */ +#define OPT_PRIV 0x1000 /* privileged option */ +#define OPT_STATIC 0x2000 /* string option goes into static array */ +#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 0x10000 /* for o_special, keep list of values */ +#define OPT_NOINCR 0x20000 /* value mustn't be increased */ +#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; +}; + +/* 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 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 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 int 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 char ppp_devnam[MAXPATHLEN]; +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 int listen_time; /* time to listen first (ms) */ + +/* 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 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 */ + +#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 + +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 + +/* + * 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; + +#define ppp_send_config(unit, mtu, accm, pc, acc) \ +do { \ + if (the_channel->send_config) \ + (*the_channel->send_config)((mtu), (accm), (pc), (acc)); \ +} while (0) + +#define ppp_recv_config(unit, mtu, accm, pc, acc) \ +do { \ + if (the_channel->send_config) \ + (*the_channel->recv_config)((mtu), (accm), (pc), (acc)); \ +} while (0) + +/* + * 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 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)); + /* Run program prog with args in child */ +void reopen_log __P((void)); /* (re)open the connection to syslog */ +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)); + +/* 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((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 */ + +/* Procedures exported from auth.c */ +void link_required __P((int)); /* we are starting to use the link */ +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 link_established __P((int)); /* the link is up; authenticate now */ +void start_networks __P((void)); /* start all the network 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, 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)); + /* 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 auth_ip_addr __P((int, u_int32_t)); + /* check if IP address 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 */ +void mp_check_options __P((void)); /* Check multilink-related options */ +int mp_join_bundle __P((void)); /* join our link to an appropriate bundle */ +char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */ +int str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */ + +/* 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 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 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 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 */ +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 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 void (*ip_up_hook) __P((void)); +extern void (*ip_down_hook) __P((void)); +extern void (*ip_choose_hook) __P((u_int32_t *)); + +/* + * 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 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 + +/* + * 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 + +#endif /* __PPP_H__ */ diff --git a/mdk-stage1/ppp/pppd/pppd.h.wtmp b/mdk-stage1/ppp/pppd/pppd.h.wtmp new file mode 100644 index 000000000..4d440be06 --- /dev/null +++ b/mdk-stage1/ppp/pppd/pppd.h.wtmp @@ -0,0 +1,789 @@ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * TODO: + */ + +#ifndef __PPPD_H__ +#define __PPPD_H__ + +#include <stdio.h> /* for FILE */ +#include <limits.h> /* for NGROUPS_MAX */ +#include <sys/param.h> /* for MAXPATHLEN and BSD4_4, if defined */ +#include <sys/types.h> /* for u_int32_t, if defined */ +#include <sys/time.h> /* for struct timeval */ +#include <net/ppp_defs.h> +#include "patchlevel.h" + +#if defined(__STDC__) +#include <stdarg.h> +#define __V(x) x +#else +#include <varargs.h> +#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; + 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 /* OR in argument to value */ +#define OPT_INC 0x800 /* increment value */ +#define OPT_PRIV 0x1000 /* privileged option */ +#define OPT_STATIC 0x2000 /* string option goes into static array */ +#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 0x10000 /* for o_special, keep list of values */ +#define OPT_NOINCR 0x20000 /* value mustn't be increased */ +#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; +}; + +/* 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 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 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 int 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 char ppp_devnam[MAXPATHLEN]; +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 int listen_time; /* time to listen first (ms) */ + +/* 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 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 */ + +#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 + +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 + +/* + * 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; + +#define ppp_send_config(unit, mtu, accm, pc, acc) \ +do { \ + if (the_channel->send_config) \ + (*the_channel->send_config)((mtu), (accm), (pc), (acc)); \ +} while (0) + +#define ppp_recv_config(unit, mtu, accm, pc, acc) \ +do { \ + if (the_channel->send_config) \ + (*the_channel->recv_config)((mtu), (accm), (pc), (acc)); \ +} while (0) + +/* + * 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 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)); + /* Run program prog with args in child */ +void reopen_log __P((void)); /* (re)open the connection to syslog */ +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)); + +/* 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((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 */ + +/* Procedures exported from auth.c */ +void link_required __P((int)); /* we are starting to use the link */ +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 link_established __P((int)); /* the link is up; authenticate now */ +void start_networks __P((void)); /* start all the network 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, 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)); + /* 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 auth_ip_addr __P((int, u_int32_t)); + /* check if IP address 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 */ +void mp_check_options __P((void)); /* Check multilink-related options */ +int mp_join_bundle __P((void)); /* join our link to an appropriate bundle */ +char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */ +int str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */ + +/* 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 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 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 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 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 void (*ip_up_hook) __P((void)); +extern void (*ip_down_hook) __P((void)); +extern void (*ip_choose_hook) __P((u_int32_t *)); + +/* + * 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 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 + +/* + * 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 + +#endif /* __PPP_H__ */ diff --git a/mdk-stage1/ppp/pppd/sys-linux.c b/mdk-stage1/ppp/pppd/sys-linux.c new file mode 100644 index 000000000..d341bb3de --- /dev/null +++ b/mdk-stage1/ppp/pppd/sys-linux.c @@ -0,0 +1,2672 @@ +/* + * sys-linux.c - System-dependent procedures for setting up + * PPP interfaces on Linux systems + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/errno.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <sys/sysmacros.h> + +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <string.h> +#include <time.h> +#include <memory.h> +#include <utmp.h> +#include <mntent.h> +#include <signal.h> +#include <fcntl.h> +#include <ctype.h> +#include <termios.h> +#include <unistd.h> + +/* 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 <asm/types.h> /* glibc 2 conflicts with linux/types.h */ +#include <net/if.h> +#include <net/if_arp.h> +#include <net/route.h> +#include <netinet/if_ether.h> +#else +#include <linux/types.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/route.h> +#include <linux/if_ether.h> +#endif +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <linux/ppp_defs.h> +#include <linux/if_ppp.h> + +#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 <netipx/ipx.h> +#else +#include <linux/ipx.h> +#endif +#endif /* IPX_CHANGE */ + +#ifdef PPP_FILTER +#include <net/bpf.h> +#include <linux/filter.h> +#endif /* PPP_FILTER */ + +#ifdef LOCKLIB +#include <sys/locks.h> +#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; +static int master_fd = -1; +#ifdef INET6 +static int sock6_fd = -1; +#endif /* INET6 */ +static 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 */ + +static 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 u_int32_t default_route_gateway; /* 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 get_flags (int fd); +static void set_flags (int fd, int flags); +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); +static void restore_loop(void); /* Transfer ppp unit back to loopback */ + +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) +{ + SYSDEBUG ((LOG_DEBUG, "setting ppp_fd to %d\n", 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; +} + +/******************************************************************** + * + * Functions to read and set the flags value in the device driver + */ + +static int get_flags (int fd) +{ + int flags; + + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) { + if ( ok_error (errno) ) + flags = 0; + else + fatal("ioctl(PPPIOCGFLAGS): %m"); + } + + SYSDEBUG ((LOG_DEBUG, "get flags = %x\n", flags)); + return flags; +} + +/********************************************************************/ + +static void set_flags (int fd, int flags) +{ + SYSDEBUG ((LOG_DEBUG, "set flags = %x\n", flags)); + + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &flags) < 0) { + if (! ok_error (errno) ) + fatal("ioctl(PPPIOCSFLAGS, %x): %m", flags, errno); + } +} + +/******************************************************************** + * + * sys_init - System-dependent initialization. + */ + +void sys_init(void) +{ + int flags; + + if (new_style_driver) { + 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"); + } + + /* 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 (default_route_gateway != 0) + cifdefaultroute(0, 0, default_route_gateway); + + 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) + close(ppp_dev_fd); + if (sock_fd >= 0) + close(sock_fd); + if (slave_fd >= 0) + close(slave_fd); + if (master_fd >= 0) + close(master_fd); + closelog(); +} + +/******************************************************************** + * + * set_kdebugflag - Define the debugging level for the kernel + */ + +static int set_kdebugflag (int requested_level) +{ + if (new_style_driver && ifunit < 0) + return 1; + if (ioctl(ppp_dev_fd, PPPIOCSDEBUG, &requested_level) < 0) { + if ( ! ok_error (errno) ) + error("ioctl(PPPIOCSDEBUG): %m"); + return (0); + } + SYSDEBUG ((LOG_INFO, "set kernel debugging level to %d", + requested_level)); + return (1); +} + +/******************************************************************** + * + * tty_establish_ppp - Turn the serial port into a ppp interface. + */ + +int tty_establish_ppp (int tty_fd) +{ + int x; + int fd = -1; + +/* + * 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"); + 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; + } + } + + if (new_style_driver) { + /* Open another instance of /dev/ppp and connect the channel to it */ + int flags; + + if (ioctl(tty_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; + } + 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) + set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) & ~SC_LOOP_TRAFFIC); + + 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 (tty_fd); + if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) { + if (ok_error (errno)) + goto err; + fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno); + } + /* 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(tty_fd, F_GETFL); + if (initfdflags == -1 || + fcntl(tty_fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) { + if ( ! ok_error (errno)) + warn("Couldn't set device to non-blocking mode: %m"); + } + } + + looped = 0; + + /* + * Enable debug in the driver if requested. + */ + if (!looped) + set_kdebugflag (kdebugflag); + +#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) + + set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB)) + | ((kdebugflag * SC_DEBUG) & SC_LOGB))); + + SYSDEBUG ((LOG_NOTICE, "Using version %d.%d.%d of PPP driver", + driver_version, driver_modification, driver_patch)); + + return ppp_fd; + + err_close: + close(fd); + err: + if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno)) + warn("Couldn't reset tty to normal line discipline: %m"); + return -1; +} + +/******************************************************************** + * + * tty_disestablish_ppp - Restore the serial port 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 tty_disestablish_ppp(int tty_fd) +{ + if (demand) + restore_loop(); + if (!hungup) { +/* + * Flush the tty output buffer so that the TIOCSETD doesn't hang. + */ + if (tcflush(tty_fd, TCIOFLUSH) < 0) + warn("tcflush failed: %m"); +/* + * Restore the previous line discipline + */ + if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0) { + if ( ! ok_error (errno)) + error("ioctl(TIOCSETD, N_TTY): %m"); + } + + if (ioctl(tty_fd, TIOCNXCL, 0) < 0) { + if ( ! ok_error (errno)) + warn("ioctl(TIOCNXCL): %m(%d)", errno); + } + + /* 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"); + } + } + initfdflags = -1; + + if (new_style_driver) { + close(ppp_fd); + ppp_fd = -1; + if (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0) + error("Couldn't release PPP unit: %m"); + if (!multilink) + remove_fd(ppp_dev_fd); + } +} + +/* + * make_ppp_unit - make a new ppp unit for ppp_dev_fd. + * Assumes new_style_driver. + */ +static int make_ppp_unit() +{ + int x; + + 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"); + 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) +{ + int flags; + + 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"); + flags = get_flags(ppp_dev_fd); + flags &= ~(SC_MP_SHORTSEQ | SC_MP_XSHORTSEQ); + flags |= (rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0) + | (mrru? SC_MULTILINK: 0); + + set_flags(ppp_dev_fd, flags); + + /* 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) +{ + if (!new_style_driver) + return -1; + + if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) { + if (errno == ENXIO) + 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); + set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK); + + ifunit = ifnum; + return 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 + { 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(%d)", errno); + 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); + } + + if (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0) + if (!ok_error(errno)) + fatal("tcsetattr: %m"); + + 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"); + } + } +} + +/******************************************************************** + * + * output - Output PPP packet. + */ + +void output (int unit, unsigned char *p, int len) +{ + int fd = ppp_fd; + int proto; + + if (debug) + dbglog("sent %P", p, len); + + if (len < PPP_HDRLEN) + return; + if (new_style_driver) { + p += 2; + len -= 2; + proto = (p[0] << 8) + p[1]; + if (ifunit >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG)) + fd = ppp_dev_fd; + } + if (write(fd, p, len) < 0) { + if (errno == EWOULDBLOCK || 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(%d)", errno); +} + +/* + * add_fd - add an fd to the set that wait_input waits for. + */ +void add_fd(int 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 != EIO && errno != EINTR) + error("read: %m"); + if (nr < 0 && errno == ENXIO) + return 0; + } + if (nr < 0 && new_style_driver && ifunit >= 0) { + /* 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 != EIO && errno != EINTR) + error("read /dev/ppp: %m"); + if (nr < 0 && errno == ENXIO) + return 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) + 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; + + SYSDEBUG ((LOG_DEBUG, "netif_set_mtu: mtu = %d\n", mtu)); + + 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) + fatal("ioctl(SIOCSIFMTU): %m"); +} + +/******************************************************************** + * + * 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) +{ + u_int x; + +/* + * Set the asyncmap and other parameters for the ppp device + */ + if (!still_ppp()) + return; + link_mtu = mtu; + SYSDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap)); + if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) { + if (!ok_error(errno)) + fatal("ioctl(PPPIOCSASYNCMAP): %m(%d)", errno); + return; + } + + x = get_flags(ppp_fd); + x = pcomp ? x | SC_COMP_PROT : x & ~SC_COMP_PROT; + x = accomp ? x | SC_COMP_AC : x & ~SC_COMP_AC; + x = sync_serial ? x | SC_SYNC : x & ~SC_SYNC; + set_flags(ppp_fd, x); +} + +/******************************************************************** + * + * tty_set_xaccm - set the extended transmit ACCM for the interface. + */ + +void tty_set_xaccm (ext_accm accm) +{ + SYSDEBUG ((LOG_DEBUG, "set_xaccm: %08lx %08lx %08lx %08lx\n", + accm[0], accm[1], accm[2], accm[3])); + + if (!still_ppp()) + return; + if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) { + if ( ! ok_error (errno)) + warn("ioctl(set extended ACCM): %m(%d)", errno); + } +} + +/******************************************************************** + * + * 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) +{ + SYSDEBUG ((LOG_DEBUG, "recv_config: mru = %d\n", mru)); +/* + * 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 ( ! ok_error (errno)) + error("ioctl(PPPIOCSMRU): %m(%d)", errno); + } + if (new_style_driver && ifunit >= 0 + && ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) + error("Couldn't set MRU in generic PPP layer: %m"); + + SYSDEBUG ((LOG_DEBUG, "recv_config: asyncmap = %lx\n", asyncmap)); + if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) { + if (!ok_error(errno)) + error("ioctl(PPPIOCSRASYNCMAP): %m(%d)", errno); + } +} + +/******************************************************************** + * + * 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) +{ + if (still_ppp()) { + int x = get_flags(ppp_dev_fd); + x = isopen? x | SC_CCP_OPEN : x &~ SC_CCP_OPEN; + x = isup? x | SC_CCP_UP : x &~ SC_CCP_UP; + set_flags (ppp_dev_fd, 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; + 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 x = get_flags(ppp_dev_fd); + + return x & 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) { + u_int32_t old_gateway = SIN_ADDR(rt.rt_gateway); + + if (old_gateway != gateway) + error("not replacing existing default route to %s [%I]", + rt.rt_dev, old_gateway); + return 0; + } + + memset (&rt, '\0', sizeof (rt)); + SET_SA_FAMILY (rt.rt_dst, AF_INET); + SET_SA_FAMILY (rt.rt_gateway, AF_INET); + + if (kernel_version > KVERSION(2,1,0)) { + SET_SA_FAMILY (rt.rt_genmask, AF_INET); + SIN_ADDR(rt.rt_genmask) = 0L; + } + + SIN_ADDR(rt.rt_gateway) = gateway; + + rt.rt_flags = RTF_UP | RTF_GATEWAY; + if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) { + if ( ! ok_error ( errno )) + error("default route ioctl(SIOCADDRT): %m(%d)", errno); + return 0; + } + + default_route_gateway = gateway; + 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; + + default_route_gateway = 0; + + memset (&rt, '\0', sizeof (rt)); + SET_SA_FAMILY (rt.rt_dst, AF_INET); + SET_SA_FAMILY (rt.rt_gateway, AF_INET); + + if (kernel_version > KVERSION(2,1,0)) { + SET_SA_FAMILY (rt.rt_genmask, AF_INET); + SIN_ADDR(rt.rt_genmask) = 0L; + } + + SIN_ADDR(rt.rt_gateway) = gateway; + + rt.rt_flags = RTF_UP | RTF_GATEWAY; + if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) { + if (still_ppp()) { + if ( ! ok_error ( errno )) + error("default route ioctl(SIOCDELRT): %m (%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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; + struct ifconf ifc; + struct ifreq ifs[MAX_IFS]; + + ifc.ifc_len = sizeof(ifs); + ifc.ifc_req = ifs; + if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) { + if ( ! ok_error ( errno )) + error("ioctl(SIOCGIFCONF): %m(%d)", errno); + return 0; + } + + SYSDEBUG ((LOG_DEBUG, "proxy arp: scanning %d interfaces for IP %s", + ifc.ifc_len / sizeof(struct ifreq), ip_ntoa(ipaddr))); +/* + * Scan through looking for an interface with an Internet + * address on the same subnet as `ipaddr'. + */ + ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq)); + for (ifr = ifc.ifc_req; ifr < ifend; ifr++) { + if (ifr->ifr_addr.sa_family == AF_INET) { + ina = SIN_ADDR(ifr->ifr_addr); + strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); + SYSDEBUG ((LOG_DEBUG, "proxy arp: examining interface %s", + 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); + SYSDEBUG ((LOG_DEBUG, "proxy arp: interface addr %s mask %lx", + ip_ntoa(ina), ntohl(mask))); + + if (((ipaddr ^ ina) & mask) != 0) + continue; + break; + } + } + + if (ifr >= ifend) + return 0; + + strlcpy(name, ifreq.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 (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr)); + if (ioctl (sock_fd, SIOCGIFHWADDR, &ifreq) < 0) { + error("SIOCGIFHWADDR(%s): %m(%d)", ifreq.ifr_name, errno); + return 0; + } + + memcpy (hwaddr, + &ifreq.ifr_hwaddr, + sizeof (struct sockaddr)); + + SYSDEBUG ((LOG_DEBUG, + "proxy arp: found hwaddr %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + (int) ((unsigned char *) &hwaddr->sa_data)[0], + (int) ((unsigned char *) &hwaddr->sa_data)[1], + (int) ((unsigned char *) &hwaddr->sa_data)[2], + (int) ((unsigned char *) &hwaddr->sa_data)[3], + (int) ((unsigned char *) &hwaddr->sa_data)[4], + (int) ((unsigned char *) &hwaddr->sa_data)[5], + (int) ((unsigned char *) &hwaddr->sa_data)[6], + (int) ((unsigned char *) &hwaddr->sa_data)[7])); + 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(%d)", errno); + 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(%d)", errno); + } 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; + struct ifreq ifr; + int size; + int my_version, my_modification, my_patch; + int osmaj, osmin, ospatch; + + 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"; + + /* 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 0 + if (fd < 0 && errno == ENOENT) { + /* try making it and see if that helps. */ + if (mknod("/dev/ppp", S_IFCHR | S_IRUSR | S_IWUSR, + makedev(108, 0)) >= 0) { + fd = open("/dev/ppp", O_RDWR); + if (fd >= 0) + info("Created /dev/ppp device node"); + else + unlink("/dev/ppp"); /* didn't work, undo the mknod */ + } else if (errno == EEXIST) { + fd = open("/dev/ppp", O_RDWR); + } + } +#endif /* 0 */ + 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; + } + if (kernel_version >= KVERSION(2,3,13)) { + if (errno == ENOENT) + no_ppp_msg = + "pppd is unable to open the /dev/ppp device.\n" + "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"; + return 0; + } + +/* + * 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; +} + +/******************************************************************** + * + * sifvjcomp - config tcp header compression + */ + +int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid) +{ + u_int x = get_flags(ppp_dev_fd); + + if (vjcomp) { + if (ioctl (ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) { + if (! ok_error (errno)) + error("ioctl(PPPIOCSMAXCID): %m(%d)", errno); + vjcomp = 0; + } + } + + x = vjcomp ? x | SC_COMP_TCP : x &~ SC_COMP_TCP; + x = cidcomp ? x & ~SC_NO_TCP_CCID : x | SC_NO_TCP_CCID; + set_flags (ppp_dev_fd, 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + } + else { + warn("ioctl(SIOCSIFADDR): Address already exists"); + } + return (0); + } +/* + * Set the gateway address + */ + SIN_ADDR(ifr.ifr_dstaddr) = his_adr; + if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) { + if (! ok_error (errno)) + error("ioctl(SIOCSIFDSTADDR): %m(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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 (%d)", errno); + 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 (%d)", errno); + 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 (%d)", errno); + 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 (%d)", errno); + 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 (%d)", errno); + } + 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); + set_flags(ppp_dev_fd, 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"); + SYSDEBUG(("using %s for loopback", loop_name)); + + 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(%d)", errno); + + 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(%d)", errno); + + if (ioctl(ppp_fd, TIOCSETD, &ppp_disc) < 0) + fatal("ioctl(TIOCSETD): %m(%d)", errno); +/* + * Find out which interface we were given. + */ + if (ioctl(ppp_fd, PPPIOCGUNIT, &ifunit) < 0) + fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno); +/* + * Enable debug in the driver if requested. + */ + set_kdebugflag (kdebugflag); + + return master_fd; +} + +/******************************************************************** + * + * restore_loop - reattach the ppp unit to the loopback. + * + * The kernel ppp driver automatically reattaches the ppp unit to + * the loopback if the serial port is set to a line discipline other + * than ppp, or if it detects a modem hangup. The former will happen + * in disestablish_ppp if the latter hasn't already happened, so we + * shouldn't need to do anything. + * + * Just to be sure, set the real serial port to the normal discipline. + */ + +static void +restore_loop(void) +{ + looped = 1; + if (new_style_driver) { + set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_LOOP_TRAFFIC); + return; + } + if (ppp_fd != slave_fd) { + (void) ioctl(ppp_fd, TIOCSETD, &tty_disc); + set_ppp_fd(slave_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 (%d)", + proto, mode, errno); + 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 (%d)", errno); + 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 (%d)", errno); + } + 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 (%d)", errno); + 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 (%d)", errno); + 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")) == 0 + || 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; +} diff --git a/mdk-stage1/ppp/pppd/sys-linux.c.wtmp b/mdk-stage1/ppp/pppd/sys-linux.c.wtmp new file mode 100644 index 000000000..f1b48423e --- /dev/null +++ b/mdk-stage1/ppp/pppd/sys-linux.c.wtmp @@ -0,0 +1,2750 @@ +/* + * sys-linux.c - System-dependent procedures for setting up + * PPP interfaces on Linux systems + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/errno.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <sys/sysmacros.h> + +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <string.h> +#include <time.h> +#include <memory.h> +#include <utmp.h> +#include <mntent.h> +#include <signal.h> +#include <fcntl.h> +#include <ctype.h> +#include <termios.h> +#include <unistd.h> + +/* 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 <asm/types.h> /* glibc 2 conflicts with linux/types.h */ +#include <net/if.h> +#include <net/if_arp.h> +#include <net/route.h> +#include <netinet/if_ether.h> +#else +#include <linux/types.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/route.h> +#include <linux/if_ether.h> +#endif +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <linux/ppp_defs.h> +#include <linux/if_ppp.h> + +#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 <netipx/ipx.h> +#else +#include <linux/ipx.h> +#endif +#endif /* IPX_CHANGE */ + +#ifdef PPP_FILTER +#include <net/bpf.h> +#include <linux/filter.h> +#endif /* PPP_FILTER */ + +#ifdef LOCKLIB +#include <sys/locks.h> +#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; +static int master_fd = -1; +#ifdef INET6 +static int sock6_fd = -1; +#endif /* INET6 */ +static 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 */ + +static 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 u_int32_t default_route_gateway; /* 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 get_flags (int fd); +static void set_flags (int fd, int flags); +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); +static void restore_loop(void); /* Transfer ppp unit back to loopback */ + +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) +{ + SYSDEBUG ((LOG_DEBUG, "setting ppp_fd to %d\n", 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; +} + +/******************************************************************** + * + * Functions to read and set the flags value in the device driver + */ + +static int get_flags (int fd) +{ + int flags; + + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) { + if ( ok_error (errno) ) + flags = 0; + else + fatal("ioctl(PPPIOCGFLAGS): %m"); + } + + SYSDEBUG ((LOG_DEBUG, "get flags = %x\n", flags)); + return flags; +} + +/********************************************************************/ + +static void set_flags (int fd, int flags) +{ + SYSDEBUG ((LOG_DEBUG, "set flags = %x\n", flags)); + + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &flags) < 0) { + if (! ok_error (errno) ) + fatal("ioctl(PPPIOCSFLAGS, %x): %m", flags, errno); + } +} + +/******************************************************************** + * + * sys_init - System-dependent initialization. + */ + +void sys_init(void) +{ + int flags; + + if (new_style_driver) { + 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"); + } + + /* 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 (default_route_gateway != 0) + cifdefaultroute(0, 0, default_route_gateway); + + 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) + close(ppp_dev_fd); + if (sock_fd >= 0) + close(sock_fd); + if (slave_fd >= 0) + close(slave_fd); + if (master_fd >= 0) + close(master_fd); + closelog(); +} + +/******************************************************************** + * + * set_kdebugflag - Define the debugging level for the kernel + */ + +static int set_kdebugflag (int requested_level) +{ + if (new_style_driver && ifunit < 0) + return 1; + if (ioctl(ppp_dev_fd, PPPIOCSDEBUG, &requested_level) < 0) { + if ( ! ok_error (errno) ) + error("ioctl(PPPIOCSDEBUG): %m"); + return (0); + } + SYSDEBUG ((LOG_INFO, "set kernel debugging level to %d", + requested_level)); + return (1); +} + +/******************************************************************** + * + * tty_establish_ppp - Turn the serial port into a ppp interface. + */ + +int tty_establish_ppp (int tty_fd) +{ + int x; + int fd = -1; + +/* + * 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"); + 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; + } + } + + if (new_style_driver) { + /* Open another instance of /dev/ppp and connect the channel to it */ + int flags; + + if (ioctl(tty_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; + } + 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) + set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) & ~SC_LOOP_TRAFFIC); + + 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 (tty_fd); + if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) { + if (ok_error (errno)) + goto err; + fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno); + } + /* 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(tty_fd, F_GETFL); + if (initfdflags == -1 || + fcntl(tty_fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) { + if ( ! ok_error (errno)) + warn("Couldn't set device to non-blocking mode: %m"); + } + } + + looped = 0; + + /* + * Enable debug in the driver if requested. + */ + if (!looped) + set_kdebugflag (kdebugflag); + +#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) + + set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB)) + | ((kdebugflag * SC_DEBUG) & SC_LOGB))); + + SYSDEBUG ((LOG_NOTICE, "Using version %d.%d.%d of PPP driver", + driver_version, driver_modification, driver_patch)); + + return ppp_fd; + + err_close: + close(fd); + err: + if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno)) + warn("Couldn't reset tty to normal line discipline: %m"); + return -1; +} + +/******************************************************************** + * + * tty_disestablish_ppp - Restore the serial port 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 tty_disestablish_ppp(int tty_fd) +{ + if (demand) + restore_loop(); + if (!hungup) { +/* + * Flush the tty output buffer so that the TIOCSETD doesn't hang. + */ + if (tcflush(tty_fd, TCIOFLUSH) < 0) + warn("tcflush failed: %m"); +/* + * Restore the previous line discipline + */ + if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0) { + if ( ! ok_error (errno)) + error("ioctl(TIOCSETD, N_TTY): %m"); + } + + if (ioctl(tty_fd, TIOCNXCL, 0) < 0) { + if ( ! ok_error (errno)) + warn("ioctl(TIOCNXCL): %m(%d)", errno); + } + + /* 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"); + } + } + initfdflags = -1; + + if (new_style_driver) { + close(ppp_fd); + ppp_fd = -1; + if (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0) + error("Couldn't release PPP unit: %m"); + if (!multilink) + remove_fd(ppp_dev_fd); + } +} + +/* + * make_ppp_unit - make a new ppp unit for ppp_dev_fd. + * Assumes new_style_driver. + */ +static int make_ppp_unit() +{ + int x; + + 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"); + 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) +{ + int flags; + + 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"); + flags = get_flags(ppp_dev_fd); + flags &= ~(SC_MP_SHORTSEQ | SC_MP_XSHORTSEQ); + flags |= (rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0) + | (mrru? SC_MULTILINK: 0); + + set_flags(ppp_dev_fd, flags); + + /* 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) +{ + if (!new_style_driver) + return -1; + + if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) { + if (errno == ENXIO) + 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); + set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK); + + ifunit = ifnum; + return 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 + { 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(%d)", errno); + 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); + } + + if (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0) + if (!ok_error(errno)) + fatal("tcsetattr: %m"); + + 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"); + } + } +} + +/******************************************************************** + * + * output - Output PPP packet. + */ + +void output (int unit, unsigned char *p, int len) +{ + int fd = ppp_fd; + int proto; + + if (debug) + dbglog("sent %P", p, len); + + if (len < PPP_HDRLEN) + return; + if (new_style_driver) { + p += 2; + len -= 2; + proto = (p[0] << 8) + p[1]; + if (ifunit >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG)) + fd = ppp_dev_fd; + } + if (write(fd, p, len) < 0) { + if (errno == EWOULDBLOCK || 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(%d)", errno); +} + +/* + * add_fd - add an fd to the set that wait_input waits for. + */ +void add_fd(int 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 != EIO && errno != EINTR) + error("read: %m"); + if (nr < 0 && errno == ENXIO) + return 0; + } + if (nr < 0 && new_style_driver && ifunit >= 0) { + /* 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 != EIO && errno != EINTR) + error("read /dev/ppp: %m"); + if (nr < 0 && errno == ENXIO) + return 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) + 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; + + SYSDEBUG ((LOG_DEBUG, "netif_set_mtu: mtu = %d\n", mtu)); + + 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) + fatal("ioctl(SIOCSIFMTU): %m"); +} + +/******************************************************************** + * + * 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) +{ + u_int x; + +/* + * Set the asyncmap and other parameters for the ppp device + */ + if (!still_ppp()) + return; + link_mtu = mtu; + SYSDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap)); + if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) { + if (!ok_error(errno)) + fatal("ioctl(PPPIOCSASYNCMAP): %m(%d)", errno); + return; + } + + x = get_flags(ppp_fd); + x = pcomp ? x | SC_COMP_PROT : x & ~SC_COMP_PROT; + x = accomp ? x | SC_COMP_AC : x & ~SC_COMP_AC; + x = sync_serial ? x | SC_SYNC : x & ~SC_SYNC; + set_flags(ppp_fd, x); +} + +/******************************************************************** + * + * tty_set_xaccm - set the extended transmit ACCM for the interface. + */ + +void tty_set_xaccm (ext_accm accm) +{ + SYSDEBUG ((LOG_DEBUG, "set_xaccm: %08lx %08lx %08lx %08lx\n", + accm[0], accm[1], accm[2], accm[3])); + + if (!still_ppp()) + return; + if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) { + if ( ! ok_error (errno)) + warn("ioctl(set extended ACCM): %m(%d)", errno); + } +} + +/******************************************************************** + * + * 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) +{ + SYSDEBUG ((LOG_DEBUG, "recv_config: mru = %d\n", mru)); +/* + * 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 ( ! ok_error (errno)) + error("ioctl(PPPIOCSMRU): %m(%d)", errno); + } + if (new_style_driver && ifunit >= 0 + && ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) + error("Couldn't set MRU in generic PPP layer: %m"); + + SYSDEBUG ((LOG_DEBUG, "recv_config: asyncmap = %lx\n", asyncmap)); + if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) { + if (!ok_error(errno)) + error("ioctl(PPPIOCSRASYNCMAP): %m(%d)", errno); + } +} + +/******************************************************************** + * + * 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) +{ + if (still_ppp()) { + int x = get_flags(ppp_dev_fd); + x = isopen? x | SC_CCP_OPEN : x &~ SC_CCP_OPEN; + x = isup? x | SC_CCP_UP : x &~ SC_CCP_UP; + set_flags (ppp_dev_fd, 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; + 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 x = get_flags(ppp_dev_fd); + + return x & 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) { + u_int32_t old_gateway = SIN_ADDR(rt.rt_gateway); + + if (old_gateway != gateway) + error("not replacing existing default route to %s [%I]", + rt.rt_dev, old_gateway); + return 0; + } + + memset (&rt, '\0', sizeof (rt)); + SET_SA_FAMILY (rt.rt_dst, AF_INET); + SET_SA_FAMILY (rt.rt_gateway, AF_INET); + + if (kernel_version > KVERSION(2,1,0)) { + SET_SA_FAMILY (rt.rt_genmask, AF_INET); + SIN_ADDR(rt.rt_genmask) = 0L; + } + + SIN_ADDR(rt.rt_gateway) = gateway; + + rt.rt_flags = RTF_UP | RTF_GATEWAY; + if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) { + if ( ! ok_error ( errno )) + error("default route ioctl(SIOCADDRT): %m(%d)", errno); + return 0; + } + + default_route_gateway = gateway; + 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; + + default_route_gateway = 0; + + memset (&rt, '\0', sizeof (rt)); + SET_SA_FAMILY (rt.rt_dst, AF_INET); + SET_SA_FAMILY (rt.rt_gateway, AF_INET); + + if (kernel_version > KVERSION(2,1,0)) { + SET_SA_FAMILY (rt.rt_genmask, AF_INET); + SIN_ADDR(rt.rt_genmask) = 0L; + } + + SIN_ADDR(rt.rt_gateway) = gateway; + + rt.rt_flags = RTF_UP | RTF_GATEWAY; + if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) { + if (still_ppp()) { + if ( ! ok_error ( errno )) + error("default route ioctl(SIOCDELRT): %m (%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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; + struct ifconf ifc; + struct ifreq ifs[MAX_IFS]; + + ifc.ifc_len = sizeof(ifs); + ifc.ifc_req = ifs; + if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) { + if ( ! ok_error ( errno )) + error("ioctl(SIOCGIFCONF): %m(%d)", errno); + return 0; + } + + SYSDEBUG ((LOG_DEBUG, "proxy arp: scanning %d interfaces for IP %s", + ifc.ifc_len / sizeof(struct ifreq), ip_ntoa(ipaddr))); +/* + * Scan through looking for an interface with an Internet + * address on the same subnet as `ipaddr'. + */ + ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq)); + for (ifr = ifc.ifc_req; ifr < ifend; ifr++) { + if (ifr->ifr_addr.sa_family == AF_INET) { + ina = SIN_ADDR(ifr->ifr_addr); + strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); + SYSDEBUG ((LOG_DEBUG, "proxy arp: examining interface %s", + 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); + SYSDEBUG ((LOG_DEBUG, "proxy arp: interface addr %s mask %lx", + ip_ntoa(ina), ntohl(mask))); + + if (((ipaddr ^ ina) & mask) != 0) + continue; + break; + } + } + + if (ifr >= ifend) + return 0; + + strlcpy(name, ifreq.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 (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr)); + if (ioctl (sock_fd, SIOCGIFHWADDR, &ifreq) < 0) { + error("SIOCGIFHWADDR(%s): %m(%d)", ifreq.ifr_name, errno); + return 0; + } + + memcpy (hwaddr, + &ifreq.ifr_hwaddr, + sizeof (struct sockaddr)); + + SYSDEBUG ((LOG_DEBUG, + "proxy arp: found hwaddr %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + (int) ((unsigned char *) &hwaddr->sa_data)[0], + (int) ((unsigned char *) &hwaddr->sa_data)[1], + (int) ((unsigned char *) &hwaddr->sa_data)[2], + (int) ((unsigned char *) &hwaddr->sa_data)[3], + (int) ((unsigned char *) &hwaddr->sa_data)[4], + (int) ((unsigned char *) &hwaddr->sa_data)[5], + (int) ((unsigned char *) &hwaddr->sa_data)[6], + (int) ((unsigned char *) &hwaddr->sa_data)[7])); + 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(%d)", errno); + 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(%d)", errno); + } 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; + struct ifreq ifr; + int size; + int my_version, my_modification, my_patch; + int osmaj, osmin, ospatch; + + 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"; + + /* 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 0 + if (fd < 0 && errno == ENOENT) { + /* try making it and see if that helps. */ + if (mknod("/dev/ppp", S_IFCHR | S_IRUSR | S_IWUSR, + makedev(108, 0)) >= 0) { + fd = open("/dev/ppp", O_RDWR); + if (fd >= 0) + info("Created /dev/ppp device node"); + else + unlink("/dev/ppp"); /* didn't work, undo the mknod */ + } else if (errno == EEXIST) { + fd = open("/dev/ppp", O_RDWR); + } + } +#endif /* 0 */ + 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; + } + if (kernel_version >= KVERSION(2,3,13)) { + if (errno == ENOENT) + no_ppp_msg = + "pppd is unable to open the /dev/ppp device.\n" + "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"; + return 0; + } + +/* + * 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 */; + + /* Is this call really necessary? There is another one after the 'put' */ + endutent(); + + 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 = get_flags(ppp_dev_fd); + + if (vjcomp) { + if (ioctl (ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) { + if (! ok_error (errno)) + error("ioctl(PPPIOCSMAXCID): %m(%d)", errno); + vjcomp = 0; + } + } + + x = vjcomp ? x | SC_COMP_TCP : x &~ SC_COMP_TCP; + x = cidcomp ? x & ~SC_NO_TCP_CCID : x | SC_NO_TCP_CCID; + set_flags (ppp_dev_fd, 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + } + else { + warn("ioctl(SIOCSIFADDR): Address already exists"); + } + return (0); + } +/* + * Set the gateway address + */ + SIN_ADDR(ifr.ifr_dstaddr) = his_adr; + if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) { + if (! ok_error (errno)) + error("ioctl(SIOCSIFDSTADDR): %m(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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(%d)", errno); + 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 (%d)", errno); + 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 (%d)", errno); + 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 (%d)", errno); + 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 (%d)", errno); + 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 (%d)", errno); + } + 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); + set_flags(ppp_dev_fd, 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"); + SYSDEBUG(("using %s for loopback", loop_name)); + + 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(%d)", errno); + + 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(%d)", errno); + + if (ioctl(ppp_fd, TIOCSETD, &ppp_disc) < 0) + fatal("ioctl(TIOCSETD): %m(%d)", errno); +/* + * Find out which interface we were given. + */ + if (ioctl(ppp_fd, PPPIOCGUNIT, &ifunit) < 0) + fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno); +/* + * Enable debug in the driver if requested. + */ + set_kdebugflag (kdebugflag); + + return master_fd; +} + +/******************************************************************** + * + * restore_loop - reattach the ppp unit to the loopback. + * + * The kernel ppp driver automatically reattaches the ppp unit to + * the loopback if the serial port is set to a line discipline other + * than ppp, or if it detects a modem hangup. The former will happen + * in disestablish_ppp if the latter hasn't already happened, so we + * shouldn't need to do anything. + * + * Just to be sure, set the real serial port to the normal discipline. + */ + +static void +restore_loop(void) +{ + looped = 1; + if (new_style_driver) { + set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_LOOP_TRAFFIC); + return; + } + if (ppp_fd != slave_fd) { + (void) ioctl(ppp_fd, TIOCSETD, &tty_disc); + set_ppp_fd(slave_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 (%d)", + proto, mode, errno); + 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 (%d)", errno); + 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 (%d)", errno); + } + 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 (%d)", errno); + 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 (%d)", errno); + 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")) == 0 + || 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; +} diff --git a/mdk-stage1/ppp/pppd/sys-solaris.c b/mdk-stage1/ppp/pppd/sys-solaris.c new file mode 100644 index 000000000..da5f9c45a --- /dev/null +++ b/mdk-stage1/ppp/pppd/sys-solaris.c @@ -0,0 +1,2737 @@ +/* + * System-dependent procedures for pppd under Solaris 2. + * + * Parts re-written by Adi Masputra <adi.masputra@sun.com>, 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) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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. + */ + +#define RCSID "$Id$" + +#include <limits.h> +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#ifndef CRTSCTS +#include <sys/termiox.h> +#endif +#include <signal.h> +#include <utmpx.h> +#include <sys/types.h> +#include <sys/ioccom.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysmacros.h> +#include <sys/systeminfo.h> +#include <sys/dlpi.h> +#include <sys/stat.h> +#include <sys/mkdev.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/route.h> +#include <net/ppp_defs.h> +#include <net/pppio.h> +#include <netinet/in.h> +#ifdef SOL2 +#include <sys/tihdr.h> +#include <sys/tiuser.h> +#include <inet/common.h> +#include <inet/mib2.h> +#include <sys/ethernet.h> +#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 = 10; \ + 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 ? + * <adi.masputra@sun.com> + */ + 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; + + if (debug) + dbglog("sent %P", 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) + 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"); + } + } +} + +/* + * ppp_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"); + } +} + +/* + * ppp_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) + 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; + 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; + } +#if 0 /* now done in ppp_send_config */ + ifr.ifr_metric = link_mtu; + if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) { + error("Couldn't set IP MTU: %m"); + } +#endif + + 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 *p, *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_id, ifname, sizeof(utmpx.ut_id)); + strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line)); + 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; + struct termios tios; + + 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; +} diff --git a/mdk-stage1/ppp/pppd/sys-sunos4.c b/mdk-stage1/ppp/pppd/sys-sunos4.c new file mode 100644 index 000000000..3344948e9 --- /dev/null +++ b/mdk-stage1/ppp/pppd/sys-sunos4.c @@ -0,0 +1,1559 @@ +/* + * System-dependent procedures for pppd under SunOS 4. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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. + */ + +#define RCSID "$Id$" + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <signal.h> +#include <malloc.h> +#include <utmp.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/poll.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/nit_if.h> +#include <net/route.h> +#include <net/ppp_defs.h> +#include <net/pppio.h> +#include <netinet/in.h> + +#include "pppd.h" + +#if defined(sun) && defined(sparc) +#include <alloca.h> +#ifndef __GNUC__ +extern void *alloca(); +#endif +#endif /*sparc*/ + +static const char rcsid[] = RCSID; + +static int pppfd; +static int fdmuxid = -1; +static int iffd; +static int sockfd; + +static int restore_term; +static struct termios inittermios; +static struct winsize wsinfo; /* Initial window size info */ +static pid_t parent_pid; /* PID of our parent */ + +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 if_is_up; /* Interface has been marked up */ +static u_int32_t ifaddrs[2]; /* local and remote addresses */ +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 strioctl __P((int, int, void *, int, int)); + + +/* + * sys_init - System-dependent initialization. + */ +void +sys_init() +{ + int x; + + /* Get an internet socket for doing socket ioctl's on. */ + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + fatal("Couldn't create IP socket: %m"); + + /* + * We may want to send a SIGHUP to the session leader associated + * with our controlling terminal later. Because SunOS doesn't + * have getsid(), we make do with sending the signal to our + * parent process. + */ + parent_pid = getppid(); + + /* + * Open the ppp device. + */ + pppfd = open("/dev/ppp", O_RDWR | O_NONBLOCK, 0); + if (pppfd < 0) + fatal("Can't open /dev/ppp: %m"); + if (kdebugflag) { + 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"); + + /* + * Open the ppp device again and push the if_ppp module on it. + */ + iffd = open("/dev/ppp", O_RDWR, 0); + if (iffd < 0) + fatal("Can't open /dev/ppp (2): %m"); + if (kdebugflag) { + x = PPPDBG_LOG + PPPDBG_DRIVER; + strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0); + } + if (strioctl(iffd, PPPIO_ATTACH, &ifunit, sizeof(int), 0) < 0) + fatal("Couldn't attach ppp interface to device: %m"); + if (ioctl(iffd, I_PUSH, "if_ppp") < 0) + fatal("Can't push ppp interface module: %m"); + if (kdebugflag) { + x = PPPDBG_LOG + PPPDBG_IF; + strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0); + } + if (strioctl(iffd, PPPIO_NEWPPA, &ifunit, sizeof(int), 0) < 0) + fatal("Couldn't create ppp interface unit: %m"); + x = PPP_IP; + if (strioctl(iffd, PPPIO_BIND, &x, sizeof(int), 0) < 0) + fatal("Couldn't bind ppp interface to IP SAP: %m"); + + 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 shouldn't call die() because it's called from die(). + */ +void +sys_cleanup() +{ + if (if_is_up) + sifdown(0); + if (ifaddrs[0]) + cifaddr(0, ifaddrs[0], ifaddrs[1]); + if (default_route_gateway) + cifdefaultroute(0, 0, default_route_gateway); + if (proxy_arp_addr) + cifproxyarp(0, proxy_arp_addr); +} + +/* + * sys_close - Clean up in a child process before execing. + */ +void +sys_close() +{ + close(iffd); + close(pppfd); + close(sockfd); +} + +/* + * 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("/dev/ppp", &buf) >= 0; +} + +/* + * 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 + || ioctl(fd, I_POP, 0) < 0) + break; + tty_nmodules = i; + + /* Push the async hdlc module and the compressor module. */ + if (ioctl(fd, I_PUSH, "ppp_ahdl") < 0) + fatal("Couldn't push PPP Async HDLC module: %m"); + if (ioctl(fd, I_PUSH, "ppp_comp") < 0) + error("Couldn't push PPP compression module: %m"); + + /* Link the serial port under the PPP multiplexor. */ + if ((fdmuxid = ioctl(pppfd, I_LINK, fd)) < 0) + fatal("Can't link tty to PPP mux: %m"); + + return pppfd; +} + +/* + * 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 (ioctl(fd, I_POP, 0) >= 0) + ; + 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 && parent_pid > 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). + * Actually we send the signal to the process that invoked + * pppd, since SunOS doesn't have getsid(). + */ + kill(parent_pid, 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 B115200 + { 115200, B115200 }, +#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 (tcgetattr(fd, &tios) < 0) + fatal("tcgetattr: %m"); + + if (!restore_term) { + inittermios = tios; + ioctl(fd, TIOCGWINSZ, &wsinfo); + } + + tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL); + if (crtscts > 0) + tios.c_cflag |= CRTSCTS; + else if (crtscts < 0) + tios.c_cflag &= ~CRTSCTS; + + tios.c_cflag |= CS8 | CREAD | HUPCL; + if (local || !modem) + tios.c_cflag |= CLOCAL; + tios.c_iflag = IGNBRK | IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + + if (crtscts == -2) { + tios.c_iflag |= 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) + fatal("Baud rate for %s is 0; need explicit baud rate", devnam); + } + + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) + fatal("tcsetattr: %m"); + + baud_rate = inspeed = baud_rate_of(speed); + 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 (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0) + if (!hungup && errno != ENXIO) + warn("tcsetattr: %m"); + 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 SunOS, 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; + + if (debug) + dbglog("sent %P", 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) { + if (errno != EAGAIN) + fatal("poll: %m"); + /* we can get EAGAIN on a heavily loaded system, + * just wait a short time and try again. */ + usleep(50000); + } +} + +/* + * 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[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. Huh? + */ + if (debug) + dbglog("got ctrl msg len=%d", 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; +} + +/* + * ppp_send_config - configure the transmit characteristics of + * the ppp interface. + */ +void +ppp_send_config(unit, mtu, asyncmap, pcomp, accomp) + int unit, mtu; + u_int32_t asyncmap; + int pcomp, accomp; +{ + int cf[2]; + struct ifreq ifr; + + link_mtu = mtu; + if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) { + if (hungup && errno == ENXIO) + return; + error("Couldn't set MTU: %m"); + } + 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 (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) { + error("Couldn't set prot/AC compression: %m"); + } + + /* set mtu for ip as well */ + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + ifr.ifr_metric = link_mtu; + if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) { + error("Couldn't set IP MTU: %m"); + } +} + +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(unit, accm) + int unit; + ext_accm accm; +{ + if (strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) { + if (!hungup || errno != ENXIO) + warn("Couldn't set extended ACCM: %m"); + } +} + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void +ppp_recv_config(unit, mru, asyncmap, pcomp, accomp) + int unit, mru; + u_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) + return; + error("Couldn't set MRU: %m"); + } + 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 (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 (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; + 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(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(sockfd, SIOCGIFFLAGS, &ifr) < 0) { + error("Couldn't mark interface up (get): %m"); + return 0; + } + ifr.ifr_flags |= IFF_UP; + if (ioctl(sockfd, 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; + + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { + error("Couldn't mark interface down (get): %m"); + return 0; + } + if ((ifr.ifr_flags & IFF_UP) != 0) { + ifr.ifr_flags &= ~IFF_UP; + if (ioctl(sockfd, 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; +} + +#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; + + 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(sockfd, SIOCSIFNETMASK, &ifr) < 0) { + error("Couldn't set IP netmask: %m"); + } + ifr.ifr_addr.sa_family = AF_INET; + INET_ADDR(ifr.ifr_addr) = o; + if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) { + error("Couldn't set local IP address: %m"); + } + ifr.ifr_dstaddr.sa_family = AF_INET; + INET_ADDR(ifr.ifr_dstaddr) = h; + if (ioctl(sockfd, SIOCSIFDSTADDR, &ifr) < 0) { + error("Couldn't set remote IP address: %m"); + } +#if 0 /* now done in ppp_send_config */ + ifr.ifr_metric = link_mtu; + if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) { + error("Couldn't set IP MTU: %m"); + } +#endif + ifaddrs[0] = o; + ifaddrs[1] = h; + + return 1; +} + +/* + * 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; +{ + struct rtentry rt; + + bzero(&rt, sizeof(rt)); + rt.rt_dst.sa_family = AF_INET; + INET_ADDR(rt.rt_dst) = h; + rt.rt_gateway.sa_family = AF_INET; + INET_ADDR(rt.rt_gateway) = o; + rt.rt_flags = RTF_HOST; + if (ioctl(sockfd, SIOCDELRT, &rt) < 0) + error("Couldn't delete route through interface: %m"); + ifaddrs[0] = 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; + + bzero(&rt, 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(sockfd, 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; + + bzero(&rt, 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(sockfd, 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; + + bzero(&arpreq, 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(sockfd, 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; + + bzero(&arpreq, sizeof(arpreq)); + arpreq.arp_pa.sa_family = AF_INET; + INET_ADDR(arpreq.arp_pa) = hisaddr; + if (ioctl(sockfd, 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; + u_int32_t ina, mask; + struct ifreq ifreq; + struct ifconf ifc; + struct ifreq ifs[MAX_IFS]; + int nit_fd; + + ifc.ifc_len = sizeof(ifs); + ifc.ifc_req = ifs; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { + error("ioctl(SIOCGIFCONF): %m"); + return 0; + } + + /* + * Scan through looking for an interface with an Internet + * address on the same subnet as `ipaddr'. + */ + ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *) + ((char *)&ifr->ifr_addr + sizeof(struct sockaddr))) { + if (ifr->ifr_addr.sa_family == AF_INET) { + + /* + * 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(sockfd, 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(sockfd, SIOCGIFNETMASK, &ifreq) < 0) + continue; + ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr; + mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr; + if ((ipaddr & mask) != (ina & mask)) + continue; + + break; + } + } + + if (ifr >= ifend) + return 0; + info("found interface %s for proxy arp", ifr->ifr_name); + + /* + * Grab the physical address for this interface. + */ + if ((nit_fd = open("/dev/nit", O_RDONLY)) < 0) { + error("Couldn't open /dev/nit: %m"); + return 0; + } + strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); + if (ioctl(nit_fd, NIOCBIND, &ifreq) < 0 + || ioctl(nit_fd, SIOCGIFADDR, &ifreq) < 0) { + error("Couldn't get hardware address for %s: %m", + ifreq.ifr_name); + close(nit_fd); + return 0; + } + + hwaddr->sa_family = AF_UNSPEC; + memcpy(hwaddr->sa_data, ifreq.ifr_addr.sa_data, 6); + close(nit_fd); + return 1; +} + +/* + * have_route_to - determine if the system has any route to + * a given IP address. + * For demand mode to work properly, we have to ignore routes + * through our own interface. + */ +int have_route_to(addr) + u_int32_t addr; +{ + return -1; +} + +#define WTMPFILE "/usr/adm/wtmp" + +void +logwtmp(line, name, host) + const char *line, *name, *host; +{ + int fd; + struct stat buf; + struct utmp ut; + + if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0) + return; + if (!fstat(fd, &buf)) { + strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + (void)time(&ut.ut_time); + if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp)) + (void)ftruncate(fd, buf.st_size); + } + close(fd); +} + +/* + * 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; + 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. + */ + ifc.ifc_len = MAX_IFS * sizeof(struct ifreq); + ifc.ifc_req = alloca(ifc.ifc_len); + if (ifc.ifc_req == 0) + return mask; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { + warn("Couldn't get system interface list: %m"); + 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(sockfd, 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(sockfd, SIOCGIFNETMASK, &ifreq) < 0) + continue; + mask |= INET_ADDR(ifreq.ifr_addr); + } + + return mask; +} + +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; +} + +/* + * Use the hostid as part of the random number seed. + */ +int +get_host_seed() +{ + return gethostid(); +} + +#if 0 +/* + * Code for locking/unlocking the serial device. + * This code is derived from chat.c. + */ + +#if !defined(HDB) && !defined(SUNOS3) +#define HDB 1 /* ascii lock files are the default */ +#endif + +#ifndef LOCK_DIR +# if HDB +# define PIDSTRING +# define LOCK_PREFIX "/usr/spool/locks/LCK.." +# else /* HDB */ +# define LOCK_PREFIX "/usr/spool/uucp/LCK.." +# endif /* HDB */ +#endif /* LOCK_DIR */ + +static char *lock_file; /* name of lock file created */ + +/* + * lock - create a lock file for the named device. + */ +int +lock(dev) + char *dev; +{ + char hdb_lock_buffer[12]; + int fd, pid, n; + char *p; + size_t l; + + if ((p = strrchr(dev, '/')) != NULL) + dev = p + 1; + l = strlen(LOCK_PREFIX) + strlen(dev) + 1; + lock_file = malloc(l); + if (lock_file == NULL) + novm("lock file name"); + slprintf(lock_file, l, "%s%s", LOCK_PREFIX, dev); + + while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) { + if (errno == EEXIST + && (fd = open(lock_file, O_RDONLY, 0)) >= 0) { + /* Read the lock file to find out who has the device locked */ +#ifdef PIDSTRING + n = read(fd, hdb_lock_buffer, 11); + if (n > 0) { + hdb_lock_buffer[n] = 0; + pid = atoi(hdb_lock_buffer); + } +#else + n = read(fd, &pid, sizeof(pid)); +#endif + if (n <= 0) { + error("Can't read pid from lock file %s", lock_file); + close(fd); + } else { + if (kill(pid, 0) == -1 && errno == ESRCH) { + /* pid no longer exists - remove the lock file */ + if (unlink(lock_file) == 0) { + close(fd); + 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); + free(lock_file); + lock_file = NULL; + return -1; + } + +#ifdef PIDSTRING + slprintf(hdb_lock_buffer, sizeof(hdb_lock_buffer), "%10d\n", getpid()); + write(fd, hdb_lock_buffer, 11); +#else + pid = getpid(); + write(fd, &pid, sizeof pid); +#endif + + close(fd); + return 0; +} + +/* + * unlock - remove our lockfile + */ +void +unlock() +{ + if (lock_file) { + unlink(lock_file); + free(lock_file); + lock_file = NULL; + } +} +#endif /* lock stuff removed */ + +/* + * get_pty - get a pty master/slave pair and chown the slave side + * to the uid given. Assumes slave_name points to >= 12 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; + char pty_name[12]; + struct termios tios; + + sfd = -1; + 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) + break; + close(mfd); + } + } + if (sfd < 0) + return 0; + + strlcpy(slave_name, pty_name, 12); + *master_fdp = mfd; + *slave_fdp = sfd; + fchown(sfd, uid, -1); + fchmod(sfd, S_IRUSR | S_IWUSR); + if (tcgetattr(sfd, &tios) == 0) { + tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB); + tios.c_cflag |= CS8 | CREAD; + tios.c_iflag = IGNPAR | CLOCAL; + 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; +} + +/* + * SunOS doesn't have strtoul :-( + */ +unsigned long +strtoul(str, ptr, base) + char *str, **ptr; + int base; +{ + return (unsigned long) strtol(str, ptr, base); +} + +/* + * Or strerror :-( + */ +extern char *sys_errlist[]; +extern int sys_nerr; + +char * +strerror(n) + int n; +{ + static char unknown[32]; + + if (n > 0 && n < sys_nerr) + return sys_errlist[n]; + slprintf(unknown, sizeof(unknown), "Error %d", n); + return unknown; +} diff --git a/mdk-stage1/ppp/pppd/tdb.c b/mdk-stage1/ppp/pppd/tdb.c new file mode 100644 index 000000000..7fd58291e --- /dev/null +++ b/mdk-stage1/ppp/pppd/tdb.c @@ -0,0 +1,1282 @@ +/* + * Database functions + * Copyright (C) Andrew Tridgell 1999 + * + * 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 provided that this software or + * any derived work is only used as part of the PPP daemon (pppd) + * and related utilities. + * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Note: this software is also available under the Gnu Public License + * version 2 or later. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include "tdb.h" + +#define TDB_VERSION (0x26011967 + 1) +#define TDB_MAGIC (0x26011999U) +#define TDB_FREE_MAGIC (~TDB_MAGIC) +#define TDB_ALIGN 4 +#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGN) +#define DEFAULT_HASH_SIZE 128 +#define TDB_PAGE_SIZE 0x2000 +#define TDB_LEN_MULTIPLIER 10 +#define FREELIST_TOP (sizeof(struct tdb_header)) + +#define LOCK_SET 1 +#define LOCK_CLEAR 0 + +/* lock offsets */ +#define GLOBAL_LOCK 0 +#define ACTIVE_LOCK 4 +#define LIST_LOCK_BASE 1024 + +#define BUCKET(hash) ((hash) % tdb->header.hash_size) + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif + +/* 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_len rec_len; /* total byte length of record */ + tdb_off next; /* offset of the next record in the list */ + tdb_len key_len; /* byte length of key */ + tdb_len data_len; /* byte length of data */ + unsigned full_hash; /* the full 32 bit hash of the key */ + unsigned magic; /* try to catch errors */ + /* + the following union is implied + union { + char record[rec_len]; + struct { + char key[key_len]; + char data[data_len]; + } + } + */ +}; + +/* a null data record - useful for error returns */ +static TDB_DATA null_data; + +/* a byte range locking function - return 0 on success + this functions locks/unlocks 1 byte at the specified offset */ +static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, + int set, int rw_type, int lck_type) +{ +#if NOLOCK + return 0; +#else + struct flock fl; + + if (tdb->fd == -1) return 0; /* for in memory tdb */ + + if (tdb->read_only) return -1; + + fl.l_type = set==LOCK_SET?rw_type:F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = 1; + fl.l_pid = 0; + + if (fcntl(tdb->fd, lck_type, &fl) != 0) { +#if TDB_DEBUG + if (lck_type == F_SETLKW) { + printf("lock %d failed at %d (%s)\n", + set, offset, strerror(errno)); + } +#endif + tdb->ecode = TDB_ERR_LOCK; + return -1; + } + return 0; +#endif +} + +/* lock a list in the database. list -1 is the alloc list */ +static int tdb_lock(TDB_CONTEXT *tdb, int list) +{ + if (list < -1 || list >= (int)tdb->header.hash_size) { +#if TDB_DEBUG + printf("bad list %d\n", list); +#endif + return -1; + } + if (tdb->locked[list+1] == 0) { + if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_SET, + F_WRLCK, F_SETLKW) != 0) { + return -1; + } + } + tdb->locked[list+1]++; + return 0; +} + +/* unlock the database. */ +static int tdb_unlock(TDB_CONTEXT *tdb, int list) +{ + if (list < -1 || list >= (int)tdb->header.hash_size) { +#if TDB_DEBUG + printf("bad unlock list %d\n", list); +#endif + return -1; + } + + if (tdb->locked[list+1] == 0) { +#if TDB_DEBUG + printf("not locked %d\n", list); +#endif + tdb->ecode = TDB_ERR_LOCK; + return -1; + } + if (tdb->locked[list+1] == 1) { + if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_CLEAR, + F_WRLCK, F_SETLKW) != 0) { + return -1; + } + } + tdb->locked[list+1]--; + return 0; +} + +/* the hash algorithm - turn a key into an integer + This is based on the hash agorithm from gdbm */ +static unsigned tdb_hash(TDB_DATA *key) +{ + unsigned value; /* Used to compute the hash value. */ + unsigned i; /* Used to cycle through random values. */ + + /* Set the initial value from the key size. */ + value = 0x238F13AF * key->dsize; + for (i=0; i < key->dsize; i++) { + value = (value + (key->dptr[i] << (i*5 % 24))); + } + + value = (1103515243 * value + 12345); + + return value; +} + +/* find the top of the hash chain for an open database */ +static tdb_off tdb_hash_top(TDB_CONTEXT *tdb, unsigned hash) +{ + tdb_off ret; + hash = BUCKET(hash); + ret = FREELIST_TOP + (hash+1)*sizeof(tdb_off); + 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 */ +static int tdb_oob(TDB_CONTEXT *tdb, tdb_off offset) +{ + struct stat st; + if ((offset <= tdb->map_size) || (tdb->fd == -1)) return 0; + + fstat(tdb->fd, &st); + if (st.st_size <= (ssize_t)offset) { + tdb->ecode = TDB_ERR_IO; + return -1; + } + +#if HAVE_MMAP + if (tdb->map_ptr) { + munmap(tdb->map_ptr, tdb->map_size); + tdb->map_ptr = NULL; + } +#endif + + tdb->map_size = st.st_size; +#if HAVE_MMAP + tdb->map_ptr = (void *)mmap(NULL, tdb->map_size, + tdb->read_only?PROT_READ:PROT_READ|PROT_WRITE, + MAP_SHARED | MAP_FILE, tdb->fd, 0); +#endif + return 0; +} + + +/* write a lump of data at a specified offset */ +static int tdb_write(TDB_CONTEXT *tdb, tdb_off offset, const char *buf, tdb_len len) +{ + if (tdb_oob(tdb, offset + len) != 0) { + /* oops - trying to write beyond the end of the database! */ + return -1; + } + + if (tdb->map_ptr) { + memcpy(offset + (char *)tdb->map_ptr, buf, len); + } else { + if (lseek(tdb->fd, offset, SEEK_SET) != offset || + write(tdb->fd, buf, len) != (ssize_t)len) { + tdb->ecode = TDB_ERR_IO; + return -1; + } + } + return 0; +} + +/* read a lump of data at a specified offset */ +static int tdb_read(TDB_CONTEXT *tdb, tdb_off offset, char *buf, tdb_len len) +{ + if (tdb_oob(tdb, offset + len) != 0) { + /* oops - trying to read beyond the end of the database! */ + return -1; + } + + if (tdb->map_ptr) { + memcpy(buf, offset + (char *)tdb->map_ptr, len); + } else { + if (lseek(tdb->fd, offset, SEEK_SET) != offset || + read(tdb->fd, buf, len) != (ssize_t)len) { + tdb->ecode = TDB_ERR_IO; + return -1; + } + } + 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; + + buf = (char *)malloc(len); + + if (!buf) { + tdb->ecode = TDB_ERR_OOM; + return NULL; + } + + if (tdb_read(tdb, offset, buf, len) == -1) { + free(buf); + return NULL; + } + + return buf; +} + +/* convenience routine for writing a record */ +static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) +{ + return tdb_write(tdb, offset, (char *)rec, sizeof(*rec)); +} + +/* convenience routine for writing a tdb_off */ +static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d) +{ + return tdb_write(tdb, offset, (char *)d, sizeof(*d)); +} + +/* read a tdb_off from the store */ +static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d) +{ + return tdb_read(tdb, offset, (char *)d, sizeof(*d)); +} + +/* read a record and check for simple errors */ +static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) +{ + if (tdb_read(tdb, offset, (char *)rec, sizeof(*rec)) == -1) return -1; + if (rec->magic != TDB_MAGIC) { +#if TDB_DEBUG + printf("bad magic 0x%08x at offset %d\n", + rec->magic, offset); +#endif + tdb->ecode = TDB_ERR_CORRUPT; + return -1; + } + if (tdb_oob(tdb, rec->next) != 0) { + return -1; + } + return 0; +} + +/* expand the database at least length bytes by expanding the + underlying file and doing the mmap again if necessary */ +static int tdb_expand(TDB_CONTEXT *tdb, tdb_off length) +{ + struct list_struct rec; + tdb_off offset, ptr; + char b = 0; + + tdb_lock(tdb,-1); + + /* make sure we know about any previous expansions by another + process */ + tdb_oob(tdb,tdb->map_size + 1); + + /* always make room for at least 10 more records */ + length *= TDB_LEN_MULTIPLIER; + + /* and round the database up to a multiple of TDB_PAGE_SIZE */ + length = ((tdb->map_size + length + TDB_PAGE_SIZE) & ~(TDB_PAGE_SIZE - 1)) - tdb->map_size; + + /* expand the file itself */ + if (tdb->fd != -1) { + lseek(tdb->fd, tdb->map_size + length - 1, SEEK_SET); + if (write(tdb->fd, &b, 1) != 1) goto fail; + } + + /* form a new freelist record */ + offset = FREELIST_TOP; + rec.rec_len = length - sizeof(rec); + rec.magic = TDB_FREE_MAGIC; + if (ofs_read(tdb, offset, &rec.next) == -1) { + goto fail; + } + +#if HAVE_MMAP + if (tdb->fd != -1 && tdb->map_ptr) { + munmap(tdb->map_ptr, tdb->map_size); + tdb->map_ptr = NULL; + } +#endif + + tdb->map_size += length; + + if (tdb->fd == -1) { + tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size); + } + + /* write it out */ + if (rec_write(tdb, tdb->map_size - length, &rec) == -1) { + goto fail; + } + + /* link it into the free list */ + ptr = tdb->map_size - length; + if (ofs_write(tdb, offset, &ptr) == -1) goto fail; + +#if HAVE_MMAP + if (tdb->fd != -1) { + tdb->map_ptr = (void *)mmap(NULL, tdb->map_size, + PROT_READ|PROT_WRITE, + MAP_SHARED | MAP_FILE, tdb->fd, 0); + } +#endif + + tdb_unlock(tdb, -1); + return 0; + + fail: + tdb_unlock(tdb,-1); + 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) +{ + tdb_off offset, rec_ptr, last_ptr; + struct list_struct rec, lastrec, newrec; + + tdb_lock(tdb, -1); + + again: + last_ptr = 0; + offset = FREELIST_TOP; + + /* read in the freelist top */ + if (ofs_read(tdb, offset, &rec_ptr) == -1) { + goto fail; + } + + /* keep looking until we find a freelist record that is big + enough */ + while (rec_ptr) { + if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) { + goto fail; + } + + if (rec.magic != TDB_FREE_MAGIC) { +#if TDB_DEBUG + printf("bad magic 0x%08x in free list\n", rec.magic); +#endif + goto fail; + } + + if (rec.rec_len >= length) { + /* found it - now possibly split it up */ + if (rec.rec_len > length + MIN_REC_SIZE) { + length = (length + TDB_ALIGN) & ~(TDB_ALIGN-1); + + newrec.rec_len = rec.rec_len - (sizeof(rec) + length); + newrec.next = rec.next; + newrec.magic = TDB_FREE_MAGIC; + + rec.rec_len = length; + rec.next = rec_ptr + sizeof(rec) + rec.rec_len; + + if (rec_write(tdb, rec.next, &newrec) == -1) { + goto fail; + } + + if (rec_write(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + } + + /* remove it from the list */ + if (last_ptr == 0) { + offset = FREELIST_TOP; + + if (ofs_write(tdb, offset, &rec.next) == -1) { + goto fail; + } + } else { + lastrec.next = rec.next; + if (rec_write(tdb, last_ptr, &lastrec) == -1) { + goto fail; + } + } + + /* all done - return the new record offset */ + tdb_unlock(tdb, -1); + return rec_ptr; + } + + /* move to the next record */ + lastrec = rec; + 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: +#if TDB_DEBUG + printf("tdb_allocate failed for size %u\n", length); +#endif + tdb_unlock(tdb, -1); + 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 header; + tdb_off offset; + int i, size = 0; + tdb_off buf[16]; + + /* create the header */ + memset(&header, 0, sizeof(header)); + memcpy(header.magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1); + header.version = TDB_VERSION; + header.hash_size = hash_size; + lseek(tdb->fd, 0, SEEK_SET); + ftruncate(tdb->fd, 0); + + if (tdb->fd != -1 && write(tdb->fd, &header, sizeof(header)) != + sizeof(header)) { + tdb->ecode = TDB_ERR_IO; + return -1; + } else size += sizeof(header); + + /* the freelist and hash pointers */ + offset = 0; + memset(buf, 0, sizeof(buf)); + + for (i=0;(hash_size+1)-i >= 16; i += 16) { + if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(buf)) != + sizeof(buf)) { + tdb->ecode = TDB_ERR_IO; + return -1; + } else size += sizeof(buf); + } + + for (;i<hash_size+1; i++) { + if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(tdb_off)) != + sizeof(tdb_off)) { + tdb->ecode = TDB_ERR_IO; + return -1; + } else size += sizeof(tdb_off); + } + + if (tdb->fd == -1) { + tdb->map_ptr = calloc(size, 1); + tdb->map_size = size; + if (tdb->map_ptr == NULL) { + tdb->ecode = TDB_ERR_IO; + return -1; + } + memcpy(&tdb->header, &header, sizeof(header)); + } + +#if TDB_DEBUG + printf("initialised database of hash_size %u\n", + hash_size); +#endif + return 0; +} + +/* 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, unsigned int hash, + struct list_struct *rec) +{ + tdb_off offset, rec_ptr; + + /* find the top of the hash chain */ + offset = tdb_hash_top(tdb, hash); + + /* read in the hash top */ + if (ofs_read(tdb, offset, &rec_ptr) == -1) + return 0; + + /* keep looking until we find the right record */ + while (rec_ptr) { + if (rec_read(tdb, rec_ptr, rec) == -1) + return 0; + + if (hash == rec->full_hash && key.dsize == rec->key_len) { + char *k; + /* a very likely hit - read the key */ + k = tdb_alloc_read(tdb, rec_ptr + sizeof(*rec), + rec->key_len); + + if (!k) + return 0; + + if (memcmp(key.dptr, k, key.dsize) == 0) { + free(k); + return rec_ptr; + } + free(k); + } + + /* move to the next record */ + rec_ptr = rec->next; + } + return 0; +} + +/* + return an error string for the last tdb error +*/ +char *tdb_error(TDB_CONTEXT *tdb) +{ + int i; + static struct { + enum TDB_ERROR ecode; + 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"}, + {-1, NULL}}; + if (tdb != NULL) { + for (i=0;emap[i].estring;i++) { + if (tdb->ecode == emap[i].ecode) return emap[i].estring; + } + } else { + return "Invalid tdb context"; + } + 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 +*/ +int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf) +{ + unsigned hash; + struct list_struct rec; + tdb_off rec_ptr; + int ret = -1; + + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_update() called with null context\n"); +#endif + return -1; + } + + /* find which hash bucket it is in */ + hash = tdb_hash(&key); + + tdb_lock(tdb, BUCKET(hash)); + rec_ptr = tdb_find(tdb, key, hash, &rec); + + if (!rec_ptr) + goto out; + + /* must be long enough */ + if (rec.rec_len < key.dsize + dbuf.dsize) + goto out; + + if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len, + dbuf.dptr, dbuf.dsize) == -1) + goto out; + + if (dbuf.dsize != rec.data_len) { + /* update size */ + rec.data_len = dbuf.dsize; + ret = rec_write(tdb, rec_ptr, &rec); + } else + ret = 0; + + out: + tdb_unlock(tdb, BUCKET(hash)); + return ret; +} + +/* find an entry in the database given a key */ +TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key) +{ + unsigned hash; + tdb_off rec_ptr; + struct list_struct rec; + TDB_DATA ret = null_data; + + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_fetch() called with null context\n"); +#endif + return null_data; + } + + /* find which hash bucket it is in */ + hash = tdb_hash(&key); + + tdb_lock(tdb, BUCKET(hash)); + rec_ptr = tdb_find(tdb, key, hash, &rec); + + if (rec_ptr) { + ret.dptr = tdb_alloc_read(tdb, + rec_ptr + sizeof(rec) + rec.key_len, + rec.data_len); + ret.dsize = rec.data_len; + } + + tdb_unlock(tdb, BUCKET(hash)); + 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 +*/ +int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key) +{ + unsigned hash; + tdb_off rec_ptr; + struct list_struct rec; + + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_exists() called with null context\n"); +#endif + return 0; + } + + /* find which hash bucket it is in */ + hash = tdb_hash(&key); + + tdb_lock(tdb, BUCKET(hash)); + rec_ptr = tdb_find(tdb, key, hash, &rec); + tdb_unlock(tdb, BUCKET(hash)); + + return rec_ptr != 0; +} + +/* 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, int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void* state), void* state) +{ + int count = 0; + unsigned h; + tdb_off offset, rec_ptr; + struct list_struct rec; + char *data; + TDB_DATA key, dbuf; + + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_traverse() called with null context\n"); +#endif + return -1; + } + + /* loop over all hash chains */ + for (h = 0; h < tdb->header.hash_size; h++) { + tdb_lock(tdb, BUCKET(h)); + + /* read in the hash top */ + offset = tdb_hash_top(tdb, h); + if (ofs_read(tdb, offset, &rec_ptr) == -1) { + goto fail; + } + + /* traverse all records for this hash */ + while (rec_ptr) { + if (rec_read(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + + /* now read the full record */ + data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), + rec.key_len + rec.data_len); + if (!data) { + goto fail; + } + + key.dptr = data; + key.dsize = rec.key_len; + dbuf.dptr = data + rec.key_len; + dbuf.dsize = rec.data_len; + count++; + + if (fn && fn(tdb, key, dbuf, state) != 0) { + /* they want us to stop traversing */ + free(data); + tdb_unlock(tdb, BUCKET(h)); + return count; + } + + /* a miss - drat */ + free(data); + + /* move to the next record */ + rec_ptr = rec.next; + } + tdb_unlock(tdb, BUCKET(h)); + } + + /* return the number traversed */ + return count; + + fail: + tdb_unlock(tdb, BUCKET(h)); + return -1; +} + + +/* find the first entry in the database and return its key */ +TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb) +{ + tdb_off offset, rec_ptr; + struct list_struct rec; + unsigned hash; + TDB_DATA ret; + + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_firstkey() called with null context\n"); +#endif + return null_data; + } + + /* look for a non-empty hash chain */ + for (hash = 0, rec_ptr = 0; + hash < tdb->header.hash_size; + hash++) { + /* find the top of the hash chain */ + offset = tdb_hash_top(tdb, hash); + + tdb_lock(tdb, BUCKET(hash)); + + /* read in the hash top */ + if (ofs_read(tdb, offset, &rec_ptr) == -1) { + goto fail; + } + + if (rec_ptr) break; + + tdb_unlock(tdb, BUCKET(hash)); + } + + if (rec_ptr == 0) return null_data; + + /* we've found a non-empty chain, now read the record */ + if (rec_read(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + + /* allocate and read the key space */ + ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len); + ret.dsize = rec.key_len; + tdb_unlock(tdb, BUCKET(hash)); + return ret; + + fail: + tdb_unlock(tdb, BUCKET(hash)); + return null_data; +} + +/* find the next entry in the database, returning its key */ +TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key) +{ + unsigned hash, hbucket; + tdb_off rec_ptr, offset; + struct list_struct rec; + TDB_DATA ret; + + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_nextkey() called with null context\n"); +#endif + return null_data; + } + + /* find which hash bucket it is in */ + hash = tdb_hash(&key); + hbucket = BUCKET(hash); + + tdb_lock(tdb, hbucket); + rec_ptr = tdb_find(tdb, key, hash, &rec); + if (rec_ptr) { + /* we want the next record after this one */ + rec_ptr = rec.next; + } + + /* not found or last in hash: look for next non-empty hash chain */ + while (rec_ptr == 0) { + tdb_unlock(tdb, hbucket); + + if (++hbucket >= tdb->header.hash_size - 1) + return null_data; + + offset = tdb_hash_top(tdb, hbucket); + tdb_lock(tdb, hbucket); + /* read in the hash top */ + if (ofs_read(tdb, offset, &rec_ptr) == -1) { + tdb_unlock(tdb, hbucket); + return null_data; + } + } + + /* Read the record. */ + if (rec_read(tdb, rec_ptr, &rec) == -1) { + tdb_unlock(tdb, hbucket); + return null_data; + } + /* allocate and read the key */ + ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len); + ret.dsize = rec.key_len; + tdb_unlock(tdb, hbucket); + + return ret; +} + +/* delete an entry in the database given a key */ +int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key) +{ + unsigned hash; + tdb_off offset, rec_ptr, last_ptr; + struct list_struct rec, lastrec; + char *data = NULL; + + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_delete() called with null context\n"); +#endif + return -1; + } + + /* find which hash bucket it is in */ + hash = tdb_hash(&key); + + tdb_lock(tdb, BUCKET(hash)); + + /* find the top of the hash chain */ + offset = tdb_hash_top(tdb, hash); + + /* read in the hash top */ + if (ofs_read(tdb, offset, &rec_ptr) == -1) { + goto fail; + } + + last_ptr = 0; + + /* keep looking until we find the right record */ + while (rec_ptr) { + if (rec_read(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + + if (hash == rec.full_hash && key.dsize == rec.key_len) { + /* a very likely hit - read the record and full key */ + data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), + rec.key_len); + if (!data) { + goto fail; + } + + if (memcmp(key.dptr, data, key.dsize) == 0) { + /* a definite match - delete it */ + if (last_ptr == 0) { + offset = tdb_hash_top(tdb, hash); + if (ofs_write(tdb, offset, &rec.next) == -1) { + goto fail; + } + } else { + lastrec.next = rec.next; + if (rec_write(tdb, last_ptr, &lastrec) == -1) { + goto fail; + } + } + tdb_unlock(tdb, BUCKET(hash)); + tdb_lock(tdb, -1); + /* and recover the space */ + offset = FREELIST_TOP; + if (ofs_read(tdb, offset, &rec.next) == -1) { + goto fail2; + } + rec.magic = TDB_FREE_MAGIC; + if (rec_write(tdb, rec_ptr, &rec) == -1) { + goto fail2; + } + if (ofs_write(tdb, offset, &rec_ptr) == -1) { + goto fail2; + } + + /* yipee - all done */ + free(data); + tdb_unlock(tdb, -1); + return 0; + } + + /* a miss - drat */ + free(data); + data = NULL; + } + + /* move to the next record */ + last_ptr = rec_ptr; + lastrec = rec; + rec_ptr = rec.next; + } + + fail: + if (data) free(data); + tdb_unlock(tdb, BUCKET(hash)); + return -1; + + fail2: + if (data) free(data); + tdb_unlock(tdb, -1); + return -1; +} + + +/* 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; + unsigned hash; + tdb_off rec_ptr, offset; + char *p = NULL; + + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_store() called with null context\n"); +#endif + return -1; + } + + /* find which hash bucket it is in */ + hash = tdb_hash(&key); + + /* check for it existing */ + if (flag == TDB_INSERT && tdb_exists(tdb, key)) { + tdb->ecode = TDB_ERR_EXISTS; + return -1; + } + + /* first try in-place update */ + if (flag != TDB_INSERT && tdb_update(tdb, key, dbuf) == 0) { + return 0; + } + + rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize); + if (rec_ptr == 0) { + return -1; + } + + tdb_lock(tdb, BUCKET(hash)); + + /* delete any existing record - if it doesn't exist we don't care */ + if (flag != TDB_INSERT) { + tdb_delete(tdb, key); + } + + /* read the newly created record */ + if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) { + goto fail; + } + + if (rec.magic != TDB_FREE_MAGIC) goto fail; + + /* find the top of the hash chain */ + offset = tdb_hash_top(tdb, hash); + + /* read in the hash top diretcly into our next pointer */ + if (ofs_read(tdb, offset, &rec.next) == -1) { + goto fail; + } + + rec.key_len = key.dsize; + rec.data_len = dbuf.dsize; + rec.full_hash = hash; + rec.magic = TDB_MAGIC; + + p = (char *)malloc(sizeof(rec) + key.dsize + dbuf.dsize); + if (!p) { + tdb->ecode = TDB_ERR_OOM; + goto fail; + } + + memcpy(p, &rec, sizeof(rec)); + memcpy(p+sizeof(rec), key.dptr, key.dsize); + memcpy(p+sizeof(rec)+key.dsize, dbuf.dptr, dbuf.dsize); + + if (tdb_write(tdb, rec_ptr, p, sizeof(rec)+key.dsize+dbuf.dsize) == -1) + goto fail; + + free(p); + p = NULL; + + /* and point the top of the hash chain at it */ + if (ofs_write(tdb, offset, &rec_ptr) == -1) goto fail; + + tdb_unlock(tdb, BUCKET(hash)); + return 0; + + fail: +#if TDB_DEBUG + printf("store failed for hash 0x%08x in bucket %u\n", hash, BUCKET(hash)); +#endif + if (p) free(p); + tdb_unlock(tdb, BUCKET(hash)); + return -1; +} + + +/* 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 +*/ +TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) +{ + TDB_CONTEXT tdb, *ret; + struct stat st; + + memset(&tdb, 0, sizeof(tdb)); + + tdb.fd = -1; + tdb.name = NULL; + tdb.map_ptr = NULL; + + if ((open_flags & O_ACCMODE) == O_WRONLY) { + goto fail; + } + + if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE; + + tdb.read_only = ((open_flags & O_ACCMODE) == O_RDONLY); + + if (name != NULL) { + tdb.fd = open(name, open_flags, mode); + if (tdb.fd == -1) { + goto fail; + } + } + + /* ensure there is only one process initialising at once */ + tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_SET, F_WRLCK, F_SETLKW); + + if (tdb_flags & TDB_CLEAR_IF_FIRST) { + /* we need to zero the database if we are the only + one with it open */ + if (tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_WRLCK, F_SETLK) == 0) { + ftruncate(tdb.fd, 0); + tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLK); + } + } + + /* leave this lock in place */ + tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_RDLCK, F_SETLKW); + + 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) { + /* its not a valid database - possibly initialise it */ + if (!(open_flags & O_CREAT)) { + goto fail; + } + if (tdb_new_database(&tdb, hash_size) == -1) goto fail; + + lseek(tdb.fd, 0, SEEK_SET); + if (tdb.fd != -1 && read(tdb.fd, &tdb.header, + sizeof(tdb.header)) != + sizeof(tdb.header)) + goto fail; + } + + if (tdb.fd != -1) { + fstat(tdb.fd, &st); + + /* map the database and fill in the return structure */ + tdb.name = (char *)strdup(name); + tdb.map_size = st.st_size; + } + + tdb.locked = (int *)calloc(tdb.header.hash_size+1, + sizeof(tdb.locked[0])); + if (!tdb.locked) { + goto fail; + } + +#if HAVE_MMAP + if (tdb.fd != -1) { + tdb.map_ptr = (void *)mmap(NULL, st.st_size, + tdb.read_only? PROT_READ : PROT_READ|PROT_WRITE, + MAP_SHARED | MAP_FILE, tdb.fd, 0); + } +#endif + + ret = (TDB_CONTEXT *)malloc(sizeof(tdb)); + if (!ret) goto fail; + + *ret = tdb; + +#if TDB_DEBUG + printf("mapped database of hash_size %u map_size=%u\n", + hash_size, tdb.map_size); +#endif + + tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLKW); + return ret; + + fail: + if (tdb.name) free(tdb.name); + if (tdb.fd != -1) close(tdb.fd); + if (tdb.map_ptr) munmap(tdb.map_ptr, tdb.map_size); + + return NULL; +} + +/* close a database */ +int tdb_close(TDB_CONTEXT *tdb) +{ + if (!tdb) return -1; + + if (tdb->name) free(tdb->name); + if (tdb->fd != -1) close(tdb->fd); + if (tdb->locked) free(tdb->locked); + + if (tdb->map_ptr) { + if (tdb->fd != -1) { + munmap(tdb->map_ptr, tdb->map_size); + } else { + free(tdb->map_ptr); + } + } + + memset(tdb, 0, sizeof(*tdb)); + free(tdb); + + return 0; +} + +/* lock the database. If we already have it locked then don't do anything */ +int tdb_writelock(TDB_CONTEXT *tdb) +{ + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_writelock() called with null context\n"); +#endif + return -1; + } + + return tdb_lock(tdb, -1); +} + +/* unlock the database. */ +int tdb_writeunlock(TDB_CONTEXT *tdb) +{ + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_writeunlock() called with null context\n"); +#endif + return -1; + } + + return tdb_unlock(tdb, -1); +} + +/* lock one hash chain. This is meant to be used to reduce locking + contention - it cannot guarantee how many records will be locked */ +int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key) +{ + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_lockchain() called with null context\n"); +#endif + return -1; + } + + return tdb_lock(tdb, BUCKET(tdb_hash(&key))); +} + + +/* unlock one hash chain */ +int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key) +{ + if (tdb == NULL) { +#ifdef TDB_DEBUG + printf("tdb_unlockchain() called with null context\n"); +#endif + return -1; + } + + return tdb_unlock(tdb, BUCKET(tdb_hash(&key))); +} diff --git a/mdk-stage1/ppp/pppd/tdb.h b/mdk-stage1/ppp/pppd/tdb.h new file mode 100644 index 000000000..56ae0ac2a --- /dev/null +++ b/mdk-stage1/ppp/pppd/tdb.h @@ -0,0 +1,77 @@ +#define STANDALONE 1 +/* + * Database functions + * Copyright (C) Andrew Tridgell 1999 + * + * 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 provided that this software or + * any derived work is only used as part of the PPP daemon (pppd) + * and related utilities. + * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Note: this software is also available under the Gnu Public License + * version 2 or later. + */ + +typedef unsigned tdb_len; +typedef unsigned tdb_off; + +#define TDB_MAGIC_FOOD "TDB file\n" + +/* this is stored at the front of every database */ +struct tdb_header { + char magic_food[32]; /* for /etc/magic */ + unsigned version; /* version of the code */ + unsigned hash_size; /* number of hash entries */ +}; + +typedef struct { + char *dptr; + size_t dsize; +} TDB_DATA; + +/* this is the context structure that is returned from a db open */ +typedef struct { + 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 */ + int *locked; /* set if we have a chain locked */ + int ecode; /* error code for last tdb error */ + struct tdb_header header; /* a cached copy of the header */ +} TDB_CONTEXT; + +/* flags to tdb_store() */ +#define TDB_REPLACE 1 +#define TDB_INSERT 2 + +/* flags for tdb_open() */ +#define TDB_CLEAR_IF_FIRST 1 + +/* error codes */ +enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, + TDB_ERR_OOM, TDB_ERR_EXISTS}; + +#if STANDALONE +TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode); +char *tdb_error(TDB_CONTEXT *tdb); +int tdb_writelock(TDB_CONTEXT *tdb); +int tdb_writeunlock(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_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, + int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state), + void *state); +int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key); +#endif diff --git a/mdk-stage1/ppp/pppd/tty.c b/mdk-stage1/ppp/pppd/tty.c new file mode 100644 index 000000000..4db707968 --- /dev/null +++ b/mdk-stage1/ppp/pppd/tty.c @@ -0,0 +1,1164 @@ +/* + * tty.c - code for handling serial ports in pppd. + * + * Copyright (C) 2000 Paul Mackerras. + * All rights reserved. + * + * Portions Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <syslog.h> +#include <netdb.h> +#include <utmp.h> +#include <pwd.h> +#include <setjmp.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#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; + +/* 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 (strncmp("/dev/", cp, 5) != 0) { + 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 && connect_script == 0) { + 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", + 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; + struct stat statbuf; + 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) + return -1; + 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. + */ + hungup = 0; + kill_link = 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); + ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); + err = errno; + if (prio < OPRIO_ROOT) + seteuid(0); + if (ttyfd >= 0) + break; + errno = err; + if (err != EINTR) { + error("Failed to open %s: %m", devnam); + status = EXIT_OPEN_FAILED; + } + if (!persist || err != EINTR) + return -1; + } + real_ttyfd = 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"); + + /* + * 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; + + /* + * 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"); + 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) + return -1; + } else { + if (device_script(ptycommand, pty_master, pty_master, 1) < 0) + return -1; + ttyfd = pty_slave; + close(pty_master); + pty_master = -1; + } + } else if (pty_socket != NULL) { + int fd = open_socket(pty_socket); + if (fd < 0) + return -1; + if (!start_charshunt(fd, fd)) + return -1; + } else if (notty) { + if (!start_charshunt(0, 1)) + return -1; + } else if (record_file != NULL) { + if (!start_charshunt(ttyfd, ttyfd)) + return -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; + return -1; + } + if (kill_link) { + disconnect_tty(); + return -1; + } + info("Serial port initialized."); + } + + if (connector && connector[0]) { + if (device_script(connector, ttyfd, ttyfd, 0) < 0) { + error("Connect script failed"); + status = EXIT_CONNECT_FAILED; + return -1; + } + if (kill_link) { + disconnect_tty(); + return -1; + } + 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 || kill_link) + return -1; + } + 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) + listen_time = connect_delay; + + return ttyfd; +} + + +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."); + } +} + +void tty_close_fds() +{ + if (pty_master >= 0) + close(pty_master); + 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); + + if (tty_mode != (mode_t) -1) { + if (fchmod(real_ttyfd, tty_mode) != 0) { + /* XXX if devnam is a symlink, this will change the link */ + chmod(devnam, tty_mode); + } + } + + 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 = fork(); + if (cpid == -1) { + error("Can't fork process for character shunt: %m"); + return 0; + } + if (cpid == 0) { + /* child */ + close(pty_slave); + setuid(uid); + if (getuid() != uid) + fatal("setuid failed"); + setgid(getgid()); + if (!nodetach) + log_to_fd = -1; + charshunt(ifd, ofd, record_file); + exit(0); + } + charshunt_pid = cpid; + add_notifier(&sigreceived, stop_charshunt, 0); + close(pty_master); + pty_master = -1; + ttyfd = pty_slave; + record_child(cpid, "pppd (charshunt)", charshunt_done, NULL); + 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 + + /* + * 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; + /* do a 0-length write, hopefully this will generate + an EOF (hangup) on the slave side. */ + write(pty_master, inpacket_buf, 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; + } + } + 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; +} diff --git a/mdk-stage1/ppp/pppd/upap.c b/mdk-stage1/ppp/pppd/upap.c new file mode 100644 index 000000000..bd569fb74 --- /dev/null +++ b/mdk-stage1/ppp/pppd/upap.c @@ -0,0 +1,640 @@ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +/* + * TODO: + */ + +#include <stdio.h> +#include <string.h> + +#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; + 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); + msglen = strlen(msg); + if (msglen > 255) + msglen = 255; + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + 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); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nakk. + */ +static void +upap_rauthnak(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char msglen; + char *msg; + + 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, "<hidden>"); + 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; +} diff --git a/mdk-stage1/ppp/pppd/upap.h b/mdk-stage1/ppp/pppd/upap.h new file mode 100644 index 000000000..42d6f4f0f --- /dev/null +++ b/mdk-stage1/ppp/pppd/upap.h @@ -0,0 +1,87 @@ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * 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; diff --git a/mdk-stage1/ppp/pppd/utils.c b/mdk-stage1/ppp/pppd/utils.c new file mode 100644 index 000000000..6a5b88e8b --- /dev/null +++ b/mdk-stage1/ppp/pppd/utils.c @@ -0,0 +1,948 @@ +/* + * utils.c - various utility functions used in pppd. + * + * Copyright (c) 1999 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define RCSID "$Id$" + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <syslog.h> +#include <netdb.h> +#include <utmp.h> +#include <pwd.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#ifdef SVR4 +#include <sys/mkdev.h> +#endif + +#include "pppd.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 + * %r (recursive format), %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 '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; + 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; + 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) + 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); +} + +/* + * 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); +} + +/* 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, sizeof(lock_file), dev); + 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; + + 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; + } +} + diff --git a/mdk-stage1/ppp/pppdump/Makefile.linux b/mdk-stage1/ppp/pppdump/Makefile.linux new file mode 100644 index 000000000..1d8d78ff7 --- /dev/null +++ b/mdk-stage1/ppp/pppdump/Makefile.linux @@ -0,0 +1,17 @@ +CFLAGS= -I../include/net $(RPM_OPT_FLAGS) +OBJS = pppdump.o bsd-comp.o deflate.o zlib.o + +INSTALL= install + +all: pppdump + +pppdump: $(OBJS) + $(CC) $(RPM_OPT_FLAGS) -o pppdump $(OBJS) + +clean: + rm -f pppdump $(OBJS) *~ + +install: + mkdir -p $(BINDIR) $(MANDIR)/man8 + $(INSTALL) -s -c pppdump $(BINDIR) + $(INSTALL) -c pppdump.8 $(MANDIR)/man8 diff --git a/mdk-stage1/ppp/pppdump/Makefile.linux.makeopt b/mdk-stage1/ppp/pppdump/Makefile.linux.makeopt new file mode 100644 index 000000000..d02fecde8 --- /dev/null +++ b/mdk-stage1/ppp/pppdump/Makefile.linux.makeopt @@ -0,0 +1,17 @@ +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)/man8 + $(INSTALL) -s -c pppdump $(BINDIR) + $(INSTALL) -c -m 444 pppdump.8 $(MANDIR)/man8 diff --git a/mdk-stage1/ppp/pppdump/Makefile.linux.pppdump-Makefile b/mdk-stage1/ppp/pppdump/Makefile.linux.pppdump-Makefile new file mode 100644 index 000000000..4c98b6c6d --- /dev/null +++ b/mdk-stage1/ppp/pppdump/Makefile.linux.pppdump-Makefile @@ -0,0 +1,17 @@ +CFLAGS= -O -I../include/net +OBJS = pppdump.o bsd-comp.o deflate.o zlib.o + +INSTALL= install + +all: pppdump + +pppdump: $(OBJS) + $(CC) $(RPM_OPT_FLAGS) -o pppdump $(OBJS) + +clean: + rm -f pppdump $(OBJS) *~ + +install: + mkdir -p $(BINDIR) $(MANDIR)/man8 + $(INSTALL) -s -c pppdump $(BINDIR) + $(INSTALL) -c -m 444 pppdump.8 $(MANDIR)/man8 diff --git a/mdk-stage1/ppp/pppdump/Makefile.sol2 b/mdk-stage1/ppp/pppdump/Makefile.sol2 new file mode 100644 index 000000000..d7e6b413e --- /dev/null +++ b/mdk-stage1/ppp/pppdump/Makefile.sol2 @@ -0,0 +1,21 @@ +# +# pppdump Makefile for SVR4 systems +# $Id$ +# + +include ../solaris/Makedefs + +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 diff --git a/mdk-stage1/ppp/pppdump/Makefile.sunos4 b/mdk-stage1/ppp/pppdump/Makefile.sunos4 new file mode 100644 index 000000000..915c8267d --- /dev/null +++ b/mdk-stage1/ppp/pppdump/Makefile.sunos4 @@ -0,0 +1,21 @@ +# +# pppstats makefile +# $Id$ +# + +include ../sunos4/Makedefs + +OBJS = pppdump.o bsd-comp.o deflate.o zlib.o +CFLAGS = $(COPTS) -I../include/net + +all: pppdump + +pppdump: $(OBJS) + $(CC) -o pppdump $(OBJS) + +clean: + rm -f pppdump $(OBJS) *~ + +install: pppdump + $(INSTALL) -c pppdump $(BINDIR)/pppdump + $(INSTALL) -c -m 444 pppdump.8 $(MANDIR)/man8/pppdump.8 diff --git a/mdk-stage1/ppp/pppdump/bsd-comp.c b/mdk-stage1/ppp/pppdump/bsd-comp.c new file mode 100644 index 000000000..1e14e98bb --- /dev/null +++ b/mdk-stage1/ppp/pppdump/bsd-comp.c @@ -0,0 +1,750 @@ +/* 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$ + */ + +#include <sys/types.h> +#include <stddef.h> +#include <stdlib.h> +#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) +#define RATIO_MAX (0x7fffffff>>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 */ diff --git a/mdk-stage1/ppp/pppdump/deflate.c b/mdk-stage1/ppp/pppdump/deflate.c new file mode 100644 index 000000000..08e8abc0d --- /dev/null +++ b/mdk-stage1/ppp/pppdump/deflate.c @@ -0,0 +1,344 @@ +/* + * ppp_deflate.c - interface the zlib procedures for Deflate compression + * and decompression (as used by gzip) to the PPP code. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +#include <sys/types.h> +#include <stddef.h> +#include <stdlib.h> +#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 */ diff --git a/mdk-stage1/ppp/pppdump/ppp-comp.h b/mdk-stage1/ppp/pppdump/ppp-comp.h new file mode 100644 index 000000000..9221c0db4 --- /dev/null +++ b/mdk-stage1/ppp/pppdump/ppp-comp.h @@ -0,0 +1,150 @@ +/* + * ppp-comp.h - Definitions for doing PPP packet compression. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +#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 */ diff --git a/mdk-stage1/ppp/pppdump/pppdump.8 b/mdk-stage1/ppp/pppdump/pppdump.8 new file mode 100644 index 000000000..c0172f519 --- /dev/null +++ b/mdk-stage1/ppp/pppdump/pppdump.8 @@ -0,0 +1,62 @@ +.\" @(#) $Id$ +.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) diff --git a/mdk-stage1/ppp/pppdump/pppdump.c b/mdk-stage1/ppp/pppdump/pppdump.c new file mode 100644 index 000000000..a8e69d4bf --- /dev/null +++ b/mdk-stage1/ppp/pppdump/pppdump.c @@ -0,0 +1,502 @@ +/* + * 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 are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms. The name of the author + * may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include <stdio.h> +#include <unistd.h> +#include <time.h> +#include <sys/types.h> +#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; + +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); +} + +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]; + +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 +}; + +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; + } +} + +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); + } +} diff --git a/mdk-stage1/ppp/pppdump/zlib.c b/mdk-stage1/ppp/pppdump/zlib.c new file mode 100644 index 000000000..5b8372719 --- /dev/null +++ b/mdk-stage1/ppp/pppdump/zlib.c @@ -0,0 +1,4614 @@ +/* + * 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$ + */ + + +/*+++++*/ +/* 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 <string.h> +#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 <stdio.h> +# 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)<<s->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 <ctype.h> +#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<<extra_lbits[code]); n++) { + length_code[length++] = (uch)code; + } + } + Assert (length == 256, "ct_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + length_code[length-1] = (uch)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<<extra_dbits[code]); n++) { + dist_code[dist++] = (uch)code; + } + } + Assert (dist == 256, "ct_static_init: dist != 256"); + dist >>= 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<<MAX_BITS)-1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracec(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +local void build_tree(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->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)<<k;k+=8;}} +#define DUMPBITS(j) {b>>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (q<s->read?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)<<k;k+=8;}} +#define UNGRAB {n+=(c=k>>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; +} diff --git a/mdk-stage1/ppp/pppdump/zlib.h b/mdk-stage1/ppp/pppdump/zlib.h new file mode 100644 index 000000000..9c7ac734d --- /dev/null +++ b/mdk-stage1/ppp/pppdump/zlib.h @@ -0,0 +1,631 @@ +/* $Id$ */ + +/* + * 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 <unix.h> +#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<<windowBits bytes. If next_out is null, the + library will allocate its own buffer (and leave next_out null). next_in + need not be provided here but must be provided by the application for the + next call of inflate(). + + If the history buffer is provided by the application, next_out must + never be changed by the application since the decompressor maintains + history information inside this buffer from call to call; the application + can only reset next_out to the beginning of the history buffer when + avail_out is zero and all output has been consumed. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was + not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as + windowBits < 8). msg is set to null if there is no error message. + inflateInit2 does not perform any decompression: this will be done by + inflate(). +*/ + +extern int inflateSync OF((z_stream *strm)); +/* + Skips invalid compressed data until the special marker (see deflate() + above) can be found, or until all available input is skipped. No output + is provided. + + inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no marker has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +extern int inflateReset OF((z_stream *strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset 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 inflateIncomp OF((z_stream *strm)); +/* + This function adds the data at next_in (avail_in bytes) to the output + history without performing any output. There must be no pending output, + and the decompressor must be expecting to see the start of a block. + Calling this function is equivalent to decompressing a stored block + containing the data at next_in (except that the data is not output). +*/ + + /* checksum functions */ + +/* + This function is not related to compression but is exported + anyway because it might be useful in applications using the + compression library. +*/ + +extern uLong adler32 OF((uLong adler, Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +#ifndef _Z_UTIL_H + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +#endif /* _ZLIB_H */ diff --git a/mdk-stage1/ppp/pppstats/Makefile.linux b/mdk-stage1/ppp/pppstats/Makefile.linux new file mode 100644 index 000000000..ef0764c5d --- /dev/null +++ b/mdk-stage1/ppp/pppstats/Makefile.linux @@ -0,0 +1,32 @@ +# +# pppstats makefile +# $Id$ +# + +PPPSTATSRCS = pppstats.c +PPPSTATOBJS = pppstats.o + +#CC = gcc +COPTS = -O +COMPILE_FLAGS = -D_linux_ -I../include +LIBS = + +INSTALL= install -o root -g daemon + +CFLAGS = $(COPTS) $(COMPILE_FLAGS) + +all: pppstats + +install: pppstats + $(INSTALL) -s -c pppstats $(BINDIR)/pppstats + $(INSTALL) -c -m 444 pppstats.8 $(MANDIR)/man8/pppstats.8 + +pppstats: $(PPPSTATSRCS) + $(CC) $(CFLAGS) -o pppstats pppstats.c $(LIBS) + +clean: + rm -f pppstats *~ #* core + +depend: + cpp -M $(CFLAGS) $(PPPSTATSRCS) >.depend +# makedepend $(CFLAGS) $(PPPSTATSRCS) diff --git a/mdk-stage1/ppp/pppstats/Makefile.sol2 b/mdk-stage1/ppp/pppstats/Makefile.sol2 new file mode 100644 index 000000000..a6544e61b --- /dev/null +++ b/mdk-stage1/ppp/pppstats/Makefile.sol2 @@ -0,0 +1,20 @@ +# +# pppstats Makefile for SVR4 systems +# $Id$ +# + +include ../solaris/Makedefs + +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 diff --git a/mdk-stage1/ppp/pppstats/Makefile.sunos4 b/mdk-stage1/ppp/pppstats/Makefile.sunos4 new file mode 100644 index 000000000..2a036f28f --- /dev/null +++ b/mdk-stage1/ppp/pppstats/Makefile.sunos4 @@ -0,0 +1,30 @@ +# +# pppstats makefile +# $Id$ +# + +include ../sunos4/Makedefs + +PPPSTATSRCS = pppstats.c +PPPSTATOBJS = pppstats.o + +COMPILE_FLAGS = -DSTREAMS -DSUNOS4 +LIBS = + +CFLAGS = -I../include $(COPTS) $(COMPILE_FLAGS) + +all: pppstats + +install: pppstats + $(INSTALL) -c pppstats $(BINDIR)/pppstats + $(INSTALL) -c -m 444 pppstats.8 $(MANDIR)/man8/pppstats.8 + +pppstats: $(PPPSTATSRCS) + $(CC) $(CFLAGS) -o pppstats pppstats.c $(LIBS) + +clean: + rm -f pppstats *~ #* core + +depend: + cpp -M $(CFLAGS) $(PPPSTATSRCS) >.depend +# makedepend $(CFLAGS) $(PPPSTATSRCS) diff --git a/mdk-stage1/ppp/pppstats/pppstats.8 b/mdk-stage1/ppp/pppstats/pppstats.8 new file mode 100644 index 000000000..1ba8d5779 --- /dev/null +++ b/mdk-stage1/ppp/pppstats/pppstats.8 @@ -0,0 +1,217 @@ +.\" @(#) $Id$ +.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 <count> +] [ +.B -w +.I <secs> +] [ +.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) diff --git a/mdk-stage1/ppp/pppstats/pppstats.c b/mdk-stage1/ppp/pppstats/pppstats.c new file mode 100644 index 000000000..77b803723 --- /dev/null +++ b/mdk-stage1/ppp/pppstats/pppstats.c @@ -0,0 +1,557 @@ +/* + * 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$"; +#endif + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#ifndef STREAMS +#if defined(_linux_) && defined(__powerpc__) \ + && (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0) +/* kludge alert! */ +#undef __GLIBC__ +#endif +#include <sys/socket.h> /* *BSD, Linux, NeXT, Ultrix etc. */ +#ifndef _linux_ +#include <net/if.h> +#include <net/ppp_defs.h> +#include <net/if_ppp.h> +#else +/* Linux */ +#if __GLIBC__ >= 2 +#include <asm/types.h> /* glibc 2 conflicts with linux/types.h */ +#include <net/if.h> +#else +#include <linux/types.h> +#include <linux/if.h> +#endif +#include <linux/ppp_defs.h> +#include <linux/if_ppp.h> +#endif /* _linux_ */ + +#else /* STREAMS */ +#include <sys/stropts.h> /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */ +#include <net/ppp_defs.h> +#include <net/pppio.h> + +#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); +} diff --git a/mdk-stage1/ppp/sample/auth-down b/mdk-stage1/ppp/sample/auth-down new file mode 100644 index 000000000..edde65db1 --- /dev/null +++ b/mdk-stage1/ppp/sample/auth-down @@ -0,0 +1,17 @@ +#!/bin/sh +# +# A program or script which is executed after the remote system +# successfully authenticates itself. It is executed with the parameters +# <interface-name> <peer-name> <user-name> <tty-device> <speed> +# + +# +# The environment is cleared before executing this script +# so the path must be reset +# +PATH=/usr/sbin:/sbin:/usr/bin:/bin +export PATH + +echo auth-down `date +'%y/%m/%d %T'` $* >> /var/log/pppstats + +# last line diff --git a/mdk-stage1/ppp/sample/auth-up b/mdk-stage1/ppp/sample/auth-up new file mode 100644 index 000000000..54722a3c0 --- /dev/null +++ b/mdk-stage1/ppp/sample/auth-up @@ -0,0 +1,17 @@ +#!/bin/sh +# +# A program or script which is executed after the remote system +# successfully authenticates itself. It is executed with the parameters +# <interface-name> <peer-name> <user-name> <tty-device> <speed> +# + +# +# The environment is cleared before executing this script +# so the path must be reset +# +PATH=/usr/sbin:/sbin:/usr/bin:/bin +export PATH + +echo auth-up `date +'%y/%m/%d %T'` $* >> /var/log/pppstats + +# last line diff --git a/mdk-stage1/ppp/sample/ip-down b/mdk-stage1/ppp/sample/ip-down new file mode 100644 index 000000000..b771fb644 --- /dev/null +++ b/mdk-stage1/ppp/sample/ip-down @@ -0,0 +1,22 @@ +#!/bin/sh +# +# This script is run by the pppd _after_ the link is brought down. +# It should be used to delete routes, unset IP addresses etc. +# +# This script is called with the following arguments: +# Arg Name Example +# $1 Interface name ppp0 +# $2 The tty ttyS1 +# $3 The link speed 38400 +# $4 Local IP number 12.34.56.78 +# $5 Peer IP number 12.34.56.99 +# + +# +# The environment is cleared before executing this script +# so the path must be reset +# +PATH=/usr/sbin:/sbin:/usr/bin:/bin +export PATH + +# last line diff --git a/mdk-stage1/ppp/sample/ip-up b/mdk-stage1/ppp/sample/ip-up new file mode 100644 index 000000000..02bb71c44 --- /dev/null +++ b/mdk-stage1/ppp/sample/ip-up @@ -0,0 +1,23 @@ +#!/bin/sh +# +# This script is run by the pppd after the link is established. +# It should be used to add routes, set IP address, run the mailq +# etc. +# +# This script is called with the following arguments: +# Arg Name Example +# $1 Interface name ppp0 +# $2 The tty ttyS1 +# $3 The link speed 38400 +# $4 Local IP number 12.34.56.78 +# $5 Peer IP number 12.34.56.99 +# + +# +# The environment is cleared before executing this script +# so the path must be reset +# +PATH=/usr/sbin:/sbin:/usr/bin:/bin +export PATH + +# last line diff --git a/mdk-stage1/ppp/sample/options b/mdk-stage1/ppp/sample/options new file mode 100644 index 000000000..8d0a3f963 --- /dev/null +++ b/mdk-stage1/ppp/sample/options @@ -0,0 +1,153 @@ +# /etc/ppp/options + +# The name of this server. Often, the FQDN is used here. +#name <host> + +# Enforce the use of the hostname as the name of the local system for +# authentication purposes (overrides the name option). +usehostname + +# If no local IP address is given, pppd will use the first IP address +# that belongs to the local hostname. If "noipdefault" is given, this +# is disabled and the peer will have to supply an IP address. +noipdefault + +# 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. +#ipcp-accept-local + +# 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. +#ipcp-accept-remote + +# Specify which DNS Servers the incoming Win95 or WinNT Connection should use +# Two Servers can be remotely configured +#ms-dns 192.168.1.1 +#ms-dns 192.168.1.2 + +# Specify which WINS Servers the incoming connection Win95 or WinNT should use +#wins-addr 192.168.1.50 +#wins-addr 192.168.1.51 + +# enable this on a server that already has a permanent default route +#nodefaultroute + +# Run the executable or shell command specified after pppd has terminated +# the link. This script could, for example, issue commands to the modem +# to cause it to hang up if hardware modem control signals were not +# available. +# If mgetty is running, it will reset the modem anyway. So there is no need +# to do it here. +#disconnect "chat -- \d+++\d\c OK ath0 OK" + +# Increase debugging level (same as -d). The debug output is written +# to syslog LOG_LOCAL2. +debug + +# Enable debugging code in the kernel-level PPP driver. The argument n +# is a number which is the sum of the following values: 1 to enable +# general debug messages, 2 to request that the contents of received +# packets be printed, and 4 to request that the contents of transmitted +# packets be printed. +#kdebug n + +# Require the peer to authenticate itself before allowing network +# packets to be sent or received. +# Please do not disable this setting. It is expected to be standard in +# future releases of pppd. Use the call option (see manpage) to disable +# authentication for specific peers. +#auth + +# authentication can either be pap or chap. As most people only want to +# use pap, you can also disable chap: +#require-pap +#refuse-chap + +# Use hardware flow control (i.e. RTS/CTS) to control the flow of data +# on the serial port. +crtscts + +# Specifies that pppd should use a UUCP-style lock on the serial device +# to ensure exclusive access to the device. +lock + +# Use the modem control lines. +modem + +# async character map -- 32-bit hex; each bit is a character +# that needs to be escaped for pppd to receive it. 0x00000001 +# represents '\x01', and 0x80000000 represents '\x1f'. +# To allow pppd to work over a rlogin/telnet connection, ou should escape +# XON (^Q), XOFF (^S) and ^]: (The peer should use "escape ff".) +#asyncmap 200a0000 +asyncmap 0 + +# 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 escape option, unlike +# the asyncmap 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. +#escape 11,13,ff + +# Set the MRU [Maximum Receive Unit] value to <n> for negotiation. pppd +# will ask the peer to send packets of no more than <n> bytes. The +# minimum MRU value is 128. The default MRU value is 1500. A value of +# 296 is recommended for slow links (40 bytes for TCP/IP header + 256 +# bytes of data). +#mru 542 + +# Set the MTU [Maximum Transmit Unit] value to <n>. 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 n bytes +# through the PPP network interface. +#mtu <n> + +# Set the interface netmask to <n>, a 32 bit netmask in "decimal dot" +# notation (e.g. 255.255.255.0). +#netmask 255.255.255.0 + +# Don't fork to become a background process (otherwise pppd will do so +# if a serial device is specified). +nodetach + +# Set the assumed name of the remote system for authentication purposes +# to <n>. +#remotename <n> + +# 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. {proxyarp,noproxyarp} +proxyarp + +# Use the system password database for authenticating the peer using +# PAP. Note: mgetty already provides this option. If this is specified +# then dialin from users using a script under Linux to fire up ppp wont work. +#login + +# If this option is given, pppd will send an LCP echo-request frame to +# the peer every n seconds. Under Linux, the echo-request is sent when +# no packets have been received from the peer for n seconds. Normally +# the peer should respond to the echo-request by sending an echo-reply. +# This option can be used with the lcp-echo-failure option to detect +# that the peer is no longer connected. +lcp-echo-interval 30 + +# If this option is given, pppd will presume the peer to be dead if n +# 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 lcp-echo-interval 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. +lcp-echo-failure 4 + +# Specifies that pppd should disconnect if the link is idle for n seconds. +idle 600 + +# Disable the IPXCP and IPX protocols. +noipx + +# ---<End of File>--- diff --git a/mdk-stage1/ppp/sample/options.ttyXX b/mdk-stage1/ppp/sample/options.ttyXX new file mode 100644 index 000000000..d4202f534 --- /dev/null +++ b/mdk-stage1/ppp/sample/options.ttyXX @@ -0,0 +1,14 @@ +# If you need to set up multiple serial lines then copy this file to +# options.<ttyname> for each tty with a modem on it. +# +# The options.tty file will assign an IP address to each PPP connection +# as it comes up. They must all be distinct! +# +# Example: +# options.ttyS1 for com2 under DOS. +# +# Edit the following line so that the first IP address +# mentioned is the ip address of the serial port while the second +# is the IP address of your host +# +hostname-s1:hostname diff --git a/mdk-stage1/ppp/sample/pap-secrets b/mdk-stage1/ppp/sample/pap-secrets new file mode 100644 index 000000000..098971b9f --- /dev/null +++ b/mdk-stage1/ppp/sample/pap-secrets @@ -0,0 +1,28 @@ +# Secrets for authentication using PAP +# client server secret IP addresses + +# OUTBOUND CONNECTIONS +# Here you should add your userid password to connect to your providers via +# pap. The * means that the password is to be used for ANY host you connect +# to. Thus you do not have to worry about the foreign machine name. Just +# replace password with your password. +# If you have different providers with different passwords then you better +# remove the following line. +#hostname * password + +# INBOUND CONNECTIONS +#client hostname <password> 192.168.1.1 + +# If you add "auth login -chap +pap" to /etc/mgetty+sendfax/login.config, +# all users in /etc/passwd can use their password for pap-authentication. +# +# Every regular user can use PPP and has to use passwords from /etc/passwd +#* hostname "" +# UserIDs that cannot use PPP at all. Check your /etc/passwd and add any +# other accounts that should not be able to use pppd! Replace hostname +# with your local hostname. +#guest hostname "*" - +#master hostname "*" - +#root hostname "*" - +#support hostname "*" - +#stats hostname "*" - diff --git a/mdk-stage1/ppp/scripts/README b/mdk-stage1/ppp/scripts/README new file mode 100644 index 000000000..00e032ca6 --- /dev/null +++ b/mdk-stage1/ppp/scripts/README @@ -0,0 +1,143 @@ +This 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 <device>" to +terminate the connection on <device>. For example, "ppp-off ppp2" will +terminate the ppp2 connection. + +------------------------------------------------------------------------ + +7. secure-card + +This script was written by Jim Isaacson <jcisaac@crl.com>. 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. diff --git a/mdk-stage1/ppp/scripts/callback b/mdk-stage1/ppp/scripts/callback new file mode 100755 index 000000000..3e74e10b2 --- /dev/null +++ b/mdk-stage1/ppp/scripts/callback @@ -0,0 +1,77 @@ +#!/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 diff --git a/mdk-stage1/ppp/scripts/chat-callback b/mdk-stage1/ppp/scripts/chat-callback new file mode 100644 index 000000000..d014d6af3 --- /dev/null +++ b/mdk-stage1/ppp/scripts/chat-callback @@ -0,0 +1,98 @@ +# ===================================================================================== +# 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" diff --git a/mdk-stage1/ppp/scripts/chatchat/README b/mdk-stage1/ppp/scripts/chatchat/README new file mode 100644 index 000000000..88a4c6939 --- /dev/null +++ b/mdk-stage1/ppp/scripts/chatchat/README @@ -0,0 +1,134 @@ +v 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. + diff --git a/mdk-stage1/ppp/scripts/chatchat/chatchat.c b/mdk-stage1/ppp/scripts/chatchat/chatchat.c new file mode 100644 index 000000000..4534fb9e3 --- /dev/null +++ b/mdk-stage1/ppp/scripts/chatchat/chatchat.c @@ -0,0 +1,409 @@ +/* ************************************************************************* +* 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 <filename> +* +* where <filename> 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 <sys/time.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +/* 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); +} diff --git a/mdk-stage1/ppp/scripts/ip-down.local.add b/mdk-stage1/ppp/scripts/ip-down.local.add new file mode 100644 index 000000000..b93590e49 --- /dev/null +++ b/mdk-stage1/ppp/scripts/ip-down.local.add @@ -0,0 +1,20 @@ + +# +# 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 + diff --git a/mdk-stage1/ppp/scripts/ip-up.local.add b/mdk-stage1/ppp/scripts/ip-up.local.add new file mode 100644 index 000000000..80172093a --- /dev/null +++ b/mdk-stage1/ppp/scripts/ip-up.local.add @@ -0,0 +1,24 @@ + +# +# 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 + diff --git a/mdk-stage1/ppp/scripts/options-rsh-loc b/mdk-stage1/ppp/scripts/options-rsh-loc new file mode 100644 index 000000000..b015b87fe --- /dev/null +++ b/mdk-stage1/ppp/scripts/options-rsh-loc @@ -0,0 +1 @@ +debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1460 diff --git a/mdk-stage1/ppp/scripts/options-rsh-rem b/mdk-stage1/ppp/scripts/options-rsh-rem new file mode 100644 index 000000000..4b10bb9e9 --- /dev/null +++ b/mdk-stage1/ppp/scripts/options-rsh-rem @@ -0,0 +1 @@ +notty debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1460 diff --git a/mdk-stage1/ppp/scripts/options-ssh-loc b/mdk-stage1/ppp/scripts/options-ssh-loc new file mode 100644 index 000000000..add03d659 --- /dev/null +++ b/mdk-stage1/ppp/scripts/options-ssh-loc @@ -0,0 +1 @@ +debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1400 diff --git a/mdk-stage1/ppp/scripts/options-ssh-rem b/mdk-stage1/ppp/scripts/options-ssh-rem new file mode 100644 index 000000000..d690722c6 --- /dev/null +++ b/mdk-stage1/ppp/scripts/options-ssh-rem @@ -0,0 +1 @@ +notty debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1400 diff --git a/mdk-stage1/ppp/scripts/ppp-off b/mdk-stage1/ppp/scripts/ppp-off new file mode 100755 index 000000000..a22b5ea3d --- /dev/null +++ b/mdk-stage1/ppp/scripts/ppp-off @@ -0,0 +1,34 @@ +#!/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 diff --git a/mdk-stage1/ppp/scripts/ppp-on b/mdk-stage1/ppp/scripts/ppp-on new file mode 100755 index 000000000..ab79db471 --- /dev/null +++ b/mdk-stage1/ppp/scripts/ppp-on @@ -0,0 +1,36 @@ +#!/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 diff --git a/mdk-stage1/ppp/scripts/ppp-on-dialer b/mdk-stage1/ppp/scripts/ppp-on-dialer new file mode 100755 index 000000000..7d66765f1 --- /dev/null +++ b/mdk-stage1/ppp/scripts/ppp-on-dialer @@ -0,0 +1,17 @@ +#!/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 diff --git a/mdk-stage1/ppp/scripts/ppp-on-rsh b/mdk-stage1/ppp/scripts/ppp-on-rsh new file mode 100755 index 000000000..30a50dba6 --- /dev/null +++ b/mdk-stage1/ppp/scripts/ppp-on-rsh @@ -0,0 +1,72 @@ +#!/bin/sh +# +# A sample script to establish PPP session(s) via rsh +# +# Adi Masputra <adi.masputra@sun.com> +# 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 + diff --git a/mdk-stage1/ppp/scripts/ppp-on-ssh b/mdk-stage1/ppp/scripts/ppp-on-ssh new file mode 100755 index 000000000..0e41acac6 --- /dev/null +++ b/mdk-stage1/ppp/scripts/ppp-on-ssh @@ -0,0 +1,76 @@ +#!/bin/sh +# +# A sample script to establish PPP session(s) via SSH 1.x +# +# Adi Masputra <adi.masputra@sun.com> +# 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 + diff --git a/mdk-stage1/ppp/scripts/redialer b/mdk-stage1/ppp/scripts/redialer new file mode 100755 index 000000000..5bbde4e9d --- /dev/null +++ b/mdk-stage1/ppp/scripts/redialer @@ -0,0 +1,96 @@ +#!/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 diff --git a/mdk-stage1/ppp/scripts/secure-card b/mdk-stage1/ppp/scripts/secure-card new file mode 100644 index 000000000..a32138b7d --- /dev/null +++ b/mdk-stage1/ppp/scripts/secure-card @@ -0,0 +1,111 @@ +#!/usr/local/bin/expect -f +# +# This script was written by Jim Isaacson <jcisaac@crl.com>. 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/cua3 > /dev/cua3" + +# +# These are the parameters for the program. +# +set user Pxxxxxx +set password xxxxxxx +set modem /dev/cua3 +set dialup <put phone number here> +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/cua3 19200 192.111.187.215: \ + crtscts modem defaultroute debug diff --git a/mdk-stage1/ppp/solaris/Makedefs b/mdk-stage1/ppp/solaris/Makedefs new file mode 100644 index 000000000..81db8ab2e --- /dev/null +++ b/mdk-stage1/ppp/solaris/Makedefs @@ -0,0 +1,16 @@ +# +# defines common to several Makefiles +# + +INSTALL= /usr/sbin/install + +BINDIR = /usr/local/bin +MANDIR = /usr/local/man +ETCDIR = /etc/ppp + +COPTS = -O -Xa + +# For compiling with gcc, comment out the COPTS definition above and +# uncomment the next 2 definitions. +#CC = gcc +#COPTS = -O2 diff --git a/mdk-stage1/ppp/solaris/Makedefs.sol2 b/mdk-stage1/ppp/solaris/Makedefs.sol2 new file mode 100644 index 000000000..1282c6e74 --- /dev/null +++ b/mdk-stage1/ppp/solaris/Makedefs.sol2 @@ -0,0 +1,59 @@ +# +# Generic make definitions for Solaris 2 +# +# $Id$ +# + +include ../solaris/Makedefs + +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) + diff --git a/mdk-stage1/ppp/solaris/Makefile.sol2 b/mdk-stage1/ppp/solaris/Makefile.sol2 new file mode 100644 index 000000000..78628847b --- /dev/null +++ b/mdk-stage1/ppp/solaris/Makefile.sol2 @@ -0,0 +1,66 @@ +# +# Makefile for STREAMS modules for Solaris 2. +# +# $Id$ +# + +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: + 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/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 diff --git a/mdk-stage1/ppp/solaris/Makefile.sol2-64 b/mdk-stage1/ppp/solaris/Makefile.sol2-64 new file mode 100644 index 000000000..63e75fc54 --- /dev/null +++ b/mdk-stage1/ppp/solaris/Makefile.sol2-64 @@ -0,0 +1,85 @@ +# +# Makefile for 64-bit STREAMS modules for Solaris 2. +# +# $Id$ +# + +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: + 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/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 diff --git a/mdk-stage1/ppp/solaris/Makefile.top b/mdk-stage1/ppp/solaris/Makefile.top new file mode 100644 index 000000000..f1200b852 --- /dev/null +++ b/mdk-stage1/ppp/solaris/Makefile.top @@ -0,0 +1,50 @@ +# +# ppp top level makefile for SVR4 and Solaris 2 +# +# $Id$ +# + +include solaris/Makedefs + +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-etcppp + +install-progs: + cd chat; $(MAKE) install + cd pppd; $(MAKE) install + cd pppstats; $(MAKE) install + cd pppdump; $(MAKE) install + cd solaris; $(MAKE) install + +install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \ + $(ETCDIR)/chap-secrets + +$(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 + diff --git a/mdk-stage1/ppp/solaris/ppp.c b/mdk-stage1/ppp/solaris/ppp.c new file mode 100644 index 000000000..44bf08dff --- /dev/null +++ b/mdk-stage1/ppp/solaris/ppp.c @@ -0,0 +1,2486 @@ +/* + * ppp.c - STREAMS multiplexing pseudo-device driver for PPP. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/errno.h> +#ifdef __osf__ +#include <sys/ioctl.h> +#include <sys/cmn_err.h> +#define queclass(mp) ((mp)->b_band & QPCTL) +#else +#include <sys/ioccom.h> +#endif +#include <sys/time.h> +#ifdef SVR4 +#include <sys/cmn_err.h> +#include <sys/conf.h> +#include <sys/dlpi.h> +#include <sys/ddi.h> +#ifdef SOL2 +#include <sys/ksynch.h> +#include <sys/kstat.h> +#include <sys/sunddi.h> +#include <sys/ethernet.h> +#else +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <netinet/in.h> +#endif /* SOL2 */ +#else /* not SVR4 */ +#include <sys/user.h> +#endif /* SVR4 */ +#include <net/ppp_defs.h> +#include <net/pppio.h> +#include "ppp_mod.h" + +/* + * Modifications marked with #ifdef PRIOQ are for priority queueing of + * interactive traffic, and are due to Marko Zec <zec@japa.tel.fer.hr>. + */ +#ifdef PRIOQ +#endif /* PRIOQ */ + +#include <netinet/in.h> /* 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 <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> + +#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 */ + diff --git a/mdk-stage1/ppp/solaris/ppp.conf b/mdk-stage1/ppp/solaris/ppp.conf new file mode 100644 index 000000000..e443a7aac --- /dev/null +++ b/mdk-stage1/ppp/solaris/ppp.conf @@ -0,0 +1 @@ +name="ppp" parent="pseudo" instance=0; diff --git a/mdk-stage1/ppp/solaris/ppp_ahdlc.c b/mdk-stage1/ppp/solaris/ppp_ahdlc.c new file mode 100644 index 000000000..d0b961258 --- /dev/null +++ b/mdk-stage1/ppp/solaris/ppp_ahdlc.c @@ -0,0 +1,878 @@ +/* + * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC. + * + * Re-written by Adi Masputra <adi.masputra@sun.com>, 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 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX. + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stream.h> +#include <sys/errno.h> + +#ifdef SVR4 +#include <sys/conf.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> +#include <sys/ddi.h> +#else +#include <sys/user.h> +#ifdef __osf__ +#include <sys/cmn_err.h> +#endif +#endif /* SVR4 */ + +#include <net/ppp_defs.h> +#include <net/pppio.h> +#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); + freemsg(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; + ushort_t fcs; +#if defined(SOL2) + mblk_t *zmp; +#endif /* SOL2 */ + +#if defined(SOL2) + /* + * In case the driver (or something below) doesn't send + * data upstream in one message block, concatenate everything + */ + if (!((mp->b_wptr - mp->b_rptr == msgdsize(mp)) && + ((intpointer_t)mp->b_rptr % sizeof(intpointer_t) == 0))) { + + zmp = msgpullup(mp, -1); + freemsg(mp); + mp = zmp; + if (mp == 0) + return; + } +#endif /* SOL2 */ + + 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 (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]; +} diff --git a/mdk-stage1/ppp/solaris/ppp_ahdlc_mod.c b/mdk-stage1/ppp/solaris/ppp_ahdlc_mod.c new file mode 100644 index 000000000..f81be8abb --- /dev/null +++ b/mdk-stage1/ppp/solaris/ppp_ahdlc_mod.c @@ -0,0 +1,49 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/sunddi.h> + +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); +} diff --git a/mdk-stage1/ppp/solaris/ppp_comp.c b/mdk-stage1/ppp/solaris/ppp_comp.c new file mode 100644 index 000000000..f6eef5ab1 --- /dev/null +++ b/mdk-stage1/ppp/solaris/ppp_comp.c @@ -0,0 +1,1126 @@ +/* + * ppp_comp.c - STREAMS module for kernel-level compression and CCP support. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/stream.h> + +#ifdef SVR4 +#include <sys/conf.h> +#include <sys/cmn_err.h> +#include <sys/ddi.h> +#else +#include <sys/user.h> +#ifdef __osf__ +#include <sys/cmn_err.h> +#endif +#endif /* SVR4 */ + +#include <net/ppp_defs.h> +#include <net/pppio.h> +#include "ppp_mod.h" + +#ifdef __osf__ +#include <sys/mbuf.h> +#include <sys/protosw.h> +#endif + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <net/vjcompress.h> + +#define PACKETPTR mblk_t * +#include <net/ppp-comp.h> + +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]; +} diff --git a/mdk-stage1/ppp/solaris/ppp_comp_mod.c b/mdk-stage1/ppp/solaris/ppp_comp_mod.c new file mode 100644 index 000000000..83ff8e252 --- /dev/null +++ b/mdk-stage1/ppp/solaris/ppp_comp_mod.c @@ -0,0 +1,81 @@ +/* + * ppp_comp_mod.c - modload support for PPP compression STREAMS module. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under Solaris 2. + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/sunddi.h> + +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); +} diff --git a/mdk-stage1/ppp/solaris/ppp_mod.c b/mdk-stage1/ppp/solaris/ppp_mod.c new file mode 100644 index 000000000..a4b1538a5 --- /dev/null +++ b/mdk-stage1/ppp/solaris/ppp_mod.c @@ -0,0 +1,174 @@ +/* + * ppp_mod.c - modload support for PPP pseudo-device driver. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under Solaris 2. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/sunddi.h> +#include <sys/ksynch.h> + +#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; +{ + return strcmp(ddi_get_name(dip), "ppp") == 0? DDI_IDENTIFIED: + DDI_NOT_IDENTIFIED; +} + +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; +} diff --git a/mdk-stage1/ppp/solaris/ppp_mod.h b/mdk-stage1/ppp/solaris/ppp_mod.h new file mode 100644 index 000000000..f0af00886 --- /dev/null +++ b/mdk-stage1/ppp/solaris/ppp_mod.h @@ -0,0 +1,190 @@ +/* + * Miscellaneous definitions for PPP STREAMS modules. + */ + +/* + * Macros for allocating and freeing kernel memory. + */ +#ifdef SVR4 /* SVR4, including Solaris 2 */ +#include <sys/kmem.h> +#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 <sys/kmem_alloc.h> /* 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 <sys/malloc.h> + +/* 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 <sys/proc.h> +#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 <sys/strlog.h> +#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 */ diff --git a/mdk-stage1/ppp/sunos4/Makedefs b/mdk-stage1/ppp/sunos4/Makedefs new file mode 100644 index 000000000..8b56a2b67 --- /dev/null +++ b/mdk-stage1/ppp/sunos4/Makedefs @@ -0,0 +1,13 @@ +# +# defines common to several Makefiles +# + +INSTALL= install -o root -g daemon + +BINDIR = /usr/local/etc +MANDIR = /usr/local/man +ETCDIR = /etc/ppp + +# To use gcc, uncomment the next line. +#CC = gcc +COPTS = -O diff --git a/mdk-stage1/ppp/sunos4/Makefile b/mdk-stage1/ppp/sunos4/Makefile new file mode 100644 index 000000000..701e24e14 --- /dev/null +++ b/mdk-stage1/ppp/sunos4/Makefile @@ -0,0 +1,57 @@ +# +# Makefile for STREAMS modules for SunOS 4. +# +# $Id$ +# + +include Makedefs + +LD = /usr/bin/ld # make sure we don't get gnu ld + +# Defining __$(ARCH)__ is for gcc's broken version of sun/vddrv.h. +ARCH = `/bin/arch -k` +DEFINES= -DKERNEL -D_KERNEL -DSUNOS4 -D$(ARCH) -D__$(ARCH)__ \ + -DDEBUG -DNO_DLPI -DSNIT_SUPPORT +CFLAGS= $(DEFINES) -I../include $(COPTS) + +MODULES= ppp_mod.o ppp_ahdl_mod.o ppp_comp_mod.o if_ppp_mod.o + +all: $(MODULES) + +ppp_mod.o: ppp.o ppp_vdcmd.o + $(LD) -r -o ppp_mod.o ppp.o ppp_vdcmd.o + +ppp_ahdl_mod.o: ppp_ahdlc.o ppp_ahdlc_vdcmd.o + $(LD) -r -o ppp_ahdl_mod.o ppp_ahdlc.o ppp_ahdlc_vdcmd.o + +COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \ + ppp_comp_vdcmd.o +ppp_comp_mod.o: $(COMP_OBJS) + $(LD) -r -o $@ $(COMP_OBJS) + +if_ppp.o: ../modules/if_ppp.c + $(CC) $(CFLAGS) -c $? +bsd-comp.o: ../modules/bsd-comp.c + $(CC) $(CFLAGS) -c $? +deflate.o: ../modules/deflate.c + $(CC) $(CFLAGS) -c $? +ppp.o: ../modules/ppp.c + $(CC) $(CFLAGS) -c $? +ppp_ahdlc.o: ../modules/ppp_ahdlc.c + $(CC) $(CFLAGS) -c $? +ppp_comp.o: ../modules/ppp_comp.c + $(CC) $(CFLAGS) -c $? +vjcompress.o: ../modules/vjcompress.c + $(CC) $(CFLAGS) -c $? +zlib.o: ../common/zlib.c + $(CC) $(CFLAGS) -c $? + +if_ppp_mod.o: if_ppp.o if_ppp_vdcmd.o + $(LD) -r -o if_ppp_mod.o if_ppp.o if_ppp_vdcmd.o + +install: all + $(INSTALL) $(MODULES) $(BINDIR) + ./ppp.INSTALL + +clean: + rm -f ppp ppp_comp ppp_ahdl *.o *~ core diff --git a/mdk-stage1/ppp/sunos4/Makefile.top b/mdk-stage1/ppp/sunos4/Makefile.top new file mode 100644 index 000000000..c86e0884d --- /dev/null +++ b/mdk-stage1/ppp/sunos4/Makefile.top @@ -0,0 +1,46 @@ +# +# ppp top level makefile +# + +include sunos4/Makedefs + +all: + cd chat; $(MAKE) all + cd pppd; $(MAKE) all + cd pppstats; $(MAKE) all + cd sunos4; $(MAKE) all + cd pppdump; $(MAKE) all + +install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp + +install-progs: + cd chat; $(MAKE) install + cd pppd; $(MAKE) install + cd pppstats; $(MAKE) install + cd pppdump; $(MAKE) install + cd sunos4; $(MAKE) install + +install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \ + $(ETCDIR)/chap-secrets + +$(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 *~ + cd chat; $(MAKE) clean + cd pppd; $(MAKE) clean + cd pppstats; $(MAKE) clean + cd sunos4; $(MAKE) clean + diff --git a/mdk-stage1/ppp/sunos4/if_ppp_vdcmd.c b/mdk-stage1/ppp/sunos4/if_ppp_vdcmd.c new file mode 100644 index 000000000..2bf9710f4 --- /dev/null +++ b/mdk-stage1/ppp/sunos4/if_ppp_vdcmd.c @@ -0,0 +1,57 @@ +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/conf.h> +#include <sun/vddrv.h> + +extern struct streamtab if_pppinfo; + +static struct vdldrv vd = { + VDMAGIC_USER, + "if_ppp" +}; + +static int fmodsw_index = -1; + +int +if_ppp_vdcmd(fun, vdp, vdi, vds) + unsigned int fun; + struct vddrv *vdp; + addr_t vdi; + struct vdstat *vds; +{ + int n, error; + + switch (fun) { + case VDLOAD: + vdp->vdd_vdtab = (struct vdlinkage *) &vd; + if (fmodsw_index >= 0) + return EBUSY; + for (n = 0; n < fmodcnt; ++n) + if (fmodsw[n].f_str == 0) + break; + if (n >= fmodcnt) + return ENODEV; + strncpy(fmodsw[n].f_name, vd.Drv_name, FMNAMESZ+1); + fmodsw[n].f_str = &if_pppinfo; + fmodsw_index = n; + break; + + case VDUNLOAD: + if (fmodsw_index <= 0) + return EINVAL; + error = if_ppp_unload(); + if (error != 0) + return error; + fmodsw[fmodsw_index].f_name[0] = 0; + fmodsw[fmodsw_index].f_str = 0; + fmodsw_index = -1; + break; + + case VDSTAT: + break; + + default: + return EIO; + } + return 0; +} diff --git a/mdk-stage1/ppp/sunos4/ppp.INSTALL b/mdk-stage1/ppp/sunos4/ppp.INSTALL new file mode 100755 index 000000000..0018bf8d0 --- /dev/null +++ b/mdk-stage1/ppp/sunos4/ppp.INSTALL @@ -0,0 +1,104 @@ +#!/bin/sh + +# Script for loading, unloading, etc. ppp modules. + +moddir=/usr/local/etc +etcppp=/etc/ppp + +PATH=/usr/etc:/usr/bin + +# Check that we're superuser +touch /tmp/su$$ +if chown root /tmp/su$$ >/dev/null; then : +else + echo "$0: must be root." + rm -f /tmp/su$$ + exit 1 +fi +rm -f /tmp/su$$ + +case "$0" in +*ppp.INSTALL) + if [ ! -f ppp.INSTALL ]; then + echo "ppp.INSTALL: not found" + exit 1 + fi + for n in INSTALL LOAD UNLOAD MKDEV RMDEV; do + if [ -h /dev/ppp.$n -o -f /dev/ppp.$n ]; then + rm /dev/ppp.$n + fi + done + cp ppp.INSTALL /dev + for n in LOAD UNLOAD MKDEV RMDEV; do + ln -s ppp.INSTALL /dev/ppp.$n + done + ;; + +*ppp.LOAD) + if modstat | grep -w ppp >/dev/null; then + echo "ppp driver is already loaded." + exit 1 + fi + if modstat | grep -w if_ppp >/dev/null; then + echo "if_ppp module already loaded: not reloading." + else + echo -n "if_ppp: " + modload $moddir/if_ppp_mod.o -sym -entry _if_ppp_vdcmd \ + -o $etcppp/if_ppp_mod + fi + echo -n "ppp: " + modload $moddir/ppp_mod.o -sym -entry _ppp_vdcmd -exec /dev/ppp.MKDEV \ + -o $etcppp/ppp_mod + echo -n "ppp_comp: " + modload $moddir/ppp_comp_mod.o -sym -entry _ppp_comp_vdcmd \ + -o $etcppp/ppp_comp + echo -n "ppp_ahdl: " + modload $moddir/ppp_ahdl_mod.o -sym -entry _ppp_ahdlc_vdcmd \ + -o $etcppp/ppp_ahdl + exit 0 + ;; + +*ppp.MKDEV) + # args: module number, type, b-major, c-major + if [ $# -ne 4 ]; then + echo "Usage: $0 module-id module-type b-major c-major" + exit 1 + fi + if [ "$2" -ne "12345607" -a "$2" -ne "12345600" ]; then + echo "$0: $2: bad module type" + exit 1 + fi + rm -f /dev/ppp + # we "just know" that 37 is the major number of the clone driver + mknod /dev/ppp c 37 $4 + chmod 644 /dev/ppp + exit 0 + ;; + +*ppp.UNLOAD) + stat=0 + if modstat | grep -w if_ppp >/dev/null; then + echo "$0: not unloading if_ppp module." + fi + for mod in ppp ppp_comp ppp_ahdl; do + id=`modstat | grep -w $mod | awk '{print $1}'` + if [ x$id = x ]; then + echo "$mod is not loaded." + stat=1 + else + modunload -id $id + fi + done + exit $stat + ;; + +*ppp.RMDEV) + rm -f /dev/ppp + exit 0 + ;; + +*) + echo "Invocation names: ppp.INSTALL ppp.LOAD ppp.UNLOAD ppp.MKDEV ppp.RMDEV" + exit 1 + ;; +esac diff --git a/mdk-stage1/ppp/sunos4/ppp_ahdlc_vdcmd.c b/mdk-stage1/ppp/sunos4/ppp_ahdlc_vdcmd.c new file mode 100644 index 000000000..2dbe8262b --- /dev/null +++ b/mdk-stage1/ppp/sunos4/ppp_ahdlc_vdcmd.c @@ -0,0 +1,57 @@ +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/conf.h> +#include <sun/vddrv.h> + +extern struct streamtab ppp_ahdlcinfo; +extern int ppp_ahdlc_count; + +static struct vdldrv vd = { + VDMAGIC_USER, + "ppp_ahdl" +}; + +static int fmodsw_index = -1; + +int +ppp_ahdlc_vdcmd(fun, vdp, vdi, vds) + unsigned int fun; + struct vddrv *vdp; + addr_t vdi; + struct vdstat *vds; +{ + int n; + + switch (fun) { + case VDLOAD: + vdp->vdd_vdtab = (struct vdlinkage *) &vd; + if (fmodsw_index >= 0) + return EBUSY; + for (n = 0; n < fmodcnt; ++n) + if (fmodsw[n].f_str == 0) + break; + if (n >= fmodcnt) + return ENODEV; + strncpy(fmodsw[n].f_name, vd.Drv_name, FMNAMESZ+1); + fmodsw[n].f_str = &ppp_ahdlcinfo; + fmodsw_index = n; + break; + + case VDUNLOAD: + if (ppp_ahdlc_count > 0) + return EBUSY; + if (fmodsw_index <= 0) + return EINVAL; + fmodsw[fmodsw_index].f_name[0] = 0; + fmodsw[fmodsw_index].f_str = 0; + fmodsw_index = -1; + break; + + case VDSTAT: + break; + + default: + return EIO; + } + return 0; +} diff --git a/mdk-stage1/ppp/sunos4/ppp_comp_vdcmd.c b/mdk-stage1/ppp/sunos4/ppp_comp_vdcmd.c new file mode 100644 index 000000000..b81bc47e8 --- /dev/null +++ b/mdk-stage1/ppp/sunos4/ppp_comp_vdcmd.c @@ -0,0 +1,57 @@ +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/conf.h> +#include <sun/vddrv.h> + +extern struct streamtab ppp_compinfo; +extern int ppp_comp_count; + +static struct vdldrv vd = { + VDMAGIC_USER, + "ppp_comp" +}; + +static int fmodsw_index = -1; + +int +ppp_comp_vdcmd(fun, vdp, vdi, vds) + unsigned int fun; + struct vddrv *vdp; + addr_t vdi; + struct vdstat *vds; +{ + int n; + + switch (fun) { + case VDLOAD: + vdp->vdd_vdtab = (struct vdlinkage *) &vd; + if (fmodsw_index >= 0) + return EBUSY; + for (n = 0; n < fmodcnt; ++n) + if (fmodsw[n].f_str == 0) + break; + if (n >= fmodcnt) + return ENODEV; + strncpy(fmodsw[n].f_name, vd.Drv_name, FMNAMESZ+1); + fmodsw[n].f_str = &ppp_compinfo; + fmodsw_index = n; + break; + + case VDUNLOAD: + if (ppp_comp_count > 0) + return EBUSY; + if (fmodsw_index <= 0) + return EINVAL; + fmodsw[fmodsw_index].f_name[0] = 0; + fmodsw[fmodsw_index].f_str = 0; + fmodsw_index = -1; + break; + + case VDSTAT: + break; + + default: + return EIO; + } + return 0; +} diff --git a/mdk-stage1/ppp/sunos4/ppp_vdcmd.c b/mdk-stage1/ppp/sunos4/ppp_vdcmd.c new file mode 100644 index 000000000..68095c92e --- /dev/null +++ b/mdk-stage1/ppp/sunos4/ppp_vdcmd.c @@ -0,0 +1,81 @@ +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/conf.h> +#include <sun/vddrv.h> + +extern struct streamtab pppinfo; +extern int ppp_count; +extern int nchrdev; + +static struct vdldrv vd = { + VDMAGIC_PSEUDO, + "ppp" +}; + +extern int nodev(); + +static struct cdevsw ppp_cdevsw = { + nodev, nodev, nodev, nodev, nodev, nodev, nodev, 0, + &pppinfo +}; + +static struct cdevsw old_entry; + +int +ppp_vdcmd(fun, vdp, vdi, vds) + unsigned int fun; + struct vddrv *vdp; + addr_t vdi; + struct vdstat *vds; +{ + static int majnum = -1; + int n, maj; + + switch (fun) { + case VDLOAD: + /* + * It seems like modload doesn't install the cdevsw entry + * for us. Oh well... + */ + for (maj = 1; maj < nchrdev; ++maj) + if (cdevsw[maj].d_open == vd_unuseddev) + break; + if (maj >= nchrdev) + return ENODEV; + vd.Drv_charmajor = maj; + old_entry = cdevsw[maj]; + cdevsw[maj] = ppp_cdevsw; + vd.Drv_cdevsw = &ppp_cdevsw; + vdp->vdd_vdtab = (struct vdlinkage *) &vd; + majnum = maj; + break; + + case VDUNLOAD: + if (ppp_count > 0) + return EBUSY; + if (vd.Drv_charmajor > 0) + cdevsw[vd.Drv_charmajor] = old_entry; + break; + + case VDSTAT: + /* + * We have to fool the modstat command into thinking + * that this module is actually a driver! This is + * so that installation commands that use the -exec + * option of modload to run a shell script find out + * the block and/or char major numbers of the driver + * loaded (so that the shell script can go off to + * /dev and *MAKE* the bloody device nodes- remember + * they might change from one load to another if + * you don't hardwire the number!). + */ + vds->vds_magic = VDMAGIC_DRV; + vds->vds_modinfo[0] = (char) 0; + vds->vds_modinfo[1] = (char) majnum; + break; + + default: + return EIO; + } + return 0; +} diff --git a/mdk-stage1/ppp/svr4/Makedefs b/mdk-stage1/ppp/svr4/Makedefs new file mode 100644 index 000000000..81db8ab2e --- /dev/null +++ b/mdk-stage1/ppp/svr4/Makedefs @@ -0,0 +1,16 @@ +# +# defines common to several Makefiles +# + +INSTALL= /usr/sbin/install + +BINDIR = /usr/local/bin +MANDIR = /usr/local/man +ETCDIR = /etc/ppp + +COPTS = -O -Xa + +# For compiling with gcc, comment out the COPTS definition above and +# uncomment the next 2 definitions. +#CC = gcc +#COPTS = -O2 diff --git a/mdk-stage1/ppp/svr4/Makedefs.sol2 b/mdk-stage1/ppp/svr4/Makedefs.sol2 new file mode 100644 index 000000000..e8b8d282e --- /dev/null +++ b/mdk-stage1/ppp/svr4/Makedefs.sol2 @@ -0,0 +1,59 @@ +# +# Generic make definitions for Solaris 2 +# +# $Id$ +# + +include ../svr4/Makedefs + +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) + diff --git a/mdk-stage1/ppp/svr4/Makefile.sol2 b/mdk-stage1/ppp/svr4/Makefile.sol2 new file mode 100644 index 000000000..5b2ca8635 --- /dev/null +++ b/mdk-stage1/ppp/svr4/Makefile.sol2 @@ -0,0 +1,66 @@ +# +# Makefile for STREAMS modules for Solaris 2. +# +# $Id$ +# + +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: ../modules/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: ../modules/ppp_ahdlc.c + $(CC) $(CFLAGS) -c $? +ppp_comp.o: ../modules/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: + 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/add_drv ppp + +SRCS = ../modules/ppp.c ppp_mod.c ../modules/ppp_ahdlc.c ppp_ahdlc_mod.c \ + ../modules/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 diff --git a/mdk-stage1/ppp/svr4/Makefile.sol2-64 b/mdk-stage1/ppp/svr4/Makefile.sol2-64 new file mode 100644 index 000000000..80c6b185e --- /dev/null +++ b/mdk-stage1/ppp/svr4/Makefile.sol2-64 @@ -0,0 +1,85 @@ +# +# Makefile for 64-bit STREAMS modules for Solaris 2. +# +# $Id$ +# + +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: ../modules/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: ../modules/ppp_ahdlc.c + $(CC) $(CFLAGS) -c $? -o $@ +$(LP64DIR)/ppp_comp.o: ../modules/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: + 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/add_drv ppp + +SRCS = ../modules/ppp.c ppp_mod.c ../modules/ppp_ahdlc.c ppp_ahdlc_mod.c \ + ../modules/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 diff --git a/mdk-stage1/ppp/svr4/Makefile.svr4 b/mdk-stage1/ppp/svr4/Makefile.svr4 new file mode 100644 index 000000000..252c52428 --- /dev/null +++ b/mdk-stage1/ppp/svr4/Makefile.svr4 @@ -0,0 +1,60 @@ +# +# Makefile for STREAMS modules for SVR4. +# +# $Id$ +# + +COPTS = -O + +CFLAGS= -D_KERNEL -DSVR4 -DLACHTCP -I../include $(COPTS) + +all: ppp ppp_ahdl ppp_comp + +ppp: ppp.o + ld -r -o $@ ppp.o + +ppp_ahdl: ppp_ahdlc.o + ld -r -o $@ ppp_ahdlc.o + +ppp_comp: ppp_comp.o bsd-comp.o vjcompress.o deflate.o zlib.o + ld -r -o $@ ppp_comp.o bsd-comp.o vjcompress.o deflate.o zlib.o + +bsd-comp.o: ../modules/bsd-comp.c + $(CC) $(CFLAGS) -c $? +deflate.o: ../modules/deflate.c + $(CC) $(CFLAGS) -c $? +ppp.o: ../modules/ppp.c + $(CC) $(CFLAGS) -c $? +ppp_ahdlc.o: ../modules/ppp_ahdlc.c + $(CC) $(CFLAGS) -c $? +ppp_comp.o: ../modules/ppp_comp.c + $(CC) $(CFLAGS) -c $? +vjcompress.o: ../modules/vjcompress.c + $(CC) $(CFLAGS) -c $? +zlib.o: ../common/zlib.c + $(CC) $(CFLAGS) -c $? + +install: all + cp ppp Driver.o + cp ppp.Master Master + cp ppp.System System + cp ppp.Node Node + /etc/conf/bin/idinstall -d ppp + /etc/conf/bin/idinstall -a ppp + cp ppp_comp Driver.o + cp ppp_comp.Master Master + cp ppp_comp.System System + /etc/conf/bin/idinstall -d ppp_comp + /etc/conf/bin/idinstall -a ppp_comp + cp ppp_ahdl Driver.o + cp ppp_ahdl.Master Master + cp ppp_ahdl.System System + /etc/conf/bin/idinstall -d ppp_ahdl + /etc/conf/bin/idinstall -a ppp_ahdl + @echo + @echo 'NOTE: You must rebuild your kernel to incorporate the driver.' + @echo '(use /etc/conf/bin/idbuild)' + @echo + +clean: + rm -f ppp ppp_comp ppp_ahdl *.o *~ core diff --git a/mdk-stage1/ppp/svr4/Makefile.top b/mdk-stage1/ppp/svr4/Makefile.top new file mode 100644 index 000000000..253e48acb --- /dev/null +++ b/mdk-stage1/ppp/svr4/Makefile.top @@ -0,0 +1,50 @@ +# +# ppp top level makefile for SVR4 and Solaris 2 +# +# $Id$ +# + +include svr4/Makedefs + +all: + cd chat; $(MAKE) all + cd pppd; $(MAKE) all + cd pppstats; $(MAKE) all + cd pppdump; $(MAKE) all + cd svr4; $(MAKE) all + +install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp + +install-progs: + cd chat; $(MAKE) install + cd pppd; $(MAKE) install + cd pppstats; $(MAKE) install + cd pppdump; $(MAKE) install + cd svr4; $(MAKE) install + +install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \ + $(ETCDIR)/chap-secrets + +$(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 svr4; $(MAKE) clean + diff --git a/mdk-stage1/ppp/svr4/ppp.Master b/mdk-stage1/ppp/svr4/ppp.Master new file mode 100644 index 000000000..346db035b --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp.Master @@ -0,0 +1 @@ +ppp - Sciof ppp 0 0 1 128 -1 diff --git a/mdk-stage1/ppp/svr4/ppp.Node b/mdk-stage1/ppp/svr4/ppp.Node new file mode 100644 index 000000000..7767ade71 --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp.Node @@ -0,0 +1 @@ +clone ppp c ppp diff --git a/mdk-stage1/ppp/svr4/ppp.System b/mdk-stage1/ppp/svr4/ppp.System new file mode 100644 index 000000000..e60c0eec3 --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp.System @@ -0,0 +1 @@ +ppp Y 1 0 0 0 0 0 0 0 diff --git a/mdk-stage1/ppp/svr4/ppp.conf b/mdk-stage1/ppp/svr4/ppp.conf new file mode 100644 index 000000000..e443a7aac --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp.conf @@ -0,0 +1 @@ +name="ppp" parent="pseudo" instance=0; diff --git a/mdk-stage1/ppp/svr4/ppp_ahdl.Master b/mdk-stage1/ppp/svr4/ppp_ahdl.Master new file mode 100644 index 000000000..4fde52596 --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp_ahdl.Master @@ -0,0 +1 @@ +ppp_ahdl - iSf phdl 0 0 1 1 -1 diff --git a/mdk-stage1/ppp/svr4/ppp_ahdl.System b/mdk-stage1/ppp/svr4/ppp_ahdl.System new file mode 100644 index 000000000..f41a500f4 --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp_ahdl.System @@ -0,0 +1 @@ +ppp_ahdl Y 1 0 0 0 0 0 0 0 diff --git a/mdk-stage1/ppp/svr4/ppp_ahdlc_mod.c b/mdk-stage1/ppp/svr4/ppp_ahdlc_mod.c new file mode 100644 index 000000000..f81be8abb --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp_ahdlc_mod.c @@ -0,0 +1,49 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/sunddi.h> + +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); +} diff --git a/mdk-stage1/ppp/svr4/ppp_comp.Master b/mdk-stage1/ppp/svr4/ppp_comp.Master new file mode 100644 index 000000000..78019064e --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp_comp.Master @@ -0,0 +1 @@ +ppp_comp - iSf pcmp 0 0 1 1 -1 diff --git a/mdk-stage1/ppp/svr4/ppp_comp.System b/mdk-stage1/ppp/svr4/ppp_comp.System new file mode 100644 index 000000000..e69d4a1a3 --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp_comp.System @@ -0,0 +1 @@ +ppp_comp Y 1 0 0 0 0 0 0 0 diff --git a/mdk-stage1/ppp/svr4/ppp_comp_mod.c b/mdk-stage1/ppp/svr4/ppp_comp_mod.c new file mode 100644 index 000000000..83ff8e252 --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp_comp_mod.c @@ -0,0 +1,81 @@ +/* + * ppp_comp_mod.c - modload support for PPP compression STREAMS module. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under Solaris 2. + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/sunddi.h> + +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); +} diff --git a/mdk-stage1/ppp/svr4/ppp_mod.c b/mdk-stage1/ppp/svr4/ppp_mod.c new file mode 100644 index 000000000..a4b1538a5 --- /dev/null +++ b/mdk-stage1/ppp/svr4/ppp_mod.c @@ -0,0 +1,174 @@ +/* + * ppp_mod.c - modload support for PPP pseudo-device driver. + * + * Copyright (c) 1994 The Australian National University. + * 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. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * 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$ + */ + +/* + * This file is used under Solaris 2. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/sunddi.h> +#include <sys/ksynch.h> + +#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; +{ + return strcmp(ddi_get_name(dip), "ppp") == 0? DDI_IDENTIFIED: + DDI_NOT_IDENTIFIED; +} + +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; +} |