[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/pppd/.sgbuilt_user b/ap/app/pppd/.sgbuilt_user
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ap/app/pppd/.sgbuilt_user
diff --git a/ap/app/pppd/Changes-2.3 b/ap/app/pppd/Changes-2.3
new file mode 100644
index 0000000..f5c954b
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/FAQ b/ap/app/pppd/FAQ
new file mode 100644
index 0000000..035da4b
--- /dev/null
+++ b/ap/app/pppd/FAQ
@@ -0,0 +1,636 @@
+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 (authenticatee)
+implementation of Microsoft's CHAP in ppp-2.3; see README.MSCHAP80.
+In ppp-2.4.2, server-side (authenticator) support was added as well as
+support for Microsoft CHAP v2; see README.MSCHAP81.
+
+
+------------------------------------------------------------------------
+
+Q: When the modem hangs up, without the remote system having
+terminated the connection properly, pppd does not notice the hangup,
+but just keeps running.  How do I get pppd to notice the hangup and
+exit?
+
+A: Pppd detects modem hangup by looking for an end-of-file indication
+from the serial driver, which should be generated when the CD (carrier
+detect) signal on the serial port is deasserted.  For this to work:
+
+- The modem has to be set to assert CD when the connection is made and
+deassert it when the phone line hangs up.  Usually the AT&C1 modem
+command sets this mode.
+
+- The cable from the modem to the serial port must connect the CD
+signal (on pin 8).
+
+- Some serial drivers have a "software carrier detect" mode, which
+must be *disabled*.  The method of doing this varies between systems.
+Under SunOS, use the ttysoftcar command.  Under NetBSD, edit /etc/ttys
+to remove the "softcar" flag from the line for the serial port, and
+run ttyflags.
+
+
+------------------------------------------------------------------------
+
+Q: Why should I use PPP compression (BSD-Compress or Deflate) when my
+modem already does V.42 compression?  Won't it slow the CPU down a
+lot?
+
+A: Using PPP compression is preferable, especially when using modems
+over phone lines, for the following reasons:
+
+- The V.42 compression in the modem isn't very strong - it's an LZW
+technique (same as BSD-Compress) with a 10, 11 or 12 bit code size.
+With BSD-Compress you can use a code size of up to 15 bits and get
+much better compression, or you can use Deflate and get even better
+compression ratios.
+
+- I have found that enabling V.42 compression in my 14.4k modem
+increases the round-trip time for a character to be sent, echoed and
+returned by around 40ms, from 160ms to 200ms (with error correction
+enabled).  This is enough to make it feel less responsive on rlogin or
+telnet sessions.  Using PPP compression adds less than 5ms (small
+enough that I couldn't measure it reliably).  I admit my modem is a
+cheapie and other modems may well perform better.
+
+- While compression and decompression do require some CPU time, they
+reduce the amount of time spent in the serial driver to transmit a
+given amount of data.  Many machines require an interrupt for each
+character sent or received, and the interrupt handler can take a
+significant amount of CPU time.  So the increase in CPU load isn't as
+great as you might think.  My measurements indicate that a system with
+a 33MHz 486 CPU should be able to do Deflate compression for serial
+link speeds of up to 100kb/s or more.  It depends somewhat on the type
+of data, of course; for example, when compressing a string of nulls
+with Deflate, it's hard to get a high output data rate from the
+compressor, simply because it compresses strings of nulls so well that
+it has to eat a very large amount of input data to get each byte of
+output.
+
+
+------------------------------------------------------------------------
+
+Q: I get messages saying "Unsupported protocol (...) received".  What do
+these mean?
+
+A: If you only get one or two when pppd starts negotiating with the
+peer, they mean that the peer wanted to negotiate some PPP protocol
+that pppd doesn't understand.  This doesn't represent a problem, it
+simply means that there is some functionality that the peer supports
+that pppd doesn't, so that functionality can't be used.
+
+If you get them sporadically while the link is operating, or if the
+protocol numbers (in parentheses) don't correspond to any valid PPP
+protocol that the peer might be using, then the problem is probably
+that characters are getting corrupted on the receive side, or that
+extra characters are being inserted into the receive stream somehow.
+If this is happening, most packets that get corrupted should get
+discarded by the FCS (Frame Check Sequence, a 16-bit CRC) check, but a
+small number may get through.
+
+One possibility may be that you are receiving broadcast messages on
+the remote system which are being sent over your serial link.  Another
+possibility is that your modem is set for XON/XOFF (software) flow
+control and is inserting ^Q and ^S characters into the receive data
+stream.
+
+
+------------------------------------------------------------------------
+
+Q: I get messages saying "Protocol-Reject for unsupported protocol ...".
+What do these mean?
+
+A: This is the other side of the previous question.  If characters are
+getting corrupted on the way to the peer, or if your system is
+inserting extra bogus characters into the transmit data stream, the
+peer may send protocol-reject messages to you, resulting in the above
+message (since your pppd doesn't recognize the protocol number
+either.)
+
+
+------------------------------------------------------------------------
+
+Q: I get a message saying something like "ioctl(TIOCSETD): Operation
+not permitted".  How do I fix this?
+
+A: This is because pppd is not running as root.  If you have not
+installed pppd setuid-root, you will have to be root to run it.  If
+you have installed pppd setuid-root and you still get this message, it
+is probably because your shell is using some other copy of pppd than
+the installed one - for example, if you are in the pppd directory
+where you've just built pppd and your $PATH has . before /usr/sbin (or
+wherever pppd gets installed).
+
+
+------------------------------------------------------------------------
+
+Q: Has your package been ported to HP/UX or IRIX or AIX?
+
+A: No.  I don't have access to systems running HP/UX or AIX.  No-one
+has volunteered to port it to HP/UX.  I had someone who did a port for
+AIX 4.x, but who is no longer able to maintain it.  And apparently AIX
+3.x is quite different, so it would need a separate port.
+
+IRIX includes a good PPP implementation in the standard distribution,
+as far as I know.
+
+
+------------------------------------------------------------------------
+
+Q: Under SunOS 4, when I try to modload the ppp modules, I get the
+message "can't open /dev/vd: No such device".
+
+A: First check in /dev that there is an entry like this:
+
+crw-r--r--  1  root         57,   0 Oct 2  1991 vd
+
+If not, make one (mknod /dev/vd c 57 0).  If the problem still exists,
+probably your kernel has been configured without the vd driver
+included.  The vd driver is needed for loadable module support.
+
+First, identify the config file that was used.  When you boot your
+machine, or if you run /etc/dmesg, you'll see a line that looks
+something like this:
+
+SunOS Release 4.1.3_U1 (CAP_XBOX) #7: Thu Mar 21 15:31:56 EST 1996
+			^^^^^^^^
+			this is the config file name
+
+The config file will be in the /sys/`arch -k`/conf directory (arch -k
+should return sun4m for a SparcStation 10, sun3x for a Sun 3/80,
+etc.).  Look in there for a line saying "options VDDRV".  If that line
+isn't present (or is commented out), add it (or uncomment it).
+
+You then need to rebuild the kernel as described in the SunOS
+manuals.  Basically you need to run config and make like this:
+
+	/usr/etc/config CAP_XBOX
+	cd ../CAP_XBOX
+	make
+
+(replacing the string CAP_XBOX by the name of the config file for your
+kernel, of course).
+
+Then copy the new kernel to /:
+
+	mv /vmunix /vmunix.working
+	cp vmunix /
+
+and reboot.  Modload should then work.
+
+
+------------------------------------------------------------------------
+
+Q: I'm running Linux (or NetBSD or FreeBSD), and my system comes with
+PPP already.  Should I consider installing this package?  Why?
+
+A: The PPP that is already installed in your system is (or is derived
+from) some version of this PPP package.  You can find out what version
+of this package is already installed with the command "pppd --help".
+If this is older than the latest version, you may wish to install the
+latest version so that you can take advantage of the new features or
+bug fixes.
+
+
+------------------------------------------------------------------------
+
+Q: I'm running pppd in demand mode, and I find that pppd often dials
+out unnecessarily when I try to make a connection within my local
+machine or with a machine on my local LAN.  What can I do about this?
+
+A: Very often the cause of this is that a program is trying to contact
+a nameserver to resolve a hostname, and the nameserver (specified in
+/etc/resolv.conf, usually) is on the far side of the ppp link.  You
+can try executing a command such as `ping myhost' (where myhost is the
+name of the local machine, or some other machine on a local LAN), to
+see whether that starts the ppp link.  If it does, check the setup of
+your /etc/hosts file to make sure you have the local machine and any
+hosts on your local LAN listed, and /etc/resolv.conf and/or
+/etc/nsswitch.conf files to make sure you resolve hostnames from
+/etc/hosts if possible before trying to contact a nameserver.
+
+
+------------------------------------------------------------------------
+
+Q: Since I installed ppp-2.3.6, dialin users to my server have been
+getting this message when they run pppd:
+
+peer authentication required but no suitable secret(s) found for 
+authenticating any peer to us (ispserver)
+
+A: In 2.3.6, the default is to let an unauthenticated peer only use IP
+addresses to which the machine doesn't already have a route.  So on a
+machine with a default route, everyone has to authenticate.  If you
+really don't want that, you can put `noauth' in the /etc/ppp/options
+file.  Note that there is then no check on who is using which IP
+address.  IMHO, this is undesirably insecure, but I guess it may be
+tolerable as long as you don't use any .rhosts files or anything like
+that.  I recommend that you require dialin users to authenticate, even
+if just with PAP using their login password (using the `login' option
+to pppd).  If you do use `noauth', you should at least have a pppusers
+group and set the permissions on pppd to allow only user and group to
+execute it.
+
+------------------------------------------------------------------------
+
+Q: When running pppd as a dial-in server, I often get the message
+"LCP: timeout sending Config-Requests" from pppd.  It seems to be
+random, but dial-out always works fine.  What is wrong?
+
+A: Most modern modems auto-detects the speed of the serial line
+between the modem and the computer.  This auto-detection occurs when
+the computer sends characters to the modem, when the modem is in
+command mode.  It does not occur when the modem is in data mode.
+Thus, if you send commands to the modem at 2400 bps, and then change
+the serial port speed to 115200 bps, the modem will not detect this
+change until something is transmitted from the computer to the modem.
+When running pppd in dial-in mode (i.e. without a connect script),
+pppd sets the speed of the serial port, but does not transmit
+anything.  If the modem was already running at the specified speed,
+everything is fine, but if not, you will just receive garbage from the
+modem.  To cure this, use an init script such as the following:
+
+	pppd ttyS0 115200 modem crtscts init "chat '' AT OK"
+
+To reset the modem and enable auto-answer, use:
+
+	pppd ttyS0 115200 modem crtscts init "chat '' ATZ OK ATS0=1 OK"
diff --git a/ap/app/pppd/PLUGINS b/ap/app/pppd/PLUGINS
new file mode 100644
index 0000000..a655a9c
--- /dev/null
+++ b/ap/app/pppd/PLUGINS
@@ -0,0 +1,287 @@
+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.
+
+A plugin is a standard shared library object, typically with a name
+ending in .so.  They are loaded using the standard dlopen() library
+call, so plugins are only supported on systems which support shared
+libraries and the dlopen call.  At present pppd is compiled with
+plugin support only under Linux and Solaris.
+
+Plugins are loaded into pppd using the `plugin' option, which takes
+one argument, the name of a shared object file.  The plugin option is
+a privileged option.  If the name given does not contain a slash, pppd
+will look in the /usr/lib/pppd/<version> directory for the file, where
+<version> is the version number of pppd, for example, 2.4.2.  I
+suggest that you either give the full path name of the shared object
+file or just the base name; if you don't, it may be possible for
+unscrupulous users to substitute another shared object file for the
+one you mean to load, e.g. by setting the LD_LIBRARY_PATH variable.
+
+Plugins are usually written in C and compiled and linked to a shared
+object file in the appropriate manner for your platform.  Using gcc
+under Linux, a plugin called `xyz' could be compiled and linked with
+the following commands:
+
+	gcc -c -O xyz.c
+	gcc -shared -o xyz.so xyz.o
+
+There are some example plugins in the pppd/plugins directory in the
+ppp distribution.  Currently there is one example, minconn.c, which
+implements a `minconnect' option, which specifies a minimum connect
+time before the idle timeout applies.
+
+Plugins can access global variables within pppd, so it is useful for
+them to #include "pppd.h" from the pppd source directory.
+
+Every plugin must contain a global procedure called `plugin_init'.
+This procedure will get called (with no arguments) immediately after
+the plugin is loaded.  Every plugin should also contain a variable
+called pppd_version declared as follows:
+
+char pppd_version[] = VERSION;
+
+If this declaration is included, pppd will not load the module if its
+version number differs from that compiled into the plugin binary.
+
+Plugins can affect the behaviour of pppd in at least four ways:
+
+1. They can add extra options which pppd will then recognize.  This is
+   done by calling the add_options() procedure with a pointer to an
+   array of option_t structures.  The last entry in the array must
+   have its name field set to NULL.
+
+2. Pppd contains `hook' variables which are procedure pointers.  If a
+   given hook is not NULL, pppd will call the procedure it points to
+   at the appropriate point in its processing.  The plugin can set any
+   of these hooks to point to its own procedures.  See below for a
+   description of the hooks which are currently implemented.
+
+3. Plugin code can call any global procedures and access any global
+   variables in pppd.
+
+4. Plugins can register procedures to be called when particular events
+   occur, using the `notifier' mechanism in pppd.  The differences
+   between hooks and notifiers are that a hook will only call one
+   function, whereas a notifier can call an arbitrary number, and that
+   a hook usually returns some value to pppd, whereas a notifier
+   function returns nothing.
+
+Here is a list of the currently implemented hooks in pppd.
+
+
+int (*idle_time_hook)(struct ppp_idle *idlep);
+
+The idle_time_hook is called when the link first comes up (i.e. when
+the first network protocol comes up) and at intervals thereafter.  On
+the first call, the idlep parameter is NULL, and the return value is
+the number of seconds before pppd should check the link activity, or 0
+if there is to be no idle timeout.
+
+On subsequent calls, idlep points to a structure giving the number of
+seconds since the last packets were sent and received.  If the return
+value is > 0, pppd will wait that many seconds before checking again.
+If it is <= 0, that indicates that the link should be terminated due
+to lack of activity.
+
+
+int (*holdoff_hook)(void);
+
+The holdoff_hook is called when an attempt to bring up the link fails,
+or the link is terminated, and the persist or demand option was used.
+It returns the number of seconds that pppd should wait before trying
+to reestablish the link (0 means immediately).
+
+
+int (*pap_check_hook)(void);
+int (*pap_passwd_hook)(char *user, char *passwd);
+int (*pap_auth_hook)(char *user, char *passwd, char **msgp,
+		     struct wordlist **paddrs,
+		     struct wordlist **popts);
+void (*pap_logout_hook)(void);
+
+These hooks are designed to allow a plugin to replace the normal PAP
+password processing in pppd with something different (e.g. contacting
+an external server).
+
+The pap_check_hook is called to check whether there is any possibility
+that the peer could authenticate itself to us.  If it returns 1, pppd
+will ask the peer to authenticate itself.  If it returns 0, pppd will
+not ask the peer to authenticate itself (but if authentication is
+required, pppd may exit, or terminate the link before network protocol
+negotiation).  If it returns -1, pppd will look in the pap-secrets
+file as it would normally.
+
+The pap_passwd_hook is called to determine what username and password
+pppd should use in authenticating itself to the peer with PAP.  The
+user string will already be initialized, by the `user' option, the
+`name' option, or from the hostname, but can be changed if necessary.
+MAXNAMELEN bytes of space are available at *user, and MAXSECRETLEN
+bytes of space at *passwd.  If this hook returns 0, pppd will use the
+values at *user and *passwd; if it returns -1, pppd will look in the
+pap-secrets file, or use the value from the +ua or password option, as
+it would normally.
+
+The pap_auth_hook is called to determine whether the username and
+password supplied by the peer are valid.  user and passwd point to
+null-terminated strings containing the username and password supplied
+by the peer, with non-printable characters converted to a printable
+form.  The pap_auth_hook function should set msg to a string to be
+returned to the peer and return 1 if the username/password was valid
+and 0 if not.  If the hook returns -1, pppd will look in the
+pap-secrets file as usual.
+
+If the username/password was valid, the hook can set *paddrs to point
+to a wordlist containing the IP address(es) which the peer is
+permitted to use, formatted as in the pap-secrets file.  It can also
+set *popts to a wordlist containing any extra options for this user
+which pppd should apply at this point.
+
+The pap_logout_hook is called when the link is terminated, instead of
+pppd's internal `plogout' function.  It can be used for accounting
+purposes.  This hook is deprecated and will be replaced by a notifier.
+
+
+int (*chap_check_hook)(void);
+int (*chap_passwd_hook)(char *user, char *passwd);
+int (*chap_verify_hook)(char *name, char *ourname, int id,
+			struct chap_digest_type *digest,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space)
+
+These hooks are designed to allow a plugin to replace the normal CHAP
+password processing in pppd with something different (e.g. contacting
+an external server).
+
+The chap_check_hook is called to check whether there is any possibility
+that the peer could authenticate itself to us.  If it returns 1, pppd
+will ask the peer to authenticate itself.  If it returns 0, pppd will
+not ask the peer to authenticate itself (but if authentication is
+required, pppd may exit, or terminate the link before network protocol
+negotiation).  If it returns -1, pppd will look in the chap-secrets
+file as it would normally.
+
+The chap_passwd_hook is called to determine what password
+pppd should use in authenticating itself to the peer with CHAP.  The
+user string will already be initialized, by the `user' option, the
+`name' option, or from the hostname, but can be changed if necessary.
+This hook is called only if pppd is a client, not if it is a server.
+
+MAXSECRETLEN bytes of space are available at *passwd.  If this hook
+returns 0, pppd will use the value *passwd; if it returns -1, pppd
+will fail to authenticate.
+
+The chap_verify_hook is called to determine whether the peer's
+response to our CHAP challenge is valid -- it should return 1 if valid
+or 0 if not.  The parameters are:
+
+* name points to a null-terminated string containing the username
+  supplied by the peer, or the remote name specified with the
+  "remotename" option.
+* ourname points to a null-terminated string containing the name of
+  the local machine (the hostname, or the name specified with the
+  "name" option).
+* id is the value of the id field from the challenge.
+* digest points to a chap_digest_type struct, which contains an
+  identifier for the type of digest in use plus function pointers for
+  functions for dealing with digests of that type.
+* challenge points to the challenge as a counted string (length byte
+  followed by the actual challenge bytes).
+* response points to the response as a counted string.
+* message points to an area of message_space bytes in which to store
+  any message that should be returned to the peer.
+
+
+int (*null_auth_hook)(struct wordlist **paddrs,
+		      struct wordlist **popts);
+
+This hook allows a plugin to determine what the policy should be if
+the peer refuses to authenticate when it is requested to.  If the
+return value is 0, the link will be terminated; if it is 1, the
+connection is allowed to proceed, and in this case *paddrs and *popts
+can be set as for pap_auth_hook, to specify what IP addresses are
+permitted and any extra options to be applied.  If the return value is
+-1, pppd will look in the pap-secrets file as usual.
+
+
+void (*ip_choose_hook)(u_int32_t *addrp);
+
+This hook is called at the beginning of IPCP negotiation.  It gives a
+plugin the opportunity to set the IP address for the peer; the address
+should be stored in *addrp.  If nothing is stored in *addrp, pppd will
+determine the peer's address in the usual manner.
+
+
+int (*allowed_address_hook)(u_int32_t addr)
+
+This hook is called to see if a peer is allowed to use the specified
+address.  If the hook returns 1, the address is accepted.  If it returns
+0, the address is rejected.  If it returns -1, the address is verified
+in the normal away against the appropriate options and secrets files.
+
+
+void (*snoop_recv_hook)(unsigned char *p, int len)
+void (*snoop_send_hook)(unsigned char *p, int len)
+
+These hooks are called whenever pppd receives or sends a packet.  The
+packet is in p; its length is len.  This allows plugins to "snoop in"
+on the pppd conversation.  The hooks may prove useful in implmenting
+L2TP.
+
+
+void (*multilink_join_hook)();
+
+This is called whenever a new link completes LCP negotiation and joins
+the bundle, if we are doing multilink.
+
+
+A plugin registers itself with a notifier by declaring a procedure of
+the form:
+
+void my_notify_proc(void *opaque, int arg);
+
+and then registering the procedure with the appropriate notifier with
+a call of the form
+
+	add_notifier(&interesting_notifier, my_notify_proc, opaque);
+
+The `opaque' parameter in the add_notifier call will be passed to
+my_notify_proc every time it is called.  The `arg' parameter to
+my_notify_proc depends on the notifier.
+
+A notify procedure can be removed from the list for a notifier with a
+call of the form
+
+	remove_notifier(&interesting_notifier, my_notify_proc, opaque);
+
+Here is a list of the currently-implemented notifiers in pppd.
+
+* pidchange.  This notifier is called in the parent when pppd has
+  forked and the child is continuing pppd's processing, i.e. when pppd
+  detaches from its controlling terminal.  The argument is the pid of
+  the child.
+
+* phasechange.  This is called when pppd moves from one phase of
+  operation to another.  The argument is the new phase number.
+
+* exitnotify.  This is called just before pppd exits.  The argument is
+  the status with which pppd will exit (i.e. the argument to exit()).
+
+* sigreceived.  This is called when a signal is received, from within
+  the signal handler.  The argument is the signal number.
+
+* ip_up_notifier.  This is called when IPCP has come up.
+
+* ip_down_notifier.  This is called when IPCP goes down.
+
+* auth_up_notifier.  This is called when the peer has successfully
+  authenticated itself.
+
+* link_down_notifier.  This is called when the link goes down.
+
+
+
+## $Id: PLUGINS,v 1.8 2008/06/15 07:02:18 paulus Exp $ ##
diff --git a/ap/app/pppd/README b/ap/app/pppd/README
new file mode 100644
index 0000000..a3bfe17
--- /dev/null
+++ b/ap/app/pppd/README
@@ -0,0 +1,290 @@
+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 IPV6 and the protocols layered above them, such as TCP
+and UDP.  The Linux port of this package also has support for IPX.
+
+This PPP implementation consists of two parts:
+
+- Kernel code, which establishes a network interface and passes
+packets between the serial port, the kernel networking code and the
+PPP daemon (pppd).  This code is implemented using STREAMS modules on
+Solaris, and as a line discipline under Linux.
+
+- The PPP daemon (pppd), which negotiates with the peer to establish
+the link and sets up the ppp network interface.  Pppd includes support
+for authentication, so you can control which other systems may make a
+PPP connection and what IP addresses they may use.
+
+The platforms supported by this package are Linux and Solaris.  I have
+code for NeXTStep, FreeBSD, SunOS 4.x, SVR4, Tru64 (Digital Unix), AIX
+and Ultrix but no active maintainers for these platforms.  Code for
+all of these except AIX is included in the ppp-2.3.11 release.
+
+The kernel code for Linux is no longer distributed with this package,
+since the relevant kernel code is in the official Linux kernel source
+(and has been for many years) and is included in all reasonably modern
+Linux distributions.  The Linux kernel code supports using PPP over
+things other than serial ports, such as PPP over Ethernet and PPP over
+ATM.
+
+
+Installation.
+*************
+
+The file SETUP contains general information about setting up your
+system for using PPP.  There is also a README file for each supported
+system, which contains more specific details for installing PPP on
+that system.  The supported systems, and the corresponding README
+files, are:
+
+	Linux				README.linux
+	Solaris				README.sol2
+
+In each case you start by running the ./configure script.  This works
+out which operating system you are using and creates the appropriate
+makefiles.  You then run `make' to compile the user-level code, and
+(as root) `make install' to install the user-level programs pppd, chat
+and pppstats.
+
+N.B. Since 2.3.0, leaving the permitted IP addresses column of the
+pap-secrets or chap-secrets file empty means that no addresses are
+permitted.  You need to put a "*" in that column to allow the peer to
+use any IP address.  (This only applies where the peer is
+authenticating itself to you, of course.)
+
+
+What's new in ppp-2.4.5.
+************************
+
+* Under Linux, pppd can now operate in a mode where it doesn't request
+  the peer's IP address, as some peers refuse to supply an IP address.
+  Since Linux supports device routes as well as gateway routes, it's
+  possible to have no remote IP address assigned to the ppp interface
+  and still route traffic over it.
+
+* Pppd now works better with 3G modems that do strange things such as
+  sending IPCP Configure-Naks with the same values over and over again.
+
+* The PPP over L2TP plugin is included, which works with the pppol2tp
+  PPP channel code in the Linux kernel.  This allows pppd to be used
+  to set up tunnels using the Layer 2 Tunneling Protocol.
+
+* A new 'enable-session' option has been added, which enables session
+  accounting via PAM or wtwp/wtmpx, as appropriate.  See the pppd man
+  page for details.
+
+* Several bugs have been fixed.
+
+
+What was new in ppp-2.4.4.
+**************************
+
+* Pppd will now run /etc/ppp/ip-pre-up, if it exists, after creating
+  the ppp interface and configuring its IP addresses but before
+  bringing it up.  This can be used, for example, for adding firewall
+  rules for the interface.
+
+* Lots of bugs fixed, particularly in the area of demand-dialled and
+  persistent connections.
+
+* The rp-pppoe plugin now accepts any interface name (that isn't an
+  existing pppd option name) without putting "nic-" on the front of
+  it, not just eth*, nas*, tap* and br*.
+
+
+What was new in ppp-2.4.3.
+**************************
+
+* The configure script now accepts --prefix and --sysconfdir options.
+  These default to /usr/local and /etc.  If you want pppd put in
+  /usr/sbin as before, use ./configure --prefix=/usr.
+
+* Doing `make install' no longer puts example configuration files in
+  /etc/ppp.  Use `make install-etcppp' if you want that.
+
+* The code has been updated to work with version 0.8.3 of libpcap.
+  Unfortunately the libpcap maintainers removed support for the
+  "inbound" and "outbound" keywords on PPP links, meaning that if you
+  link pppd with libpcap-0.8.3, you can't use those keywords in the
+  active-filter and pass-filter expressions.  The support has been
+  reinstated in the CVS version and should be in future libpcap
+  releases.  If you need the in/outbound keywords, use a later release
+  than 0.8.3, or get the CVS version from http://www.tcpdump.org.
+
+* There is a new option, child-timeout, which sets the length of time
+  that pppd will wait for child processes (such as the command
+  specified with the pty option) to exit before exiting itself.  It
+  defaults to 5 seconds.  After the timeout, pppd will send a SIGTERM
+  to any remaining child processes and exit.  A value of 0 means no
+  timeout.
+
+* Various bugs have been fixed, including some CBCP packet parsing
+  bugs that could lead to the peer being able to crash pppd if CBCP
+  support is enabled.
+
+* Various fixes and enhancements to the radius and rp-pppoe plugins
+  have been added.
+
+* There is a new winbind plugin, from Andrew Bartlet of the Samba
+  team, which provides the ability to authenticate the peer against an
+  NT domain controller using MS-CHAP or MS-CHAPV2.
+
+* There is a new pppoatm plugin, by various authors, sent in by David
+  Woodhouse.
+
+* The multilink code has been substantially reworked.  The first pppd
+  for a bundle still controls the ppp interface, but it doesn't exit
+  until all the links in the bundle have terminated.  If the first
+  pppd is signalled to exit, it signals all the other pppds
+  controlling links in the bundle.
+
+* The TDB code has been updated to the latest version.  This should
+  eliminate the problem that some people have seen where the database
+  file (/var/run/pppd.tdb) keeps on growing.  Unfortunately, however,
+  the new code uses an incompatible database format.  For this reason,
+  pppd now uses /var/run/pppd2.tdb as the database filename.
+
+
+What was new in ppp-2.4.2.
+**************************
+
+* The CHAP code has been rewritten.  Pppd now has support for MS-CHAP
+  V1 and V2 authentication, both as server and client.  The new CHAP
+  code is cleaner than the old code and avoids some copyright problems
+  that existed in the old code.
+
+* MPPE (Microsoft Point-to-Point Encryption) support has been added,
+  although the current implementation shouldn't be considered
+  completely secure.  (There is no assurance that the current code
+  won't ever transmit an unencrypted packet.)
+
+* James Carlson's implementation of the Extensible Authentication
+  Protocol (EAP) has been added.
+
+* Support for the Encryption Control Protocol (ECP) has been added.
+
+* Some new plug-ins have been included:
+  - A plug-in for kernel-mode PPPoE (PPP over Ethernet)
+  - A plug-in for supplying the PAP password over a pipe from another
+    process
+  - A plug-in for authenticating using a Radius server.
+
+* Updates and bug-fixes for the Solaris port.
+
+* The CBCP (Call Back Control Protocol) code has been updated.  There
+  are new options `remotenumber' and `allow-number'.
+
+* Extra hooks for plugins to use have been added.
+
+* There is now a `maxoctets' option, which causes pppd to terminate
+  the link once the number of bytes passed on the link exceeds a given
+  value.
+
+* There are now options to control whether pppd can use the IPCP
+  IP-Address and IP-Addresses options: `ipcp-no-address' and
+  `ipcp-no-addresses'.
+
+* Fixed several bugs, including potential buffer overflows in chat.
+
+
+What was new in ppp-2.4.1.
+**************************
+
+* Pppd can now print out the set of options that are in effect.  The
+  new `dump' option causes pppd to print out the option values after
+  option parsing is complete.  The `dryrun' option causes pppd to
+  print the options and then exit.
+
+* The option parsing code has been fixed so that options in the
+  per-tty options file are parsed correctly, and don't override values
+  from the command line in most cases.
+
+* The plugin option now looks in /usr/lib/pppd/<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
+  2.4.x and later Linux kernels.
+
+* All the pppd processes running on a system now write information
+  into a common database.  I used the `tdb' code from samba for this.
+
+* New hooks have been added.
+
+For a list of the changes made during the 2.3 series releases of this
+package, see the Changes-2.3 file.
+
+
+Compression methods.
+********************
+
+This package supports two packet compression methods: Deflate and
+BSD-Compress.  Other compression methods which are in common use
+include Predictor, LZS, and MPPC.  These methods are not supported for
+two reasons - they are patent-encumbered, and they cause some packets
+to expand slightly, which pppd doesn't currently allow for.
+BSD-Compress and Deflate (which uses the same algorithm as gzip) don't
+ever expand packets.
+
+
+Contacts.
+*********
+
+The comp.protocols.ppp newsgroup is a useful place to get help if you
+have trouble getting your ppp connections to work.  Please do not send
+me questions of the form "please help me get connected to my ISP" -
+I'm sorry, but I simply do not have the time to answer all the
+questions like this that I get.
+
+If you find bugs in this package, please report them to the maintainer
+for the port for the operating system you are using:
+
+Linux			Paul Mackerras <paulus@samba.org>
+Solaris			James Carlson <carlson@workingcode.com>
+
+
+Copyrights:
+***********
+
+All of the code can be freely used and redistributed.  The individual
+source files each have their own copyright and permission notice.
+Pppd, pppstats and pppdump are under BSD-style notices.  Some of the
+pppd plugins are GPL'd.  Chat is public domain.
+
+
+Distribution:
+*************
+
+The primary site for releases of this software is:
+
+	ftp://ftp.samba.org/pub/ppp/
+
+
diff --git a/ap/app/pppd/README.MPPE b/ap/app/pppd/README.MPPE
new file mode 100644
index 0000000..1d62efd
--- /dev/null
+++ b/ap/app/pppd/README.MPPE
@@ -0,0 +1,78 @@
+PPP Support for MPPE (Microsoft Point to Point Encryption)
+==========================================================
+
+Frank Cusack		frank@google.com
+Mar 19, 2002
+
+Updated by Paul Mackerras, Sep 2008
+
+
+DISCUSSION
+
+MPPE is Microsoft's encryption scheme for PPP links.  It is pretty much
+solely intended for use with PPP over Internet links -- if you have a true
+point to point link you have little need for encryption.  It is generally
+used with PPTP.
+
+MPPE is negotiated within CCP (Compression Control Protocol) as option
+18.  In order for MPPE to work, both peers must agree to do it.  This
+complicates things enough that I chose to implement it as strictly a binary
+option, off by default.  If you turn it on, all other compression options
+are disabled and MPPE *must* be negotiated successfully in both directions
+(CCP is unidirectional) or the link will be disconnected.  I think this is
+reasonable since, if you want encryption, you want encryption.  That is,
+I am not convinced that optional encryption is useful.
+
+While PPP regards MPPE as a "compressor", it actually expands every frame
+by 4 bytes, the MPPE overhead (encapsulation).
+
+Because of the data expansion, you'll see that ppp interfaces get their
+mtu reduced by 4 bytes whenever MPPE is negotiated.  This is because
+when MPPE is active, it is *required* that *every* packet be encrypted.
+PPPD sets the mtu = MIN(peer mru, configured mtu).  To ensure that
+MPPE frames are not larger than the peer's mru, we reduce the mtu by 4
+bytes so that the network layer never sends ppp a packet that's too large.
+
+There is an option to compress the data before encrypting (MPPC), however
+the algorithm is patented and requires execution of a license with Hifn.
+MPPC as an RFC is a complete farce.  I have no further details on MPPC.
+
+Some recommendations:
+
+- Use stateless mode.  Stateful mode is disabled by default.  Unfortunately,
+  stateless mode is very expensive as the peers must rekey for every packet.
+- Use 128-bit encryption.
+- Use MS-CHAPv2 only.
+
+Reference documents:
+
+    <http://www.ietf.org/rfc/rfc3078.txt> MPPE
+    <http://www.ietf.org/rfc/rfc3079.txt> MPPE Key Derivation
+    <http://www.ietf.org/rfc/rfc2118.txt> MPPC
+    <http://www.ietf.org/rfc/rfc2637.txt> PPTP
+    <http://www.ietf.org/rfc/rfc2548.txt> MS RADIUS Attributes
+
+You might be interested in PoPToP, a Linux PPTP server.  You can find it at
+<http://www.poptop.org/>
+
+RADIUS support for MPPE is from Ralf Hofmann, <ralf.hofmann@elvido.net>.
+
+
+BUILDING THE PPPD
+
+The userland component of PPPD has no additional requirements above
+those for MS-CHAP and MS-CHAPv2.
+
+MPPE support is now included in the mainline Linux kernel releases.
+
+
+CONFIGURATION
+
+See pppd(8) for the MPPE options.  Under Linux, if your modutils is earlier
+than 2.4.15, you will need to add
+
+    alias ppp-compress-18 ppp_mppe
+
+to /etc/modules.conf.
+
+
diff --git a/ap/app/pppd/README.MSCHAP80 b/ap/app/pppd/README.MSCHAP80
new file mode 100644
index 0000000..2c3172a
--- /dev/null
+++ b/ap/app/pppd/README.MSCHAP80
@@ -0,0 +1,151 @@
+PPP Support for Microsoft's CHAP-80
+===================================
+
+Eric Rosenquist          rosenqui@strataware.com
+(updated by Paul Mackerras)
+(updated by Al Longyear)
+(updated by Farrell Woods)
+(updated by Frank Cusack)
+
+INTRODUCTION
+
+Microsoft has introduced an extension to the Challenge/Handshake
+Authentication Protocol (CHAP) which avoids storing cleartext
+passwords on a server.  (Unfortunately, this is not as secure as it
+sounds, because the encrypted password stored on a server can be used
+by a bogus client to gain access to the server just as easily as if
+the password were stored in cleartext.)  The details of the Microsoft
+extensions can be found in the document:
+
+    <http://www.ietf.org/rfc/rfc2433.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 MS> <magic 0x46a3>]
+                                           ^^^^^^^
+
+MS-CHAP is enabled by default under Linux in pppd/Makefile.linux by
+the line "CHAPMS=y".
+
+
+CONFIGURATION
+
+If you've never used PPPD with CHAP before, read the man page (type
+"man pppd") and read the description in there.  Basically, you need to
+edit the "chap-secrets" file typically named /etc/ppp/chap-secrets.
+This should contain the following two lines for each system with which
+you use CHAP (with no leading blanks):
+
+    RemoteHost  Account     Secret
+    Account     RemoteHost  Secret
+
+Note that you need both lines and that item 1 and 2 are swapped in the
+second line.  I'm not sure why you need it twice, but it works and I didn't
+have time to look into it further.  The "RemoteHost" is a somewhat
+arbitrary name for the remote Windows NT system you're dialing.  It doesn't
+have to match the NT system's name, but it *does* have to match what you
+use with the "remotename" parameter.  The "Account" is the Windows NT
+account name you have been told to use when dialing, and the "Secret" is
+the password for that account.  For example, if your service provider calls
+their machine "DialupNT" and tells you your account and password are
+"customer47" and "foobar", add the following to your chap-secrets file:
+
+    DialupNT    customer47  foobar
+    customer47  DialupNT    foobar
+
+The only other thing you need to do for MS-CHAP (compared to normal CHAP)
+is to always use the "remotename" option, either on the command line or in
+your "options" file (see the pppd man page for details).  In the case of
+the above example, you would need to use the following command line:
+
+    pppd name customer47 remotename DialupNT <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
+RFC 2433:
+
+ 646 ERROR_RESTRICTED_LOGON_HOURS
+ 647 ERROR_ACCT_DISABLED
+ 648 ERROR_PASSWD_EXPIRED
+ 649 ERROR_NO_DIALIN_PERMISSION
+ 691 ERROR_AUTHENTICATION_FAILURE
+ 709 ERROR_CHANGING_PASSWORD
+
+You'll see these in your pppd log as a line similar to:
+
+   Remote message: E=649 R=0
+
+The "E=" is the error number from the table above, and the "R=" flag
+indicates whether the error is transient and the client should retry.  If
+you consistently get error 691, then either you're using the wrong account
+name/password, or the DES library or MD4 hashing (in md4.c) aren't working
+properly.  Verify your account name and password (use a Windows NT or
+Windows 95 system to dial-in if you have one available).  If that checks
+out, test the DES library with the "destest" program included with the DES
+library.  If DES checks out, the md4.c routines are probably failing
+(system byte ordering may be a problem) or my code is screwing up.  I've
+only got access to a Linux system, so you're on your own for anything else.
+
+Another thing that might cause problems is that some RAS servers won't
+respond at all to LCP config requests without seeing the word "CLIENT"
+from the other end.  If you see pppd sending out LCP config requests
+without getting any reply, try putting something in your chat script
+to send the word CLIENT after the modem has connected.
+
+STILL TO DO
+
+A site using only MS-CHAP to authenticate has no need to store cleartext
+passwords in the "chap-secrets" file.  A utility that spits out the ASCII
+hex MD4 hash of a given password would be nice, and would allow that hash
+to be used in chap-secrets in place of the password.  The code to do this
+could quite easily be lifted from chap_ms.c (you have to convert the
+password to Unicode before hashing it).  The chap_ms.c file would also have
+to be changed to recognize a password hash (16 binary bytes == 32 ASCII hex
+characters) and skip the hashing stage.  This would have no real security
+value as the hash is plaintext-equivalent.
diff --git a/ap/app/pppd/README.MSCHAP81 b/ap/app/pppd/README.MSCHAP81
new file mode 100644
index 0000000..91199f3
--- /dev/null
+++ b/ap/app/pppd/README.MSCHAP81
@@ -0,0 +1,65 @@
+PPP Support for Microsoft's CHAP-81
+===================================
+
+Frank Cusack		frank@google.com
+
+Some text verbatim from README.MSCHAP80,
+by Eric Rosenquist, rosenqui@strataware.com
+
+INTRODUCTION
+
+First, please read README.MSCHAP80; almost everything there applies here.
+MS-CHAP was basically devised by Microsoft because rather than store
+plaintext passwords, they (Microsoft) store the md4 hash of passwords.
+It provides no advantage over standard CHAP, since the hash is used
+as plaintext-equivalent.  (Well, the Change-Password packet is arguably
+an advantage.)  It does introduce a significant weakness if the LM hash
+is used.  Additionally, the format of the failure packet potentially
+gives information to an attacker.  The weakness of the LM hash is partly
+addressed in RFC 2433, which deprecates its use.
+
+MS-CHAPv2 adds 2 benefits to MS-CHAP.  (1) The LM hash is no longer
+used.  (2) Mutual authentication is required.  Note that the mutual
+authentication in MS-CHAPv2 is different than the case where both PPP
+peers require authentication from the other; the former proves that
+the server has access to the client's password, the latter proves that
+the server has access to a secret which the client also has -- which
+may or may not be the same as the client's password (but should not be
+the same, per RFC 1994).  Whether this provides any actual benefit is
+outside the scope of this document.  The details of MS-CHAPv2 can be
+found in the document:
+
+    <http://www.ietf.org/rfc/rfc2759.txt>
+
+
+BUILDING THE PPPD
+
+In addition to the requirements for MS-CHAP, MS-CHAPv2 uses the SHA-1
+hash algorithm.  A public domain implementation is provided with pppd.
+
+
+TROUBLESHOOTING
+
+Assuming that everything else has been configured correctly for PPP and
+CHAP, the MS-CHAPv2-specific problems you're likely to encounter are mostly
+related to your Windows NT account and its settings.  A Microsoft server
+returns error codes in its CHAP response.  The following are extracted from
+RFC 2759:
+
+ 646 ERROR_RESTRICTED_LOGON_HOURS
+ 647 ERROR_ACCT_DISABLED
+ 648 ERROR_PASSWD_EXPIRED
+ 649 ERROR_NO_DIALIN_PERMISSION
+ 691 ERROR_AUTHENTICATION_FAILURE
+ 709 ERROR_CHANGING_PASSWORD
+
+You'll see these in your pppd log as a line similar to:
+
+   Remote message: E=649 No dialin permission
+
+Previously, pppd would log this as:
+
+   Remote message: E=649 R=0
+
+Now, the text message is logged (both for MS-CHAP and MS-CHAPv2).
+
diff --git a/ap/app/pppd/README.cbcp b/ap/app/pppd/README.cbcp
new file mode 100644
index 0000000..f1e4ba1
--- /dev/null
+++ b/ap/app/pppd/README.cbcp
@@ -0,0 +1,51 @@
+	     Microsoft Call Back Configuration Protocol.
+			by Pedro Roque Marques
+			(updated by Paul Mackerras)
+
+The CBCP is a method by which the Microsoft Windows NT Server may
+implement additional security. It is possible to configure the server
+in such a manner so as to require that the client systems which
+connect with it are required that following a valid authentication to
+leave a method by which the number may be returned call.
+
+It is a requirement of servers to be so configured that the protocol be
+exchanged.
+
+So, this set of patches may be applied to the pppd process to enable
+the cbcp client *only* portion of the specification. It is primarily
+meant to permit connection with Windows NT Servers.
+
+The ietf-working specification may be obtained from ftp.microsoft.com
+in the developr/rfc directory.
+
+The ietf task group has decided to recommend that the LCP sequence be
+extended to permit the callback operation. For this reason, these
+patches are not 'part' of pppd but are an adjunct to the code.
+
+To enable CBCP support, all that is required is to uncomment the line
+in Makefile.linux that sets CBCP=y and recompile pppd.
+
+I use such script to make a callback:
+
+pppd debug nodetach /dev/modem 115200 crtscts modem	\
+callback 222222 name NAME remotename SERVER	\
+connect 'chat -v "" atz OK atdt111111 CONNECT ""'
+sleep 1
+pppd debug /dev/modem 115200 crtscts modem	\
+name NAME remotename SERVER defaultroute	\
+connect 'chat -v RING ATA CONNECT "\c"'
+
+First we invoke pppd with 'nodetach' option in order to not detach from
+the controlling terminal and 'callback NUMBER' option, then wait for
+1 second and invoke pppd again which waits for a callback (RING) and
+then answers (ATA). Number 222222 is a callback number, i.e. server will
+call us back at this number, while number 111111 is the number we are
+calling to.
+
+You have to put in /etc/ppp/chap-secrets the following two lines:
+
+NAME SERVER PASSWORD
+SERVER NAME PASSWORD
+
+You have to use your real login name, remote server name and password.
+
diff --git a/ap/app/pppd/README.eap-srp b/ap/app/pppd/README.eap-srp
new file mode 100644
index 0000000..6900b0d
--- /dev/null
+++ b/ap/app/pppd/README.eap-srp
@@ -0,0 +1,149 @@
+EAP with MD5-Challenge and SRP-SHA1 support
+by James Carlson, Sun Microsystems
+Version 2, September 22nd, 2002
+
+
+1.  What it does
+
+    The Extensible Authentication Protocol (EAP; RFC 2284) is a
+    security protocol that can be used with PPP.  It provides a means
+    to plug in multiple optional authentication methods.
+
+    This implementation includes the required default MD5-Challenge
+    method, which is similar to CHAP (RFC 1994), as well as the new
+    SRP-SHA1 method.  This latter method relies on an exchange that is
+    not vulnerable to dictionary attacks (as is CHAP), does not
+    require the server to keep a cleartext copy of the secret (as in
+    CHAP), supports identity privacy, and produces a temporary shared
+    key that could be used for data encryption.
+
+    The SRP-SHA1 method is based on draft-ietf-pppext-eap-srp-03.txt,
+    a work in progress.
+
+2.  Required libraries
+
+    Two other packages are required first.  Download and install
+    OpenSSL and Thomas Wu's SRP implementation.
+
+	http://www.openssl.org/ (or ftp://ftp.openssl.org/source/)
+	http://srp.stanford.edu/
+
+    Follow the directions in each package to install the SSL and SRP
+    libraries.  Once SRP is installed, you may run tconf as root to
+    create known fields, if desired.  (This step is not required.)
+
+3.  Installing the patch
+
+    The EAP-SRP patch described here is integrated into this version
+    of pppd.  The following patch may be used with older pppd sources:
+
+	ftp://playground.sun.com/carlsonj/eap/ppp-2.4.1-eap-1.tar.gz
+
+    Configure, compile, and install as root.  You may want to edit
+    pppd/Makefile after configuring to enable or disable optional
+    features.
+
+	% ./configure
+	% make
+	% su
+	# make install
+
+    If you use csh or tcsh, run "rehash" to pick up the new commands.
+
+    If you're using Solaris, and you run into trouble with the
+    pseudonym feature on the server side ("no DES here" shows in the
+    log file), make sure that you have the "domestic" versions of the
+    DES libraries linked.  You should see "crypt_d" in "ldd
+    /usr/local/bin/pppd".  If you see "crypt_i" instead, then make
+    sure that /usr/lib/libcrypt.* links to /usr/lib/libcrypt_d.*.  (If
+    you have the international version of Solaris, then you won't have
+    crypt_d.  You might want to find an alternative DES library.)
+
+4.  Adding the secrets
+
+    On the EAP SRP-SHA1 client side, access to the cleartext secret is
+    required.  This can be done in two ways:
+
+	- Enter the client name, server name, and password in the
+          /etc/ppp/srp-secrets file.  This file has the same format as
+          the existing chap-secrets and pap-secrets files.
+
+	  clientname servername "secret here"
+
+	- Use the "password" option in any of the standard
+          configuration files (or the command line) to specify the
+          secret.
+
+	  password "secret here"
+
+    On the EAP SRP-SHA1 server side, a secret verifier is required.
+    This is a one-way hash of the client's name and password.  To
+    generate this value, run the srp-entry program (see srp-entry(8)).
+    This program prompts for the client name and the passphrase (the
+    secret).  The output will be an entry, such as the following,
+    suitable for use in the server's srp-secrets file.  Note that if
+    this is transferred by cut-and-paste, the entry must be a single
+    line of text in the file.
+
+pppuser srpserver 0:LFDpwg4HBLi4/kWByzbZpW6pE95/iIWBSt7L.DAkHsvwQphtiq0f6reoUy/1LC1qYqjcrV97lCDmQHQd4KIACGgtkhttLdP3KMowvS0wLXLo25FPJeG2sMAUEWu/HlJPn2/gHyh9aT.ZxUs5MsoQ1E61sJkVBc.2qze1CdZiQGTK3qtWRP6DOpM1bfhKtPoVm.g.MiCcTMWzc54xJUIA0mgKtpthE3JrqCc81cXUt4DYi5yBzeeGTqrI0z2/Gj8Jp7pS4Fkq3GmnYjMxnKfQorFXNwl3m7JSaPa8Gj9/BqnorJOsnSMlIhBe6dy4CYytuTbNb4Wv/nFkmSThK782V:2cIyMp1yKslQgE *
+
+    The "secret" field consists of three entries separated by colons.
+    The first entry is the index of the modulus and generator from
+    SRP's /etc/tpasswd.conf.  If the special value 0 is used, then the
+    well-known modulus/generator value is used (this is recommended,
+    because it is much faster).  The second value is the verifier
+    value.  The third is the password "salt."  These latter two values
+    are encoded in base64 notation.
+
+    For EAP MD5-Challenge, both client and server use the existing
+    /etc/ppp/chap-secrets file.
+
+5.  Configuration options
+
+    There are two main options relating to EAP available for the
+    client.  These are:
+
+	refuse-eap		- refuse to authenticate with EAP
+	srp-use-pseudonym	- use the identity privacy if
+				  offered by server
+
+    The second option stores a pseudonym, if offered by the EAP
+    SRP-SHA1 server, in the $HOME/.ppp_pseudonym file.  The pseudonym
+    is typically an encrypted version of the client identity.  During
+    EAP start-up, the pseudonym stored in this file is offered to the
+    peer as the identity.  If this is accepted by the peer, then
+    eavesdroppers will be unable to determine the identity of the
+    client.  Each time the client is authenticated, the server will
+    offer a new pseudoname to the client using an obscured (reversibly
+    encrypted) message.  Thus, access across successive sessions
+    cannot be tracked.
+
+    There are two main options for EAP on the server:
+
+	require-eap		- require client to use EAP
+	srp-pn-secret "string"	- set server's pseudoname secret
+
+    The second option sets the long-term secret used on the server to
+    encrypt the user's identity to produce pseudonames.  The
+    pseudoname is constructed by hashing this string with the current
+    date (to the nearest day) with SHA1, then using this hash as the
+    key for a DES encryption of the client's name.  The date is added
+    to the hash for two reasons.  First, this allows the pseudonym to
+    change daily.  Second, it allows the server to decode any previous
+    pseudonym by trying previous dates.
+
+    See the pppd(8) man page for additional options.
+
+6.  Comments welcome!
+
+    This is still an experimental implementation.  It has been tested
+    and reviewed carefully for correctness, but may still be
+    incomplete or have other flaws.  All comments are welcome.  Please
+    address them to the author:
+
+		james.d.carlson@sun.com
+
+    or, for EAP itself or the SRP extensions to EAP, to the IETF PPP
+    Extensions working group:
+
+		ietf-ppp@merit.edu
diff --git a/ap/app/pppd/README.linux b/ap/app/pppd/README.linux
new file mode 100644
index 0000000..d441427
--- /dev/null
+++ b/ap/app/pppd/README.linux
@@ -0,0 +1,278 @@
+		PPP for Linux
+		-------------
+
+		Paul Mackerras
+		8 March 2001
+
+		for ppp-2.4.2
+		Updated for ppp-2.4.5, Sep 08
+
+1. Introduction
+---------------
+
+The Linux PPP implementation includes both kernel and user-level
+parts.  This package contains the user-level part, which consists of
+the PPP daemon (pppd) and associated utilities.  In the past this
+package has contained updated kernel drivers.  This is no longer
+necessary, as the current kernel sources contain up-to-date drivers
+(and have done since the 2.4.x kernel series).
+
+The Linux PPP implementation is capable of being used both for
+initiating PPP connections (as a `client') or for handling incoming
+PPP connections (as a `server').  Note that this is an operational
+distinction, based on how the connection is created, rather than a
+distinction that is made in the PPP protocols themselves.
+
+Mostly this package is used for PPP connections over modems connected
+via asynchronous serial ports, so this guide concentrates on this
+situation.
+
+The PPP protocol consists of two parts.  One is a scheme for framing
+and encoding packets, the other is a series of protocols called LCP,
+IPCP, PAP and CHAP, for negotiating link options and for
+authentication.  This package similarly consists of two parts: a
+kernel module which handles PPP's low-level framing protocol, and a
+user-level program called pppd which implements PPP's negotiation
+protocols.
+
+The kernel module assembles/disassembles PPP frames, handles error
+detection, and forwards packets between the serial port and either the
+kernel network code or the user-level program pppd.  IP packets go
+directly to the kernel network code.  So once pppd has negotiated the
+link, it in practice lies completely dormant until you want to take
+the link down, when it negotiates a graceful disconnect.
+
+
+2. Installation
+---------------
+
+2.1 Kernel driver
+
+Assuming you are running a recent 2.4 or 2.6 (or later) series kernel,
+the kernel source code will contain an up-to-date kernel PPP driver.
+If the PPP driver was included in your kernel configuration when your
+kernel was built, then you only need to install the user-level
+programs.  Otherwise you will need to get the source tree for your
+kernel version, configure it with PPP included, and recompile.  Most
+Linux distribution vendors ship kernels with PPP included in the
+configuration.
+
+The PPP driver can be either compiled into the kernel or compiled as a
+kernel module.  If it is compiled into the kernel, the PPP driver is
+included in the kernel image which is loaded at boot time.  If it is
+compiled as a module, the PPP driver is present in one or more files
+under /lib/modules and is loaded into the kernel when needed.
+
+The 2.2 series kernels contain an older version of the kernel PPP
+driver, one which doesn't support multilink.  If you want multilink,
+you need to run a 2.4 or 2.6 series kernel.  The kernel PPP driver
+was completely rewritten for the 2.4 series kernels to support
+multilink and to allow it to operate over diverse kinds of
+communication medium (the 2.2 driver only operates over serial ports
+and devices which look like serial ports, such as pseudo-ttys).
+
+Under the 2.2 kernels, if PPP is compiled as a module, the PPP driver
+modules should be present in the /lib/modules/`uname -r`/net directory
+(where `uname -r` represents the kernel version number).  The PPP
+driver module itself is called ppp.o, and there will usually be
+compression modules there, ppp_deflate.o and bsd_comp.o, as well as
+slhc.o, which handles TCP/IP header compression.  If the PPP driver is
+compiled into the kernel, the compression code will still be compiled
+as modules, for kernels before 2.2.17pre12.  For 2.2.17pre12 and later,
+if the PPP driver is compiled in, the compression code will also.
+
+Under the 2.4 kernels, there are two PPP modules, ppp_generic.o and
+ppp_async.o, plus the compression modules (ppp_deflate.o, bsd_comp.o
+and slhc.o).  If the PPP generic driver is compiled into the kernel,
+the other four can then be present either as modules or compiled into
+the kernel.  There is a sixth module, ppp_synctty.o, which is used for
+synchronous tty devices such as high-speed WAN adaptors.
+
+
+2.2 User-level programs
+
+If you obtained this package in .rpm or .deb format, you simply follow
+the usual procedure for installing the package.
+
+If you are using the .tar.gz form of this package, then cd into the
+ppp-2.4.5 directory you obtained by unpacking the archive and issue
+the following commands:
+
+$ ./configure
+$ make
+# make install
+
+The `make install' has to be done as root.  This makes and installs
+four programs and their man pages: pppd, chat, pppstats and pppdump.
+If the /etc/ppp configuration directory doesn't exist, the `make
+install' step will create it and install some default configuration
+files.
+
+
+2.3 System setup for 2.4 kernels
+
+Under the 2.4 series kernels, pppd needs to be able to open /dev/ppp,
+character device (108,0).  If you are using udev (as most distributions
+do), the /dev/ppp node should be created by udevd.
+
+Otherwise you may need to create a /dev/ppp device node with the
+commands:
+
+# mknod /dev/ppp c 108 0
+# chmod 600 /dev/ppp
+
+
+2.4 System setup under 2.2 series kernels
+
+Under the 2.2 series kernels, you should add the following to your
+/etc/modules.conf or /etc/conf.modules:
+
+alias tty-ldisc-3	ppp
+alias ppp-compress-21	bsd_comp
+alias ppp-compress-24	ppp_deflate
+alias ppp-compress-26	ppp_deflate
+
+
+3. Getting help with problems
+-----------------------------
+
+If you have problems with your PPP setup, or you just want to ask some
+questions, or better yet if you can help others with their PPP
+questions, then you should join the linux-ppp mailing list.  Send an
+email to majordomo@vger.kernel.org with a line in the body saying
+
+subscribe linux-ppp
+
+To leave the mailing list, send an email to majordomo@vger.kernel.org
+with a line in the body saying
+
+unsubscribe linux-ppp
+
+To send a message to the list, email it to linux-ppp@vger.kernel.org.
+You don't have to be subscribed to send messages to the list.
+
+You can also email me (paulus@samba.org) but I am overloaded with
+email and I can't respond to most messages I get in a timely fashion.
+
+There are also several relevant news groups, such as comp.protocols.ppp,
+comp.os.linux.networking, or comp.os.linux.setup.
+
+
+4. Configuring your dial-out PPP connections
+--------------------------------------------
+
+Some Linux distribution makers include tools in their distributions
+for setting up PPP connections.  For example, for Red Hat Linux and
+derivatives, you should probably use linuxconf or netcfg to set up
+your PPP connections.
+
+The two main windowing environments for Linux, KDE and Gnome, both
+come with GUI utilities for configuring and controlling PPP dial-out
+connections.  They are convenient and relatively easy to configure.
+
+A third alternative is to use a PPP front-end package such as wvdial
+or ezppp.  These also will handle most of the details of talking to
+the modem and setting up the PPP connection for you.
+
+Assuming that you don't want to use any of these tools, you want to
+set up the configuration manually yourself, then read on.  This
+document gives a brief description and example.  More details can be
+found by reading the pppd and chat man pages and the PPP-HOWTO.
+
+We assume that you have a modem that uses the Hayes-compatible AT
+command set connected to an async serial port (e.g. /dev/ttyS0) and
+that you are dialling out to an ISP.  
+
+The trickiest and most variable part of setting up a dial-out PPP
+connection is the part which involves getting the modem to dial and
+then invoking PPP service at the far end.  Generally, once both ends
+are talking PPP the rest is relatively straightforward.
+
+Now in fact pppd doesn't know anything about how to get modems to dial
+or what you have to say to the system at the far end to get it to talk
+PPP.  That's handled by an external program such as chat, specified
+with the connect option to pppd.  Chat takes a series of strings to
+expect from the modem interleaved with a series of strings to send to
+the modem.  See the chat man page for more information.  Here is a
+simple example for connecting to an ISP, assuming that the ISP's
+system starts talking PPP as soon as it answers the phone:
+
+pppd connect 'chat -v "" AT OK ATDT5551212 ~' \
+	/dev/ttyS0 57600 crtscts debug defaultroute
+
+Going through pppd's options in order:
+    connect 'chat ...'  This gives a command to run to contact the
+    PPP server.  Here the supplied 'chat' program is used to dial a
+    remote computer.  The whole command is enclosed in single quotes
+    because pppd expects a one-word argument for the 'connect' option.
+    The options to 'chat' itself are:
+
+         -v            verbose mode; log what we do to syslog
+         ""            don't wait for any prompt, but instead...
+	 AT	       send the string "AT"
+	 OK	       expect the response "OK", then
+         ATDT5551212   dial the modem, then
+         ~             wait for a ~ character, indicating the start
+		       of a PPP frame from the server
+
+    /dev/ttyS0	       specifies which serial port the modem is connected to
+    57600	       specifies the baud rate to use
+    crtscts	       use hardware flow control using the RTS & CTS signals
+    debug	       log the PPP negotiation with syslog
+    defaultroute       add default network route via the PPP link
+
+Pppd will write error messages and debugging logs to the syslogd
+daemon using the facility name "daemon".  These messages may already
+be logged to the console or to a file like /var/log/messages; consult
+your /etc/syslog.conf file to see.  If you want to make all pppd
+messages go to a file such as /var/log/ppp-debug, add the line
+
+daemon.*					/var/log/ppp-debug
+        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+           This is one or more tabs. Do not use spaces.
+
+to syslog.conf; make sure to put one or more TAB characters (not
+spaces!) between the two fields.  Then you need to create an empty
+/var/log/ppp-debug file with a command such as
+
+	touch /var/log/ppp-debug
+
+and then restart syslogd, usually by sending it a SIGHUP signal with a
+command like this:
+
+	killall -HUP syslogd
+
+
+4.1 Is the link up?
+
+The main way to tell if your PPP link is up and operational is the
+ifconfig ("interface configuration") command.  Type
+
+	/sbin/ifconfig
+
+at a shell prompt.  It should print a list of interfaces including one
+like this example:
+
+ppp0      Link encap Point-to-Point Protocol
+          inet addr 192.76.32.3  P-t-P 129.67.1.165  Mask 255.255.255.0
+          UP POINTOPOINT RUNNING  MTU 1500  Metric 1
+          RX packets 33 errors 0 dropped 0 overrun 0
+          TX packets 42 errors 0 dropped 0 overrun 0
+
+Assuming that ifconfig shows the ppp network interface, you can test
+the link using the ping command like this:
+
+	/sbin/ping -c 3 129.67.1.165
+
+where the address you give is the address shown as the P-t-P address
+in the ifconfig output.  If the link is operating correctly, you
+should see output like this:
+
+  PING 129.67.1.165 (129.67.1.165): 56 data bytes
+  64 bytes from 129.67.1.165: icmp_seq=0 ttl=255 time=268 ms
+  64 bytes from 129.67.1.165: icmp_seq=1 ttl=255 time=247 ms
+  64 bytes from 129.67.1.165: icmp_seq=2 ttl=255 time=266 ms
+  --- 129.67.1.165 ping statistics ---
+  3 packets transmitted, 3 packets received, 0% packet loss
+  round-trip min/avg/max = 247/260/268 ms
+
diff --git a/ap/app/pppd/README.pppoe b/ap/app/pppd/README.pppoe
new file mode 100644
index 0000000..5284e4d
--- /dev/null
+++ b/ap/app/pppd/README.pppoe
@@ -0,0 +1,93 @@
+		PPPoE Support
+		-------------
+
+		Michal Ostrowski
+		8 August 2001
+
+		for ppp-2.4.2
+		Updated for ppp-2.4.5 by Paul Mackerras, Sep 08
+
+1. Introduction
+---------------
+
+This document describes the support for PPP over Ethernet (PPPoE)
+included with this package.  It is assumed that the reader is
+familiar with Linux PPP (as it pertains to tty/modem-based
+connections).  In particular, users of PPP in the Linux 2.2 series
+kernels should ensure they are familiar with the changes to the PPP
+implementation in the 2.4 series kernels before attempting to use
+PPPoE features.
+
+If you are not familiar with PPP, I recommend looking at other
+packages which include end-user configuration tools, such as Roaring
+Penguin (http://www.roaringpenguin.com/pppoe).
+
+PPPoE is a protocol typically used by *DSL providers to manage IP
+addresses and authenticate users.  Essentially, PPPoE provides for a
+PPP connection to be established not over a physical serial-line or
+modem, but over a logical connection between two unique MAC-addresses
+on an ethernet network.  Once the PPPoE layer discovers the end-points
+to be used in the link and negotiates it, frames may be sent to and
+received from the PPPoE layer just as if the link was a serial line
+(or that is how it's supposed to be).
+
+With this in mind, the goal of the implementation of PPPoE support in
+Linux is to allow users to simply specify that the device they intend
+to use for the PPP connection is an ethernet device (i.e. "eth0") and
+the rest of the system should function as usual.
+
+2. Using PPPoE
+--------------
+
+This section is a quick guide for getting PPPoE working, to allow one
+to connect to their ISP who is providing PPPoE based services.
+
+1.  Enable "Prompt for development and/or incomplete code/drivers" and
+    "PPP over Ethernet" in your kernel configuration.  Most distributions
+    will include the kernel PPPoE module by default.
+
+2.  Compile and install your kernel.
+
+3.  Install the ppp package.
+
+4.  Add the following line to /etc/ppp/options:
+
+    plugin rp-pppoe.so
+
+    The effect of this line is simply to make "eth0", "eth1",
+    ....,"ethx" all valid device names for pppd (just like ttyS0,
+    ttyS1).
+
+5.  Add the necessary authentication options to your pppd
+    configuration (i.e. PAP/CHAP information).  If you wish to
+    maintain seperate configurations for different devices you may
+    place configuration options in device-specific configuration
+    files: /etc/ppp/options.devname (devname=ttyS0, ttyS1, eth0, eth1
+    or any other valid device name).
+
+6.  Invoke pppd with the appropriate device name: e.g. "pppd eth0"
+
+
+Do not include any compression or flow control options in your PPPoE
+configuration.  They will be ignored.
+
+Again, here it is assumed that the reader is familiar with the general
+process of configuring PPP.  The steps outlined here refer only to the
+steps and configuration options which are PPPoE specific, and it is
+assumed that the reader will also configure other aspects of the system
+(e.g. PAP authentication parameters).
+
+3.  Advanced Functionality
+--------------------------
+
+For more advanced functionality (such as providing PPPoE services) and
+user configuration tools, look to the Roaring Penguin PPPoE software
+package (http://www.roaringpenguin.com/pppoe).
+
+4.  Credits
+-----------
+
+The PPPoE plugin included in this package is a component of the
+Roaring Penguin PPPoE package, included in this package courtesy of
+Roaring Penguin Software. (http://www.roaringpenguin.com).
+
diff --git a/ap/app/pppd/README.pppol2tp b/ap/app/pppd/README.pppol2tp
new file mode 100644
index 0000000..f34e89f
--- /dev/null
+++ b/ap/app/pppd/README.pppol2tp
@@ -0,0 +1,66 @@
+PPPoL2TP plugin
+===============
+
+The pppol2tp plugin lets pppd use the Linux kernel driver pppol2tp.ko
+to pass PPP frames in L2TP tunnels. The driver was integrated into the
+kernel in the 2.6.23 release. For kernels before 2.6.23, an
+out-of-tree kernel module is available from the pppol2tp-kmod package
+in the OpenL2TP project.
+
+Note that pppd receives only PPP control frames over the PPPoL2TP
+socket; data frames are handled entirely by the kernel.
+
+The pppol2tp plugin adds extra arguments to pppd and uses the Linux kernel
+PPP-over-L2TP driver to set up each session's data path.
+
+Arguments are:-
+
+pppol2tp <fd>                   - FD for PPPoL2TP socket
+pppol2tp_lns_mode               - PPPoL2TP LNS behavior. Default off.
+pppol2tp_send_seq               - PPPoL2TP enable sequence numbers in
+                                  transmitted data packets. Default off.
+pppol2tp_recv_seq               - PPPoL2TP enforce sequence numbers in
+                                  received data packets. Default off.
+pppol2tp_reorderto <millisecs>  - PPPoL2TP data packet reorder timeout.
+                                  Default 0 (no reordering).
+pppol2tp_debug_mask <mask>      - PPPoL2TP debug mask. Bitwise OR of
+				  1 - verbose debug
+				  2 - control
+				  4 - kernel transport
+				  8 - ppp packet data
+				  Default: 0 (no debug).
+pppol2tp_ifname <ifname>	- Name of PPP network interface visible
+				  to "ifconfig" and "ip link".
+				  Default: "pppN"
+pppol2tp_tunnel_id <id>		- L2TP tunnel_id tunneling this PPP
+				  session.
+pppol2tp_session_id <id>	- L2TP session_id of this PPP session.
+				  The tunnel_id/session_id pair is used
+				  when sending event messages to openl2tpd.
+
+pppd will typically be started by an L2TP daemon for each L2TP sesion,
+supplying one or more of the above arguments as required. The pppd
+user will usually have no visibility of these arguments.
+
+Two hooks are exported by this plugin.
+
+void (*pppol2tp_send_accm_hook)(int tunnel_id, int session_id,
+     uint32_t send_accm, uint32_t recv_accm);
+void (*pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up);
+
+Credits
+=======
+
+This plugin was developed by Katalix Systems as part of the OpenL2TP
+project, http://openl2tp.sourceforge.net. OpenL2TP is a full-featured
+L2TP client-server, suitable for use as an enterprise L2TP VPN server
+or a VPN client.
+
+Please copy problems to the OpenL2TP mailing list:
+openl2tp-users@lists.sourceforge.net.
+
+Maintained by:
+	James Chapman
+	jchapman@katalix.com
+	Katalix Systems Ltd
+	http://www.katalix.com
diff --git a/ap/app/pppd/README.pwfd b/ap/app/pppd/README.pwfd
new file mode 100644
index 0000000..f6c5d9b
--- /dev/null
+++ b/ap/app/pppd/README.pwfd
@@ -0,0 +1,174 @@
+
+	Support to pass the password via a pipe to the pppd
+	---------------------------------------------------
+
+	Arvin Schnell <arvin@suse.de>
+	2002-02-08
+
+
+1. Introduction
+---------------
+
+Normally programs like wvdial or kppp read the online password from their
+config file and store them in the pap- and chap-secrets before they start the
+pppd and remove them afterwards. Sure they need special privileges to do so.
+
+The passwordfd feature offers a simpler and more secure solution. The program
+that starts the pppd opens a pipe and writes the password into it. The pppd
+simply reads the password from that pipe.
+
+This methods is used for quite a while on SuSE Linux by the programs wvdial,
+kppp and smpppd.
+
+
+2. Example
+----------
+
+Here is a short C program that uses the passwordfd feature. It starts the pppd
+to buildup a pppoe connection.
+
+
+--snip--
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <paths.h>
+
+#ifndef _PATH_PPPD
+#define _PATH_PPPD "/usr/sbin/pppd"
+#endif
+
+
+// Of course these values can be read from a configuration file or
+// entered in a graphical dialog.
+char *device = "eth0";
+char *username = "1122334455661122334455660001@t-online.de";
+char *password = "hello";
+
+pid_t pid = 0;
+
+
+void
+sigproc (int src)
+{
+    fprintf (stderr, "Sending signal %d to pid %d\n", src, pid);
+    kill (pid, src);
+    exit (EXIT_SUCCESS);
+}
+
+
+void
+sigchild (int src)
+{
+    fprintf (stderr, "Daemon died\n");
+    exit (EXIT_SUCCESS);
+}
+
+
+int
+start_pppd ()
+{
+    signal (SIGINT, &sigproc);
+    signal (SIGTERM, &sigproc);
+    signal (SIGCHLD, &sigchild);
+
+    pid = fork ();
+    if (pid < 0) {
+	fprintf (stderr, "unable to fork() for pppd: %m\n");
+	return 0;
+    }
+
+    if (pid == 0) {
+
+	int i, pppd_argc = 0;
+	char *pppd_argv[20];
+	char buffer[32] = "";
+	int pppd_passwdfd[2];
+
+	for (i = 0; i < 20; i++)
+	    pppd_argv[i] = NULL;
+
+	pppd_argv[pppd_argc++] = "pppd";
+
+	pppd_argv[pppd_argc++] = "call";
+	pppd_argv[pppd_argc++] = "pwfd-test";
+
+	// The device must be after the call, since the call loads the plugin.
+	pppd_argv[pppd_argc++] = device;
+
+	pppd_argv[pppd_argc++] = "user";
+	pppd_argv[pppd_argc++] = username;
+
+	// Open a pipe to pass the password to pppd.
+	if (pipe (pppd_passwdfd) == -1) {
+	    fprintf (stderr, "pipe failed: %m\n");
+	    exit (EXIT_FAILURE);
+	}
+
+	// Of course this only works it the password is shorter
+	// than the pipe buffer. Otherwise you have to fork to
+	// prevent that your main program blocks.
+	write (pppd_passwdfd[1], password, strlen (password));
+	close (pppd_passwdfd[1]);
+
+	// Tell the pppd to read the password from the fd.
+	pppd_argv[pppd_argc++] = "passwordfd";
+	snprintf (buffer, 32, "%d", pppd_passwdfd[0]);
+	pppd_argv[pppd_argc++] = buffer;
+
+	if (execv (_PATH_PPPD, (char **) pppd_argv) < 0) {
+	    fprintf (stderr, "cannot execl %s: %m\n", _PATH_PPPD);
+	    exit (EXIT_FAILURE);
+	}
+    }
+
+    pause ();
+
+    return 1;
+}
+
+
+int
+main (int argc, char **argv)
+{
+    if (start_pppd ())
+	exit (EXIT_SUCCESS);
+
+    exit (EXIT_FAILURE);
+}
+
+---snip---
+
+
+Copy this file to /etc/ppp/peers/pwfd-test. The plugins can't be loaded on the
+command line (unless you are root) since the plugin option is privileged.
+
+
+---snip---
+
+#
+# PPPoE plugin for kernel 2.4
+#
+plugin pppoe.so
+
+#
+# This plugin enables us to pipe the password to pppd, thus we don't have
+# to fiddle with pap-secrets and chap-secrets. The user is also passed
+# on the command line.
+#
+plugin passwordfd.so
+
+noauth
+usepeerdns
+defaultroute
+hide-password
+nodetach
+nopcomp
+novjccomp
+noccp
+
+---snip---
+
diff --git a/ap/app/pppd/README.sol2 b/ap/app/pppd/README.sol2
new file mode 100644
index 0000000..7421664
--- /dev/null
+++ b/ap/app/pppd/README.sol2
@@ -0,0 +1,260 @@
+This file describes the installation process for ppp-2.4 on systems
+running Solaris.  The Solaris and SVR4 ports share a lot of code but
+are not identical.  The STREAMS kernel modules and driver for Solaris
+are in the solaris directory (and use some code from the modules
+directory).
+
+NOTE: Although the kernel driver and modules have been designed to
+operate correctly on SMP systems, they have not been extensively
+tested on SMP machines.  Some users of SMP Solaris x86 systems have
+reported system problems apparently linked to the use of previous
+versions of this software.  I believe these problems have been fixed.
+
+
+Installation.
+*************
+
+1. Run the configure script and make the user-level programs and the
+   kernel modules.
+
+	./configure
+	make
+
+    The configure script will automatically find Sun's cc if it's in
+    the standard location (/opt/SUNWspro/bin/cc).  If you do not have
+    Sun's WorkShop compiler, configure will attempt to use 'gcc'.  If
+    this is found and you have a 64 bit kernel, it will check that gcc
+    accepts the "-m64" option, which is required to build kernel
+    modules.
+
+    You should not have to edit the Makefiles for most ordinary cases.
+
+2. Install the programs and kernel modules: as root, do
+
+	make install
+
+    This installs pppd, chat and pppstats in /usr/local/bin and the
+    kernel modules in /kernel/drv and /kernel/strmod, and creates the
+    /etc/ppp directory and populates it with default configuration
+    files.  You can change the installation directories by editing
+    solaris/Makedefs.  If you have a 64 bit kernel, the 64-bit drivers
+    are installed in /kernel/drv/sparcv9 and /kernel/strmod/sparcv9.
+
+    If your system normally has only one network interface at boot
+    time, the default Solaris system startup scripts will disable IP
+    forwarding in the IP kernel module.  This will prevent the remote
+    machine from using the local machine as a gateway to access other
+    hosts.  The solution is to create an /etc/ppp/ip-up script
+    containing something like this:
+
+	#!/bin/sh
+	/usr/sbin/ndd -set /dev/ip ip_forwarding 1
+
+    See the man page for ip(7p) for details.
+
+Integrated pppd
+***************
+
+  Solaris 8 07/01 (Update 5) and later have an integrated version of
+  pppd, known as "Solaris PPP 4.0," and is based on ppp-2.4.0.  This
+  version comes with the standard Solaris software distribution and is
+  supported by Sun.  It is fully tested in 64-bit and SMP modes, and
+  with bundled and unbundled synchronous drivers.  Solaris 8 10/01
+  (Update 6) and later includes integrated PPPoE client and server
+  support, with kernel-resident data handling.  See pppd(1M).
+
+  The feature is part of the regular full installation, and is
+  provided by these packages:
+
+	SUNWpppd	- 32-bit mode kernel drivers
+	SUNWpppdr	- root-resident /etc/ppp config samples
+	SUNWpppdu	- /usr/bin/pppd itself, plus chat
+	SUNWpppdx	- 64-bit mode kernel drivers
+	SUNWpppdt	- PPPoE support
+	SUNWpppg	- GPL'd optional 'pppdump' and plugins
+	SUNWpppgS	- Source for GPL'd optional features
+
+  Use the open source version of pppd if you wish to recompile to add
+  new features or to experiment with the code.  Production systems,
+  however, should run the Sun-supplied version, if at all possible.
+
+  You can run both versions on a single system if you wish.  The
+  Solaris PPP 4.0 interfaces are named "spppN," while this open source
+  version names its interfaces as "pppN".  The STREAMS modules are
+  similarly separated.  The Sun-supplied pppd lives in /usr/bin/pppd,
+  while the open source version installs (by default) in
+  /usr/local/bin/pppd.
+
+Dynamic STREAMS Re-Plumbing Support.
+************************************
+
+  Solaris 8 (and later) includes dynamic re-plumbing support.  With
+  this feature, modules below ip can be inserted, or removed, without
+  having the ip stream be unplumbed, and re-plumbed again.  All state
+  in ip for the interface will be preserved as modules are added or
+  removed.  Users can install (or upgrade) modules such as firewall,
+  bandwidth manager, cache manager, tunneling, etc., without shutting
+  the interface down.
+
+  To support this, ppp driver now uses /dev/udp instead of /dev/ip for
+  the ip stream. The interface stream (where ip module pushed on top
+  of ppp) is then I_PLINK'ed below the ip stream. /dev/udp is used
+  because STREAMS will not let a driver be PLINK'ed under itself, and
+  /dev/ip is typically the driver at the bottom of the tunneling
+  interfaces stream.  The mux ids of the ip streams are then added
+  using SIOCSxIFMUXID ioctl.
+
+  Users will be able to see the modules on the interface stream by,
+  for example:
+
+    pikapon# ifconfig ppp modlist
+    0 ip
+    1 ppp
+
+  Or arbitrarily if bandwidth manager and firewall modules are installed:
+
+    pikapon# ifconfig hme0 modlist
+    0 arp
+    1 ip
+    2 ipqos
+    3 firewall
+    4 hme
+
+Snoop Support.
+**************
+
+  This version includes support for /usr/sbin/snoop.  Tests have been
+  done on Solaris 7 through 9. Only IPv4 and IPv6 packets will be sent
+  up to stream(s) marked as promiscuous (i.e., those used by snoop).
+
+  Users will be able to see the packets on the ppp interface by, for
+  example:
+
+    snoop -d ppp0
+
+  See the man page for snoop(1M) for details.
+
+IPv6 Support.
+*************
+
+  This is for Solaris 8 and later.
+
+  This version has been tested under Solaris 8 and 9 running IPv6.
+  Interoperability testing has only been done between Solaris machines
+  in terms of the IPV6 NCP.  An additional command line option for the
+  pppd daemon has been added: ipv6cp-use-persistent.
+
+  By default, compilation for IPv6 support is not enabled.  Uncomment
+  the necessary lines in pppd/Makefile.sol2 to enable it.  Once done,
+  the quickest way to get IPv6 running is to add the following
+  somewhere in the command line option:
+
+	+ipv6 ipv6cp-use-persistent
+
+  The persistent id for the link-local address was added to conform to
+  RFC 2472; such that if there's an EUI-48 available, use that to make
+  up the EUI-64.  As of now, the Solaris implementation extracts the
+  EUI-48 id from the Ethernet's MAC address (the ethernet interface
+  needs to be up).  Future work might support other ways of obtaining
+  a unique yet persistent id, such as EEPROM serial numbers, etc.
+
+  There need not be any up/down scripts for ipv6,
+  e.g. /etc/ppp/ipv6-up or /etc/ppp/ipv6-down, to trigger IPv6
+  neighbor discovery for auto configuration and routing.  The in.ndpd
+  daemon will perform all of the necessary jobs in the
+  background. /etc/inet/ndpd.conf can be further customized to enable
+  the machine as an IPv6 router. See the man page for in.ndpd(1M) and
+  ndpd.conf(4) for details.
+
+  Below is a sample output of "ifconfig -a" with persistent link-local
+  address.  Note the UNNUMBERED flag is set because hme0 and ppp0 both
+  have identical link-local IPv6 addresses:
+
+lo0: flags=1000849<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 through 9 in both 32-
+  and 64-bit environments (Ultra class machines).  Installing the
+  package by executing "make install" will result in additional files
+  residing in /kernel/drv/sparcv9 and /kernel/strmod/sparcv9
+  subdirectories.
+
+  64-bit modules and driver have been compiled and tested using Sun's
+  cc and gcc.
+
+Synchronous Serial Support.
+***************************
+
+  This version has working but limited support for the on-board
+  synchronous HDLC interfaces.  It has been tested with the
+  /dev/se_hdlc, /dev/zsh, HSI/S, and HSI/P drivers.  Synchronous mode
+  was tested with a Cisco router.
+
+  The ppp daemon does not directly support controlling the serial
+  interface.  It relies on the /usr/sbin/syncinit command to
+  initialize HDLC mode and clocking.
+
+  There is a confirmed bug with NRZ/NRZI mode in the /dev/se_hdlc
+  driver, and Solaris patch 104596-11 is needed to correct it.
+  (However this patch seems to introduce other serial problems.  If
+  you don't apply the patch, the workaround is to change the nrzi mode
+  to yes or no, whichever works.)
+
+  How to start pppd with synchronous support:
+
+	#!/bin/sh
+
+	local=1.1.1.1   # your ip address here
+	baud=38400	# needed, but ignored by serial driver
+
+	# Change to the correct serial driver/port
+	#dev=/dev/zsh0
+	dev=/dev/se_hdlc0
+ 
+	# Change the driver, nrzi mode, speed and clocking to match
+	# your setup.
+	# This configuration is for external clocking from the DCE
+	connect="syncinit se_hdlc0 nrzi=no speed=64000 txc=rxc rxc=rxc"
+ 
+	/usr/sbin/pppd $dev sync $baud novj noauth $local: connect "$connect"
+
+  Sample Cisco router config excerpt:
+
+	!
+	! Cisco router setup as DCE with RS-232 DCE cable
+	! 
+	!         
+	interface Serial0
+	 ip address 1.1.1.2 255.255.255.0
+	 encapsulation ppp
+	 clockrate 64000
+	 no nrzi-encoding
+	 no shutdown
+	!         
diff --git a/ap/app/pppd/SETUP b/ap/app/pppd/SETUP
new file mode 100644
index 0000000..d52b869
--- /dev/null
+++ b/ap/app/pppd/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:
+
+ttyS0		# modem is connected to /dev/ttyS0
+38400		# run the serial port at 38400 baud
+crtscts		# use hardware flow control
+noauth		# don't require the ISP to authenticate itself
+defaultroute	# use the ISP as our default route
+connect '/usr/sbin/chat -v -f /etc/ppp/chat-isp'
+
+If there are any other pppd options that should apply when calling
+this ISP, they can also be placed in this file.
+
+The /etc/ppp/chat-isp file named in the last line contains the script
+for chat(8) to use to dial the ISP and go through any username/
+password authentication required before PPP service starts.  Here is
+an example (for dialling an Annex terminal server):
+
+ABORT "NO CARRIER"
+ABORT "NO DIALTONE"
+ABORT "ERROR"
+ABORT "NO ANSWER"
+ABORT "BUSY"
+ABORT "Username/Password Incorrect"
+"" "at"
+OK "at&d2&c1"
+OK "atdt2479381"
+"name:" "^Uusername"
+"word:" "\qpassword"
+"annex" "ppp"
+"Switching to PPP-ppp-Switching to PPP"
+
+See the chat(8) man page for details of the script.  If you are not
+sure how the initial dialog with your ISP will go, you could use
+a terminal emulator such as kermit or minicom to go through the
+process manually.
+
+If your ISP requires PAP or CHAP authentication, you will have to
+create a line in /etc/ppp/pap-secrets or /etc/ppp/chap-secrets like
+this:
+
+myhostname	*	"password"
+
+(Replace myhostname with the hostname of your machine.)
+
+At this point, you can initiate the link with the command:
+
+/usr/sbin/pppd call isp
+
+(N.B.: pppd might be installed in a different directory on some
+systems).
+
+This will return to the shell prompt immediately, as pppd will detach
+itself from its controlling terminal.  (If you don't want it to do
+this, use the "nodetach" option.)
+
+Pppd will log messages describing the progress of the connection and
+any errors using the syslog facility (see the syslogd(8) and
+syslog.conf(5) man pages).  Pppd issues messages using syslog facility
+daemon (or local2 if it has been compiled with debugging enabled);
+chat uses facility local2.  It is often useful to see messages of
+priority notice or higher on the console.  To see these, find the line
+in /etc/syslog.conf which has /dev/console on the right-hand side, and
+add `daemon.notice' on the left.  This line should end up something
+like this:
+
+*.err;kern.debug;daemon,local2,auth.notice;mail.crit	/dev/console
+
+If you want to see more messages from pppd, request messages of
+priority info or higher for facility daemon, like this:
+
+*.err;kern.debug;daemon.info;local2,auth.notice;mail.crit  /dev/console
+
+It is also useful to add a line like this:
+
+daemon,local2.debug		/etc/ppp/ppp-log
+
+If you do this, you will need to create an empty /etc/ppp/ppp-log
+file.
+
+After modifying syslog.conf, you will then need to send a HUP signal
+to syslogd (or reboot).
+
+When you wish terminate the PPP link, you should send a TERM or INTR
+signal to pppd.  Pppd writes its process ID to a file called
+ppp<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/ap/app/pppd/chat/Makefile.linux b/ap/app/pppd/chat/Makefile.linux
new file mode 100644
index 0000000..484aa6b
--- /dev/null
+++ b/ap/app/pppd/chat/Makefile.linux
@@ -0,0 +1,40 @@
+#	$Id: Makefile.linux,v 1.6 2007-10-31 03:38:05 davidm Exp $
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+
+CDEF1=	-DTERMIOS			# Use the termios structure
+CDEF2=	-DSIGTYPE=void			# Standard definition
+CDEF3=	-UNO_SLEEP			# Use the usleep function
+CDEF4=	-DFNDELAY=O_NDELAY		# Old name value
+CDEFS=	$(CDEF1) $(CDEF2) $(CDEF3) $(CDEF4)
+
+#COPTS=	-O2 -g -pipe
+CFLAGS+=$(COPTS) $(CDEFS)
+
+ifdef CONFIG_USER_CHAT_CHAT_NETWORK
+CFLAGS += -DENABLE_NETWORK_SUPPORT=0
+endif
+
+INSTALL= install
+
+all:	chat
+
+chat:	chat.o
+	$(CC) $(LDFLAGS) -o chat chat.o $(LDLIBS$(LDLIBS_$@))
+
+chat.o:	chat.c
+	$(CC) -c $(CFLAGS) -o chat.o chat.c
+
+install: chat
+	mkdir -p $(BINDIR) $(MANDIR)
+	$(INSTALL) -s -c chat $(BINDIR)
+	$(INSTALL) -c -m 644 chat.8 $(MANDIR)
+
+romfs:
+	$(ROMFSINST) chat /bin/chat
+	$(ROMFSINST) initchat /etc_ro/initchat
+
+clean:
+	rm -f chat.o chat *~
diff --git a/ap/app/pppd/chat/Makefile.sol2 b/ap/app/pppd/chat/Makefile.sol2
new file mode 100644
index 0000000..10d3314
--- /dev/null
+++ b/ap/app/pppd/chat/Makefile.sol2
@@ -0,0 +1,19 @@
+#
+# Makefile for chat on Solaris 2
+#
+
+include ../Makedefs.com
+
+CFLAGS = $(COPTS) -DNO_USLEEP -DSOL2
+
+all:	chat
+
+chat: chat.o
+	$(CC) -o chat chat.o
+
+install: chat
+	$(INSTALL) -f $(BINDIR) chat
+	$(INSTALL) -m 444 -f $(MANDIR)/man8 chat.8
+
+clean:
+	rm -f *~ *.o chat
diff --git a/ap/app/pppd/chat/chat.8 b/ap/app/pppd/chat/chat.8
new file mode 100644
index 0000000..5cddb07
--- /dev/null
+++ b/ap/app/pppd/chat/chat.8
@@ -0,0 +1,515 @@
+.\" -*- nroff -*-
+.\" manual page [] for chat 1.8
+.\" $Id: chat.8,v 1.2 2007-06-08 04:02:37 gerg Exp $
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH CHAT 8 "22 May 1999" "Chat Version 1.22"
+.SH NAME
+chat \- Automated conversational script with a modem
+.SH SYNOPSIS
+.B chat
+[
+.I options
+]
+.I script
+.SH DESCRIPTION
+.LP
+The \fIchat\fR program defines a conversational exchange between the
+computer and the modem. Its primary purpose is to establish the
+connection between the Point-to-Point Protocol Daemon (\fIpppd\fR) and
+the remote's \fIpppd\fR process.
+.SH OPTIONS
+.TP
+.B \-f \fI<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/ap/app/pppd/chat/chat.c b/ap/app/pppd/chat/chat.c
new file mode 100644
index 0000000..a399892
--- /dev/null
+++ b/ap/app/pppd/chat/chat.c
@@ -0,0 +1,1998 @@
+/*
+ *	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.
+ *
+ * -----------------
+ *  21st March 2003  - added network/telnet code option <davidm@snapgear.com>
+ *                   - added -R (truncated/created report file option)
+ *
+ *	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: chat.c,v 1.8 2007-10-31 03:38:05 davidm Exp $";
+#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;
+char *device      = 0;
+//#define syslog(...) 
+#ifdef ENABLE_NETWORK_SUPPORT
+
+int network       = 0;
+char *net_host    = NULL;
+int net_port      = 23; /* telnet by default */
+
+#define IAC  0xff
+#define DONT 0xfe
+#define DO   0xfd
+#define WONT 0xfc
+#define WILL 0xfb
+
+extern int net_get_char(unsigned char *cp);
+extern void net_open();
+
+#endif /* ENABLE_NETWORK_SUPPORT */
+
+int have_tty_parameters = 0;
+
+#ifdef TERMIO
+#define term_parms struct termio
+#define get_term_param(param) ioctl(0, TCGETA, param)
+#define set_term_param(param) ioctl(0, TCSETA, param)
+struct termio saved_tty_parameters;
+#endif
+
+#ifdef TERMIOS
+#define term_parms struct termios
+#define get_term_param(param) tcgetattr(0, param)
+#define set_term_param(param) tcsetattr(0, TCSANOW, param)
+struct termios saved_tty_parameters;
+#endif
+
+char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
+	fail_buffer[50];
+int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
+int clear_abort_next = 0;
+
+char *report_string[MAX_REPORTS] ;
+char  report_buffer[256] ;
+int n_reports = 0, report_next = 0, report_gathering = 0 ; 
+int clear_report_next = 0;
+
+int say_next = 0, hup_next = 0;
+
+void *dup_mem __P((void *b, size_t c));
+void *copy_of __P((char *s));
+char *grow __P((char *s, char **p, size_t len));
+void usage __P((void));
+void msgf __P((const char *fmt, ...));
+void fatal __P((int code, const char *fmt, ...));
+SIGTYPE sigalrm __P((int signo));
+SIGTYPE sigint __P((int signo));
+SIGTYPE sigterm __P((int signo));
+SIGTYPE sighup __P((int signo));
+void unalarm __P((void));
+void init __P((void));
+void set_tty_parameters __P((void));
+void echo_stderr __P((int));
+void break_sequence __P((void));
+void terminate __P((int status));
+void do_file __P((char *chat_file));
+int  get_string __P((register char *string));
+int  put_string __P((register char *s));
+int  write_char __P((int c));
+int  put_char __P((int c));
+int  get_char __P((void));
+void chat_send __P((register char *s));
+char *character __P((int c));
+void chat_expect __P((register char *s));
+char *clean __P((register char *s, int sending));
+void break_sequence __P((void));
+void terminate __P((int status));
+void pack_array __P((char **array, int end));
+char *expect_strtok __P((char *, char *));
+int vfmtmsg __P((char *, int, const char *, va_list));	/* vsprintf++ */
+
+int main __P((int, char *[]));
+
+void *dup_mem(b, c)
+void *b;
+size_t c;
+{
+    void *ans = malloc (c);
+    if (!ans)
+	fatal(2, "memory error!");
+
+    memcpy (ans, b, c);
+    return ans;
+}
+
+void *copy_of (s)
+char *s;
+{
+    return dup_mem (s, strlen (s) + 1);
+}
+
+/* grow a char buffer and keep a pointer offset */
+char *grow(s, p, len)
+char *s;
+char **p;
+size_t len;
+{
+    size_t l = *p - s;		/* save p as distance into s */
+
+    s = realloc(s, len);
+    if (!s)
+	fatal(2, "memory error!");
+    *p = s + l;			/* restore p */
+    return s;
+}
+
+/*
+ * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \
+ * [ -r report-file ] [-d device] \
+ *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
+ *
+ *	Perform a UUCP-dialer-like chat script on stdin and stdout.
+ *	(or the given device if specified)
+ */
+int
+main(argc, argv)
+     int argc;
+     char **argv;
+{
+    int option;
+    char *arg;
+    program_name = *argv;
+#ifndef EMBED
+//aaaaaaaa
+    //tzset();
+#endif
+
+    while ((option = OPTION(argc, argv)) != 0) {
+	switch (option) {
+	case 'e':
+	    ++echo;
+	    break;
+
+	case 'E':
+	    ++use_env;
+	    break;
+
+	case 'v':
+	    ++verbose;
+	    break;
+
+#ifdef ENABLE_NETWORK_SUPPORT
+	case 'h':
+	    ++network;
+	    if ((arg = OPTARG(argc, argv)) != NULL)
+		net_host = copy_of(arg);
+	    else
+		usage();
+	    break;
+
+	case 'p':
+	    if ((arg = OPTARG(argc, argv)) != NULL)
+		net_port = atoi(arg);
+	    else
+		usage();
+	    break;
+#endif
+
+	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 'd':
+	    if ((arg = OPTARG(argc, argv)) != NULL)
+	    	{
+	    	//syslog(LOG_ERR,"device is %s", arg);
+		device = arg;
+		}
+	    else
+		usage();
+	    break;
+
+	case 'r':
+	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, option == 'r' ? "a" : "w");
+		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
+    }
+
+    if (device != 0) {
+	int fd = open(device, O_RDWR, S_IRUSR | S_IWUSR);
+	if (fd == -1)
+	    usage();
+	dup2(fd, 0);
+	dup2(fd, 1);
+    }
+
+    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;
+	//syslog(LOG_ERR,"do_file");
+    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;
+		//syslog(LOG_ERR," arg is %s", arg);
+		while (*sp != quote) {
+		    if (*sp == '\0')
+			fatal(1, "unterminated quote (line %d)", linect);
+
+		    if (*sp++ == '\\') {
+			if (*sp != '\0')
+			    ++sp;
+		    }
+		}
+	    }
+	    else {
+		arg = sp;
+		//syslog(LOG_ERR," arg is %s", arg);
+		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
+		    ++sp;
+	    }
+
+	    if (*sp != '\0')
+		*sp++ = '\0';
+
+	    if (sendflg)
+	    	{
+	    	//syslog(LOG_ERR,"chat_send arg is %s", arg);
+		chat_send (arg);
+	    	}
+	    else
+	    	{
+	    	//syslog(LOG_ERR,"chat_expect arg is %s", arg);
+		chat_expect (arg);
+	    	}
+	    sendflg = !sendflg;
+	}
+    }
+	//syslog(LOG_ERR,"close file done");
+    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] [-R report-file]\n"
+#ifdef ENABLE_NETWORK_SUPPORT
+"     [-h hostname] [-p tcp-port]\n"
+#endif /* ENABLE_NETWORK_SUPPORT */
+"     [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name);
+    exit(1);
+}
+
+char line[1024];
+
+/*
+ * Send a message to syslog and/or stderr.
+ */
+void msgf __V((const char *fmt, ...))
+{
+    va_list args;
+
+#ifdef __STDC__
+    va_start(args, fmt);
+#else
+    char *fmt;
+    va_start(args);
+    fmt = va_arg(args, char *);
+#endif
+
+    vfmtmsg(line, sizeof(line), fmt, args);
+    if (to_log)
+	//syslog(LOG_INFO, "%s", line);
+    if (to_stderr)
+	fprintf(stderr, "%s\n", line);
+}
+
+/*
+ *	Print an error message and terminate.
+ */
+
+void fatal __V((int code, const char *fmt, ...))
+{
+    va_list args;
+
+#ifdef __STDC__
+    va_start(args, fmt);
+#else
+    int code;
+    char *fmt;
+    va_start(args);
+    code = va_arg(args, int);
+    fmt = va_arg(args, char *);
+#endif
+
+    vfmtmsg(line, sizeof(line), fmt, args);
+    if (to_log)
+	//syslog(LOG_ERR, "%s", line);
+    if (to_stderr)
+	fprintf(stderr, "%s\n", line);
+    terminate(code);
+}
+
+int alarmed = 0;
+
+SIGTYPE sigalrm(signo)
+int signo;
+{
+    int flags;
+
+    alarm(1);
+    alarmed = 1;		/* Reset alarm to avoid race window */
+    signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
+
+    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+	fatal(2, "Can't get file mode flags on stdin: %m");
+
+    if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
+	fatal(2, "Can't set file mode flags on stdin: %m");
+
+    if (verbose)
+	msgf("alarm");
+}
+
+void unalarm()
+{
+    int flags;
+
+    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+	fatal(2, "Can't get file mode flags on stdin: %m");
+
+    if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
+	fatal(2, "Can't set file mode flags on stdin: %m");
+}
+
+SIGTYPE sigint(signo)
+int signo;
+{
+    fatal(2, "SIGINT");
+}
+
+SIGTYPE sigterm(signo)
+int signo;
+{
+    fatal(2, "SIGTERM");
+}
+
+SIGTYPE sighup(signo)
+int signo;
+{
+    fatal(2, "SIGHUP");
+}
+
+void init()
+{
+    signal(SIGINT, sigint);
+    signal(SIGTERM, sigterm);
+    signal(SIGHUP, sighup);
+
+#ifdef ENABLE_NETWORK_SUPPORT
+    if (network)
+	net_open();
+    else
+#endif
+    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)
+#ifndef EMBED
+	fatal(2, "Can't get terminal parameters: %m");
+#else
+	syslog(LOG_ERR, "Can't get terminal parameters: %m");
+#endif
+
+    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)
+#ifndef EMBED
+	fatal(2, "Can't set terminal parameters: %m");
+#else
+	syslog(LOG_ERR, "Can't set terminal parameters: %m");
+#endif
+#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 &&
+    	report_file != (char *) 0 && report_fp != (FILE *) NULL) {
+	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)
+#ifndef EMBED
+	    fatal(2, "Can't restore terminal parameters: %m");
+#else
+	    syslog(LOG_ERR, "Can't restore terminal parameters: %m");
+#endif
+    }
+#endif
+	syslog(LOG_ERR,"terminate done");
+    exit(status);
+}
+
+/*
+ *	'Clean up' this string.
+ */
+char *clean(s, sending)
+register char *s;
+int sending;  /* set to 1 when sending (putting) this string. */
+{
+    char cur_chr;
+    char *s1, *p, *phchar;
+    int add_return = sending;
+    size_t len = strlen(s) + 3;		/* see len comments below */
+
+#define isoctal(chr)	(((chr) >= '0') && ((chr) <= '7'))
+#define isalnumx(chr)	((((chr) >= '0') && ((chr) <= '9')) \
+			 || (((chr) >= 'a') && ((chr) <= 'z')) \
+			 || (((chr) >= 'A') && ((chr) <= 'Z')) \
+			 || (chr) == '_')
+
+    p = s1 = malloc(len);
+    if (!p)
+	fatal(2, "memory error!");
+    while (*s) {
+	cur_chr = *s++;
+	if (cur_chr == '^') {
+	    cur_chr = *s++;
+	    if (cur_chr == '\0') {
+		*p++ = '^';
+		break;
+	    }
+	    cur_chr &= 0x1F;
+	    if (cur_chr != 0) {
+		*p++ = cur_chr;
+	    }
+	    continue;
+	}
+
+	if (use_env && cur_chr == '$') {		/* ARI */
+	    char c;
+
+	    phchar = s;
+	    while (isalnumx(*s))
+		s++;
+	    c = *s;		/* save */
+	    *s = '\0';
+	    phchar = getenv(phchar);
+	    *s = c;		/* restore */
+	    if (phchar) {
+		len += strlen(phchar);
+		s1 = grow(s1, &p, len);
+		while (*phchar)
+		    *p++ = *phchar++;
+	    }
+	    continue;
+	}
+
+	if (cur_chr != '\\') {
+	    *p++ = cur_chr;
+	    continue;
+	}
+
+	cur_chr = *s++;
+	if (cur_chr == '\0') {
+	    if (sending) {
+		*p++ = '\\';
+		*p++ = '\\';	/* +1 for len */
+	    }
+	    break;
+	}
+
+	switch (cur_chr) {
+	case 'b':
+	    *p++ = '\b';
+	    break;
+
+	case 'c':
+	    if (sending && *s == '\0')
+		add_return = 0;
+	    else
+		*p++ = cur_chr;
+	    break;
+
+	case '\\':
+	case 'K':
+	case 'p':
+	case 'd':
+	    if (sending)
+		*p++ = '\\';
+	    *p++ = cur_chr;
+	    break;
+
+	case 'T':
+	    if (sending && phone_num) {
+		len += strlen(phone_num);
+		s1 = grow(s1, &p, len);
+		for (phchar = phone_num; *phchar != '\0'; phchar++) 
+		    *p++ = *phchar;
+	    }
+	    else {
+		*p++ = '\\';
+		*p++ = 'T';
+	    }
+	    break;
+
+	case 'U':
+	    if (sending && phone_num2) {
+		len += strlen(phone_num2);
+		s1 = grow(s1, &p, len);
+		for (phchar = phone_num2; *phchar != '\0'; phchar++) 
+		    *p++ = *phchar;
+	    }
+	    else {
+		*p++ = '\\';
+		*p++ = 'U';
+	    }
+	    break;
+
+	case 'q':
+	    quiet = 1;
+	    break;
+
+	case 'r':
+	    *p++ = '\r';
+	    break;
+
+	case 'n':
+	    *p++ = '\n';
+	    break;
+
+	case 's':
+	    *p++ = ' ';
+	    break;
+
+	case 't':
+	    *p++ = '\t';
+	    break;
+
+	case 'N':
+	    if (sending) {
+		*p++ = '\\';
+		*p++ = '\0';
+	    }
+	    else
+		*p++ = 'N';
+	    break;
+
+	case '$':			/* ARI */
+	    if (use_env) {
+		*p++ = cur_chr;
+		break;
+	    }
+	    /* FALL THROUGH */
+
+	default:
+	    if (isoctal (cur_chr)) {
+		cur_chr &= 0x07;
+		if (isoctal (*s)) {
+		    cur_chr <<= 3;
+		    cur_chr |= *s++ - '0';
+		    if (isoctal (*s)) {
+			cur_chr <<= 3;
+			cur_chr |= *s++ - '0';
+		    }
+		}
+
+		if (cur_chr != 0 || sending) {
+		    if (sending && (cur_chr == '\\' || cur_chr == 0))
+			*p++ = '\\';
+		    *p++ = cur_chr;
+		}
+		break;
+	    }
+
+	    if (sending)
+		*p++ = '\\';
+	    *p++ = cur_chr;
+	    break;
+	}
+    }
+
+    if (add_return)
+	*p++ = '\r';	/* +2 for len */
+
+    *p = '\0';		/* +3 for len */
+    return s1;
+}
+
+/*
+ * A modified version of 'strtok'. This version skips \ sequences.
+ */
+
+char *expect_strtok (s, term)
+     char *s, *term;
+{
+    static  char *str   = "";
+    int	    escape_flag = 0;
+    char   *result;
+
+/*
+ * If a string was specified then do initial processing.
+ */
+    if (s)
+	str = s;
+
+/*
+ * If this is the escape flag then reset it and ignore the character.
+ */
+    if (*str)
+	result = str;
+    else
+	result = (char *) 0;
+
+    while (*str) {
+	if (escape_flag) {
+	    escape_flag = 0;
+	    ++str;
+	    continue;
+	}
+
+	if (*str == '\\') {
+	    ++str;
+	    escape_flag = 1;
+	    continue;
+	}
+
+/*
+ * If this is not in the termination string, continue.
+ */
+	if (strchr (term, *str) == (char *) 0) {
+	    ++str;
+	    continue;
+	}
+
+/*
+ * This is the terminator. Mark the end of the string and stop.
+ */
+	*str++ = '\0';
+	break;
+    }
+    return (result);
+}
+
+/*
+ * Process the expect string
+ */
+
+void chat_expect (s)
+char *s;
+{
+    char *expect;
+    char *reply;
+
+    if (strcmp(s, "HANGUP") == 0) {
+	++hup_next;
+        return;
+    }
+ 
+    if (strcmp(s, "ABORT") == 0) {
+	++abort_next;
+	return;
+    }
+
+    if (strcmp(s, "CLR_ABORT") == 0) {
+	++clear_abort_next;
+	return;
+    }
+
+    if (strcmp(s, "REPORT") == 0) {
+	++report_next;
+	return;
+    }
+
+    if (strcmp(s, "CLR_REPORT") == 0) {
+	++clear_report_next;
+	return;
+    }
+
+    if (strcmp(s, "TIMEOUT") == 0) {
+	++timeout_next;
+	return;
+    }
+
+    if (strcmp(s, "ECHO") == 0) {
+	++echo_next;
+	return;
+    }
+
+    if (strcmp(s, "SAY") == 0) {
+	++say_next;
+	return;
+    }
+
+/*
+ * Fetch the expect and reply string.
+ */
+    for (;;) {
+	expect = expect_strtok (s, "-");
+	s      = (char *) 0;
+
+	if (expect == (char *) 0)
+	    return;
+
+	reply = expect_strtok (s, "-");
+
+/*
+ * Handle the expect string. If successful then exit.
+ */
+	if (get_string (expect))
+	    return;
+
+/*
+ * If there is a sub-reply string then send it. Otherwise any condition
+ * is terminal.
+ */
+	if (reply == (char *) 0 || exit_code != 3)
+	    break;
+
+	chat_send (reply);
+    }
+
+/*
+ * The expectation did not occur. This is terminal.
+ */
+    if (fail_reason)
+	msgf("Failed (%s)", fail_reason);
+    else
+	msgf("Failed");
+    terminate(exit_code);
+}
+
+/*
+ * Translate the input character to the appropriate string for printing
+ * the data.
+ */
+
+char *character(c)
+int c;
+{
+    static char string[10];
+    char *meta;
+
+    meta = (c & 0x80) ? "M-" : "";
+    c &= 0x7F;
+
+    if (c < 32)
+	sprintf(string, "%s^%c", meta, (int)c + '@');
+    else if (c == 127)
+	sprintf(string, "%s^?", meta);
+    else
+	sprintf(string, "%s%c", meta, c);
+
+    return (string);
+}
+
+/*
+ *  process the reply string
+ */
+void chat_send (s)
+register char *s;
+{
+    char file_data[STR_LEN];
+
+    if (say_next) {
+	say_next = 0;
+	s = clean(s, 1);
+	write(2, s, strlen(s));
+        free(s);
+	return;
+    }
+
+    if (hup_next) {
+        hup_next = 0;
+	if (strcmp(s, "OFF") == 0)
+           signal(SIGHUP, SIG_IGN);
+        else
+           signal(SIGHUP, sighup);
+        return;
+    }
+
+    if (echo_next) {
+	echo_next = 0;
+	echo = (strcmp(s, "ON") == 0);
+	return;
+    }
+
+    if (abort_next) {
+	char *s1;
+	
+	abort_next = 0;
+	
+	if (n_aborts >= MAX_ABORTS)
+	    fatal(2, "Too many ABORT strings");
+	
+	s1 = clean(s, 0);
+	
+	if (strlen(s1) > strlen(s)
+	    || strlen(s1) + 1 > sizeof(fail_buffer))
+	    fatal(1, "Illegal or too-long ABORT string ('%v')", s);
+
+	abort_string[n_aborts++] = s1;
+
+	if (verbose)
+	    msgf("abort on (%v)", s);
+	return;
+    }
+
+    if (clear_abort_next) {
+	char *s1;
+	int   i;
+        int   old_max;
+	int   pack = 0;
+	
+	clear_abort_next = 0;
+	
+	s1 = clean(s, 0);
+	
+	if (strlen(s1) > strlen(s)
+	    || strlen(s1) + 1 > sizeof(fail_buffer))
+	    fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
+
+        old_max = n_aborts;
+	for (i=0; i < n_aborts; i++) {
+	    if ( strcmp(s1,abort_string[i]) == 0 ) {
+		free(abort_string[i]);
+		abort_string[i] = NULL;
+		pack++;
+		n_aborts--;
+		if (verbose)
+		    msgf("clear abort on (%v)", s);
+	    }
+	}
+        free(s1);
+	if (pack)
+	    pack_array(abort_string,old_max);
+	return;
+    }
+
+    if (report_next) {
+	char *s1;
+	
+	report_next = 0;
+	if (n_reports >= MAX_REPORTS)
+	    fatal(2, "Too many REPORT strings");
+	
+	s1 = clean(s, 0);
+	
+	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof(fail_buffer) - 1)
+	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
+	
+	report_string[n_reports++] = s1;
+	
+	if (verbose)
+	    msgf("report (%v)", s);
+	return;
+    }
+
+    if (clear_report_next) {
+	char *s1;
+	int   i;
+	int   old_max;
+	int   pack = 0;
+	
+	clear_report_next = 0;
+	
+	s1 = clean(s, 0);
+	
+	if (strlen(s1) > strlen(s) || strlen(s1) > 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)
+		    msgf("clear report (%v)", s);
+	    }
+	}
+        free(s1);
+        if (pack)
+	    pack_array(report_string,old_max);
+	
+	return;
+    }
+
+    if (timeout_next) {
+	timeout_next = 0;
+	timeout = atoi(s);
+	
+	if (timeout <= 0)
+	    timeout = DEFAULT_CHAT_TIMEOUT;
+
+	if (verbose)
+	    msgf("timeout set to %d seconds", timeout);
+
+	return;
+    }
+
+    /*
+     * The syntax @filename means read the string to send from the
+     * file `filename'.
+     */
+    if (s[0] == '@') {
+	/* skip the @ and any following white-space */
+	char *fn = s;
+	while (*++fn == ' ' || *fn == '\t')
+	    ;
+
+	if (*fn != 0) {
+	    FILE *f;
+	    int n = 0;
+
+	    /* open the file and read until STR_LEN-1 bytes or end-of-file */
+	    f = fopen(fn, "r");
+	    if (f == NULL)
+		fatal(1, "%s -- open failed: %m", fn);
+	    while (n < STR_LEN - 1) {
+		int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f);
+		if (nr < 0)
+		    fatal(1, "%s -- read error", fn);
+		if (nr == 0)
+		    break;
+		n += nr;
+	    }
+	    fclose(f);
+
+	    /* use the string we got as the string to send,
+	       but trim off the final newline if any. */
+	    if (n > 0 && file_data[n-1] == '\n')
+		--n;
+	    file_data[n] = 0;
+	    s = file_data;
+	}
+    }
+
+    if (strcmp(s, "EOT") == 0)
+	s = "^D\\c";
+    else if (strcmp(s, "BREAK") == 0)
+	s = "\\K\\c";
+
+    if (!put_string(s))
+	fatal(1, "Failed");
+}
+
+int get_char()
+{
+    int status;
+    unsigned char c;
+
+    status = read(0, &c, 1);
+    switch (status) {
+    case 1:
+#ifdef ENABLE_NETWORK_SUPPORT
+	if (network && c == IAC) {
+	    if (net_get_char(&c) != -1)
+		return ((int)c & 0x7F);
+	    /* drop through to error below */
+	} else
+#endif
+	return ((int)c & 0x7F);
+
+    default:
+	msgf("warning: read() on stdin returned %d", status);
+
+    case -1:
+	if ((status = fcntl(0, F_GETFL, 0)) == -1)
+	    fatal(2, "Can't get file mode flags on stdin: %m");
+
+	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
+	    fatal(2, "Can't set file mode flags on stdin: %m");
+	
+	return (-1);
+    }
+}
+
+int put_char(c)
+int c;
+{
+    int status;
+    char ch = c;
+
+    usleep(10000);		/* inter-character typing delay (?) */
+
+    status = write(1, &ch, 1);/*wangming*/
+
+    switch (status) {
+    case 1:
+	return (0);
+	
+    default:
+	msgf("warning: write() on stdout returned %d", status);
+	
+    case -1:
+	if ((status = fcntl(0, F_GETFL, 0)) == -1)
+	    fatal(2, "Can't get file mode flags on stdin, %m");
+
+	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
+	    fatal(2, "Can't set file mode flags on stdin: %m");
+	
+	return (-1);
+    }
+}
+
+int write_char (c)
+int c;
+{
+    if (alarmed || put_char(c) < 0) {
+	alarm(0);
+	alarmed = 0;
+
+	if (verbose) {
+	    if (errno == EINTR || errno == EWOULDBLOCK)
+		msgf(" -- write timed out");
+	    else
+		msgf(" -- write failed: %m");
+	}
+	return (0);
+    }
+    return (1);
+}
+
+int put_string (s)
+register char *s;
+{
+    quiet = 0;
+    s = clean(s, 1);
+
+    if (verbose) {
+	if (quiet)
+	    msgf("send (?????\?)");
+	else
+	    msgf("send (%v)", s);
+    }
+
+    alarm(timeout); alarmed = 0;
+
+    while (*s) {
+	register char c = *s++;
+
+	if (c != '\\') {
+	    if (!write_char (c))
+		return 0;
+	    continue;
+	}
+
+	c = *s++;
+	switch (c) {
+	case 'd':
+	    sleep(1);
+	    break;
+
+	case 'K':
+	    break_sequence();
+	    break;
+
+	case 'p':
+	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
+	    break;
+
+	default:
+	    if (!write_char (c))
+		return 0;
+	    break;
+	}
+    }
+
+    alarm(0);
+    alarmed = 0;
+    return (1);
+}
+
+/*
+ *	Echo a character to stderr.
+ *	When called with -1, a '\n' character is generated when
+ *	the cursor is not at the beginning of a line.
+ */
+void echo_stderr(n)
+int n;
+{
+    static int need_lf;
+    char *s;
+
+    switch (n) {
+    case '\r':		/* ignore '\r' */
+	break;
+    case -1:
+	if (need_lf == 0)
+	    break;
+	/* fall through */
+    case '\n':
+	write(2, "\n", 1);
+	need_lf = 0;
+	break;
+    default:
+	s = character(n);
+	write(2, s, strlen(s));
+	need_lf = 1;
+	break;
+    }
+}
+
+/*
+ *	'Wait for' this string to appear on this file descriptor.
+ */
+int get_string(string)
+register char *string;
+{
+    char temp[STR_LEN];
+    int c, printed = 0, len, minlen;
+    register char *s = temp, *end = s + STR_LEN;
+    char *logged = temp;
+
+    fail_reason = (char *)0;
+    string = clean(string, 0);
+    len = strlen(string);
+    minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
+    if (verbose)
+	msgf("expect (%v)", string);
+
+    if (len > STR_LEN) {
+	msgf("expect string is too long");
+	exit_code = 1;
+	return 0;
+    }
+
+    if (len == 0) {
+	if (verbose)
+	    msgf("got it");
+	return (1);
+    }
+    alarm(timeout);
+    alarmed = 0;
+
+    while ( ! alarmed && (c = get_char()) >= 0) {
+	int n, abort_len, report_len;
+
+	if (echo)
+	    echo_stderr(c);
+	if (verbose && c == '\n') {
+	    if (s == logged)
+		msgf("");	/* blank line */
+	    else
+		msgf("%0.*v", s - logged, logged);
+	    logged = s + 1;
+	}
+
+	*s++ = c;
+
+	if (verbose && s >= logged + 80) {
+	    msgf("%0.*v", s - logged, logged);
+	    logged = s;
+	}
+
+	if (Verbose) {
+	   if (c == '\n')
+	       fputc( '\n', stderr );
+	   else if (c != '\r')
+	       fprintf( stderr, "%s", character(c) );
+	}
+
+	if (!report_gathering) {
+	    for (n = 0; n < n_reports; ++n) {
+		if ((report_string[n] != (char*) NULL) &&
+		    s - temp >= (report_len = strlen(report_string[n])) &&
+		    strncmp(s - report_len, report_string[n], report_len) == 0) {
+		    time_t time_now   = time ((time_t*) NULL);
+		    struct tm* tm_now = localtime (&time_now);
+
+		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
+		    strcat (report_buffer, report_string[n]);
+
+		    report_string[n] = (char *) NULL;
+		    report_gathering = 1;
+		    break;
+		}
+	    }
+	}
+	else {
+	    if (!iscntrl (c)) {
+		int rep_len = strlen (report_buffer);
+		report_buffer[rep_len]     = c;
+		report_buffer[rep_len + 1] = '\0';
+	    }
+	    else {
+		report_gathering = 0;
+		fprintf (report_fp, "chat:  %s\n", report_buffer);
+	    }
+	}
+
+	if (s - temp >= len &&
+	    c == string[len - 1] &&
+	    strncmp(s - len, string, len) == 0) {
+	    if (verbose) {
+		if (s > logged)
+		    msgf("%0.*v", s - logged, logged);
+		msgf(" -- got it\n");
+	    }
+
+	    alarm(0);
+	    alarmed = 0;
+	    return (1);
+	}
+
+	for (n = 0; n < n_aborts; ++n) {
+	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
+		strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
+		if (verbose) {
+		    if (s > logged)
+			msgf("%0.*v", s - logged, logged);
+		    msgf(" -- failed");
+		}
+
+		alarm(0);
+		alarmed = 0;
+		exit_code = n + 4;
+		strcpy(fail_reason = fail_buffer, abort_string[n]);
+		return (0);
+	    }
+	}
+
+	if (s >= end) {
+	    if (logged < s - minlen) {
+		if (verbose)
+		    msgf("%0.*v", s - logged, logged);
+		logged = s;
+	    }
+	    s -= minlen;
+	    memmove(temp, s, minlen);
+	    logged = temp + (logged - s);
+	    s = temp + minlen;
+	}
+
+	if (alarmed && verbose)
+	    msgf("warning: alarm synchronization problem");
+    }
+
+    alarm(0);
+    
+    if (verbose && printed) {
+	if (alarmed)
+	    msgf(" -- read timed out");
+	else
+	    msgf(" -- read failed: %m");
+    }
+
+    exit_code = 3;
+    alarmed   = 0;
+    return (0);
+}
+
+/*
+ * Gross kludge to handle Solaris versions >= 2.6 having usleep.
+ */
+#ifdef SOL2
+#include <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;
+}
+
+
+#ifdef ENABLE_NETWORK_SUPPORT
+/*
+ * Allow chat to chat to a network connection easily
+ */
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+void
+net_open()
+{
+	struct	sockaddr_in sock_in;
+	struct hostent *host;
+	int s;
+											aaaaaaaaaaaaa
+	bzero((char *)&sock_in, sizeof (sock_in));
+	sock_in.sin_family = AF_INET;
+	s = socket(AF_INET, SOCK_STREAM, 0);
+	if (s == -1) {
+		perror("socket");
+		exit(1);
+	}
+
+	if (bind(s, (struct sockaddr*)&sock_in, sizeof (sock_in)) == -1) {
+		perror("bind");
+		exit(2);
+	}
+
+	host = gethostbyname(net_host);
+	if (host) {
+		sock_in.sin_family = host->h_addrtype;
+		bcopy(host->h_addr, &sock_in.sin_addr, host->h_length);
+	} else {
+		sock_in.sin_family = AF_INET;
+		sock_in.sin_addr.s_addr = inet_addr(net_host);
+		if (sock_in.sin_addr.s_addr == -1) {
+			fprintf(stderr, "%s: %s unknown host\n", program_name, net_host);
+			exit(3);
+		}
+	}
+
+	sock_in.sin_port = htons(net_port);
+
+	if (connect(s, (struct sockaddr*)&sock_in, sizeof(sock_in)) == -1) {
+		perror("connect:");
+		exit(4);
+	}
+
+	/*
+	 * make the socket stdin/stdout
+	 */
+	if (s != 0) {
+		dup2(s, 0);
+		close(s);
+	}
+	dup2(0, 1);
+}
+
+
+/* we arrive here having already receive an IAC */
+int
+net_get_char(unsigned char *cp)
+{
+	int status;
+	unsigned char cmd, option;
+
+	do {
+		status = read(0, &cmd, 1);
+		if (status <= 0)
+			return(IAC);
+
+		if (cmd == IAC) /* escaped IAC */
+			return(IAC);
+
+		status = read(0, &option, 1);
+		if (status <= 0) {
+			put_char(IAC);
+			return(cmd);
+		}
+
+		switch (cmd) {
+		case WILL:
+			put_char(IAC);
+			put_char(DONT);
+			put_char(option);
+			break;
+		case WONT:
+			break;
+		case DO:
+			put_char(IAC);
+			put_char(WONT);
+			put_char(option);
+			break;
+		case DONT:
+			break;
+		default:
+			put_char(IAC);
+			put_char(cmd);
+			return(option);
+		}
+
+		/*
+		 * get next char
+		 */
+		status = read(0, cp, 1);
+		if (status <= 0)
+			return(-1);
+	} while (*cp == IAC);
+
+	return(*cp);
+}
+
+#endif /* ENABLE_NETWORK_SUPPORT */
diff --git a/ap/app/pppd/chat/chat.c.saved b/ap/app/pppd/chat/chat.c.saved
new file mode 100644
index 0000000..e9a3503
--- /dev/null
+++ b/ap/app/pppd/chat/chat.c.saved
@@ -0,0 +1,2002 @@
+/*
+ *	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.
+ *
+ * -----------------
+ *  21st March 2003  - added network/telnet code option <davidm@snapgear.com>
+ *                   - added -R (truncated/created report file option)
+ *
+ *	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: chat.c,v 1.8 2007-10-31 03:38:05 davidm Exp $";
+#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;
+char *device      = 0;
+
+#ifdef ENABLE_NETWORK_SUPPORT
+
+int network       = 0;
+char *net_host    = NULL;
+int net_port      = 23; /* telnet by default */
+
+#define IAC  0xff
+#define DONT 0xfe
+#define DO   0xfd
+#define WONT 0xfc
+#define WILL 0xfb
+
+extern int net_get_char(unsigned char *cp);
+extern void net_open();
+
+#endif /* ENABLE_NETWORK_SUPPORT */
+
+int have_tty_parameters = 0;
+
+#ifdef TERMIO
+#define term_parms struct termio
+#define get_term_param(param) ioctl(0, TCGETA, param)
+#define set_term_param(param) ioctl(0, TCSETA, param)
+struct termio saved_tty_parameters;
+#endif
+
+#ifdef TERMIOS
+#define term_parms struct termios
+#define get_term_param(param) tcgetattr(0, param)
+#define set_term_param(param) tcsetattr(0, TCSANOW, param)
+struct termios saved_tty_parameters;
+#endif
+
+char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
+	fail_buffer[50];
+int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
+int clear_abort_next = 0;
+
+char *report_string[MAX_REPORTS] ;
+char  report_buffer[256] ;
+int n_reports = 0, report_next = 0, report_gathering = 0 ; 
+int clear_report_next = 0;
+
+int say_next = 0, hup_next = 0;
+
+void *dup_mem __P((void *b, size_t c));
+void *copy_of __P((char *s));
+char *grow __P((char *s, char **p, size_t len));
+void usage __P((void));
+void msgf __P((const char *fmt, ...));
+void fatal __P((int code, const char *fmt, ...));
+SIGTYPE sigalrm __P((int signo));
+SIGTYPE sigint __P((int signo));
+SIGTYPE sigterm __P((int signo));
+SIGTYPE sighup __P((int signo));
+void unalarm __P((void));
+void init __P((void));
+void set_tty_parameters __P((void));
+void echo_stderr __P((int));
+void break_sequence __P((void));
+void terminate __P((int status));
+void do_file __P((char *chat_file));
+int  get_string __P((register char *string));
+int  put_string __P((register char *s));
+int  write_char __P((int c));
+int  put_char __P((int c));
+int  get_char __P((void));
+void chat_send __P((register char *s));
+char *character __P((int c));
+void chat_expect __P((register char *s));
+char *clean __P((register char *s, int sending));
+void break_sequence __P((void));
+void terminate __P((int status));
+void pack_array __P((char **array, int end));
+char *expect_strtok __P((char *, char *));
+int vfmtmsg __P((char *, int, const char *, va_list));	/* vsprintf++ */
+
+int main __P((int, char *[]));
+
+void *dup_mem(b, c)
+void *b;
+size_t c;
+{
+    void *ans = malloc (c);
+    if (!ans)
+	warn("memory error!");
+
+    memcpy (ans, b, c);
+    return ans;
+}
+
+void *copy_of (s)
+char *s;
+{
+    return dup_mem (s, strlen (s) + 1);
+}
+
+/* grow a char buffer and keep a pointer offset */
+char *grow(s, p, len)
+char *s;
+char **p;
+size_t len;
+{
+    size_t l = *p - s;		/* save p as distance into s */
+
+    s = realloc(s, len);
+    if (!s)
+	warn("memory error!");
+    *p = s + l;			/* restore p */
+    return s;
+}
+
+/*
+ * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \
+ * [ -r report-file ] [-d device] \
+ *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
+ *
+ *	Perform a UUCP-dialer-like chat script on stdin and stdout.
+ *	(or the given device if specified)
+ */
+int
+main(argc, argv)
+     int argc;
+     char **argv;
+{
+    int option;
+    char *arg;
+
+    program_name = *argv;
+#ifndef EMBED
+    tzset();
+#endif
+
+    while ((option = OPTION(argc, argv)) != 0) {
+	switch (option) {
+	case 'e':
+	    ++echo;
+	    break;
+
+	case 'E':
+	    ++use_env;
+	    break;
+
+	case 'v':
+	    ++verbose;
+	    break;
+
+#ifdef ENABLE_NETWORK_SUPPORT
+	case 'h':
+	    ++network;
+	    if ((arg = OPTARG(argc, argv)) != NULL)
+		net_host = copy_of(arg);
+	    else
+		usage();
+	    break;
+
+	case 'p':
+	    if ((arg = OPTARG(argc, argv)) != NULL)
+		net_port = atoi(arg);
+	    else
+		usage();
+	    break;
+#endif
+
+	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 'd':
+	    if ((arg = OPTARG(argc, argv)) != NULL)
+		device = arg;
+	    else
+		usage();
+	    break;
+
+	case 'r':
+	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, option == 'r' ? "a" : "w");
+		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
+    }
+
+    if (device != 0) {
+	int fd = open(device, O_RDWR, S_IRUSR | S_IWUSR);
+	if (fd == -1)
+	    usage();
+	dup2(fd, 0);
+	dup2(fd, 1);
+    }
+
+    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)
+    {
+    syslog(LOG_ERR, "Can't set terminal parameters");
+    warn("Warning: disabling multilink");
+	warn("%s -- open failed: %m", chat_file);
+	//syslog(LOG_ERR, "Can't set terminal parameters");
+    }
+    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')
+			warn("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
+		{
+		warn("wangming: %s---->%d", __FILE__, __LINE__);
+		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] [-R report-file]\n"
+#ifdef ENABLE_NETWORK_SUPPORT
+"     [-h hostname] [-p tcp-port]\n"
+#endif /* ENABLE_NETWORK_SUPPORT */
+"     [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name);
+    exit(1);
+}
+
+char line[1024];
+
+/*
+ * Send a message to syslog and/or stderr.
+ */
+void msgf __V((const char *fmt, ...))
+{
+    va_list args;
+
+#ifdef __STDC__
+    va_start(args, fmt);
+#else
+    char *fmt;
+    va_start(args);
+    fmt = va_arg(args, char *);
+#endif
+
+    vfmtmsg(line, sizeof(line), fmt, args);
+    if (to_log)
+	syslog(LOG_INFO, "%s", line);
+    if (to_stderr)
+	fprintf(stderr, "%s\n", line);
+}
+
+/*
+ *	Print an error message and terminate.
+ */
+
+void fatal __V((int code, const char *fmt, ...))
+{
+    va_list args;
+
+#ifdef __STDC__
+    va_start(args, fmt);
+#else
+    int code;
+    char *fmt;
+    va_start(args);
+    code = va_arg(args, int);
+    fmt = va_arg(args, char *);
+#endif
+
+    vfmtmsg(line, sizeof(line), fmt, args);
+    if (to_log)
+	syslog(LOG_ERR, "%s", line);
+    if (to_stderr)
+	fprintf(stderr, "%s\n", line);
+    terminate(code);
+}
+
+int alarmed = 0;
+
+SIGTYPE sigalrm(signo)
+int signo;
+{
+    int flags;
+
+    alarm(1);
+    alarmed = 1;		/* Reset alarm to avoid race window */
+    signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
+
+    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+	warn("Can't get file mode flags on stdin: %m");
+
+    if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
+	warn("Can't set file mode flags on stdin: %m");
+
+    if (verbose)
+	warn("alarm");
+}
+
+void unalarm()
+{
+    int flags;
+
+    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+	warn("Can't get file mode flags on stdin: %m");
+
+    if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
+	warn("Can't set file mode flags on stdin: %m");
+}
+
+SIGTYPE sigint(signo)
+int signo;
+{
+    warn("SIGINT");
+}
+
+SIGTYPE sigterm(signo)
+int signo;
+{
+    warn("SIGTERM");
+}
+
+SIGTYPE sighup(signo)
+int signo;
+{
+    warn("SIGHUP");
+}
+
+void init()
+{
+    signal(SIGINT, sigint);
+    signal(SIGTERM, sigterm);
+    signal(SIGHUP, sighup);
+
+#ifdef ENABLE_NETWORK_SUPPORT
+    if (network)
+	net_open();
+    else
+#endif
+    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)
+#ifndef EMBED
+	warn("Can't get terminal parameters: %m");
+#else
+	syslog(LOG_ERR, "Can't get terminal parameters: %m");
+#endif
+
+    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)
+#ifndef EMBED
+	warn("Can't set terminal parameters: %m");
+#else
+	syslog(LOG_ERR, "Can't set terminal parameters: %m");
+#endif
+#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 &&
+    	report_file != (char *) 0 && report_fp != (FILE *) NULL) {
+	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)
+#ifndef EMBED
+	    warn("Can't restore terminal parameters: %m");
+#else
+	    syslog(LOG_ERR, "Can't restore terminal parameters: %m");
+#endif
+    }
+#endif
+
+    exit(status);
+}
+
+/*
+ *	'Clean up' this string.
+ */
+char *clean(s, sending)
+register char *s;
+int sending;  /* set to 1 when sending (putting) this string. */
+{
+    char cur_chr;
+    char *s1, *p, *phchar;
+    int add_return = sending;
+    size_t len = strlen(s) + 3;		/* see len comments below */
+
+#define isoctal(chr)	(((chr) >= '0') && ((chr) <= '7'))
+#define isalnumx(chr)	((((chr) >= '0') && ((chr) <= '9')) \
+			 || (((chr) >= 'a') && ((chr) <= 'z')) \
+			 || (((chr) >= 'A') && ((chr) <= 'Z')) \
+			 || (chr) == '_')
+
+    p = s1 = malloc(len);
+    if (!p)
+	warn("memory error!");
+    while (*s) {
+	cur_chr = *s++;
+	if (cur_chr == '^') {
+	    cur_chr = *s++;
+	    if (cur_chr == '\0') {
+		*p++ = '^';
+		break;
+	    }
+	    cur_chr &= 0x1F;
+	    if (cur_chr != 0) {
+		*p++ = cur_chr;
+	    }
+	    continue;
+	}
+
+	if (use_env && cur_chr == '$') {		/* ARI */
+	    char c;
+
+	    phchar = s;
+	    while (isalnumx(*s))
+		s++;
+	    c = *s;		/* save */
+	    *s = '\0';
+	    phchar = getenv(phchar);
+	    *s = c;		/* restore */
+	    if (phchar) {
+		len += strlen(phchar);
+		s1 = grow(s1, &p, len);
+		while (*phchar)
+		    *p++ = *phchar++;
+	    }
+	    continue;
+	}
+
+	if (cur_chr != '\\') {
+	    *p++ = cur_chr;
+	    continue;
+	}
+
+	cur_chr = *s++;
+	if (cur_chr == '\0') {
+	    if (sending) {
+		*p++ = '\\';
+		*p++ = '\\';	/* +1 for len */
+	    }
+	    break;
+	}
+
+	switch (cur_chr) {
+	case 'b':
+	    *p++ = '\b';
+	    break;
+
+	case 'c':
+	    if (sending && *s == '\0')
+		add_return = 0;
+	    else
+		*p++ = cur_chr;
+	    break;
+
+	case '\\':
+	case 'K':
+	case 'p':
+	case 'd':
+	    if (sending)
+		*p++ = '\\';
+	    *p++ = cur_chr;
+	    break;
+
+	case 'T':
+	    if (sending && phone_num) {
+		len += strlen(phone_num);
+		s1 = grow(s1, &p, len);
+		for (phchar = phone_num; *phchar != '\0'; phchar++) 
+		    *p++ = *phchar;
+	    }
+	    else {
+		*p++ = '\\';
+		*p++ = 'T';
+	    }
+	    break;
+
+	case 'U':
+	    if (sending && phone_num2) {
+		len += strlen(phone_num2);
+		s1 = grow(s1, &p, len);
+		for (phchar = phone_num2; *phchar != '\0'; phchar++) 
+		    *p++ = *phchar;
+	    }
+	    else {
+		*p++ = '\\';
+		*p++ = 'U';
+	    }
+	    break;
+
+	case 'q':
+	    quiet = 1;
+	    break;
+
+	case 'r':
+	    *p++ = '\r';
+	    break;
+
+	case 'n':
+	    *p++ = '\n';
+	    break;
+
+	case 's':
+	    *p++ = ' ';
+	    break;
+
+	case 't':
+	    *p++ = '\t';
+	    break;
+
+	case 'N':
+	    if (sending) {
+		*p++ = '\\';
+		*p++ = '\0';
+	    }
+	    else
+		*p++ = 'N';
+	    break;
+
+	case '$':			/* ARI */
+	    if (use_env) {
+		*p++ = cur_chr;
+		break;
+	    }
+	    /* FALL THROUGH */
+
+	default:
+	    if (isoctal (cur_chr)) {
+		cur_chr &= 0x07;
+		if (isoctal (*s)) {
+		    cur_chr <<= 3;
+		    cur_chr |= *s++ - '0';
+		    if (isoctal (*s)) {
+			cur_chr <<= 3;
+			cur_chr |= *s++ - '0';
+		    }
+		}
+
+		if (cur_chr != 0 || sending) {
+		    if (sending && (cur_chr == '\\' || cur_chr == 0))
+			*p++ = '\\';
+		    *p++ = cur_chr;
+		}
+		break;
+	    }
+
+	    if (sending)
+		*p++ = '\\';
+	    *p++ = cur_chr;
+	    break;
+	}
+    }
+
+    if (add_return)
+	*p++ = '\r';	/* +2 for len */
+
+    *p = '\0';		/* +3 for len */
+    return s1;
+}
+
+/*
+ * A modified version of 'strtok'. This version skips \ sequences.
+ */
+
+char *expect_strtok (s, term)
+     char *s, *term;
+{
+    static  char *str   = "";
+    int	    escape_flag = 0;
+    char   *result;
+
+/*
+ * If a string was specified then do initial processing.
+ */
+    if (s)
+	str = s;
+
+/*
+ * If this is the escape flag then reset it and ignore the character.
+ */
+    if (*str)
+	result = str;
+    else
+	result = (char *) 0;
+
+    while (*str) {
+	if (escape_flag) {
+	    escape_flag = 0;
+	    ++str;
+	    continue;
+	}
+
+	if (*str == '\\') {
+	    ++str;
+	    escape_flag = 1;
+	    continue;
+	}
+
+/*
+ * If this is not in the termination string, continue.
+ */
+	if (strchr (term, *str) == (char *) 0) {
+	    ++str;
+	    continue;
+	}
+
+/*
+ * This is the terminator. Mark the end of the string and stop.
+ */
+	*str++ = '\0';
+	break;
+    }
+    return (result);
+}
+
+/*
+ * Process the expect string
+ */
+
+void chat_expect (s)
+char *s;
+{
+    char *expect;
+    char *reply;
+	warn("wangming: %s---->%d, s is %s", __FILE__, __LINE__, s);
+    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, "-");
+	warn("wangming: %s---->%d, expect is %s", __FILE__, __LINE__, expect);
+	s      = (char *) 0;
+
+	if (expect == (char *) 0)
+	    return;
+
+	reply = expect_strtok (s, "-");
+	warn("wangming: %s---->%d, reply is %s", __FILE__, __LINE__, reply);
+
+/*
+ * 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)
+	warn("Failed (%s)", fail_reason);
+    else
+	warn("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)
+	    warn("Too many ABORT strings");
+	
+	s1 = clean(s, 0);
+	
+	if (strlen(s1) > strlen(s)
+	    || strlen(s1) + 1 > sizeof(fail_buffer))
+	    warn("Illegal or too-long ABORT string ('%v')", s);
+
+	abort_string[n_aborts++] = s1;
+
+	if (verbose)
+	    warn("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))
+	    warn("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)
+		    warn("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)
+	    warn("Too many REPORT strings");
+	
+	s1 = clean(s, 0);
+	
+	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof(fail_buffer) - 1)
+	    warn("Illegal or too-long REPORT string ('%v')", s);
+	
+	report_string[n_reports++] = s1;
+	
+	if (verbose)
+	    warn("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)
+	    warn("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)
+		    warn("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)
+	    warn("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)
+		warn("%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)
+		    warn("%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))
+	warn("Failed");
+}
+
+int get_char()
+{
+    int status;
+    unsigned char c;
+
+    status = read(0, &c, 1);
+
+    switch (status) {
+    case 1:
+#ifdef ENABLE_NETWORK_SUPPORT
+	if (network && c == IAC) {
+	    if (net_get_char(&c) != -1)
+		return ((int)c & 0x7F);
+	    /* drop through to error below */
+	} else
+#endif
+	return ((int)c & 0x7F);
+
+    default:
+	warn("warning: read() on stdin returned %d", status);
+
+    case -1:
+	if ((status = fcntl(0, F_GETFL, 0)) == -1)
+	    warn("Can't get file mode flags on stdin: %m");
+
+	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
+	    warn("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:
+	warn("warning: write() on stdout returned %d", status);
+	
+    case -1:
+	if ((status = fcntl(0, F_GETFL, 0)) == -1)
+	    warn("Can't get file mode flags on stdin, %m");
+
+	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
+	    warn("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)
+		warn(" -- write timed out");
+	    else
+		warn(" -- write failed: %m");
+	}
+	return (0);
+    }
+    return (1);
+}
+
+int put_string (s)
+register char *s;
+{
+    quiet = 0;
+    s = clean(s, 1);
+
+    if (verbose) {
+	if (quiet)
+	    warn("send (?????\?)");
+	else
+	    warn("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;
+	warn("wangming: %s---->%d, string is %s", __FILE__, __LINE__, string);
+    if (verbose)
+    	{
+    warn("expect (%v)", string);	
+	//warn("expect (%v)", string);
+    	}
+    if (len > STR_LEN) {
+	warn("expect string is too long");
+	exit_code = 1;
+	return 0;
+    }
+
+    if (len == 0) {
+	if (verbose)
+	    warn("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)
+		warn("");	/* blank line */
+	    else
+		warn("%0.*v", s - logged, logged);
+	    logged = s + 1;
+	}
+
+	*s++ = c;
+
+	if (verbose && s >= logged + 80) {
+	    warn("%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)
+		    warn("%0.*v", s - logged, logged);
+		warn(" -- 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)
+			warn("%0.*v", s - logged, logged);
+		    warn(" -- 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)
+		    warn("%0.*v", s - logged, logged);
+		logged = s;
+	    }
+	    s -= minlen;
+	    memmove(temp, s, minlen);
+	    logged = temp + (logged - s);
+	    s = temp + minlen;
+	}
+
+	if (alarmed && verbose)
+	    warn("warning: alarm synchronization problem");
+    }
+
+    alarm(0);
+    
+    if (verbose && printed) {
+	if (alarmed)
+	    warn(" -- read timed out");
+	else
+	    warn(" -- 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;
+}
+
+
+#ifdef ENABLE_NETWORK_SUPPORT
+/*
+ * Allow chat to chat to a network connection easily
+ */
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+void
+net_open()
+{
+	struct	sockaddr_in sock_in;
+	struct hostent *host;
+	int s;
+
+	bzero((char *)&sock_in, sizeof (sock_in));
+	sock_in.sin_family = AF_INET;
+	s = socket(AF_INET, SOCK_STREAM, 0);
+	if (s == -1) {
+		perror("socket");
+		exit(1);
+	}
+
+	if (bind(s, (struct sockaddr*)&sock_in, sizeof (sock_in)) == -1) {
+		perror("bind");
+		exit(2);
+	}
+
+	host = gethostbyname(net_host);
+	if (host) {
+		sock_in.sin_family = host->h_addrtype;
+		bcopy(host->h_addr, &sock_in.sin_addr, host->h_length);
+	} else {
+		sock_in.sin_family = AF_INET;
+		sock_in.sin_addr.s_addr = inet_addr(net_host);
+		if (sock_in.sin_addr.s_addr == -1) {
+			fprintf(stderr, "%s: %s unknown host\n", program_name, net_host);
+			exit(3);
+		}
+	}
+
+	sock_in.sin_port = htons(net_port);
+
+	if (connect(s, (struct sockaddr*)&sock_in, sizeof(sock_in)) == -1) {
+		perror("connect:");
+		exit(4);
+	}
+
+	/*
+	 * make the socket stdin/stdout
+	 */
+	if (s != 0) {
+		dup2(s, 0);
+		close(s);
+	}
+	dup2(0, 1);
+}
+
+
+/* we arrive here having already receive an IAC */
+int
+net_get_char(unsigned char *cp)
+{
+	int status;
+	unsigned char cmd, option;
+
+	do {
+		status = read(0, &cmd, 1);
+		if (status <= 0)
+			return(IAC);
+
+		if (cmd == IAC) /* escaped IAC */
+			return(IAC);
+
+		status = read(0, &option, 1);
+		if (status <= 0) {
+			put_char(IAC);
+			return(cmd);
+		}
+
+		switch (cmd) {
+		case WILL:
+			put_char(IAC);
+			put_char(DONT);
+			put_char(option);
+			break;
+		case WONT:
+			break;
+		case DO:
+			put_char(IAC);
+			put_char(WONT);
+			put_char(option);
+			break;
+		case DONT:
+			break;
+		default:
+			put_char(IAC);
+			put_char(cmd);
+			return(option);
+		}
+
+		/*
+		 * get next char
+		 */
+		status = read(0, cp, 1);
+		if (status <= 0)
+			return(-1);
+	} while (*cp == IAC);
+
+	return(*cp);
+}
+
+#endif /* ENABLE_NETWORK_SUPPORT */
diff --git a/ap/app/pppd/chat/initchat b/ap/app/pppd/chat/initchat
new file mode 100644
index 0000000..244c534
--- /dev/null
+++ b/ap/app/pppd/chat/initchat
@@ -0,0 +1,8 @@
+AT\r '\r\nOK\r\n'
+ATE0V1\r '\r\nOK\r\n'
+AT '\r\nOK\r\n'
+ATS0=0 '\r\nOK\r\n'
+AT '\r\nOK\r\n'
+ATE0V1 '\r\nOK\r\n'
+AT '\r\nOK\r\n'
+ATDT '\r\nCONNECT\r\n'
\ No newline at end of file
diff --git a/ap/app/pppd/common/zlib.c b/ap/app/pppd/common/zlib.c
new file mode 100644
index 0000000..23b724d
--- /dev/null
+++ b/ap/app/pppd/common/zlib.c
@@ -0,0 +1,5379 @@
+/*
+ * This file is derived from various .h and .c files from the zlib-1.0.4
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.  See zlib.h for conditions of
+ * distribution and use.
+ *
+ * Changes that have been made include:
+ * - added Z_PACKET_FLUSH (see zlib.h for details)
+ * - added inflateIncomp and deflateOutputPending
+ * - allow strm->next_out to be NULL, meaning discard the output
+ *
+ * $Id: zlib.c,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+/* 
+ *  ==FILEVERSION 971210==
+ *
+ * This marker is used by the Linux installation script to determine
+ * whether an up-to-date version of this file is already installed.
+ */
+
+#define NO_DUMMY_DECL
+#define NO_ZCFUNCS
+#define MY_ZCALLOC
+
+#if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL))
+#define inflate	inflate_ppp	/* FreeBSD already has an inflate :-( */
+#endif
+
+
+/* +++ zutil.h */
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* From: zutil.h,v 1.16 1996/07/24 13:41:13 me Exp $ */
+
+#ifndef _Z_UTIL_H
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#if defined(KERNEL) || defined(_KERNEL)
+/* Assume this is a *BSD or SVR4 kernel */
+#include <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)
+      {
+        r = t;
+        if (r == Z_DATA_ERROR) {
+          s->mode = BADB;
+          ZFREE(z, s->sub.trees.blens);
+        }
+        LEAVE
+      }
+      s->sub.trees.index = 0;
+      Tracev((stderr, "inflate:       bits tree ok\n"));
+      s->mode = DTREE;
+    case DTREE:
+      while (t = s->sub.trees.table,
+             s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+      {
+        inflate_huft *h;
+        uInt i, j, c;
+
+        t = s->sub.trees.bb;
+        NEEDBITS(t)
+        h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+        t = h->word.what.Bits;
+        c = h->more.Base;
+        if (c < 16)
+        {
+          DUMPBITS(t)
+          s->sub.trees.blens[s->sub.trees.index++] = c;
+        }
+        else /* c == 16..18 */
+        {
+          i = c == 18 ? 7 : c - 14;
+          j = c == 18 ? 11 : 3;
+          NEEDBITS(t + i)
+          DUMPBITS(t)
+          j += (uInt)b & inflate_mask[i];
+          DUMPBITS(i)
+          i = s->sub.trees.index;
+          t = s->sub.trees.table;
+          if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+              (c == 16 && i < 1))
+          {
+            inflate_trees_free(s->sub.trees.tb, z);
+            ZFREE(z, s->sub.trees.blens);
+            s->mode = BADB;
+            z->msg = (char*)"invalid bit length repeat";
+            r = Z_DATA_ERROR;
+            LEAVE
+          }
+          c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+          do {
+            s->sub.trees.blens[i++] = c;
+          } while (--j);
+          s->sub.trees.index = i;
+        }
+      }
+      inflate_trees_free(s->sub.trees.tb, z);
+      s->sub.trees.tb = Z_NULL;
+      {
+        uInt bl, bd;
+        inflate_huft *tl, *td;
+        inflate_codes_statef *c;
+
+        bl = 9;         /* must be <= 9 for lookahead assumptions */
+        bd = 6;         /* must be <= 9 for lookahead assumptions */
+        t = s->sub.trees.table;
+#ifdef DEBUG_ZLIB
+      inflate_hufts = 0;
+#endif
+        t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+                                  s->sub.trees.blens, &bl, &bd, &tl, &td, z);
+        if (t != Z_OK)
+        {
+          if (t == (uInt)Z_DATA_ERROR) {
+            s->mode = BADB;
+            ZFREE(z, s->sub.trees.blens);
+          }
+          r = t;
+          LEAVE
+        }
+        ZFREE(z, s->sub.trees.blens);
+        Tracev((stderr, "inflate:       trees ok, %d * %d bytes used\n",
+              inflate_hufts, sizeof(inflate_huft)));
+        if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+        {
+          inflate_trees_free(td, z);
+          inflate_trees_free(tl, z);
+          r = Z_MEM_ERROR;
+          LEAVE
+        }
+        s->sub.decode.codes = c;
+        s->sub.decode.tl = tl;
+        s->sub.decode.td = td;
+      }
+      s->mode = CODES;
+    case CODES:
+      UPDATE
+      if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+        return inflate_flush(s, z, r);
+      r = Z_OK;
+      inflate_codes_free(s->sub.decode.codes, z);
+      inflate_trees_free(s->sub.decode.td, z);
+      inflate_trees_free(s->sub.decode.tl, z);
+      LOAD
+      Tracev((stderr, "inflate:       codes end, %lu total out\n",
+              z->total_out + (q >= s->read ? q - s->read :
+              (s->end - s->read) + (q - s->window))));
+      if (!s->last)
+      {
+        s->mode = TYPE;
+        break;
+      }
+      if (k > 7)              /* return unused byte, if any */
+      {
+        Assert(k < 16, "inflate_codes grabbed too many bytes")
+        k -= 8;
+        n++;
+        p--;                    /* can always return one */
+      }
+      s->mode = DRY;
+    case DRY:
+      FLUSH
+      if (s->read != s->write)
+        LEAVE
+      s->mode = DONEB;
+    case DONEB:
+      r = Z_STREAM_END;
+      LEAVE
+    case BADB:
+      r = Z_DATA_ERROR;
+      LEAVE
+    default:
+      r = Z_STREAM_ERROR;
+      LEAVE
+  }
+}
+
+
+int inflate_blocks_free(s, z, c)
+inflate_blocks_statef *s;
+z_streamp z;
+uLongf *c;
+{
+  inflate_blocks_reset(s, z, c);
+  ZFREE(z, s->window);
+  ZFREE(z, s);
+  Trace((stderr, "inflate:   blocks freed\n"));
+  return Z_OK;
+}
+
+
+void inflate_set_dictionary(s, d, n)
+inflate_blocks_statef *s;
+const Bytef *d;
+uInt  n;
+{
+  zmemcpy((charf *)s->window, d, n);
+  s->read = s->write = s->window + n;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output.  The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS).  On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+int inflate_addhistory(s, z)
+inflate_blocks_statef *s;
+z_stream *z;
+{
+    uLong b;              /* bit buffer */  /* NOT USED HERE */
+    uInt k;               /* bits in bit buffer */ /* NOT USED HERE */
+    uInt t;               /* temporary storage */
+    Bytef *p;             /* input data pointer */
+    uInt n;               /* bytes available there */
+    Bytef *q;             /* output window write pointer */
+    uInt m;               /* bytes to end of window or read pointer */
+
+    if (s->read != s->write)
+	return Z_STREAM_ERROR;
+    if (s->mode != TYPE)
+	return Z_DATA_ERROR;
+
+    /* we're ready to rock */
+    LOAD
+    /* while there is input ready, copy to output buffer, moving
+     * pointers as needed.
+     */
+    while (n) {
+	t = n;  /* how many to do */
+	/* is there room until end of buffer? */
+	if (t > m) t = m;
+	/* update check information */
+	if (s->checkfn != Z_NULL)
+	    s->check = (*s->checkfn)(s->check, q, t);
+	zmemcpy(q, p, t);
+	q += t;
+	p += t;
+	n -= t;
+	z->total_out += t;
+	s->read = q;    /* drag read pointer forward */
+/*      WWRAP  */ 	/* expand WWRAP macro by hand to handle s->read */
+	if (q == s->end) {
+	    s->read = q = s->window;
+	    m = WAVAIL;
+	}
+    }
+    UPDATE
+    return Z_OK;
+}
+
+
+/*
+ * At the end of a Deflate-compressed PPP packet, we expect to have seen
+ * a `stored' block type value but not the (zero) length bytes.
+ */
+int inflate_packet_flush(s)
+    inflate_blocks_statef *s;
+{
+    if (s->mode != LENS)
+	return Z_DATA_ERROR;
+    s->mode = TYPE;
+    return Z_OK;
+}
+/* --- infblock.c */
+
+/* +++ inftrees.c */
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* #include "zutil.h" */
+/* #include "inftrees.h" */
+
+char inflate_copyright[] = " inflate 1.0.4 Copyright 1995-1996 Mark Adler ";
+/*
+  If you use the zlib library in a product, an acknowledgment is welcome
+  in the documentation of your product. If for some reason you cannot
+  include such an acknowledgment, I would appreciate that you keep this
+  copyright string in the executable of your product.
+ */
+
+#ifndef NO_DUMMY_DECL
+struct internal_state  {int dummy;}; /* for buggy compilers */
+#endif
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+    uIntf *,            /* code lengths in bits */
+    uInt,               /* number of codes */
+    uInt,               /* number of "simple" codes */
+    const uIntf *,      /* list of base values for non-simple codes */
+    const uIntf *,      /* list of extra bits for non-simple codes */
+    inflate_huft * FAR*,/* result: starting table */
+    uIntf *,            /* maximum lookup bits (returns actual) */
+    z_streamp ));       /* for zalloc function */
+
+local voidpf falloc OF((
+    voidpf,             /* opaque pointer (not used) */
+    uInt,               /* number of items */
+    uInt));             /* size of item */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+        /* see note #13 above about 258 */
+local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
+local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577};
+local const uInt cpdext[30] = { /* Extra bits for distance codes */
+        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+        12, 12, 13, 13};
+
+/*
+   Huffman code decoding is performed using a multi-level table lookup.
+   The fastest way to decode is to simply build a lookup table whose
+   size is determined by the longest code.  However, the time it takes
+   to build this table can also be a factor if the data being decoded
+   is not very long.  The most common codes are necessarily the
+   shortest codes, so those codes dominate the decoding time, and hence
+   the speed.  The idea is you can have a shorter table that decodes the
+   shorter, more probable codes, and then point to subsidiary tables for
+   the longer codes.  The time it costs to decode the longer codes is
+   then traded against the time it takes to make longer tables.
+
+   This results of this trade are in the variables lbits and dbits
+   below.  lbits is the number of bits the first level table for literal/
+   length codes can decode in one step, and dbits is the same thing for
+   the distance codes.  Subsequent tables are also less than or equal to
+   those sizes.  These values may be adjusted either when all of the
+   codes are shorter than that, in which case the longest code length in
+   bits is used, or when the shortest code is *longer* than the requested
+   table size, in which case the length of the shortest code in bits is
+   used.
+
+   There are two different values for the two tables, since they code a
+   different number of possibilities each.  The literal/length table
+   codes 286 possible values, or in a flat code, a little over eight
+   bits.  The distance table codes 30 possible values, or a little less
+   than five bits, flat.  The optimum values for speed end up being
+   about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+   The optimum values may differ though from machine to machine, and
+   possibly even between compilers.  Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15         /* maximum bit length of any code */
+#define N_MAX 288       /* maximum number of codes in any set */
+
+#ifdef DEBUG_ZLIB
+  uInt inflate_hufts;
+#endif
+
+local int huft_build(b, n, s, d, e, t, m, zs)
+uIntf *b;               /* code lengths in bits (all assumed <= BMAX) */
+uInt n;                 /* number of codes (assumed <= N_MAX) */
+uInt s;                 /* number of simple-valued codes (0..s-1) */
+const uIntf *d;         /* list of base values for non-simple codes */
+const uIntf *e;         /* list of extra bits for non-simple codes */
+inflate_huft * FAR *t;  /* result: starting table */
+uIntf *m;               /* maximum lookup bits, returns actual */
+z_streamp zs;           /* for zalloc function */
+/* Given a list of code lengths and a maximum table size, make a set of
+   tables to decode that set of codes.  Return Z_OK on success, Z_BUF_ERROR
+   if the given code set is incomplete (the tables are still built in this
+   case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of
+   lengths), or Z_MEM_ERROR if not enough memory. */
+{
+
+  uInt a;                       /* counter for codes of length k */
+  uInt c[BMAX+1];               /* bit length count table */
+  uInt f;                       /* i repeats in table every f entries */
+  int g;                        /* maximum code length */
+  int h;                        /* table level */
+  register uInt i;              /* counter, current code */
+  register uInt j;              /* counter */
+  register int k;               /* number of bits in current code */
+  int l;                        /* bits per table (returned in m) */
+  register uIntf *p;            /* pointer into c[], b[], or v[] */
+  inflate_huft *q;              /* points to current table */
+  struct inflate_huft_s r;      /* table entry for structure assignment */
+  inflate_huft *u[BMAX];        /* table stack */
+  uInt v[N_MAX];                /* values in order of bit length */
+  register int w;               /* bits before this table == (l * h) */
+  uInt x[BMAX+1];               /* bit offsets, then code stack */
+  uIntf *xp;                    /* pointer into x */
+  int y;                        /* number of dummy codes added */
+  uInt z;                       /* number of entries in current table */
+
+
+  /* Generate counts for each bit length */
+  p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+  C4                            /* clear c[]--assume BMAX+1 is 16 */
+  p = b;  i = n;
+  do {
+    c[*p++]++;                  /* assume all entries <= BMAX */
+  } while (--i);
+  if (c[0] == n)                /* null input--all zero length codes */
+  {
+    *t = (inflate_huft *)Z_NULL;
+    *m = 0;
+    return Z_OK;
+  }
+
+
+  /* Find minimum and maximum length, bound *m by those */
+  l = *m;
+  for (j = 1; j <= BMAX; j++)
+    if (c[j])
+      break;
+  k = j;                        /* minimum code length */
+  if ((uInt)l < j)
+    l = j;
+  for (i = BMAX; i; i--)
+    if (c[i])
+      break;
+  g = i;                        /* maximum code length */
+  if ((uInt)l > i)
+    l = i;
+  *m = l;
+
+
+  /* Adjust last length count to fill out codes, if needed */
+  for (y = 1 << j; j < i; j++, y <<= 1)
+    if ((y -= c[j]) < 0)
+      return Z_DATA_ERROR;
+  if ((y -= c[i]) < 0)
+    return Z_DATA_ERROR;
+  c[i] += y;
+
+
+  /* Generate starting offsets into the value table for each length */
+  x[1] = j = 0;
+  p = c + 1;  xp = x + 2;
+  while (--i) {                 /* note that i == g from above */
+    *xp++ = (j += *p++);
+  }
+
+
+  /* Make a table of values in order of bit lengths */
+  p = b;  i = 0;
+  do {
+    if ((j = *p++) != 0)
+      v[x[j]++] = i;
+  } while (++i < n);
+  n = x[g];                   /* set n to length of v */
+
+
+  /* Generate the Huffman codes and for each, make the table entries */
+  x[0] = i = 0;                 /* first Huffman code is zero */
+  p = v;                        /* grab values in bit order */
+  h = -1;                       /* no tables yet--level -1 */
+  w = -l;                       /* bits decoded == (l * h) */
+  u[0] = (inflate_huft *)Z_NULL;        /* just to keep compilers happy */
+  q = (inflate_huft *)Z_NULL;   /* ditto */
+  z = 0;                        /* ditto */
+
+  /* go through the bit lengths (k already is bits in shortest code) */
+  for (; k <= g; k++)
+  {
+    a = c[k];
+    while (a--)
+    {
+      /* here i is the Huffman code of length k bits for value *p */
+      /* make tables up to required level */
+      while (k > w + l)
+      {
+        h++;
+        w += l;                 /* previous table always l bits */
+
+        /* compute minimum size table less than or equal to l bits */
+        z = g - w;
+        z = z > (uInt)l ? l : z;        /* table size upper limit */
+        if ((f = 1 << (j = k - w)) > a + 1)     /* try a k-w bit table */
+        {                       /* too few codes for k-w bit table */
+          f -= a + 1;           /* deduct codes from patterns left */
+          xp = c + k;
+          if (j < z)
+            while (++j < z)     /* try smaller tables up to z bits */
+            {
+              if ((f <<= 1) <= *++xp)
+                break;          /* enough codes to use up j bits */
+              f -= *xp;         /* else deduct codes from patterns */
+            }
+        }
+        z = 1 << j;             /* table entries for j-bit table */
+
+        /* allocate and link in new table */
+        if ((q = (inflate_huft *)ZALLOC
+             (zs,z + 1,sizeof(inflate_huft))) == Z_NULL)
+        {
+          if (h)
+            inflate_trees_free(u[0], zs);
+          return Z_MEM_ERROR;   /* not enough memory */
+        }
+#ifdef DEBUG_ZLIB
+        inflate_hufts += z + 1;
+#endif
+        *t = q + 1;             /* link to list for huft_free() */
+        *(t = &(q->next)) = Z_NULL;
+        u[h] = ++q;             /* table starts after link */
+
+        /* connect to last table, if there is one */
+        if (h)
+        {
+          x[h] = i;             /* save pattern for backing up */
+          r.bits = (Byte)l;     /* bits to dump before this table */
+          r.exop = (Byte)j;     /* bits in this table */
+          r.next = q;           /* pointer to this table */
+          j = i >> (w - l);     /* (get around Turbo C bug) */
+          u[h-1][j] = r;        /* connect to last table */
+        }
+      }
+
+      /* set up table entry in r */
+      r.bits = (Byte)(k - w);
+      if (p >= v + n)
+        r.exop = 128 + 64;      /* out of values--invalid code */
+      else if (*p < s)
+      {
+        r.exop = (Byte)(*p < 256 ? 0 : 32 + 64);     /* 256 is end-of-block */
+        r.base = *p++;          /* simple code is just the value */
+      }
+      else
+      {
+        r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
+        r.base = d[*p++ - s];
+      }
+
+      /* fill code-like entries with r */
+      f = 1 << (k - w);
+      for (j = i >> w; j < z; j += f)
+        q[j] = r;
+
+      /* backwards increment the k-bit code i */
+      for (j = 1 << (k - 1); i & j; j >>= 1)
+        i ^= j;
+      i ^= j;
+
+      /* backup over finished tables */
+      while ((i & ((1 << w) - 1)) != x[h])
+      {
+        h--;                    /* don't need to update q */
+        w -= l;
+      }
+    }
+  }
+
+
+  /* Return Z_BUF_ERROR if we were given an incomplete table */
+  return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+int inflate_trees_bits(c, bb, tb, z)
+uIntf *c;               /* 19 code lengths */
+uIntf *bb;              /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+z_streamp z;            /* for zfree function */
+{
+  int r;
+
+  r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z);
+  if (r == Z_DATA_ERROR)
+    z->msg = (char*)"oversubscribed dynamic bit lengths tree";
+  else if (r == Z_BUF_ERROR || *bb == 0)
+  {
+    inflate_trees_free(*tb, z);
+    z->msg = (char*)"incomplete dynamic bit lengths tree";
+    r = Z_DATA_ERROR;
+  }
+  return r;
+}
+
+
+int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z)
+uInt nl;                /* number of literal/length codes */
+uInt nd;                /* number of distance codes */
+uIntf *c;               /* that many (total) code lengths */
+uIntf *bl;              /* literal desired/actual bit depth */
+uIntf *bd;              /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+z_streamp z;            /* for zfree function */
+{
+  int r;
+
+  /* build literal/length tree */
+  r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z);
+  if (r != Z_OK || *bl == 0)
+  {
+    if (r == Z_DATA_ERROR)
+      z->msg = (char*)"oversubscribed literal/length tree";
+    else if (r != Z_MEM_ERROR)
+    {
+      inflate_trees_free(*tl, z);
+      z->msg = (char*)"incomplete literal/length tree";
+      r = Z_DATA_ERROR;
+    }
+    return r;
+  }
+
+  /* build distance tree */
+  r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z);
+  if (r != Z_OK || (*bd == 0 && nl > 257))
+  {
+    if (r == Z_DATA_ERROR)
+      z->msg = (char*)"oversubscribed distance tree";
+    else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+      r = Z_OK;
+    }
+#else
+      inflate_trees_free(*td, z);
+      z->msg = (char*)"incomplete distance tree";
+      r = Z_DATA_ERROR;
+    }
+    else if (r != Z_MEM_ERROR)
+    {
+      z->msg = (char*)"empty distance tree with lengths";
+      r = Z_DATA_ERROR;
+    }
+    inflate_trees_free(*tl, z);
+    return r;
+#endif
+  }
+
+  /* done */
+  return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+local int fixed_built = 0;
+#define FIXEDH 530      /* number of hufts used by fixed tables */
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+
+
+local voidpf falloc(q, n, s)
+voidpf q;       /* opaque pointer */
+uInt n;         /* number of items */
+uInt s;         /* size of item */
+{
+  Assert(s == sizeof(inflate_huft) && n <= *(intf *)q,
+         "inflate_trees falloc overflow");
+  *(intf *)q -= n+s-s; /* s-s to avoid warning */
+  return (voidpf)(fixed_mem + *(intf *)q);
+}
+
+
+int inflate_trees_fixed(bl, bd, tl, td)
+uIntf *bl;               /* literal desired/actual bit depth */
+uIntf *bd;               /* distance desired/actual bit depth */
+inflate_huft * FAR *tl;  /* literal/length tree result */
+inflate_huft * FAR *td;  /* distance tree result */
+{
+  /* build fixed tables if not already (multiple overlapped executions ok) */
+  if (!fixed_built)
+  {
+    int k;              /* temporary variable */
+    unsigned c[288];    /* length list for huft_build */
+    z_stream z;         /* for falloc function */
+    int f = FIXEDH;     /* number of hufts left in fixed_mem */
+
+    /* set up fake z_stream for memory routines */
+    z.zalloc = falloc;
+    z.zfree = Z_NULL;
+    z.opaque = (voidpf)&f;
+
+    /* literal table */
+    for (k = 0; k < 144; k++)
+      c[k] = 8;
+    for (; k < 256; k++)
+      c[k] = 9;
+    for (; k < 280; k++)
+      c[k] = 7;
+    for (; k < 288; k++)
+      c[k] = 8;
+    fixed_bl = 7;
+    huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z);
+
+    /* distance table */
+    for (k = 0; k < 30; k++)
+      c[k] = 5;
+    fixed_bd = 5;
+    huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z);
+
+    /* done */
+    Assert(f == 0, "invalid build of fixed tables");
+    fixed_built = 1;
+  }
+  *bl = fixed_bl;
+  *bd = fixed_bd;
+  *tl = fixed_tl;
+  *td = fixed_td;
+  return Z_OK;
+}
+
+
+int inflate_trees_free(t, z)
+inflate_huft *t;        /* table to free */
+z_streamp z;            /* for zfree function */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+   list of the tables it made, with the links in a dummy first entry of
+   each table. */
+{
+  register inflate_huft *p, *q, *r;
+
+  /* Reverse linked list */
+  p = Z_NULL;
+  q = t;
+  while (q != Z_NULL)
+  {
+    r = (q - 1)->next;
+    (q - 1)->next = p;
+    p = q;
+    q = r;
+  }
+  /* Go through linked list, freeing from the malloced (t[-1]) address. */
+  while (p != Z_NULL)
+  {
+    q = (--p)->next;
+    ZFREE(z,p);
+    p = q;
+  } 
+  return Z_OK;
+}
+/* --- inftrees.c */
+
+/* +++ infcodes.c */
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* #include "zutil.h" */
+/* #include "inftrees.h" */
+/* #include "infblock.h" */
+/* #include "infcodes.h" */
+/* #include "infutil.h" */
+
+/* +++ inffast.h */
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+extern int inflate_fast OF((
+    uInt,
+    uInt,
+    inflate_huft *,
+    inflate_huft *,
+    inflate_blocks_statef *,
+    z_streamp ));
+/* --- inffast.h */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+  /* mode */
+  enum {        /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+      START,    /* x: set up for LEN */
+      LEN,      /* i: get length/literal/eob next */
+      LENEXT,   /* i: getting length extra (have base) */
+      DIST,     /* i: get distance next */
+      DISTEXT,  /* i: getting distance extra */
+      COPY,     /* o: copying bytes in window, waiting for space */
+      LIT,      /* o: got literal, waiting for output space */
+      WASH,     /* o: got eob, possibly still output waiting */
+      END,      /* x: got eob and all data flushed */
+      BADCODE}  /* x: got error */
+    mode;               /* current inflate_codes mode */
+
+  /* mode dependent information */
+  uInt len;
+  union {
+    struct {
+      inflate_huft *tree;       /* pointer into tree */
+      uInt need;                /* bits needed */
+    } code;             /* if LEN or DIST, where in tree */
+    uInt lit;           /* if LIT, literal */
+    struct {
+      uInt get;                 /* bits to get for extra */
+      uInt dist;                /* distance back to copy from */
+    } copy;             /* if EXT or COPY, where and how much */
+  } sub;                /* submode */
+
+  /* mode independent information */
+  Byte lbits;           /* ltree bits decoded per branch */
+  Byte dbits;           /* dtree bits decoder per branch */
+  inflate_huft *ltree;          /* literal/length/eob tree */
+  inflate_huft *dtree;          /* distance tree */
+
+};
+
+
+inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+z_streamp z;
+{
+  inflate_codes_statef *c;
+
+  if ((c = (inflate_codes_statef *)
+       ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+  {
+    c->mode = START;
+    c->lbits = (Byte)bl;
+    c->dbits = (Byte)bd;
+    c->ltree = tl;
+    c->dtree = td;
+    Tracev((stderr, "inflate:       codes new\n"));
+  }
+  return c;
+}
+
+
+int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+  uInt j;               /* temporary storage */
+  inflate_huft *t;      /* temporary pointer */
+  uInt e;               /* extra bits or operation */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+  Bytef *f;             /* pointer to copy strings from */
+  inflate_codes_statef *c = s->sub.decode.codes;  /* codes state */
+
+  /* copy input/output information to locals (UPDATE macro restores) */
+  LOAD
+
+  /* process input and output based on current state */
+  while (1) switch (c->mode)
+  {             /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+    case START:         /* x: set up for LEN */
+#ifndef SLOW
+      if (m >= 258 && n >= 10)
+      {
+        UPDATE
+        r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+        LOAD
+        if (r != Z_OK)
+        {
+          c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+          break;
+        }
+      }
+#endif /* !SLOW */
+      c->sub.code.need = c->lbits;
+      c->sub.code.tree = c->ltree;
+      c->mode = LEN;
+    case LEN:           /* i: get length/literal/eob next */
+      j = c->sub.code.need;
+      NEEDBITS(j)
+      t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+      DUMPBITS(t->bits)
+      e = (uInt)(t->exop);
+      if (e == 0)               /* literal */
+      {
+        c->sub.lit = t->base;
+        Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                 "inflate:         literal '%c'\n" :
+                 "inflate:         literal 0x%02x\n", t->base));
+        c->mode = LIT;
+        break;
+      }
+      if (e & 16)               /* length */
+      {
+        c->sub.copy.get = e & 15;
+        c->len = t->base;
+        c->mode = LENEXT;
+        break;
+      }
+      if ((e & 64) == 0)        /* next table */
+      {
+        c->sub.code.need = e;
+        c->sub.code.tree = t->next;
+        break;
+      }
+      if (e & 32)               /* end of block */
+      {
+        Tracevv((stderr, "inflate:         end of block\n"));
+        c->mode = WASH;
+        break;
+      }
+      c->mode = BADCODE;        /* invalid code */
+      z->msg = (char*)"invalid literal/length code";
+      r = Z_DATA_ERROR;
+      LEAVE
+    case LENEXT:        /* i: getting length extra (have base) */
+      j = c->sub.copy.get;
+      NEEDBITS(j)
+      c->len += (uInt)b & inflate_mask[j];
+      DUMPBITS(j)
+      c->sub.code.need = c->dbits;
+      c->sub.code.tree = c->dtree;
+      Tracevv((stderr, "inflate:         length %u\n", c->len));
+      c->mode = DIST;
+    case DIST:          /* i: get distance next */
+      j = c->sub.code.need;
+      NEEDBITS(j)
+      t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+      DUMPBITS(t->bits)
+      e = (uInt)(t->exop);
+      if (e & 16)               /* distance */
+      {
+        c->sub.copy.get = e & 15;
+        c->sub.copy.dist = t->base;
+        c->mode = DISTEXT;
+        break;
+      }
+      if ((e & 64) == 0)        /* next table */
+      {
+        c->sub.code.need = e;
+        c->sub.code.tree = t->next;
+        break;
+      }
+      c->mode = BADCODE;        /* invalid code */
+      z->msg = (char*)"invalid distance code";
+      r = Z_DATA_ERROR;
+      LEAVE
+    case DISTEXT:       /* i: getting distance extra */
+      j = c->sub.copy.get;
+      NEEDBITS(j)
+      c->sub.copy.dist += (uInt)b & inflate_mask[j];
+      DUMPBITS(j)
+      Tracevv((stderr, "inflate:         distance %u\n", c->sub.copy.dist));
+      c->mode = COPY;
+    case COPY:          /* o: copying bytes in window, waiting for space */
+#ifndef __TURBOC__ /* Turbo C bug for following expression */
+      f = (uInt)(q - s->window) < c->sub.copy.dist ?
+          s->end - (c->sub.copy.dist - (q - s->window)) :
+          q - c->sub.copy.dist;
+#else
+      f = q - c->sub.copy.dist;
+      if ((uInt)(q - s->window) < c->sub.copy.dist)
+        f = s->end - (c->sub.copy.dist - (uInt)(q - s->window));
+#endif
+      while (c->len)
+      {
+        NEEDOUT
+        OUTBYTE(*f++)
+        if (f == s->end)
+          f = s->window;
+        c->len--;
+      }
+      c->mode = START;
+      break;
+    case LIT:           /* o: got literal, waiting for output space */
+      NEEDOUT
+      OUTBYTE(c->sub.lit)
+      c->mode = START;
+      break;
+    case WASH:          /* o: got eob, possibly more output */
+      FLUSH
+      if (s->read != s->write)
+        LEAVE
+      c->mode = END;
+    case END:
+      r = Z_STREAM_END;
+      LEAVE
+    case BADCODE:       /* x: got error */
+      r = Z_DATA_ERROR;
+      LEAVE
+    default:
+      r = Z_STREAM_ERROR;
+      LEAVE
+  }
+}
+
+
+void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_streamp z;
+{
+  ZFREE(z, c);
+  Tracev((stderr, "inflate:       codes free\n"));
+}
+/* --- infcodes.c */
+
+/* +++ infutil.c */
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* #include "zutil.h" */
+/* #include "infblock.h" */
+/* #include "inftrees.h" */
+/* #include "infcodes.h" */
+/* #include "infutil.h" */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* And'ing with mask[n] masks the lower n bits */
+uInt inflate_mask[17] = {
+    0x0000,
+    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+
+/* copy as much as possible from the sliding window to the output area */
+int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+  uInt n;
+  Bytef *p;
+  Bytef *q;
+
+  /* local copies of source and destination pointers */
+  p = z->next_out;
+  q = s->read;
+
+  /* compute number of bytes to copy as far as end of window */
+  n = (uInt)((q <= s->write ? s->write : s->end) - q);
+  if (n > z->avail_out) n = z->avail_out;
+  if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+  /* update counters */
+  z->avail_out -= n;
+  z->total_out += n;
+
+  /* update check information */
+  if (s->checkfn != Z_NULL)
+    z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+  /* copy as far as end of window */
+  if (p != Z_NULL) {
+    zmemcpy(p, q, n);
+    p += n;
+  }
+  q += n;
+
+  /* see if more to copy at beginning of window */
+  if (q == s->end)
+  {
+    /* wrap pointers */
+    q = s->window;
+    if (s->write == s->end)
+      s->write = s->window;
+
+    /* compute bytes to copy */
+    n = (uInt)(s->write - q);
+    if (n > z->avail_out) n = z->avail_out;
+    if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+    /* update counters */
+    z->avail_out -= n;
+    z->total_out += n;
+
+    /* update check information */
+    if (s->checkfn != Z_NULL)
+      z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+    /* copy */
+    if (p != Z_NULL) {
+      zmemcpy(p, q, n);
+      p += n;
+    }
+    q += n;
+  }
+
+  /* update pointers */
+  z->next_out = p;
+  s->read = q;
+
+  /* done */
+  return r;
+}
+/* --- infutil.c */
+
+/* +++ inffast.c */
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* #include "zutil.h" */
+/* #include "inftrees.h" */
+/* #include "infblock.h" */
+/* #include "infcodes.h" */
+/* #include "infutil.h" */
+/* #include "inffast.h" */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<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/ap/app/pppd/common/zlib.h b/ap/app/pppd/common/zlib.h
new file mode 100644
index 0000000..c6bbd24
--- /dev/null
+++ b/ap/app/pppd/common/zlib.h
@@ -0,0 +1,1010 @@
+/*	$Id: zlib.h,v 1.2 2007-06-08 04:02:37 gerg Exp $	*/
+
+/*
+ * This file is derived from zlib.h and zconf.h from the zlib-1.0.4
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.
+ */
+
+/*
+ *  ==FILEVERSION 971127==
+ *
+ * This marker is used by the Linux installation script to determine
+ * whether an up-to-date version of this file is already installed.
+ */
+
+
+/* +++ zlib.h */
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.0.4, Jul 24th, 1996.
+
+  Copyright (C) 1995-1996 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  gzip@prep.ai.mit.edu    madler@alumni.caltech.edu
+
+
+  The data format used by the zlib library is described by RFCs (Request for
+  Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+  (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* +++ zconf.h */
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* From: zconf.h,v 1.20 1996/07/02 15:09:28 me Exp $ */
+
+#ifndef _ZCONF_H
+#define _ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+#  define deflateInit_	z_deflateInit_
+#  define deflate	z_deflate
+#  define deflateEnd	z_deflateEnd
+#  define inflateInit_ 	z_inflateInit_
+#  define inflate	z_inflate
+#  define inflateEnd	z_inflateEnd
+#  define deflateInit2_	z_deflateInit2_
+#  define deflateSetDictionary z_deflateSetDictionary
+#  define deflateCopy	z_deflateCopy
+#  define deflateReset	z_deflateReset
+#  define deflateParams	z_deflateParams
+#  define inflateInit2_	z_inflateInit2_
+#  define inflateSetDictionary z_inflateSetDictionary
+#  define inflateSync	z_inflateSync
+#  define inflateReset	z_inflateReset
+#  define compress	z_compress
+#  define uncompress	z_uncompress
+#  define adler32	z_adler32
+#  define crc32		z_crc32
+#  define get_crc_table z_get_crc_table
+
+#  define Byte		z_Byte
+#  define uInt		z_uInt
+#  define uLong		z_uLong
+#  define Bytef	        z_Bytef
+#  define charf		z_charf
+#  define intf		z_intf
+#  define uIntf		z_uIntf
+#  define uLongf	z_uLongf
+#  define voidpf	z_voidpf
+#  define voidp		z_voidp
+#endif
+
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+#  define WIN32
+#endif
+#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
+#  ifndef __32BIT__
+#    define __32BIT__
+#  endif
+#endif
+#if defined(__MSDOS__) && !defined(MSDOS)
+#  define MSDOS
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#if defined(MSDOS) && !defined(__32BIT__)
+#  define MAXSEG_64K
+#endif
+#ifdef MSDOS
+#  define UNALIGNED_OK
+#endif
+
+#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32))  && !defined(STDC)
+#  define STDC
+#endif
+#if (defined(__STDC__) || defined(__cplusplus)) && !defined(STDC)
+#  define STDC
+#endif
+
+#ifndef STDC
+#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+#    define const
+#  endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
+#  define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2 */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            1 << (windowBits+2)   +  1 << (memLevel+9)
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
+   /* MSC small or medium model */
+#  define SMALL_MEDIUM
+#  ifdef _MSC_VER
+#    define FAR __far
+#  else
+#    define FAR far
+#  endif
+#endif
+#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
+#  ifndef __32BIT__
+#    define SMALL_MEDIUM
+#    define FAR __far
+#  endif
+#endif
+#ifndef FAR
+#   define FAR
+#endif
+
+typedef unsigned char  Byte;  /* 8 bits */
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+#if defined(__BORLANDC__) && defined(SMALL_MEDIUM)
+   /* Borland C/C++ ignores FAR inside typedef */
+#  define Bytef Byte FAR
+#else
+   typedef Byte  FAR Bytef;
+#endif
+typedef char  FAR charf;
+typedef int   FAR intf;
+typedef uInt  FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void FAR *voidpf;
+   typedef void     *voidp;
+#else
+   typedef Byte FAR *voidpf;
+   typedef Byte     *voidp;
+#endif
+
+
+/* Compile with -DZLIB_DLL for Windows DLL support */
+#if (defined(_WINDOWS) || defined(WINDOWS)) && defined(ZLIB_DLL)
+#  include <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/ap/app/pppd/configure b/ap/app/pppd/configure
new file mode 100644
index 0000000..cea27bf
--- /dev/null
+++ b/ap/app/pppd/configure
@@ -0,0 +1,207 @@
+#!/bin/sh
+# $Id: configure,v 1.38 2008/06/15 07:08:49 paulus Exp $
+
+# Where to install stuff by default
+DESTDIR=/usr/local
+SYSCONF=/etc
+
+#  if [ -d /NextApps ]; then
+#    system="NeXTStep"
+#  else
+  system=`uname -s`
+  release=`uname -r`
+  arch=`uname -m`
+#  fi
+state="unknown"
+
+case $system in
+  Linux)
+    makext="linux";
+    ksrc="linux";
+    state="known";;
+  SunOS)
+    karch=`/usr/bin/isainfo -k`
+    case $release in
+#      [0-3]*)	state="ancient";;
+#      4*)	state="known"; ksrc="sunos4"; makext="sunos4";;
+      5.[7-9]*|5.[1-9][0-9]*)	state="known"; ksrc="solaris"; makext="sol2";
+              case "$karch" in
+		amd64)		archvariant='-64x';;
+		sparcv9)	archvariant='-64';;
+		*)	;;
+	      esac;;
+      5.[1-6]*)	state="known"; ksrc="solaris"; makext="sol2";;
+    esac
+    if [ "$1" = gcc ]; then
+	shift
+	usegcc=gcc
+    fi
+    if [ "$1" = 32 ]; then
+	shift
+	archvariant=
+    fi
+    if [ -x /opt/SUNWspro/bin/cc -a "$usegcc" != gcc ] &&
+       /opt/SUNWspro/bin/cc -flags >/dev/null 2>&1; then
+      if [ "$archvariant" = "-64x" ]; then
+        ( cd /tmp; echo "int x;" > ppp$$.c
+	  /opt/SUNWspro/bin/cc -c -errwarn -xchip=opteron -xarch=amd64 ppp$$.c >/dev/null 2>&1 || (
+	    echo "WorkShop C is unable to make 64 bit modules, and your $karch system needs"
+	    echo "them.  Consider upgrading cc on this machine."
+	    rm -f ppp$$.c
+	    exit 1
+	  ) || exit 1
+	  rm -f ppp$$.c ppp$$.o
+        ) || exit 1
+      fi
+    elif gcc --version >/dev/null 2>&1; then
+      archvariant=gcc$archvariant
+      compiletype=.gcc
+      if [ "$archvariant" = "gcc-64" -o"$archvariant" = "gcc-64x" ]; then
+        ( cd /tmp; touch ppp$$.c
+	  gcc -c -m64 ppp$$.c >/dev/null 2>&1 || (
+	    echo "gcc is unable to make 64 bit modules, and your $karch system needs them."
+	    echo "Consider upgrading gcc on this machine, or switching to Sun WorkShop."
+	    rm -f ppp$$.c
+	    exit 1
+	  ) || exit 1
+	  rm -f ppp$$.c ppp$$.o
+        ) || exit 1
+      fi
+    else
+      echo "C compiler not found; hoping for the best."
+    fi;;
+  NetBSD|FreeBSD|ULTRIX|OSF1|NeXTStep|SINIX-?|UNIX_SV|UNIX_System_V)
+    state="notincluded";;
+#    NetBSD)
+#      makext="bsd";
+#      case $release in
+#        0.*)	state="ancient";;
+#        1.0*)	state="ancient";;
+#        1.1*)	state="known"; ksrc="netbsd-1.1";;
+#        1.2*)	state="known"; ksrc="netbsd-1.2"; makext="netbsd-1.2";;
+#        1.[3-9]*|[2-9]*)
+#  		state="late"; ksrc="netbsd-1.2";;
+#      esac;;
+#    ULTRIX)
+#      makext="ultrix";
+#      case $release in
+#        [0-3]*)	state="ancient";;
+#        4.[01]*)	state="early"; ksrc="ultrix";;
+#        4.[234])	state="known"; ksrc="ultrix";;
+#      esac;;
+#    OSF1)
+#      makext="osf";
+#      case $release in
+#        V1.*)   state="neolithic"; ksrc="osf1";;
+#        V[23].*)	state="neolithic"; ksrc="osf1";;
+#        V4.*)	state="known"; ksrc="osf1";;
+#        V[5-9]*) state="late"; ksrc="osf1";;
+#      esac;;
+#    FreeBSD)
+#      makext="bsd";
+#      case $release in
+#        1.*)	state="known"; ksrc="freebsd-old";;
+#        2.[01]*)	state="known"; ksrc="freebsd-2.0";;
+#        2.2.[2-7]*) state="late"; ksrc="freebsd-2.0";;
+#        2.2.8*)   state="known"; ksrc="freebsd-2.2.8";;
+#        3.[0-1]*)	state="known"; ksrc="freebsd-3.0";;
+#      esac;;
+#    NeXTStep)
+#      makext="NeXT";
+#      ksrc="NeXT";
+#      state="known";;
+#    SINIX-?)
+#      case $release in
+#        5.4[01]) state=known; ksrc=svr4; makext=svr4;;
+#        5.4[2-9]) state=late; ksrc=svr4; makext=svr4;;
+#      esac;;
+#    # Intel SVR4 systems come with a bug in the uname program.  Unless
+#    # your provider fixed the bug, or you get a fix for it, uname -S will
+#    # overwrite the system name with the node name!
+#    UNIX_SV|UNIX_System_V|`uname -n`)
+#      case $release in
+#        4.0) state=known; ksrc=svr4; makext=svr4;;
+#        4.2) state=late; ksrc=svr4; makext=svr4;;
+#      esac;;
+esac
+
+if [ -d "$ksrc" ]; then :; else
+  state="notincluded"
+  unset ksrc
+fi
+
+case $state in
+  neolithic) 
+    echo "This is a newer release on an outdated OS ($system)."
+    echo " This software may or may not work on this OS."
+    echo " You may want to download an older version of PPP for this OS.";;
+  ancient)
+    echo "This is an old release of a supported OS ($system)."
+    echo "This software cannot be used as-is on this system,"
+    echo "but you may be able to port it.  Good luck!"
+    exit;;
+  early)
+    echo "This is an old release of a supported OS ($system)."
+    echo "This software should install and run on this system,"
+    echo "but it hasn't been tested.";;
+  late)
+    echo "This is a newer release of $system than is supported by"
+    echo "this software.  It may or may not work.";;
+  unknown)
+    echo "This software has not been ported to $system.  Sorry.";;
+  notincluded)
+    echo "Support for $system has not been included"
+    echo "in this distribution.  Sorry.";;
+  known)
+    echo "Configuring for $system";;
+esac
+
+# Parse arguments
+while [ $# -gt 0 ]; do
+    arg=$1
+    val=
+    shift
+    case $arg in
+	*=*)
+	    val=`expr "x$arg" : 'x[^=]*=\(.*\)'`
+	    arg=`expr "x$arg" : 'x\([^=]*\)=.*'`
+	    ;;
+	--prefix|--sysconf)
+	    if [ $# -eq 0 ]; then
+		echo "error: the $arg argument requires a value" 1>&2
+		exit 1
+	    fi
+	    val=$1
+	    shift
+	    ;;
+    esac
+    case $arg in
+	--prefix)	DESTDIR=$val ;;
+	--sysconfdir)	SYSCONF=$val ;;
+    esac
+done
+
+mkmkf() {
+    rm -f $2
+    if [ -f $1 ]; then
+	echo "  $2 <= $1"
+	sed -e "s,@DESTDIR@,$DESTDIR,g" -e "s,@SYSCONF@,$SYSCONF,g" $1 >$2
+    fi
+}
+
+if [ -d "$ksrc" ]; then
+    echo "Creating Makefiles."
+    mkmkf $ksrc/Makefile.top Makefile
+    mkmkf $ksrc/Makedefs$compiletype Makedefs.com
+    for dir in pppd pppstats chat pppdump pppd/plugins pppd/plugins/rp-pppoe \
+	       pppd/plugins/radius pppd/plugins/pppoatm \
+	       pppd/plugins/pppol2tp pppd/plugins/pptp ; do
+	mkmkf $dir/Makefile.$makext $dir/Makefile
+    done
+    if [ -f $ksrc/Makefile.$makext$archvariant ]; then
+	mkmkf $ksrc/Makefile.$makext$archvariant $ksrc/Makefile
+    fi
+else
+  echo "Unable to locate kernel source $ksrc"
+  exit 1
+fi
diff --git a/ap/app/pppd/contrib/pppgetpass/Makefile.linux b/ap/app/pppd/contrib/pppgetpass/Makefile.linux
new file mode 100644
index 0000000..7eb217d
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/contrib/pppgetpass/pppgetpass.8 b/ap/app/pppd/contrib/pppgetpass/pppgetpass.8
new file mode 100644
index 0000000..ade5769
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/contrib/pppgetpass/pppgetpass.gtk.c b/ap/app/pppd/contrib/pppgetpass/pppgetpass.gtk.c
new file mode 100644
index 0000000..48ca042
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/contrib/pppgetpass/pppgetpass.sh b/ap/app/pppd/contrib/pppgetpass/pppgetpass.sh
new file mode 100644
index 0000000..09c4805
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/contrib/pppgetpass/pppgetpass.vt.c b/ap/app/pppd/contrib/pppgetpass/pppgetpass.vt.c
new file mode 100644
index 0000000..a152088
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/etc.ppp/chap-secrets b/ap/app/pppd/etc.ppp/chap-secrets
new file mode 100644
index 0000000..7d1c3cd
--- /dev/null
+++ b/ap/app/pppd/etc.ppp/chap-secrets
@@ -0,0 +1,2 @@
+# Secrets for authentication using CHAP
+# client	server	secret			IP addresses
diff --git a/ap/app/pppd/etc.ppp/options b/ap/app/pppd/etc.ppp/options
new file mode 100644
index 0000000..4b67b6a
--- /dev/null
+++ b/ap/app/pppd/etc.ppp/options
@@ -0,0 +1 @@
+lock
diff --git a/ap/app/pppd/etc.ppp/pap-secrets b/ap/app/pppd/etc.ppp/pap-secrets
new file mode 100644
index 0000000..f8b7dce
--- /dev/null
+++ b/ap/app/pppd/etc.ppp/pap-secrets
@@ -0,0 +1,2 @@
+# Secrets for authentication using PAP
+# client	server	secret			IP addresses
diff --git a/ap/app/pppd/include/linux/if_ppp.h b/ap/app/pppd/include/linux/if_ppp.h
new file mode 100644
index 0000000..0011407
--- /dev/null
+++ b/ap/app/pppd/include/linux/if_ppp.h
@@ -0,0 +1,178 @@
+/*	$Id: if_ppp.h,v 1.2 2007-06-08 04:02:37 gerg Exp $	*/
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ *  ==FILEVERSION 20000724==
+ *
+ *  NOTE TO MAINTAINERS:
+ *     If you modify this file at all, please set the above date.
+ *     if_ppp.h is shipped with a PPP distribution as well as with the kernel;
+ *     if everyone increases the FILEVERSION number above, then scripts
+ *     can do the right thing when deciding whether to install a new if_ppp.h
+ *     file.  Don't change the format of that line otherwise, so the
+ *     installation script can recognize it.
+ */
+
+#ifndef _IF_PPP_H_
+#define _IF_PPP_H_
+
+/*
+ * Packet sizes
+ */
+
+#define	PPP_MTU		1500	/* Default MTU (size of Info field) */
+#define PPP_MAXMRU	65000	/* Largest MRU we allow */
+#define PROTO_IPX	0x002b	/* protocol numbers */
+#define PROTO_DNA_RT    0x0027  /* DNA Routing */
+
+
+/*
+ * Bit definitions for flags.
+ */
+
+#define SC_COMP_PROT	0x00000001	/* protocol compression (output) */
+#define SC_COMP_AC	0x00000002	/* header compression (output) */
+#define	SC_COMP_TCP	0x00000004	/* TCP (VJ) compression (output) */
+#define SC_NO_TCP_CCID	0x00000008	/* disable VJ connection-id comp. */
+#define SC_REJ_COMP_AC	0x00000010	/* reject adrs/ctrl comp. on input */
+#define SC_REJ_COMP_TCP	0x00000020	/* reject TCP (VJ) comp. on input */
+#define SC_CCP_OPEN	0x00000040	/* Look at CCP packets */
+#define SC_CCP_UP	0x00000080	/* May send/recv compressed packets */
+#define SC_ENABLE_IP	0x00000100	/* IP packets may be exchanged */
+#define SC_LOOP_TRAFFIC	0x00000200	/* send traffic to pppd */
+#define SC_MULTILINK	0x00000400	/* do multilink encapsulation */
+#define SC_MP_SHORTSEQ	0x00000800	/* use short MP sequence numbers */
+#define SC_COMP_RUN	0x00001000	/* compressor has been inited */
+#define SC_DECOMP_RUN	0x00002000	/* decompressor has been inited */
+#define SC_MP_XSHORTSEQ	0x00004000	/* transmit short MP seq numbers */
+#define SC_DEBUG	0x00010000	/* enable debug messages */
+#define SC_LOG_INPKT	0x00020000	/* log contents of good pkts recvd */
+#define SC_LOG_OUTPKT	0x00040000	/* log contents of pkts sent */
+#define SC_LOG_RAWIN	0x00080000	/* log all chars received */
+#define SC_LOG_FLUSH	0x00100000	/* log all chars flushed */
+#define	SC_SYNC		0x00200000	/* synchronous serial mode */
+#define	SC_MASK		0x0f200fff	/* bits that user can change */
+
+/* state bits */
+#define SC_XMIT_BUSY	0x10000000	/* (used by isdn_ppp?) */
+#define SC_RCV_ODDP	0x08000000	/* have rcvd char with odd parity */
+#define SC_RCV_EVNP	0x04000000	/* have rcvd char with even parity */
+#define SC_RCV_B7_1	0x02000000	/* have rcvd char with bit 7 = 1 */
+#define SC_RCV_B7_0	0x01000000	/* have rcvd char with bit 7 = 0 */
+#define SC_DC_FERROR	0x00800000	/* fatal decomp error detected */
+#define SC_DC_ERROR	0x00400000	/* non-fatal decomp error detected */
+
+/*
+ * Ioctl definitions.
+ */
+
+struct npioctl {
+	int		protocol;	/* PPP protocol, e.g. PPP_IP */
+	enum NPmode	mode;
+};
+
+/* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */
+struct ppp_option_data {
+	__u8	*ptr;
+	__u32	length;
+	int	transmit;
+};
+
+struct ifpppstatsreq {
+	struct ifreq	 b;
+	struct ppp_stats stats;			/* statistic information */
+};
+
+struct ifpppcstatsreq {
+	struct ifreq	      b;
+	struct ppp_comp_stats stats;
+};
+
+#define ifr__name       b.ifr_ifrn.ifrn_name
+#define stats_ptr       b.ifr_ifru.ifru_data
+
+/*
+ * Ioctl definitions.
+ */
+
+#define	PPPIOCGFLAGS	_IOR('t', 90, int)	/* get configuration flags */
+#define	PPPIOCSFLAGS	_IOW('t', 89, int)	/* set configuration flags */
+#define	PPPIOCGASYNCMAP	_IOR('t', 88, int)	/* get async map */
+#define	PPPIOCSASYNCMAP	_IOW('t', 87, int)	/* set async map */
+#define	PPPIOCGUNIT	_IOR('t', 86, int)	/* get ppp unit number */
+#define	PPPIOCGRASYNCMAP _IOR('t', 85, int)	/* get receive async map */
+#define	PPPIOCSRASYNCMAP _IOW('t', 84, int)	/* set receive async map */
+#define	PPPIOCGMRU	_IOR('t', 83, int)	/* get max receive unit */
+#define	PPPIOCSMRU	_IOW('t', 82, int)	/* set max receive unit */
+#define	PPPIOCSMAXCID	_IOW('t', 81, int)	/* set VJ max slot ID */
+#define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */
+#define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */
+#define PPPIOCXFERUNIT	_IO('t', 78)		/* transfer PPP unit */
+#define PPPIOCSCOMPRESS	_IOW('t', 77, struct ppp_option_data)
+#define PPPIOCGNPMODE	_IOWR('t', 76, struct npioctl) /* get NP mode */
+#define PPPIOCSNPMODE	_IOW('t', 75, struct npioctl)  /* set NP mode */
+#define PPPIOCSPASS	_IOW('t', 71, struct sock_fprog) /* set pass filter */
+#define PPPIOCSACTIVE	_IOW('t', 70, struct sock_fprog) /* set active filt */
+#define PPPIOCGDEBUG	_IOR('t', 65, int)	/* Read debug level */
+#define PPPIOCSDEBUG	_IOW('t', 64, int)	/* Set debug level */
+#define PPPIOCGIDLE	_IOR('t', 63, struct ppp_idle) /* get idle time */
+#define PPPIOCNEWUNIT	_IOWR('t', 62, int)	/* create new ppp unit */
+#define PPPIOCATTACH	_IOW('t', 61, int)	/* attach to ppp unit */
+#define PPPIOCDETACH	_IOW('t', 60, int)	/* detach from ppp unit/chan */
+#define PPPIOCSMRRU	_IOW('t', 59, int)	/* set multilink MRU */
+#define PPPIOCCONNECT	_IOW('t', 58, int)	/* connect channel to unit */
+#define PPPIOCDISCONN	_IO('t', 57)		/* disconnect channel */
+#define PPPIOCATTCHAN	_IOW('t', 56, int)	/* attach to ppp channel */
+#define PPPIOCGCHAN	_IOR('t', 55, int)	/* get ppp channel number */
+
+#define SIOCGPPPSTATS   (SIOCDEVPRIVATE + 0)
+#define SIOCGPPPVER     (SIOCDEVPRIVATE + 1)	/* NEVER change this!! */
+#define SIOCGPPPCSTATS  (SIOCDEVPRIVATE + 2)
+
+#if !defined(ifr_mtu)
+#define ifr_mtu	ifr_ifru.ifru_metric
+#endif
+
+#endif /* _IF_PPP_H_ */
diff --git a/ap/app/pppd/include/linux/if_pppol2tp.h b/ap/app/pppd/include/linux/if_pppol2tp.h
new file mode 100644
index 0000000..8e1221c
--- /dev/null
+++ b/ap/app/pppd/include/linux/if_pppol2tp.h
@@ -0,0 +1,104 @@
+/***************************************************************************
+ * Linux PPP over L2TP (PPPoL2TP) Socket Implementation (RFC 2661)
+ *
+ * This file supplies definitions required by the PPP over L2TP driver
+ * (l2tp_ppp.c).  All version information wrt this file is located in l2tp_ppp.c
+ *
+ * License:
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef __LINUX_IF_PPPOL2TP_H
+#define __LINUX_IF_PPPOL2TP_H
+
+#include <linux/types.h>
+
+
+/* Structure used to connect() the socket to a particular tunnel UDP
+ * socket over IPv4.
+ */
+struct pppol2tp_addr {
+	__kernel_pid_t	pid;		/* pid that owns the fd.
+					 * 0 => current */
+	int	fd;			/* FD of UDP socket to use */
+
+	struct sockaddr_in addr;	/* IP address and port to send to */
+
+	__u16 s_tunnel, s_session;	/* For matching incoming packets */
+	__u16 d_tunnel, d_session;	/* For sending outgoing packets */
+};
+
+/* Structure used to connect() the socket to a particular tunnel UDP
+ * socket over IPv6.
+ */
+struct pppol2tpin6_addr {
+	__kernel_pid_t	pid;		/* pid that owns the fd.
+					 * 0 => current */
+	int	fd;			/* FD of UDP socket to use */
+
+	__u16 s_tunnel, s_session;	/* For matching incoming packets */
+	__u16 d_tunnel, d_session;	/* For sending outgoing packets */
+
+	struct sockaddr_in6 addr;	/* IP address and port to send to */
+};
+
+/* The L2TPv3 protocol changes tunnel and session ids from 16 to 32
+ * bits. So we need a different sockaddr structure.
+ */
+struct pppol2tpv3_addr {
+	__kernel_pid_t	pid;		/* pid that owns the fd.
+					 * 0 => current */
+	int	fd;			/* FD of UDP or IP socket to use */
+
+	struct sockaddr_in addr;	/* IP address and port to send to */
+
+	__u32 s_tunnel, s_session;	/* For matching incoming packets */
+	__u32 d_tunnel, d_session;	/* For sending outgoing packets */
+};
+
+struct pppol2tpv3in6_addr {
+	__kernel_pid_t	pid;		/* pid that owns the fd.
+					 * 0 => current */
+	int	fd;			/* FD of UDP or IP socket to use */
+
+	__u32 s_tunnel, s_session;	/* For matching incoming packets */
+	__u32 d_tunnel, d_session;	/* For sending outgoing packets */
+
+	struct sockaddr_in6 addr;	/* IP address and port to send to */
+};
+
+/* Socket options:
+ * DEBUG	- bitmask of debug message categories
+ * SENDSEQ	- 0 => don't send packets with sequence numbers
+ *		  1 => send packets with sequence numbers
+ * RECVSEQ	- 0 => receive packet sequence numbers are optional
+ *		  1 => drop receive packets without sequence numbers
+ * LNSMODE	- 0 => act as LAC.
+ *		  1 => act as LNS.
+ * REORDERTO	- reorder timeout (in millisecs). If 0, don't try to reorder.
+ */
+enum {
+	PPPOL2TP_SO_DEBUG	= 1,
+	PPPOL2TP_SO_RECVSEQ	= 2,
+	PPPOL2TP_SO_SENDSEQ	= 3,
+	PPPOL2TP_SO_LNSMODE	= 4,
+	PPPOL2TP_SO_REORDERTO	= 5,
+};
+
+/* Debug message categories for the DEBUG socket option */
+enum {
+	PPPOL2TP_MSG_DEBUG	= (1 << 0),	/* verbose debug (if
+						 * compiled in) */
+	PPPOL2TP_MSG_CONTROL	= (1 << 1),	/* userspace - kernel
+						 * interface */
+	PPPOL2TP_MSG_SEQ	= (1 << 2),	/* sequence numbers */
+	PPPOL2TP_MSG_DATA	= (1 << 3),	/* data packets */
+};
+
+
+
+#endif /* __LINUX_IF_PPPOL2TP_H */
diff --git a/ap/app/pppd/include/linux/ppp-comp.h b/ap/app/pppd/include/linux/ppp-comp.h
new file mode 100644
index 0000000..a776cb6
--- /dev/null
+++ b/ap/app/pppd/include/linux/ppp-comp.h
@@ -0,0 +1,213 @@
+/*
+ * ppp-comp.h - Definitions for doing PPP packet compression.
+ *
+ * Copyright (c) 1984 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp-comp.h,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+/*
+ *  ==FILEVERSION 20020319==
+ *
+ *  NOTE TO MAINTAINERS:
+ *     If you modify this file at all, please set the above date.
+ *     ppp-comp.h is shipped with a PPP distribution as well as with the kernel;
+ *     if everyone increases the FILEVERSION number above, then scripts
+ *     can do the right thing when deciding whether to install a new ppp-comp.h
+ *     file.  Don't change the format of that line otherwise, so the
+ *     installation script can recognize it.
+ */
+
+#ifndef _NET_PPP_COMP_H
+#define _NET_PPP_COMP_H
+
+/*
+ * The following symbols control whether we include code for
+ * various compression methods.
+ */
+
+#ifndef DO_BSD_COMPRESS
+#define DO_BSD_COMPRESS	1	/* by default, include BSD-Compress */
+#endif
+#ifndef DO_DEFLATE
+#define DO_DEFLATE	1	/* by default, include Deflate */
+#endif
+#define DO_PREDICTOR_1	0
+#define DO_PREDICTOR_2	0
+
+/*
+ * Structure giving methods for compression/decompression.
+ */
+
+struct compressor {
+	int	compress_proto;	/* CCP compression protocol number */
+
+	/* Allocate space for a compressor (transmit side) */
+	void	*(*comp_alloc) (unsigned char *options, int opt_len);
+
+	/* Free space used by a compressor */
+	void	(*comp_free) (void *state);
+
+	/* Initialize a compressor */
+	int	(*comp_init) (void *state, unsigned char *options,
+			      int opt_len, int unit, int opthdr, int debug);
+
+	/* Reset a compressor */
+	void	(*comp_reset) (void *state);
+
+	/* Compress a packet */
+	int     (*compress) (void *state, unsigned char *rptr,
+			      unsigned char *obuf, int isize, int osize);
+
+	/* Return compression statistics */
+	void	(*comp_stat) (void *state, struct compstat *stats);
+
+	/* Allocate space for a decompressor (receive side) */
+	void	*(*decomp_alloc) (unsigned char *options, int opt_len);
+
+	/* Free space used by a decompressor */
+	void	(*decomp_free) (void *state);
+
+	/* Initialize a decompressor */
+	int	(*decomp_init) (void *state, unsigned char *options,
+				int opt_len, int unit, int opthdr, int mru,
+				int debug);
+
+	/* Reset a decompressor */
+	void	(*decomp_reset) (void *state);
+
+	/* Decompress a packet. */
+	int	(*decompress) (void *state, unsigned char *ibuf, int isize,
+				unsigned char *obuf, int osize);
+
+	/* Update state for an incompressible packet received */
+	void	(*incomp) (void *state, unsigned char *ibuf, int icnt);
+
+	/* Return decompression statistics */
+	void	(*decomp_stat) (void *state, struct compstat *stats);
+};
+
+/*
+ * The return value from decompress routine is the length of the
+ * decompressed packet if successful, otherwise DECOMP_ERROR
+ * or DECOMP_FATALERROR if an error occurred.
+ * 
+ * We need to make this distinction so that we can disable certain
+ * useful functionality, namely sending a CCP reset-request as a result
+ * of an error detected after decompression.  This is to avoid infringing
+ * a patent held by Motorola.
+ * Don't you just lurve software patents.
+ */
+
+#define DECOMP_ERROR		-1	/* error detected before decomp. */
+#define DECOMP_FATALERROR	-2	/* error detected after decomp. */
+
+/*
+ * CCP codes.
+ */
+
+#define CCP_CONFREQ	1
+#define CCP_CONFACK	2
+#define CCP_TERMREQ	5
+#define CCP_TERMACK	6
+#define CCP_RESETREQ	14
+#define CCP_RESETACK	15
+
+/*
+ * Max # bytes for a CCP option
+ */
+
+#define CCP_MAX_OPTION_LENGTH	32
+
+/*
+ * Parts of a CCP packet.
+ */
+
+#define CCP_CODE(dp)		((dp)[0])
+#define CCP_ID(dp)		((dp)[1])
+#define CCP_LENGTH(dp)		(((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN		4
+
+#define CCP_OPT_CODE(dp)	((dp)[0])
+#define CCP_OPT_LENGTH(dp)	((dp)[1])
+#define CCP_OPT_MINLEN		2
+
+/*
+ * Definitions for BSD-Compress.
+ */
+
+#define CI_BSD_COMPRESS		21	/* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS	3	/* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x)		((x) & 0x1F)	/* number of bits requested */
+#define BSD_VERSION(x)		((x) >> 5)	/* version of option format */
+#define BSD_CURRENT_VERSION	1		/* current version number */
+#define BSD_MAKE_OPT(v, n)	(((v) << 5) | (n))
+
+#define BSD_MIN_BITS		9	/* smallest code size supported */
+#define BSD_MAX_BITS		15	/* largest code size supported */
+
+/*
+ * Definitions for Deflate.
+ */
+
+#define CI_DEFLATE		26	/* config option for Deflate */
+#define CI_DEFLATE_DRAFT	24	/* value used in original draft RFC */
+#define CILEN_DEFLATE		4	/* length of its config option */
+
+#define DEFLATE_MIN_SIZE	8
+#define DEFLATE_MAX_SIZE	15
+#define DEFLATE_METHOD_VAL	8
+#define DEFLATE_SIZE(x)		(((x) >> 4) + DEFLATE_MIN_SIZE)
+#define DEFLATE_METHOD(x)	((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w)	((((w) - DEFLATE_MIN_SIZE) << 4) \
+				 + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE	0
+
+/*
+ * Definitions for MPPE.
+ */
+
+#define CI_MPPE			18	/* config option for MPPE */
+#define CILEN_MPPE		6	/* length of config option */
+
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+
+#define CI_PREDICTOR_1		1	/* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1	2	/* length of its config option */
+#define CI_PREDICTOR_2		2	/* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2	2	/* length of its config option */
+
+#endif /* _NET_PPP_COMP_H */
diff --git a/ap/app/pppd/include/linux/ppp_defs.h b/ap/app/pppd/include/linux/ppp_defs.h
new file mode 100644
index 0000000..0218036
--- /dev/null
+++ b/ap/app/pppd/include/linux/ppp_defs.h
@@ -0,0 +1,195 @@
+/*	$Id: ppp_defs.h,v 1.2 2007-06-08 04:02:37 gerg Exp $	*/
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * Copyright (c) 1989-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ *  ==FILEVERSION 20020521==
+ *
+ *  NOTE TO MAINTAINERS:
+ *     If you modify this file at all, please set the above date.
+ *     ppp_defs.h is shipped with a PPP distribution as well as with the kernel;
+ *     if everyone increases the FILEVERSION number above, then scripts
+ *     can do the right thing when deciding whether to install a new ppp_defs.h
+ *     file.  Don't change the format of that line otherwise, so the
+ *     installation script can recognize it.
+ */
+
+#ifndef _PPP_DEFS_H_
+#define _PPP_DEFS_H_
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN	4	/* octets for standard ppp header */
+#define PPP_FCSLEN	2	/* octets for FCS */
+#define PPP_MRU		1500	/* default MRU = max length of info field */
+
+#define PPP_ADDRESS(p)	(((__u8 *)(p))[0])
+#define PPP_CONTROL(p)	(((__u8 *)(p))[1])
+#define PPP_PROTOCOL(p)	((((__u8 *)(p))[2] << 8) + ((__u8 *)(p))[3])
+
+/*
+ * Significant octet values.
+ */
+#define	PPP_ALLSTATIONS	0xff	/* All-Stations broadcast address */
+#define	PPP_UI		0x03	/* Unnumbered Information */
+#define	PPP_FLAG	0x7e	/* Flag Sequence */
+#define	PPP_ESCAPE	0x7d	/* Asynchronous Control Escape */
+#define	PPP_TRANS	0x20	/* Asynchronous transparency modifier */
+
+/*
+ * Protocol field values.
+ */
+#define PPP_IP		0x21	/* Internet Protocol */
+#define PPP_AT		0x29	/* AppleTalk Protocol */
+#define PPP_IPX		0x2b	/* IPX protocol */
+#define	PPP_VJC_COMP	0x2d	/* VJ compressed TCP */
+#define	PPP_VJC_UNCOMP	0x2f	/* VJ uncompressed TCP */
+#define PPP_MP		0x3d	/* Multilink protocol */
+#define PPP_IPV6	0x57	/* Internet Protocol Version 6 */
+#define PPP_COMPFRAG	0xfb	/* fragment compressed below bundle */
+#define PPP_COMP	0xfd	/* compressed packet */
+#define PPP_IPCP	0x8021	/* IP Control Protocol */
+#define PPP_ATCP	0x8029	/* AppleTalk Control Protocol */
+#define PPP_IPXCP	0x802b	/* IPX Control Protocol */
+#define PPP_IPV6CP	0x8057	/* IPv6 Control Protocol */
+#define PPP_CCPFRAG	0x80fb	/* CCP at link level (below MP bundle) */
+#define PPP_CCP		0x80fd	/* Compression Control Protocol */
+#define PPP_ECPFRAG	0x8055	/* ECP at link level (below MP bundle) */
+#define PPP_ECP		0x8053	/* Encryption Control Protocol */
+#define PPP_LCP		0xc021	/* Link Control Protocol */
+#define PPP_PAP		0xc023	/* Password Authentication Protocol */
+#define PPP_LQR		0xc025	/* Link Quality Report protocol */
+#define PPP_CHAP	0xc223	/* Cryptographic Handshake Auth. Protocol */
+#define PPP_CBCP	0xc029	/* Callback Control Protocol */
+
+/*
+ * Values for FCS calculations.
+ */
+
+#define PPP_INITFCS	0xffff	/* Initial FCS value */
+#define PPP_GOODFCS	0xf0b8	/* Good final FCS value */
+#define PPP_FCS(fcs, c)	(((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+
+typedef __u32		ext_accm[8];
+
+/*
+ * What to do with network protocol (NP) packets.
+ */
+enum NPmode {
+    NPMODE_PASS,		/* pass the packet through */
+    NPMODE_DROP,		/* silently drop the packet */
+    NPMODE_ERROR,		/* return an error */
+    NPMODE_QUEUE		/* save it up for later. */
+};
+
+/*
+ * Statistics for LQRP and pppstats
+ */
+struct pppstat	{
+    __u32	ppp_discards;	/* # frames discarded */
+
+    __u32	ppp_ibytes;	/* bytes received */
+    __u32	ppp_ioctects;	/* bytes received not in error */
+    __u32	ppp_ipackets;	/* packets received */
+    __u32	ppp_ierrors;	/* receive errors */
+    __u32	ppp_ilqrs;	/* # LQR frames received */
+
+    __u32	ppp_obytes;	/* raw bytes sent */
+    __u32	ppp_ooctects;	/* frame bytes sent */
+    __u32	ppp_opackets;	/* packets sent */
+    __u32	ppp_oerrors;	/* transmit errors */ 
+    __u32	ppp_olqrs;	/* # LQR frames sent */
+};
+
+struct vjstat {
+    __u32	vjs_packets;	/* outbound packets */
+    __u32	vjs_compressed;	/* outbound compressed packets */
+    __u32	vjs_searches;	/* searches for connection state */
+    __u32	vjs_misses;	/* times couldn't find conn. state */
+    __u32	vjs_uncompressedin; /* inbound uncompressed packets */
+    __u32	vjs_compressedin;   /* inbound compressed packets */
+    __u32	vjs_errorin;	/* inbound unknown type packets */
+    __u32	vjs_tossed;	/* inbound packets tossed because of error */
+};
+
+struct compstat {
+    __u32	unc_bytes;	/* total uncompressed bytes */
+    __u32	unc_packets;	/* total uncompressed packets */
+    __u32	comp_bytes;	/* compressed bytes */
+    __u32	comp_packets;	/* compressed packets */
+    __u32	inc_bytes;	/* incompressible bytes */
+    __u32	inc_packets;	/* incompressible packets */
+
+    /* the compression ratio is defined as in_count / bytes_out */
+    __u32       in_count;	/* Bytes received */
+    __u32       bytes_out;	/* Bytes transmitted */
+
+    double	ratio;		/* not computed in kernel. */
+};
+
+struct ppp_stats {
+    struct pppstat	p;	/* basic PPP statistics */
+    struct vjstat	vj;	/* VJ header compression statistics */
+};
+
+struct ppp_comp_stats {
+    struct compstat	c;	/* packet compression statistics */
+    struct compstat	d;	/* packet decompression statistics */
+};
+
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+    time_t xmit_idle;		/* time since last NP packet sent */
+    time_t recv_idle;		/* time since last NP packet received */
+};
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x)	x
+#else
+#define __P(x)	()
+#endif
+#endif
+
+#endif /* _PPP_DEFS_H_ */
diff --git a/ap/app/pppd/include/net/if_ppp.h b/ap/app/pppd/include/net/if_ppp.h
new file mode 100644
index 0000000..ffc41fd
--- /dev/null
+++ b/ap/app/pppd/include/net/if_ppp.h
@@ -0,0 +1,156 @@
+/*	$Id: if_ppp.h,v 1.2 2007-06-08 04:02:37 gerg Exp $	*/
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IF_PPP_H_
+#define _IF_PPP_H_
+
+/*
+ * Bit definitions for flags.
+ */
+#define SC_COMP_PROT	0x00000001	/* protocol compression (output) */
+#define SC_COMP_AC	0x00000002	/* header compression (output) */
+#define	SC_COMP_TCP	0x00000004	/* TCP (VJ) compression (output) */
+#define SC_NO_TCP_CCID	0x00000008	/* disable VJ connection-id comp. */
+#define SC_REJ_COMP_AC	0x00000010	/* reject adrs/ctrl comp. on input */
+#define SC_REJ_COMP_TCP	0x00000020	/* reject TCP (VJ) comp. on input */
+#define SC_CCP_OPEN	0x00000040	/* Look at CCP packets */
+#define SC_CCP_UP	0x00000080	/* May send/recv compressed packets */
+#define SC_DEBUG	0x00010000	/* enable debug messages */
+#define SC_LOG_INPKT	0x00020000	/* log contents of good pkts recvd */
+#define SC_LOG_OUTPKT	0x00040000	/* log contents of pkts sent */
+#define SC_LOG_RAWIN	0x00080000	/* log all chars received */
+#define SC_LOG_FLUSH	0x00100000	/* log all chars flushed */
+#define SC_RCV_B7_0	0x01000000	/* have rcvd char with bit 7 = 0 */
+#define SC_RCV_B7_1	0x02000000	/* have rcvd char with bit 7 = 1 */
+#define SC_RCV_EVNP	0x04000000	/* have rcvd char with even parity */
+#define SC_RCV_ODDP	0x08000000	/* have rcvd char with odd parity */
+#define SC_SYNC		0x00200000	/* use synchronous HDLC framing */
+#define	SC_MASK		0x0fff00ff	/* bits that user can change */
+
+/*
+ * State bits in sc_flags, not changeable by user.
+ */
+#define SC_TIMEOUT	0x00000400	/* timeout is currently pending */
+#define SC_VJ_RESET	0x00000800	/* need to reset VJ decomp */
+#define SC_COMP_RUN	0x00001000	/* compressor has been inited */
+#define SC_DECOMP_RUN	0x00002000	/* decompressor has been inited */
+#define SC_DC_ERROR	0x00004000	/* non-fatal decomp error detected */
+#define SC_DC_FERROR	0x00008000	/* fatal decomp error detected */
+#define SC_TBUSY	0x10000000	/* xmitter doesn't need a packet yet */
+#define SC_PKTLOST	0x20000000	/* have lost or dropped a packet */
+#define	SC_FLUSH	0x40000000	/* flush input until next PPP_FLAG */
+#define	SC_ESCAPED	0x80000000	/* saw a PPP_ESCAPE */
+
+/*
+ * Ioctl definitions.
+ */
+
+struct npioctl {
+    int		protocol;	/* PPP procotol, e.g. PPP_IP */
+    enum NPmode	mode;
+};
+
+/* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */
+struct ppp_option_data {
+	u_char	*ptr;
+	u_int	length;
+	int	transmit;
+};
+
+struct ifpppstatsreq {
+    char ifr_name[IFNAMSIZ];
+    struct ppp_stats stats;
+};
+
+struct ifpppcstatsreq {
+    char ifr_name[IFNAMSIZ];
+    struct ppp_comp_stats stats;
+};
+
+/*
+ * Ioctl definitions.
+ */
+
+#define	PPPIOCGFLAGS	_IOR('t', 90, int)	/* get configuration flags */
+#define	PPPIOCSFLAGS	_IOW('t', 89, int)	/* set configuration flags */
+#define	PPPIOCGASYNCMAP	_IOR('t', 88, int)	/* get async map */
+#define	PPPIOCSASYNCMAP	_IOW('t', 87, int)	/* set async map */
+#define	PPPIOCGUNIT	_IOR('t', 86, int)	/* get ppp unit number */
+#define	PPPIOCGRASYNCMAP _IOR('t', 85, int)	/* get receive async map */
+#define	PPPIOCSRASYNCMAP _IOW('t', 84, int)	/* set receive async map */
+#define	PPPIOCGMRU	_IOR('t', 83, int)	/* get max receive unit */
+#define	PPPIOCSMRU	_IOW('t', 82, int)	/* set max receive unit */
+#define	PPPIOCSMAXCID	_IOW('t', 81, int)	/* set VJ max slot ID */
+#define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */
+#define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */
+#define PPPIOCXFERUNIT	_IO('t', 78)		/* transfer PPP unit */
+#define PPPIOCSCOMPRESS	_IOW('t', 77, struct ppp_option_data)
+#define PPPIOCGNPMODE	_IOWR('t', 76, struct npioctl) /* get NP mode */
+#define PPPIOCSNPMODE	_IOW('t', 75, struct npioctl)  /* set NP mode */
+#define PPPIOCGIDLE	_IOR('t', 74, struct ppp_idle) /* get idle time */
+#ifdef PPP_FILTER
+#define PPPIOCSPASS	_IOW('t', 71, struct bpf_program) /* set pass filter */
+#define PPPIOCSACTIVE	_IOW('t', 70, struct bpf_program) /* set active filt */
+#endif /* PPP_FILTER */
+
+/* PPPIOC[GS]MTU are alternatives to SIOC[GS]IFMTU, used under Ultrix */
+#define PPPIOCGMTU	_IOR('t', 73, int)	/* get interface MTU */
+#define PPPIOCSMTU	_IOW('t', 72, int)	/* set interface MTU */
+
+/*
+ * These two are interface ioctls so that pppstats can do them on
+ * a socket without having to open the serial device.
+ */
+#define SIOCGPPPSTATS	_IOWR('i', 123, struct ifpppstatsreq)
+#define SIOCGPPPCSTATS	_IOWR('i', 122, struct ifpppcstatsreq)
+
+#if !defined(ifr_mtu)
+#define ifr_mtu	ifr_ifru.ifru_metric
+#endif
+
+#if (defined(_KERNEL) || defined(KERNEL)) && !defined(NeXT)
+void pppattach __P((void));
+void pppintr __P((void));
+#endif
+#endif /* _IF_PPP_H_ */
diff --git a/ap/app/pppd/include/net/ppp-comp.h b/ap/app/pppd/include/net/ppp-comp.h
new file mode 100644
index 0000000..ce287ec
--- /dev/null
+++ b/ap/app/pppd/include/net/ppp-comp.h
@@ -0,0 +1,179 @@
+/*
+ * ppp-comp.h - Definitions for doing PPP packet compression.
+ *
+ * Copyright (c) 1984 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp-comp.h,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+#ifndef _NET_PPP_COMP_H
+#define _NET_PPP_COMP_H
+
+/*
+ * The following symbols control whether we include code for
+ * various compression methods.
+ */
+#ifndef DO_BSD_COMPRESS
+#define DO_BSD_COMPRESS	1	/* by default, include BSD-Compress */
+#endif
+#ifndef DO_DEFLATE
+#define DO_DEFLATE	1	/* by default, include Deflate */
+#endif
+#define DO_PREDICTOR_1	0
+#define DO_PREDICTOR_2	0
+
+/*
+ * Structure giving methods for compression/decompression.
+ */
+#ifdef PACKETPTR
+struct compressor {
+	int	compress_proto;	/* CCP compression protocol number */
+
+	/* Allocate space for a compressor (transmit side) */
+	void	*(*comp_alloc) __P((u_char *options, int opt_len));
+	/* Free space used by a compressor */
+	void	(*comp_free) __P((void *state));
+	/* Initialize a compressor */
+	int	(*comp_init) __P((void *state, u_char *options, int opt_len,
+				  int unit, int hdrlen, int debug));
+	/* Reset a compressor */
+	void	(*comp_reset) __P((void *state));
+	/* Compress a packet */
+	int	(*compress) __P((void *state, PACKETPTR *mret,
+				 PACKETPTR mp, int orig_len, int max_len));
+	/* Return compression statistics */
+	void	(*comp_stat) __P((void *state, struct compstat *stats));
+
+	/* Allocate space for a decompressor (receive side) */
+	void	*(*decomp_alloc) __P((u_char *options, int opt_len));
+	/* Free space used by a decompressor */
+	void	(*decomp_free) __P((void *state));
+	/* Initialize a decompressor */
+	int	(*decomp_init) __P((void *state, u_char *options, int opt_len,
+				    int unit, int hdrlen, int mru, int debug));
+	/* Reset a decompressor */
+	void	(*decomp_reset) __P((void *state));
+	/* Decompress a packet. */
+	int	(*decompress) __P((void *state, PACKETPTR mp,
+				   PACKETPTR *dmpp));
+	/* Update state for an incompressible packet received */
+	void	(*incomp) __P((void *state, PACKETPTR mp));
+	/* Return decompression statistics */
+	void	(*decomp_stat) __P((void *state, struct compstat *stats));
+};
+#endif /* PACKETPTR */
+
+/*
+ * Return values for decompress routine.
+ * We need to make these distinctions so that we can disable certain
+ * useful functionality, namely sending a CCP reset-request as a result
+ * of an error detected after decompression.  This is to avoid infringing
+ * a patent held by Motorola.
+ * Don't you just lurve software patents.
+ */
+#define DECOMP_OK		0	/* everything went OK */
+#define DECOMP_ERROR		1	/* error detected before decomp. */
+#define DECOMP_FATALERROR	2	/* error detected after decomp. */
+
+/*
+ * CCP codes.
+ */
+#define CCP_CONFREQ	1
+#define CCP_CONFACK	2
+#define CCP_TERMREQ	5
+#define CCP_TERMACK	6
+#define CCP_RESETREQ	14
+#define CCP_RESETACK	15
+
+/*
+ * Max # bytes for a CCP option
+ */
+#define CCP_MAX_OPTION_LENGTH	32
+
+/*
+ * Parts of a CCP packet.
+ */
+#define CCP_CODE(dp)		((dp)[0])
+#define CCP_ID(dp)		((dp)[1])
+#define CCP_LENGTH(dp)		(((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN		4
+
+#define CCP_OPT_CODE(dp)	((dp)[0])
+#define CCP_OPT_LENGTH(dp)	((dp)[1])
+#define CCP_OPT_MINLEN		2
+
+/*
+ * Definitions for BSD-Compress.
+ */
+#define CI_BSD_COMPRESS		21	/* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS	3	/* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x)		((x) & 0x1F)	/* number of bits requested */
+#define BSD_VERSION(x)		((x) >> 5)	/* version of option format */
+#define BSD_CURRENT_VERSION	1		/* current version number */
+#define BSD_MAKE_OPT(v, n)	(((v) << 5) | (n))
+
+#define BSD_MIN_BITS		9	/* smallest code size supported */
+#define BSD_MAX_BITS		15	/* largest code size supported */
+
+/*
+ * Definitions for Deflate.
+ */
+#define CI_DEFLATE		26	/* config option for Deflate */
+#define CI_DEFLATE_DRAFT	24	/* value used in original draft RFC */
+#define CILEN_DEFLATE		4	/* length of its config option */
+
+#define DEFLATE_MIN_SIZE	8
+#define DEFLATE_MAX_SIZE	15
+#define DEFLATE_METHOD_VAL	8
+#define DEFLATE_SIZE(x)		(((x) >> 4) + DEFLATE_MIN_SIZE)
+#define DEFLATE_METHOD(x)	((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w)	((((w) - DEFLATE_MIN_SIZE) << 4) \
+				 + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE	0
+
+/*
+ * Definitions for MPPE.
+ */
+#define CI_MPPE			18	/* config option for MPPE */
+#define CILEN_MPPE		6	/* length of config option */
+
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+#define CI_PREDICTOR_1		1	/* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1	2	/* length of its config option */
+#define CI_PREDICTOR_2		2	/* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2	2	/* length of its config option */
+
+#endif /* _NET_PPP_COMP_H */
diff --git a/ap/app/pppd/include/net/ppp_defs.h b/ap/app/pppd/include/net/ppp_defs.h
new file mode 100644
index 0000000..16c890f
--- /dev/null
+++ b/ap/app/pppd/include/net/ppp_defs.h
@@ -0,0 +1,194 @@
+/*	$Id: ppp_defs.h,v 1.2 2007-06-08 04:02:37 gerg Exp $	*/
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * Copyright (c) 1984 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _PPP_DEFS_H_
+#define _PPP_DEFS_H_
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN	4	/* octets for standard ppp header */
+#define PPP_FCSLEN	2	/* octets for FCS */
+
+/*
+ * Packet sizes
+ *
+ * Note - lcp shouldn't be allowed to negotiate stuff outside these
+ *	  limits.  See lcp.h in the pppd directory.
+ * (XXX - these constants should simply be shared by lcp.c instead
+ *	  of living in lcp.h)
+ */
+#define	PPP_MTU		1500	/* Default MTU (size of Info field) */
+#define PPP_MAXMTU	65535 - (PPP_HDRLEN + PPP_FCSLEN)
+#define PPP_MINMTU	64
+#define PPP_MRU		1500	/* default MRU = max length of info field */
+#define PPP_MAXMRU	65000	/* Largest MRU we allow */
+#define PPP_MINMRU	128
+
+#define PPP_ADDRESS(p)	(((u_char *)(p))[0])
+#define PPP_CONTROL(p)	(((u_char *)(p))[1])
+#define PPP_PROTOCOL(p)	((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3])
+
+/*
+ * Significant octet values.
+ */
+#define	PPP_ALLSTATIONS	0xff	/* All-Stations broadcast address */
+#define	PPP_UI		0x03	/* Unnumbered Information */
+#define	PPP_FLAG	0x7e	/* Flag Sequence */
+#define	PPP_ESCAPE	0x7d	/* Asynchronous Control Escape */
+#define	PPP_TRANS	0x20	/* Asynchronous transparency modifier */
+
+/*
+ * Protocol field values.
+ */
+#define PPP_IP		0x21	/* Internet Protocol */
+#define PPP_AT		0x29	/* AppleTalk Protocol */
+#define PPP_IPX		0x2b	/* IPX protocol */
+#define	PPP_VJC_COMP	0x2d	/* VJ compressed TCP */
+#define	PPP_VJC_UNCOMP	0x2f	/* VJ uncompressed TCP */
+#define PPP_IPV6	0x57	/* Internet Protocol Version 6 */
+#define PPP_COMP	0xfd	/* compressed packet */
+#define PPP_IPCP	0x8021	/* IP Control Protocol */
+#define PPP_ATCP	0x8029	/* AppleTalk Control Protocol */
+#define PPP_IPXCP	0x802b	/* IPX Control Protocol */
+#define PPP_IPV6CP	0x8057	/* IPv6 Control Protocol */
+#define PPP_CCP		0x80fd	/* Compression Control Protocol */
+#define PPP_ECP		0x8053	/* Encryption Control Protocol */
+#define PPP_LCP		0xc021	/* Link Control Protocol */
+#define PPP_PAP		0xc023	/* Password Authentication Protocol */
+#define PPP_LQR		0xc025	/* Link Quality Report protocol */
+#define PPP_CHAP	0xc223	/* Cryptographic Handshake Auth. Protocol */
+#define PPP_CBCP	0xc029	/* Callback Control Protocol */
+#define PPP_EAP		0xc227	/* Extensible Authentication Protocol */
+
+/*
+ * Values for FCS calculations.
+ */
+#define PPP_INITFCS	0xffff	/* Initial FCS value */
+#define PPP_GOODFCS	0xf0b8	/* Good final FCS value */
+#define PPP_FCS(fcs, c)	(((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+
+/*
+ * A 32-bit unsigned integral type.
+ */
+
+#if !defined(__BIT_TYPES_DEFINED__) && !defined(_BITYPES) \
+ && !defined(__FreeBSD__) && (NS_TARGET < 40)
+#ifdef	UINT32_T
+typedef UINT32_T	u_int32_t;
+#else
+typedef unsigned int	u_int32_t;
+typedef unsigned short  u_int16_t;
+#endif
+#endif
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+typedef u_int32_t	ext_accm[8];
+
+/*
+ * What to do with network protocol (NP) packets.
+ */
+enum NPmode {
+    NPMODE_PASS,		/* pass the packet through */
+    NPMODE_DROP,		/* silently drop the packet */
+    NPMODE_ERROR,		/* return an error */
+    NPMODE_QUEUE		/* save it up for later. */
+};
+
+/*
+ * Statistics.
+ */
+struct pppstat	{
+    unsigned int ppp_ibytes;	/* bytes received */
+    unsigned int ppp_ipackets;	/* packets received */
+    unsigned int ppp_ierrors;	/* receive errors */
+    unsigned int ppp_obytes;	/* bytes sent */
+    unsigned int ppp_opackets;	/* packets sent */
+    unsigned int ppp_oerrors;	/* transmit errors */
+};
+
+struct vjstat {
+    unsigned int vjs_packets;	/* outbound packets */
+    unsigned int vjs_compressed; /* outbound compressed packets */
+    unsigned int vjs_searches;	/* searches for connection state */
+    unsigned int vjs_misses;	/* times couldn't find conn. state */
+    unsigned int vjs_uncompressedin; /* inbound uncompressed packets */
+    unsigned int vjs_compressedin; /* inbound compressed packets */
+    unsigned int vjs_errorin;	/* inbound unknown type packets */
+    unsigned int vjs_tossed;	/* inbound packets tossed because of error */
+};
+
+struct ppp_stats {
+    struct pppstat p;		/* basic PPP statistics */
+    struct vjstat vj;		/* VJ header compression statistics */
+};
+
+struct compstat {
+    unsigned int unc_bytes;	/* total uncompressed bytes */
+    unsigned int unc_packets;	/* total uncompressed packets */
+    unsigned int comp_bytes;	/* compressed bytes */
+    unsigned int comp_packets;	/* compressed packets */
+    unsigned int inc_bytes;	/* incompressible bytes */
+    unsigned int inc_packets;	/* incompressible packets */
+    unsigned int ratio;		/* recent compression ratio << 8 */
+};
+
+struct ppp_comp_stats {
+    struct compstat c;		/* packet compression statistics */
+    struct compstat d;		/* packet decompression statistics */
+};
+
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+    time_t xmit_idle;		/* time since last NP packet sent */
+    time_t recv_idle;		/* time since last NP packet received */
+};
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x)	x
+#else
+#define __P(x)	()
+#endif
+#endif
+
+#endif /* _PPP_DEFS_H_ */
diff --git a/ap/app/pppd/include/net/pppio.h b/ap/app/pppd/include/net/pppio.h
new file mode 100644
index 0000000..1b7934e
--- /dev/null
+++ b/ap/app/pppd/include/net/pppio.h
@@ -0,0 +1,107 @@
+/*
+ * pppio.h - ioctl and other misc. definitions for STREAMS modules.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: pppio.h,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+#define _PPPIO(n)	(('p' << 8) + (n))
+
+#define PPPIO_NEWPPA	_PPPIO(130)	/* allocate a new PPP unit */
+#define PPPIO_GETSTAT	_PPPIO(131)	/* get PPP statistics */
+#define PPPIO_GETCSTAT	_PPPIO(132)	/* get PPP compression stats */
+#define PPPIO_MTU	_PPPIO(133)	/* set max transmission unit */
+#define PPPIO_MRU	_PPPIO(134)	/* set max receive unit */
+#define PPPIO_CFLAGS	_PPPIO(135)	/* set/clear/get compression flags */
+#define PPPIO_XCOMP	_PPPIO(136)	/* alloc transmit compressor */
+#define PPPIO_RCOMP	_PPPIO(137)	/* alloc receive decompressor */
+#define PPPIO_XACCM	_PPPIO(138)	/* set transmit asyncmap */
+#define PPPIO_RACCM	_PPPIO(139)	/* set receive asyncmap */
+#define PPPIO_VJINIT	_PPPIO(140)	/* initialize VJ comp/decomp */
+#define PPPIO_ATTACH	_PPPIO(141)	/* attach to a ppa (without putmsg) */
+#define PPPIO_LASTMOD	_PPPIO(142)	/* mark last ppp module */
+#define PPPIO_GCLEAN	_PPPIO(143)	/* get 8-bit-clean flags */
+#define PPPIO_DEBUG	_PPPIO(144)	/* request debug information */
+#define PPPIO_BIND	_PPPIO(145)	/* bind to SAP */
+#define PPPIO_NPMODE	_PPPIO(146)	/* set mode for handling data pkts */
+#define PPPIO_GIDLE	_PPPIO(147)	/* get time since last data pkt */
+#define PPPIO_PASSFILT	_PPPIO(148)	/* set filter for packets to pass */
+#define PPPIO_ACTIVEFILT _PPPIO(149)	/* set filter for "link active" pkts */
+
+/*
+ * Values for PPPIO_CFLAGS
+ */
+#define COMP_AC		0x1		/* compress address/control */
+#define DECOMP_AC	0x2		/* decompress address/control */
+#define COMP_PROT	0x4		/* compress PPP protocol */
+#define DECOMP_PROT	0x8		/* decompress PPP protocol */
+
+#define COMP_VJC	0x10		/* compress TCP/IP headers */
+#define COMP_VJCCID	0x20		/* compress connection ID as well */
+#define DECOMP_VJC	0x40		/* decompress TCP/IP headers */
+#define DECOMP_VJCCID	0x80		/* accept compressed connection ID */
+
+#define CCP_ISOPEN	0x100		/* look at CCP packets */
+#define CCP_ISUP	0x200		/* do packet comp/decomp */
+#define CCP_ERROR	0x400		/* (status) error in packet decomp */
+#define CCP_FATALERROR	0x800		/* (status) fatal error ditto */
+#define CCP_COMP_RUN	0x1000		/* (status) seen CCP ack sent */
+#define CCP_DECOMP_RUN	0x2000		/* (status) seen CCP ack rcvd */
+
+/*
+ * Values for 8-bit-clean flags.
+ */
+#define RCV_B7_0	1		/* have rcvd char with bit 7 = 0 */
+#define RCV_B7_1	2		/* have rcvd char with bit 7 = 1 */
+#define RCV_EVNP	4		/* have rcvd char with even parity */
+#define RCV_ODDP	8		/* have rcvd char with odd parity */
+
+/*
+ * Values for the first byte of M_CTL messages passed between
+ * PPP modules.
+ */
+#define PPPCTL_OERROR	0xe0		/* output error [up] */
+#define PPPCTL_IERROR	0xe1		/* input error (e.g. FCS) [up] */
+#define PPPCTL_MTU	0xe2		/* set MTU [down] */
+#define PPPCTL_MRU	0xe3		/* set MRU [down] */
+#define PPPCTL_UNIT	0xe4		/* note PPP unit number [down] */
+
+/*
+ * Values for the integer argument to PPPIO_DEBUG.
+ */
+#define PPPDBG_DUMP	0x10000		/* print out debug info now */
+#define PPPDBG_LOG	0x100		/* log various things */
+#define PPPDBG_DRIVER	0		/* identifies ppp driver as target */
+#define PPPDBG_IF	1		/* identifies ppp network i/f target */
+#define PPPDBG_COMP	2		/* identifies ppp compression target */
+#define PPPDBG_AHDLC	3		/* identifies ppp async hdlc target */
diff --git a/ap/app/pppd/include/net/slcompress.h b/ap/app/pppd/include/net/slcompress.h
new file mode 100644
index 0000000..9f92b11
--- /dev/null
+++ b/ap/app/pppd/include/net/slcompress.h
@@ -0,0 +1,148 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id: slcompress.h,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *	- Initial distribution.
+ */
+
+#ifndef _SLCOMPRESS_H_
+#define _SLCOMPRESS_H_
+
+#define MAX_STATES 16		/* must be > 2 and < 256 */
+#define MAX_HDR MLEN		/* XXX 4bsd-ism: should really be 128 */
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits).  The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet.  The next two octets are the TCP checksum
+ * from the original datagram.  The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ * 
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID.  (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.)  Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0.  (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type.  There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows.  Top
+ * three bits are actual packet type.  For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C	0x40	/* flag bits for what changed in a packet */
+#define NEW_I	0x20
+#define NEW_S	0x08
+#define NEW_A	0x04
+#define NEW_W	0x02
+#define NEW_U	0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire.  This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+	struct cstate *cs_next;	/* next most recently used cstate (xmit only) */
+	u_short cs_hlen;	/* size of hdr (receive only) */
+	u_char cs_id;		/* connection # associated with this state */
+	u_char cs_filler;
+	union {
+		char csu_hdr[MAX_HDR];
+		struct ip csu_ip;	/* ip/tcp hdr from most recent packet */
+	} slcs_u;
+};
+#define cs_ip slcs_u.csu_ip
+#define cs_hdr slcs_u.csu_hdr
+
+/*
+ * all the state data for one serial line (we need one of these
+ * per line).
+ */
+struct slcompress {
+	struct cstate *last_cs;	/* most recently used tstate */
+	u_char last_recv;	/* last rcvd conn. id */
+	u_char last_xmit;	/* last sent conn. id */
+	u_short flags;
+#ifndef SL_NO_STATS
+	int sls_packets;	/* outbound packets */
+	int sls_compressed;	/* outbound compressed packets */
+	int sls_searches;	/* searches for connection state */
+	int sls_misses;		/* times couldn't find conn. state */
+	int sls_uncompressedin;	/* inbound uncompressed packets */
+	int sls_compressedin;	/* inbound compressed packets */
+	int sls_errorin;	/* inbound unknown type packets */
+	int sls_tossed;		/* inbound packets tossed because of error */
+#endif
+	struct cstate tstate[MAX_STATES];	/* xmit connection states */
+	struct cstate rstate[MAX_STATES];	/* receive connection states */
+};
+/* flag values */
+#define SLF_TOSS 1		/* tossing rcvd frames because of input err */
+
+void	sl_compress_init __P((struct slcompress *));
+void	sl_compress_setup __P((struct slcompress *, int));
+u_int	sl_compress_tcp __P((struct mbuf *,
+	    struct ip *, struct slcompress *, int));
+int	sl_uncompress_tcp __P((u_char **, int, u_int, struct slcompress *));
+int	sl_uncompress_tcp_core __P((u_char *, int, int, u_int,
+	    struct slcompress *, u_char **, u_int *));
+
+#endif /* _SLCOMPRESS_H_ */
diff --git a/ap/app/pppd/include/net/vjcompress.h b/ap/app/pppd/include/net/vjcompress.h
new file mode 100644
index 0000000..8431696
--- /dev/null
+++ b/ap/app/pppd/include/net/vjcompress.h
@@ -0,0 +1,144 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id: vjcompress.h,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *	- Initial distribution.
+ */
+
+#ifndef _VJCOMPRESS_H_
+#define _VJCOMPRESS_H_
+
+#define MAX_STATES 16		/* must be > 2 and < 256 */
+#define MAX_HDR	   128
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits).  The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet.  The next two octets are the TCP checksum
+ * from the original datagram.  The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ * 
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID.  (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.)  Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0.  (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type.  There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows.  Top
+ * three bits are actual packet type.  For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C	0x40	/* flag bits for what changed in a packet */
+#define NEW_I	0x20
+#define NEW_S	0x08
+#define NEW_A	0x04
+#define NEW_W	0x02
+#define NEW_U	0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire.  This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+    struct cstate *cs_next;	/* next most recently used state (xmit only) */
+    u_short cs_hlen;		/* size of hdr (receive only) */
+    u_char cs_id;		/* connection # associated with this state */
+    u_char cs_filler;
+    union {
+	char csu_hdr[MAX_HDR];
+	struct ip csu_ip;	/* ip/tcp hdr from most recent packet */
+    } vjcs_u;
+};
+#define cs_ip vjcs_u.csu_ip
+#define cs_hdr vjcs_u.csu_hdr
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct vjcompress {
+    struct cstate *last_cs;	/* most recently used tstate */
+    u_char last_recv;		/* last rcvd conn. id */
+    u_char last_xmit;		/* last sent conn. id */
+    u_short flags;
+#ifndef VJ_NO_STATS
+    struct vjstat stats;
+#endif
+    struct cstate tstate[MAX_STATES];	/* xmit connection states */
+    struct cstate rstate[MAX_STATES];	/* receive connection states */
+};
+
+/* flag values */
+#define VJF_TOSS 1		/* tossing rcvd frames because of input err */
+
+extern void  vj_compress_init __P((struct vjcompress *comp, int max_state));
+extern u_int vj_compress_tcp __P((struct ip *ip, u_int mlen,
+				struct vjcompress *comp, int compress_cid_flag,
+				u_char **vjhdrp));
+extern void  vj_uncompress_err __P((struct vjcompress *comp));
+extern int   vj_uncompress_uncomp __P((u_char *buf, int buflen,
+				struct vjcompress *comp));
+extern int   vj_uncompress_tcp __P((u_char *buf, int buflen, int total_len,
+				struct vjcompress *comp, u_char **hdrp,
+				u_int *hlenp));
+
+#endif /* _VJCOMPRESS_H_ */
diff --git a/ap/app/pppd/linux/Makefile.top b/ap/app/pppd/linux/Makefile.top
new file mode 100644
index 0000000..2e481c7
--- /dev/null
+++ b/ap/app/pppd/linux/Makefile.top
@@ -0,0 +1,75 @@
+# PPP top-level Makefile for Linux.
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+INCDIR = $(DESTDIR)/include
+MANDIR = $(DESTDIR)/share/man
+ETCDIR = $(INSTROOT)@SYSCONF@/ppp
+
+FLTFLAGS += -s 8192
+
+ifeq ($(CONFIG_USER_FLATFSD_FLATFSD),y)
+CFLAGS += -DPATH_CONFIG='"/etc/config"'
+CFLAGS += -DPATH_AUTH='"/bin"'
+CFLAGS += -DPATH_LOG='"/var/log"'
+CFLAGS += -DPATH_RESOLV='"/var/run"'
+endif
+
+# uid 0 = root
+INSTALL= install
+
+all:
+	cd chat; $(MAKE) $(MFLAGS) all
+	cd pppd/plugins; $(MAKE) $(MFLAGS) all
+	cd pppd; $(MAKE) $(MFLAGS) all
+	cd pppstats; $(MAKE) $(MFLAGS) all
+	cd pppdump; $(MAKE) $(MFLAGS) all
+
+install: $(BINDIR) $(MANDIR)/man8 install-progs install-devel
+
+install-progs:
+	cd chat; $(MAKE) $(MFLAGS) install
+	cd pppd/plugins; $(MAKE) $(MFLAGS) install
+	cd pppd; $(MAKE) $(MFLAGS) install
+	cd pppstats; $(MAKE) $(MFLAGS) install
+	cd pppdump; $(MAKE) $(MFLAGS) install
+
+install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
+	$(ETCDIR)/chap-secrets
+
+install-devel:
+	cd pppd; $(MAKE) $(MFLAGS) install-devel
+
+$(ETCDIR)/options:
+	$(INSTALL) -c -m 644 etc.ppp/options $@
+$(ETCDIR)/pap-secrets:
+	$(INSTALL) -c -m 600 etc.ppp/pap-secrets $@
+$(ETCDIR)/chap-secrets:
+	$(INSTALL) -c -m 600 etc.ppp/chap-secrets $@
+
+$(BINDIR):
+	$(INSTALL) -d -m 755 $@
+$(MANDIR)/man8:
+	$(INSTALL) -d -m 755 $@
+$(ETCDIR):
+	$(INSTALL) -d -m 755 $@
+
+clean:
+	rm -f `find . -name '*.[oas]' -print`
+	rm -f `find . -name 'core' -print`
+	rm -f `find . -name '*~' -print`
+	cd chat; $(MAKE) clean
+	cd pppd/plugins; $(MAKE) clean
+	cd pppd; $(MAKE) clean
+	cd pppstats; $(MAKE) clean
+	cd pppdump; $(MAKE) clean
+
+dist-clean:	clean
+	rm -f Makefile `find . -name Makefile -print`
+
+#kernel:
+#	cd linux; ./kinstall.sh
+
+# no tests yet, one day...
+installcheck:
+	true
diff --git a/ap/app/pppd/makefile b/ap/app/pppd/makefile
new file mode 100644
index 0000000..067a009
--- /dev/null
+++ b/ap/app/pppd/makefile
@@ -0,0 +1,32 @@
+
+CONFOPTS=
+
+all: build/configured
+	$(MAKE) -C build
+	@cp ./build/pppd/pppd pppd.elf
+
+build/configured: makefile
+	rm -rf build
+	find . -type d > .dirs
+	find . ! -type d | grep -v ./makefile > .files
+	while read t; do mkdir -p build/$$t; done < .dirs
+	while read t; do ln -s `pwd`/$$t build/$$t; done < .files
+	rm -f .dirs .files
+	chmod +x build/configure
+	cd build; sh ./configure $(CONFIGURE_OPTS) $(CONFOPTS)
+	touch build/configured
+
+clean:
+	rm -rf build
+	
+romfs:rootfs_bin
+
+rootfs_bin:
+	$(ROMFSINST) build/pppd/pppd /sbin/pppd
+	$(ROMFSINST) build/chat/chat /sbin/chat
+ifdef CONFIG_USER_PPPD_WITH_RADIUS
+	[ -d $(ROMFSDIR)/etc/radiusclient ] || mkdir $(ROMFSDIR)/etc/radiusclient
+	$(ROMFSINST) -e CONFIG_USER_PPPD_WITH_RADIUS scripts/radiusclient/dictionary /etc/radiusclient/dictionary
+	$(ROMFSINST) -e CONFIG_USER_PPPD_WITH_RADIUS scripts/radiusclient/dictionary.ms /etc/radiusclient/dictionary.ms
+	$(ROMFSINST) -e CONFIG_USER_PPPD_WITH_RADIUS scripts/radiusclient/dictionary.sg /etc/radiusclient/dictionary.sg
+endif
diff --git a/ap/app/pppd/modules/bsd-comp.c b/ap/app/pppd/modules/bsd-comp.c
new file mode 100644
index 0000000..5efc4d8
--- /dev/null
+++ b/ap/app/pppd/modules/bsd-comp.c
@@ -0,0 +1,1120 @@
+/* Because this code is derived from the 4.3BSD compress source:
+ *
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This version is for use with STREAMS under SunOS 4.x,
+ * Digital UNIX, AIX 4.x, and SVR4 systems including Solaris 2.
+ *
+ * $Id: bsd-comp.c,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+#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
+
+#ifdef SOL2
+#include <sys/sunddi.h>
+#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/ap/app/pppd/modules/deflate.c b/ap/app/pppd/modules/deflate.c
new file mode 100644
index 0000000..713dc12
--- /dev/null
+++ b/ap/app/pppd/modules/deflate.c
@@ -0,0 +1,772 @@
+/*
+ * ppp_deflate.c - interface the zlib procedures for Deflate compression
+ * and decompression (as used by gzip) to the PPP code.
+ * This version is for use with STREAMS under SunOS 4.x, Solaris 2,
+ * SVR4, OSF/1 and AIX 4.x.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: deflate.c,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+#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
+
+#ifdef SOL2
+#include <sys/sunddi.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/ap/app/pppd/modules/if_ppp.c b/ap/app/pppd/modules/if_ppp.c
new file mode 100644
index 0000000..fe6d0c5
--- /dev/null
+++ b/ap/app/pppd/modules/if_ppp.c
@@ -0,0 +1,873 @@
+/*
+ * if_ppp.c - a network interface connected to a STREAMS module.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: if_ppp.c,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+/*
+ * 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/ap/app/pppd/modules/ppp.c b/ap/app/pppd/modules/ppp.c
new file mode 100644
index 0000000..3e97904
--- /dev/null
+++ b/ap/app/pppd/modules/ppp.c
@@ -0,0 +1,2494 @@
+/*
+ * ppp.c - STREAMS multiplexing pseudo-device driver for PPP.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp.c,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+/*
+ * 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/ap/app/pppd/modules/ppp_ahdlc.c b/ap/app/pppd/modules/ppp_ahdlc.c
new file mode 100644
index 0000000..67a542e
--- /dev/null
+++ b/ap/app/pppd/modules/ppp_ahdlc.c
@@ -0,0 +1,873 @@
+/*
+ * 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 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id: ppp_ahdlc.c,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+/*
+ * 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);
+	break;
+
+    case M_HANGUP:
+#if defined(USE_MUTEX)
+	mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+	if (state->rx_buf != 0) {
+	    /* XXX would like to send this up for debugging */
+	    freemsg(state->rx_buf);
+	    state->rx_buf = 0;
+	}
+	state->flags = IFLUSH;
+#if defined(USE_MUTEX)
+	mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+	putnext(q, mp);
+	break;
+
+    default:
+	putnext(q, mp);
+    }
+    return 0;
+}
+
+/*
+ * Extract bit c from map m, to determine if c needs to be escaped
+ */
+#define IN_TX_MAP(c, m)	((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
+
+static void
+ahdlc_encode(q, mp)
+    queue_t	*q;
+    mblk_t	*mp;
+{
+    ahdlc_state_t	*state;
+    u_int32_t		*xaccm, loc_xaccm[8];
+    ushort_t		fcs;
+    size_t		outmp_len;
+    mblk_t		*outmp, *tmp;
+    uchar_t		*dp, fcs_val;
+    int			is_lcp, code;
+#if defined(SOL2)
+    clock_t		lbolt;
+#endif /* SOL2 */
+
+    if (msgdsize(mp) < 4) {
+	return;
+    }
+
+    state = (ahdlc_state_t *)q->q_ptr;
+#if defined(USE_MUTEX)
+    mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+    /*
+     * Allocate an output buffer large enough to handle a case where all
+     * characters need to be escaped
+     */
+    outmp_len = (msgdsize(mp)	 << 1) +		/* input block x 2 */
+		(sizeof(fcs)	 << 2) +		/* HDLC FCS x 4 */
+		(sizeof(uchar_t) << 1);			/* HDLC flags x 2 */
+
+    outmp = allocb(outmp_len, BPRI_MED);
+    if (outmp == NULL) {
+	state->stats.ppp_oerrors++;
+#if defined(USE_MUTEX)
+	mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+	putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+	return;
+    }
+
+#if defined(SOL2)
+    /*
+     * Check if our last transmit happenned within flag_time, using
+     * the system's LBOLT value in clock ticks
+     */
+    if (drv_getparm(LBOLT, &lbolt) != -1) {
+	if (ABS((clock_t)lbolt - state->lbolt) > state->flag_time) {
+	    *outmp->b_wptr++ = PPP_FLAG;
+	} 
+	state->lbolt = lbolt;
+    } else {
+	*outmp->b_wptr++ = PPP_FLAG;
+    }
+#else
+    /*
+     * If the driver below still has a message to process, skip the
+     * HDLC flag, otherwise, put one in the beginning
+     */
+    if (qsize(q->q_next) == 0) {
+	*outmp->b_wptr++ = PPP_FLAG;
+    }
+#endif
+
+    /*
+     * All control characters must be escaped for LCP packets with code
+     * values between 1 (Conf-Req) and 7 (Code-Rej).
+     */
+    is_lcp = ((MSG_BYTE(mp, 0) == PPP_ALLSTATIONS) && 
+	      (MSG_BYTE(mp, 1) == PPP_UI) && 
+	      (MSG_BYTE(mp, 2) == (PPP_LCP >> 8)) &&
+	      (MSG_BYTE(mp, 3) == (PPP_LCP & 0xff)) &&
+	      LCP_USE_DFLT(mp));
+
+    xaccm = state->xaccm;
+    if (is_lcp) {
+	bcopy((caddr_t)state->xaccm, (caddr_t)loc_xaccm, sizeof(loc_xaccm));
+	loc_xaccm[0] = ~0;	/* force escape on 0x00 through 0x1f */
+	xaccm = loc_xaccm;
+    }
+
+    fcs = PPP_INITFCS;		/* Initial FCS is 0xffff */
+
+    /*
+     * Process this block and the rest (if any) attached to the this one
+     */
+    for (tmp = mp; tmp; tmp = tmp->b_cont) {
+	if (tmp->b_datap->db_type == M_DATA) {
+	    for (dp = tmp->b_rptr; dp < tmp->b_wptr; dp++) {
+		fcs = PPP_FCS(fcs, *dp);
+		if (IN_TX_MAP(*dp, xaccm)) {
+		    *outmp->b_wptr++ = PPP_ESCAPE;
+		    *outmp->b_wptr++ = *dp ^ PPP_TRANS;
+		} else {
+		    *outmp->b_wptr++ = *dp;
+		}
+	    }
+	} else {
+	    continue;	/* skip if db_type is something other than M_DATA */
+	}
+    }
+
+    /*
+     * Append the HDLC FCS, making sure that escaping is done on any
+     * necessary bytes
+     */
+    fcs_val = (fcs ^ 0xffff) & 0xff;
+    if (IN_TX_MAP(fcs_val, xaccm)) {
+	*outmp->b_wptr++ = PPP_ESCAPE;
+	*outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+    } else {
+	*outmp->b_wptr++ = fcs_val;
+    }
+
+    fcs_val = ((fcs ^ 0xffff) >> 8) & 0xff;
+    if (IN_TX_MAP(fcs_val, xaccm)) {
+	*outmp->b_wptr++ = PPP_ESCAPE;
+	*outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+    } else {
+	*outmp->b_wptr++ = fcs_val;
+    }
+
+    /*
+     * And finally, append the HDLC flag, and send it away
+     */
+    *outmp->b_wptr++ = PPP_FLAG;
+
+    state->stats.ppp_obytes += msgdsize(outmp);
+    state->stats.ppp_opackets++;
+
+#if defined(USE_MUTEX)
+    mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+
+    putnext(q, outmp);
+    return;
+}
+
+/*
+ * Checks the 32-bit receive ACCM to see if the byte needs un-escaping
+ */
+#define IN_RX_MAP(c, m)	((((unsigned int) (uchar_t) (c)) < 0x20) && \
+			(m) & (1 << (c)))
+
+
+/*
+ * Process received characters.
+ */
+static void
+ahdlc_decode(q, mp)
+    queue_t *q;
+    mblk_t  *mp;
+{
+    ahdlc_state_t   *state;
+    mblk_t	    *om;
+    uchar_t	    *dp;
+
+    state = (ahdlc_state_t *) q->q_ptr;
+
+#if defined(USE_MUTEX)
+    mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+    state->stats.ppp_ibytes += msgdsize(mp);
+
+    for (; mp != 0; om = mp->b_cont, freeb(mp), mp = om)
+    for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) {
+
+	/*
+	 * This should detect the lack of 8-bit communication channel
+	 * which is necessary for PPP to work. In addition, it also
+	 * checks on the parity.
+	 */
+	if (*dp & 0x80)
+	    state->flags |= RCV_B7_1;
+	else
+	    state->flags |= RCV_B7_0;
+
+	if (paritytab[*dp >> 5] & (1 << (*dp & 0x1f)))
+	    state->flags |= RCV_ODDP;
+	else
+	    state->flags |= RCV_EVNP;
+
+	/*
+	 * So we have a HDLC flag ...
+	 */
+	if (*dp == PPP_FLAG) {
+
+	    /*
+	     * If we think that it marks the beginning of the frame,
+	     * then continue to process the next octects
+	     */
+	    if ((state->flags & IFLUSH) ||
+		(state->rx_buf == 0) ||
+		(msgdsize(state->rx_buf) == 0)) {
+
+		state->flags &= ~IFLUSH;
+		continue;
+	    }
+
+	    /*
+	     * We get here because the above condition isn't true,
+	     * in which case the HDLC flag was there to mark the end
+	     * of the frame (or so we think)
+	     */
+	    om = state->rx_buf;
+
+	    if (state->infcs == PPP_GOODFCS) {
+		state->stats.ppp_ipackets++;
+		adjmsg(om, -PPP_FCSLEN);
+		putnext(q, om);
+	    } else {
+		DPRINT2("ppp%d: bad fcs (len=%d)\n",
+                    state->unit, msgdsize(state->rx_buf));
+		freemsg(state->rx_buf);
+		state->flags &= ~(IFLUSH | ESCAPED);
+		state->stats.ppp_ierrors++;
+		putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+	    }
+
+	    state->rx_buf = 0;
+	    continue;
+	}
+
+	if (state->flags & IFLUSH) {
+	    continue;
+	}
+
+	/*
+	 * Allocate a receive buffer, large enough to store a frame (after
+	 * un-escaping) of at least 1500 octets. If MRU is negotiated to
+	 * be more than the default, then allocate that much. In addition,
+	 * we add an extra 32-bytes for a fudge factor
+	 */ 
+	if (state->rx_buf == 0) {
+	    state->rx_buf_size  = (state->mru < PPP_MRU ? PPP_MRU : state->mru);
+	    state->rx_buf_size += (sizeof(u_int32_t) << 3);
+	    state->rx_buf = allocb(state->rx_buf_size, BPRI_MED);
+
+	    /*
+	     * If allocation fails, try again on the next frame
+	     */
+	    if (state->rx_buf == 0) {
+		state->flags |= IFLUSH;
+		continue;
+	    }
+	    state->flags &= ~(IFLUSH | ESCAPED);
+	    state->infcs  = PPP_INITFCS;
+	}
+
+	if (*dp == PPP_ESCAPE) {
+	    state->flags |= ESCAPED;
+	    continue;
+	}
+
+	/*
+	 * Make sure we un-escape the necessary characters, as well as the
+	 * ones in our receive async control character map
+	 */
+	if (state->flags & ESCAPED) {
+	    *dp ^= PPP_TRANS;
+	    state->flags &= ~ESCAPED;
+	} else if (IN_RX_MAP(*dp, state->raccm)) 
+	    continue;
+
+	/*
+	 * Unless the peer lied to us about the negotiated MRU, we should
+	 * never get a frame which is too long. If it happens, toss it away
+	 * and grab the next incoming one
+	 */
+	if (msgdsize(state->rx_buf) < state->rx_buf_size) {
+	    state->infcs = PPP_FCS(state->infcs, *dp);
+	    *state->rx_buf->b_wptr++ = *dp;
+	} else {
+	    DPRINT2("ppp%d: frame too long (%d)\n",
+		state->unit, msgdsize(state->rx_buf));
+	    freemsg(state->rx_buf);
+	    state->rx_buf     = 0;
+	    state->flags     |= IFLUSH;
+	}
+    }
+
+#if defined(USE_MUTEX)
+    mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+}
+
+static int
+msg_byte(mp, i)
+    mblk_t *mp;
+    unsigned int i;
+{
+    while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
+	mp = mp->b_cont;
+    if (mp == 0)
+	return -1;
+    return mp->b_rptr[i];
+}
diff --git a/ap/app/pppd/modules/ppp_comp.c b/ap/app/pppd/modules/ppp_comp.c
new file mode 100644
index 0000000..a59c571
--- /dev/null
+++ b/ap/app/pppd/modules/ppp_comp.c
@@ -0,0 +1,1134 @@
+/*
+ * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp_comp.c,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+/*
+ * 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/ap/app/pppd/modules/ppp_mod.h b/ap/app/pppd/modules/ppp_mod.h
new file mode 100644
index 0000000..f0af008
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/modules/vjcompress.c b/ap/app/pppd/modules/vjcompress.c
new file mode 100644
index 0000000..d940806
--- /dev/null
+++ b/ap/app/pppd/modules/vjcompress.c
@@ -0,0 +1,591 @@
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *	- Initial distribution.
+ *
+ * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
+ * so that the entire packet being decompressed doesn't have
+ * to be in contiguous memory (just the compressed header).
+ */
+
+/*
+ * This version is used under SunOS 4.x, Digital UNIX, AIX 4.x,
+ * and SVR4 systems including Solaris 2.
+ *
+ * $Id: vjcompress.c,v 1.2 2007-06-08 04:02:37 gerg Exp $
+ */
+
+#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
+
+#ifdef SOL2
+#include <sys/sunddi.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/ap/app/pppd/ppp_readme.txt b/ap/app/pppd/ppp_readme.txt
new file mode 100644
index 0000000..5ce3f1e
--- /dev/null
+++ b/ap/app/pppd/ppp_readme.txt
@@ -0,0 +1,13 @@
+1°æ±¾Îª2.4.5

+2¡¢Æô¶¯·½Ê½ ÍâÍø¿ÚÕÒµ½Íøºóµ÷ÓÃpppd_up.shÆô¶¯pppdÄ£¿é£»

+3¡¢Ö÷ÒªÐ޸ĵã

+¾ßÌå¿É²Î¿¼Ä£¿éÖ§³ÖºÍ¶¨Î»Îĵµ¼°·½°¸Îĵµ£¬ÓÐÏêϸÃèÊö£¬Ö÷ÒªÐ޸ĵãÃèÊöÈçÏ£º

+auth.c network_phase, set lcp_finish NV to control when send out data£»

+auth.c check_passwd, check UPAP auth password, and force return ACK;

+chap-new.c chap_generate_challenge, set authinfo[0].challenge;

+chap-new.c chap_handle_response, set authinfo[0].pw, the value is the response id in CHAP mode;

+chap-new.c chap_verify_response, set authinfo[0], and send msg to ATServer£»

+fsm.c fsm_sdata, control how to send out data, if no auth, need send out,if already auth, need send out, while waiting auth, do not send out;

+ipcp.c ipcp_resetci, if the true AUTH finished, send the IP addr, DNS addr to PPPD's global value£»

+upap.c upap_rauthreq, set username and password in UPAP mode;

+Ïà¹ØnvÅäÖúÍÃèÊö²Î¿¼nvÎĵµ¡£
\ No newline at end of file
diff --git a/ap/app/pppd/pppd/Makefile.linux b/ap/app/pppd/pppd/Makefile.linux
new file mode 100755
index 0000000..3ad1890
--- /dev/null
+++ b/ap/app/pppd/pppd/Makefile.linux
@@ -0,0 +1,294 @@
+#
+# pppd makefile for Linux
+# $Id: Makefile.linux,v 1.70 2007/06/19 02:08:34 carlsonj Exp $
+#
+
+# Default installation locations
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+INCDIR = $(DESTDIR)/include
+include $(zte_app_mak)
+TARGETS = pppd
+
+PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap-new.c md5.c ccp.c \
+	   ecp.c ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c \
+	   demand.c utils.c tty.c eap.c chap-md5.c session.c
+
+HEADERS = ../../../include/netapi.h ccp.h session.h chap-new.h ecp.h fsm.h ipcp.h \
+	ipxcp.h lcp.h magic.h md5.h patchlevel.h pathnames.h pppd.h \
+	upap.h eap.h
+
+MANPAGES = pppd.8
+PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o md5.o ccp.o \
+	   ecp.o auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o \
+	   eap.o chap-md5.o session.o
+
+#
+# include dependencies if present
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+# Uncomment the next 2 lines to include support for Microsoft's
+# MS-CHAP authentication protocol.  Also, edit plugins/radius/Makefile.linux.
+CHAPMS=y
+USE_CRYPT=y
+
+# Don't use MSLANMAN unless you really know what you're doing.
+#MSLANMAN=y
+
+# Uncomment the next line to include support for MPPE.  CHAPMS (above) must
+# also be enabled.  Also, edit plugins/radius/Makefile.linux.
+ifeq ($(CONFIG_USER_PPPD_WITH_MPPE),y)
+MPPE=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.
+#FILTER=y
+
+# Uncomment the next line to enable multilink PPP (enabled by default)
+# Linux distributions: Please leave multilink ENABLED in your builds
+# of pppd!
+HAVE_MULTILINK=y
+
+# Uncomment the next line to enable the TDB database (enabled by default.)
+# If you enable multilink, then TDB is automatically enabled also.
+# Linux distributions: Please leave TDB ENABLED in your builds.
+USE_TDB=y
+
+ifneq ($(CONFIG_USER_SHADOW_UTILS)$(USER_BUSYBOX_USE_BB_SHADOW),)
+HAS_SHADOW=y
+endif
+
+ifeq ($(CONFIG_USER_PPPD_WITH_PAM),y)
+USE_PAM=y
+endif
+
+ifeq ($(CONFIG_PROP_STATSD_STATSD),y)
+USE_EXTERNAL_STATS_PROG=y
+endif
+
+#ifeq ($(CONFIG_USER_PPPD_WITH_IPV6),y)
+HAVE_INET6=y
+#endif
+
+# Enable plugins
+PLUGIN=y
+
+ifeq ($(CONFIG_USER_PPPD_WITH_DYNAMIC_PLUGINS),y)
+DYNAMIC_PLUGIN=y
+endif
+
+# Enable Microsoft proprietary Callback Control Protocol
+#CBCP=y
+
+# Enable EAP SRP-SHA1 authentication (requires libsrp)
+#USE_SRP=y
+
+MAXOCTETS=y
+
+INCLUDE_DIRS= -I../include -I$(zte_app_path)/include
+COMPILE_FLAGS= -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MMAP
+CFLAGS+= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) '-DDESTDIR="@DESTDIR@"'
+
+ifdef CHAPMS
+CFLAGS   += -DCHAPMS=1
+NEEDDES=y
+PPPDOBJS += md4.o chap_ms.o
+HEADERS	+= md4.h chap_ms.h
+ifdef MSLANMAN
+CFLAGS   += -DMSLANMAN=1
+endif
+ifdef MPPE
+CFLAGS   += -DMPPE=1
+endif
+endif
+
+# EAP SRP-SHA1
+ifdef USE_SRP
+CFLAGS	+= -DUSE_SRP -DOPENSSL -I/usr/local/ssl/include
+#LIBS	+= -lsrp -L/usr/local/ssl/lib -lcrypto
+LIBS	+= -lsrp -lcrypto
+TARGETS	+= srp-entry
+EXTRAINSTALL = $(INSTALL) -s -c -m 555 srp-entry $(BINDIR)/srp-entry
+MANPAGES += srp-entry.8
+EXTRACLEAN += srp-entry.o
+NEEDDES=y
+else
+# OpenSSL has an integrated version of SHA-1, and its implementation
+# is incompatible with this local SHA-1 implementation.  We must use
+# one or the other, not both.
+PPPDSRCS += sha1.c
+HEADERS += sha1.h
+PPPDOBJS += sha1.o
+endif
+
+ifdef HAS_SHADOW
+CFLAGS   += -DHAS_SHADOW
+#LIBS     += -lshadow $(LIBS)
+endif
+
+ifneq ($(wildcard /usr/include/crypt.h),)
+CFLAGS  += -DHAVE_CRYPT_H=1
+LIBS	+= -lcrypt
+endif
+
+ifdef NEEDDES
+ifndef USE_CRYPT
+LIBS     += -ldes $(LIBS)
+else
+CFLAGS   += -DUSE_CRYPT=1
+endif
+PPPDOBJS += pppcrypt.o
+HEADERS += pppcrypt.h
+endif
+
+# For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/.
+ifdef USE_PAM
+CFLAGS   += -DUSE_PAM
+LIBS     += -lpam
+endif
+
+ifdef USE_EXTERNAL_STATS_PROG
+CFLAGS   += -DUSE_EXTERNAL_STATS_PROG
+endif
+
+# Multi-linnk
+ifdef HAVE_MULTILINK
+	# Multilink implies the use of TDB
+	USE_TDB=y
+
+	CFLAGS += -DHAVE_MULTILINK
+	PPPDSRCS += multilink.c
+	PPPDOBJS += multilink.o
+endif
+
+# TDB
+ifdef USE_TDB
+	CFLAGS += -DUSE_TDB=1
+	PPPDSRCS += tdb.c spinlock.c
+	PPPDOBJS += tdb.o spinlock.o
+	HEADERS += tdb.h spinlock.h
+endif
+
+# Lock library binary for Linux is included in 'linux' subdirectory.
+ifdef LOCKLIB
+LIBS     += -llock
+CFLAGS   += -DLOCKLIB=1
+endif
+
+ifdef PLUGIN
+CFLAGS	+= -DPLUGIN
+ifdef DYNAMIC_PLUGIN
+CFLAGS	+= -DPLUGIN_DYNAMIC
+LDFLAGS	+= -Wl,-E
+LIBS	+= -ldl
+else
+ifeq ($(CONFIG_USER_PPPD_WITH_TACACS),y)
+PLUGINOBJS += plugins/tacacs.o plugins/tacc/lib/libtac.a
+CFLAGS += -DPLUGIN_TACACS
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_RADIUS),y)
+PLUGINOBJS += plugins/radius/libradius.o
+CFLAGS += -DPLUGIN_RADIUS
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PPPOA),y)
+PLUGINOBJS += plugins/pppoatm/libpppoatm.o
+CFLAGS += -DPLUGIN_PPPOA
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PPPOE),y)
+PLUGINOBJS += plugins/rp-pppoe/libpppoe.o
+CFLAGS += -DPLUGIN_PPPOE
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PPPPPOL2TP),y)
+PLUGINOBJS += plugins/pppol2tp/libpppol2tp.o
+CFLAGS += -DPLUGIN_PPPOL2TP
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PPTP),y)
+PLUGINOBJS += plugins/pptp/libpptp.o
+CFLAGS += -DPLUGIN_PPTP
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_MINCONN),y)
+PLUGINOBJS += plugins/minconn.o
+CFLAGS += -DPLUGIN_MINCONN
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PASSPROMPT),y)
+PLUGINOBJS += plugins/passprompt.o
+CFLAGS += -DPLUGIN_PASSPROMPT
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PASSWORDFD),y)
+PLUGINOBJS += plugins/passwordfd.o
+CFLAGS += -DPLUGIN_PASSWORDFD
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_WINBIND),y)
+PLUGINOBJS += plugins/winbind.o
+CFLAGS += -DPLUGIN_WINBIND
+endif
+endif
+endif
+
+ifdef FILTER
+ifneq ($(wildcard /usr/include/pcap-bpf.h),)
+LIBS    += -lpcap
+CFLAGS  += -DPPP_FILTER
+endif
+endif
+
+ifdef HAVE_INET6
+     PPPDSRCS += ipv6cp.c eui64.c
+     HEADERS  += ipv6cp.h eui64.h
+     PPPDOBJS += ipv6cp.o eui64.o
+     CFLAGS   += -DINET6=1
+endif
+
+ifdef CBCP
+     PPPDSRCS += cbcp.c
+     PPPDOBJS += cbcp.o
+     CFLAGS += -DCBCP_SUPPORT
+     HEADERS += cbcp.h
+endif
+
+ifdef MAXOCTETS
+     CFLAGS += -DMAXOCTETS
+endif
+ifdef CONFIG_DEFAULTS_LIBC_UC_LIBC
+        CFLAGS += -DNO_DRAND48
+else
+        CFLAGS += -DUSE_LASTLOG
+endif
+LDLIBS += -lnvram
+LDLIBS  += -L$(zte_lib_path)/libnvram
+LDLIBS += -lsoftap
+LDLIBS  += -L$(zte_lib_path)/libsoftap
+LDLIBS += -lsoft_timer
+LDLIBS  += -L$(zte_lib_path)/libsoft_timer
+INSTALL= install
+
+all: $(TARGETS)
+
+install: pppd
+	mkdir -p $(BINDIR) $(MANDIR)
+	$(EXTRAINSTALL)
+	$(INSTALL) -s -c -m 555 pppd $(BINDIR)/pppd
+	if chgrp pppusers $(BINDIR)/pppd 2>/dev/null; then \
+	  chmod o-rx,u+s $(BINDIR)/pppd; fi
+	$(INSTALL) -c -m 444 pppd.8 $(MANDIR)
+
+pppd: $(PPPDOBJS)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(PLUGINOBJS) $(LIBS) $(LDLIBS$(LDLIBS_$@))
+
+srp-entry:	srp-entry.c
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ srp-entry.c $(LIBS) $(LDLIBS$(LDLIBS_$@))
+
+install-devel:
+	mkdir -p $(INCDIR)/pppd
+	$(INSTALL) -c -m 644 $(HEADERS) $(INCDIR)/pppd
+
+clean:
+	rm -f $(PPPDOBJS) $(EXTRACLEAN) $(TARGETS) *~ #* core
+
+depend:
+	$(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend
diff --git a/ap/app/pppd/pppd/Makefile.sol2 b/ap/app/pppd/pppd/Makefile.sol2
new file mode 100644
index 0000000..1ad4b13
--- /dev/null
+++ b/ap/app/pppd/pppd/Makefile.sol2
@@ -0,0 +1,63 @@
+#
+# Makefile for pppd under Solaris 2.
+# $Id: Makefile.sol2,v 1.30 2008/01/30 14:26:52 carlsonj Exp $
+#
+
+include ../Makedefs.com
+
+CFLAGS	=  -I../include -DSVR4 -DSOL2 $(COPTS) '-DDESTDIR="@DESTDIR@"'
+LIBS	= -lsocket -lnsl
+
+OBJS	=  main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o eap.o md5.o \
+	tty.o ccp.o ecp.o auth.o options.o demand.o utils.o sys-solaris.o \
+	chap-md5.o session.o
+
+# Solaris uses shadow passwords
+CFLAGS	+= -DHAS_SHADOW
+
+#
+# Comment the following out to disable plugins
+#
+CFLAGS	+= -DPLUGIN
+LIBS	+= -ldl
+
+#
+# Solaris 8 and above accomodates /var/run, so uncomment the
+# following to place pppd process IDs on that location
+#
+#CFLAGS += -D_PATH_VARRUN='"/var/run/"'
+
+#
+# uncomment the following to enable IPv6
+#
+# Solaris 8 and on includes support for IPv6
+#
+#CFLAGS	+= -DINET6
+#OBJS	+= ipv6cp.o eui64.o
+
+# Uncomment to enable MS-CHAP
+#CFLAGS += -DUSE_CRYPT -DCHAPMS -DMSLANMAN -DHAVE_CRYPT_H
+#OBJS += chap_ms.o pppcrypt.o md4.o sha1.o
+
+# Uncomment for CBCP
+#CFLAGS += -DCBCP_SUPPORT
+#OBJS += cbcp.o
+
+# Uncomment for PAM
+#CFLAGS += -DUSE_PAM
+#LIBS += -lpam
+
+#
+# Make targets
+#
+all: pppd
+
+pppd:	$(OBJS)
+	$(CC) -o pppd $(OBJS) $(LIBS)
+
+install:
+	$(INSTALL) -f $(BINDIR) -m 4755 -u root pppd
+	$(INSTALL) -f $(MANDIR)/man8 -m 444 pppd.8
+
+clean:
+	rm -f $(OBJS) pppd *~ core y.tab.c y.tab.h
diff --git a/ap/app/pppd/pppd/auth.c b/ap/app/pppd/pppd/auth.c
new file mode 100644
index 0000000..c557220
--- /dev/null
+++ b/ap/app/pppd/pppd/auth.c
@@ -0,0 +1,2739 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from main.c, which is:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: auth.c,v 1.117 2008/07/01 12:27:56 paulus Exp $"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.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(USE_LASTLOG) && defined(_PATH_LASTLOG) && defined(__linux__)
+#include <lastlog.h>
+#endif
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef PW_PPP
+#define PW_PPP PW_LOGIN
+#endif
+#endif
+#include <time.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "ecp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap-new.h"
+#include "eap.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+#include "pathnames.h"
+#include "session.h"
+
+static const char rcsid[] = RCSID;
+extern char PATH_UPAPFILE[30];
+extern char PATH_CHAPFILE[30];
+/* 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];
+
+extern pppd_auth authinfo[NUM_PPPD_AUTH];	
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NUM_PPP];
+
+/* Records which authentication operations have been completed. */
+int auth_done[NUM_PPP];
+
+/* 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;
+
+/* Remote telephone number, if available */
+char remote_number[MAXNAMELEN];
+
+/* Wordlist giving remote telephone numbers which may connect. */
+static struct wordlist *permitted_numbers;
+
+/* Extra options to apply, from the secrets file entry for the peer. */
+static struct wordlist *extra_options;
+
+/* Number of network protocols which we have opened. */
+static int num_np_open;
+
+/* Number of network protocols which have come up. */
+static int num_np_up;
+
+/* Set if we got the contents of passwd[] from the pap-secrets file. */
+static int passwd_from_file;
+
+/* Set if we require authentication only because we have a default route. */
+static bool default_auth;
+
+/* Hook to enable a plugin to control the idle time limit */
+int (*idle_time_hook) __P((struct ppp_idle *)) = NULL;
+
+/* Hook for a plugin to say whether we can possibly authenticate any peer */
+int (*pap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to check the PAP user and password */
+int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+			  struct wordlist **paddrs,
+			  struct wordlist **popts)) = NULL;
+
+/* Hook for a plugin to know about the PAP user logout */
+void (*pap_logout_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the PAP password for authenticating us */
+int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */
+int (*chap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the CHAP password for authenticating us */
+int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/* Hook for a plugin to say whether it is OK if the peer
+   refuses to authenticate. */
+int (*null_auth_hook) __P((struct wordlist **paddrs,
+			   struct wordlist **popts)) = NULL;
+
+int (*allowed_address_hook) __P((u_int32_t addr)) = NULL;
+
+#ifdef HAVE_MULTILINK
+/* Hook for plugin to hear when an interface joins a multilink bundle */
+void (*multilink_join_hook) __P((void)) = NULL;
+#endif
+
+/* A notifier for when the peer has authenticated itself,
+   and we are proceeding to the network phase. */
+struct notifier *auth_up_notifier = NULL;
+
+/* A notifier for when the link goes down. */
+struct notifier *link_down_notifier = NULL;
+
+/*
+ * This is used to ensure that we don't start an auth-up/down
+ * script while one is already running.
+ */
+enum script_state {
+    s_down,
+    s_up
+};
+
+static enum script_state auth_state = s_down;
+static enum script_state auth_script_state = s_down;
+static pid_t auth_script_pid = 0;
+
+/*
+ * Option variables.
+ */
+bool uselogin = 0;		/* Use /etc/passwd for checking PAP */
+bool session_mgmt = 0;		/* Do session management (login records) */
+bool cryptpap = 0;		/* Passwords in pap-secrets are encrypted */
+bool refuse_pap = 0;		/* Don't wanna auth. ourselves with PAP */
+bool refuse_chap = 0;		/* Don't wanna auth. ourselves with CHAP */
+bool refuse_eap = 0;		/* Don't wanna auth. ourselves with EAP */
+#ifdef CHAPMS
+bool refuse_mschap = 0;		/* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 0;	/* Don't wanna auth. ourselves with MS-CHAPv2 */
+#else
+bool refuse_mschap = 1;		/* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 1;	/* Don't wanna auth. ourselves with MS-CHAPv2 */
+#endif
+bool usehostname = 0;		/* Use hostname for our_name */
+bool auth_required = 0;		/* Always require authentication from peer */
+bool allow_any_ip = 0;		/* Allow peer to use any IP address */
+bool explicit_remote = 0;	/* User specified explicit remote name */
+bool explicit_user = 0;		/* Set if "user" option supplied */
+bool explicit_passwd = 0;	/* Set if "password" option supplied */
+char remote_name[MAXNAMELEN];	/* Peer's name for authentication */
+#if USE_PAM
+bool explicit_pamservice = 0;	/* User specified explicit PAM service */
+char pamservice[MAXNAMELEN];	/* Service for pam_start() */
+bool obey_restrictions = 0; 	/* Obey PAM account restrictions */
+#endif
+const char *auth_group = NULL;	/* If our authentication source can provide us
+				   				   with a group, put it here */
+bool external_auth = 0;		/* If we're using an external authenticator (radius, tacas, etc)
+				   set this flag */
+
+static char *uafname;		/* name of most recent +ua file */
+int ppp_act_num = 0;
+extern char *crypt __P((const char *, const char *));
+
+/* Prototypes for procedures local to this file. */
+
+static void network_phase __P((int));
+static void check_idle __P((void *));
+static void connect_time_expired __P((void *));
+static int  null_login __P((int));
+static int  get_pap_passwd __P((char *));
+static int  have_pap_secret __P((int *));
+static int  have_chap_secret __P((char *, char *, int, int *));
+static int  have_srp_secret __P((char *client, char *server, int need_ip,
+    int *lacks_ipp));
+static int  ip_addr_check __P((u_int32_t, struct permitted_ip *));
+static int  scan_authfile __P((FILE *, char *, char *, char *,
+			       struct wordlist **, struct wordlist **,
+			       char *, int));
+static void free_wordlist __P((struct wordlist *));
+static void auth_script __P((char *));
+static void auth_script_done __P((void *));
+static void set_allowed_addrs __P((int, struct wordlist *, struct wordlist *));
+static int  some_ip_ok __P((struct wordlist *));
+static int  setupapfile __P((char **));
+static int  privgroup __P((char **));
+static int  set_noauth_addr __P((char **));
+static int  set_permitted_number __P((char **));
+static void check_access __P((FILE *, char *));
+static int  wordlist_count __P((struct wordlist *));
+
+#ifdef MAXOCTETS
+static void check_maxoctets __P((void *));
+#endif
+
+/*
+ * Authentication-related options.
+ */
+option_t auth_options[] = {
+    { "auth", o_bool, &auth_required,
+      "Require authentication from peer", OPT_PRIO | 1 },
+    { "noauth", o_bool, &auth_required,
+      "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV,
+      &allow_any_ip },
+    { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
+      "Require PAP authentication from peer",
+      OPT_PRIOSUB | 1, &auth_required },
+    { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
+      "Require PAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
+    { "require-chap", o_bool, &auth_required,
+      "Require CHAP authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+chap", o_bool, &auth_required,
+      "Require CHAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+      &lcp_wantoptions[0].chap_mdtype },
+#ifdef CHAPMS
+    { "require-mschap", o_bool, &auth_required,
+      "Require MS-CHAP authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap", o_bool, &auth_required,
+      "Require MS-CHAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "require-mschap-v2", o_bool, &auth_required,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap-v2", o_bool, &auth_required,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+      &lcp_wantoptions[0].chap_mdtype },
+#endif
+
+    { "refuse-pap", o_bool, &refuse_pap,
+      "Don't agree to auth to peer with PAP", 1 },
+    { "-pap", o_bool, &refuse_pap,
+      "Don't allow PAP authentication with peer", OPT_ALIAS | 1 },
+    { "refuse-chap", o_bool, &refuse_chap,
+      "Don't agree to auth to peer with CHAP",
+      OPT_A2CLRB | MDTYPE_MD5,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-chap", o_bool, &refuse_chap,
+      "Don't allow CHAP authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5,
+      &lcp_allowoptions[0].chap_mdtype },
+#ifdef CHAPMS
+    { "refuse-mschap", o_bool, &refuse_mschap,
+      "Don't agree to auth to peer with MS-CHAP",
+      OPT_A2CLRB | MDTYPE_MICROSOFT,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-mschap", o_bool, &refuse_mschap,
+      "Don't allow MS-CHAP authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "refuse-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't agree to auth to peer with MS-CHAPv2",
+      OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't allow MS-CHAPv2 authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+      &lcp_allowoptions[0].chap_mdtype },
+#endif
+
+    { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap,
+      "Require EAP authentication from peer", OPT_PRIOSUB | 1,
+      &auth_required },
+    { "refuse-eap", o_bool, &refuse_eap,
+      "Don't agree to authenticate to peer with EAP", 1 },
+
+    { "name", o_string, our_name,
+      "Set local name for authentication",
+      OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN },
+
+    { "+ua", o_special, (void *)setupapfile,
+      "Get PAP user and password from file",
+      OPT_PRIO | OPT_A2STRVAL, &uafname },
+
+    { "user", o_string, user,
+      "Set name for auth with peer", OPT_PRIO | OPT_STATIC,
+      &explicit_user, MAXNAMELEN },
+
+    { "password", o_string, passwd,
+      "Password for authenticating us to the peer",
+      OPT_PRIO | OPT_STATIC | OPT_HIDE,
+      &explicit_passwd, MAXSECRETLEN },
+
+    { "usehostname", o_bool, &usehostname,
+      "Must use hostname for authentication", 1 },
+
+    { "remotename", o_string, remote_name,
+      "Set remote name for authentication", OPT_PRIO | OPT_STATIC,
+      &explicit_remote, MAXNAMELEN },
+
+    { "login", o_bool, &uselogin,
+      "Use system password database for PAP", OPT_A2COPY | 1 ,
+      &session_mgmt },
+    { "enable-session", o_bool, &session_mgmt,
+      "Enable session accounting for remote peers", OPT_PRIV | 1 },
+
+    { "papcrypt", o_bool, &cryptpap,
+      "PAP passwords are encrypted", 1 },
+
+    { "privgroup", o_special, (void *)privgroup,
+      "Allow group members to use privileged options", OPT_PRIV | OPT_A2LIST },
+
+    { "allow-ip", o_special, (void *)set_noauth_addr,
+      "Set IP address(es) which can be used without authentication",
+      OPT_PRIV | OPT_A2LIST },
+
+    { "remotenumber", o_string, remote_number,
+      "Set remote telephone number for authentication", OPT_PRIO | OPT_STATIC,
+      NULL, MAXNAMELEN },
+
+    { "allow-number", o_special, (void *)set_permitted_number,
+      "Set telephone number(s) which are allowed to connect",
+      OPT_PRIV | OPT_A2LIST },
+
+#ifdef USE_PAM
+    { "pamservice", o_string, pamservice,
+      "Set PAM service for authentication", OPT_PRIO | OPT_STATIC,
+      &explicit_pamservice, MAXNAMELEN },
+    { "obey_acct_restrict", o_bool, &obey_restrictions,
+      "Obey any PAM account restrictions for an authed user", 1},
+#endif
+    { NULL }
+};
+
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(argv)
+    char **argv;
+{
+    FILE *ufile;
+    int l;
+    uid_t euid;
+    char u[MAXNAMELEN], p[MAXSECRETLEN];
+    char *fname;
+
+    lcp_allowoptions[0].neg_upap = 1;
+
+    /* open user info file */
+    fname = strdup(*argv);
+    if (fname == NULL)
+	novm("+ua file name");
+    euid = geteuid();
+    if (seteuid(getuid()) == -1) {
+	option_error("unable to reset uid before opening %s: %m", fname);
+	return 0;
+    }
+    ufile = fopen(fname, "r");
+    if (seteuid(euid) == -1)
+	fatal("unable to regain privileges: %m");
+    if (ufile == NULL) {
+	option_error("unable to open user login data file %s", fname);
+	return 0;
+    }
+    check_access(ufile, fname);
+    uafname = fname;
+
+    /* get username */
+    if (fgets(u, MAXNAMELEN - 1, ufile) == NULL
+	|| fgets(p, MAXSECRETLEN - 1, ufile) == NULL) {
+	fclose(ufile);
+	option_error("unable to read user login data file %s", fname);
+	return 0;
+    }
+    fclose(ufile);
+
+    /* get rid of newlines */
+    l = strlen(u);
+    if (l > 0 && u[l-1] == '\n')
+	u[l-1] = 0;
+    l = strlen(p);
+    if (l > 0 && p[l-1] == '\n')
+	p[l-1] = 0;
+
+    if (override_value("user", option_priority, fname)) {
+	strlcpy(user, u, sizeof(user));
+	explicit_user = 1;
+    }
+    if (override_value("passwd", option_priority, fname)) {
+	strlcpy(passwd, p, sizeof(passwd));
+	explicit_passwd = 1;
+    }
+
+    return (1);
+}
+
+
+/*
+ * privgroup - allow members of the group to have privileged access.
+ */
+static int
+privgroup(argv)
+    char **argv;
+{
+    struct group *g;
+    int i;
+
+    g = getgrnam(*argv);
+    if (g == 0) {
+	option_error("group %s is unknown", *argv);
+	return 0;
+    }
+    for (i = 0; i < ngroups; ++i) {
+	if (groups[i] == g->gr_gid) {
+	    privileged = 1;
+	    break;
+	}
+    }
+    return 1;
+}
+
+
+/*
+ * set_noauth_addr - set address(es) that can be used without authentication.
+ * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets.
+ */
+static int
+set_noauth_addr(argv)
+    char **argv;
+{
+    char *addr = *argv;
+    int l = strlen(addr) + 1;
+    struct wordlist *wp;
+
+    wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+    if (wp == NULL)
+	novm("allow-ip argument");
+    wp->word = (char *) (wp + 1);
+    wp->next = noauth_addrs;
+    BCOPY(addr, wp->word, l);
+    noauth_addrs = wp;
+    return 1;
+}
+
+
+/*
+ * set_permitted_number - set remote telephone number(s) that may connect.
+ */
+static int
+set_permitted_number(argv)
+    char **argv;
+{
+    char *number = *argv;
+    int l = strlen(number) + 1;
+    struct wordlist *wp;
+
+    wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+    if (wp == NULL)
+	novm("allow-number argument");
+    wp->word = (char *) (wp + 1);
+    wp->next = permitted_numbers;
+    BCOPY(number, wp->word, l);
+    permitted_numbers = wp;
+    return 1;
+}
+
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ */
+void
+link_required(unit)
+    int unit;
+{
+}
+
+/*
+ * Bring the link up to the point of being able to do ppp.
+ */
+void start_link(unit)
+    int unit;
+{
+    char *msg;
+    new_phase(PHASE_SERIALCONN);
+    hungup = 0;
+    devfd = the_channel->connect();
+	warn("start_link, now the devfd is %d", devfd);
+    msg = "Connect script failed";
+    if (devfd < 0)
+	goto fail;
+    /* set up the serial device as a ppp interface */
+    /*
+     * N.B. we used to do tdb_writelock/tdb_writeunlock around this
+     * (from establish_ppp to set_ifunit).  However, we won't be
+     * doing the set_ifunit in multilink mode, which is the only time
+     * we need the atomicity that the tdb_writelock/tdb_writeunlock
+     * gives us.  Thus we don't need the tdb_writelock/tdb_writeunlock.
+     */
+    fd_ppp = the_channel->establish_ppp(devfd);
+	warn("establish ppp fd is %d", fd_ppp);
+    msg = "ppp establishment failed";
+    if (fd_ppp < 0) {
+	status = EXIT_FATAL_ERROR;
+	goto disconnect;
+    }
+
+    if (!demand && ifunit >= 0)
+	set_ifunit(1);
+
+    /*
+     * Start opening the connection and wait for
+     * incoming events (reply, timeout, etc.).
+     */
+    if (ifunit >= 0)
+	warn("Connect: %s <--> %s", ifname, ppp_devnam);
+    else
+	warn("Starting negotiation on %s", ppp_devnam);
+    add_fd(fd_ppp);
+
+    status = EXIT_NEGOTIATION_FAILED;
+    new_phase(PHASE_ESTABLISH);
+
+    lcp_lowerup(0);
+    return;
+
+ disconnect:
+    new_phase(PHASE_DISCONNECT);
+    if (the_channel->disconnect)
+	the_channel->disconnect();
+
+ fail:
+    new_phase(PHASE_DEAD);
+    if (the_channel->cleanup)
+	(*the_channel->cleanup)();
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void
+link_terminated(unit)
+    int unit;
+{
+    if (phase == PHASE_DEAD || phase == PHASE_MASTER)
+	return;
+    new_phase(PHASE_DISCONNECT);
+
+    if (pap_logout_hook) {
+	pap_logout_hook();
+    }
+    session_end(devnam);
+
+    if (!doing_multilink) {
+	warn("Connection terminated.");
+	print_link_stats();
+    } else
+	warn("Link terminated.");
+
+    /*
+     * Delete pid files before disestablishing ppp.  Otherwise it
+     * can happen that another pppd gets the same unit and then
+     * we delete its pid file.
+     */
+    if (!doing_multilink && !demand)
+	remove_pidfiles();
+
+    /*
+     * If we may want to bring the link up again, transfer
+     * the ppp unit back to the loopback.  Set the
+     * real serial device back to its normal mode of operation.
+     */
+    if (fd_ppp >= 0) {
+	remove_fd(fd_ppp);
+	clean_check();
+	the_channel->disestablish_ppp(devfd);
+	if (doing_multilink)
+	    mp_exit_bundle();
+	fd_ppp = -1;
+    }
+    if (!hungup)
+	lcp_lowerdown(0);
+    if (!doing_multilink && !demand)
+	script_unsetenv("IFNAME");
+
+    /*
+     * Run disconnector script, if requested.
+     * XXX we may not be able to do this if the line has hung up!
+     */
+    if (devfd >= 0 && the_channel->disconnect) {
+	the_channel->disconnect();
+	devfd = -1;
+    }
+    if (the_channel->cleanup)
+	(*the_channel->cleanup)();
+
+    if (doing_multilink && multilink_master) {
+	if (!bundle_terminating)
+	    new_phase(PHASE_MASTER);
+	else
+	    mp_bundle_terminated();
+    } else
+	new_phase(PHASE_DEAD);
+
+	warn("pppd: link_terminated erase the NV value %s---->%d", __FILE__, __LINE__);
+	sc_cfg_set("lcp_finish","");
+	sc_cfg_set("ip_ok","");
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void
+link_down(unit)
+    int unit;
+{
+    if (auth_state != s_down) {
+	notify(link_down_notifier, 0);
+	auth_state = s_down;
+	if (auth_script_state == s_up && auth_script_pid == 0) {
+	    update_link_stats(unit);
+	    auth_script_state = s_down;
+	    auth_script(_PATH_AUTHDOWN);
+	}
+    }
+    if (!doing_multilink) {
+	upper_layers_down(unit);
+	if (phase != PHASE_DEAD && phase != PHASE_MASTER)
+	    new_phase(PHASE_ESTABLISH);
+    }
+    /* XXX if doing_multilink, should do something to stop
+       network-layer traffic on the link */
+}
+
+void upper_layers_down(int unit)
+{
+    int i;
+    struct protent *protp;
+
+    for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+	if (!protp->enabled_flag)
+	    continue;
+        if (protp->protocol != PPP_LCP && protp->lowerdown != NULL)
+	    (*protp->lowerdown)(unit);
+        if (protp->protocol < 0xC000 && protp->close != NULL)
+	    (*protp->close)(unit, "LCP down");
+    }
+    num_np_open = 0;
+    num_np_up = 0;
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(unit)
+    int unit;
+{
+    int auth;
+    lcp_options *wo = &lcp_wantoptions[unit];
+    lcp_options *go = &lcp_gotoptions[unit];
+    lcp_options *ho = &lcp_hisoptions[unit];
+    int i;
+    struct protent *protp;
+    /*
+     * Tell higher-level protocols that LCP is up.
+     */
+    if (!doing_multilink) {
+	for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	    if (protp->protocol != PPP_LCP && protp->enabled_flag
+		&& protp->lowerup != NULL)
+		(*protp->lowerup)(unit);
+    }
+
+    if (!auth_required && noauth_addrs != NULL)
+    	{
+	set_allowed_addrs(unit, NULL, NULL);
+    	}
+    if (auth_required && !(go->neg_upap || go->neg_chap || go->neg_eap)) {
+	/*
+	 * We wanted the peer to authenticate itself, and it refused:
+	 * if we have some address(es) it can use without auth, fine,
+	 * otherwise treat it as though it authenticated with PAP using
+	 * a username of "" and a password of "".  If that's not OK,
+	 * boot it out.
+	 */
+	if (noauth_addrs != NULL) {
+	    set_allowed_addrs(unit, NULL, NULL);
+	} else if (!wo->neg_upap || uselogin || !null_login(unit)) {
+	    warn("peer refused to authenticate: terminating link");
+	    status = EXIT_PEER_AUTH_FAILED;
+	    lcp_close(unit, "peer refused to authenticate");
+	    return;
+	}
+    }
+
+    new_phase(PHASE_AUTHENTICATE);
+	warn("pppd: %s---->%d, auth %d, %d, %d, %d %d, %d", __FILE__, __LINE__, go->neg_eap,
+		go->neg_chap, go->neg_upap, ho->neg_eap, ho->neg_chap, ho->neg_upap);
+    auth = 0;
+    if (go->neg_eap) {
+	eap_authpeer(unit, our_name);
+	auth |= EAP_PEER;
+    } else if (go->neg_chap) {
+	chap_auth_peer(unit, our_name, CHAP_DIGEST(go->chap_mdtype));
+	auth |= CHAP_PEER;
+    } else if (go->neg_upap) {
+	upap_authpeer(unit);
+	auth |= PAP_PEER;
+    }
+    if (ho->neg_eap) {
+	eap_authwithpeer(unit, user);
+	auth |= EAP_WITHPEER;
+    } else if (ho->neg_chap) {
+	chap_auth_with_peer(unit, user, CHAP_DIGEST(ho->chap_mdtype));
+	auth |= CHAP_WITHPEER;
+    } else if (ho->neg_upap) {
+	/* If a blank password was explicitly given as an option, trust
+	   the user and don't try to look up one. */
+	if (passwd[0] == 0 && !explicit_passwd) {
+	    passwd_from_file = 1;
+	    if (!get_pap_passwd(passwd))
+		error("No secret found for PAP login");
+	}
+	upap_authwithpeer(unit, user, passwd);
+	auth |= PAP_WITHPEER;
+    }
+    auth_pending[unit] = auth;
+    auth_done[unit] = 0;
+	warn("pppd: %s---->%d, auth is %d", __FILE__, __LINE__, auth);
+	if(auth == 0)
+	{
+	    //zdm
+	    AT_PDP_ACT_REQ_INFO pdp_info = {0};
+        pdp_info.auth_type = PPP_NONE_AUTH;
+     	strcpy(pdp_info.ip_type,"IP");
+        
+		memset(authinfo, 0, sizeof(struct pppd_auth));
+		authinfo[0].auth_type = PPP_NONE_AUTH;
+		if(ppp_act_num == 0)
+		{
+			ipc_send_message(MODULE_ID_PPPD, MODULE_ID_AT_CTL, MSG_CMD_PDP_ACT_REQ, sizeof(AT_PDP_ACT_REQ_INFO), (UCHAR *)&pdp_info,0);
+			ppp_act_num=1;
+		}
+	}
+	
+	
+    if (!auth)
+    	{
+		network_phase(unit);
+    	}
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void
+network_phase(unit)
+    int unit;
+{
+    lcp_options *go = &lcp_gotoptions[unit];
+	char lcp_finish[100] = {0};
+    /* Log calling number. */
+    if (*remote_number)
+	warn("peer from calling number %q authorized", remote_number);
+
+    /*
+     * If the peer had to authenticate, run the auth-up script now.
+     */
+    if (go->neg_chap || go->neg_upap || go->neg_eap) {
+	notify(auth_up_notifier, 0);
+	auth_state = s_up;
+	if (auth_script_state == s_down && auth_script_pid == 0) {
+	    auth_script_state = s_up;
+	    auth_script(_PATH_AUTHUP);
+	}
+    }
+
+#ifdef CBCP_SUPPORT
+    /*
+     * If we negotiated callback, do it now.
+     */
+    if (go->neg_cbcp) {
+	new_phase(PHASE_CALLBACK);
+	(*cbcp_protent.open)(unit);
+	return;
+    }
+#endif
+
+    /*
+     * Process extra options from the secrets file
+     */
+    if (extra_options) {
+	options_from_list(extra_options, 1);
+	free_wordlist(extra_options);
+	extra_options = 0;
+    }
+	
+	sc_cfg_set("lcp_finish","lcp_finish");
+	sc_cfg_get("lcp_finish",lcp_finish,sizeof(lcp_finish));
+	warn("pppd: %s---->%d, now lcp_finish is %s", __FILE__, __LINE__, lcp_finish);
+    start_networks(unit);
+}
+
+void
+start_networks(unit)
+    int unit;
+{
+    int i;
+    struct protent *protp;
+    int ecp_required, mppe_required;
+
+    new_phase(PHASE_NETWORK);
+
+#ifdef HAVE_MULTILINK
+    if (multilink) {
+	if (mp_join_bundle()) {
+	    if (multilink_join_hook)
+		(*multilink_join_hook)();
+	    if (updetach && !nodetach)
+		detach();
+	    return;
+	}
+    }
+#endif /* HAVE_MULTILINK */
+
+#ifdef PPP_FILTER
+    if (!demand)
+	set_filters(&pass_filter, &active_filter);
+#endif
+    /* Start CCP and ECP */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if ((protp->protocol == PPP_ECP || protp->protocol == PPP_CCP)
+	    && protp->enabled_flag && protp->open != NULL)
+	    (*protp->open)(0);
+
+    /*
+     * Bring up other network protocols iff encryption is not required.
+     */
+    ecp_required = ecp_gotoptions[unit].required;
+    mppe_required = ccp_gotoptions[unit].mppe;
+    if (!ecp_required && !mppe_required)
+	continue_networks(unit);
+}
+
+void
+continue_networks(unit)
+    int unit;
+{
+    int i;
+    struct protent *protp;
+
+    /*
+     * Start the "real" network protocols.
+     */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if (protp->protocol < 0xC000
+	    && protp->protocol != PPP_CCP && protp->protocol != PPP_ECP
+	    && protp->enabled_flag && protp->open != NULL) {
+	    (*protp->open)(0);
+	    ++num_np_open;
+	}
+
+    if (num_np_open == 0)
+	/* nothing to do */
+	lcp_close(0, "No network protocols running");
+}
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void
+auth_peer_fail(unit, protocol)
+    int unit, protocol;
+{
+    /*
+     * Authentication failure: take the link down
+     */
+    status = EXIT_PEER_AUTH_FAILED;
+    if (auth_group) {
+		free(auth_group);
+		auth_group = NULL;
+    }
+
+    lcp_close(unit, "Authentication failed");
+}
+
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(unit, protocol, prot_flavor, name, namelen)
+    int unit, protocol, prot_flavor;
+    char *name;
+    int namelen;
+{
+    int bit;
+
+    switch (protocol) {
+    case PPP_CHAP:
+	bit = CHAP_PEER;
+	switch (prot_flavor) {
+	case CHAP_MD5:
+	    bit |= CHAP_MD5_PEER;
+	    break;
+#ifdef CHAPMS
+	case CHAP_MICROSOFT:
+	    bit |= CHAP_MS_PEER;
+	    break;
+	case CHAP_MICROSOFT_V2:
+	    bit |= CHAP_MS2_PEER;
+	    break;
+#endif
+	}
+	break;
+    case PPP_PAP:
+	bit = PAP_PEER;
+	break;
+    case PPP_EAP:
+	bit = EAP_PEER;
+	break;
+    default:
+	warn("auth_peer_success: unknown protocol %x", protocol);
+	return;
+    }
+
+    /*
+     * Save the authenticated name of the peer for later.
+     */
+    if (namelen > sizeof(peer_authname) - 1)
+	namelen = sizeof(peer_authname) - 1;
+    BCOPY(name, peer_authname, namelen);
+    peer_authname[namelen] = 0;
+    script_setenv("PEERNAME", peer_authname, 0);
+
+    /* Save the authentication method for later. */
+    auth_done[unit] |= bit;
+
+    /*
+     * If there is no more authentication still to be done,
+     * proceed to the network (or callback) phase.
+     */
+    if ((auth_pending[unit] &= ~bit) == 0)
+        network_phase(unit);
+
+    if (auth_group) {
+		free(auth_group);
+		auth_group = NULL;
+    }
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void
+auth_withpeer_fail(unit, protocol)
+    int unit, protocol;
+{
+    if (passwd_from_file)
+	BZERO(passwd, MAXSECRETLEN);
+    /*
+     * We've failed to authenticate ourselves to our peer.
+     * Some servers keep sending CHAP challenges, but there
+     * is no point in persisting without any way to get updated
+     * authentication secrets.
+     */
+    status = EXIT_AUTH_TOPEER_FAILED;
+    lcp_close(unit, "Failed to authenticate ourselves to peer");
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(unit, protocol, prot_flavor)
+    int unit, protocol, prot_flavor;
+{
+    int bit;
+    const char *prot = "";
+
+    switch (protocol) {
+    case PPP_CHAP:
+	bit = CHAP_WITHPEER;
+	prot = "CHAP";
+	switch (prot_flavor) {
+	case CHAP_MD5:
+	    bit |= CHAP_MD5_WITHPEER;
+	    break;
+#ifdef CHAPMS
+	case CHAP_MICROSOFT:
+	    bit |= CHAP_MS_WITHPEER;
+	    break;
+	case CHAP_MICROSOFT_V2:
+	    bit |= CHAP_MS2_WITHPEER;
+	    break;
+#endif
+	}
+	break;
+    case PPP_PAP:
+	if (passwd_from_file)
+	    BZERO(passwd, MAXSECRETLEN);
+	bit = PAP_WITHPEER;
+	prot = "PAP";
+	break;
+    case PPP_EAP:
+	bit = EAP_WITHPEER;
+	prot = "EAP";
+	break;
+    default:
+	warn("auth_withpeer_success: unknown protocol %x", protocol);
+	bit = 0;
+    }
+
+    warn("%s authentication succeeded", prot);
+
+    /* Save the authentication method for later. */
+    auth_done[unit] |= bit;
+
+    /*
+     * If there is no more authentication still being done,
+     * proceed to the network (or callback) phase.
+     */
+    if ((auth_pending[unit] &= ~bit) == 0)
+	network_phase(unit);
+}
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void
+np_up(unit, proto)
+    int unit, proto;
+{
+    int tlim;
+
+    if (num_np_up == 0) {
+	/*
+	 * At this point we consider that the link has come up successfully.
+	 */
+	status = EXIT_OK;
+	unsuccess = 0;
+	new_phase(PHASE_RUNNING);
+
+	if (idle_time_hook != 0)
+	    tlim = (*idle_time_hook)(NULL);
+	else
+	    tlim = idle_time_limit;
+	if (tlim > 0)
+	    TIMEOUT(check_idle, NULL, tlim);
+
+	/*
+	 * Set a timeout to close the connection once the maximum
+	 * connect time has expired.
+	 */
+	if (maxconnect > 0)
+	    TIMEOUT(connect_time_expired, 0, maxconnect);
+
+#ifdef MAXOCTETS
+	if (maxoctets > 0)
+	    TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+#endif
+
+	/*
+	 * Detach now, if the updetach option was given.
+	 */
+	if (updetach && !nodetach)
+	    detach();
+    }
+    ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void
+np_down(unit, proto)
+    int unit, proto;
+{
+    if (--num_np_up == 0) {
+	UNTIMEOUT(check_idle, NULL);
+	UNTIMEOUT(connect_time_expired, NULL);
+#ifdef MAXOCTETS
+	UNTIMEOUT(check_maxoctets, NULL);
+#endif	
+	new_phase(PHASE_NETWORK);
+    }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void
+np_finished(unit, proto)
+    int unit, proto;
+{
+    if (--num_np_open <= 0) {
+	/* no further use for the link: shut up shop. */
+	lcp_close(0, "No network protocols running");
+    }
+}
+
+#ifdef MAXOCTETS
+static void
+check_maxoctets(arg)
+    void *arg;
+{
+    unsigned int used;
+
+    update_link_stats(ifunit);
+    link_stats_valid=0;
+    
+    switch(maxoctets_dir) {
+	case PPP_OCTETS_DIRECTION_IN:
+	    used = link_stats.bytes_in;
+	    break;
+	case PPP_OCTETS_DIRECTION_OUT:
+	    used = link_stats.bytes_out;
+	    break;
+	case PPP_OCTETS_DIRECTION_MAXOVERAL:
+	case PPP_OCTETS_DIRECTION_MAXSESSION:
+	    used = (link_stats.bytes_in > link_stats.bytes_out) ? link_stats.bytes_in : link_stats.bytes_out;
+	    break;
+	default:
+	    used = link_stats.bytes_in+link_stats.bytes_out;
+	    break;
+    }
+    if (used > maxoctets) {
+	warn("Traffic limit reached. Limit: %u Used: %u", maxoctets, used);
+	status = EXIT_TRAFFIC_LIMIT;
+	lcp_close(0, "Traffic limit");
+	need_holdoff = 0;
+    } else {
+        TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+    }
+}
+#endif
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void
+check_idle(arg)
+    void *arg;
+{
+    struct ppp_idle idle;
+    time_t itime;
+    int tlim;
+
+    if (!get_idle_time(0, &idle))
+	return;
+    if (idle_time_hook != 0) {
+	tlim = idle_time_hook(&idle);
+    } else {
+	itime = MIN(idle.xmit_idle, idle.recv_idle);
+	tlim = idle_time_limit - itime;
+    }
+    if (tlim <= 0) {
+	/* link is idle: shut it down. */
+	warn("Terminating connection due to lack of activity.");
+	status = EXIT_IDLE_TIMEOUT;
+	lcp_close(0, "Link inactive");
+	need_holdoff = 0;
+    } else {
+	TIMEOUT(check_idle, NULL, tlim);
+    }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void
+connect_time_expired(arg)
+    void *arg;
+{
+    warn("Connect time expired");
+    status = EXIT_CONNECT_TIME;
+    lcp_close(0, "Connect time expired");	/* Close connection */
+}
+
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options()
+{
+    lcp_options *wo = &lcp_wantoptions[0];
+    int can_auth;
+    int lacks_ip;
+
+    /* Default our_name to hostname, and user to our_name */
+    if (our_name[0] == 0 || usehostname)
+	strlcpy(our_name, hostname, sizeof(our_name));
+    /* If a blank username was explicitly given as an option, trust
+       the user and don't use our_name */
+    if (user[0] == 0 && !explicit_user)
+	strlcpy(user, our_name, sizeof(user));
+
+    /*
+     * If we have a default route, require the peer to authenticate
+     * unless the noauth option was given or the real user is root.
+     */
+    if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) {
+	auth_required = 1;
+	default_auth = 1;
+    }
+
+    /* If we selected any CHAP flavors, we should probably negotiate it. :-) */
+    if (wo->chap_mdtype)
+	wo->neg_chap = 1;
+
+    /* If authentication is required, ask peer for CHAP, PAP, or EAP. */
+    if (auth_required) {
+	allow_any_ip = 0;
+	if (!wo->neg_chap && !wo->neg_upap && !wo->neg_eap) {
+	    wo->neg_chap = chap_mdtype_all != MDTYPE_NONE;
+	    wo->chap_mdtype = chap_mdtype_all;
+	    wo->neg_upap = 1;
+	    wo->neg_eap = 1;
+	}
+    } else {
+	wo->neg_chap = 0;
+	wo->chap_mdtype = MDTYPE_NONE;
+	wo->neg_upap = 0;
+	wo->neg_eap = 0;
+    }
+
+    /*
+     * Check whether we have appropriate secrets to use
+     * to authenticate the peer.  Note that EAP can authenticate by way
+     * of a CHAP-like exchanges as well as SRP.
+     */
+    lacks_ip = 0;
+    can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip));
+    if (!can_auth && (wo->neg_chap || wo->neg_eap)) {
+	can_auth = have_chap_secret((explicit_remote? remote_name: NULL),
+				    our_name, 1, &lacks_ip);
+    }
+    if (!can_auth && wo->neg_eap) {
+	can_auth = have_srp_secret((explicit_remote? remote_name: NULL),
+				    our_name, 1, &lacks_ip);
+    }
+
+    if (auth_required && !can_auth && noauth_addrs == NULL) {
+	if (default_auth) {
+	    option_error(
+"By default the remote system is required to authenticate itself");
+	    option_error(
+"(because this system has a default route to the internet)");
+	} else if (explicit_remote)
+	    option_error(
+"The remote system (%s) is required to authenticate itself",
+			 remote_name);
+	else
+	    option_error(
+"The remote system is required to authenticate itself");
+	option_error(
+"but I couldn't find any suitable secret (password) for it to use to do so.");
+	if (lacks_ip)
+	    option_error(
+"(None of the available passwords would let it use an IP address.)");
+
+	exit(1);
+    }
+
+    /*
+     * Early check for remote number authorization.
+     */
+    if (!auth_number()) {
+	warn("calling number %q is not authorized", remote_number);
+	exit(EXIT_CNID_AUTH_FAILED);
+    }
+}
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(unit)
+    int unit;
+{
+    lcp_options *go = &lcp_gotoptions[unit];
+    lcp_options *ao = &lcp_allowoptions[unit];
+    int hadchap;
+
+    hadchap = -1;
+    ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
+    ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2)
+	&& (passwd[0] != 0 ||
+	    (hadchap = have_chap_secret(user, (explicit_remote? remote_name:
+					       NULL), 0, NULL)));
+    ao->neg_eap = !refuse_eap && (
+	passwd[0] != 0 ||
+	(hadchap == 1 || (hadchap == -1 && have_chap_secret(user,
+	    (explicit_remote? remote_name: NULL), 0, NULL))) ||
+	have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL));
+
+    hadchap = -1;
+    if (go->neg_upap && !uselogin && !have_pap_secret(NULL))
+	go->neg_upap = 0;
+    if (go->neg_chap) {
+	if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL),
+			      our_name, 1, NULL)))
+	    go->neg_chap = 0;
+    }
+
+    if (go->neg_eap &&
+	(hadchap == 0 || (hadchap == -1 &&
+	    !have_chap_secret((explicit_remote? remote_name: NULL), our_name,
+		1, NULL))) &&
+	!have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1,
+	    NULL))
+	go->neg_eap = 0;
+}
+
+
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file.  If requested, also check against the system password database,
+ * and login the user if OK.
+ * ÊÊÓÃÓÚPAP
+ * 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 = "";
+	warn("pppd: %s---->%d, user is %s, pass is %s", __FILE__, __LINE__, user, passwd);	
+    /*
+     * Check if a plugin wants to handle this.
+     */
+    if (pap_auth_hook) {
+	ret = (*pap_auth_hook)(user, passwd, msg, &addrs, &opts);
+	if (ret >= 0) {
+	    /* note: set_allowed_addrs() saves opts (but not addrs):
+	       don't free it! */
+	    if (ret)
+		set_allowed_addrs(unit, addrs, opts);
+	    else if (opts != 0)
+		free_wordlist(opts);
+	    if (addrs != 0)
+		free_wordlist(addrs);
+	    BZERO(passwd, sizeof(passwd));
+	    return ret? UPAP_AUTHACK: UPAP_AUTHNAK;
+	}
+    }
+
+    /*
+     * Open the file of pap secrets and scan for a suitable secret
+     * for authenticating this user.
+     */
+    //filename = _PATH_UPAPFILE;
+    addrs = opts = NULL;
+    ret = UPAP_AUTHNAK;
+    f = fopen(PATH_UPAPFILE, "r");
+    if (f == NULL) {
+	error("Can't open PAP password file %s: %m", PATH_UPAPFILE);
+
+    } else {
+	check_access(f, PATH_UPAPFILE);
+	if (scan_authfile(f, user, our_name, secret, &addrs, &opts, PATH_UPAPFILE, 0) < 0) {
+		/*wangming modified*/
+	    warn("no PAP secret found for %s, but we force set it as ACK", user);
+        //zdm
+        AT_PDP_ACT_REQ_INFO pdp_info = {0};
+        pdp_info.auth_type = authinfo[0].auth_type;
+        memcpy(pdp_info.username, authinfo[0].user, USER_MAX);
+        memcpy(pdp_info.password, authinfo[0].pw, PW_MAX);
+        memcpy(pdp_info.challenge, authinfo[0].challenge, CHALLENGE_MAX);
+     	strcpy(pdp_info.ip_type,"IP");
+		
+		if(ppp_act_num == 0)
+		{
+			ipc_send_message(MODULE_ID_PPPD, MODULE_ID_AT_CTL, MSG_CMD_PDP_ACT_REQ, sizeof(AT_PDP_ACT_REQ_INFO), (UCHAR *)&pdp_info,0);
+			ppp_act_num=2;
+		}
+		/*here send msg to atserver, todo*/
+		 ret = UPAP_AUTHACK;
+	} else {
+	    /*
+	     * If the secret is "@login", it means to check
+	     * the password against the login database.
+	     */
+	    int login_secret = strcmp(secret, "@login") == 0;
+	    ret = UPAP_AUTHACK;
+	    if (uselogin || login_secret) {
+		/* login option or secret is @login */
+		if (session_full(user, passwd, devnam, msg) == 0) {
+		    ret = UPAP_AUTHNAK;
+		}
+	    } else if (session_mgmt) {
+		if (session_check(user, NULL, devnam, NULL) == 0) {
+		    warn("Peer %q failed PAP Session verification", user);
+		    ret = UPAP_AUTHNAK;
+		}
+	    }
+	    if (secret[0] != 0 && !login_secret) {
+		/* password given in pap-secrets - must match */
+		if ((cryptpap || strcmp(passwd, secret) != 0)
+		    && strcmp(crypt(passwd, secret), secret) != 0)
+		    ret = UPAP_AUTHNAK;
+	    }
+	}
+	fclose(f);
+    }
+
+    if (ret == UPAP_AUTHNAK) {
+        if (**msg == 0)
+	    *msg = "Login incorrect";
+	/*
+	 * XXX can we ever get here more than once??
+	 * Frustrate passwd stealer programs.
+	 * Allow 10 tries, but start backing off after 3 (stolen from login).
+	 * On 10'th, drop the connection.
+	 */
+	if (attempts++ >= 10) {
+	    warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user);
+	    lcp_close(unit, "login failed");
+	}
+	if (attempts > 3)
+	    sleep((u_int) (attempts - 3) * 5);
+	if (opts != NULL)
+	    free_wordlist(opts);
+
+    } else {
+	attempts = 0;			/* Reset count */
+	if (**msg == 0)
+	    *msg = "Login ok";
+	set_allowed_addrs(unit, addrs, opts);
+    }
+
+    if (addrs != NULL)
+	free_wordlist(addrs);
+    BZERO(passwd, sizeof(passwd));
+    BZERO(secret, sizeof(secret));
+
+    return ret;
+}
+
+/*
+ * This function is needed for PAM.
+ */
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+
+/* 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,
+#ifndef SOL2
+    const
+#endif
+    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 (explicit_pamservice ? pamservice : "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, ifname);		/* Add wtmp login entry */
+
+#if defined(USE_LASTLOG) && 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 /* USE_LASTLOG and _PATH_LASTLOG and not USE_PAM */
+
+    warn("user %s logged in", user);
+    logged_in = 1;
+
+    return (UPAP_AUTHACK);
+}
+
+/*
+ * plogout - Logout the user.
+ */
+static void
+plogout()
+{
+    char *tty;
+#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();
+#endif /* USE_PAM */
+
+    tty = devnam;
+    if (strncmp(tty, "/dev/", 5) == 0)
+	tty += 5;
+    logwtmp(tty, "", "");		/* Wipe out utmp logout entry */
+    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];
+
+    /*
+     * Check if a plugin wants to handle this.
+     */
+    ret = -1;
+    if (null_auth_hook)
+	ret = (*null_auth_hook)(&addrs, &opts);
+
+    /*
+     * Open the file of pap secrets and scan for a suitable secret.
+     */
+    if (ret <= 0) {
+	//filename = _PATH_UPAPFILE;
+	addrs = NULL;
+	f = fopen(PATH_UPAPFILE, "r");
+	if (f == NULL)
+	    return 0;
+	check_access(f, PATH_UPAPFILE);
+
+	i = scan_authfile(f, "", our_name, secret, &addrs, &opts, PATH_UPAPFILE, 0);
+	ret = i >= 0 && secret[0] == 0;
+	BZERO(secret, sizeof(secret));
+	fclose(f);
+    }
+
+    if (ret)
+	set_allowed_addrs(unit, addrs, opts);
+    else if (opts != 0)
+	free_wordlist(opts);
+    if (addrs != 0)
+	free_wordlist(addrs);
+
+    return ret;
+}
+
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP.  Returns 1 on success, 0 if no suitable password
+ * could be found.
+ * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null).
+ */
+static int
+get_pap_passwd(passwd)
+    char *passwd;
+{
+    //char *filename;
+    FILE *f;
+    int ret;
+    char secret[MAXWORDLEN];
+
+    /*
+     * Check whether a plugin wants to supply this.
+     */
+    if (pap_passwd_hook) {
+	ret = (*pap_passwd_hook)(user, passwd);
+	if (ret >= 0)
+	    return ret;
+    }
+
+    //filename = _PATH_UPAPFILE;
+    f = fopen(PATH_UPAPFILE, "r");
+    if (f == NULL)
+	return 0;
+    check_access(f, PATH_UPAPFILE);
+    ret = scan_authfile(f, user,
+			(remote_name[0]? remote_name: NULL),
+			secret, NULL, NULL, PATH_UPAPFILE, 0);
+    fclose(f);
+    if (ret < 0)
+	return 0;
+    if (passwd != NULL)
+	strlcpy(passwd, secret, MAXSECRETLEN);
+    BZERO(secret, sizeof(secret));
+    return 1;
+}
+
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret(lacks_ipp)
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    //char *filename;
+    struct wordlist *addrs;
+
+    /* let the plugin decide, if there is one */
+    if (pap_check_hook) {
+	ret = (*pap_check_hook)();
+	if (ret >= 0)
+	    return ret;
+    }
+
+    //filename = _PATH_UPAPFILE;
+    f = fopen(PATH_UPAPFILE, "r");
+    if (f == NULL)
+	return 0;
+
+    ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name,
+			NULL, &addrs, NULL, PATH_UPAPFILE, 0);
+    fclose(f);
+    if (ret >= 0 && !some_ip_ok(addrs)) {
+	if (lacks_ipp != 0)
+	    *lacks_ipp = 1;
+	ret = -1;
+    }
+    if (addrs != 0)
+	free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'.  Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_chap_secret(client, server, need_ip, lacks_ipp)
+    char *client;
+    char *server;
+    int need_ip;
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    //char *filename;
+    struct wordlist *addrs;
+
+    if (chap_check_hook) {
+	ret = (*chap_check_hook)();
+	if (ret >= 0) {
+	    return ret;
+	}
+    }
+
+    //filename = _PATH_CHAPFILE;
+    f = fopen(PATH_CHAPFILE, "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, PATH_CHAPFILE, 0);
+    fclose(f);
+    if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+	if (lacks_ipp != 0)
+	    *lacks_ipp = 1;
+	ret = -1;
+    }
+    if (addrs != 0)
+	free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+/*
+ * have_srp_secret - check whether we have a SRP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'.  Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_srp_secret(client, server, need_ip, lacks_ipp)
+    char *client;
+    char *server;
+    int need_ip;
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+
+    filename = _PATH_SRPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+	return 0;
+
+    if (client != NULL && client[0] == 0)
+	client = NULL;
+    else if (server != NULL && server[0] == 0)
+	server = NULL;
+
+    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
+    fclose(f);
+    if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+	if (lacks_ipp != 0)
+	    *lacks_ipp = 1;
+	ret = -1;
+    }
+    if (addrs != 0)
+	free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_secret(unit, client, server, secret, secret_len, am_server)
+    int unit;
+    char *client;
+    char *server;
+    char *secret;
+    int *secret_len;
+    int am_server;
+{
+    FILE *f;
+    int ret, len;
+    //char *filename;
+    struct wordlist *addrs, *opts;
+    char secbuf[MAXWORDLEN];
+
+    if (!am_server && passwd[0] != 0) {
+	strlcpy(secbuf, passwd, sizeof(secbuf));
+    } else if (!am_server && chap_passwd_hook) {
+	if ( (*chap_passwd_hook)(client, secbuf) < 0) {
+	    error("Unable to obtain CHAP password for %s on %s from plugin",
+		  client, server);
+	    return 0;
+	}
+    } else {
+	//filename = _PATH_CHAPFILE;
+	addrs = NULL;
+	secbuf[0] = 0;
+
+	f = fopen(PATH_CHAPFILE, "r");
+	if (f == NULL) {
+	    error("Can't open chap secret file %s: %m", PATH_CHAPFILE);
+	    return 0;
+	}
+	check_access(f, PATH_CHAPFILE);
+
+	ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, PATH_CHAPFILE, 0);
+	fclose(f);
+	if (ret < 0)
+	    return 0;
+
+	if (am_server)
+	    set_allowed_addrs(unit, addrs, opts);
+	else if (opts != 0)
+	    free_wordlist(opts);
+	if (addrs != 0)
+	    free_wordlist(addrs);
+    }
+
+    len = strlen(secbuf);
+    if (len > MAXSECRETLEN) {
+	error("Secret for %s on %s is too long", client, server);
+	len = MAXSECRETLEN;
+    }
+    BCOPY(secbuf, secret, len);
+    BZERO(secbuf, sizeof(secbuf));
+    *secret_len = len;
+
+    return 1;
+}
+
+
+/*
+ * get_srp_secret - open the SRP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_srp_secret(unit, client, server, secret, am_server)
+    int unit;
+    char *client;
+    char *server;
+    char *secret;
+    int am_server;
+{
+    FILE *fp;
+    int ret;
+    char *filename;
+    struct wordlist *addrs, *opts;
+
+    if (!am_server && passwd[0] != '\0') {
+	strlcpy(secret, passwd, MAXWORDLEN);
+    } else {
+	filename = _PATH_SRPFILE;
+	addrs = NULL;
+
+	fp = fopen(filename, "r");
+	if (fp == NULL) {
+	    error("Can't open srp secret file %s: %m", filename);
+	    return 0;
+	}
+	check_access(fp, filename);
+
+	secret[0] = '\0';
+	ret = scan_authfile(fp, client, server, secret, &addrs, &opts,
+	    filename, am_server);
+	fclose(fp);
+	if (ret < 0)
+	    return 0;
+
+	if (am_server)
+	    set_allowed_addrs(unit, addrs, opts);
+	else if (opts != NULL)
+	    free_wordlist(opts);
+	if (addrs != NULL)
+	    free_wordlist(addrs);
+    }
+
+    return 1;
+}
+
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ * Also looks for `--' indicating options to apply for this peer
+ * and leaves the following words in extra_options.
+ */
+static void
+set_allowed_addrs(unit, addrs, opts)
+    int unit;
+    struct wordlist *addrs;
+    struct wordlist *opts;
+{
+    int n;
+    struct wordlist *ap, **plink;
+    struct permitted_ip *ip;
+    char *ptr_word, *ptr_mask;
+    struct hostent *hp;
+    struct netent *np;
+    u_int32_t a, mask, ah, offset;
+    struct ipcp_options *wo = &ipcp_wantoptions[unit];
+    u_int32_t suggested_ip = 0;
+
+    if (addresses[unit] != NULL)
+	free(addresses[unit]);
+    addresses[unit] = NULL;
+    if (extra_options != NULL)
+	free_wordlist(extra_options);
+    extra_options = opts;
+
+    /*
+     * Count the number of IP addresses given.
+     */
+    n = wordlist_count(addrs) + wordlist_count(noauth_addrs);
+    if (n == 0)
+	return;
+    ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip));
+    if (ip == 0)
+	return;
+
+    /* temporarily append the noauth_addrs list to addrs */
+    for (plink = &addrs; *plink != NULL; plink = &(*plink)->next)
+	;
+    *plink = noauth_addrs;
+
+    n = 0;
+    for (ap = addrs; ap != NULL; ap = ap->next) {
+	/* "-" means no addresses authorized, "*" means any address allowed */
+	ptr_word = ap->word;
+	if (strcmp(ptr_word, "-") == 0)
+	    break;
+	if (strcmp(ptr_word, "*") == 0) {
+	    ip[n].permit = 1;
+	    ip[n].base = ip[n].mask = 0;
+	    ++n;
+	    break;
+	}
+
+	ip[n].permit = 1;
+	if (*ptr_word == '!') {
+	    ip[n].permit = 0;
+	    ++ptr_word;
+	}
+
+	mask = ~ (u_int32_t) 0;
+	offset = 0;
+	ptr_mask = strchr (ptr_word, '/');
+	if (ptr_mask != NULL) {
+	    int bit_count;
+	    char *endp;
+
+	    bit_count = (int) strtol (ptr_mask+1, &endp, 10);
+	    if (bit_count <= 0 || bit_count > 32) {
+		warn("invalid address length %v in auth. address list",
+		     ptr_mask+1);
+		continue;
+	    }
+	    bit_count = 32 - bit_count;	/* # bits in host part */
+	    if (*endp == '+') {
+		offset = ifunit + 1;
+		++endp;
+	    }
+	    if (*endp != 0) {
+		warn("invalid address length syntax: %v", ptr_mask+1);
+		continue;
+	    }
+	    *ptr_mask = '\0';
+	    mask <<= bit_count;
+	}
+
+	hp = gethostbyname(ptr_word);
+	if (hp != NULL && hp->h_addrtype == AF_INET) {
+	    a = *(u_int32_t *)hp->h_addr;
+	} else {
+	    np = getnetbyname (ptr_word);
+	    if (np != NULL && np->n_addrtype == AF_INET) {
+		a = htonl ((u_int32_t)np->n_net);
+		if (ptr_mask == NULL) {
+		    /* calculate appropriate mask for net */
+		    ah = ntohl(a);
+		    if (IN_CLASSA(ah))
+			mask = IN_CLASSA_NET;
+		    else if (IN_CLASSB(ah))
+			mask = IN_CLASSB_NET;
+		    else if (IN_CLASSC(ah))
+			mask = IN_CLASSC_NET;
+		}
+	    } else {
+		a = inet_addr (ptr_word);
+	    }
+	}
+
+	if (ptr_mask != NULL)
+	    *ptr_mask = '/';
+
+	if (a == (u_int32_t)-1L) {
+	    warn("unknown host %s in auth. address list", ap->word);
+	    continue;
+	}
+	if (offset != 0) {
+	    if (offset >= ~mask) {
+		warn("interface unit %d too large for subnet %v",
+		     ifunit, ptr_word);
+		continue;
+	    }
+	    a = htonl((ntohl(a) & mask) + offset);
+	    mask = ~(u_int32_t)0;
+	}
+	ip[n].mask = htonl(mask);
+	ip[n].base = a & ip[n].mask;
+	++n;
+	if (~mask == 0 && suggested_ip == 0)
+	    suggested_ip = a;
+    }
+    *plink = NULL;
+
+    ip[n].permit = 0;		/* make the last entry forbid all addresses */
+    ip[n].base = 0;		/* to terminate the list */
+    ip[n].mask = 0;
+
+    addresses[unit] = ip;
+
+    /*
+     * If the address given for the peer isn't authorized, or if
+     * the user hasn't given one, AND there is an authorized address
+     * which is a single host, then use that if we find one.
+     */
+    if (suggested_ip != 0
+	&& (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr))) {
+	wo->hisaddr = suggested_ip;
+	/*
+	 * Do we insist on this address?  No, if there are other
+	 * addresses authorized than the suggested one.
+	 */
+	if (n > 1)
+	    wo->accept_remote = 1;
+    }
+}
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address.  Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(unit, addr)
+    int unit;
+    u_int32_t addr;
+{
+    int ok;
+
+    /* don't allow loopback or multicast address */
+    if (bad_ip_adrs(addr))
+	return 0;
+
+    if (allowed_address_hook) {
+	ok = allowed_address_hook(addr);
+	if (ok >= 0) return ok;
+    }
+
+    if (addresses[unit] != NULL) {
+	ok = ip_addr_check(addr, addresses[unit]);
+	if (ok >= 0)
+	    return ok;
+    }
+
+    if (auth_required)
+    {
+     	warn("pppd: %s---->%d, force return", __FILE__, __LINE__);
+		//return 0;		/* no addresses authorized */
+		return 1;	
+    }
+	return allow_any_ip || privileged || !have_route_to(addr);
+}
+
+static int
+ip_addr_check(addr, addrs)
+    u_int32_t addr;
+    struct permitted_ip *addrs;
+{
+    for (; ; ++addrs)
+	if ((addr & addrs->mask) == addrs->base)
+	    return addrs->permit;
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(addr)
+    u_int32_t addr;
+{
+    addr = ntohl(addr);
+    return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+	|| IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+/*
+ * some_ip_ok - check a wordlist to see if it authorizes any
+ * IP address(es).
+ */
+static int
+some_ip_ok(addrs)
+    struct wordlist *addrs;
+{
+    for (; addrs != 0; addrs = addrs->next) {
+	if (addrs->word[0] == '-')
+	    break;
+	if (addrs->word[0] != '!')
+	    return 1;		/* some IP address is allowed */
+    }
+    return 0;
+}
+
+/*
+ * auth_number - check whether the remote number is allowed to connect.
+ * Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_number()
+{
+    struct wordlist *wp = permitted_numbers;
+    int l;
+
+    /* Allow all if no authorization list. */
+    if (!wp)
+	return 1;
+
+    /* Allow if we have a match in the authorization list. */
+    while (wp) {
+	/* trailing '*' wildcard */
+	l = strlen(wp->word);
+	if ((wp->word)[l - 1] == '*')
+	    l--;
+	if (!strncasecmp(wp->word, remote_number, l))
+	    return 1;
+	wp = wp->next;
+    }
+
+    return 0;
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+static void
+check_access(f, filename)
+    FILE *f;
+    char *filename;
+{
+    struct stat sbuf;
+
+    if (fstat(fileno(f), &sbuf) < 0) {
+	warn("cannot stat secret file %s: %m", filename);
+    } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+	warn("Warning - secret file %s has world and/or group access",
+	     filename);
+    }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'.  The return value is -1
+ * if no secret is found, otherwise >= 0.  The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line up to a "--" (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs.  Any
+ * following words (extra options) are placed in a wordlist and
+ * returned in *opts.
+ * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ * Flags are non-zero if we need two colons in the secret in order to
+ * match.
+ */
+static int
+scan_authfile(f, client, server, secret, addrs, opts, filename, flags)
+    FILE *f;
+    char *client;
+    char *server;
+    char *secret;
+    struct wordlist **addrs;
+    struct wordlist **opts;
+    char *filename;
+    int flags;
+{
+    int newline, xxx;
+    int got_flag, best_flag;
+    FILE *sf;
+    struct wordlist *ap, *addr_list, *alist, **app;
+    char word[MAXWORDLEN];
+    char atfile[MAXWORDLEN];
+    char lsecret[MAXWORDLEN];
+    char *cp;
+
+    if (addrs != NULL)
+	*addrs = NULL;
+    if (opts != NULL)
+	*opts = NULL;
+    addr_list = NULL;
+    if (!getword(f, word, &newline, filename))
+	return -1;		/* file is empty??? */
+    newline = 1;
+    best_flag = -1;
+    for (;;) {
+	/*
+	 * Skip until we find a word at the start of a line.
+	 */
+	while (!newline && getword(f, word, &newline, filename))
+	    ;
+	if (!newline)
+	    break;		/* got to end of file */
+
+	/*
+	 * Got a client - check if it's a match or a wildcard.
+	 */
+	got_flag = 0;
+	if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+	    newline = 0;
+	    continue;
+	}
+	if (!ISWILD(word))
+	    got_flag = NONWILD_CLIENT;
+
+	/*
+	 * Now get a server and check if it matches.
+	 */
+	if (!getword(f, word, &newline, filename))
+	    break;
+	if (newline)
+	    continue;
+	if (!ISWILD(word)) {
+	    if (server != NULL && strcmp(word, server) != 0)
+		continue;
+	    got_flag |= NONWILD_SERVER;
+	}
+
+	/*
+	 * Got some sort of a match - see if it's better than what
+	 * we have already.
+	 */
+	if (got_flag <= best_flag)
+	    continue;
+
+	/*
+	 * Get the secret.
+	 */
+	if (!getword(f, word, &newline, filename))
+	    break;
+	if (newline)
+	    continue;
+
+	/*
+	 * SRP-SHA1 authenticator should never be reading secrets from
+	 * a file.  (Authenticatee may, though.)
+	 */
+	if (flags && ((cp = strchr(word, ':')) == NULL ||
+	    strchr(cp + 1, ':') == NULL))
+	    continue;
+
+	if (secret != NULL) {
+	    /*
+	     * Special syntax: @/pathname means read secret from file.
+	     */
+	    if (word[0] == '@' && word[1] == '/') {
+		strlcpy(atfile, word+1, sizeof(atfile));
+		if ((sf = fopen(atfile, "r")) == NULL) {
+		    warn("can't open indirect secret file %s", atfile);
+		    continue;
+		}
+		check_access(sf, atfile);
+		if (!getword(sf, word, &xxx, atfile)) {
+		    warn("no secret in indirect secret file %s", atfile);
+		    fclose(sf);
+		    continue;
+		}
+		fclose(sf);
+	    }
+	    strlcpy(lsecret, word, sizeof(lsecret));
+	}
+
+	/*
+	 * Now read address authorization info and make a wordlist.
+	 */
+	app = &alist;
+	for (;;) {
+	    if (!getword(f, word, &newline, filename) || newline)
+		break;
+	    ap = (struct wordlist *)
+		    malloc(sizeof(struct wordlist) + strlen(word) + 1);
+	    if (ap == NULL)
+		novm("authorized addresses");
+	    ap->word = (char *) (ap + 1);
+	    strcpy(ap->word, word);
+	    *app = ap;
+	    app = &ap->next;
+	}
+	*app = NULL;
+
+	/*
+	 * This is the best so far; remember it.
+	 */
+	best_flag = got_flag;
+	if (addr_list)
+	    free_wordlist(addr_list);
+	addr_list = alist;
+	if (secret != NULL)
+	    strlcpy(secret, lsecret, MAXWORDLEN);
+
+	if (!newline)
+	    break;
+    }
+
+    /* scan for a -- word indicating the start of options */
+    for (app = &addr_list; (ap = *app) != NULL; app = &ap->next)
+	if (strcmp(ap->word, "--") == 0)
+	    break;
+    /* ap = start of options */
+    if (ap != NULL) {
+	ap = ap->next;		/* first option */
+	free(*app);			/* free the "--" word */
+	*app = NULL;		/* terminate addr list */
+    }
+    if (opts != NULL)
+	*opts = ap;
+    else if (ap != NULL)
+	free_wordlist(ap);
+    if (addrs != NULL)
+	*addrs = addr_list;
+    else if (addr_list != NULL)
+	free_wordlist(addr_list);
+
+    return best_flag;
+}
+
+/*
+ * wordlist_count - return the number of items in a wordlist
+ */
+static int
+wordlist_count(wp)
+    struct wordlist *wp;
+{
+    int n;
+
+    for (n = 0; wp != NULL; wp = wp->next)
+	++n;
+    return n;
+}
+
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(wp)
+    struct wordlist *wp;
+{
+    struct wordlist *next;
+
+    while (wp != NULL) {
+	next = wp->next;
+	free(wp);
+	wp = next;
+    }
+}
+
+/*
+ * auth_script_done - called when the auth-up or auth-down script
+ * has finished.
+ */
+static void
+auth_script_done(arg)
+    void *arg;
+{
+    auth_script_pid = 0;
+    switch (auth_script_state) {
+    case s_up:
+	if (auth_state == s_down) {
+	    auth_script_state = s_down;
+	    auth_script(_PATH_AUTHDOWN);
+	}
+	break;
+    case s_down:
+	if (auth_state == s_up) {
+	    auth_script_state = s_up;
+	    auth_script(_PATH_AUTHUP);
+	}
+	break;
+    }
+}
+
+/*
+ * auth_script - execute a script with arguments
+ * interface-name peer-name real-user tty speed
+ */
+static void
+auth_script(script)
+    char *script;
+{
+    char strspeed[32];
+    struct passwd *pw;
+    char struid[32];
+    char *user_name;
+    char *argv[8];
+
+    if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
+	user_name = pw->pw_name;
+    else {
+	slprintf(struid, sizeof(struid), "%d", getuid());
+	user_name = struid;
+    }
+    slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+
+    argv[0] = script;
+    argv[1] = ifname;
+    argv[2] = peer_authname;
+    argv[3] = user_name;
+    argv[4] = devnam;
+    argv[5] = strspeed;
+    argv[6] = ipparam;
+    argv[7] = NULL;
+
+    auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL, 0);
+}
+
+#ifdef USE_PAM
+/* check_pam_account_restrictions - check if the authenticated user account is valid.
+   This is useful when doing CHAPpy things, which don't support PAM as an authentication
+   mechanism. It means we still obey PAM's account restrictions
+ */
+int check_pam_account_restrictions(const char *user) {
+	int pam_error;
+	pam_handle_t *loc_handle;
+
+	if (obey_restrictions) {
+		const char *username;
+
+		/* We should remove the domain first */
+		if ((username = strrchr(user, '\\')) != NULL)
+			++username;
+		else
+            username = user;
+		
+		pam_error = pam_start(explicit_pamservice ? pamservice : "ppp", username, &PAM_conversation, &loc_handle);
+
+		if (pam_error != PAM_SUCCESS) {
+			return 1;
+		}
+		/* If we have an authentication group, send it */
+		if (auth_group != NULL) {
+			char buf[128];
+			snprintf(buf, 128, "SG-GROUP=%s", auth_group);
+			pam_putenv(loc_handle, buf);
+		}
+		/* Call the account management function */
+		pam_error = pam_acct_mgmt(loc_handle, 0);	
+		pam_end(loc_handle, 0);
+
+		if (pam_error != PAM_SUCCESS)
+			return 1;
+	}
+
+	return 0;
+}
+#endif /* USE_PAM */
+
+#ifdef USE_EXTERNAL_STATS_PROG
+void notify_login_failure(const char *user) {
+	char buf[500];
+        snprintf(buf, 500-1,
+        	"statsd -a incr pam_failed_%s %s \\;"
+                " push pam_last_failure_%s %s \"Permission Denied\" 0 \\;"
+                " incr pam_users %s \\; incr pam_services %s",
+                        user,
+                        explicit_pamservice ? pamservice : "ppp",
+                        user,
+			explicit_pamservice ? pamservice : "ppp",
+			user,
+			explicit_pamservice ? pamservice : "ppp");
+        if (system(buf) == -1) {
+            warn("%s - failed", buf);
+        }
+
+	if (!external_auth) {
+		/* Do the same for pcidssd */
+		snprintf(buf, 500-1, "pcidssd -f %s", user);
+		if (system(buf) == -1) {
+			warn("%s - failed", buf);
+		}
+	}          
+}
+#endif 
diff --git a/ap/app/pppd/pppd/cbcp.c b/ap/app/pppd/pppd/cbcp.c
new file mode 100644
index 0000000..7d276a6
--- /dev/null
+++ b/ap/app/pppd/pppd/cbcp.c
@@ -0,0 +1,488 @@
+/*
+ * cbcp - Call Back Configuration Protocol.
+ *
+ * Copyright (c) 1995 Pedro Roque Marques.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The names of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Pedro Roque Marques
+ *     <pedro_m@yahoo.com>"
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: cbcp.c,v 1.2 2007-06-08 04:02:38 gerg Exp $"
+
+#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, (void *)setcbcp,
+      "Ask for callback", OPT_PRIO | OPT_A2STRVAL, &cbcp[0].us_number },
+    { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void cbcp_init      __P((int unit));
+static void cbcp_open      __P((int unit));
+static void cbcp_lowerup   __P((int unit));
+static void cbcp_input     __P((int unit, u_char *pkt, int len));
+static void cbcp_protrej   __P((int unit));
+static int  cbcp_printpkt  __P((u_char *pkt, int len,
+				void (*printer) __P((void *, char *, ...)),
+				void *arg));
+
+struct protent cbcp_protent = {
+    PPP_CBCP,
+    cbcp_init,
+    cbcp_input,
+    cbcp_protrej,
+    cbcp_lowerup,
+    NULL,
+    cbcp_open,
+    NULL,
+    cbcp_printpkt,
+    NULL,
+    0,
+    "CBCP",
+    NULL,
+    cbcp_option_list,
+    NULL,
+    NULL,
+    NULL
+};
+
+cbcp_state cbcp[NUM_PPP];	
+
+/* internal prototypes */
+
+static void cbcp_recvreq __P((cbcp_state *us, u_char *pckt, int len));
+static void cbcp_resp __P((cbcp_state *us));
+static void cbcp_up __P((cbcp_state *us));
+static void cbcp_recvack __P((cbcp_state *us, u_char *pckt, int len));
+static void cbcp_send __P((cbcp_state *us, int code, u_char *buf, int len));
+
+/* option processing */
+static int
+setcbcp(argv)
+    char **argv;
+{
+    lcp_wantoptions[0].neg_cbcp = 1;
+    cbcp_protent.enabled_flag = 1;
+    cbcp[0].us_number = strdup(*argv);
+    if (cbcp[0].us_number == 0)
+	novm("callback number");
+    cbcp[0].us_type |= (1 << CB_CONF_USER);
+    cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
+    return (1);
+}
+
+/* init state */
+static void
+cbcp_init(iface)
+    int iface;
+{
+    cbcp_state *us;
+
+    us = &cbcp[iface];
+    memset(us, 0, sizeof(cbcp_state));
+    us->us_unit = iface;
+    us->us_type |= (1 << CB_CONF_NO);
+}
+
+/* lower layer is up */
+static void
+cbcp_lowerup(iface)
+    int iface;
+{
+    cbcp_state *us = &cbcp[iface];
+
+    warn("cbcp_lowerup");
+    warn("want: %d", us->us_type);
+
+    if (us->us_type == CB_CONF_USER)
+        warn("phone no: %s", us->us_number);
+}
+
+static void
+cbcp_open(unit)
+    int unit;
+{
+    warn("cbcp_open");
+}
+
+/* process an incomming packet */
+static void
+cbcp_input(unit, inpacket, pktlen)
+    int unit;
+    u_char *inpacket;
+    int pktlen;
+{
+    u_char *inp;
+    u_char code, id;
+    u_short len;
+
+    cbcp_state *us = &cbcp[unit];
+
+    inp = inpacket;
+
+    if (pktlen < CBCP_MINLEN) {
+	if (debug)
+	    warn("CBCP packet is too small");
+	return;
+    }
+
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    GETSHORT(len, inp);
+
+    if (len > pktlen || len < CBCP_MINLEN) {
+	if (debug)
+	    warn("CBCP packet: invalid length %d", len);
+        return;
+    }
+
+    len -= CBCP_MINLEN;
+ 
+    switch(code) {
+    case CBCP_REQ:
+        us->us_id = id;
+	cbcp_recvreq(us, inp, len);
+	break;
+
+    case CBCP_RESP:
+	if (debug)
+	    warn("CBCP_RESP received");
+	break;
+
+    case CBCP_ACK:
+	if (debug && id != us->us_id)
+	    warn("id doesn't match: expected %d recv %d",
+		   us->us_id, id);
+
+	cbcp_recvack(us, inp, len);
+	break;
+
+    default:
+	break;
+    }
+}
+
+/* protocol was rejected by foe */
+void cbcp_protrej(int iface)
+{
+}
+
+char *cbcp_codenames[] = {
+    "Request", "Response", "Ack"
+};
+
+char *cbcp_optionnames[] = {
+    "NoCallback",
+    "UserDefined",
+    "AdminDefined",
+    "List"
+};
+
+/* pretty print a packet */
+static int
+cbcp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    int code, opt, id, len, olen, delay;
+    u_char *pstart;
+
+    if (plen < HEADERLEN)
+	return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+	return 0;
+
+    if (code >= 1 && code <= sizeof(cbcp_codenames) / sizeof(char *))
+	printer(arg, " %s", cbcp_codenames[code-1]);
+    else
+	printer(arg, " code=0x%x", code); 
+
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+
+    switch (code) {
+    case CBCP_REQ:
+    case CBCP_RESP:
+    case CBCP_ACK:
+        while(len >= 2) {
+	    GETCHAR(opt, p);
+	    GETCHAR(olen, p);
+
+	    if (olen < 2 || olen > len) {
+	        break;
+	    }
+
+	    printer(arg, " <");
+	    len -= olen;
+
+	    if (opt >= 1 && opt <= sizeof(cbcp_optionnames) / sizeof(char *))
+	    	printer(arg, " %s", cbcp_optionnames[opt-1]);
+	    else
+	        printer(arg, " option=0x%x", opt); 
+
+	    if (olen > 2) {
+	        GETCHAR(delay, p);
+		printer(arg, " delay = %d", delay);
+	    }
+
+	    if (olen > 3) {
+	        int addrt;
+		char str[256];
+
+		GETCHAR(addrt, p);
+		memcpy(str, p, olen - 4);
+		str[olen - 4] = 0;
+		printer(arg, " number = %s", str);
+	    }
+	    printer(arg, ">");
+	}
+	break;
+
+    default:
+	break;
+    }
+
+    for (; len > 0; --len) {
+	GETCHAR(code, p);
+	printer(arg, " %.2x", code);
+    }
+
+    return p - pstart;
+}
+
+/* received CBCP request */
+static void
+cbcp_recvreq(us, pckt, pcktlen)
+    cbcp_state *us;
+    u_char *pckt;
+    int pcktlen;
+{
+    u_char type, opt_len, delay, addr_type;
+    char address[256];
+    int len = pcktlen;
+
+    address[0] = 0;
+
+    while (len >= 2) {
+        warn("length: %d", len);
+
+	GETCHAR(type, pckt);
+	GETCHAR(opt_len, pckt);
+	if (opt_len < 2 || opt_len > len)
+	    break;
+
+	if (opt_len > 2)
+	    GETCHAR(delay, pckt);
+
+	us->us_allowed |= (1 << type);
+
+	switch(type) {
+	case CB_CONF_NO:
+	    warn("no callback allowed");
+	    break;
+
+	case CB_CONF_USER:
+	    warn("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])
+		    warn("address: %s", address);
+	    }
+	    break;
+
+	case CB_CONF_ADMIN:
+	    warn("user admin defined allowed");
+	    break;
+
+	case CB_CONF_LIST:
+	    break;
+	}
+	len -= opt_len;
+    }
+    if (len != 0) {
+	if (debug)
+	    warn("cbcp_recvreq: malformed packet (%d bytes left)", len);
+	return;
+    }
+
+    cbcp_resp(us);
+}
+
+static void
+cbcp_resp(us)
+    cbcp_state *us;
+{
+    u_char cb_type;
+    u_char buf[256];
+    u_char *bufp = buf;
+    int len = 0;
+    int slen;
+
+    cb_type = us->us_allowed & us->us_type;
+    warn("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 ) ) {
+	warn("cbcp_resp CONF_USER");
+	slen = strlen(us->us_number);
+	if (slen > 250) {
+	    warn("callback number truncated to 250 characters");
+	    slen = 250;
+	}
+	PUTCHAR(CB_CONF_USER, bufp);
+	len = 3 + 1 + slen + 1;
+	PUTCHAR(len , bufp);
+	PUTCHAR(5, bufp); /* delay */
+	PUTCHAR(1, bufp);
+	BCOPY(us->us_number, bufp, slen + 1);
+	cbcp_send(us, CBCP_RESP, buf, len);
+	return;
+    }
+
+    if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
+	warn("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 ) ) {
+        warn("cbcp_resp CONF_NO");
+	PUTCHAR(CB_CONF_NO, bufp);
+	len = 2;
+	PUTCHAR(len , bufp);
+	cbcp_send(us, CBCP_RESP, buf, len);
+	start_networks(us->us_unit);
+	return;
+    }
+}
+
+static void
+cbcp_send(us, code, buf, len)
+    cbcp_state *us;
+    int code;
+    u_char *buf;
+    int len;
+{
+    u_char *outp;
+    int outlen;
+
+    outp = outpacket_buf;
+
+    outlen = 4 + len;
+    
+    MAKEHEADER(outp, PPP_CBCP);
+
+    PUTCHAR(code, outp);
+    PUTCHAR(us->us_id, outp);
+    PUTSHORT(outlen, outp);
+    
+    if (len)
+        BCOPY(buf, outp, len);
+
+    output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+static void
+cbcp_recvack(us, pckt, len)
+    cbcp_state *us;
+    u_char *pckt;
+    int len;
+{
+    u_char type, delay, addr_type;
+    int opt_len;
+    char address[256];
+
+    if (len >= 2) {
+        GETCHAR(type, pckt);
+	GETCHAR(opt_len, pckt);
+	if (opt_len >= 2 && opt_len <= len) {
+     
+	    if (opt_len > 2)
+		GETCHAR(delay, pckt);
+
+	    if (opt_len > 4) {
+		GETCHAR(addr_type, pckt);
+		memcpy(address, pckt, opt_len - 4);
+		address[opt_len - 4] = 0;
+		if (address[0])
+		    warn("peer will call: %s", address);
+	    }
+	    if (type == CB_CONF_NO)
+		return;
+
+	    cbcp_up(us);
+
+	} else if (debug)
+	    warn("cbcp_recvack: malformed packet");
+    }
+}
+
+/* ok peer will do callback */
+static void
+cbcp_up(us)
+    cbcp_state *us;
+{
+    persist = 0;
+    status = EXIT_CALLBACK;
+    lcp_close(0, "Call me back, please");
+}
diff --git a/ap/app/pppd/pppd/cbcp.h b/ap/app/pppd/pppd/cbcp.h
new file mode 100644
index 0000000..c2ab3f6
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/pppd/ccp.c b/ap/app/pppd/pppd/ccp.c
new file mode 100644
index 0000000..b047773
--- /dev/null
+++ b/ap/app/pppd/pppd/ccp.c
@@ -0,0 +1,1678 @@
+/*
+ * ccp.c - PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: ccp.c,v 1.50 2005/06/26 19:34:41 carlsonj Exp $"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ccp.h"
+#include <net/ppp-comp.h>
+
+#ifdef MPPE
+#include "chap_ms.h"	/* mppe_xxxx_key, mppe_keys_set */
+#include "lcp.h"	/* lcp_close(), lcp_fsm */
+#endif
+
+static const char rcsid[] = RCSID;
+
+/*
+ * Unfortunately there is a bug in zlib which means that using a
+ * size of 8 (window size = 256) for Deflate compression will cause
+ * buffer overruns and kernel crashes in the deflate module.
+ * Until this is fixed we only accept sizes in the range 9 .. 15.
+ * Thanks to James Carlson for pointing this out.
+ */
+#define DEFLATE_MIN_WORKS	9
+
+/*
+ * Command-line options.
+ */
+static int setbsdcomp __P((char **));
+static int setdeflate __P((char **));
+static char bsd_value[8];
+static char deflate_value[8];
+
+/*
+ * Option variables.
+ */
+#ifdef MPPE
+bool refuse_mppe_stateful = 1;		/* Allow stateful mode? */
+#endif
+
+static option_t ccp_option_list[] = {
+    { "noccp", o_bool, &ccp_protent.enabled_flag,
+      "Disable CCP negotiation" },
+    { "-ccp", o_bool, &ccp_protent.enabled_flag,
+      "Disable CCP negotiation", OPT_ALIAS },
+
+    { "bsdcomp", o_special, (void *)setbsdcomp,
+      "Request BSD-Compress packet compression",
+      OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, bsd_value },
+    { "nobsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+      "don't allow BSD-Compress", OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].bsd_compress },
+    { "-bsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+      "don't allow BSD-Compress", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].bsd_compress },
+
+    { "deflate", o_special, (void *)setdeflate,
+      "request Deflate compression",
+      OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, deflate_value },
+    { "nodeflate", o_bool, &ccp_wantoptions[0].deflate,
+      "don't allow Deflate compression", OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].deflate },
+    { "-deflate", o_bool, &ccp_wantoptions[0].deflate,
+      "don't allow Deflate compression", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].deflate },
+
+    { "nodeflatedraft", o_bool, &ccp_wantoptions[0].deflate_draft,
+      "don't use draft deflate #", OPT_A2COPY,
+      &ccp_allowoptions[0].deflate_draft },
+
+    { "predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+      "request Predictor-1", OPT_PRIO | 1 },
+    { "nopredictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+      "don't allow Predictor-1", OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].predictor_1 },
+    { "-predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+      "don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].predictor_1 },
+
+#ifdef MPPE
+    /* MPPE options are symmetrical ... we only set wantoptions here */
+    { "require-mppe", o_bool, &ccp_wantoptions[0].mppe,
+      "require MPPE encryption",
+      OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+    { "+mppe", o_bool, &ccp_wantoptions[0].mppe,
+      "require MPPE encryption",
+      OPT_ALIAS | OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+    { "nomppe", o_bool, &ccp_wantoptions[0].mppe,
+      "don't allow MPPE encryption", OPT_PRIO },
+    { "-mppe", o_bool, &ccp_wantoptions[0].mppe,
+      "don't allow MPPE encryption", OPT_ALIAS | OPT_PRIO },
+
+    /* We use ccp_allowoptions[0].mppe as a junk var ... it is reset later */
+    { "require-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+      "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+      &ccp_wantoptions[0].mppe },
+    { "+mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+      "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+      &ccp_wantoptions[0].mppe },
+    { "nomppe-40", o_bool, &ccp_allowoptions[0].mppe,
+      "don't allow MPPE 40-bit encryption",
+      OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, &ccp_wantoptions[0].mppe },
+    { "-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+      "don't allow MPPE 40-bit encryption",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40,
+      &ccp_wantoptions[0].mppe },
+
+    { "require-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+      "require MPPE 128-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+      &ccp_wantoptions[0].mppe },
+    { "+mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+      "require MPPE 128-bit encryption",
+      OPT_ALIAS | OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+      &ccp_wantoptions[0].mppe },
+    { "nomppe-128", o_bool, &ccp_allowoptions[0].mppe,
+      "don't allow MPPE 128-bit encryption",
+      OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, &ccp_wantoptions[0].mppe },
+    { "-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+      "don't allow MPPE 128-bit encryption",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128,
+      &ccp_wantoptions[0].mppe },
+
+    /* strange one; we always request stateless, but will we allow stateful? */
+    { "mppe-stateful", o_bool, &refuse_mppe_stateful,
+      "allow MPPE stateful mode", OPT_PRIO },
+    { "nomppe-stateful", o_bool, &refuse_mppe_stateful,
+      "disallow MPPE stateful mode", OPT_PRIO | 1 },
+#endif /* MPPE */
+
+    { NULL }
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ccp_init __P((int unit));
+static void ccp_open __P((int unit));
+static void ccp_close __P((int unit, char *));
+static void ccp_lowerup __P((int unit));
+static void ccp_lowerdown __P((int));
+static void ccp_input __P((int unit, u_char *pkt, int len));
+static void ccp_protrej __P((int unit));
+static int  ccp_printpkt __P((u_char *pkt, int len,
+			      void (*printer) __P((void *, char *, ...)),
+			      void *arg));
+static void ccp_datainput __P((int unit, u_char *pkt, int len));
+
+struct protent ccp_protent = {
+    PPP_CCP,
+    ccp_init,
+    ccp_input,
+    ccp_protrej,
+    ccp_lowerup,
+    ccp_lowerdown,
+    ccp_open,
+    ccp_close,
+    ccp_printpkt,
+    ccp_datainput,
+    1,
+    "CCP",
+    "Compressed",
+    ccp_option_list,
+    NULL,
+    NULL,
+    NULL
+};
+
+fsm ccp_fsm[NUM_PPP];
+ccp_options ccp_wantoptions[NUM_PPP];	/* what to request the peer to use */
+ccp_options ccp_gotoptions[NUM_PPP];	/* what the peer agreed to do */
+ccp_options ccp_allowoptions[NUM_PPP];	/* what we'll agree to do */
+ccp_options ccp_hisoptions[NUM_PPP];	/* what we agreed to do */
+
+/*
+ * Callbacks for fsm code.
+ */
+static void ccp_resetci __P((fsm *));
+static int  ccp_cilen __P((fsm *));
+static void ccp_addci __P((fsm *, u_char *, int *));
+static int  ccp_ackci __P((fsm *, u_char *, int));
+static int  ccp_nakci __P((fsm *, u_char *, int, int));
+static int  ccp_rejci __P((fsm *, u_char *, int));
+static int  ccp_reqci __P((fsm *, u_char *, int *, int));
+static void ccp_up __P((fsm *));
+static void ccp_down __P((fsm *));
+static int  ccp_extcode __P((fsm *, int, int, u_char *, int));
+static void ccp_rack_timeout __P((void *));
+static char *method_name __P((ccp_options *, ccp_options *));
+
+static fsm_callbacks ccp_callbacks = {
+    ccp_resetci,
+    ccp_cilen,
+    ccp_addci,
+    ccp_ackci,
+    ccp_nakci,
+    ccp_rejci,
+    ccp_reqci,
+    ccp_up,
+    ccp_down,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    ccp_extcode,
+    "CCP"
+};
+
+/*
+ * Do we want / did we get any compression?
+ */
+#define ANY_COMPRESS(opt)	((opt).deflate || (opt).bsd_compress \
+				 || (opt).predictor_1 || (opt).predictor_2 \
+				 || (opt).mppe)
+
+/*
+ * Local state (mainly for handling reset-reqs and reset-acks).
+ */
+static int ccp_localstate[NUM_PPP];
+#define RACK_PENDING	1	/* waiting for reset-ack */
+#define RREQ_REPEAT	2	/* send another reset-req if no reset-ack */
+
+#define RACKTIMEOUT	1	/* second */
+
+static int all_rejected[NUM_PPP];	/* we rejected all peer's options */
+
+/*
+ * Option parsing.
+ */
+static int
+setbsdcomp(argv)
+    char **argv;
+{
+    int rbits, abits;
+    char *str, *endp;
+
+    str = *argv;
+    abits = rbits = strtol(str, &endp, 0);
+    if (endp != str && *endp == ',') {
+	str = endp + 1;
+	abits = strtol(str, &endp, 0);
+    }
+    if (*endp != 0 || endp == str) {
+	option_error("invalid parameter '%s' for bsdcomp option", *argv);
+	return 0;
+    }
+    if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS))
+	|| (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) {
+	option_error("bsdcomp option values must be 0 or %d .. %d",
+		     BSD_MIN_BITS, BSD_MAX_BITS);
+	return 0;
+    }
+    if (rbits > 0) {
+	ccp_wantoptions[0].bsd_compress = 1;
+	ccp_wantoptions[0].bsd_bits = rbits;
+    } else
+	ccp_wantoptions[0].bsd_compress = 0;
+    if (abits > 0) {
+	ccp_allowoptions[0].bsd_compress = 1;
+	ccp_allowoptions[0].bsd_bits = abits;
+    } else
+	ccp_allowoptions[0].bsd_compress = 0;
+    slprintf(bsd_value, sizeof(bsd_value),
+	     rbits == abits? "%d": "%d,%d", rbits, abits);
+
+    return 1;
+}
+
+static int
+setdeflate(argv)
+    char **argv;
+{
+    int rbits, abits;
+    char *str, *endp;
+
+    str = *argv;
+    abits = rbits = strtol(str, &endp, 0);
+    if (endp != str && *endp == ',') {
+	str = endp + 1;
+	abits = strtol(str, &endp, 0);
+    }
+    if (*endp != 0 || endp == str) {
+	option_error("invalid parameter '%s' for deflate option", *argv);
+	return 0;
+    }
+    if ((rbits != 0 && (rbits < DEFLATE_MIN_SIZE || rbits > DEFLATE_MAX_SIZE))
+	|| (abits != 0 && (abits < DEFLATE_MIN_SIZE
+			  || abits > DEFLATE_MAX_SIZE))) {
+	option_error("deflate option values must be 0 or %d .. %d",
+		     DEFLATE_MIN_SIZE, DEFLATE_MAX_SIZE);
+	return 0;
+    }
+    if (rbits == DEFLATE_MIN_SIZE || abits == DEFLATE_MIN_SIZE) {
+	if (rbits == DEFLATE_MIN_SIZE)
+	    rbits = DEFLATE_MIN_WORKS;
+	if (abits == DEFLATE_MIN_SIZE)
+	    abits = DEFLATE_MIN_WORKS;
+	warn("deflate option value of %d changed to %d to avoid zlib bug",
+	     DEFLATE_MIN_SIZE, DEFLATE_MIN_WORKS);
+    }
+    if (rbits > 0) {
+	ccp_wantoptions[0].deflate = 1;
+	ccp_wantoptions[0].deflate_size = rbits;
+    } else
+	ccp_wantoptions[0].deflate = 0;
+    if (abits > 0) {
+	ccp_allowoptions[0].deflate = 1;
+	ccp_allowoptions[0].deflate_size = abits;
+    } else
+	ccp_allowoptions[0].deflate = 0;
+    slprintf(deflate_value, sizeof(deflate_value),
+	     rbits == abits? "%d": "%d,%d", rbits, abits);
+
+    return 1;
+}
+
+/*
+ * ccp_init - initialize CCP.
+ */
+static void
+ccp_init(unit)
+    int unit;
+{
+    fsm *f = &ccp_fsm[unit];
+
+    f->unit = unit;
+    f->protocol = PPP_CCP;
+    f->callbacks = &ccp_callbacks;
+    fsm_init(f);
+
+    memset(&ccp_wantoptions[unit],  0, sizeof(ccp_options));
+    memset(&ccp_gotoptions[unit],   0, sizeof(ccp_options));
+    memset(&ccp_allowoptions[unit], 0, sizeof(ccp_options));
+    memset(&ccp_hisoptions[unit],   0, sizeof(ccp_options));
+
+    ccp_wantoptions[0].deflate = 1;
+    ccp_wantoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+    ccp_wantoptions[0].deflate_correct = 1;
+    ccp_wantoptions[0].deflate_draft = 1;
+    ccp_allowoptions[0].deflate = 1;
+    ccp_allowoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+    ccp_allowoptions[0].deflate_correct = 1;
+    ccp_allowoptions[0].deflate_draft = 1;
+
+    ccp_wantoptions[0].bsd_compress = 1;
+    ccp_wantoptions[0].bsd_bits = BSD_MAX_BITS;
+    ccp_allowoptions[0].bsd_compress = 1;
+    ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS;
+
+    ccp_allowoptions[0].predictor_1 = 1;
+}
+
+/*
+ * ccp_open - CCP is allowed to come up.
+ */
+static void
+ccp_open(unit)
+    int unit;
+{
+    fsm *f = &ccp_fsm[unit];
+
+    if (f->state != OPENED)
+	ccp_flags_set(unit, 1, 0);
+
+    /*
+     * Find out which compressors the kernel supports before
+     * deciding whether to open in silent mode.
+     */
+    ccp_resetci(f);
+    if (!ANY_COMPRESS(ccp_gotoptions[unit]))
+	f->flags |= OPT_SILENT;
+
+    fsm_open(f);
+}
+
+/*
+ * ccp_close - Terminate CCP.
+ */
+static void
+ccp_close(unit, reason)
+    int unit;
+    char *reason;
+{
+    ccp_flags_set(unit, 0, 0);
+    fsm_close(&ccp_fsm[unit], reason);
+}
+
+/*
+ * ccp_lowerup - we may now transmit CCP packets.
+ */
+static void
+ccp_lowerup(unit)
+    int unit;
+{
+    fsm_lowerup(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_lowerdown - we may not transmit CCP packets.
+ */
+static void
+ccp_lowerdown(unit)
+    int unit;
+{
+    fsm_lowerdown(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_input - process a received CCP packet.
+ */
+static void
+ccp_input(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    fsm *f = &ccp_fsm[unit];
+    int oldstate;
+
+    /*
+     * Check for a terminate-request so we can print a message.
+     */
+    oldstate = f->state;
+    fsm_input(f, p, len);
+    if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED) {
+	warn("Compression disabled by peer.");
+#ifdef MPPE
+	if (ccp_gotoptions[unit].mppe) {
+	    error("MPPE disabled, closing LCP");
+	    lcp_close(unit, "MPPE disabled by peer");
+	}
+#endif
+    }
+
+    /*
+     * If we get a terminate-ack and we're not asking for compression,
+     * close CCP.
+     */
+    if (oldstate == REQSENT && p[0] == TERMACK
+	&& !ANY_COMPRESS(ccp_gotoptions[unit]))
+	ccp_close(unit, "No compression negotiated");
+}
+
+/*
+ * Handle a CCP-specific code.
+ */
+static int
+ccp_extcode(f, code, id, p, len)
+    fsm *f;
+    int code, id;
+    u_char *p;
+    int len;
+{
+    switch (code) {
+    case CCP_RESETREQ:
+	if (f->state != OPENED)
+	    break;
+	/* send a reset-ack, which the transmitter will see and
+	   reset its compression state. */
+	fsm_sdata(f, CCP_RESETACK, id, NULL, 0);
+	break;
+
+    case CCP_RESETACK:
+	if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) {
+	    ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT);
+	    UNTIMEOUT(ccp_rack_timeout, f);
+	}
+	break;
+
+    default:
+	return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * ccp_protrej - peer doesn't talk CCP.
+ */
+static void
+ccp_protrej(unit)
+    int unit;
+{
+    ccp_flags_set(unit, 0, 0);
+    fsm_lowerdown(&ccp_fsm[unit]);
+
+#ifdef MPPE
+    if (ccp_gotoptions[unit].mppe) {
+	error("MPPE required but peer negotiation failed");
+	lcp_close(unit, "MPPE required but peer negotiation failed");
+    }
+#endif
+
+}
+
+/*
+ * ccp_resetci - initialize at start of negotiation.
+ */
+static void
+ccp_resetci(f)
+    fsm *f;
+{
+    ccp_options *go = &ccp_gotoptions[f->unit];
+    u_char opt_buf[CCP_MAX_OPTION_LENGTH];
+
+    *go = ccp_wantoptions[f->unit];
+    all_rejected[f->unit] = 0;
+
+#ifdef MPPE
+    if (go->mppe) {
+	ccp_options *ao = &ccp_allowoptions[f->unit];
+	int auth_mschap_bits = auth_done[f->unit];
+	int numbits;
+
+	/*
+	 * Start with a basic sanity check: mschap[v2] auth must be in
+	 * exactly one direction.  RFC 3079 says that the keys are
+	 * 'derived from the credentials of the peer that initiated the call',
+	 * however the PPP protocol doesn't have such a concept, and pppd
+	 * cannot get this info externally.  Instead we do the best we can.
+	 * NB: If MPPE is required, all other compression opts are invalid.
+	 *     So, we return right away if we can't do it.
+	 */
+
+	/* Leave only the mschap auth bits set */
+	auth_mschap_bits &= (CHAP_MS_WITHPEER  | CHAP_MS_PEER |
+			     CHAP_MS2_WITHPEER | CHAP_MS2_PEER);
+	/* Count the mschap auths */
+	auth_mschap_bits >>= CHAP_MS_SHIFT;
+	numbits = 0;
+	do {
+	    numbits += auth_mschap_bits & 1;
+	    auth_mschap_bits >>= 1;
+	} while (auth_mschap_bits);
+	if (numbits > 1) {
+	    error("MPPE required, but auth done in both directions.");
+	    lcp_close(f->unit, "MPPE required but not available");
+	    return;
+	}
+	if (!numbits) {
+	    error("MPPE required, but MS-CHAP[v2] auth not performed.");
+	    lcp_close(f->unit, "MPPE required but not available");
+	    return;
+	}
+
+	/* A plugin (eg radius) may not have obtained key material. */
+	if (!mppe_keys_set) {
+	    error("MPPE required, but keys are not available.  "
+		  "Possible plugin problem?");
+	    lcp_close(f->unit, "MPPE required but not available");
+	    return;
+	}
+
+	/* LM auth not supported for MPPE */
+	if (auth_done[f->unit] & (CHAP_MS_WITHPEER | CHAP_MS_PEER)) {
+	    /* This might be noise */
+	    if (go->mppe & MPPE_OPT_40) {
+		warn("Disabling 40-bit MPPE; MS-CHAP LM not supported");
+		go->mppe &= ~MPPE_OPT_40;
+		ccp_wantoptions[f->unit].mppe &= ~MPPE_OPT_40;
+	    }
+	}
+
+	/* Last check: can we actually negotiate something? */
+	if (!(go->mppe & (MPPE_OPT_40 | MPPE_OPT_128))) {
+	    /* Could be misconfig, could be 40-bit disabled above. */
+	    error("MPPE required, but both 40-bit and 128-bit disabled.");
+	    lcp_close(f->unit, "MPPE required but not available");
+	    return;
+	}
+
+	/* sync options */
+	ao->mppe = go->mppe;
+	/* MPPE is not compatible with other compression types */
+	ao->bsd_compress = go->bsd_compress = 0;
+	ao->predictor_1  = go->predictor_1  = 0;
+	ao->predictor_2  = go->predictor_2  = 0;
+	ao->deflate      = go->deflate      = 0;
+    }
+#endif /* MPPE */
+
+    /*
+     * Check whether the kernel knows about the various
+     * compression methods we might request.
+     */
+#ifdef MPPE
+    if (go->mppe) {
+	opt_buf[0] = CI_MPPE;
+	opt_buf[1] = CILEN_MPPE;
+	MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+	/* Key material unimportant here. */
+	if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0) {
+	    error("MPPE required, but kernel has no support.");
+	    lcp_close(f->unit, "MPPE required but not available");
+	}
+    }
+#endif
+    if (go->bsd_compress) {
+	opt_buf[0] = CI_BSD_COMPRESS;
+	opt_buf[1] = CILEN_BSD_COMPRESS;
+	opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, BSD_MIN_BITS);
+	if (ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0) <= 0)
+	    go->bsd_compress = 0;
+    }
+    if (go->deflate) {
+	if (go->deflate_correct) {
+	    opt_buf[0] = CI_DEFLATE;
+	    opt_buf[1] = CILEN_DEFLATE;
+	    opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_WORKS);
+	    opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+	    if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+		go->deflate_correct = 0;
+	}
+	if (go->deflate_draft) {
+	    opt_buf[0] = CI_DEFLATE_DRAFT;
+	    opt_buf[1] = CILEN_DEFLATE;
+	    opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_WORKS);
+	    opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+	    if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+		go->deflate_draft = 0;
+	}
+	if (!go->deflate_correct && !go->deflate_draft)
+	    go->deflate = 0;
+    }
+    if (go->predictor_1) {
+	opt_buf[0] = CI_PREDICTOR_1;
+	opt_buf[1] = CILEN_PREDICTOR_1;
+	if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_1, 0) <= 0)
+	    go->predictor_1 = 0;
+    }
+    if (go->predictor_2) {
+	opt_buf[0] = CI_PREDICTOR_2;
+	opt_buf[1] = CILEN_PREDICTOR_2;
+	if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_2, 0) <= 0)
+	    go->predictor_2 = 0;
+    }
+}
+
+/*
+ * ccp_cilen - Return total length of our configuration info.
+ */
+static int
+ccp_cilen(f)
+    fsm *f;
+{
+    ccp_options *go = &ccp_gotoptions[f->unit];
+
+    return (go->bsd_compress? CILEN_BSD_COMPRESS: 0)
+	+ (go->deflate? CILEN_DEFLATE: 0)
+	+ (go->predictor_1? CILEN_PREDICTOR_1: 0)
+	+ (go->predictor_2? CILEN_PREDICTOR_2: 0)
+	+ (go->mppe? CILEN_MPPE: 0);
+}
+
+/*
+ * ccp_addci - put our requests in a packet.
+ */
+static void
+ccp_addci(f, p, lenp)
+    fsm *f;
+    u_char *p;
+    int *lenp;
+{
+    int res;
+    ccp_options *go = &ccp_gotoptions[f->unit];
+    u_char *p0 = p;
+
+    /*
+     * Add the compression types that we can receive, in decreasing
+     * preference order.  Get the kernel to allocate the first one
+     * in case it gets Acked.
+     */
+#ifdef MPPE
+    if (go->mppe) {
+	u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN];
+
+	p[0] = opt_buf[0] = CI_MPPE;
+	p[1] = opt_buf[1] = CILEN_MPPE;
+	MPPE_OPTS_TO_CI(go->mppe, &p[2]);
+	MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+	BCOPY(mppe_recv_key, &opt_buf[CILEN_MPPE], MPPE_MAX_KEY_LEN);
+	res = ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0);
+	if (res > 0)
+	    p += CILEN_MPPE;
+	else
+	    /* This shouldn't happen, we've already tested it! */
+	    lcp_close(f->unit, "MPPE required but not available in kernel");
+    }
+#endif
+    if (go->deflate) {
+	p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT;
+	p[1] = CILEN_DEFLATE;
+	p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+	p[3] = DEFLATE_CHK_SEQUENCE;
+	if (p != p0) {
+	    p += CILEN_DEFLATE;
+	} else {
+	    for (;;) {
+		if (go->deflate_size < DEFLATE_MIN_WORKS) {
+		    go->deflate = 0;
+		    break;
+		}
+		res = ccp_test(f->unit, p, CILEN_DEFLATE, 0);
+		if (res > 0) {
+		    p += CILEN_DEFLATE;
+		    break;
+		} else if (res < 0) {
+		    go->deflate = 0;
+		    break;
+		}
+		--go->deflate_size;
+		p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+	    }
+	}
+	if (p != p0 && go->deflate_correct && go->deflate_draft) {
+	    p[0] = CI_DEFLATE_DRAFT;
+	    p[1] = CILEN_DEFLATE;
+	    p[2] = p[2 - CILEN_DEFLATE];
+	    p[3] = DEFLATE_CHK_SEQUENCE;
+	    p += CILEN_DEFLATE;
+	}
+    }
+    if (go->bsd_compress) {
+	p[0] = CI_BSD_COMPRESS;
+	p[1] = CILEN_BSD_COMPRESS;
+	p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+	if (p != p0) {
+	    p += CILEN_BSD_COMPRESS;	/* not the first option */
+	} else {
+	    for (;;) {
+		if (go->bsd_bits < BSD_MIN_BITS) {
+		    go->bsd_compress = 0;
+		    break;
+		}
+		res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0);
+		if (res > 0) {
+		    p += CILEN_BSD_COMPRESS;
+		    break;
+		} else if (res < 0) {
+		    go->bsd_compress = 0;
+		    break;
+		}
+		--go->bsd_bits;
+		p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+	    }
+	}
+    }
+    /* XXX Should Predictor 2 be preferable to Predictor 1? */
+    if (go->predictor_1) {
+	p[0] = CI_PREDICTOR_1;
+	p[1] = CILEN_PREDICTOR_1;
+	if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 0) <= 0) {
+	    go->predictor_1 = 0;
+	} else {
+	    p += CILEN_PREDICTOR_1;
+	}
+    }
+    if (go->predictor_2) {
+	p[0] = CI_PREDICTOR_2;
+	p[1] = CILEN_PREDICTOR_2;
+	if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 0) <= 0) {
+	    go->predictor_2 = 0;
+	} else {
+	    p += CILEN_PREDICTOR_2;
+	}
+    }
+
+    go->method = (p > p0)? p0[0]: -1;
+
+    *lenp = p - p0;
+}
+
+/*
+ * ccp_ackci - process a received configure-ack, and return
+ * 1 iff the packet was OK.
+ */
+static int
+ccp_ackci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    ccp_options *go = &ccp_gotoptions[f->unit];
+    u_char *p0 = p;
+
+#ifdef MPPE
+    if (go->mppe) {
+	u_char opt_buf[CILEN_MPPE];
+
+	opt_buf[0] = CI_MPPE;
+	opt_buf[1] = CILEN_MPPE;
+	MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+	if (len < CILEN_MPPE || memcmp(opt_buf, p, CILEN_MPPE))
+	    return 0;
+	p += CILEN_MPPE;
+	len -= CILEN_MPPE;
+	/* XXX Cope with first/fast ack */
+	if (len == 0)
+	    return 1;
+    }
+#endif
+    if (go->deflate) {
+	if (len < CILEN_DEFLATE
+	    || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+	    || p[1] != CILEN_DEFLATE
+	    || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+	    || p[3] != DEFLATE_CHK_SEQUENCE)
+	    return 0;
+	p += CILEN_DEFLATE;
+	len -= CILEN_DEFLATE;
+	/* XXX Cope with first/fast ack */
+	if (len == 0)
+	    return 1;
+	if (go->deflate_correct && go->deflate_draft) {
+	    if (len < CILEN_DEFLATE
+		|| p[0] != CI_DEFLATE_DRAFT
+		|| p[1] != CILEN_DEFLATE
+		|| p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+		|| p[3] != DEFLATE_CHK_SEQUENCE)
+		return 0;
+	    p += CILEN_DEFLATE;
+	    len -= CILEN_DEFLATE;
+	}
+    }
+    if (go->bsd_compress) {
+	if (len < CILEN_BSD_COMPRESS
+	    || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS
+	    || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+	    return 0;
+	p += CILEN_BSD_COMPRESS;
+	len -= CILEN_BSD_COMPRESS;
+	/* XXX Cope with first/fast ack */
+	if (p == p0 && len == 0)
+	    return 1;
+    }
+    if (go->predictor_1) {
+	if (len < CILEN_PREDICTOR_1
+	    || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1)
+	    return 0;
+	p += CILEN_PREDICTOR_1;
+	len -= CILEN_PREDICTOR_1;
+	/* XXX Cope with first/fast ack */
+	if (p == p0 && len == 0)
+	    return 1;
+    }
+    if (go->predictor_2) {
+	if (len < CILEN_PREDICTOR_2
+	    || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2)
+	    return 0;
+	p += CILEN_PREDICTOR_2;
+	len -= CILEN_PREDICTOR_2;
+	/* XXX Cope with first/fast ack */
+	if (p == p0 && len == 0)
+	    return 1;
+    }
+
+    if (len != 0)
+	return 0;
+    return 1;
+}
+
+/*
+ * ccp_nakci - process received configure-nak.
+ * Returns 1 iff the nak was OK.
+ */
+static int
+ccp_nakci(f, p, len, treat_as_reject)
+    fsm *f;
+    u_char *p;
+    int len;
+    int treat_as_reject;
+{
+    ccp_options *go = &ccp_gotoptions[f->unit];
+    ccp_options no;		/* options we've seen already */
+    ccp_options try;		/* options to ask for next time */
+
+    memset(&no, 0, sizeof(no));
+    try = *go;
+
+#ifdef MPPE
+    if (go->mppe && len >= CILEN_MPPE
+	&& p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+	no.mppe = 1;
+	/*
+	 * Peer wants us to use a different strength or other setting.
+	 * Fail if we aren't willing to use his suggestion.
+	 */
+	MPPE_CI_TO_OPTS(&p[2], try.mppe);
+	if ((try.mppe & MPPE_OPT_STATEFUL) && refuse_mppe_stateful) {
+	    error("Refusing MPPE stateful mode offered by peer");
+	    try.mppe = 0;
+	} else if (((go->mppe | MPPE_OPT_STATEFUL) & try.mppe) != try.mppe) {
+	    /* Peer must have set options we didn't request (suggest) */
+	    try.mppe = 0;
+	}
+
+	if (!try.mppe) {
+	    error("MPPE required but peer negotiation failed");
+	    lcp_close(f->unit, "MPPE required but peer negotiation failed");
+	}
+    }
+#endif /* MPPE */
+    if (go->deflate && len >= CILEN_DEFLATE
+	&& p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+	&& p[1] == CILEN_DEFLATE) {
+	no.deflate = 1;
+	/*
+	 * Peer wants us to use a different code size or something.
+	 * Stop asking for Deflate if we don't understand his suggestion.
+	 */
+	if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+	    || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_WORKS
+	    || p[3] != DEFLATE_CHK_SEQUENCE)
+	    try.deflate = 0;
+	else if (DEFLATE_SIZE(p[2]) < go->deflate_size)
+	    try.deflate_size = DEFLATE_SIZE(p[2]);
+	p += CILEN_DEFLATE;
+	len -= CILEN_DEFLATE;
+	if (go->deflate_correct && go->deflate_draft
+	    && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
+	    && p[1] == CILEN_DEFLATE) {
+	    p += CILEN_DEFLATE;
+	    len -= CILEN_DEFLATE;
+	}
+    }
+
+    if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+	&& p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+	no.bsd_compress = 1;
+	/*
+	 * Peer wants us to use a different number of bits
+	 * or a different version.
+	 */
+	if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION)
+	    try.bsd_compress = 0;
+	else if (BSD_NBITS(p[2]) < go->bsd_bits)
+	    try.bsd_bits = BSD_NBITS(p[2]);
+	p += CILEN_BSD_COMPRESS;
+	len -= CILEN_BSD_COMPRESS;
+    }
+
+    /*
+     * Predictor-1 and 2 have no options, so they can't be Naked.
+     *
+     * There may be remaining options but we ignore them.
+     */
+
+    if (f->state != OPENED)
+	*go = try;
+    return 1;
+}
+
+/*
+ * ccp_rejci - reject some of our suggested compression methods.
+ */
+static int
+ccp_rejci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    ccp_options *go = &ccp_gotoptions[f->unit];
+    ccp_options try;		/* options to request next time */
+
+    try = *go;
+
+    /*
+     * Cope with empty configure-rejects by ceasing to send
+     * configure-requests.
+     */
+    if (len == 0 && all_rejected[f->unit])
+	return -1;
+
+#ifdef MPPE
+    if (go->mppe && len >= CILEN_MPPE
+	&& p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+	error("MPPE required but peer refused");
+	lcp_close(f->unit, "MPPE required but peer refused");
+	p += CILEN_MPPE;
+	len -= CILEN_MPPE;
+    }
+#endif
+    if (go->deflate_correct && len >= CILEN_DEFLATE
+	&& p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) {
+	if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+	    || p[3] != DEFLATE_CHK_SEQUENCE)
+	    return 0;		/* Rej is bad */
+	try.deflate_correct = 0;
+	p += CILEN_DEFLATE;
+	len -= CILEN_DEFLATE;
+    }
+    if (go->deflate_draft && len >= CILEN_DEFLATE
+	&& p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) {
+	if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+	    || p[3] != DEFLATE_CHK_SEQUENCE)
+	    return 0;		/* Rej is bad */
+	try.deflate_draft = 0;
+	p += CILEN_DEFLATE;
+	len -= CILEN_DEFLATE;
+    }
+    if (!try.deflate_correct && !try.deflate_draft)
+	try.deflate = 0;
+    if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+	&& p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+	if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+	    return 0;
+	try.bsd_compress = 0;
+	p += CILEN_BSD_COMPRESS;
+	len -= CILEN_BSD_COMPRESS;
+    }
+    if (go->predictor_1 && len >= CILEN_PREDICTOR_1
+	&& p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) {
+	try.predictor_1 = 0;
+	p += CILEN_PREDICTOR_1;
+	len -= CILEN_PREDICTOR_1;
+    }
+    if (go->predictor_2 && len >= CILEN_PREDICTOR_2
+	&& p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) {
+	try.predictor_2 = 0;
+	p += CILEN_PREDICTOR_2;
+	len -= CILEN_PREDICTOR_2;
+    }
+
+    if (len != 0)
+	return 0;
+
+    if (f->state != OPENED)
+	*go = try;
+
+    return 1;
+}
+
+/*
+ * ccp_reqci - processed a received configure-request.
+ * Returns CONFACK, CONFNAK or CONFREJ and the packet modified
+ * appropriately.
+ */
+static int
+ccp_reqci(f, p, lenp, dont_nak)
+    fsm *f;
+    u_char *p;
+    int *lenp;
+    int dont_nak;
+{
+    int ret, newret, res;
+    u_char *p0, *retp;
+    int len, clen, type, nb;
+    ccp_options *ho = &ccp_hisoptions[f->unit];
+    ccp_options *ao = &ccp_allowoptions[f->unit];
+#ifdef MPPE
+    bool rej_for_ci_mppe = 1;	/* Are we rejecting based on a bad/missing */
+				/* CI_MPPE, or due to other options?       */
+#endif
+
+    ret = CONFACK;
+    retp = p0 = p;
+    len = *lenp;
+
+    memset(ho, 0, sizeof(ccp_options));
+    ho->method = (len > 0)? p[0]: -1;
+
+    while (len > 0) {
+	newret = CONFACK;
+	if (len < 2 || p[1] < 2 || p[1] > len) {
+	    /* length is bad */
+	    clen = len;
+	    newret = CONFREJ;
+
+	} else {
+	    type = p[0];
+	    clen = p[1];
+
+	    switch (type) {
+#ifdef MPPE
+	    case CI_MPPE:
+		if (!ao->mppe || clen != CILEN_MPPE) {
+		    newret = CONFREJ;
+		    break;
+		}
+		MPPE_CI_TO_OPTS(&p[2], ho->mppe);
+
+		/* Nak if anything unsupported or unknown are set. */
+		if (ho->mppe & MPPE_OPT_UNSUPPORTED) {
+		    newret = CONFNAK;
+		    ho->mppe &= ~MPPE_OPT_UNSUPPORTED;
+		}
+		if (ho->mppe & MPPE_OPT_UNKNOWN) {
+		    newret = CONFNAK;
+		    ho->mppe &= ~MPPE_OPT_UNKNOWN;
+		}
+
+		/* Check state opt */
+		if (ho->mppe & MPPE_OPT_STATEFUL) {
+		    /*
+		     * We can Nak and request stateless, but it's a
+		     * lot easier to just assume the peer will request
+		     * it if he can do it; stateful mode is bad over
+		     * the Internet -- which is where we expect MPPE.
+		     */
+		   if (refuse_mppe_stateful) {
+			error("Refusing MPPE stateful mode offered by peer");
+			newret = CONFREJ;
+			break;
+		    }
+		}
+
+		/* Find out which of {S,L} are set. */
+		if ((ho->mppe & MPPE_OPT_128)
+		     && (ho->mppe & MPPE_OPT_40)) {
+		    /* Both are set, negotiate the strongest. */
+		    newret = CONFNAK;
+		    if (ao->mppe & MPPE_OPT_128)
+			ho->mppe &= ~MPPE_OPT_40;
+		    else if (ao->mppe & MPPE_OPT_40)
+			ho->mppe &= ~MPPE_OPT_128;
+		    else {
+			newret = CONFREJ;
+			break;
+		    }
+		} else if (ho->mppe & MPPE_OPT_128) {
+		    if (!(ao->mppe & MPPE_OPT_128)) {
+			newret = CONFREJ;
+			break;
+		    }
+		} else if (ho->mppe & MPPE_OPT_40) {
+		    if (!(ao->mppe & MPPE_OPT_40)) {
+			newret = CONFREJ;
+			break;
+		    }
+		} else {
+		    /* Neither are set. */
+		    /* We cannot accept this.  */
+		    newret = CONFNAK;
+		    /* Give the peer our idea of what can be used,
+		       so it can choose and confirm */
+		    ho->mppe = ao->mppe;
+		}
+
+		/* rebuild the opts */
+		MPPE_OPTS_TO_CI(ho->mppe, &p[2]);
+		if (newret == CONFACK) {
+		    u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN];
+		    int mtu;
+
+		    BCOPY(p, opt_buf, CILEN_MPPE);
+		    BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+			  MPPE_MAX_KEY_LEN);
+		    if (ccp_test(f->unit, opt_buf,
+				 CILEN_MPPE + MPPE_MAX_KEY_LEN, 1) <= 0) {
+			/* This shouldn't happen, we've already tested it! */
+			error("MPPE required, but kernel has no support.");
+			lcp_close(f->unit, "MPPE required but not available");
+			newret = CONFREJ;
+			break;
+		    }
+		    /*
+		     * We need to decrease the interface MTU by MPPE_PAD
+		     * because MPPE frames **grow**.  The kernel [must]
+		     * allocate MPPE_PAD extra bytes in xmit buffers.
+		     */
+		    mtu = netif_get_mtu(f->unit);
+		    if (mtu)
+			netif_set_mtu(f->unit, mtu - MPPE_PAD);
+		    else
+			newret = CONFREJ;
+		}
+
+		/*
+		 * We have accepted MPPE or are willing to negotiate
+		 * MPPE parameters.  A CONFREJ is due to subsequent
+		 * (non-MPPE) processing.
+		 */
+		rej_for_ci_mppe = 0;
+		break;
+#endif /* MPPE */
+	    case CI_DEFLATE:
+	    case CI_DEFLATE_DRAFT:
+		if (!ao->deflate || clen != CILEN_DEFLATE
+		    || (!ao->deflate_correct && type == CI_DEFLATE)
+		    || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) {
+		    newret = CONFREJ;
+		    break;
+		}
+
+		ho->deflate = 1;
+		ho->deflate_size = nb = DEFLATE_SIZE(p[2]);
+		if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+		    || p[3] != DEFLATE_CHK_SEQUENCE
+		    || nb > ao->deflate_size || nb < DEFLATE_MIN_WORKS) {
+		    newret = CONFNAK;
+		    if (!dont_nak) {
+			p[2] = DEFLATE_MAKE_OPT(ao->deflate_size);
+			p[3] = DEFLATE_CHK_SEQUENCE;
+			/* fall through to test this #bits below */
+		    } else
+			break;
+		}
+
+		/*
+		 * Check whether we can do Deflate with the window
+		 * size they want.  If the window is too big, reduce
+		 * it until the kernel can cope and nak with that.
+		 * We only check this for the first option.
+		 */
+		if (p == p0) {
+		    for (;;) {
+			res = ccp_test(f->unit, p, CILEN_DEFLATE, 1);
+			if (res > 0)
+			    break;		/* it's OK now */
+			if (res < 0 || nb == DEFLATE_MIN_WORKS || dont_nak) {
+			    newret = CONFREJ;
+			    p[2] = DEFLATE_MAKE_OPT(ho->deflate_size);
+			    break;
+			}
+			newret = CONFNAK;
+			--nb;
+			p[2] = DEFLATE_MAKE_OPT(nb);
+		    }
+		}
+		break;
+
+	    case CI_BSD_COMPRESS:
+		if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) {
+		    newret = CONFREJ;
+		    break;
+		}
+
+		ho->bsd_compress = 1;
+		ho->bsd_bits = nb = BSD_NBITS(p[2]);
+		if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION
+		    || nb > ao->bsd_bits || nb < BSD_MIN_BITS) {
+		    newret = CONFNAK;
+		    if (!dont_nak) {
+			p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits);
+			/* fall through to test this #bits below */
+		    } else
+			break;
+		}
+
+		/*
+		 * Check whether we can do BSD-Compress with the code
+		 * size they want.  If the code size is too big, reduce
+		 * it until the kernel can cope and nak with that.
+		 * We only check this for the first option.
+		 */
+		if (p == p0) {
+		    for (;;) {
+			res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 1);
+			if (res > 0)
+			    break;
+			if (res < 0 || nb == BSD_MIN_BITS || dont_nak) {
+			    newret = CONFREJ;
+			    p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION,
+						ho->bsd_bits);
+			    break;
+			}
+			newret = CONFNAK;
+			--nb;
+			p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb);
+		    }
+		}
+		break;
+
+	    case CI_PREDICTOR_1:
+		if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) {
+		    newret = CONFREJ;
+		    break;
+		}
+
+		ho->predictor_1 = 1;
+		if (p == p0
+		    && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 1) <= 0) {
+		    newret = CONFREJ;
+		}
+		break;
+
+	    case CI_PREDICTOR_2:
+		if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) {
+		    newret = CONFREJ;
+		    break;
+		}
+
+		ho->predictor_2 = 1;
+		if (p == p0
+		    && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 1) <= 0) {
+		    newret = CONFREJ;
+		}
+		break;
+
+	    default:
+		newret = CONFREJ;
+	    }
+	}
+
+	if (newret == CONFNAK && dont_nak)
+	    newret = CONFREJ;
+	if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) {
+	    /* we're returning this option */
+	    if (newret == CONFREJ && ret == CONFNAK)
+		retp = p0;
+	    ret = newret;
+	    if (p != retp)
+		BCOPY(p, retp, clen);
+	    retp += clen;
+	}
+
+	p += clen;
+	len -= clen;
+    }
+
+    if (ret != CONFACK) {
+	if (ret == CONFREJ && *lenp == retp - p0)
+	    all_rejected[f->unit] = 1;
+	else
+	    *lenp = retp - p0;
+    }
+#ifdef MPPE
+    if (ret == CONFREJ && ao->mppe && rej_for_ci_mppe) {
+	error("MPPE required but peer negotiation failed");
+	lcp_close(f->unit, "MPPE required but peer negotiation failed");
+    }
+#endif
+    return ret;
+}
+
+/*
+ * Make a string name for a compression method (or 2).
+ */
+static char *
+method_name(opt, opt2)
+    ccp_options *opt, *opt2;
+{
+    static char result[64];
+
+    if (!ANY_COMPRESS(*opt))
+	return "(none)";
+    switch (opt->method) {
+#ifdef MPPE
+    case CI_MPPE:
+    {
+	char *p = result;
+	char *q = result + sizeof(result); /* 1 past result */
+
+	slprintf(p, q - p, "MPPE ");
+	p += 5;
+	if (opt->mppe & MPPE_OPT_128) {
+	    slprintf(p, q - p, "128-bit ");
+	    p += 8;
+	}
+	if (opt->mppe & MPPE_OPT_40) {
+	    slprintf(p, q - p, "40-bit ");
+	    p += 7;
+	}
+	if (opt->mppe & MPPE_OPT_STATEFUL)
+	    slprintf(p, q - p, "stateful");
+	else
+	    slprintf(p, q - p, "stateless");
+
+	break;
+    }
+#endif
+    case CI_DEFLATE:
+    case CI_DEFLATE_DRAFT:
+	if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
+	    slprintf(result, sizeof(result), "Deflate%s (%d/%d)",
+		     (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+		     opt->deflate_size, opt2->deflate_size);
+	else
+	    slprintf(result, sizeof(result), "Deflate%s (%d)",
+		     (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+		     opt->deflate_size);
+	break;
+    case CI_BSD_COMPRESS:
+	if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits)
+	    slprintf(result, sizeof(result), "BSD-Compress (%d/%d)",
+		     opt->bsd_bits, opt2->bsd_bits);
+	else
+	    slprintf(result, sizeof(result), "BSD-Compress (%d)",
+		     opt->bsd_bits);
+	break;
+    case CI_PREDICTOR_1:
+	return "Predictor 1";
+    case CI_PREDICTOR_2:
+	return "Predictor 2";
+    default:
+	slprintf(result, sizeof(result), "Method %d", opt->method);
+    }
+    return result;
+}
+
+/*
+ * CCP has come up - inform the kernel driver and log a message.
+ */
+static void
+ccp_up(f)
+    fsm *f;
+{
+    ccp_options *go = &ccp_gotoptions[f->unit];
+    ccp_options *ho = &ccp_hisoptions[f->unit];
+    char method1[64];
+
+    ccp_flags_set(f->unit, 1, 1);
+    if (ANY_COMPRESS(*go)) {
+	if (ANY_COMPRESS(*ho)) {
+	    if (go->method == ho->method) {
+		warn("%s compression enabled", method_name(go, ho));
+	    } else {
+		strlcpy(method1, method_name(go, NULL), sizeof(method1));
+		warn("%s / %s compression enabled",
+		       method1, method_name(ho, NULL));
+	    }
+	} else
+	    warn("%s receive compression enabled", method_name(go, NULL));
+    } else if (ANY_COMPRESS(*ho))
+	warn("%s transmit compression enabled", method_name(ho, NULL));
+#ifdef MPPE
+    if (go->mppe) {
+	BZERO(mppe_recv_key, MPPE_MAX_KEY_LEN);
+	BZERO(mppe_send_key, MPPE_MAX_KEY_LEN);
+	continue_networks(f->unit);		/* Bring up IP et al */
+    }
+#endif
+}
+
+/*
+ * CCP has gone down - inform the kernel driver.
+ */
+static void
+ccp_down(f)
+    fsm *f;
+{
+    if (ccp_localstate[f->unit] & RACK_PENDING)
+	UNTIMEOUT(ccp_rack_timeout, f);
+    ccp_localstate[f->unit] = 0;
+    ccp_flags_set(f->unit, 1, 0);
+#ifdef MPPE
+    if (ccp_gotoptions[f->unit].mppe) {
+	ccp_gotoptions[f->unit].mppe = 0;
+	if (lcp_fsm[f->unit].state == OPENED) {
+	    /* If LCP is not already going down, make sure it does. */
+	    error("MPPE disabled");
+	    lcp_close(f->unit, "MPPE disabled");
+	}
+    }
+#endif
+}
+
+/*
+ * Print the contents of a CCP packet.
+ */
+static char *ccp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej",
+    NULL, NULL, NULL, NULL, NULL, NULL,
+    "ResetReq", "ResetAck",
+};
+
+static int
+ccp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    u_char *p0, *optend;
+    int code, id, len;
+    int optlen;
+
+    p0 = p;
+    if (plen < HEADERLEN)
+	return 0;
+    code = p[0];
+    id = p[1];
+    len = (p[2] << 8) + p[3];
+    if (len < HEADERLEN || len > plen)
+	return 0;
+
+    if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *)
+	&& ccp_codenames[code-1] != NULL)
+	printer(arg, " %s", ccp_codenames[code-1]);
+    else
+	printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    p += HEADERLEN;
+
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+	/* print list of possible compression methods */
+	while (len >= 2) {
+	    code = p[0];
+	    optlen = p[1];
+	    if (optlen < 2 || optlen > len)
+		break;
+	    printer(arg, " <");
+	    len -= optlen;
+	    optend = p + optlen;
+	    switch (code) {
+#ifdef MPPE
+	    case CI_MPPE:
+		if (optlen >= CILEN_MPPE) {
+		    u_char mppe_opts;
+
+		    MPPE_CI_TO_OPTS(&p[2], mppe_opts);
+		    printer(arg, "mppe %s %s %s %s %s %s%s",
+			    (p[2] & MPPE_H_BIT)? "+H": "-H",
+			    (p[5] & MPPE_M_BIT)? "+M": "-M",
+			    (p[5] & MPPE_S_BIT)? "+S": "-S",
+			    (p[5] & MPPE_L_BIT)? "+L": "-L",
+			    (p[5] & MPPE_D_BIT)? "+D": "-D",
+			    (p[5] & MPPE_C_BIT)? "+C": "-C",
+			    (mppe_opts & MPPE_OPT_UNKNOWN)? " +U": "");
+		    if (mppe_opts & MPPE_OPT_UNKNOWN)
+			printer(arg, " (%.2x %.2x %.2x %.2x)",
+				p[2], p[3], p[4], p[5]);
+		    p += CILEN_MPPE;
+		}
+		break;
+#endif
+	    case CI_DEFLATE:
+	    case CI_DEFLATE_DRAFT:
+		if (optlen >= CILEN_DEFLATE) {
+		    printer(arg, "deflate%s %d",
+			    (code == CI_DEFLATE_DRAFT? "(old#)": ""),
+			    DEFLATE_SIZE(p[2]));
+		    if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL)
+			printer(arg, " method %d", DEFLATE_METHOD(p[2]));
+		    if (p[3] != DEFLATE_CHK_SEQUENCE)
+			printer(arg, " check %d", p[3]);
+		    p += CILEN_DEFLATE;
+		}
+		break;
+	    case CI_BSD_COMPRESS:
+		if (optlen >= CILEN_BSD_COMPRESS) {
+		    printer(arg, "bsd v%d %d", BSD_VERSION(p[2]),
+			    BSD_NBITS(p[2]));
+		    p += CILEN_BSD_COMPRESS;
+		}
+		break;
+	    case CI_PREDICTOR_1:
+		if (optlen >= CILEN_PREDICTOR_1) {
+		    printer(arg, "predictor 1");
+		    p += CILEN_PREDICTOR_1;
+		}
+		break;
+	    case CI_PREDICTOR_2:
+		if (optlen >= CILEN_PREDICTOR_2) {
+		    printer(arg, "predictor 2");
+		    p += CILEN_PREDICTOR_2;
+		}
+		break;
+	    }
+	    while (p < optend)
+		printer(arg, " %.2x", *p++);
+	    printer(arg, ">");
+	}
+	break;
+
+    case TERMACK:
+    case TERMREQ:
+	if (len > 0 && *p >= ' ' && *p < 0x7f) {
+	    print_string((char *)p, len, printer, arg);
+	    p += len;
+	    len = 0;
+	}
+	break;
+    }
+
+    /* dump out the rest of the packet in hex */
+    while (--len >= 0)
+	printer(arg, " %.2x", *p++);
+
+    return p - p0;
+}
+
+/*
+ * We have received a packet that the decompressor failed to
+ * decompress.  Here we would expect to issue a reset-request, but
+ * Motorola has a patent on resetting the compressor as a result of
+ * detecting an error in the decompressed data after decompression.
+ * (See US patent 5,130,993; international patent publication number
+ * WO 91/10289; Australian patent 73296/91.)
+ *
+ * So we ask the kernel whether the error was detected after
+ * decompression; if it was, we take CCP down, thus disabling
+ * compression :-(, otherwise we issue the reset-request.
+ */
+static void
+ccp_datainput(unit, pkt, len)
+    int unit;
+    u_char *pkt;
+    int len;
+{
+    fsm *f;
+
+    f = &ccp_fsm[unit];
+    if (f->state == OPENED) {
+	if (ccp_fatal_error(unit)) {
+	    /*
+	     * Disable compression by taking CCP down.
+	     */
+	    error("Lost compression sync: disabling compression");
+	    ccp_close(unit, "Lost compression sync");
+#ifdef MPPE
+	    /*
+	     * If we were doing MPPE, we must also take the link down.
+	     */
+	    if (ccp_gotoptions[unit].mppe) {
+		error("Too many MPPE errors, closing LCP");
+		lcp_close(unit, "Too many MPPE errors");
+	    }
+#endif
+	} else {
+	    /*
+	     * Send a reset-request to reset the peer's compressor.
+	     * We don't do that if we are still waiting for an
+	     * acknowledgement to a previous reset-request.
+	     */
+	    if (!(ccp_localstate[f->unit] & RACK_PENDING)) {
+		fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+		TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+		ccp_localstate[f->unit] |= RACK_PENDING;
+	    } else
+		ccp_localstate[f->unit] |= RREQ_REPEAT;
+	}
+    }
+}
+
+/*
+ * Timeout waiting for reset-ack.
+ */
+static void
+ccp_rack_timeout(arg)
+    void *arg;
+{
+    fsm *f = arg;
+
+    if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) {
+	fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0);
+	TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+	ccp_localstate[f->unit] &= ~RREQ_REPEAT;
+    } else
+	ccp_localstate[f->unit] &= ~RACK_PENDING;
+}
+
diff --git a/ap/app/pppd/pppd/ccp.h b/ap/app/pppd/pppd/ccp.h
new file mode 100644
index 0000000..49a86e0
--- /dev/null
+++ b/ap/app/pppd/pppd/ccp.h
@@ -0,0 +1,52 @@
+/*
+ * ccp.h - Definitions for PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ccp.h,v 1.2 2007-06-08 04:02:38 gerg Exp $
+ */
+
+typedef struct ccp_options {
+    bool bsd_compress;		/* do BSD Compress? */
+    bool deflate;		/* do Deflate? */
+    bool predictor_1;		/* do Predictor-1? */
+    bool predictor_2;		/* do Predictor-2? */
+    bool deflate_correct;	/* use correct code for deflate? */
+    bool deflate_draft;		/* use draft RFC code for deflate? */
+    bool mppe;			/* do MPPE? */
+    u_short bsd_bits;		/* # bits/code for BSD Compress */
+    u_short deflate_size;	/* lg(window size) for Deflate */
+    short method;		/* code for chosen compression method */
+} ccp_options;
+
+extern fsm ccp_fsm[];
+extern ccp_options ccp_wantoptions[];
+extern ccp_options ccp_gotoptions[];
+extern ccp_options ccp_allowoptions[];
+extern ccp_options ccp_hisoptions[];
+
+extern struct protent ccp_protent;
diff --git a/ap/app/pppd/pppd/chap-md5.c b/ap/app/pppd/pppd/chap-md5.c
new file mode 100644
index 0000000..77dd4ec
--- /dev/null
+++ b/ap/app/pppd/pppd/chap-md5.c
@@ -0,0 +1,117 @@
+/*
+ * chap-md5.c - New CHAP/MD5 implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: chap-md5.c,v 1.4 2004/11/09 22:39:25 paulus Exp $"
+
+#include <stdlib.h>
+#include <string.h>
+#include "pppd.h"
+#include "chap-new.h"
+#include "chap-md5.h"
+#include "magic.h"
+#include "md5.h"
+
+#define MD5_HASH_SIZE		16
+#define MD5_MIN_CHALLENGE	16
+#define MD5_MAX_CHALLENGE	24
+
+static void
+chap_md5_generate_challenge(unsigned char *cp)
+{
+	int clen;
+
+	clen = (int)(drand48() * (MD5_MAX_CHALLENGE - MD5_MIN_CHALLENGE))
+		+ MD5_MIN_CHALLENGE;
+	*cp++ = clen;
+	random_bytes(cp, clen);
+}
+
+static int
+chap_md5_verify_response(int id, char *name,
+			 unsigned char *secret, int secret_len,
+			 unsigned char *challenge, unsigned char *response,
+			 char *message, int message_space)
+{
+	MD5_CTX ctx;
+	unsigned char idbyte = id;
+	unsigned char hash[MD5_HASH_SIZE];
+	int challenge_len, response_len;
+
+	challenge_len = *challenge++;
+	response_len = *response++;
+	if (response_len == MD5_HASH_SIZE) {
+		/* Generate hash of ID, secret, challenge */
+		MD5_Init(&ctx);
+		MD5_Update(&ctx, &idbyte, 1);
+		MD5_Update(&ctx, secret, secret_len);
+		MD5_Update(&ctx, challenge, challenge_len);
+		MD5_Final(hash, &ctx);
+
+		/* Test if our hash matches the peer's response */
+		if (memcmp(hash, response, MD5_HASH_SIZE) == 0) {
+			slprintf(message, message_space, "Access granted");
+			return 1;
+		}
+	}
+	slprintf(message, message_space, "Access denied");
+	return 0;
+}
+
+static void
+chap_md5_make_response(unsigned char *response, int id, char *our_name,
+		       unsigned char *challenge, char *secret, int secret_len,
+		       unsigned char *private)
+{
+	MD5_CTX ctx;
+	unsigned char idbyte = id;
+	int challenge_len = *challenge++;
+
+	MD5_Init(&ctx);
+	MD5_Update(&ctx, &idbyte, 1);
+	MD5_Update(&ctx, (u_char *)secret, secret_len);
+	MD5_Update(&ctx, challenge, challenge_len);
+	MD5_Final(&response[1], &ctx);
+	response[0] = MD5_HASH_SIZE;
+}
+
+static struct chap_digest_type md5_digest = {
+	CHAP_MD5,		/* code */
+	chap_md5_generate_challenge,
+	chap_md5_verify_response,
+	chap_md5_make_response,
+	NULL,			/* check_success */
+	NULL,			/* handle_failure */
+};
+
+void
+chap_md5_init(void)
+{
+	chap_register_digest(&md5_digest);
+}
diff --git a/ap/app/pppd/pppd/chap-md5.h b/ap/app/pppd/pppd/chap-md5.h
new file mode 100644
index 0000000..30d0658
--- /dev/null
+++ b/ap/app/pppd/pppd/chap-md5.h
@@ -0,0 +1,31 @@
+/*
+ * chap-md5.h - New CHAP/MD5 implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+extern void chap_md5_init(void);
diff --git a/ap/app/pppd/pppd/chap-new.c b/ap/app/pppd/pppd/chap-new.c
new file mode 100644
index 0000000..4c1bead
--- /dev/null
+++ b/ap/app/pppd/pppd/chap-new.c
@@ -0,0 +1,726 @@
+/*
+ * chap-new.c - New CHAP implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: chap-new.c,v 1.9 2007/06/19 02:08:35 carlsonj Exp $"
+
+#include <stdlib.h>
+#include <string.h>
+#include "pppd.h"
+#include "session.h"
+#include "chap-new.h"
+#include "chap-md5.h"
+
+#ifdef CHAPMS
+#include "chap_ms.h"
+#define MDTYPE_ALL (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5)
+#else
+#define MDTYPE_ALL (MDTYPE_MD5)
+#endif
+
+int chap_mdtype_all = MDTYPE_ALL;
+extern pppd_auth authinfo[NUM_PPPD_AUTH];
+/* Hook for a plugin to validate CHAP challenge */
+int (*chap_verify_hook)(char *name, char *ourname, int id,
+			struct chap_digest_type *digest,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space) = NULL;
+
+/*
+ * Option variables.
+ */
+int chap_timeout_time = 3;
+int chap_max_transmits = 10;
+int chap_rechallenge_time = 0;
+
+/*
+ * Command-line options.
+ */
+static option_t chap_option_list[] = {
+	{ "chap-restart", o_int, &chap_timeout_time,
+	  "Set timeout for CHAP", OPT_PRIO },
+	{ "chap-max-challenge", o_int, &chap_max_transmits,
+	  "Set max #xmits for challenge", OPT_PRIO },
+	{ "chap-interval", o_int, &chap_rechallenge_time,
+	  "Set interval for rechallenge", OPT_PRIO },
+	{ NULL }
+};
+
+/*
+ * Internal state.
+ */
+static struct chap_client_state {
+	int flags;
+	char *name;
+	struct chap_digest_type *digest;
+	unsigned char priv[64];		/* private area for digest's use */
+} client;
+
+/*
+ * These limits apply to challenge and response packets we send.
+ * The +4 is the +1 that we actually need rounded up.
+ */
+#define CHAL_MAX_PKTLEN	(PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_CHALLENGE_LEN + MAXNAMELEN)
+#define RESP_MAX_PKTLEN	(PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_RESPONSE_LEN + MAXNAMELEN)
+
+static struct chap_server_state {
+	int flags;
+	int id;
+	char *name;
+	struct chap_digest_type *digest;
+	int challenge_xmits;
+	int challenge_pktlen;
+	unsigned char challenge[CHAL_MAX_PKTLEN];
+	char message[256];
+} server;
+
+/* Values for flags in chap_client_state and chap_server_state */
+#define LOWERUP			1
+#define AUTH_STARTED		2
+#define AUTH_DONE		4
+#define AUTH_FAILED		8
+#define TIMEOUT_PENDING		0x10
+#define CHALLENGE_VALID		0x20
+
+/*
+ * Prototypes.
+ */
+static void chap_init(int unit);
+static void chap_lowerup(int unit);
+static void chap_lowerdown(int unit);
+static void chap_timeout(void *arg);
+static void chap_generate_challenge(struct chap_server_state *ss);
+static void chap_handle_response(struct chap_server_state *ss, int code,
+		unsigned char *pkt, int len);
+static int chap_verify_response(char *name, char *ourname, int id,
+		struct chap_digest_type *digest,
+		unsigned char *challenge, unsigned char *response,
+		char *message, int message_space);
+static void chap_respond(struct chap_client_state *cs, int id,
+		unsigned char *pkt, int len);
+static void chap_handle_status(struct chap_client_state *cs, int code, int id,
+		unsigned char *pkt, int len);
+static void chap_protrej(int unit);
+static void chap_input(int unit, unsigned char *pkt, int pktlen);
+static int chap_print_pkt(unsigned char *p, int plen,
+		void (*printer) __P((void *, char *, ...)), void *arg);
+
+/* List of digest types that we know about */
+static struct chap_digest_type *chap_digests;
+
+/*
+ * chap_init - reset to initial state.
+ */
+static void
+chap_init(int unit)
+{
+	memset(&client, 0, sizeof(client));
+	memset(&server, 0, sizeof(server));
+
+	chap_md5_init();
+#ifdef CHAPMS
+	chapms_init();
+#endif
+}
+
+/*
+ * Add a new digest type to the list.
+ */
+void
+chap_register_digest(struct chap_digest_type *dp)
+{
+	dp->next = chap_digests;
+	chap_digests = dp;
+}
+
+/*
+ * chap_lowerup - we can start doing stuff now.
+ */
+static void
+chap_lowerup(int unit)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_server_state *ss = &server;
+
+	cs->flags |= LOWERUP;
+	ss->flags |= LOWERUP;
+	if (ss->flags & AUTH_STARTED)
+		chap_timeout(ss);
+}
+
+static void
+chap_lowerdown(int unit)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_server_state *ss = &server;
+
+	cs->flags = 0;
+	if (ss->flags & TIMEOUT_PENDING)
+		UNTIMEOUT(chap_timeout, ss);
+	ss->flags = 0;
+}
+
+/*
+ * chap_auth_peer - Start authenticating the peer.
+ * If the lower layer is already up, we start sending challenges,
+ * otherwise we wait for the lower layer to come up.
+ */
+void
+chap_auth_peer(int unit, char *our_name, int digest_code)
+{
+	struct chap_server_state *ss = &server;
+	struct chap_digest_type *dp;
+
+	if (ss->flags & AUTH_STARTED) {
+		error("CHAP: peer authentication already started!");
+		return;
+	}
+	for (dp = chap_digests; dp != NULL; dp = dp->next)
+		if (dp->code == digest_code)
+			break;
+	if (dp == NULL)
+		fatal("CHAP digest 0x%x requested but not available",
+		      digest_code);
+
+	ss->digest = dp;
+	ss->name = our_name;
+	/* Start with a random ID value */
+	ss->id = (unsigned char)(drand48() * 256);
+	ss->flags |= AUTH_STARTED;
+	if (ss->flags & LOWERUP)
+		chap_timeout(ss);
+}
+
+/*
+ * chap_auth_with_peer - Prepare to authenticate ourselves to the peer.
+ * There isn't much to do until we receive a challenge.
+ */
+void
+chap_auth_with_peer(int unit, char *our_name, int digest_code)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_digest_type *dp;
+
+	if (cs->flags & AUTH_STARTED) {
+		error("CHAP: authentication with peer already started!");
+		return;
+	}
+	for (dp = chap_digests; dp != NULL; dp = dp->next)
+		if (dp->code == digest_code)
+			break;
+	if (dp == NULL)
+		fatal("CHAP digest 0x%x requested but not available",
+		      digest_code);
+
+	cs->digest = dp;
+	cs->name = our_name;
+	cs->flags |= AUTH_STARTED;
+}
+
+/*
+ * chap_timeout - It's time to send another challenge to the peer.
+ * This could be either a retransmission of a previous challenge,
+ * or a new challenge to start re-authentication.
+ */
+static void
+chap_timeout(void *arg)
+{
+	struct chap_server_state *ss = arg;
+
+	ss->flags &= ~TIMEOUT_PENDING;
+	if ((ss->flags & CHALLENGE_VALID) == 0) {
+		ss->challenge_xmits = 0;
+		chap_generate_challenge(ss);
+		ss->flags |= CHALLENGE_VALID;
+	} else if (ss->challenge_xmits >= chap_max_transmits) {
+		ss->flags &= ~CHALLENGE_VALID;
+		ss->flags |= AUTH_DONE | AUTH_FAILED;
+		auth_peer_fail(0, PPP_CHAP);
+		return;
+	}
+	/*wangming modified*/
+	output(0, ss->challenge, ss->challenge_pktlen);
+	++ss->challenge_xmits;
+	ss->flags |= TIMEOUT_PENDING;
+
+
+	
+	TIMEOUT(chap_timeout, arg, chap_timeout_time);
+}
+
+/*
+ * chap_generate_challenge - generate a challenge string and format
+ * the challenge packet in ss->challenge_pkt.
+ */
+ /*challenge packet with PPP header*/
+static void
+chap_generate_challenge(struct chap_server_state *ss)
+{
+	int clen = 1, nlen, len, i=0;
+	unsigned char x;
+	unsigned char y;
+	unsigned char *p, *q;
+	unsigned char challenge[50] = {0};
+	p = ss->challenge;
+	MAKEHEADER(p, PPP_CHAP);/*0xc223*/
+
+	/*ÍùºóÒÆ¶¯4¸ö×Ö½Ú*/
+	/*	code 1
+		id	 1
+		length 2
+	*/
+	p += CHAP_HDRLEN;
+	
+	ss->digest->generate_challenge(p);
+	/*size + id + name*/
+	clen = *p;
+	warn("pppd: %s---->%d clen is %d", __FILE__, __LINE__, clen);
+	authinfo[0].auth_type = PPP_CHAP_AUTH;
+	authinfo[0].chg_len = clen;
+	++p;
+	i = clen;
+	for (; i > 0; --i) {
+		GETCHAR(x, p);
+		authinfo[0].challenge[clen-i] = x;
+		warn("pppd: %s---->%d, %.2x", __FILE__, __LINE__, x);
+	}
+	
+	p = p - clen - 1;
+	//y = *p;
+	
+	nlen = strlen(ss->name);
+	/*Ìî³äname*/
+	memcpy(p + 1 + clen, ss->name, nlen);
+
+	len = CHAP_HDRLEN + 1 + clen + nlen;
+	ss->challenge_pktlen = PPP_HDRLEN + len;
+
+	p = ss->challenge + PPP_HDRLEN;
+	p[0] = CHAP_CHALLENGE;
+	p[1] = ++ss->id;
+	p[2] = len >> 8;
+	p[3] = len;
+	
+	
+}
+
+/*
+ * chap_handle_response - check the response to our challenge.
+ */
+static void
+chap_handle_response(struct chap_server_state *ss, int id,
+		     unsigned char *pkt, int len)
+{
+	int response_len, ok, mlen, i;
+	unsigned char *response, *p;
+	char *name = NULL;	/* initialized to shut gcc up */
+	int (*verifier)(char *, char *, int, struct chap_digest_type *,
+		unsigned char *, unsigned char *, char *, int);
+	char rname[MAXNAMELEN+1];
+
+	if ((ss->flags & LOWERUP) == 0)
+		return;
+	if (id != ss->challenge[PPP_HDRLEN+1] || len < 2)
+		return;
+	if (ss->flags & CHALLENGE_VALID) {
+		response = pkt;
+		GETCHAR(response_len, pkt);
+		len -= response_len + 1;	/* length of name */
+		name = (char *)pkt + response_len;
+		authinfo[0].user_len = len;
+		memcpy(authinfo[0].user, name, len);
+		
+		warn("pppd: %s---->%d, response len is %d", __FILE__, __LINE__,response_len);
+		authinfo[0].pw_len = response_len;
+		for(i=0; i<response_len;i++)
+		{
+		authinfo[0].pw[i] = *(pkt+i);
+		warn("pppd: %s---->%d, pkt len is %.2x", __FILE__, __LINE__,authinfo[0].pw[i]);
+		}
+		if (len < 0)
+			return;
+
+		if (ss->flags & TIMEOUT_PENDING) {
+			ss->flags &= ~TIMEOUT_PENDING;
+			UNTIMEOUT(chap_timeout, ss);
+		}
+
+		if (explicit_remote) {
+			name = remote_name;
+		} else {
+			/* Null terminate and clean remote name. */
+			slprintf(rname, sizeof(rname), "%.*v", len, name);
+			name = rname;
+		}
+
+		if (chap_verify_hook)
+			verifier = chap_verify_hook;
+		else
+			verifier = chap_verify_response;
+		ok = (*verifier)(name, ss->name, id, ss->digest,
+				 ss->challenge + PPP_HDRLEN + CHAP_HDRLEN,
+				 response, ss->message, sizeof(ss->message));
+		if (!ok || !auth_number()) {
+			ss->flags |= AUTH_FAILED;
+			warn("pppd: %s---->%d", __FILE__, __LINE__);
+			warn("Peer %q failed CHAP authentication", name);
+		}
+#ifdef USE_PAM
+		if (!(ss->flags & AUTH_FAILED)) {
+			warn("pppd: %s---->%d", __FILE__, __LINE__);
+			if (check_pam_account_restrictions(name)) {
+				ss->flags |= AUTH_FAILED;
+				warn("Peer %q failed PAM Account provisions", name);
+			}
+		}
+#endif 
+
+#ifdef USE_EXTERNAL_STATS_PROG
+		if (ss->flags & AUTH_FAILED) {
+			warn("pppd: %s---->%d", __FILE__, __LINE__);
+			notify_login_failure(name);
+		}
+#endif
+	} else if ((ss->flags & AUTH_DONE) == 0)
+	{
+		return;
+	}
+	/* send the response */
+	p = outpacket_buf;
+	MAKEHEADER(p, PPP_CHAP);
+	mlen = strlen(ss->message);
+	len = CHAP_HDRLEN + mlen;
+	p[0] = (ss->flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS;
+	p[1] = id;
+	p[2] = len >> 8;
+	p[3] = len;
+	if (mlen > 0)
+		memcpy(p + CHAP_HDRLEN, ss->message, mlen);
+	output(0, outpacket_buf, PPP_HDRLEN + len);
+
+	if (ss->flags & CHALLENGE_VALID) {
+		ss->flags &= ~CHALLENGE_VALID;
+		if (!(ss->flags & AUTH_DONE) && !(ss->flags & AUTH_FAILED)) {
+		    /*
+		     * Auth is OK, so now we need to check session restrictions
+		     * to ensure everything is OK, but only if we used a
+		     * plugin, and only if we're configured to check.  This
+		     * allows us to do PAM checks on PPP servers that
+		     * authenticate against ActiveDirectory, and use AD for
+		     * account info (like when using Winbind integrated with
+		     * PAM).
+		     */
+		    if (session_mgmt &&
+			session_check(name, NULL, devnam, NULL) == 0) {
+			ss->flags |= AUTH_FAILED;
+			warn("Peer %q failed CHAP Session verification", name);
+		    }
+		}
+		if (ss->flags & AUTH_FAILED) {
+			auth_peer_fail(0, PPP_CHAP);
+		} else {
+			if ((ss->flags & AUTH_DONE) == 0)
+				auth_peer_success(0, PPP_CHAP,
+						  ss->digest->code,
+						  name, strlen(name));
+			if (chap_rechallenge_time) {
+				ss->flags |= TIMEOUT_PENDING;
+				TIMEOUT(chap_timeout, ss,
+					chap_rechallenge_time);
+			}
+		}
+		ss->flags |= AUTH_DONE;
+	}
+}
+
+/*
+ * chap_verify_response - check whether the peer's response matches
+ * what we think it should be.  Returns 1 if it does (authentication
+ * succeeded), or 0 if it doesn't.
+ */
+extern int ppp_act_num;
+static int
+chap_verify_response(char *name, char *ourname, int id,
+		     struct chap_digest_type *digest,
+		     unsigned char *challenge, unsigned char *response,
+		     char *message, int message_space)
+{
+	int ok;
+	unsigned char secret[MAXSECRETLEN];
+	int secret_len;
+
+	/* Get the secret that the peer is supposed to know */
+	if (!get_secret(0, name, ourname, (char *)secret, &secret_len, 1)) {
+		error("No CHAP secret found for authenticating %q", name);
+        
+        //zdm
+        AT_PDP_ACT_REQ_INFO pdp_info = {0};
+        pdp_info.auth_type = authinfo[0].auth_type;
+        memcpy(pdp_info.username, authinfo[0].user, USER_MAX);
+        memcpy(pdp_info.password, authinfo[0].pw, PW_MAX);
+        memcpy(pdp_info.challenge, authinfo[0].challenge, CHALLENGE_MAX);
+     	strcpy(pdp_info.ip_type,"IP");
+		if(ppp_act_num == 0)
+		{
+			ipc_send_message(MODULE_ID_PPPD, MODULE_ID_AT_CTL, MSG_CMD_PDP_ACT_REQ, sizeof(AT_PDP_ACT_REQ_INFO), (UCHAR *)&pdp_info,0);
+			ppp_act_num=3;
+		}
+		return 1;
+	}
+
+	ok = digest->verify_response(id, name, secret, secret_len, challenge,
+				     response, message, message_space);
+	memset(secret, 0, sizeof(secret));
+	return ok;
+}
+
+/*
+ * chap_respond - Generate and send a response to a challenge.
+ */
+static void
+chap_respond(struct chap_client_state *cs, int id,
+	     unsigned char *pkt, int len)
+{
+	int clen, nlen;
+	int secret_len;
+	unsigned char *p;
+	unsigned char response[RESP_MAX_PKTLEN];
+	char rname[MAXNAMELEN+1];
+	char secret[MAXSECRETLEN+1];
+	if ((cs->flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED))
+		return;		/* not ready */
+	if (len < 2 || len < pkt[0] + 1)
+		return;		/* too short */
+	clen = pkt[0];
+	nlen = len - (clen + 1);
+
+	/* Null terminate and clean remote name. */
+	slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1);
+
+	/* Microsoft doesn't send their name back in the PPP packet */
+	if (explicit_remote || (remote_name[0] != 0 && rname[0] == 0))
+		strlcpy(rname, remote_name, sizeof(rname));
+
+	/* get secret for authenticating ourselves with the specified host */
+	if (!get_secret(0, cs->name, rname, secret, &secret_len, 0)) {
+		secret_len = 0;	/* assume null secret if can't find one */
+		warn("No CHAP secret found for authenticating us to %q", rname);
+	}
+
+	p = response;
+	MAKEHEADER(p, PPP_CHAP);
+	p += CHAP_HDRLEN;
+
+	cs->digest->make_response(p, id, cs->name, pkt,
+				  secret, secret_len, cs->priv);
+	memset(secret, 0, secret_len);
+
+	clen = *p;
+	nlen = strlen(cs->name);
+	memcpy(p + clen + 1, cs->name, nlen);
+
+	p = response + PPP_HDRLEN;
+	len = CHAP_HDRLEN + clen + 1 + nlen;
+	p[0] = CHAP_RESPONSE;
+	p[1] = id;
+	p[2] = len >> 8;
+	p[3] = len;
+
+	output(0, response, PPP_HDRLEN + len);
+}
+
+static void
+chap_handle_status(struct chap_client_state *cs, int code, int id,
+		   unsigned char *pkt, int len)
+{
+	const char *msg = NULL;
+
+	if ((cs->flags & (AUTH_DONE|AUTH_STARTED|LOWERUP))
+	    != (AUTH_STARTED|LOWERUP))
+		return;
+	cs->flags |= AUTH_DONE;
+
+	if (code == CHAP_SUCCESS) {
+		/* used for MS-CHAP v2 mutual auth, yuck */
+		if (cs->digest->check_success != NULL) {
+			if (!(*cs->digest->check_success)(pkt, len, cs->priv))
+				code = CHAP_FAILURE;
+		} else
+			msg = "CHAP authentication succeeded";
+	} else {
+		if (cs->digest->handle_failure != NULL)
+			(*cs->digest->handle_failure)(pkt, len);
+		else
+			msg = "CHAP authentication failed";
+	}
+	if (msg) {
+		if (len > 0)
+			warn("%s: %.*v", msg, len, pkt);
+		else
+			warn("%s", msg);
+	}
+	if (code == CHAP_SUCCESS)
+		auth_withpeer_success(0, PPP_CHAP, cs->digest->code);
+	else {
+		cs->flags |= AUTH_FAILED;
+		error("CHAP authentication failed");
+		auth_withpeer_fail(0, PPP_CHAP);
+	}
+}
+
+static void
+chap_input(int unit, unsigned char *pkt, int pktlen)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_server_state *ss = &server;
+	unsigned char code, id;
+	int len;
+
+	if (pktlen < CHAP_HDRLEN)
+		return;
+	GETCHAR(code, pkt);
+	GETCHAR(id, pkt);
+	GETSHORT(len, pkt);
+	if (len < CHAP_HDRLEN || len > pktlen)
+		return;
+	len -= CHAP_HDRLEN;
+	switch (code) {
+	case CHAP_CHALLENGE:
+		chap_respond(cs, id, pkt, len);
+		break;
+	case CHAP_RESPONSE:
+		chap_handle_response(ss, id, pkt, len);
+		break;
+	case CHAP_FAILURE:
+	case CHAP_SUCCESS:
+		chap_handle_status(cs, code, id, pkt, len);
+		break;
+	}
+}
+
+static void
+chap_protrej(int unit)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_server_state *ss = &server;
+
+	if (ss->flags & TIMEOUT_PENDING) {
+		ss->flags &= ~TIMEOUT_PENDING;
+		UNTIMEOUT(chap_timeout, ss);
+	}
+	if (ss->flags & AUTH_STARTED) {
+		ss->flags = 0;
+		auth_peer_fail(0, PPP_CHAP);
+	}
+	if ((cs->flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) {
+		cs->flags &= ~AUTH_STARTED;
+		error("CHAP authentication failed due to protocol-reject");
+		auth_withpeer_fail(0, PPP_CHAP);
+	}
+}
+
+/*
+ * chap_print_pkt - print the contents of a CHAP packet.
+ */
+static char *chap_code_names[] = {
+	"Challenge", "Response", "Success", "Failure"
+};
+
+static int
+chap_print_pkt(unsigned char *p, int plen,
+	       void (*printer) __P((void *, char *, ...)), void *arg)
+{
+	int code, id, len;
+	int clen, nlen;
+	unsigned char x;
+
+	if (plen < CHAP_HDRLEN)
+		return 0;
+	GETCHAR(code, p);
+	GETCHAR(id, p);
+	GETSHORT(len, p);
+	if (len < CHAP_HDRLEN || len > plen)
+		return 0;
+
+	if (code >= 1 && code <= sizeof(chap_code_names) / sizeof(char *))
+		printer(arg, " %s", chap_code_names[code-1]);
+	else
+		printer(arg, " code=0x%x", code);
+	printer(arg, " id=0x%x", id);
+	len -= CHAP_HDRLEN;
+	switch (code) {
+	case CHAP_CHALLENGE:
+	case CHAP_RESPONSE:
+		if (len < 1)
+			break;
+		clen = p[0];
+		if (len < clen + 1)
+			break;
+		++p;
+		nlen = len - clen - 1;
+		printer(arg, " <");
+		for (; clen > 0; --clen) {
+			GETCHAR(x, p);
+			printer(arg, "%.2x", x);
+		}
+		printer(arg, ">, name = ");
+		print_string((char *)p, nlen, printer, arg);
+		break;
+	case CHAP_FAILURE:
+	case CHAP_SUCCESS:
+		printer(arg, " ");
+		print_string((char *)p, len, printer, arg);
+		break;
+	default:
+		for (clen = len; clen > 0; --clen) {
+			GETCHAR(x, p);
+			printer(arg, " %.2x", x);
+		}
+	}
+
+	return len + CHAP_HDRLEN;
+}
+
+struct protent chap_protent = {
+	PPP_CHAP,
+	chap_init,
+	chap_input,
+	chap_protrej,
+	chap_lowerup,
+	chap_lowerdown,
+	NULL,		/* open */
+	NULL,		/* close */
+	chap_print_pkt,
+	NULL,		/* datainput */
+	1,		/* enabled_flag */
+	"CHAP",		/* name */
+	NULL,		/* data_name */
+	chap_option_list,
+	NULL,		/* check_options */
+};
diff --git a/ap/app/pppd/pppd/chap-new.h b/ap/app/pppd/pppd/chap-new.h
new file mode 100644
index 0000000..48235d4
--- /dev/null
+++ b/ap/app/pppd/pppd/chap-new.h
@@ -0,0 +1,130 @@
+/*
+ * chap-new.c - New CHAP implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * CHAP packets begin with a standard header with code, id, len (2 bytes).
+ */
+#define CHAP_HDRLEN	4
+
+/*
+ * Values for the code field.
+ */
+#define CHAP_CHALLENGE	1
+#define CHAP_RESPONSE	2
+#define CHAP_SUCCESS	3
+#define CHAP_FAILURE	4
+
+/*
+ * CHAP digest codes.
+ */
+#define CHAP_MD5		5
+#define CHAP_MICROSOFT		0x80
+#define CHAP_MICROSOFT_V2	0x81
+
+/*
+ * Semi-arbitrary limits on challenge and response fields.
+ */
+#define MAX_CHALLENGE_LEN	64
+#define MAX_RESPONSE_LEN	64
+
+/* bitmask of supported algorithms */
+#define MDTYPE_MICROSOFT_V2	0x1
+#define MDTYPE_MICROSOFT	0x2
+#define MDTYPE_MD5		0x4
+#define MDTYPE_NONE		0
+
+/* hashes supported by this instance of pppd */
+extern int chap_mdtype_all;
+
+/* Return the digest alg. ID for the most preferred digest type. */
+#define CHAP_DIGEST(mdtype) \
+    ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \
+    ((mdtype) & MDTYPE_MICROSOFT_V2)? CHAP_MICROSOFT_V2: \
+    ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \
+    0
+
+/* Return the bit flag (lsb set) for our most preferred digest type. */
+#define CHAP_MDTYPE(mdtype) ((mdtype) ^ ((mdtype) - 1)) & (mdtype)
+
+/* Return the bit flag for a given digest algorithm ID. */
+#define CHAP_MDTYPE_D(digest) \
+    ((digest) == CHAP_MICROSOFT_V2)? MDTYPE_MICROSOFT_V2: \
+    ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \
+    ((digest) == CHAP_MD5)? MDTYPE_MD5: \
+    0
+
+/* Can we do the requested digest? */
+#define CHAP_CANDIGEST(mdtype, digest) \
+    ((digest) == CHAP_MICROSOFT_V2)? (mdtype) & MDTYPE_MICROSOFT_V2: \
+    ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \
+    ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \
+    0
+
+/*
+ * The code for each digest type has to supply one of these.
+ */
+struct chap_digest_type {
+	int code;
+
+	/*
+	 * Note: challenge and response arguments below are formatted as
+	 * a length byte followed by the actual challenge/response data.
+	 */
+	void (*generate_challenge)(unsigned char *challenge);
+	int (*verify_response)(int id, char *name,
+		unsigned char *secret, int secret_len,
+		unsigned char *challenge, unsigned char *response,
+		char *message, int message_space);
+	void (*make_response)(unsigned char *response, int id, char *our_name,
+		unsigned char *challenge, char *secret, int secret_len,
+		unsigned char *priv);
+	int (*check_success)(unsigned char *pkt, int len, unsigned char *priv);
+	void (*handle_failure)(unsigned char *pkt, int len);
+
+	struct chap_digest_type *next;
+};
+
+/* Hook for a plugin to validate CHAP challenge */
+extern int (*chap_verify_hook)(char *name, char *ourname, int id,
+			struct chap_digest_type *digest,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space);
+
+/* Called by digest code to register a digest type */
+extern void chap_register_digest(struct chap_digest_type *);
+
+/* Called by authentication code to start authenticating the peer. */
+extern void chap_auth_peer(int unit, char *our_name, int digest_code);
+
+/* Called by auth. code to start authenticating us to the peer. */
+extern void chap_auth_with_peer(int unit, char *our_name, int digest_code);
+
+/* Represents the CHAP protocol to the main pppd code */
+extern struct protent chap_protent;
diff --git a/ap/app/pppd/pppd/chap_ms.c b/ap/app/pppd/pppd/chap_ms.c
new file mode 100644
index 0000000..a53cc2c
--- /dev/null
+++ b/ap/app/pppd/pppd/chap_ms.c
@@ -0,0 +1,941 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ *   Implemented LANManager type password response to MS-CHAP challenges.
+ *   Now pppd provides both NT style and LANMan style blocks, and the
+ *   prefered is set by option "ms-lanman". Default is to use NT.
+ *   The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ *   You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+/*
+ * Modifications by Frank Cusack, frank@google.com, March 2002.
+ *
+ *   Implemented MS-CHAPv2 functionality, heavily based on sample
+ *   implementation in RFC 2759.  Implemented MPPE functionality,
+ *   heavily based on sample implementation in RFC 3079.
+ *
+ * Copyright (c) 2002 Google, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#define RCSID	"$Id: chap_ms.c,v 1.38 2007/12/01 20:10:51 carlsonj Exp $"
+
+#ifdef CHAPMS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "pppd.h"
+#include "chap-new.h"
+#include "chap_ms.h"
+#include "md4.h"
+#include "sha1.h"
+#include "pppcrypt.h"
+#include "magic.h"
+
+static const char rcsid[] = RCSID;
+
+
+static void	ascii2unicode __P((char[], int, u_char[]));
+static void	NTPasswordHash __P((u_char *, int, u_char[MD4_SIGNATURE_SIZE]));
+static void	ChallengeResponse __P((u_char *, u_char *, u_char[24]));
+static void	ChapMS_NT __P((u_char *, char *, int, u_char[24]));
+static void	ChapMS2_NT __P((u_char *, u_char[16], char *, char *, int,
+				u_char[24]));
+static void	GenerateAuthenticatorResponsePlain
+			__P((char*, int, u_char[24], u_char[16], u_char *,
+			     char *, u_char[41]));
+#ifdef MSLANMAN
+static void	ChapMS_LANMan __P((u_char *, char *, int, u_char *));
+#endif
+
+#ifdef MPPE
+static void	Set_Start_Key __P((u_char *, char *, int));
+static void	SetMasterKeys __P((char *, int, u_char[24], int));
+#endif
+
+#ifdef MSLANMAN
+bool	ms_lanman = 0;    	/* Use LanMan password instead of NT */
+			  	/* Has meaning only with MS-CHAP challenges */
+#endif
+
+#ifdef MPPE
+u_char mppe_send_key[MPPE_MAX_KEY_LEN];
+u_char mppe_recv_key[MPPE_MAX_KEY_LEN];
+int mppe_keys_set = 0;		/* Have the MPPE keys been set? */
+
+#ifdef DEBUGMPPEKEY
+/* For MPPE debug */
+/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
+static char *mschap_challenge = NULL;
+/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
+static char *mschap2_peer_challenge = NULL;
+#endif
+
+#include "fsm.h"		/* Need to poke MPPE options */
+#include "ccp.h"
+#include <net/ppp-comp.h>
+#endif
+
+/*
+ * Command-line options.
+ */
+static option_t chapms_option_list[] = {
+#ifdef MSLANMAN
+	{ "ms-lanman", o_bool, &ms_lanman,
+	  "Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+#ifdef DEBUGMPPEKEY
+	{ "mschap-challenge", o_string, &mschap_challenge,
+	  "specify CHAP challenge" },
+	{ "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
+	  "specify CHAP peer challenge" },
+#endif
+	{ NULL }
+};
+
+/*
+ * chapms_generate_challenge - generate a challenge for MS-CHAP.
+ * For MS-CHAP the challenge length is fixed at 8 bytes.
+ * The length goes in challenge[0] and the actual challenge starts
+ * at challenge[1].
+ */
+static void
+chapms_generate_challenge(unsigned char *challenge)
+{
+	*challenge++ = 8;
+#ifdef DEBUGMPPEKEY
+	if (mschap_challenge && strlen(mschap_challenge) == 8)
+		memcpy(challenge, mschap_challenge, 8);
+	else
+#endif
+		random_bytes(challenge, 8);
+}
+
+static void
+chapms2_generate_challenge(unsigned char *challenge)
+{
+	*challenge++ = 16;
+#ifdef DEBUGMPPEKEY
+	if (mschap_challenge && strlen(mschap_challenge) == 16)
+		memcpy(challenge, mschap_challenge, 16);
+	else
+#endif
+		random_bytes(challenge, 16);
+}
+
+static int
+chapms_verify_response(int id, char *name,
+		       unsigned char *secret, int secret_len,
+		       unsigned char *challenge, unsigned char *response,
+		       char *message, int message_space)
+{
+	unsigned char md[MS_CHAP_RESPONSE_LEN];
+	int diff;
+	int challenge_len, response_len;
+
+	challenge_len = *challenge++;	/* skip length, is 8 */
+	response_len = *response++;
+	if (response_len != MS_CHAP_RESPONSE_LEN)
+		goto bad;
+
+#ifndef MSLANMAN
+	if (!response[MS_CHAP_USENT]) {
+		/* Should really propagate this into the error packet. */
+		warn("Peer request for LANMAN auth not supported");
+		goto bad;
+	}
+#endif
+
+	/* Generate the expected response. */
+	ChapMS(challenge, (char *)secret, secret_len, md);
+
+#ifdef MSLANMAN
+	/* Determine which part of response to verify against */
+	if (!response[MS_CHAP_USENT])
+		diff = memcmp(&response[MS_CHAP_LANMANRESP],
+			      &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
+	else
+#endif
+		diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
+			      MS_CHAP_NTRESP_LEN);
+
+	if (diff == 0) {
+		slprintf(message, message_space, "Access granted");
+		return 1;
+	}
+
+ bad:
+	/* See comments below for MS-CHAP V2 */
+	slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
+		 challenge_len, challenge);
+	return 0;
+}
+
+static int
+chapms2_verify_response(int id, char *name,
+			unsigned char *secret, int secret_len,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space)
+{
+	unsigned char md[MS_CHAP2_RESPONSE_LEN];
+	char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
+	int challenge_len, response_len;
+
+	challenge_len = *challenge++;	/* skip length, is 16 */
+	response_len = *response++;
+	if (response_len != MS_CHAP2_RESPONSE_LEN)
+		goto bad;	/* not even the right length */
+
+	/* Generate the expected response and our mutual auth. */
+	ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name,
+		(char *)secret, secret_len, md,
+		(unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
+
+	/* compare MDs and send the appropriate status */
+	/*
+	 * Per RFC 2759, success message must be formatted as
+	 *     "S=<auth_string> M=<message>"
+	 * where
+	 *     <auth_string> is the Authenticator Response (mutual auth)
+	 *     <message> is a text message
+	 *
+	 * However, some versions of Windows (win98 tested) do not know
+	 * about the M=<message> part (required per RFC 2759) and flag
+	 * it as an error (reported incorrectly as an encryption error
+	 * to the user).  Since the RFC requires it, and it can be
+	 * useful information, we supply it if the peer is a conforming
+	 * system.  Luckily (?), win98 sets the Flags field to 0x04
+	 * (contrary to RFC requirements) so we can use that to
+	 * distinguish between conforming and non-conforming systems.
+	 *
+	 * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
+	 * help debugging this.
+	 */
+	if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
+		   MS_CHAP2_NTRESP_LEN) == 0) {
+		if (response[MS_CHAP2_FLAGS])
+			slprintf(message, message_space, "S=%s", saresponse);
+		else
+			slprintf(message, message_space, "S=%s M=%s",
+				 saresponse, "Access granted");
+		return 1;
+	}
+
+ bad:
+	/*
+	 * Failure message must be formatted as
+	 *     "E=e R=r C=c V=v M=m"
+	 * where
+	 *     e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
+	 *     r = retry (we use 1, ok to retry)
+	 *     c = challenge to use for next response, we reuse previous
+	 *     v = Change Password version supported, we use 0
+	 *     m = text message
+	 *
+	 * The M=m part is only for MS-CHAPv2.  Neither win2k nor
+	 * win98 (others untested) display the message to the user anyway.
+	 * They also both ignore the E=e code.
+	 *
+	 * Note that it's safe to reuse the same challenge as we don't
+	 * actually accept another response based on the error message
+	 * (and no clients try to resend a response anyway).
+	 *
+	 * Basically, this whole bit is useless code, even the small
+	 * implementation here is only because of overspecification.
+	 */
+	slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
+		 challenge_len, challenge, "Access denied");
+	return 0;
+}
+
+static void
+chapms_make_response(unsigned char *response, int id, char *our_name,
+		     unsigned char *challenge, char *secret, int secret_len,
+		     unsigned char *private)
+{
+	challenge++;	/* skip length, should be 8 */
+	*response++ = MS_CHAP_RESPONSE_LEN;
+	ChapMS(challenge, secret, secret_len, response);
+}
+
+static void
+chapms2_make_response(unsigned char *response, int id, char *our_name,
+		      unsigned char *challenge, char *secret, int secret_len,
+		      unsigned char *private)
+{
+	challenge++;	/* skip length, should be 16 */
+	*response++ = MS_CHAP2_RESPONSE_LEN;
+	ChapMS2(challenge,
+#ifdef DEBUGMPPEKEY
+		mschap2_peer_challenge,
+#else
+		NULL,
+#endif
+		our_name, secret, secret_len, response, private,
+		MS_CHAP2_AUTHENTICATEE);
+}
+
+static int
+chapms2_check_success(unsigned char *msg, int len, unsigned char *private)
+{
+	if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
+	    strncmp((char *)msg, "S=", 2) != 0) {
+		/* Packet does not start with "S=" */
+		error("MS-CHAPv2 Success packet is badly formed.");
+		return 0;
+	}
+	msg += 2;
+	len -= 2;
+	if (len < MS_AUTH_RESPONSE_LENGTH
+	    || memcmp(msg, private, MS_AUTH_RESPONSE_LENGTH)) {
+		/* Authenticator Response did not match expected. */
+		error("MS-CHAPv2 mutual authentication failed.");
+		return 0;
+	}
+	/* Authenticator Response matches. */
+	msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
+	len -= MS_AUTH_RESPONSE_LENGTH;
+	if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
+		msg += 3; /* Eat the delimiter */
+	} else if (len) {
+		/* Packet has extra text which does not begin " M=" */
+		error("MS-CHAPv2 Success packet is badly formed.");
+		return 0;
+	}
+	return 1;
+}
+
+static void
+chapms_handle_failure(unsigned char *inp, int len)
+{
+	int err;
+	char *p, *msg;
+
+	/* We want a null-terminated string for strxxx(). */
+	msg = malloc(len + 1);
+	if (!msg) {
+		warn("Out of memory in chapms_handle_failure");
+		return;
+	}
+	BCOPY(inp, msg, len);
+	msg[len] = 0;
+	p = msg;
+
+	/*
+	 * Deal with MS-CHAP formatted failure messages; just print the
+	 * M=<message> part (if any).  For MS-CHAP we're not really supposed
+	 * to use M=<message>, but it shouldn't hurt.  See
+	 * chapms[2]_verify_response.
+	 */
+	if (!strncmp(p, "E=", 2))
+		err = strtol(p+2, NULL, 10); /* Remember the error code. */
+	else
+		goto print_msg; /* Message is badly formatted. */
+
+	if (len && ((p = strstr(p, " M=")) != NULL)) {
+		/* M=<message> field found. */
+		p += 3;
+	} else {
+		/* No M=<message>; use the error code. */
+		switch (err) {
+		case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
+			p = "E=646 Restricted logon hours";
+			break;
+
+		case MS_CHAP_ERROR_ACCT_DISABLED:
+			p = "E=647 Account disabled";
+			break;
+
+		case MS_CHAP_ERROR_PASSWD_EXPIRED:
+			p = "E=648 Password expired";
+			break;
+
+		case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
+			p = "E=649 No dialin permission";
+			break;
+
+		case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
+			p = "E=691 Authentication failure";
+			break;
+
+		case MS_CHAP_ERROR_CHANGING_PASSWORD:
+			/* Should never see this, we don't support Change Password. */
+			p = "E=709 Error changing password";
+			break;
+
+		default:
+			free(msg);
+			error("Unknown MS-CHAP authentication failure: %.*v",
+			      len, inp);
+			return;
+		}
+	}
+print_msg:
+	if (p != NULL)
+		error("MS-CHAP authentication failed: %v", p);
+	free(msg);
+}
+
+static void
+ChallengeResponse(u_char *challenge,
+		  u_char PasswordHash[MD4_SIGNATURE_SIZE],
+		  u_char response[24])
+{
+    u_char    ZPasswordHash[21];
+
+    BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+    BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
+
+#if 0
+    warn("ChallengeResponse - ZPasswordHash %.*B",
+	   sizeof(ZPasswordHash), ZPasswordHash);
+#endif
+
+    (void) DesSetkey(ZPasswordHash + 0);
+    DesEncrypt(challenge, response + 0);
+    (void) DesSetkey(ZPasswordHash + 7);
+    DesEncrypt(challenge, response + 8);
+    (void) DesSetkey(ZPasswordHash + 14);
+    DesEncrypt(challenge, response + 16);
+
+#if 0
+    warn("ChallengeResponse - response %.24B", response);
+#endif
+}
+
+void
+ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
+	      char *username, u_char Challenge[8])
+    
+{
+    SHA1_CTX	sha1Context;
+    u_char	sha1Hash[SHA1_SIGNATURE_SIZE];
+    char	*user;
+
+    /* remove domain from "domain\username" */
+    if ((user = strrchr(username, '\\')) != NULL)
+	++user;
+    else
+	user = username;
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PeerChallenge, 16);
+    SHA1_Update(&sha1Context, rchallenge, 16);
+    SHA1_Update(&sha1Context, (unsigned char *)user, strlen(user));
+    SHA1_Final(sha1Hash, &sha1Context);
+
+    BCOPY(sha1Hash, Challenge, 8);
+}
+
+/*
+ * Convert the ASCII version of the password to Unicode.
+ * This implicitly supports 8-bit ISO8859/1 characters.
+ * This gives us the little-endian representation, which
+ * is assumed by all M$ CHAP RFCs.  (Unicode byte ordering
+ * is machine-dependent.)
+ */
+static void
+ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
+{
+    int i;
+
+    BZERO(unicode, ascii_len * 2);
+    for (i = 0; i < ascii_len; i++)
+	unicode[i * 2] = (u_char) ascii[i];
+}
+
+static void
+NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
+{
+#ifdef __NetBSD__
+    /* NetBSD uses the libc md4 routines which take bytes instead of bits */
+    int			mdlen = secret_len;
+#else
+    int			mdlen = secret_len * 8;
+#endif
+    MD4_CTX		md4Context;
+
+    MD4Init(&md4Context);
+    /* MD4Update can take at most 64 bytes at a time */
+    while (mdlen > 512) {
+	MD4Update(&md4Context, secret, 512);
+	secret += 64;
+	mdlen -= 512;
+    }
+    MD4Update(&md4Context, secret, mdlen);
+    MD4Final(hash, &md4Context);
+
+}
+
+static void
+ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
+	  u_char NTResponse[24])
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+
+    ChallengeResponse(rchallenge, PasswordHash, NTResponse);
+}
+
+static void
+ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username,
+	   char *secret, int secret_len, u_char NTResponse[24])
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	Challenge[8];
+
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+
+    ChallengeResponse(Challenge, PasswordHash, NTResponse);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void
+ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
+	      unsigned char *response)
+{
+    int			i;
+    u_char		UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+    u_char		PasswordHash[MD4_SIGNATURE_SIZE];
+
+    /* LANMan password is case insensitive */
+    BZERO(UcasePassword, sizeof(UcasePassword));
+    for (i = 0; i < secret_len; i++)
+       UcasePassword[i] = (u_char)toupper(secret[i]);
+    (void) DesSetkey(UcasePassword + 0);
+    DesEncrypt( StdText, PasswordHash + 0 );
+    (void) DesSetkey(UcasePassword + 7);
+    DesEncrypt( StdText, PasswordHash + 8 );
+    ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
+}
+#endif
+
+
+void
+GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+			      u_char NTResponse[24], u_char PeerChallenge[16],
+			      u_char *rchallenge, char *username,
+			      u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
+{
+    /*
+     * "Magic" constants used in response generation, from RFC 2759.
+     */
+    u_char Magic1[39] = /* "Magic server to client signing constant" */
+	{ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+	  0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+	  0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+	  0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
+    u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
+	{ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+	  0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+	  0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+	  0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+	  0x6E };
+
+    int		i;
+    SHA1_CTX	sha1Context;
+    u_char	Digest[SHA1_SIGNATURE_SIZE];
+    u_char	Challenge[8];
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, NTResponse, 24);
+    SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
+    SHA1_Final(Digest, &sha1Context);
+
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, Digest, sizeof(Digest));
+    SHA1_Update(&sha1Context, Challenge, sizeof(Challenge));
+    SHA1_Update(&sha1Context, Magic2, sizeof(Magic2));
+    SHA1_Final(Digest, &sha1Context);
+
+    /* Convert to ASCII hex string. */
+    for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++)
+	sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
+}
+
+
+static void
+GenerateAuthenticatorResponsePlain
+		(char *secret, int secret_len,
+		 u_char NTResponse[24], u_char PeerChallenge[16],
+		 u_char *rchallenge, char *username,
+		 u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	PasswordHashHash[MD4_SIGNATURE_SIZE];
+
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash),
+		   PasswordHashHash);
+
+    GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
+				  rchallenge, username, authResponse);
+}
+
+
+#ifdef MPPE
+/*
+ * Set mppe_xxxx_key from the NTPasswordHashHash.
+ * RFC 2548 (RADIUS support) requires us to export this function (ugh).
+ */
+void
+mppe_set_keys(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE])
+{
+    SHA1_CTX	sha1Context;
+    u_char	Digest[SHA1_SIGNATURE_SIZE];	/* >= MPPE_MAX_KEY_LEN */
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, rchallenge, 8);
+    SHA1_Final(Digest, &sha1Context);
+
+    /* Same key in both directions. */
+    BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key));
+    BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key));
+
+    mppe_keys_set = 1;
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
+ */
+static void
+Set_Start_Key(u_char *rchallenge, char *secret, int secret_len)
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	PasswordHashHash[MD4_SIGNATURE_SIZE];
+
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+    mppe_set_keys(rchallenge, PasswordHashHash);
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
+ *
+ * This helper function used in the Winbind module, which gets the
+ * NTHashHash from the server.
+ */
+void
+mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+	       u_char NTResponse[24], int IsServer)
+{
+    SHA1_CTX	sha1Context;
+    u_char	MasterKey[SHA1_SIGNATURE_SIZE];	/* >= MPPE_MAX_KEY_LEN */
+    u_char	Digest[SHA1_SIGNATURE_SIZE];	/* >= MPPE_MAX_KEY_LEN */
+
+    u_char SHApad1[40] =
+	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+    u_char SHApad2[40] =
+	{ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
+
+    /* "This is the MPPE Master Key" */
+    u_char Magic1[27] =
+	{ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+	  0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+	  0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
+    /* "On the client side, this is the send key; "
+       "on the server side, it is the receive key." */
+    u_char Magic2[84] =
+	{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+	  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+	  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+	  0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+	  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+	  0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+	  0x6b, 0x65, 0x79, 0x2e };
+    /* "On the client side, this is the receive key; "
+       "on the server side, it is the send key." */
+    u_char Magic3[84] =
+	{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+	  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+	  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+	  0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+	  0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+	  0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+	  0x6b, 0x65, 0x79, 0x2e };
+    u_char *s;
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, NTResponse, 24);
+    SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
+    SHA1_Final(MasterKey, &sha1Context);
+
+    /*
+     * generate send key
+     */
+    if (IsServer)
+	s = Magic3;
+    else
+	s = Magic2;
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, MasterKey, 16);
+    SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1));
+    SHA1_Update(&sha1Context, s, 84);
+    SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2));
+    SHA1_Final(Digest, &sha1Context);
+
+    BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key));
+
+    /*
+     * generate recv key
+     */
+    if (IsServer)
+	s = Magic2;
+    else
+	s = Magic3;
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, MasterKey, 16);
+    SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1));
+    SHA1_Update(&sha1Context, s, 84);
+    SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2));
+    SHA1_Final(Digest, &sha1Context);
+
+    BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key));
+
+    mppe_keys_set = 1;
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
+ */
+static void
+SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer)
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	PasswordHashHash[MD4_SIGNATURE_SIZE];
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+    mppe_set_keys2(PasswordHashHash, NTResponse, IsServer);
+}
+
+#endif /* MPPE */
+
+
+void
+ChapMS(u_char *rchallenge, char *secret, int secret_len,
+       unsigned char *response)
+{
+    BZERO(response, MS_CHAP_RESPONSE_LEN);
+
+    ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
+
+#ifdef MSLANMAN
+    ChapMS_LANMan(rchallenge, secret, secret_len,
+		  &response[MS_CHAP_LANMANRESP]);
+
+    /* preferred method is set by option  */
+    response[MS_CHAP_USENT] = !ms_lanman;
+#else
+    response[MS_CHAP_USENT] = 1;
+#endif
+
+#ifdef MPPE
+    Set_Start_Key(rchallenge, secret, secret_len);
+#endif
+}
+
+
+/*
+ * If PeerChallenge is NULL, one is generated and the PeerChallenge
+ * field of response is filled in.  Call this way when generating a response.
+ * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
+ * Call this way when verifying a response (or debugging).
+ * Do not call with PeerChallenge = response.
+ *
+ * The PeerChallenge field of response is then used for calculation of the
+ * Authenticator Response.
+ */
+void
+ChapMS2(u_char *rchallenge, u_char *PeerChallenge,
+	char *user, char *secret, int secret_len, unsigned char *response,
+	u_char authResponse[], int authenticator)
+{
+    /* ARGSUSED */
+    u_char *p = &response[MS_CHAP2_PEER_CHALLENGE];
+    int i;
+
+    BZERO(response, MS_CHAP2_RESPONSE_LEN);
+
+    /* Generate the Peer-Challenge if requested, or copy it if supplied. */
+    if (!PeerChallenge)
+	for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++)
+	    *p++ = (u_char) (drand48() * 0xff);
+    else
+	BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE],
+	      MS_CHAP2_PEER_CHAL_LEN);
+
+    /* Generate the NT-Response */
+    ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
+	       secret, secret_len, &response[MS_CHAP2_NTRESP]);
+
+    /* Generate the Authenticator Response. */
+    GenerateAuthenticatorResponsePlain(secret, secret_len,
+				       &response[MS_CHAP2_NTRESP],
+				       &response[MS_CHAP2_PEER_CHALLENGE],
+				       rchallenge, user, authResponse);
+
+#ifdef MPPE
+    SetMasterKeys(secret, secret_len,
+		  &response[MS_CHAP2_NTRESP], authenticator);
+#endif
+}
+
+#ifdef MPPE
+/*
+ * Set MPPE options from plugins.
+ */
+void
+set_mppe_enc_types(int policy, int types)
+{
+    /* Early exit for unknown policies. */
+    if (policy != MPPE_ENC_POL_ENC_ALLOWED ||
+	policy != MPPE_ENC_POL_ENC_REQUIRED)
+	return;
+
+    /* Don't modify MPPE if it's optional and wasn't already configured. */
+    if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe)
+	return;
+
+    /*
+     * Disable undesirable encryption types.  Note that we don't ENABLE
+     * any encryption types, to avoid overriding manual configuration.
+     */
+    switch(types) {
+	case MPPE_ENC_TYPES_RC4_40:
+	    ccp_wantoptions[0].mppe &= ~MPPE_OPT_128;	/* disable 128-bit */
+	    break;
+	case MPPE_ENC_TYPES_RC4_128:
+	    ccp_wantoptions[0].mppe &= ~MPPE_OPT_40;	/* disable 40-bit */
+	    break;
+	default:
+	    break;
+    }
+}
+#endif /* MPPE */
+
+static struct chap_digest_type chapms_digest = {
+	CHAP_MICROSOFT,		/* code */
+	chapms_generate_challenge,
+	chapms_verify_response,
+	chapms_make_response,
+	NULL,			/* check_success */
+	chapms_handle_failure,
+};
+
+static struct chap_digest_type chapms2_digest = {
+	CHAP_MICROSOFT_V2,	/* code */
+	chapms2_generate_challenge,
+	chapms2_verify_response,
+	chapms2_make_response,
+	chapms2_check_success,
+	chapms_handle_failure,
+};
+
+void
+chapms_init(void)
+{
+	chap_register_digest(&chapms_digest);
+	chap_register_digest(&chapms2_digest);
+	add_options(chapms_option_list);
+}
+
+#endif /* CHAPMS */
diff --git a/ap/app/pppd/pppd/chap_ms.h b/ap/app/pppd/pppd/chap_ms.h
new file mode 100644
index 0000000..0177289
--- /dev/null
+++ b/ap/app/pppd/pppd/chap_ms.h
@@ -0,0 +1,109 @@
+/*
+ * chap_ms.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: chap_ms.h,v 1.3 2007-06-08 04:02:38 gerg Exp $
+ */
+
+#ifndef __CHAPMS_INCLUDE__
+
+#define MD4_SIGNATURE_SIZE	16	/* 16 bytes in a MD4 message digest */
+#define MAX_NT_PASSWORD		256	/* Max (Unicode) chars in an NT pass */
+
+#define MS_CHAP_RESPONSE_LEN	49	/* Response length for MS-CHAP */
+#define MS_CHAP2_RESPONSE_LEN	49	/* Response length for MS-CHAPv2 */
+#define MS_AUTH_RESPONSE_LENGTH	40	/* MS-CHAPv2 authenticator response, */
+					/* as ASCII */
+
+/* E=eeeeeeeeee error codes for MS-CHAP failure messages. */
+#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS	646
+#define MS_CHAP_ERROR_ACCT_DISABLED		647
+#define MS_CHAP_ERROR_PASSWD_EXPIRED		648
+#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION	649
+#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE	691
+#define MS_CHAP_ERROR_CHANGING_PASSWORD		709
+
+/*
+ * Offsets within the response field for MS-CHAP
+ */
+#define MS_CHAP_LANMANRESP	0
+#define MS_CHAP_LANMANRESP_LEN	24
+#define MS_CHAP_NTRESP		24
+#define MS_CHAP_NTRESP_LEN	24
+#define MS_CHAP_USENT		48
+
+/*
+ * Offsets within the response field for MS-CHAP2
+ */
+#define MS_CHAP2_PEER_CHALLENGE	0
+#define MS_CHAP2_PEER_CHAL_LEN	16
+#define MS_CHAP2_RESERVED_LEN	8
+#define MS_CHAP2_NTRESP		24
+#define MS_CHAP2_NTRESP_LEN	24
+#define MS_CHAP2_FLAGS		48
+
+#ifdef MPPE
+#include "mppe.h"	/* MPPE_MAX_KEY_LEN */
+extern u_char mppe_send_key[MPPE_MAX_KEY_LEN];
+extern u_char mppe_recv_key[MPPE_MAX_KEY_LEN];
+extern int mppe_keys_set;
+
+/* These values are the RADIUS attribute values--see RFC 2548. */
+#define MPPE_ENC_POL_ENC_ALLOWED 1
+#define MPPE_ENC_POL_ENC_REQUIRED 2
+#define MPPE_ENC_TYPES_RC4_40 2
+#define MPPE_ENC_TYPES_RC4_128 4
+
+/* used by plugins (using above values) */
+extern void set_mppe_enc_types(int, int);
+#endif
+
+/* Are we the authenticator or authenticatee?  For MS-CHAPv2 key derivation. */
+#define MS_CHAP2_AUTHENTICATEE 0
+#define MS_CHAP2_AUTHENTICATOR 1
+
+void ChapMS __P((u_char *, char *, int, u_char *));
+void ChapMS2 __P((u_char *, u_char *, char *, char *, int,
+		  u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int));
+#ifdef MPPE
+void mppe_set_keys __P((u_char *, u_char[MD4_SIGNATURE_SIZE]));
+void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+		    u_char NTResponse[24], int IsServer);
+#endif
+
+void	ChallengeHash __P((u_char[16], u_char *, char *, u_char[8]));
+
+void GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+			u_char NTResponse[24], u_char PeerChallenge[16],
+			u_char *rchallenge, char *username,
+			u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]);
+
+void chapms_init(void);
+
+#define __CHAPMS_INCLUDE__
+#endif /* __CHAPMS_INCLUDE__ */
diff --git a/ap/app/pppd/pppd/demand.c b/ap/app/pppd/pppd/demand.c
new file mode 100644
index 0000000..64b6ed4
--- /dev/null
+++ b/ap/app/pppd/pppd/demand.c
@@ -0,0 +1,364 @@
+/*
+ * demand.c - Support routines for demand-dialling.
+ *
+ * Copyright (c) 1996-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: demand.c,v 1.2 2007-06-08 04:02:38 gerg Exp $"
+
+#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 <pcap-bpf.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));
+    if (ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0) < 0
+	|| ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0) < 0)
+	    fatal("Couldn't set up demand-dialled PPP interface: %m");
+
+#ifdef PPP_FILTER
+    set_filters(&pass_filter, &active_filter);
+#endif
+
+    /*
+     * Call the demand_conf procedure for each protocol that's got one.
+     */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if (protp->enabled_flag && protp->demand_conf != NULL)
+	    if (!((*protp->demand_conf)(0)))
+		die(1);
+}
+
+
+/*
+ * demand_block - set each network protocol to block further packets.
+ */
+void
+demand_block()
+{
+    int i;
+    struct protent *protp;
+
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if (protp->enabled_flag && protp->demand_conf != NULL)
+	    sifnpmode(0, protp->protocol & ~0x8000, NPMODE_QUEUE);
+    get_loop_output();
+}
+
+/*
+ * demand_discard - set each network protocol to discard packets
+ * with an error.
+ */
+void
+demand_discard()
+{
+    struct packet *pkt, *nextpkt;
+    int i;
+    struct protent *protp;
+
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if (protp->enabled_flag && protp->demand_conf != NULL)
+	    sifnpmode(0, protp->protocol & ~0x8000, NPMODE_ERROR);
+    get_loop_output();
+
+    /* discard all saved packets */
+    for (pkt = pend_q; pkt != NULL; pkt = nextpkt) {
+	nextpkt = pkt->next;
+	free(pkt);
+    }
+    pend_q = NULL;
+    framelen = 0;
+    flush_flag = 0;
+    escape_flag = 0;
+    fcs = PPP_INITFCS;
+}
+
+/*
+ * demand_unblock - set each enabled network protocol to pass packets.
+ */
+void
+demand_unblock()
+{
+    int i;
+    struct protent *protp;
+
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if (protp->enabled_flag && protp->demand_conf != NULL)
+	    sifnpmode(0, protp->protocol & ~0x8000, NPMODE_PASS);
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+	0x0000,	0x1189,	0x2312,	0x329b,	0x4624,	0x57ad,	0x6536,	0x74bf,
+	0x8c48,	0x9dc1,	0xaf5a,	0xbed3,	0xca6c,	0xdbe5,	0xe97e,	0xf8f7,
+	0x1081,	0x0108,	0x3393,	0x221a,	0x56a5,	0x472c,	0x75b7,	0x643e,
+	0x9cc9,	0x8d40,	0xbfdb,	0xae52,	0xdaed,	0xcb64,	0xf9ff,	0xe876,
+	0x2102,	0x308b,	0x0210,	0x1399,	0x6726,	0x76af,	0x4434,	0x55bd,
+	0xad4a,	0xbcc3,	0x8e58,	0x9fd1,	0xeb6e,	0xfae7,	0xc87c,	0xd9f5,
+	0x3183,	0x200a,	0x1291,	0x0318,	0x77a7,	0x662e,	0x54b5,	0x453c,
+	0xbdcb,	0xac42,	0x9ed9,	0x8f50,	0xfbef,	0xea66,	0xd8fd,	0xc974,
+	0x4204,	0x538d,	0x6116,	0x709f,	0x0420,	0x15a9,	0x2732,	0x36bb,
+	0xce4c,	0xdfc5,	0xed5e,	0xfcd7,	0x8868,	0x99e1,	0xab7a,	0xbaf3,
+	0x5285,	0x430c,	0x7197,	0x601e,	0x14a1,	0x0528,	0x37b3,	0x263a,
+	0xdecd,	0xcf44,	0xfddf,	0xec56,	0x98e9,	0x8960,	0xbbfb,	0xaa72,
+	0x6306,	0x728f,	0x4014,	0x519d,	0x2522,	0x34ab,	0x0630,	0x17b9,
+	0xef4e,	0xfec7,	0xcc5c,	0xddd5,	0xa96a,	0xb8e3,	0x8a78,	0x9bf1,
+	0x7387,	0x620e,	0x5095,	0x411c,	0x35a3,	0x242a,	0x16b1,	0x0738,
+	0xffcf,	0xee46,	0xdcdd,	0xcd54,	0xb9eb,	0xa862,	0x9af9,	0x8b70,
+	0x8408,	0x9581,	0xa71a,	0xb693,	0xc22c,	0xd3a5,	0xe13e,	0xf0b7,
+	0x0840,	0x19c9,	0x2b52,	0x3adb,	0x4e64,	0x5fed,	0x6d76,	0x7cff,
+	0x9489,	0x8500,	0xb79b,	0xa612,	0xd2ad,	0xc324,	0xf1bf,	0xe036,
+	0x18c1,	0x0948,	0x3bd3,	0x2a5a,	0x5ee5,	0x4f6c,	0x7df7,	0x6c7e,
+	0xa50a,	0xb483,	0x8618,	0x9791,	0xe32e,	0xf2a7,	0xc03c,	0xd1b5,
+	0x2942,	0x38cb,	0x0a50,	0x1bd9,	0x6f66,	0x7eef,	0x4c74,	0x5dfd,
+	0xb58b,	0xa402,	0x9699,	0x8710,	0xf3af,	0xe226,	0xd0bd,	0xc134,
+	0x39c3,	0x284a,	0x1ad1,	0x0b58,	0x7fe7,	0x6e6e,	0x5cf5,	0x4d7c,
+	0xc60c,	0xd785,	0xe51e,	0xf497,	0x8028,	0x91a1,	0xa33a,	0xb2b3,
+	0x4a44,	0x5bcd,	0x6956,	0x78df,	0x0c60,	0x1de9,	0x2f72,	0x3efb,
+	0xd68d,	0xc704,	0xf59f,	0xe416,	0x90a9,	0x8120,	0xb3bb,	0xa232,
+	0x5ac5,	0x4b4c,	0x79d7,	0x685e,	0x1ce1,	0x0d68,	0x3ff3,	0x2e7a,
+	0xe70e,	0xf687,	0xc41c,	0xd595,	0xa12a,	0xb0a3,	0x8238,	0x93b1,
+	0x6b46,	0x7acf,	0x4854,	0x59dd,	0x2d62,	0x3ceb,	0x0e70,	0x1ff9,
+	0xf78f,	0xe606,	0xd49d,	0xc514,	0xb1ab,	0xa022,	0x92b9,	0x8330,
+	0x7bc7,	0x6a4e,	0x58d5,	0x495c,	0x3de3,	0x2c6a,	0x1ef1,	0x0f78
+};
+
+/*
+ * loop_chars - process characters received from the loopback.
+ * Calls loop_frame when a complete frame has been accumulated.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+loop_chars(p, n)
+    unsigned char *p;
+    int n;
+{
+    int c, rv;
+
+    rv = 0;
+    for (; n > 0; --n) {
+	c = *p++;
+	if (c == PPP_FLAG) {
+	    if (!escape_flag && !flush_flag
+		&& framelen > 2 && fcs == PPP_GOODFCS) {
+		framelen -= 2;
+		if (loop_frame((unsigned char *)frame, framelen))
+		    rv = 1;
+	    }
+	    framelen = 0;
+	    flush_flag = 0;
+	    escape_flag = 0;
+	    fcs = PPP_INITFCS;
+	    continue;
+	}
+	if (flush_flag)
+	    continue;
+	if (escape_flag) {
+	    c ^= PPP_TRANS;
+	    escape_flag = 0;
+	} else if (c == PPP_ESCAPE) {
+	    escape_flag = 1;
+	    continue;
+	}
+	if (framelen >= framemax) {
+	    flush_flag = 1;
+	    continue;
+	}
+	frame[framelen++] = c;
+	fcs = PPP_FCS(fcs, c);
+    }
+    return rv;
+}
+
+/*
+ * loop_frame - given a frame obtained from the loopback,
+ * decide whether to bring up the link or not, and, if we want
+ * to transmit this frame later, put it on the pending queue.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ * We assume that the kernel driver has already applied the
+ * pass_filter, so we won't get packets it rejected.
+ * We apply the active_filter to see if we want this packet to
+ * bring up the link.
+ */
+int
+loop_frame(frame, len)
+    unsigned char *frame;
+    int len;
+{
+    struct packet *pkt;
+
+    /* warn("from loop: %P", frame, len); */
+    if (len < PPP_HDRLEN)
+	return 0;
+    if ((PPP_PROTOCOL(frame) & 0x8000) != 0)
+	return 0;		/* shouldn't get any of these anyway */
+    if (!active_packet(frame, len))
+	return 0;
+
+    pkt = (struct packet *) malloc(sizeof(struct packet) + len);
+    if (pkt != NULL) {
+	pkt->length = len;
+	pkt->next = NULL;
+	memcpy(pkt->data, frame, len);
+	if (pend_q == NULL)
+	    pend_q = pkt;
+	else
+	    pend_qtail->next = pkt;
+	pend_qtail = pkt;
+    }
+    return 1;
+}
+
+/*
+ * demand_rexmit - Resend all those frames which we got via the
+ * loopback, now that the real serial link is up.
+ */
+void
+demand_rexmit(proto)
+    int proto;
+{
+    struct packet *pkt, *prev, *nextpkt;
+
+    prev = NULL;
+    pkt = pend_q;
+    pend_q = NULL;
+    for (; pkt != NULL; pkt = nextpkt) {
+	nextpkt = pkt->next;
+	if (PPP_PROTOCOL(pkt->data) == proto) {
+	    output(0, pkt->data, pkt->length);
+	    free(pkt);
+	} else {
+	    if (prev == NULL)
+		pend_q = pkt;
+	    else
+		prev->next = pkt;
+	    prev = pkt;
+	}
+    }
+    pend_qtail = prev;
+    if (prev != NULL)
+	prev->next = NULL;
+}
+
+/*
+ * Scan a packet to decide whether it is an "active" packet,
+ * that is, whether it is worth bringing up the link for.
+ */
+static int
+active_packet(p, len)
+    unsigned char *p;
+    int len;
+{
+    int proto, i;
+    struct protent *protp;
+
+    if (len < PPP_HDRLEN)
+	return 0;
+    proto = PPP_PROTOCOL(p);
+#ifdef PPP_FILTER
+    p[0] = 1;		/* outbound packet indicator */
+    if ((pass_filter.bf_len != 0
+	 && bpf_filter(pass_filter.bf_insns, p, len, len) == 0)
+	|| (active_filter.bf_len != 0
+	    && bpf_filter(active_filter.bf_insns, p, len, len) == 0)) {
+	p[0] = 0xff;
+	return 0;
+    }
+    p[0] = 0xff;
+#endif
+    for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+	if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
+	    if (!protp->enabled_flag)
+		return 0;
+	    if (protp->active_pkt == NULL)
+		return 1;
+	    return (*protp->active_pkt)(p, len);
+	}
+    }
+    return 0;			/* not a supported protocol !!?? */
+}
diff --git a/ap/app/pppd/pppd/eap.c b/ap/app/pppd/pppd/eap.c
new file mode 100644
index 0000000..b57844f
--- /dev/null
+++ b/ap/app/pppd/pppd/eap.c
@@ -0,0 +1,2428 @@
+/*
+ * eap.c - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * This implementation of EAP supports MD5-Challenge and SRP-SHA1
+ * authentication styles.  Note that support of MD5-Challenge is a
+ * requirement of RFC 2284, and that it's essentially just a
+ * reimplementation of regular RFC 1994 CHAP using EAP messages.
+ *
+ * As an authenticator ("server"), there are multiple phases for each
+ * style.  In the first phase of each style, the unauthenticated peer
+ * name is queried using the EAP Identity request type.  If the
+ * "remotename" option is used, then this phase is skipped, because
+ * the peer's name is presumed to be known.
+ *
+ * For MD5-Challenge, there are two phases, and the second phase
+ * consists of sending the challenge itself and handling the
+ * associated response.
+ *
+ * For SRP-SHA1, there are four phases.  The second sends 's', 'N',
+ * and 'g'.  The reply contains 'A'.  The third sends 'B', and the
+ * reply contains 'M1'.  The forth sends the 'M2' value.
+ *
+ * As an authenticatee ("client"), there's just a single phase --
+ * responding to the queries generated by the peer.  EAP is an
+ * authenticator-driven protocol.
+ *
+ * Based on draft-ietf-pppext-eap-srp-03.txt.
+ */
+
+#define RCSID	"$Id: eap.c,v 1.4 2004/11/09 22:39:25 paulus Exp $"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "pppd.h"
+#include "pathnames.h"
+#include "md5.h"
+#include "eap.h"
+
+#ifdef USE_SRP
+#include <t_pwd.h>
+#include <t_server.h>
+#include <t_client.h>
+#include "pppcrypt.h"
+#endif /* USE_SRP */
+
+#ifndef SHA_DIGESTSIZE
+#define	SHA_DIGESTSIZE 20
+#endif
+
+static const char rcsid[] = RCSID;
+
+eap_state eap_states[NUM_PPP];		/* EAP state; one for each unit */
+#ifdef USE_SRP
+static char *pn_secret = NULL;		/* Pseudonym generating secret */
+#endif
+
+/*
+ * Command-line options.
+ */
+static option_t eap_option_list[] = {
+    { "eap-restart", o_int, &eap_states[0].es_server.ea_timeout,
+      "Set retransmit timeout for EAP Requests (server)" },
+    { "eap-max-sreq", o_int, &eap_states[0].es_server.ea_maxrequests,
+      "Set max number of EAP Requests sent (server)" },
+    { "eap-timeout", o_int, &eap_states[0].es_client.ea_timeout,
+      "Set time limit for peer EAP authentication" },
+    { "eap-max-rreq", o_int, &eap_states[0].es_client.ea_maxrequests,
+      "Set max number of EAP Requests allows (client)" },
+    { "eap-interval", o_int, &eap_states[0].es_rechallenge,
+      "Set interval for EAP rechallenge" },
+#ifdef USE_SRP
+    { "srp-interval", o_int, &eap_states[0].es_lwrechallenge,
+      "Set interval for SRP lightweight rechallenge" },
+    { "srp-pn-secret", o_string, &pn_secret,
+      "Long term pseudonym generation secret" },
+    { "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo,
+      "Use pseudonym if offered one by server", 1 },
+#endif
+    { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void eap_init __P((int unit));
+static void eap_input __P((int unit, u_char *inp, int inlen));
+static void eap_protrej __P((int unit));
+static void eap_lowerup __P((int unit));
+static void eap_lowerdown __P((int unit));
+static int  eap_printpkt __P((u_char *inp, int inlen,
+    void (*)(void *arg, char *fmt, ...), void *arg));
+
+struct protent eap_protent = {
+	PPP_EAP,		/* protocol number */
+	eap_init,		/* initialization procedure */
+	eap_input,		/* process a received packet */
+	eap_protrej,		/* process a received protocol-reject */
+	eap_lowerup,		/* lower layer has gone up */
+	eap_lowerdown,		/* lower layer has gone down */
+	NULL,			/* open the protocol */
+	NULL,			/* close the protocol */
+	eap_printpkt,		/* print a packet in readable form */
+	NULL,			/* process a received data packet */
+	1,			/* protocol enabled */
+	"EAP",			/* text name of protocol */
+	NULL,			/* text name of corresponding data protocol */
+	eap_option_list,	/* list of command-line options */
+	NULL,			/* check requested options; assign defaults */
+	NULL,			/* configure interface for demand-dial */
+	NULL			/* say whether to bring up link for this pkt */
+};
+
+/*
+ * A well-known 2048 bit modulus.
+ */
+static const u_char wkmodulus[] = {
+	0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
+	0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
+	0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
+	0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
+	0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
+	0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
+	0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
+	0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
+	0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
+	0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
+	0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
+	0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
+	0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
+	0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
+	0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
+	0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
+	0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
+	0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
+	0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
+	0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
+	0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
+	0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
+	0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
+	0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
+	0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
+	0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
+	0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
+	0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
+	0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
+	0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
+	0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
+	0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
+};
+
+/* Local forward declarations. */
+static void eap_server_timeout __P((void *arg));
+
+/*
+ * Convert EAP state code to printable string for debug.
+ */
+static const char *
+eap_state_name(esc)
+enum eap_state_code esc;
+{
+	static const char *state_names[] = { EAP_STATES };
+
+	return (state_names[(int)esc]);
+}
+
+/*
+ * eap_init - Initialize state for an EAP user.  This is currently
+ * called once by main() during start-up.
+ */
+static void
+eap_init(unit)
+int unit;
+{
+	eap_state *esp = &eap_states[unit];
+
+	BZERO(esp, sizeof (*esp));
+	esp->es_unit = unit;
+	esp->es_server.ea_timeout = EAP_DEFTIMEOUT;
+	esp->es_server.ea_maxrequests = EAP_DEFTRANSMITS;
+	esp->es_server.ea_id = (u_char)(drand48() * 0x100);
+	esp->es_client.ea_timeout = EAP_DEFREQTIME;
+	esp->es_client.ea_maxrequests = EAP_DEFALLOWREQ;
+}
+
+/*
+ * eap_client_timeout - Give up waiting for the peer to send any
+ * Request messages.
+ */
+static void
+eap_client_timeout(arg)
+void *arg;
+{
+	eap_state *esp = (eap_state *) arg;
+
+	if (!eap_client_active(esp))
+		return;
+
+	error("EAP: timeout waiting for Request from peer");
+	auth_withpeer_fail(esp->es_unit, PPP_EAP);
+	esp->es_client.ea_state = eapBadAuth;
+}
+
+/*
+ * eap_authwithpeer - Authenticate to our peer (behave as client).
+ *
+ * Start client state and wait for requests.  This is called only
+ * after eap_lowerup.
+ */
+void
+eap_authwithpeer(unit, localname)
+int unit;
+char *localname;
+{
+	eap_state *esp = &eap_states[unit];
+
+	/* Save the peer name we're given */
+	esp->es_client.ea_name = localname;
+	esp->es_client.ea_namelen = strlen(localname);
+
+	esp->es_client.ea_state = eapListen;
+
+	/*
+	 * Start a timer so that if the other end just goes
+	 * silent, we don't sit here waiting forever.
+	 */
+	if (esp->es_client.ea_timeout > 0)
+		TIMEOUT(eap_client_timeout, (void *)esp,
+		    esp->es_client.ea_timeout);
+}
+
+/*
+ * Format a standard EAP Failure message and send it to the peer.
+ * (Server operation)
+ */
+static void
+eap_send_failure(esp)
+eap_state *esp;
+{
+	u_char *outp;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_FAILURE, outp);
+	esp->es_server.ea_id++;
+	PUTCHAR(esp->es_server.ea_id, outp);
+	PUTSHORT(EAP_HEADERLEN, outp);
+
+	output(esp->es_unit, outpacket_buf, EAP_HEADERLEN + PPP_HDRLEN);
+
+	esp->es_server.ea_state = eapBadAuth;
+	auth_peer_fail(esp->es_unit, PPP_EAP);
+}
+
+/*
+ * Format a standard EAP Success message and send it to the peer.
+ * (Server operation)
+ */
+static void
+eap_send_success(esp)
+eap_state *esp;
+{
+	u_char *outp;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_SUCCESS, outp);
+	esp->es_server.ea_id++;
+	PUTCHAR(esp->es_server.ea_id, outp);
+	PUTSHORT(EAP_HEADERLEN, outp);
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + EAP_HEADERLEN);
+
+	auth_peer_success(esp->es_unit, PPP_EAP, 0,
+	    esp->es_server.ea_peer, esp->es_server.ea_peerlen);
+}
+
+#ifdef USE_SRP
+/*
+ * Set DES key according to pseudonym-generating secret and current
+ * date.
+ */
+static bool
+pncrypt_setkey(int timeoffs)
+{
+	struct tm *tp;
+	char tbuf[9];
+	SHA1_CTX ctxt;
+	u_char dig[SHA_DIGESTSIZE];
+	time_t reftime;
+
+	if (pn_secret == NULL)
+		return (0);
+	reftime = time(NULL) + timeoffs;
+	tp = localtime(&reftime);
+	SHA1Init(&ctxt);
+	SHA1Update(&ctxt, pn_secret, strlen(pn_secret));
+	strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp);
+	SHA1Update(&ctxt, tbuf, strlen(tbuf));
+	SHA1Final(dig, &ctxt);
+	return (DesSetkey(dig));
+}
+
+static char base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+struct b64state {
+	u_int32_t bs_bits;
+	int bs_offs;
+};
+
+static int
+b64enc(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+	int outlen = 0;
+
+	while (inlen > 0) {
+		bs->bs_bits = (bs->bs_bits << 8) | *inp++;
+		inlen--;
+		bs->bs_offs += 8;
+		if (bs->bs_offs >= 24) {
+			*outp++ = base64[(bs->bs_bits >> 18) & 0x3F];
+			*outp++ = base64[(bs->bs_bits >> 12) & 0x3F];
+			*outp++ = base64[(bs->bs_bits >> 6) & 0x3F];
+			*outp++ = base64[bs->bs_bits & 0x3F];
+			outlen += 4;
+			bs->bs_offs = 0;
+			bs->bs_bits = 0;
+		}
+	}
+	return (outlen);
+}
+
+static int
+b64flush(bs, outp)
+struct b64state *bs;
+u_char *outp;
+{
+	int outlen = 0;
+
+	if (bs->bs_offs == 8) {
+		*outp++ = base64[(bs->bs_bits >> 2) & 0x3F];
+		*outp++ = base64[(bs->bs_bits << 4) & 0x3F];
+		outlen = 2;
+	} else if (bs->bs_offs == 16) {
+		*outp++ = base64[(bs->bs_bits >> 10) & 0x3F];
+		*outp++ = base64[(bs->bs_bits >> 4) & 0x3F];
+		*outp++ = base64[(bs->bs_bits << 2) & 0x3F];
+		outlen = 3;
+	}
+	bs->bs_offs = 0;
+	bs->bs_bits = 0;
+	return (outlen);
+}
+
+static int
+b64dec(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+	int outlen = 0;
+	char *cp;
+
+	while (inlen > 0) {
+		if ((cp = strchr(base64, *inp++)) == NULL)
+			break;
+		bs->bs_bits = (bs->bs_bits << 6) | (cp - base64);
+		inlen--;
+		bs->bs_offs += 6;
+		if (bs->bs_offs >= 8) {
+			*outp++ = bs->bs_bits >> (bs->bs_offs - 8);
+			outlen++;
+			bs->bs_offs -= 8;
+		}
+	}
+	return (outlen);
+}
+#endif /* USE_SRP */
+
+/*
+ * Assume that current waiting server state is complete and figure
+ * next state to use based on available authentication data.  'status'
+ * indicates if there was an error in handling the last query.  It is
+ * 0 for success and non-zero for failure.
+ */
+static void
+eap_figure_next_state(esp, status)
+eap_state *esp;
+int status;
+{
+#ifdef USE_SRP
+	unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp;
+	struct t_pw tpw;
+	struct t_confent *tce, mytce;
+	char *cp, *cp2;
+	struct t_server *ts;
+	int id, i, plen, toffs;
+	u_char vals[2];
+	struct b64state bs;
+#endif /* USE_SRP */
+
+	esp->es_server.ea_timeout = esp->es_savedtime;
+	switch (esp->es_server.ea_state) {
+	case eapBadAuth:
+		return;
+
+	case eapIdentify:
+#ifdef USE_SRP
+		/* Discard any previous session. */
+		ts = (struct t_server *)esp->es_server.ea_session;
+		if (ts != NULL) {
+			t_serverclose(ts);
+			esp->es_server.ea_session = NULL;
+			esp->es_server.ea_skey = NULL;
+		}
+#endif /* USE_SRP */
+		if (status != 0) {
+			esp->es_server.ea_state = eapBadAuth;
+			break;
+		}
+#ifdef USE_SRP
+		/* If we've got a pseudonym, try to decode to real name. */
+		if (esp->es_server.ea_peerlen > SRP_PSEUDO_LEN &&
+		    strncmp(esp->es_server.ea_peer, SRP_PSEUDO_ID,
+			SRP_PSEUDO_LEN) == 0 &&
+		    (esp->es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 <
+		    sizeof (secbuf)) {
+			BZERO(&bs, sizeof (bs));
+			plen = b64dec(&bs,
+			    esp->es_server.ea_peer + SRP_PSEUDO_LEN,
+			    esp->es_server.ea_peerlen - SRP_PSEUDO_LEN,
+			    secbuf);
+			toffs = 0;
+			for (i = 0; i < 5; i++) {
+				pncrypt_setkey(toffs);
+				toffs -= 86400;
+				if (!DesDecrypt(secbuf, clear)) {
+					warn("no DES here; cannot decode "
+					    "pseudonym");
+					return;
+				}
+				id = *(unsigned char *)clear;
+				if (id + 1 <= plen && id + 9 > plen)
+					break;
+			}
+			if (plen % 8 == 0 && i < 5) {
+				/*
+				 * Note that this is always shorter than the
+				 * original stored string, so there's no need
+				 * to realloc.
+				 */
+				if ((i = plen = *(unsigned char *)clear) > 7)
+					i = 7;
+				esp->es_server.ea_peerlen = plen;
+				dp = (unsigned char *)esp->es_server.ea_peer;
+				BCOPY(clear + 1, dp, i);
+				plen -= i;
+				dp += i;
+				sp = secbuf + 8;
+				while (plen > 0) {
+					(void) DesDecrypt(sp, dp);
+					sp += 8;
+					dp += 8;
+					plen -= 8;
+				}
+				esp->es_server.ea_peer[
+					esp->es_server.ea_peerlen] = '\0';
+				warn("decoded pseudonym to \"%.*q\"",
+				    esp->es_server.ea_peerlen,
+				    esp->es_server.ea_peer);
+			} else {
+				warn("failed to decode real name");
+				/* Stay in eapIdentfy state; requery */
+				break;
+			}
+		}
+		/* Look up user in secrets database. */
+		if (get_srp_secret(esp->es_unit, esp->es_server.ea_peer,
+		    esp->es_server.ea_name, (char *)secbuf, 1) != 0) {
+			/* Set up default in case SRP entry is bad */
+			esp->es_server.ea_state = eapMD5Chall;
+			/* Get t_confent based on index in srp-secrets */
+			id = strtol((char *)secbuf, &cp, 10);
+			if (*cp++ != ':' || id < 0)
+				break;
+			if (id == 0) {
+				mytce.index = 0;
+				mytce.modulus.data = (u_char *)wkmodulus;
+				mytce.modulus.len = sizeof (wkmodulus);
+				mytce.generator.data = (u_char *)"\002";
+				mytce.generator.len = 1;
+				tce = &mytce;
+			} else if ((tce = gettcid(id)) != NULL) {
+				/*
+				 * Client will have to verify this modulus/
+				 * generator combination, and that will take
+				 * a while.  Lengthen the timeout here.
+				 */
+				if (esp->es_server.ea_timeout > 0 &&
+				    esp->es_server.ea_timeout < 30)
+					esp->es_server.ea_timeout = 30;
+			} else {
+				break;
+			}
+			if ((cp2 = strchr(cp, ':')) == NULL)
+				break;
+			*cp2++ = '\0';
+			tpw.pebuf.name = esp->es_server.ea_peer;
+			tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf,
+			    cp);
+			tpw.pebuf.password.data = tpw.pwbuf;
+			tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf,
+			    cp2);
+			tpw.pebuf.salt.data = tpw.saltbuf;
+			if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL)
+				break;
+			esp->es_server.ea_session = (void *)ts;
+			esp->es_server.ea_state = eapSRP1;
+			vals[0] = esp->es_server.ea_id + 1;
+			vals[1] = EAPT_SRP;
+			t_serveraddexdata(ts, vals, 2);
+			/* Generate B; must call before t_servergetkey() */
+			t_servergenexp(ts);
+			break;
+		}
+#endif /* USE_SRP */
+		esp->es_server.ea_state = eapMD5Chall;
+		break;
+
+	case eapSRP1:
+#ifdef USE_SRP
+		ts = (struct t_server *)esp->es_server.ea_session;
+		if (ts != NULL && status != 0) {
+			t_serverclose(ts);
+			esp->es_server.ea_session = NULL;
+			esp->es_server.ea_skey = NULL;
+		}
+#endif /* USE_SRP */
+		if (status == 1) {
+			esp->es_server.ea_state = eapMD5Chall;
+		} else if (status != 0 || esp->es_server.ea_session == NULL) {
+			esp->es_server.ea_state = eapBadAuth;
+		} else {
+			esp->es_server.ea_state = eapSRP2;
+		}
+		break;
+
+	case eapSRP2:
+#ifdef USE_SRP
+		ts = (struct t_server *)esp->es_server.ea_session;
+		if (ts != NULL && status != 0) {
+			t_serverclose(ts);
+			esp->es_server.ea_session = NULL;
+			esp->es_server.ea_skey = NULL;
+		}
+#endif /* USE_SRP */
+		if (status != 0 || esp->es_server.ea_session == NULL) {
+			esp->es_server.ea_state = eapBadAuth;
+		} else {
+			esp->es_server.ea_state = eapSRP3;
+		}
+		break;
+
+	case eapSRP3:
+	case eapSRP4:
+#ifdef USE_SRP
+		ts = (struct t_server *)esp->es_server.ea_session;
+		if (ts != NULL && status != 0) {
+			t_serverclose(ts);
+			esp->es_server.ea_session = NULL;
+			esp->es_server.ea_skey = NULL;
+		}
+#endif /* USE_SRP */
+		if (status != 0 || esp->es_server.ea_session == NULL) {
+			esp->es_server.ea_state = eapBadAuth;
+		} else {
+			esp->es_server.ea_state = eapOpen;
+		}
+		break;
+
+	case eapMD5Chall:
+		if (status != 0) {
+			esp->es_server.ea_state = eapBadAuth;
+		} else {
+			esp->es_server.ea_state = eapOpen;
+		}
+		break;
+
+	default:
+		esp->es_server.ea_state = eapBadAuth;
+		break;
+	}
+	if (esp->es_server.ea_state == eapBadAuth)
+		eap_send_failure(esp);
+}
+
+/*
+ * Format an EAP Request message and send it to the peer.  Message
+ * type depends on current state.  (Server operation)
+ */
+static void
+eap_send_request(esp)
+eap_state *esp;
+{
+	u_char *outp;
+	u_char *lenloc;
+	u_char *ptr;
+	int outlen;
+	int challen;
+	char *str;
+#ifdef USE_SRP
+	struct t_server *ts;
+	u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp;
+	int i, j;
+	struct b64state b64;
+	SHA1_CTX ctxt;
+#endif /* USE_SRP */
+
+	/* Handle both initial auth and restart */
+	if (esp->es_server.ea_state < eapIdentify &&
+	    esp->es_server.ea_state != eapInitial) {
+		esp->es_server.ea_state = eapIdentify;
+		if (explicit_remote) {
+			/*
+			 * If we already know the peer's
+			 * unauthenticated name, then there's no
+			 * reason to ask.  Go to next state instead.
+			 */
+			esp->es_server.ea_peer = remote_name;
+			esp->es_server.ea_peerlen = strlen(remote_name);
+			eap_figure_next_state(esp, 0);
+		}
+	}
+
+	if (esp->es_server.ea_maxrequests > 0 &&
+	    esp->es_server.ea_requests >= esp->es_server.ea_maxrequests) {
+		if (esp->es_server.ea_responses > 0)
+			error("EAP: too many Requests sent");
+		else
+			error("EAP: no response to Requests");
+		eap_send_failure(esp);
+		return;
+	}
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_REQUEST, outp);
+	PUTCHAR(esp->es_server.ea_id, outp);
+	lenloc = outp;
+	INCPTR(2, outp);
+
+	switch (esp->es_server.ea_state) {
+	case eapIdentify:
+		PUTCHAR(EAPT_IDENTITY, outp);
+		str = "Name";
+		challen = strlen(str);
+		BCOPY(str, outp, challen);
+		INCPTR(challen, outp);
+		break;
+
+	case eapMD5Chall:
+		PUTCHAR(EAPT_MD5CHAP, outp);
+		/*
+		 * pick a random challenge length between
+		 * MIN_CHALLENGE_LENGTH and MAX_CHALLENGE_LENGTH
+		 */
+		challen = (drand48() *
+		    (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+			    MIN_CHALLENGE_LENGTH;
+		PUTCHAR(challen, outp);
+		esp->es_challen = challen;
+		ptr = esp->es_challenge;
+		while (--challen >= 0)
+			*ptr++ = (u_char) (drand48() * 0x100);
+		BCOPY(esp->es_challenge, outp, esp->es_challen);
+		INCPTR(esp->es_challen, outp);
+		BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen);
+		INCPTR(esp->es_server.ea_namelen, outp);
+		break;
+
+#ifdef USE_SRP
+	case eapSRP1:
+		PUTCHAR(EAPT_SRP, outp);
+		PUTCHAR(EAPSRP_CHALLENGE, outp);
+
+		PUTCHAR(esp->es_server.ea_namelen, outp);
+		BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen);
+		INCPTR(esp->es_server.ea_namelen, outp);
+
+		ts = (struct t_server *)esp->es_server.ea_session;
+		assert(ts != NULL);
+		PUTCHAR(ts->s.len, outp);
+		BCOPY(ts->s.data, outp, ts->s.len);
+		INCPTR(ts->s.len, outp);
+
+		if (ts->g.len == 1 && ts->g.data[0] == 2) {
+			PUTCHAR(0, outp);
+		} else {
+			PUTCHAR(ts->g.len, outp);
+			BCOPY(ts->g.data, outp, ts->g.len);
+			INCPTR(ts->g.len, outp);
+		}
+
+		if (ts->n.len != sizeof (wkmodulus) ||
+		    BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) {
+			BCOPY(ts->n.data, outp, ts->n.len);
+			INCPTR(ts->n.len, outp);
+		}
+		break;
+
+	case eapSRP2:
+		PUTCHAR(EAPT_SRP, outp);
+		PUTCHAR(EAPSRP_SKEY, outp);
+
+		ts = (struct t_server *)esp->es_server.ea_session;
+		assert(ts != NULL);
+		BCOPY(ts->B.data, outp, ts->B.len);
+		INCPTR(ts->B.len, outp);
+		break;
+
+	case eapSRP3:
+		PUTCHAR(EAPT_SRP, outp);
+		PUTCHAR(EAPSRP_SVALIDATOR, outp);
+		PUTLONG(SRPVAL_EBIT, outp);
+		ts = (struct t_server *)esp->es_server.ea_session;
+		assert(ts != NULL);
+		BCOPY(t_serverresponse(ts), outp, SHA_DIGESTSIZE);
+		INCPTR(SHA_DIGESTSIZE, outp);
+
+		if (pncrypt_setkey(0)) {
+			/* Generate pseudonym */
+			optr = outp;
+			cp = (unsigned char *)esp->es_server.ea_peer;
+			if ((j = i = esp->es_server.ea_peerlen) > 7)
+				j = 7;
+			clear[0] = i;
+			BCOPY(cp, clear + 1, j);
+			i -= j;
+			cp += j;
+			if (!DesEncrypt(clear, cipher)) {
+				warn("no DES here; not generating pseudonym");
+				break;
+			}
+			BZERO(&b64, sizeof (b64));
+			outp++;		/* space for pseudonym length */
+			outp += b64enc(&b64, cipher, 8, outp);
+			while (i >= 8) {
+				(void) DesEncrypt(cp, cipher);
+				outp += b64enc(&b64, cipher, 8, outp);
+				cp += 8;
+				i -= 8;
+			}
+			if (i > 0) {
+				BCOPY(cp, clear, i);
+				cp += i;
+				while (i < 8) {
+					*cp++ = drand48() * 0x100;
+					i++;
+				}
+				(void) DesEncrypt(clear, cipher);
+				outp += b64enc(&b64, cipher, 8, outp);
+			}
+			outp += b64flush(&b64, outp);
+
+			/* Set length and pad out to next 20 octet boundary */
+			i = outp - optr - 1;
+			*optr = i;
+			i %= SHA_DIGESTSIZE;
+			if (i != 0) {
+				while (i < SHA_DIGESTSIZE) {
+					*outp++ = drand48() * 0x100;
+					i++;
+				}
+			}
+
+			/* Obscure the pseudonym with SHA1 hash */
+			SHA1Init(&ctxt);
+			SHA1Update(&ctxt, &esp->es_server.ea_id, 1);
+			SHA1Update(&ctxt, esp->es_server.ea_skey,
+			    SESSION_KEY_LEN);
+			SHA1Update(&ctxt, esp->es_server.ea_peer,
+			    esp->es_server.ea_peerlen);
+			while (optr < outp) {
+				SHA1Final(dig, &ctxt);
+				cp = dig;
+				while (cp < dig + SHA_DIGESTSIZE)
+					*optr++ ^= *cp++;
+				SHA1Init(&ctxt);
+				SHA1Update(&ctxt, &esp->es_server.ea_id, 1);
+				SHA1Update(&ctxt, esp->es_server.ea_skey,
+				    SESSION_KEY_LEN);
+				SHA1Update(&ctxt, optr - SHA_DIGESTSIZE,
+				    SHA_DIGESTSIZE);
+			}
+		}
+		break;
+
+	case eapSRP4:
+		PUTCHAR(EAPT_SRP, outp);
+		PUTCHAR(EAPSRP_LWRECHALLENGE, outp);
+		challen = MIN_CHALLENGE_LENGTH +
+		    ((MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH) * drand48());
+		esp->es_challen = challen;
+		ptr = esp->es_challenge;
+		while (--challen >= 0)
+			*ptr++ = drand48() * 0x100;
+		BCOPY(esp->es_challenge, outp, esp->es_challen);
+		INCPTR(esp->es_challen, outp);
+		break;
+#endif /* USE_SRP */
+
+	default:
+		return;
+	}
+
+	outlen = (outp - outpacket_buf) - PPP_HDRLEN;
+	PUTSHORT(outlen, lenloc);
+
+	output(esp->es_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+	esp->es_server.ea_requests++;
+
+	if (esp->es_server.ea_timeout > 0)
+		TIMEOUT(eap_server_timeout, esp, esp->es_server.ea_timeout);
+}
+
+/*
+ * eap_authpeer - Authenticate our peer (behave as server).
+ *
+ * Start server state and send first request.  This is called only
+ * after eap_lowerup.
+ */
+void
+eap_authpeer(unit, localname)
+int unit;
+char *localname;
+{
+	eap_state *esp = &eap_states[unit];
+
+	/* Save the name we're given. */
+	esp->es_server.ea_name = localname;
+	esp->es_server.ea_namelen = strlen(localname);
+
+	esp->es_savedtime = esp->es_server.ea_timeout;
+
+	/* Lower layer up yet? */
+	if (esp->es_server.ea_state == eapInitial ||
+	    esp->es_server.ea_state == eapPending) {
+		esp->es_server.ea_state = eapPending;
+		return;
+	}
+
+	esp->es_server.ea_state = eapPending;
+
+	/* ID number not updated here intentionally; hashed into M1 */
+	eap_send_request(esp);
+}
+
+/*
+ * eap_server_timeout - Retransmission timer for sending Requests
+ * expired.
+ */
+static void
+eap_server_timeout(arg)
+void *arg;
+{
+	eap_state *esp = (eap_state *) arg;
+
+	if (!eap_server_active(esp))
+		return;
+
+	/* EAP ID number must not change on timeout. */
+	eap_send_request(esp);
+}
+
+/*
+ * When it's time to send rechallenge the peer, this timeout is
+ * called.  Once the rechallenge is successful, the response handler
+ * will restart the timer.  If it fails, then the link is dropped.
+ */
+static void
+eap_rechallenge(arg)
+void *arg;
+{
+	eap_state *esp = (eap_state *)arg;
+
+	if (esp->es_server.ea_state != eapOpen &&
+	    esp->es_server.ea_state != eapSRP4)
+		return;
+
+	esp->es_server.ea_requests = 0;
+	esp->es_server.ea_state = eapIdentify;
+	eap_figure_next_state(esp, 0);
+	esp->es_server.ea_id++;
+	eap_send_request(esp);
+}
+
+static void
+srp_lwrechallenge(arg)
+void *arg;
+{
+	eap_state *esp = (eap_state *)arg;
+
+	if (esp->es_server.ea_state != eapOpen ||
+	    esp->es_server.ea_type != EAPT_SRP)
+		return;
+
+	esp->es_server.ea_requests = 0;
+	esp->es_server.ea_state = eapSRP4;
+	esp->es_server.ea_id++;
+	eap_send_request(esp);
+}
+
+/*
+ * eap_lowerup - The lower layer is now up.
+ *
+ * This is called before either eap_authpeer or eap_authwithpeer.  See
+ * link_established() in auth.c.  All that's necessary here is to
+ * return to closed state so that those two routines will do the right
+ * thing.
+ */
+static void
+eap_lowerup(unit)
+int unit;
+{
+	eap_state *esp = &eap_states[unit];
+
+	/* Discard any (possibly authenticated) peer name. */
+	if (esp->es_server.ea_peer != NULL &&
+	    esp->es_server.ea_peer != remote_name)
+		free(esp->es_server.ea_peer);
+	esp->es_server.ea_peer = NULL;
+	if (esp->es_client.ea_peer != NULL)
+		free(esp->es_client.ea_peer);
+	esp->es_client.ea_peer = NULL;
+
+	esp->es_client.ea_state = eapClosed;
+	esp->es_server.ea_state = eapClosed;
+}
+
+/*
+ * eap_lowerdown - The lower layer is now down.
+ *
+ * Cancel all timeouts and return to initial state.
+ */
+static void
+eap_lowerdown(unit)
+int unit;
+{
+	eap_state *esp = &eap_states[unit];
+
+	if (eap_client_active(esp) && esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+	}
+	if (eap_server_active(esp)) {
+		if (esp->es_server.ea_timeout > 0) {
+			UNTIMEOUT(eap_server_timeout, (void *)esp);
+		}
+	} else {
+		if ((esp->es_server.ea_state == eapOpen ||
+		    esp->es_server.ea_state == eapSRP4) &&
+		    esp->es_rechallenge > 0) {
+			UNTIMEOUT(eap_rechallenge, (void *)esp);
+		}
+		if (esp->es_server.ea_state == eapOpen &&
+		    esp->es_lwrechallenge > 0) {
+			UNTIMEOUT(srp_lwrechallenge, (void *)esp);
+		}
+	}
+
+	esp->es_client.ea_state = esp->es_server.ea_state = eapInitial;
+	esp->es_client.ea_requests = esp->es_server.ea_requests = 0;
+}
+
+/*
+ * eap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen.  If it does, it represents authentication
+ * failure.
+ */
+static void
+eap_protrej(unit)
+int unit;
+{
+	eap_state *esp = &eap_states[unit];
+
+	if (eap_client_active(esp)) {
+		error("EAP authentication failed due to Protocol-Reject");
+		auth_withpeer_fail(unit, PPP_EAP);
+	}
+	if (eap_server_active(esp)) {
+		error("EAP authentication of peer failed on Protocol-Reject");
+		auth_peer_fail(unit, PPP_EAP);
+	}
+	eap_lowerdown(unit);
+}
+
+/*
+ * Format and send a regular EAP Response message.
+ */
+static void
+eap_send_response(esp, id, typenum, str, lenstr)
+eap_state *esp;
+u_char id;
+u_char typenum;
+u_char *str;
+int lenstr;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + sizeof (u_char) + lenstr;
+	PUTSHORT(msglen, outp);
+	PUTCHAR(typenum, outp);
+	if (lenstr > 0) {
+		BCOPY(str, outp, lenstr);
+	}
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+/*
+ * Format and send an MD5-Challenge EAP Response message.
+ */
+static void
+eap_chap_response(esp, id, hash, name, namelen)
+eap_state *esp;
+u_char id;
+u_char *hash;
+char *name;
+int namelen;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE +
+	    namelen;
+	PUTSHORT(msglen, outp);
+	PUTCHAR(EAPT_MD5CHAP, outp);
+	PUTCHAR(MD5_SIGNATURE_SIZE, outp);
+	BCOPY(hash, outp, MD5_SIGNATURE_SIZE);
+	INCPTR(MD5_SIGNATURE_SIZE, outp);
+	if (namelen > 0) {
+		BCOPY(name, outp, namelen);
+	}
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+#ifdef USE_SRP
+/*
+ * Format and send a SRP EAP Response message.
+ */
+static void
+eap_srp_response(esp, id, subtypenum, str, lenstr)
+eap_state *esp;
+u_char id;
+u_char subtypenum;
+u_char *str;
+int lenstr;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + lenstr;
+	PUTSHORT(msglen, outp);
+	PUTCHAR(EAPT_SRP, outp);
+	PUTCHAR(subtypenum, outp);
+	if (lenstr > 0) {
+		BCOPY(str, outp, lenstr);
+	}
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+/*
+ * Format and send a SRP EAP Client Validator Response message.
+ */
+static void
+eap_srpval_response(esp, id, flags, str)
+eap_state *esp;
+u_char id;
+u_int32_t flags;
+u_char *str;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u_int32_t) +
+	    SHA_DIGESTSIZE;
+	PUTSHORT(msglen, outp);
+	PUTCHAR(EAPT_SRP, outp);
+	PUTCHAR(EAPSRP_CVALIDATOR, outp);
+	PUTLONG(flags, outp);
+	BCOPY(str, outp, SHA_DIGESTSIZE);
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+#endif /* USE_SRP */
+
+static void
+eap_send_nak(esp, id, type)
+eap_state *esp;
+u_char id;
+u_char type;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + 2 * sizeof (u_char);
+	PUTSHORT(msglen, outp);
+	PUTCHAR(EAPT_NAK, outp);
+	PUTCHAR(type, outp);
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+#ifdef USE_SRP
+static char *
+name_of_pn_file()
+{
+	char *user, *path, *file;
+	struct passwd *pw;
+	size_t pl;
+	static bool pnlogged = 0;
+
+	pw = getpwuid(getuid());
+	if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) {
+		errno = EINVAL;
+		return (NULL);
+	}
+	file = _PATH_PSEUDONYM;
+	pl = strlen(user) + strlen(file) + 2;
+	path = malloc(pl);
+	if (path == NULL)
+		return (NULL);
+	(void) slprintf(path, pl, "%s/%s", user, file);
+	if (!pnlogged) {
+		warn("pseudonym file: %s", path);
+		pnlogged = 1;
+	}
+	return (path);
+}
+
+static int
+open_pn_file(modebits)
+mode_t modebits;
+{
+	char *path;
+	int fd, err;
+
+	if ((path = name_of_pn_file()) == NULL)
+		return (-1);
+	fd = open(path, modebits, S_IRUSR | S_IWUSR);
+	err = errno;
+	free(path);
+	errno = err;
+	return (fd);
+}
+
+static void
+remove_pn_file()
+{
+	char *path;
+
+	if ((path = name_of_pn_file()) != NULL) {
+		(void) unlink(path);
+		(void) free(path);
+	}
+}
+
+static void
+write_pseudonym(esp, inp, len, id)
+eap_state *esp;
+u_char *inp;
+int len, id;
+{
+	u_char val;
+	u_char *datp, *digp;
+	SHA1_CTX ctxt;
+	u_char dig[SHA_DIGESTSIZE];
+	int dsize, fd, olen = len;
+
+	/*
+	 * Do the decoding by working backwards.  This eliminates the need
+	 * to save the decoded output in a separate buffer.
+	 */
+	val = id;
+	while (len > 0) {
+		if ((dsize = len % SHA_DIGESTSIZE) == 0)
+			dsize = SHA_DIGESTSIZE;
+		len -= dsize;
+		datp = inp + len;
+		SHA1Init(&ctxt);
+		SHA1Update(&ctxt, &val, 1);
+		SHA1Update(&ctxt, esp->es_client.ea_skey, SESSION_KEY_LEN);
+		if (len > 0) {
+			SHA1Update(&ctxt, datp, SHA_DIGESTSIZE);
+		} else {
+			SHA1Update(&ctxt, esp->es_client.ea_name,
+			    esp->es_client.ea_namelen);
+		}
+		SHA1Final(dig, &ctxt);
+		for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++)
+			*datp++ ^= *digp;
+	}
+
+	/* Now check that the result is sane */
+	if (olen <= 0 || *inp + 1 > olen) {
+		warn("EAP: decoded pseudonym is unusable <%.*B>", olen, inp);
+		return;
+	}
+
+	/* Save it away */
+	fd = open_pn_file(O_WRONLY | O_CREAT | O_TRUNC);
+	if (fd < 0) {
+		warn("EAP: error saving pseudonym: %m");
+		return;
+	}
+	len = write(fd, inp + 1, *inp);
+	if (close(fd) != -1 && len == *inp) {
+		warn("EAP: saved pseudonym");
+		esp->es_usedpseudo = 0;
+	} else {
+		warn("EAP: failed to save pseudonym");
+		remove_pn_file();
+	}
+}
+#endif /* USE_SRP */
+
+/*
+ * eap_request - Receive EAP Request message (client mode).
+ */
+static void
+eap_request(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+	u_char typenum;
+	u_char vallen;
+	int secret_len;
+	char secret[MAXWORDLEN];
+	char rhostname[256];
+	MD5_CTX mdContext;
+	u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+	struct t_client *tc;
+	struct t_num sval, gval, Nval, *Ap, Bval;
+	u_char vals[2];
+	SHA1_CTX ctxt;
+	u_char dig[SHA_DIGESTSIZE];
+	int fd;
+#endif /* USE_SRP */
+
+	/*
+	 * Note: we update es_client.ea_id *only if* a Response
+	 * message is being generated.  Otherwise, we leave it the
+	 * same for duplicate detection purposes.
+	 */
+
+	esp->es_client.ea_requests++;
+	if (esp->es_client.ea_maxrequests != 0 &&
+	    esp->es_client.ea_requests > esp->es_client.ea_maxrequests) {
+		warn("EAP: received too many Request messages");
+		if (esp->es_client.ea_timeout > 0) {
+			UNTIMEOUT(eap_client_timeout, (void *)esp);
+		}
+		auth_withpeer_fail(esp->es_unit, PPP_EAP);
+		return;
+	}
+
+	if (len <= 0) {
+		error("EAP: empty Request message discarded");
+		return;
+	}
+
+	GETCHAR(typenum, inp);
+	len--;
+
+	switch (typenum) {
+	case EAPT_IDENTITY:
+		if (len > 0)
+			warn("EAP: Identity prompt \"%.*q\"", len, inp);
+#ifdef USE_SRP
+		if (esp->es_usepseudo &&
+		    (esp->es_usedpseudo == 0 ||
+			(esp->es_usedpseudo == 1 &&
+			    id == esp->es_client.ea_id))) {
+			esp->es_usedpseudo = 1;
+			/* Try to get a pseudonym */
+			if ((fd = open_pn_file(O_RDONLY)) >= 0) {
+				strcpy(rhostname, SRP_PSEUDO_ID);
+				len = read(fd, rhostname + SRP_PSEUDO_LEN,
+				    sizeof (rhostname) - SRP_PSEUDO_LEN);
+				/* XXX NAI unsupported */
+				if (len > 0) {
+					eap_send_response(esp, id, typenum,
+					    rhostname, len + SRP_PSEUDO_LEN);
+				}
+				(void) close(fd);
+				if (len > 0)
+					break;
+			}
+		}
+		/* Stop using pseudonym now. */
+		if (esp->es_usepseudo && esp->es_usedpseudo != 2) {
+			remove_pn_file();
+			esp->es_usedpseudo = 2;
+		}
+#endif /* USE_SRP */
+		eap_send_response(esp, id, typenum, esp->es_client.ea_name,
+		    esp->es_client.ea_namelen);
+		break;
+
+	case EAPT_NOTIFICATION:
+		if (len > 0)
+			warn("EAP: Notification \"%.*q\"", len, inp);
+		eap_send_response(esp, id, typenum, NULL, 0);
+		break;
+
+	case EAPT_NAK:
+		/*
+		 * Avoid the temptation to send Response Nak in reply
+		 * to Request Nak here.  It can only lead to trouble.
+		 */
+		warn("EAP: unexpected Nak in Request; ignored");
+		/* Return because we're waiting for something real. */
+		return;
+
+	case EAPT_MD5CHAP:
+		if (len < 1) {
+			error("EAP: received MD5-Challenge with no data");
+			/* Bogus request; wait for something real. */
+			return;
+		}
+		GETCHAR(vallen, inp);
+		len--;
+		if (vallen < 8 || vallen > len) {
+			error("EAP: MD5-Challenge with bad length %d (8..%d)",
+			    vallen, len);
+			/* Try something better. */
+			eap_send_nak(esp, id, EAPT_SRP);
+			break;
+		}
+
+		/* Not so likely to happen. */
+		if (len - vallen >= sizeof (rhostname)) {
+			warn("EAP: trimming really long peer name down");
+			BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1);
+			rhostname[sizeof (rhostname) - 1] = '\0';
+		} else {
+			BCOPY(inp + vallen, rhostname, len - vallen);
+			rhostname[len - vallen] = '\0';
+		}
+
+		/* In case the remote doesn't give us his name. */
+		if (explicit_remote ||
+		    (remote_name[0] != '\0' && vallen == len))
+			strlcpy(rhostname, remote_name, sizeof (rhostname));
+
+		/*
+		 * Get the secret for authenticating ourselves with
+		 * the specified host.
+		 */
+		if (!get_secret(esp->es_unit, esp->es_client.ea_name,
+		    rhostname, secret, &secret_len, 0)) {
+			warn("EAP: no MD5 secret for auth to %q", rhostname);
+			eap_send_nak(esp, id, EAPT_SRP);
+			break;
+		}
+		MD5_Init(&mdContext);
+		typenum = id;
+		MD5_Update(&mdContext, &typenum, 1);
+		MD5_Update(&mdContext, (u_char *)secret, secret_len);
+		BZERO(secret, sizeof (secret));
+		MD5_Update(&mdContext, inp, vallen);
+		MD5_Final(hash, &mdContext);
+		eap_chap_response(esp, id, hash, esp->es_client.ea_name,
+		    esp->es_client.ea_namelen);
+		break;
+
+#ifdef USE_SRP
+	case EAPT_SRP:
+		if (len < 1) {
+			error("EAP: received empty SRP Request");
+			/* Bogus request; wait for something real. */
+			return;
+		}
+
+		/* Get subtype */
+		GETCHAR(vallen, inp);
+		len--;
+		switch (vallen) {
+		case EAPSRP_CHALLENGE:
+			tc = NULL;
+			if (esp->es_client.ea_session != NULL) {
+				tc = (struct t_client *)esp->es_client.
+				    ea_session;
+				/*
+				 * If this is a new challenge, then start
+				 * over with a new client session context.
+				 * Otherwise, just resend last response.
+				 */
+				if (id != esp->es_client.ea_id) {
+					t_clientclose(tc);
+					esp->es_client.ea_session = NULL;
+					tc = NULL;
+				}
+			}
+			/* No session key just yet */
+			esp->es_client.ea_skey = NULL;
+			if (tc == NULL) {
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen >= len) {
+					error("EAP: badly-formed SRP Challenge"
+					    " (name)");
+					/* Ignore badly-formed messages */
+					return;
+				}
+				BCOPY(inp, rhostname, vallen);
+				rhostname[vallen] = '\0';
+				INCPTR(vallen, inp);
+				len -= vallen;
+
+				/*
+				 * In case the remote doesn't give us his name,
+				 * use configured name.
+				 */
+				if (explicit_remote ||
+				    (remote_name[0] != '\0' && vallen == 0)) {
+					strlcpy(rhostname, remote_name,
+					    sizeof (rhostname));
+				}
+
+				if (esp->es_client.ea_peer != NULL)
+					free(esp->es_client.ea_peer);
+				esp->es_client.ea_peer = strdup(rhostname);
+				esp->es_client.ea_peerlen = strlen(rhostname);
+
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen >= len) {
+					error("EAP: badly-formed SRP Challenge"
+					    " (s)");
+					/* Ignore badly-formed messages */
+					return;
+				}
+				sval.data = inp;
+				sval.len = vallen;
+				INCPTR(vallen, inp);
+				len -= vallen;
+
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen > len) {
+					error("EAP: badly-formed SRP Challenge"
+					    " (g)");
+					/* Ignore badly-formed messages */
+					return;
+				}
+				/* If no generator present, then use value 2 */
+				if (vallen == 0) {
+					gval.data = (u_char *)"\002";
+					gval.len = 1;
+				} else {
+					gval.data = inp;
+					gval.len = vallen;
+				}
+				INCPTR(vallen, inp);
+				len -= vallen;
+
+				/*
+				 * If no modulus present, then use well-known
+				 * value.
+				 */
+				if (len == 0) {
+					Nval.data = (u_char *)wkmodulus;
+					Nval.len = sizeof (wkmodulus);
+				} else {
+					Nval.data = inp;
+					Nval.len = len;
+				}
+				tc = t_clientopen(esp->es_client.ea_name,
+				    &Nval, &gval, &sval);
+				if (tc == NULL) {
+					eap_send_nak(esp, id, EAPT_MD5CHAP);
+					break;
+				}
+				esp->es_client.ea_session = (void *)tc;
+
+				/* Add Challenge ID & type to verifier */
+				vals[0] = id;
+				vals[1] = EAPT_SRP;
+				t_clientaddexdata(tc, vals, 2);
+			}
+			Ap = t_clientgenexp(tc);
+			eap_srp_response(esp, id, EAPSRP_CKEY, Ap->data,
+			    Ap->len);
+			break;
+
+		case EAPSRP_SKEY:
+			tc = (struct t_client *)esp->es_client.ea_session;
+			if (tc == NULL) {
+				warn("EAP: peer sent Subtype 2 without 1");
+				eap_send_nak(esp, id, EAPT_MD5CHAP);
+				break;
+			}
+			if (esp->es_client.ea_skey != NULL) {
+				/*
+				 * ID number should not change here.  Warn
+				 * if it does (but otherwise ignore).
+				 */
+				if (id != esp->es_client.ea_id) {
+					warn("EAP: ID changed from %d to %d "
+					    "in SRP Subtype 2 rexmit",
+					    esp->es_client.ea_id, id);
+				}
+			} else {
+				if (get_srp_secret(esp->es_unit,
+				    esp->es_client.ea_name,
+				    esp->es_client.ea_peer, secret, 0) == 0) {
+					/*
+					 * Can't work with this peer because
+					 * the secret is missing.  Just give
+					 * up.
+					 */
+					eap_send_nak(esp, id, EAPT_MD5CHAP);
+					break;
+				}
+				Bval.data = inp;
+				Bval.len = len;
+				t_clientpasswd(tc, secret);
+				BZERO(secret, sizeof (secret));
+				esp->es_client.ea_skey =
+				    t_clientgetkey(tc, &Bval);
+				if (esp->es_client.ea_skey == NULL) {
+					/* Server is rogue; stop now */
+					error("EAP: SRP server is rogue");
+					goto client_failure;
+				}
+			}
+			eap_srpval_response(esp, id, SRPVAL_EBIT,
+			    t_clientresponse(tc));
+			break;
+
+		case EAPSRP_SVALIDATOR:
+			tc = (struct t_client *)esp->es_client.ea_session;
+			if (tc == NULL || esp->es_client.ea_skey == NULL) {
+				warn("EAP: peer sent Subtype 3 without 1/2");
+				eap_send_nak(esp, id, EAPT_MD5CHAP);
+				break;
+			}
+			/*
+			 * If we're already open, then this ought to be a
+			 * duplicate.  Otherwise, check that the server is
+			 * who we think it is.
+			 */
+			if (esp->es_client.ea_state == eapOpen) {
+				if (id != esp->es_client.ea_id) {
+					warn("EAP: ID changed from %d to %d "
+					    "in SRP Subtype 3 rexmit",
+					    esp->es_client.ea_id, id);
+				}
+			} else {
+				len -= sizeof (u_int32_t) + SHA_DIGESTSIZE;
+				if (len < 0 || t_clientverify(tc, inp +
+					sizeof (u_int32_t)) != 0) {
+					error("EAP: SRP server verification "
+					    "failed");
+					goto client_failure;
+				}
+				GETLONG(esp->es_client.ea_keyflags, inp);
+				/* Save pseudonym if user wants it. */
+				if (len > 0 && esp->es_usepseudo) {
+					INCPTR(SHA_DIGESTSIZE, inp);
+					write_pseudonym(esp, inp, len, id);
+				}
+			}
+			/*
+			 * We've verified our peer.  We're now mostly done,
+			 * except for waiting on the regular EAP Success
+			 * message.
+			 */
+			eap_srp_response(esp, id, EAPSRP_ACK, NULL, 0);
+			break;
+
+		case EAPSRP_LWRECHALLENGE:
+			if (len < 4) {
+				warn("EAP: malformed Lightweight rechallenge");
+				return;
+			}
+			SHA1Init(&ctxt);
+			vals[0] = id;
+			SHA1Update(&ctxt, vals, 1);
+			SHA1Update(&ctxt, esp->es_client.ea_skey,
+			    SESSION_KEY_LEN);
+			SHA1Update(&ctxt, inp, len);
+			SHA1Update(&ctxt, esp->es_client.ea_name,
+			    esp->es_client.ea_namelen);
+			SHA1Final(dig, &ctxt);
+			eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig,
+			    SHA_DIGESTSIZE);
+			break;
+
+		default:
+			error("EAP: unknown SRP Subtype %d", vallen);
+			eap_send_nak(esp, id, EAPT_MD5CHAP);
+			break;
+		}
+		break;
+#endif /* USE_SRP */
+
+	default:
+		warn("EAP: unknown authentication type %d; Naking", typenum);
+		eap_send_nak(esp, id, EAPT_SRP);
+		break;
+	}
+
+	if (esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+		TIMEOUT(eap_client_timeout, (void *)esp,
+		    esp->es_client.ea_timeout);
+	}
+	return;
+
+#ifdef USE_SRP
+client_failure:
+	esp->es_client.ea_state = eapBadAuth;
+	if (esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+	}
+	esp->es_client.ea_session = NULL;
+	t_clientclose(tc);
+	auth_withpeer_fail(esp->es_unit, PPP_EAP);
+#endif /* USE_SRP */
+}
+
+/*
+ * eap_response - Receive EAP Response message (server mode).
+ */
+static void
+eap_response(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+	u_char typenum;
+	u_char vallen;
+	int secret_len;
+	char secret[MAXSECRETLEN];
+	char rhostname[256];
+	MD5_CTX mdContext;
+	u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+	struct t_server *ts;
+	struct t_num A;
+	SHA1_CTX ctxt;
+	u_char dig[SHA_DIGESTSIZE];
+#endif /* USE_SRP */
+
+	if (esp->es_server.ea_id != id) {
+		warn("EAP: discarding Response %d; expected ID %d", id,
+		    esp->es_server.ea_id);
+		return;
+	}
+
+	esp->es_server.ea_responses++;
+
+	if (len <= 0) {
+		error("EAP: empty Response message discarded");
+		return;
+	}
+
+	GETCHAR(typenum, inp);
+	len--;
+
+	switch (typenum) {
+	case EAPT_IDENTITY:
+		if (esp->es_server.ea_state != eapIdentify) {
+			warn("EAP discarding unwanted Identify \"%.q\"", len,
+			    inp);
+			break;
+		}
+		warn("EAP: unauthenticated peer name \"%.*q\"", len, inp);
+		if (esp->es_server.ea_peer != NULL &&
+		    esp->es_server.ea_peer != remote_name)
+			free(esp->es_server.ea_peer);
+		esp->es_server.ea_peer = malloc(len + 1);
+		if (esp->es_server.ea_peer == NULL) {
+			esp->es_server.ea_peerlen = 0;
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+		BCOPY(inp, esp->es_server.ea_peer, len);
+		esp->es_server.ea_peer[len] = '\0';
+		esp->es_server.ea_peerlen = len;
+		eap_figure_next_state(esp, 0);
+		break;
+
+	case EAPT_NOTIFICATION:
+		warn("EAP unexpected Notification; response discarded");
+		break;
+
+	case EAPT_NAK:
+		if (len < 1) {
+			warn("EAP: Nak Response with no suggested protocol");
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+
+		GETCHAR(vallen, inp);
+		len--;
+
+		if (!explicit_remote && esp->es_server.ea_state == eapIdentify){
+			/* Peer cannot Nak Identify Request */
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+
+		switch (vallen) {
+		case EAPT_SRP:
+			/* Run through SRP validator selection again. */
+			esp->es_server.ea_state = eapIdentify;
+			eap_figure_next_state(esp, 0);
+			break;
+
+		case EAPT_MD5CHAP:
+			esp->es_server.ea_state = eapMD5Chall;
+			break;
+
+		default:
+			warn("EAP: peer requesting unknown Type %d", vallen);
+			switch (esp->es_server.ea_state) {
+			case eapSRP1:
+			case eapSRP2:
+			case eapSRP3:
+				esp->es_server.ea_state = eapMD5Chall;
+				break;
+			case eapMD5Chall:
+			case eapSRP4:
+				esp->es_server.ea_state = eapIdentify;
+				eap_figure_next_state(esp, 0);
+				break;
+			default:
+				break;
+			}
+			break;
+		}
+		break;
+
+	case EAPT_MD5CHAP:
+		if (esp->es_server.ea_state != eapMD5Chall) {
+			error("EAP: unexpected MD5-Response");
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+		if (len < 1) {
+			error("EAP: received MD5-Response with no data");
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+		GETCHAR(vallen, inp);
+		len--;
+		if (vallen != 16 || vallen > len) {
+			error("EAP: MD5-Response with bad length %d", vallen);
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+
+		/* Not so likely to happen. */
+		if (len - vallen >= sizeof (rhostname)) {
+			warn("EAP: trimming really long peer name down");
+			BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1);
+			rhostname[sizeof (rhostname) - 1] = '\0';
+		} else {
+			BCOPY(inp + vallen, rhostname, len - vallen);
+			rhostname[len - vallen] = '\0';
+		}
+
+		/* In case the remote doesn't give us his name. */
+		if (explicit_remote ||
+		    (remote_name[0] != '\0' && vallen == len))
+			strlcpy(rhostname, remote_name, sizeof (rhostname));
+
+		/*
+		 * Get the secret for authenticating the specified
+		 * host.
+		 */
+		if (!get_secret(esp->es_unit, rhostname,
+		    esp->es_server.ea_name, secret, &secret_len, 1)) {
+			warn("EAP: no MD5 secret for auth of %q", rhostname);
+			eap_send_failure(esp);
+			break;
+		}
+		MD5_Init(&mdContext);
+		MD5_Update(&mdContext, &esp->es_server.ea_id, 1);
+		MD5_Update(&mdContext, (u_char *)secret, secret_len);
+		BZERO(secret, sizeof (secret));
+		MD5_Update(&mdContext, esp->es_challenge, esp->es_challen);
+		MD5_Final(hash, &mdContext);
+		if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) {
+			eap_send_failure(esp);
+			break;
+		}
+		esp->es_server.ea_type = EAPT_MD5CHAP;
+		eap_send_success(esp);
+		eap_figure_next_state(esp, 0);
+		if (esp->es_rechallenge != 0)
+			TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge);
+		break;
+
+#ifdef USE_SRP
+	case EAPT_SRP:
+		if (len < 1) {
+			error("EAP: empty SRP Response");
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+		GETCHAR(typenum, inp);
+		len--;
+		switch (typenum) {
+		case EAPSRP_CKEY:
+			if (esp->es_server.ea_state != eapSRP1) {
+				error("EAP: unexpected SRP Subtype 1 Response");
+				eap_figure_next_state(esp, 1);
+				break;
+			}
+			A.data = inp;
+			A.len = len;
+			ts = (struct t_server *)esp->es_server.ea_session;
+			assert(ts != NULL);
+			esp->es_server.ea_skey = t_servergetkey(ts, &A);
+			if (esp->es_server.ea_skey == NULL) {
+				/* Client's A value is bogus; terminate now */
+				error("EAP: bogus A value from client");
+				eap_send_failure(esp);
+			} else {
+				eap_figure_next_state(esp, 0);
+			}
+			break;
+
+		case EAPSRP_CVALIDATOR:
+			if (esp->es_server.ea_state != eapSRP2) {
+				error("EAP: unexpected SRP Subtype 2 Response");
+				eap_figure_next_state(esp, 1);
+				break;
+			}
+			if (len < sizeof (u_int32_t) + SHA_DIGESTSIZE) {
+				error("EAP: M1 length %d < %d", len,
+				    sizeof (u_int32_t) + SHA_DIGESTSIZE);
+				eap_figure_next_state(esp, 1);
+				break;
+			}
+			GETLONG(esp->es_server.ea_keyflags, inp);
+			ts = (struct t_server *)esp->es_server.ea_session;
+			assert(ts != NULL);
+			if (t_serververify(ts, inp)) {
+				warn("EAP: unable to validate client identity");
+				eap_send_failure(esp);
+				break;
+			}
+			eap_figure_next_state(esp, 0);
+			break;
+
+		case EAPSRP_ACK:
+			if (esp->es_server.ea_state != eapSRP3) {
+				error("EAP: unexpected SRP Subtype 3 Response");
+				eap_send_failure(esp);
+				break;
+			}
+			esp->es_server.ea_type = EAPT_SRP;
+			eap_send_success(esp);
+			eap_figure_next_state(esp, 0);
+			if (esp->es_rechallenge != 0)
+				TIMEOUT(eap_rechallenge, esp,
+				    esp->es_rechallenge);
+			if (esp->es_lwrechallenge != 0)
+				TIMEOUT(srp_lwrechallenge, esp,
+				    esp->es_lwrechallenge);
+			break;
+
+		case EAPSRP_LWRECHALLENGE:
+			if (esp->es_server.ea_state != eapSRP4) {
+				warn("EAP: unexpected SRP Subtype 4 Response");
+				return;
+			}
+			if (len != SHA_DIGESTSIZE) {
+				error("EAP: bad Lightweight rechallenge "
+				    "response");
+				return;
+			}
+			SHA1Init(&ctxt);
+			vallen = id;
+			SHA1Update(&ctxt, &vallen, 1);
+			SHA1Update(&ctxt, esp->es_server.ea_skey,
+			    SESSION_KEY_LEN);
+			SHA1Update(&ctxt, esp->es_challenge, esp->es_challen);
+			SHA1Update(&ctxt, esp->es_server.ea_peer,
+			    esp->es_server.ea_peerlen);
+			SHA1Final(dig, &ctxt);
+			if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) {
+				error("EAP: failed Lightweight rechallenge");
+				eap_send_failure(esp);
+				break;
+			}
+			esp->es_server.ea_state = eapOpen;
+			if (esp->es_lwrechallenge != 0)
+				TIMEOUT(srp_lwrechallenge, esp,
+				    esp->es_lwrechallenge);
+			break;
+		}
+		break;
+#endif /* USE_SRP */
+
+	default:
+		/* This can't happen. */
+		error("EAP: unknown Response type %d; ignored", typenum);
+		return;
+	}
+
+	if (esp->es_server.ea_timeout > 0) {
+		UNTIMEOUT(eap_server_timeout, (void *)esp);
+	}
+
+	if (esp->es_server.ea_state != eapBadAuth &&
+	    esp->es_server.ea_state != eapOpen) {
+		esp->es_server.ea_id++;
+		eap_send_request(esp);
+	}
+}
+
+/*
+ * eap_success - Receive EAP Success message (client mode).
+ */
+static void
+eap_success(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+	if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)) {
+		warn("EAP unexpected success message in state %s (%d)",
+		    eap_state_name(esp->es_client.ea_state),
+		    esp->es_client.ea_state);
+		return;
+	}
+
+	if (esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+	}
+
+	if (len > 0) {
+		/* This is odd.  The spec doesn't allow for this. */
+		PRINTMSG(inp, len);
+	}
+
+	esp->es_client.ea_state = eapOpen;
+	auth_withpeer_success(esp->es_unit, PPP_EAP, 0);
+}
+
+/*
+ * eap_failure - Receive EAP Failure message (client mode).
+ */
+static void
+eap_failure(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+	if (!eap_client_active(esp)) {
+		warn("EAP unexpected failure message in state %s (%d)",
+		    eap_state_name(esp->es_client.ea_state),
+		    esp->es_client.ea_state);
+	}
+
+	if (esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+	}
+
+	if (len > 0) {
+		/* This is odd.  The spec doesn't allow for this. */
+		PRINTMSG(inp, len);
+	}
+
+	esp->es_client.ea_state = eapBadAuth;
+
+	error("EAP: peer reports authentication failure");
+	auth_withpeer_fail(esp->es_unit, PPP_EAP);
+}
+
+/*
+ * eap_input - Handle received EAP message.
+ */
+static void
+eap_input(unit, inp, inlen)
+int unit;
+u_char *inp;
+int inlen;
+{
+	eap_state *esp = &eap_states[unit];
+	u_char code, id;
+	int len;
+
+	/*
+	 * Parse header (code, id and length).  If packet too short,
+	 * drop it.
+	 */
+	if (inlen < EAP_HEADERLEN) {
+		error("EAP: packet too short: %d < %d", inlen, EAP_HEADERLEN);
+		return;
+	}
+	GETCHAR(code, inp);
+	GETCHAR(id, inp);
+	GETSHORT(len, inp);
+	if (len < EAP_HEADERLEN || len > inlen) {
+		error("EAP: packet has illegal length field %d (%d..%d)", len,
+		    EAP_HEADERLEN, inlen);
+		return;
+	}
+	len -= EAP_HEADERLEN;
+
+	/* Dispatch based on message code */
+	switch (code) {
+	case EAP_REQUEST:
+		eap_request(esp, inp, id, len);
+		break;
+
+	case EAP_RESPONSE:
+		eap_response(esp, inp, id, len);
+		break;
+
+	case EAP_SUCCESS:
+		eap_success(esp, inp, id, len);
+		break;
+
+	case EAP_FAILURE:
+		eap_failure(esp, inp, id, len);
+		break;
+
+	default:				/* XXX Need code reject */
+		/* Note: it's not legal to send EAP Nak here. */
+		warn("EAP: unknown code %d received", code);
+		break;
+	}
+}
+
+/*
+ * eap_printpkt - print the contents of an EAP packet.
+ */
+static char *eap_codenames[] = {
+	"Request", "Response", "Success", "Failure"
+};
+
+static char *eap_typenames[] = {
+	"Identity", "Notification", "Nak", "MD5-Challenge",
+	"OTP", "Generic-Token", NULL, NULL,
+	"RSA", "DSS", "KEA", "KEA-Validate",
+	"TLS", "Defender", "Windows 2000", "Arcot",
+	"Cisco", "Nokia", "SRP"
+};
+
+static int
+eap_printpkt(inp, inlen, printer, arg)
+u_char *inp;
+int inlen;
+void (*printer) __P((void *, char *, ...));
+void *arg;
+{
+	int code, id, len, rtype, vallen;
+	u_char *pstart;
+	u_int32_t uval;
+
+	if (inlen < EAP_HEADERLEN)
+		return (0);
+	pstart = inp;
+	GETCHAR(code, inp);
+	GETCHAR(id, inp);
+	GETSHORT(len, inp);
+	if (len < EAP_HEADERLEN || len > inlen)
+		return (0);
+
+	if (code >= 1 && code <= sizeof(eap_codenames) / sizeof(char *))
+		printer(arg, " %s", eap_codenames[code-1]);
+	else
+		printer(arg, " code=0x%x", code);
+	printer(arg, " id=0x%x", id);
+	len -= EAP_HEADERLEN;
+	switch (code) {
+	case EAP_REQUEST:
+		if (len < 1) {
+			printer(arg, " <missing type>");
+			break;
+		}
+		GETCHAR(rtype, inp);
+		len--;
+		if (rtype >= 1 &&
+		    rtype <= sizeof (eap_typenames) / sizeof (char *))
+			printer(arg, " %s", eap_typenames[rtype-1]);
+		else
+			printer(arg, " type=0x%x", rtype);
+		switch (rtype) {
+		case EAPT_IDENTITY:
+		case EAPT_NOTIFICATION:
+			if (len > 0) {
+				printer(arg, " <Message ");
+				print_string((char *)inp, len, printer, arg);
+				printer(arg, ">");
+				INCPTR(len, inp);
+				len = 0;
+			} else {
+				printer(arg, " <No message>");
+			}
+			break;
+
+		case EAPT_MD5CHAP:
+			if (len <= 0)
+				break;
+			GETCHAR(vallen, inp);
+			len--;
+			if (vallen > len)
+				goto truncated;
+			printer(arg, " <Value%.*B>", vallen, inp);
+			INCPTR(vallen, inp);
+			len -= vallen;
+			if (len > 0) {
+				printer(arg, " <Name ");
+				print_string((char *)inp, len, printer, arg);
+				printer(arg, ">");
+				INCPTR(len, inp);
+				len = 0;
+			} else {
+				printer(arg, " <No name>");
+			}
+			break;
+
+		case EAPT_SRP:
+			if (len < 3)
+				goto truncated;
+			GETCHAR(vallen, inp);
+			len--;
+			printer(arg, "-%d", vallen);
+			switch (vallen) {
+			case EAPSRP_CHALLENGE:
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen >= len)
+					goto truncated;
+				if (vallen > 0) {
+					printer(arg, " <Name ");
+					print_string((char *)inp, vallen, printer,
+					    arg);
+					printer(arg, ">");
+				} else {
+					printer(arg, " <No name>");
+				}
+				INCPTR(vallen, inp);
+				len -= vallen;
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen >= len)
+					goto truncated;
+				printer(arg, " <s%.*B>", vallen, inp);
+				INCPTR(vallen, inp);
+				len -= vallen;
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen > len)
+					goto truncated;
+				if (vallen == 0) {
+					printer(arg, " <Default g=2>");
+				} else {
+					printer(arg, " <g%.*B>", vallen, inp);
+				}
+				INCPTR(vallen, inp);
+				len -= vallen;
+				if (len == 0) {
+					printer(arg, " <Default N>");
+				} else {
+					printer(arg, " <N%.*B>", len, inp);
+					INCPTR(len, inp);
+					len = 0;
+				}
+				break;
+
+			case EAPSRP_SKEY:
+				printer(arg, " <B%.*B>", len, inp);
+				INCPTR(len, inp);
+				len = 0;
+				break;
+
+			case EAPSRP_SVALIDATOR:
+				if (len < sizeof (u_int32_t))
+					break;
+				GETLONG(uval, inp);
+				len -= sizeof (u_int32_t);
+				if (uval & SRPVAL_EBIT) {
+					printer(arg, " E");
+					uval &= ~SRPVAL_EBIT;
+				}
+				if (uval != 0) {
+					printer(arg, " f<%X>", uval);
+				}
+				if ((vallen = len) > SHA_DIGESTSIZE)
+					vallen = SHA_DIGESTSIZE;
+				printer(arg, " <M2%.*B%s>", len, inp,
+				    len < SHA_DIGESTSIZE ? "?" : "");
+				INCPTR(vallen, inp);
+				len -= vallen;
+				if (len > 0) {
+					printer(arg, " <PN%.*B>", len, inp);
+					INCPTR(len, inp);
+					len = 0;
+				}
+				break;
+
+			case EAPSRP_LWRECHALLENGE:
+				printer(arg, " <Challenge%.*B>", len, inp);
+				INCPTR(len, inp);
+				len = 0;
+				break;
+			}
+			break;
+		}
+		break;
+
+	case EAP_RESPONSE:
+		if (len < 1)
+			break;
+		GETCHAR(rtype, inp);
+		len--;
+		if (rtype >= 1 &&
+		    rtype <= sizeof (eap_typenames) / sizeof (char *))
+			printer(arg, " %s", eap_typenames[rtype-1]);
+		else
+			printer(arg, " type=0x%x", rtype);
+		switch (rtype) {
+		case EAPT_IDENTITY:
+			if (len > 0) {
+				printer(arg, " <Name ");
+				print_string((char *)inp, len, printer, arg);
+				printer(arg, ">");
+				INCPTR(len, inp);
+				len = 0;
+			}
+			break;
+
+		case EAPT_NAK:
+			if (len <= 0) {
+				printer(arg, " <missing hint>");
+				break;
+			}
+			GETCHAR(rtype, inp);
+			len--;
+			printer(arg, " <Suggested-type %02X", rtype);
+			if (rtype >= 1 &&
+			    rtype < sizeof (eap_typenames) / sizeof (char *))
+				printer(arg, " (%s)", eap_typenames[rtype-1]);
+			printer(arg, ">");
+			break;
+
+		case EAPT_MD5CHAP:
+			if (len <= 0) {
+				printer(arg, " <missing length>");
+				break;
+			}
+			GETCHAR(vallen, inp);
+			len--;
+			if (vallen > len)
+				goto truncated;
+			printer(arg, " <Value%.*B>", vallen, inp);
+			INCPTR(vallen, inp);
+			len -= vallen;
+			if (len > 0) {
+				printer(arg, " <Name ");
+				print_string((char *)inp, len, printer, arg);
+				printer(arg, ">");
+				INCPTR(len, inp);
+				len = 0;
+			} else {
+				printer(arg, " <No name>");
+			}
+			break;
+
+		case EAPT_SRP:
+			if (len < 1)
+				goto truncated;
+			GETCHAR(vallen, inp);
+			len--;
+			printer(arg, "-%d", vallen);
+			switch (vallen) {
+			case EAPSRP_CKEY:
+				printer(arg, " <A%.*B>", len, inp);
+				INCPTR(len, inp);
+				len = 0;
+				break;
+
+			case EAPSRP_CVALIDATOR:
+				if (len < sizeof (u_int32_t))
+					break;
+				GETLONG(uval, inp);
+				len -= sizeof (u_int32_t);
+				if (uval & SRPVAL_EBIT) {
+					printer(arg, " E");
+					uval &= ~SRPVAL_EBIT;
+				}
+				if (uval != 0) {
+					printer(arg, " f<%X>", uval);
+				}
+				printer(arg, " <M1%.*B%s>", len, inp,
+				    len == SHA_DIGESTSIZE ? "" : "?");
+				INCPTR(len, inp);
+				len = 0;
+				break;
+
+			case EAPSRP_ACK:
+				break;
+
+			case EAPSRP_LWRECHALLENGE:
+				printer(arg, " <Response%.*B%s>", len, inp,
+				    len == SHA_DIGESTSIZE ? "" : "?");
+				if ((vallen = len) > SHA_DIGESTSIZE)
+					vallen = SHA_DIGESTSIZE;
+				INCPTR(vallen, inp);
+				len -= vallen;
+				break;
+			}
+			break;
+		}
+		break;
+
+	case EAP_SUCCESS:	/* No payload expected for these! */
+	case EAP_FAILURE:
+		break;
+
+	truncated:
+		printer(arg, " <truncated>");
+		break;
+	}
+
+	if (len > 8)
+		printer(arg, "%8B...", inp);
+	else if (len > 0)
+		printer(arg, "%.*B", len, inp);
+	INCPTR(len, inp);
+
+	return (inp - pstart);
+}
diff --git a/ap/app/pppd/pppd/eap.h b/ap/app/pppd/pppd/eap.h
new file mode 100644
index 0000000..199d184
--- /dev/null
+++ b/ap/app/pppd/pppd/eap.h
@@ -0,0 +1,158 @@
+/*
+ * eap.h - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * $Id: eap.h,v 1.2 2003/06/11 23:56:26 paulus Exp $
+ */
+
+#ifndef PPP_EAP_H
+#define	PPP_EAP_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define	EAP_HEADERLEN	4
+
+
+/* EAP message codes. */
+#define	EAP_REQUEST	1
+#define	EAP_RESPONSE	2
+#define	EAP_SUCCESS	3
+#define	EAP_FAILURE	4
+
+/* EAP types */
+#define	EAPT_IDENTITY		1
+#define	EAPT_NOTIFICATION	2
+#define	EAPT_NAK		3	/* (response only) */
+#define	EAPT_MD5CHAP		4
+#define	EAPT_OTP		5	/* One-Time Password; RFC 1938 */
+#define	EAPT_TOKEN		6	/* Generic Token Card */
+/* 7 and 8 are unassigned. */
+#define	EAPT_RSA		9	/* RSA Public Key Authentication */
+#define	EAPT_DSS		10	/* DSS Unilateral */
+#define	EAPT_KEA		11	/* KEA */
+#define	EAPT_KEA_VALIDATE	12	/* KEA-VALIDATE	*/
+#define	EAPT_TLS		13	/* EAP-TLS */
+#define	EAPT_DEFENDER		14	/* Defender Token (AXENT) */
+#define	EAPT_W2K		15	/* Windows 2000 EAP */
+#define	EAPT_ARCOT		16	/* Arcot Systems */
+#define	EAPT_CISCOWIRELESS	17	/* Cisco Wireless */
+#define	EAPT_NOKIACARD		18	/* Nokia IP smart card */
+#define	EAPT_SRP		19	/* Secure Remote Password */
+/* 20 is deprecated */
+
+/* EAP SRP-SHA1 Subtypes */
+#define	EAPSRP_CHALLENGE	1	/* Request 1 - Challenge */
+#define	EAPSRP_CKEY		1	/* Response 1 - Client Key */
+#define	EAPSRP_SKEY		2	/* Request 2 - Server Key */
+#define	EAPSRP_CVALIDATOR	2	/* Response 2 - Client Validator */
+#define	EAPSRP_SVALIDATOR	3	/* Request 3 - Server Validator */
+#define	EAPSRP_ACK		3	/* Response 3 - final ack */
+#define	EAPSRP_LWRECHALLENGE	4	/* Req/resp 4 - Lightweight rechal */
+
+#define	SRPVAL_EBIT	0x00000001	/* Use shared key for ECP */
+
+#define	SRP_PSEUDO_ID	"pseudo_"
+#define	SRP_PSEUDO_LEN	7
+
+#define MD5_SIGNATURE_SIZE	16
+#define MIN_CHALLENGE_LENGTH	16
+#define MAX_CHALLENGE_LENGTH	24
+
+enum eap_state_code {
+	eapInitial = 0,	/* No EAP authentication yet requested */
+	eapPending,	/* Waiting for LCP (no timer) */
+	eapClosed,	/* Authentication not in use */
+	eapListen,	/* Client ready (and timer running) */
+	eapIdentify,	/* EAP Identify sent */
+	eapSRP1,	/* Sent EAP SRP-SHA1 Subtype 1 */
+	eapSRP2,	/* Sent EAP SRP-SHA1 Subtype 2 */
+	eapSRP3,	/* Sent EAP SRP-SHA1 Subtype 3 */
+	eapMD5Chall,	/* Sent MD5-Challenge */
+	eapOpen,	/* Completed authentication */
+	eapSRP4,	/* Sent EAP SRP-SHA1 Subtype 4 */
+	eapBadAuth	/* Failed authentication */
+};
+
+#define	EAP_STATES	\
+	"Initial", "Pending", "Closed", "Listen", "Identify", \
+	"SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth"
+
+#define	eap_client_active(esp)	((esp)->es_client.ea_state == eapListen)
+#define	eap_server_active(esp)	\
+	((esp)->es_server.ea_state >= eapIdentify && \
+	 (esp)->es_server.ea_state <= eapMD5Chall)
+
+struct eap_auth {
+	char *ea_name;		/* Our name */
+	char *ea_peer;		/* Peer's name */
+	void *ea_session;	/* Authentication library linkage */
+	u_char *ea_skey;	/* Shared encryption key */
+	int ea_timeout;		/* Time to wait (for retransmit/fail) */
+	int ea_maxrequests;	/* Max Requests allowed */
+	u_short ea_namelen;	/* Length of our name */
+	u_short ea_peerlen;	/* Length of peer's name */
+	enum eap_state_code ea_state;
+	u_char ea_id;		/* Current id */
+	u_char ea_requests;	/* Number of Requests sent/received */
+	u_char ea_responses;	/* Number of Responses */
+	u_char ea_type;		/* One of EAPT_* */
+	u_int32_t ea_keyflags;	/* SRP shared key usage flags */
+};
+
+/*
+ * Complete EAP state for one PPP session.
+ */
+typedef struct eap_state {
+	int es_unit;			/* Interface unit number */
+	struct eap_auth es_client;	/* Client (authenticatee) data */
+	struct eap_auth es_server;	/* Server (authenticator) data */
+	int es_savedtime;		/* Saved timeout */
+	int es_rechallenge;		/* EAP rechallenge interval */
+	int es_lwrechallenge;		/* SRP lightweight rechallenge inter */
+	bool es_usepseudo;		/* Use SRP Pseudonym if offered one */
+	int es_usedpseudo;		/* Set if we already sent PN */
+	int es_challen;			/* Length of challenge string */
+	u_char es_challenge[MAX_CHALLENGE_LENGTH];
+} eap_state;
+
+/*
+ * Timeouts.
+ */
+#define	EAP_DEFTIMEOUT		3	/* Timeout (seconds) for rexmit */
+#define	EAP_DEFTRANSMITS	10	/* max # times to transmit */
+#define	EAP_DEFREQTIME		20	/* Time to wait for peer request */
+#define	EAP_DEFALLOWREQ		20	/* max # times to accept requests */
+
+extern eap_state eap_states[];
+
+void eap_authwithpeer __P((int unit, char *localname));
+void eap_authpeer __P((int unit, char *localname));
+
+extern struct protent eap_protent;
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* PPP_EAP_H */
+
diff --git a/ap/app/pppd/pppd/ecp.c b/ap/app/pppd/pppd/ecp.c
new file mode 100644
index 0000000..e5754e5
--- /dev/null
+++ b/ap/app/pppd/pppd/ecp.c
@@ -0,0 +1,173 @@
+/*
+ * ecp.c - PPP Encryption Control Protocol.
+ *
+ * Copyright (c) 2002 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from ccp.c, which is:
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: ecp.c,v 1.4 2004/11/04 10:02:26 paulus Exp $"
+
+static const char rcsid[] = RCSID;
+
+#include <string.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ecp.h"
+
+static option_t ecp_option_list[] = {
+    { "noecp", o_bool, &ecp_protent.enabled_flag,
+      "Disable ECP negotiation" },
+    { "-ecp", o_bool, &ecp_protent.enabled_flag,
+      "Disable ECP negotiation", OPT_ALIAS },
+
+    { NULL }
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ecp_init __P((int unit));
+/*
+static void ecp_open __P((int unit));
+static void ecp_close __P((int unit, char *));
+static void ecp_lowerup __P((int unit));
+static void ecp_lowerdown __P((int));
+static void ecp_input __P((int unit, u_char *pkt, int len));
+static void ecp_protrej __P((int unit));
+*/
+static int  ecp_printpkt __P((u_char *pkt, int len,
+			      void (*printer) __P((void *, char *, ...)),
+			      void *arg));
+/*
+static void ecp_datainput __P((int unit, u_char *pkt, int len));
+*/
+
+struct protent ecp_protent = {
+    PPP_ECP,
+    ecp_init,
+    NULL, /* ecp_input, */
+    NULL, /* ecp_protrej, */
+    NULL, /* ecp_lowerup, */
+    NULL, /* ecp_lowerdown, */
+    NULL, /* ecp_open, */
+    NULL, /* ecp_close, */
+    ecp_printpkt,
+    NULL, /* ecp_datainput, */
+    0,
+    "ECP",
+    "Encrypted",
+    ecp_option_list,
+    NULL,
+    NULL,
+    NULL
+};
+
+fsm ecp_fsm[NUM_PPP];
+ecp_options ecp_wantoptions[NUM_PPP];	/* what to request the peer to use */
+ecp_options ecp_gotoptions[NUM_PPP];	/* what the peer agreed to do */
+ecp_options ecp_allowoptions[NUM_PPP];	/* what we'll agree to do */
+ecp_options ecp_hisoptions[NUM_PPP];	/* what we agreed to do */
+
+static fsm_callbacks ecp_callbacks = {
+    NULL, /* ecp_resetci, */
+    NULL, /* ecp_cilen, */
+    NULL, /* ecp_addci, */
+    NULL, /* ecp_ackci, */
+    NULL, /* ecp_nakci, */
+    NULL, /* ecp_rejci, */
+    NULL, /* ecp_reqci, */
+    NULL, /* ecp_up, */
+    NULL, /* ecp_down, */
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL, /* ecp_extcode, */
+    "ECP"
+};
+
+/*
+ * ecp_init - initialize ECP.
+ */
+static void
+ecp_init(unit)
+    int unit;
+{
+    fsm *f = &ecp_fsm[unit];
+
+    f->unit = unit;
+    f->protocol = PPP_ECP;
+    f->callbacks = &ecp_callbacks;
+    fsm_init(f);
+
+    memset(&ecp_wantoptions[unit],  0, sizeof(ecp_options));
+    memset(&ecp_gotoptions[unit],   0, sizeof(ecp_options));
+    memset(&ecp_allowoptions[unit], 0, sizeof(ecp_options));
+    memset(&ecp_hisoptions[unit],   0, sizeof(ecp_options));
+
+}
+
+
+static int
+ecp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    return 0;
+}
+
diff --git a/ap/app/pppd/pppd/ecp.h b/ap/app/pppd/pppd/ecp.h
new file mode 100644
index 0000000..df6e3ca
--- /dev/null
+++ b/ap/app/pppd/pppd/ecp.h
@@ -0,0 +1,45 @@
+/*
+ * ecp.h - Definitions for PPP Encryption Control Protocol.
+ *
+ * Copyright (c) 2002 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ecp.h,v 1.2 2003/01/10 07:12:36 fcusack Exp $
+ */
+
+typedef struct ecp_options {
+    bool required;		/* Is ECP required? */
+    unsigned enctype;		/* Encryption type */
+} ecp_options;
+
+extern fsm ecp_fsm[];
+extern ecp_options ecp_wantoptions[];
+extern ecp_options ecp_gotoptions[];
+extern ecp_options ecp_allowoptions[];
+extern ecp_options ecp_hisoptions[];
+
+extern struct protent ecp_protent;
diff --git a/ap/app/pppd/pppd/eui64.c b/ap/app/pppd/pppd/eui64.c
new file mode 100644
index 0000000..d025eff
--- /dev/null
+++ b/ap/app/pppd/pppd/eui64.c
@@ -0,0 +1,57 @@
+/*
+ * eui64.c - EUI64 routines for IPv6CP.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $
+ */
+
+#define RCSID	"$Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $"
+
+#include "pppd.h"
+
+static const char rcsid[] = RCSID;
+
+/*
+ * eui64_ntoa - Make an ascii representation of an interface identifier
+ */
+char *
+eui64_ntoa(e)
+    eui64_t e;
+{
+    static char buf[32];
+
+    snprintf(buf, 32, "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+	     e.e8[0], e.e8[1], e.e8[2], e.e8[3], 
+	     e.e8[4], e.e8[5], e.e8[6], e.e8[7]);
+    return buf;
+}
diff --git a/ap/app/pppd/pppd/eui64.h b/ap/app/pppd/pppd/eui64.h
new file mode 100644
index 0000000..0f6b6fd
--- /dev/null
+++ b/ap/app/pppd/pppd/eui64.h
@@ -0,0 +1,114 @@
+/*
+ * eui64.h - EUI64 routines for IPv6CP.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: eui64.h,v 1.6 2002/12/04 23:03:32 paulus Exp $
+*/
+
+#ifndef __EUI64_H__
+#define __EUI64_H__
+
+#if !defined(INET6)
+#error	"this file should only be included when INET6 is defined"
+#endif /* not defined(INET6) */
+
+#if defined(SOL2)
+#include <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/ap/app/pppd/pppd/fsm.c b/ap/app/pppd/pppd/fsm.c
new file mode 100755
index 0000000..1195c32
--- /dev/null
+++ b/ap/app/pppd/pppd/fsm.c
@@ -0,0 +1,938 @@
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: fsm.c,v 1.3 2007-06-08 04:02:38 gerg Exp $"
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+static const char rcsid[] = RCSID;
+#define ZTE_ROUTER_IP_ADDR_LEN 32
+static void fsm_timeout __P((void *));
+static void fsm_rconfreq __P((fsm *, int, u_char *, int));
+static void fsm_rconfack __P((fsm *, int, u_char *, int));
+static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int));
+static void fsm_rtermreq __P((fsm *, int, u_char *, int));
+static void fsm_rtermack __P((fsm *));
+static void fsm_rcoderej __P((fsm *, u_char *, int));
+static void fsm_sconfreq __P((fsm *, int));
+
+#define PROTO_NAME(f)	((f)->callbacks->proto_name)
+
+int peer_mru[NUM_PPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(f)
+    fsm *f;
+{
+    f->state = INITIAL;
+    f->flags = 0;
+    f->id = 0;				/* XXX Start with random id? */
+    f->timeouttime = DEFTIMEOUT;
+    f->maxconfreqtransmits = DEFMAXCONFREQS;
+    f->maxtermtransmits = DEFMAXTERMREQS;
+    f->maxnakloops = DEFMAXNAKLOOPS;
+    f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case INITIAL:
+	f->state = CLOSED;
+	break;
+
+    case STARTING:
+	if( f->flags & OPT_SILENT )
+	    f->state = STOPPED;
+	else {
+	    /* Send an initial configure-request */
+	    fsm_sconfreq(f, 0);
+	    f->state = REQSENT;
+	}
+	break;
+
+    default:
+	FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case CLOSED:
+	f->state = INITIAL;
+	break;
+
+    case STOPPED:
+	f->state = STARTING;
+	if( f->callbacks->starting )
+	    (*f->callbacks->starting)(f);
+	break;
+
+    case CLOSING:
+	f->state = INITIAL;
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	break;
+
+    case STOPPING:
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+	f->state = STARTING;
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	break;
+
+    case OPENED:
+	if( f->callbacks->down )
+	    (*f->callbacks->down)(f);
+	f->state = STARTING;
+	break;
+
+    default:
+	FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case INITIAL:
+	f->state = STARTING;
+	if( f->callbacks->starting )
+	    (*f->callbacks->starting)(f);
+	break;
+
+    case CLOSED:
+	if( f->flags & OPT_SILENT )
+	    f->state = STOPPED;
+	else {
+	    /* Send an initial configure-request */
+	    fsm_sconfreq(f, 0);
+	    f->state = REQSENT;
+	}
+	break;
+
+    case CLOSING:
+	f->state = STOPPING;
+	/* fall through */
+    case STOPPED:
+    case OPENED:
+	if( f->flags & OPT_RESTART ){
+	    fsm_lowerdown(f);
+	    fsm_lowerup(f);
+	}
+	break;
+    }
+}
+
+/*
+ * terminate_layer - Start process of shutting down the FSM
+ *
+ * Cancel any timeout running, notify upper layers we're done, and
+ * send a terminate-request message as configured.
+ */
+static void
+terminate_layer(f, nextstate)
+    fsm *f;
+    int nextstate;
+{
+    if( f->state != OPENED )
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+    else if( f->callbacks->down )
+	(*f->callbacks->down)(f);	/* Inform upper layers we're down */
+
+    /* Init restart counter and send Terminate-Request */
+    f->retransmits = f->maxtermtransmits;
+    fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+	      (u_char *) f->term_reason, f->term_reason_len);
+
+    if (f->retransmits == 0) {
+	/*
+	 * User asked for no terminate requests at all; just close it.
+	 * We've already fired off one Terminate-Request just to be nice
+	 * to the peer, but we're not going to wait for a reply.
+	 */
+	f->state = nextstate == CLOSING ? CLOSED : STOPPED;
+	if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
+	return;
+    }
+
+    TIMEOUT(fsm_timeout, f, f->timeouttime);
+    --f->retransmits;
+
+    f->state = nextstate;
+}
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the CLOSED state.
+ */
+void
+fsm_close(f, reason)
+    fsm *f;
+    char *reason;
+{
+    f->term_reason = reason;
+    f->term_reason_len = (reason == NULL? 0: strlen(reason));
+    switch( f->state ){
+    case STARTING:
+	f->state = INITIAL;
+	break;
+    case STOPPED:
+	f->state = CLOSED;
+	break;
+    case STOPPING:
+	f->state = CLOSING;
+	break;
+
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+    case OPENED:
+	terminate_layer(f, CLOSING);
+	break;
+    }
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(arg)
+    void *arg;
+{
+    fsm *f = (fsm *) arg;
+
+    switch (f->state) {
+    case CLOSING:
+    case STOPPING:
+	if( f->retransmits <= 0 ){
+	    /*
+	     * We've waited for an ack long enough.  Peer probably heard us.
+	     */
+	    f->state = (f->state == CLOSING)? CLOSED: STOPPED;
+	    if( f->callbacks->finished )
+		(*f->callbacks->finished)(f);
+	} else {
+	    /* Send Terminate-Request */
+	    fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+		      (u_char *) f->term_reason, f->term_reason_len);
+	    TIMEOUT(fsm_timeout, f, f->timeouttime);
+	    --f->retransmits;
+	}
+	break;
+
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+	if (f->retransmits <= 0) {
+		 warn("%s: timeout sending Config-Requests, stateis %d\n", PROTO_NAME(f), f->state);
+	    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.
+     */
+     FSMDEBUG(("fsm_input(%x): code is %d, state is %d", f->protocol, code, f->state));
+    switch (code) {
+    case CONFREQ:
+	fsm_rconfreq(f, id, inp, len);
+	break;
+    
+    case CONFACK:
+	fsm_rconfack(f, id, inp, len);
+	break;
+    
+    case CONFNAK:
+    case CONFREJ:
+	fsm_rconfnakrej(f, code, id, inp, len);
+	break;
+    
+    case TERMREQ:
+	fsm_rtermreq(f, id, inp, len);
+	break;
+    
+    case TERMACK:
+	fsm_rtermack(f);
+	break;
+    
+    case CODEREJ:
+	fsm_rcoderej(f, inp, len);
+	break;
+    
+    default:
+	if( !f->callbacks->extcode
+	   || !(*f->callbacks->extcode)(f, code, id, inp, len) )
+	    fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+	break;
+    }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(f, id, inp, len)
+    fsm *f;
+    u_char id;
+    u_char *inp;
+    int len;
+{
+    int code, reject_if_disagree;
+
+    switch( f->state ){
+    case CLOSED:
+	/* Go away, we're closed */
+	fsm_sdata(f, TERMACK, id, NULL, 0);
+	return;
+    case CLOSING:
+    case STOPPING:
+	return;
+
+    case OPENED:
+	/* Go down and restart negotiation */
+	if( f->callbacks->down )
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	fsm_sconfreq(f, 0);		/* Send initial Configure-Request */
+	f->state = REQSENT;
+	break;
+
+    case STOPPED:
+	/* Negotiation started by our peer */
+	fsm_sconfreq(f, 0);		/* Send initial Configure-Request */
+	f->state = REQSENT;
+	break;
+    }
+
+    /*
+     * Pass the requested configuration options
+     * to protocol-specific code for checking.
+     */
+    if (f->callbacks->reqci){		/* Check CI */
+	reject_if_disagree = (f->nakloops >= f->maxnakloops);
+	code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+    } else if (len)
+	code = CONFREJ;			/* Reject all CI */
+    else
+	code = CONFACK;
+
+    /* send the Ack, Nak or Rej to the peer */
+    fsm_sdata(f, code, id, inp, len);
+
+    if (code == CONFACK) {
+	if (f->state == ACKRCVD) {
+	    UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	    f->state = OPENED;
+	    if (f->callbacks->up)
+		(*f->callbacks->up)(f);	/* Inform upper layers */
+	} else
+	    f->state = ACKSENT;
+	f->nakloops = 0;
+
+    } else {
+	/* we sent CONFACK or CONFREJ */
+	if (f->state != ACKRCVD)
+	    f->state = REQSENT;
+	if( code == CONFNAK )
+	    ++f->nakloops;
+    }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(f, id, inp, len)
+    fsm *f;
+    int id;
+    u_char *inp;
+    int len;
+{
+    if (id != f->reqid || f->seen_ack)		/* Expected id? */
+	return;					/* Nope, toss... */
+    if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
+	  (len == 0)) ){
+	/* Ack is bad - ignore it */
+	error("Received bad configure-ack: %P", inp, len);
+	return;
+    }
+    f->seen_ack = 1;
+    f->rnakloops = 0;
+	
+	FSMDEBUG(("fsm_rconfack(%x): state is %d", f->protocol, f->state));
+	
+	switch (f->state) {
+    case CLOSED:
+    case STOPPED:
+	fsm_sdata(f, TERMACK, id, NULL, 0);
+	break;
+
+    case REQSENT:
+	f->state = ACKRCVD;
+	f->retransmits = f->maxconfreqtransmits;
+	break;
+
+    case ACKRCVD:
+	/* Huh? an extra valid Ack? oh well... */
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	fsm_sconfreq(f, 0);
+	f->state = REQSENT;
+	break;
+
+    case ACKSENT:
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	f->state = OPENED;
+	f->retransmits = f->maxconfreqtransmits;
+	if (f->callbacks->up)
+	    (*f->callbacks->up)(f);	/* Inform upper layers */
+	break;
+
+    case OPENED:
+	/* Go down and restart negotiation */
+	if (f->callbacks->down)
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	fsm_sconfreq(f, 0);		/* Send initial Configure-Request */
+	f->state = REQSENT;
+	break;
+    }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(f, code, id, inp, len)
+    fsm *f;
+    int code, id;
+    u_char *inp;
+    int len;
+{
+    int ret;
+    int treat_as_reject;
+
+    if (id != f->reqid || f->seen_ack)	/* Expected id? */
+	return;				/* Nope, toss... */
+
+    if (code == CONFNAK) {
+	++f->rnakloops;
+	treat_as_reject = (f->rnakloops >= f->maxnakloops);
+	if (f->callbacks->nakci == NULL
+	    || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) {
+	    error("Received bad configure-nak: %P", inp, len);
+	    return;
+	}
+    } else {
+	f->rnakloops = 0;
+	if (f->callbacks->rejci == NULL
+	    || !(ret = f->callbacks->rejci(f, inp, len))) {
+	    error("Received bad configure-rej: %P", inp, len);
+	    return;
+	}
+    }
+
+    f->seen_ack = 1;
+
+    switch (f->state) {
+    case CLOSED:
+    case STOPPED:
+	fsm_sdata(f, TERMACK, id, NULL, 0);
+	break;
+
+    case REQSENT:
+    case ACKSENT:
+	/* They didn't agree to what we wanted - try another request */
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	if (ret < 0)
+	    f->state = STOPPED;		/* kludge for stopping CCP */
+	else
+	    fsm_sconfreq(f, 0);		/* Send Configure-Request */
+	break;
+
+    case ACKRCVD:
+	/* Got a Nak/reject when we had already had an Ack?? oh well... */
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	fsm_sconfreq(f, 0);
+	f->state = REQSENT;
+	break;
+
+    case OPENED:
+	/* Go down and restart negotiation */
+	if (f->callbacks->down)
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	fsm_sconfreq(f, 0);		/* Send initial Configure-Request */
+	f->state = REQSENT;
+	break;
+    }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(f, id, p, len)
+    fsm *f;
+    int id;
+    u_char *p;
+    int len;
+{
+    switch (f->state) {
+    case ACKRCVD:
+    case ACKSENT:
+	f->state = REQSENT;		/* Start over but keep trying */
+	break;
+
+    case OPENED:
+	if (len > 0) {
+	    warn("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
+	} else
+	    warn("%s terminated by peer", PROTO_NAME(f));
+	f->retransmits = 0;
+	f->state = STOPPING;
+	if (f->callbacks->down)
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	TIMEOUT(fsm_timeout, f, f->timeouttime);
+	break;
+    }
+
+    fsm_sdata(f, TERMACK, id, NULL, 0);
+	/*wangming added*/
+	if(f->protocol == PPP_LCP)
+	{
+		char c_id[2] = {0};
+		AT_PDP_DEACT_REQ_INFO deact_info = {0};
+		sleep(1);
+		//sc_cfg_set("pppd_user_term","terminate");
+		sc_cfg_get("ppp_cid",c_id, sizeof(c_id));
+		deact_info.c_id = atoi(c_id);
+        
+		ipc_send_message(MODULE_ID_PPPD, MODULE_ID_AT_CTL, MSG_CMD_PDP_DEACT_REQ, sizeof(deact_info), &deact_info, 0);
+		warn("pppd: fsm_rtermreq erase the NV value %s---->%d", __FILE__, __LINE__);
+		sc_cfg_set("lcp_finish","");
+		sc_cfg_set("ip_ok","");
+	}
+}
+
+
+/*
+ * 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);
+
+	if(f->protocol == PPP_LCP)
+	{
+
+		sleep(1);
+		ipc_send_message(MODULE_ID_PPPD, MODULE_ID_AT_CTL, MSG_CMD_PPP_QUICK_DISCONNECT, 0, 0, 0);
+		warn("pppd: receive term ack erase the NV value %s---->%d", __FILE__, __LINE__);
+		sc_cfg_set("lcp_finish","");
+		sc_cfg_set("ip_ok","");
+	}
+	
+	
+	break;
+    case STOPPING:
+	UNTIMEOUT(fsm_timeout, f);
+	f->state = STOPPED;
+	if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
+	break;
+
+    case ACKRCVD:
+	f->state = REQSENT;
+	break;
+
+    case OPENED:
+	if (f->callbacks->down)
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	fsm_sconfreq(f, 0);
+	f->state = REQSENT;
+	break;
+    }
+
+	
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void
+fsm_rcoderej(f, inp, len)
+    fsm *f;
+    u_char *inp;
+    int len;
+{
+    u_char code, id;
+
+    if (len < HEADERLEN) {
+	FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
+	return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
+
+    if( f->state == ACKRCVD )
+	f->state = REQSENT;
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case CLOSING:
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	/* fall through */
+    case CLOSED:
+	f->state = CLOSED;
+	if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
+	break;
+
+    case STOPPING:
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	/* fall through */
+    case STOPPED:
+	f->state = STOPPED;
+	if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
+	break;
+
+    case OPENED:
+	terminate_layer(f, STOPPING);
+	break;
+
+    default:
+	FSMDEBUG(("%s: Protocol-reject event in state %d!",
+		  PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(f, retransmit)
+    fsm *f;
+    int retransmit;
+{
+    u_char *outp;
+    int cilen;
+
+    if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
+	/* Not currently negotiating - reset options */
+	if( f->callbacks->resetci )
+	    (*f->callbacks->resetci)(f);
+	f->nakloops = 0;
+	f->rnakloops = 0;
+    }
+
+    if( !retransmit ){
+	/* New request - reset retransmission counter, use new ID */
+	f->retransmits = f->maxconfreqtransmits;
+	f->reqid = ++f->id;
+    }
+
+    f->seen_ack = 0;
+
+    /*
+     * Make up the request packet
+     */
+    outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
+    if( f->callbacks->cilen && f->callbacks->addci ){
+	cilen = (*f->callbacks->cilen)(f);
+	if( cilen > peer_mru[f->unit] - HEADERLEN )
+	    cilen = peer_mru[f->unit] - HEADERLEN;
+	if (f->callbacks->addci)
+	    (*f->callbacks->addci)(f, outp, &cilen);
+    } else
+	cilen = 0;
+
+    /* send the request to our peer */
+    fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
+
+    /* start the retransmit timer */
+    --f->retransmits;
+    TIMEOUT(fsm_timeout, f, f->timeouttime);
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata(f, code, id, data, datalen)
+    fsm *f;
+    u_char code, id;
+    u_char *data;
+    int datalen;
+{
+    u_char *outp;
+    int outlen;
+
+    /* Adjust length to be smaller than MTU */
+    outp = outpacket_buf;
+    if (datalen > peer_mru[f->unit] - HEADERLEN)
+	datalen = peer_mru[f->unit] - HEADERLEN;
+    if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
+	BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
+    outlen = datalen + HEADERLEN;
+    MAKEHEADER(outp, f->protocol);
+    PUTCHAR(code, outp);
+    PUTCHAR(id, outp);
+    PUTSHORT(outlen, outp);
+	char lcp_finish[100] = {0};
+	//sc_cfg_set(lcp_finish,"lcp_finish");
+	sc_cfg_get("lcp_finish",lcp_finish,sizeof(lcp_finish));
+
+	char ip_ok[100] = {0};
+	//sc_cfg_set(lcp_finish,"lcp_finish");
+	sc_cfg_get("ip_ok",ip_ok,sizeof(ip_ok));
+	if(strcmp(lcp_finish, "lcp_finish") != 0)
+	{
+		warn("noauch, need seed out!");
+		output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
+	}
+	else
+	{
+		/*here need to check the true PDP result*/
+		if(strcmp(ip_ok, "ip_ok") == 0)
+		{
+			warn("already auth, send out data");
+			output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
+		}
+		else
+		{
+			warn("already auth, but need atserver have ip");
+			ipcp_options *go = &ipcp_gotoptions[f->unit];
+			ipcp_options *wo = &ipcp_wantoptions[f->unit];
+    		ipcp_options *ao = &ipcp_allowoptions[f->unit];
+
+			char nv_pswan[NV_NAME_LEN] = {0};
+			char nv_ppp[NV_NAME_LEN] = {0};
+			char nv_ppp_cid[NV_NAME_LEN] = {0};
+    		char nv_ip[NV_NAME_LEN] = {0};
+			
+			char nv_ppp_ip[NV_NAME_LEN] = {0};
+			
+			char nv_wan_ip[ZTE_ROUTER_IP_ADDR_LEN] = {0};
+			char nv_pri_dns[ZTE_ROUTER_IP_ADDR_LEN] = {0};
+			char nv_sec_dns[ZTE_ROUTER_IP_ADDR_LEN] = {0};
+			
+    		sc_cfg_get("pswan", nv_pswan, sizeof(nv_pswan));
+			sc_cfg_get("ppp_cid", nv_ppp_cid, sizeof(nv_ppp_cid));
+			
+			sprintf(nv_ip,"%s%s_ip",nv_pswan, nv_ppp_cid);/*Ŀǰ¹Ì¶¨1Óû§pdp¼¤»î*/
+			sc_cfg_get(nv_ip, nv_wan_ip, sizeof(nv_wan_ip));
+
+			sprintf(nv_ip,"%s%s_pridns", nv_pswan,nv_ppp_cid);
+			sc_cfg_get(nv_ip, nv_pri_dns, sizeof(nv_pri_dns));
+			
+			sprintf(nv_ip,"%s%s_secdns", nv_pswan,nv_ppp_cid);
+			sc_cfg_get(nv_ip, nv_sec_dns, sizeof(nv_sec_dns));
+
+			
+			sc_cfg_get("ppp_name", nv_ppp, sizeof(nv_ppp));
+    		sprintf(nv_ip,"%s_ip", nv_ppp);
+    		sc_cfg_get(nv_ip, nv_ppp_ip, sizeof(nv_ppp_ip));
+
+			warn("pppd:  fsm_sdata %s---->%d,%s,%s,%s,%s", __FILE__, __LINE__, nv_wan_ip,nv_ppp_ip,nv_pri_dns,nv_sec_dns);
+    		
+			if (0 != strcmp(nv_wan_ip, "0.0.0.0") && (0 != strcmp(nv_wan_ip, ""))
+				&& 0 != strcmp(nv_ppp_ip, "0.0.0.0") && (0 != strcmp(nv_ppp_ip, "")))
+    		{
+    			wo->hisaddr 	= inet_addr(nv_wan_ip);
+				ao->dnsaddr[0] 	= inet_addr(nv_pri_dns);
+				ao->dnsaddr[1] 	= inet_addr(nv_sec_dns);
+				go->ouraddr 	= inet_addr(nv_ppp_ip);
+				warn("pppd: %s---->%d,ip_ok %x", __FILE__, __LINE__, go->ouraddr);
+		
+				sc_cfg_set("ip_ok","ip_ok");
+			}
+
+			ipv6cp_options *wo6 = &ipv6cp_wantoptions[f->unit];
+			char nv_ip6[NV_NAME_LEN] = {0};
+			char nv_wan_ip6[64] = {0};
+			struct in6_addr addr = {0};
+			
+			sprintf(nv_ip6,"%s%s_pppv6_ip", nv_pswan, nv_ppp_cid);/*Ŀǰ¹Ì¶¨1Óû§pdp¼¤»î*/
+			sc_cfg_get(nv_ip6, nv_wan_ip6, sizeof(nv_wan_ip6));
+			warn("pppd: %s---->%d,v6=%s", __FILE__, __LINE__, nv_wan_ip6);
+			if (inet_pton(AF_INET6, nv_wan_ip6, &addr) == 0) {
+				warn("pppd: v6 fail %x:%x", __FILE__, __LINE__, addr.s6_addr32[2],addr.s6_addr32[3]);
+			} else {
+				eui64_copy(addr.s6_addr32[2], wo6->hisid);
+				wo6->opt_remote = 1;
+				sc_cfg_set("ip_ok","ip_ok");
+			}
+		}
+	}
+}
diff --git a/ap/app/pppd/pppd/fsm.h b/ap/app/pppd/pppd/fsm.h
new file mode 100644
index 0000000..c0d46d9
--- /dev/null
+++ b/ap/app/pppd/pppd/fsm.h
@@ -0,0 +1,168 @@
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: fsm.h,v 1.2 2007-06-08 04:02:38 gerg Exp $
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define HEADERLEN	4
+
+
+/*
+ *  CP (LCP, IPCP, etc.) codes.
+ */
+#define CONFREQ		1	/* Configuration Request */
+#define CONFACK		2	/* Configuration Ack */
+#define CONFNAK		3	/* Configuration Nak */
+#define CONFREJ		4	/* Configuration Reject */
+#define TERMREQ		5	/* Termination Request */
+#define TERMACK		6	/* Termination Ack */
+#define CODEREJ		7	/* Code Reject */
+
+
+/*
+ * Each FSM is described by an fsm structure and fsm callbacks.
+ */
+typedef struct fsm {
+    int unit;			/* Interface unit number */
+    int protocol;		/* Data Link Layer Protocol field value */
+    int state;			/* State */
+    int flags;			/* Contains option bits */
+    u_char id;			/* Current id */
+    u_char reqid;		/* Current request id */
+    u_char seen_ack;		/* Have received valid Ack/Nak/Rej to Req */
+    int timeouttime;		/* Timeout time in milliseconds */
+    int maxconfreqtransmits;	/* Maximum Configure-Request transmissions */
+    int retransmits;		/* Number of retransmissions left */
+    int maxtermtransmits;	/* Maximum Terminate-Request transmissions */
+    int nakloops;		/* Number of nak loops since last ack */
+    int rnakloops;		/* Number of naks received */
+    int maxnakloops;		/* Maximum number of nak loops tolerated */
+    struct fsm_callbacks *callbacks;	/* Callback routines */
+    char *term_reason;		/* Reason for closing protocol */
+    int term_reason_len;	/* Length of term_reason */
+} fsm;
+
+
+typedef struct fsm_callbacks {
+    void (*resetci)		/* Reset our Configuration Information */
+		__P((fsm *));
+    int  (*cilen)		/* Length of our Configuration Information */
+		__P((fsm *));
+    void (*addci) 		/* Add our Configuration Information */
+		__P((fsm *, u_char *, int *));
+    int  (*ackci)		/* ACK our Configuration Information */
+		__P((fsm *, u_char *, int));
+    int  (*nakci)		/* NAK our Configuration Information */
+		__P((fsm *, u_char *, int, int));
+    int  (*rejci)		/* Reject our Configuration Information */
+		__P((fsm *, u_char *, int));
+    int  (*reqci)		/* Request peer's Configuration Information */
+		__P((fsm *, u_char *, int *, int));
+    void (*up)			/* Called when fsm reaches OPENED state */
+		__P((fsm *));
+    void (*down)		/* Called when fsm leaves OPENED state */
+		__P((fsm *));
+    void (*starting)		/* Called when we want the lower layer */
+		__P((fsm *));
+    void (*finished)		/* Called when we don't want the lower layer */
+		__P((fsm *));
+    void (*protreject)		/* Called when Protocol-Reject received */
+		__P((int));
+    void (*retransmit)		/* Retransmission is necessary */
+		__P((fsm *));
+    int  (*extcode)		/* Called when unknown code received */
+		__P((fsm *, int, int, u_char *, int));
+    char *proto_name;		/* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+/*
+ * Link states.
+ */
+#define INITIAL		0	/* Down, hasn't been opened */
+#define STARTING	1	/* Down, been opened */
+#define CLOSED		2	/* Up, hasn't been opened */
+#define STOPPED		3	/* Open, waiting for down event */
+#define CLOSING		4	/* Terminating the connection, not open */
+#define STOPPING	5	/* Terminating, but open */
+#define REQSENT		6	/* We've sent a Config Request */
+#define ACKRCVD		7	/* We've received a Config Ack */
+#define ACKSENT		8	/* We've sent a Config Ack */
+#define OPENED		9	/* Connection available */
+
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE	1	/* Don't die if we don't get a response */
+#define OPT_RESTART	2	/* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT	4	/* Wait for peer to speak first */
+
+
+/*
+ * Timeouts.
+ */
+#define DEFTIMEOUT	3	/* Timeout time in seconds */
+#define DEFMAXTERMREQS	2	/* Maximum Terminate-Request transmissions */
+#define DEFMAXCONFREQS	10	/* Maximum Configure-Request transmissions */
+#define DEFMAXNAKLOOPS	5	/* Maximum number of nak loops */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init __P((fsm *));
+void fsm_lowerup __P((fsm *));
+void fsm_lowerdown __P((fsm *));
+void fsm_open __P((fsm *));
+void fsm_close __P((fsm *, char *));
+void fsm_input __P((fsm *, u_char *, int));
+void fsm_protreject __P((fsm *));
+void fsm_sdata __P((fsm *, int, int, u_char *, int));
+
+
+/*
+ * Variables
+ */
+extern int peer_mru[];		/* currently negotiated peer MRU (per unit) */
diff --git a/ap/app/pppd/pppd/ipcp.c b/ap/app/pppd/pppd/ipcp.c
new file mode 100644
index 0000000..25bd19a
--- /dev/null
+++ b/ap/app/pppd/pppd/ipcp.c
@@ -0,0 +1,2367 @@
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: ipcp.c,v 1.73 2008/05/26 08:33:22 paulus Exp $"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "pathnames.h"
+#define ZTE_ROUTER_IP_ADDR_LEN 32
+static const char rcsid[] = RCSID;
+
+/* global vars */
+ipcp_options ipcp_wantoptions[NUM_PPP];	/* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP];	/* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP];	/* Options that we ack'd */
+
+u_int32_t netmask = 0;		/* IP netmask to set on interface */
+
+bool	disable_defaultip = 0;	/* Don't use hostname for default IP adrs */
+bool	noremoteip = 0;		/* Let him have no IP address */
+
+/* Hook for a plugin to know when IP protocol has come up */
+void (*ip_up_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to know when IP protocol has come down */
+void (*ip_down_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to choose the remote IP address */
+void (*ip_choose_hook) __P((u_int32_t *)) = NULL;
+
+/* Notifiers for when IPCP goes up and down */
+struct notifier *ip_up_notifier = NULL;
+struct notifier *ip_down_notifier = NULL;
+
+/* local vars */
+static int default_route_set[NUM_PPP];	/* Have set up a default route */
+static int proxy_arp_set[NUM_PPP];	/* Have created proxy arp entry */
+static bool usepeerdns;			/* Ask peer for DNS addrs */
+static int ipcp_is_up;			/* have called np_up() */
+static int ipcp_is_open;		/* haven't called np_finished() */
+static bool ask_for_local;		/* request our address from peer */
+static char vj_value[8];		/* string form of vj option value */
+static char netmask_str[20];		/* string form of netmask value */
+
+/*
+ * Callbacks for fsm code.  (CI = Configuration Information)
+ */
+static void ipcp_resetci __P((fsm *));	/* Reset our CI */
+static int  ipcp_cilen __P((fsm *));	        /* Return length of our CI */
+static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int  ipcp_ackci __P((fsm *, u_char *, int));	/* Peer ack'd our CI */
+static int  ipcp_nakci __P((fsm *, u_char *, int, int));/* Peer nak'd our CI */
+static int  ipcp_rejci __P((fsm *, u_char *, int));	/* Peer rej'd our CI */
+static int  ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipcp_up __P((fsm *));		/* We're UP */
+static void ipcp_down __P((fsm *));		/* We're DOWN */
+static void ipcp_finished __P((fsm *));	/* Don't need lower layer */
+
+fsm ipcp_fsm[NUM_PPP];		/* IPCP fsm structure */
+
+static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+    ipcp_resetci,		/* Reset our Configuration Information */
+    ipcp_cilen,			/* Length of our Configuration Information */
+    ipcp_addci,			/* Add our Configuration Information */
+    ipcp_ackci,			/* ACK our Configuration Information */
+    ipcp_nakci,			/* NAK our Configuration Information */
+    ipcp_rejci,			/* Reject our Configuration Information */
+    ipcp_reqci,			/* Request peer's Configuration Information */
+    ipcp_up,			/* Called when fsm reaches OPENED state */
+    ipcp_down,			/* Called when fsm leaves OPENED state */
+    NULL,			/* Called when we want the lower layer up */
+    ipcp_finished,		/* Called when we want the lower layer down */
+    NULL,			/* Called when Protocol-Reject received */
+    NULL,			/* Retransmission is necessary */
+    NULL,			/* Called to handle protocol-specific codes */
+    "IPCP"			/* String name of protocol */
+};
+
+/*
+ * Command-line options.
+ */
+static int setvjslots __P((char **));
+static int setdnsaddr __P((char **));
+static int setwinsaddr __P((char **));
+static int setnetmask __P((char **));
+int setipaddr __P((char *, char **, int));
+static void printipaddr __P((option_t *, void (*)(void *, char *,...),void *));
+
+static option_t ipcp_option_list[] = {
+    { "noip", o_bool, &ipcp_protent.enabled_flag,
+      "Disable IP and IPCP" },
+    { "-ip", o_bool, &ipcp_protent.enabled_flag,
+      "Disable IP and IPCP", OPT_ALIAS },
+
+    { "novj", o_bool, &ipcp_wantoptions[0].neg_vj,
+      "Disable VJ compression", OPT_A2CLR, &ipcp_allowoptions[0].neg_vj },
+    { "-vj", o_bool, &ipcp_wantoptions[0].neg_vj,
+      "Disable VJ compression", OPT_ALIAS | OPT_A2CLR,
+      &ipcp_allowoptions[0].neg_vj },
+
+    { "novjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+      "Disable VJ connection-ID compression", OPT_A2CLR,
+      &ipcp_allowoptions[0].cflag },
+    { "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+      "Disable VJ connection-ID compression", OPT_ALIAS | OPT_A2CLR,
+      &ipcp_allowoptions[0].cflag },
+
+    { "vj-max-slots", o_special, (void *)setvjslots,
+      "Set maximum VJ header slots",
+      OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, vj_value },
+
+    { "ipcp-accept-local", o_bool, &ipcp_wantoptions[0].accept_local,
+      "Accept peer's address for us", 1 },
+    { "ipcp-accept-remote", o_bool, &ipcp_wantoptions[0].accept_remote,
+      "Accept peer's address for it", 1 },
+
+    { "ip-up", o_string, &ip_up,
+      "Set ip-up script name", OPT_PRIV },
+    { "ip-down", o_string, &ip_down,
+      "Set ip-down script name", OPT_PRIV },
+
+    { "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 },
+
+    { "forcedefaultroute", o_bool, &ipcp_wantoptions[0].force_default_route,
+      "Always add default route", OPT_ENABLE|1, &ipcp_allowoptions[0].force_default_route },
+    { "noforcedefaultroute", o_bool, &ipcp_allowoptions[0].force_default_route,
+      "disable forcedefaultroute option", OPT_A2CLR,
+      &ipcp_wantoptions[0].force_default_route },
+
+    { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp,
+      "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp },
+    { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+      "disable proxyarp option", OPT_A2CLR,
+      &ipcp_wantoptions[0].proxy_arp },
+    { "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+      "disable proxyarp option", OPT_ALIAS | OPT_A2CLR,
+      &ipcp_wantoptions[0].proxy_arp },
+
+    { "usepeerdns", o_bool, &usepeerdns,
+      "Ask peer for DNS address(es)", 1 },
+
+    { "netmask", o_special, (void *)setnetmask,
+      "set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str },
+
+    { "ipcp-no-addresses", o_bool, &ipcp_wantoptions[0].old_addrs,
+      "Disable old-style IP-Addresses usage", OPT_A2CLR,
+      &ipcp_allowoptions[0].old_addrs },
+    { "ipcp-no-address", o_bool, &ipcp_wantoptions[0].neg_addr,
+      "Disable IP-Address usage", OPT_A2CLR,
+      &ipcp_allowoptions[0].neg_addr },
+#ifdef __linux__
+    { "noremoteip", o_bool, &noremoteip,
+      "Allow peer to have no IP address", 1 },
+#endif
+    { "nosendip", o_bool, &ipcp_wantoptions[0].neg_addr,
+      "Don't send our IP address to peer", OPT_A2CLR,
+      &ipcp_wantoptions[0].old_addrs},
+
+    { "IP addresses", o_wild, (void *) &setipaddr,
+      "set local and remote IP addresses",
+      OPT_NOARG | OPT_A2PRINTER, (void *) &printipaddr },
+
+    { NULL }
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init __P((int));
+static void ipcp_open __P((int));
+static void ipcp_close __P((int, char *));
+static void ipcp_lowerup __P((int));
+static void ipcp_lowerdown __P((int));
+static void ipcp_input __P((int, u_char *, int));
+static void ipcp_protrej __P((int));
+static int  ipcp_printpkt __P((u_char *, int,
+			       void (*) __P((void *, char *, ...)), void *));
+static void ip_check_options __P((void));
+static int  ip_demand_conf __P((int));
+static int  ip_active_pkt __P((u_char *, int));
+static void create_resolv __P((u_int32_t, u_int32_t));
+
+struct protent ipcp_protent = {
+    PPP_IPCP,
+    ipcp_init,
+    ipcp_input,
+    ipcp_protrej,
+    ipcp_lowerup,
+    ipcp_lowerdown,
+    ipcp_open,
+    ipcp_close,
+    ipcp_printpkt,
+    NULL,
+    1,
+    "IPCP",
+    "IP",
+    ipcp_option_list,
+    ip_check_options,
+    ip_demand_conf,
+    ip_active_pkt
+};
+
+static void ipcp_clear_addrs __P((int, u_int32_t, u_int32_t));
+static void ipcp_script_up __P((void));
+static void ipcp_script_down __P((void));
+static void ipcp_script __P((char *, int));	/* Run an up/down script */
+static void ipcp_script_done __P((void *));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID	2
+#define CILEN_COMPRESS	4	/* min length for compression protocol opt. */
+#define CILEN_VJ	6	/* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR	6	/* new-style single address option */
+#define CILEN_ADDRS	10	/* old-style dual address option */
+
+
+#define CODENAME(x)	((x) == CONFACK ? "ACK" : \
+			 (x) == CONFNAK ? "NAK" : "REJ")
+
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+    s_down,
+    s_up,
+} ipcp_script_state;
+static pid_t ipcp_script_pid;
+
+/*
+ * Make a string representation of a network IP address.
+ */
+char *
+ip_ntoa(ipaddr)
+u_int32_t ipaddr;
+{
+    static char b[64];
+
+    slprintf(b, sizeof(b), "%I", ipaddr);
+    return b;
+}
+
+/*
+ * Option parsing.
+ */
+
+/*
+ * setvjslots - set maximum number of connection slots for VJ compression
+ */
+static int
+setvjslots(argv)
+    char **argv;
+{
+    int value;
+
+    if (!int_option(*argv, &value))
+	return 0;
+    if (value < 2 || value > 16) {
+	option_error("vj-max-slots value must be between 2 and 16");
+	return 0;
+    }
+    ipcp_wantoptions [0].maxslotindex =
+        ipcp_allowoptions[0].maxslotindex = value - 1;
+    slprintf(vj_value, sizeof(vj_value), "%d", value);
+    return 1;
+}
+
+/*
+ * setdnsaddr - set the dns address(es)
+ */
+static int
+setdnsaddr(argv)
+    char **argv;
+{
+    u_int32_t dns;
+    struct hostent *hp;
+
+    dns = inet_addr(*argv);
+	//dns = 0x0A0B0C0D;
+	#if 1
+	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;
+    }
+	#endif
+    /* We take the last 2 values given, the 2nd-last as the primary
+       and the last as the secondary.  If only one is given it
+       becomes both primary and secondary. */
+    if (ipcp_allowoptions[0].dnsaddr[1] == 0)
+	ipcp_allowoptions[0].dnsaddr[0] = dns;
+    else
+	ipcp_allowoptions[0].dnsaddr[0] = ipcp_allowoptions[0].dnsaddr[1];
+
+    /* always set the secondary address value. */
+    ipcp_allowoptions[0].dnsaddr[1] = dns;
+
+    return (1);
+}
+
+/*
+ * setwinsaddr - set the wins address(es)
+ * This is primrarly used with the Samba package under UNIX or for pointing
+ * the caller to the existing WINS server on a Windows NT platform.
+ */
+static int
+setwinsaddr(argv)
+    char **argv;
+{
+    u_int32_t wins;
+    struct hostent *hp;
+
+    wins = inet_addr(*argv);
+    if (wins == (u_int32_t) -1) {
+	if ((hp = gethostbyname(*argv)) == NULL) {
+	    option_error("invalid address parameter '%s' for ms-wins option",
+			 *argv);
+	    return 0;
+	}
+	wins = *(u_int32_t *)hp->h_addr;
+    }
+
+    /* We take the last 2 values given, the 2nd-last as the primary
+       and the last as the secondary.  If only one is given it
+       becomes both primary and secondary. */
+    if (ipcp_allowoptions[0].winsaddr[1] == 0)
+	ipcp_allowoptions[0].winsaddr[0] = wins;
+    else
+	ipcp_allowoptions[0].winsaddr[0] = ipcp_allowoptions[0].winsaddr[1];
+
+    /* always set the secondary address value. */
+    ipcp_allowoptions[0].winsaddr[1] = wins;
+
+    return (1);
+}
+
+/*
+ * setipaddr - Set the IP address
+ * If doit is 0, the call is to check whether this option is
+ * potentially an IP address specification.
+ * Not static so that plugins can call it to set the addresses
+ */
+int
+setipaddr(arg, argv, doit)
+    char *arg;
+    char **argv;
+    int doit;
+{
+    struct hostent *hp;
+    char *colon;
+    u_int32_t local, remote;
+    ipcp_options *wo = &ipcp_wantoptions[0];
+    static int prio_local = 0, prio_remote = 0;
+
+    /*
+     * IP address pair separated by ":".
+     */
+    if ((colon = strchr(arg, ':')) == NULL)
+	return 0;
+    if (!doit)
+	return 1;
+  
+    /*
+     * If colon first character, then no local addr.
+     */
+    if (colon != arg && option_priority >= prio_local) {
+	*colon = '\0';
+	if ((local = inet_addr(arg)) == (u_int32_t) -1) {
+	    if ((hp = gethostbyname(arg)) == NULL) {
+		option_error("unknown host: %s", arg);
+		return 0;
+	    }
+	    local = *(u_int32_t *)hp->h_addr;
+	}
+	if (bad_ip_adrs(local)) {
+	    option_error("bad local IP address %s", ip_ntoa(local));
+	    return 0;
+	}
+	if (local != 0)
+	    wo->ouraddr = local;
+	*colon = ':';
+	prio_local = option_priority;
+    }
+  
+    /*
+     * If colon last character, then no remote addr.
+     */
+    if (*++colon != '\0' && option_priority >= prio_remote) {
+	if ((remote = inet_addr(colon)) == (u_int32_t) -1) {
+	    if ((hp = gethostbyname(colon)) == NULL) {
+		option_error("unknown host: %s", colon);
+		return 0;
+	    }
+	    remote = *(u_int32_t *)hp->h_addr;
+	    if (remote_name[0] == 0)
+		strlcpy(remote_name, colon, sizeof(remote_name));
+	}
+	if (bad_ip_adrs(remote)) {
+	    option_error("bad remote IP address %s", ip_ntoa(remote));
+	    return 0;
+	}
+	if (remote != 0)
+	    wo->hisaddr = remote;
+	prio_remote = option_priority;
+    }
+
+    return 1;
+}
+
+static void
+printipaddr(opt, printer, arg)
+    option_t *opt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	ipcp_options *wo = &ipcp_wantoptions[0];
+
+	if (wo->ouraddr != 0)
+		printer(arg, "%I", wo->ouraddr);
+	printer(arg, ":");
+	if (wo->hisaddr != 0)
+		printer(arg, "%I", wo->hisaddr);
+}
+
+/*
+ * setnetmask - set the netmask to be used on the interface.
+ */
+static int
+setnetmask(argv)
+    char **argv;
+{
+    u_int32_t mask;
+    int n;
+    char *p;
+
+    /*
+     * Unfortunately, if we use inet_addr, we can't tell whether
+     * a result of all 1s is an error or a valid 255.255.255.255.
+     */
+    p = *argv;
+    n = parse_dotted_ip(p, &mask);
+
+    mask = htonl(mask);
+
+    if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
+	option_error("invalid netmask value '%s'", *argv);
+	return 0;
+    }
+
+    netmask = mask;
+    slprintf(netmask_str, sizeof(netmask_str), "%I", mask);
+
+    return (1);
+}
+
+int
+parse_dotted_ip(p, vp)
+    char *p;
+    u_int32_t *vp;
+{
+    int n;
+    u_int32_t v, b;
+    char *endp, *p0 = p;
+
+    v = 0;
+    for (n = 3;; --n) {
+	b = strtoul(p, &endp, 0);
+	if (endp == p)
+	    return 0;
+	if (b > 255) {
+	    if (n < 3)
+		return 0;
+	    /* accept e.g. 0xffffff00 */
+	    *vp = b;
+	    return endp - p0;
+	}
+	v |= b << (n * 8);
+	p = endp;
+	if (n == 0)
+	    break;
+	if (*p != '.')
+	    return 0;
+	++p;
+    }
+    *vp = v;
+    return p - p0;
+}
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void
+ipcp_init(unit)
+    int unit;
+{
+    fsm *f = &ipcp_fsm[unit];
+    ipcp_options *wo = &ipcp_wantoptions[unit];
+    ipcp_options *ao = &ipcp_allowoptions[unit];
+
+    f->unit = unit;
+    f->protocol = PPP_IPCP;
+    f->callbacks = &ipcp_callbacks;
+    fsm_init(&ipcp_fsm[unit]);
+
+    /*
+     * Some 3G modems use repeated IPCP NAKs as a way of stalling
+     * until they can contact a server on the network, so we increase
+     * the default number of NAKs we accept before we start treating
+     * them as rejects.
+     */
+    f->maxnakloops = 100;
+
+    memset(wo, 0, sizeof(*wo));
+    memset(ao, 0, sizeof(*ao));
+
+    wo->neg_addr = wo->old_addrs = 1;
+    wo->neg_vj = 1;
+    wo->vj_protocol = IPCP_VJ_COMP;
+    wo->maxslotindex = MAX_STATES - 1; /* really max index */
+    wo->cflag = 1;
+
+
+    /* max slots and slot-id compression are currently hardwired in */
+    /* ppp_if.c to 16 and 1, this needs to be changed (among other */
+    /* things) gmc */
+
+    ao->neg_addr = ao->old_addrs = 1;
+    ao->neg_vj = 1;
+    ao->maxslotindex = MAX_STATES - 1;
+    ao->cflag = 1;
+
+    /*
+     * XXX These control whether the user may use the proxyarp
+     * and defaultroute options.
+     */
+    ao->proxy_arp = 1;
+    ao->default_route = 1;
+    ao->force_default_route = 1;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void
+ipcp_open(unit)
+    int unit;
+{
+    fsm_open(&ipcp_fsm[unit]);
+    ipcp_is_open = 1;
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void
+ipcp_close(unit, reason)
+    int unit;
+    char *reason;
+{
+    fsm_close(&ipcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void
+ipcp_lowerup(unit)
+    int unit;
+{
+    fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void
+ipcp_lowerdown(unit)
+    int unit;
+{
+    fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void
+ipcp_input(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipcp_protrej(unit)
+    int unit;
+{
+    fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void
+ipcp_resetci(f)
+    fsm *f;
+{
+    ipcp_options *wo = &ipcp_wantoptions[f->unit];
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    ipcp_options *ao = &ipcp_allowoptions[f->unit];
+	u_int32_t test;
+    wo->req_addr = (wo->neg_addr || wo->old_addrs) &&
+	(ao->neg_addr || ao->old_addrs);
+    if (wo->ouraddr == 0)
+	wo->accept_local = 1;
+    if (wo->hisaddr == 0)
+	wo->accept_remote = 1;
+    wo->req_dns1 = usepeerdns;	/* Request DNS addresses from the peer */
+    wo->req_dns2 = usepeerdns;
+    *go = *wo;
+    if (!ask_for_local)
+    	{
+	go->ouraddr = 0;
+	warn("pppd: %s---->%d,%d", __FILE__, __LINE__, go->ouraddr);
+}
+    if (ip_choose_hook) {
+	ip_choose_hook(&wo->hisaddr);
+	if (wo->hisaddr) {
+	    wo->accept_remote = 0;
+	}
+    }
+	
+    BZERO(&ipcp_hisoptions[f->unit], sizeof(ipcp_options));
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static int
+ipcp_cilen(f)
+    fsm *f;
+{
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    ipcp_options *wo = &ipcp_wantoptions[f->unit];
+    ipcp_options *ho = &ipcp_hisoptions[f->unit];
+
+#define LENCIADDRS(neg)		(neg ? CILEN_ADDRS : 0)
+#define LENCIVJ(neg, old)	(neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg)		(neg ? CILEN_ADDR : 0)
+#define LENCIDNS(neg)		LENCIADDR(neg)
+#define LENCIWINS(neg)		LENCIADDR(neg)
+
+    /*
+     * First see if we want to change our options to the old
+     * forms because we have received old forms from the peer.
+     */
+    if (go->neg_addr && go->old_addrs && !ho->neg_addr && ho->old_addrs)
+	go->neg_addr = 0;
+    if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+	/* try an older style of VJ negotiation */
+	/* use the old style only if the peer did */
+	if (ho->neg_vj && ho->old_vj) {
+	    go->neg_vj = 1;
+	    go->old_vj = 1;
+	    go->vj_protocol = ho->vj_protocol;
+	}
+    }
+
+    return (LENCIADDRS(!go->neg_addr && go->old_addrs) +
+	    LENCIVJ(go->neg_vj, go->old_vj) +
+	    LENCIADDR(go->neg_addr) +
+	    LENCIDNS(go->req_dns1) +
+	    LENCIDNS(go->req_dns2) +
+	    LENCIWINS(go->winsaddr[0]) +
+	    LENCIWINS(go->winsaddr[1])) ;
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void
+ipcp_addci(f, ucp, lenp)
+    fsm *f;
+    u_char *ucp;
+    int *lenp;
+{
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    int len = *lenp;
+#define ADDCIADDRS(opt, neg, val1, val2) \
+    if (neg) { \
+	if (len >= CILEN_ADDRS) { \
+	    u_int32_t l; \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_ADDRS, ucp); \
+	    l = ntohl(val1); \
+	    PUTLONG(l, ucp); \
+	    l = ntohl(val2); \
+	    PUTLONG(l, ucp); \
+	    len -= CILEN_ADDRS; \
+	} else \
+	    go->old_addrs = 0; \
+    }
+
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+    if (neg) { \
+	int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+	if (len >= vjlen) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(vjlen, ucp); \
+	    PUTSHORT(val, ucp); \
+	    if (!old) { \
+		PUTCHAR(maxslotindex, ucp); \
+		PUTCHAR(cflag, ucp); \
+	    } \
+	    len -= vjlen; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCIADDR(opt, neg, val) \
+    if (neg) { \
+	if (len >= CILEN_ADDR) { \
+	    u_int32_t l; \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_ADDR, ucp); \
+	    l = ntohl(val); \
+	    PUTLONG(l, ucp); \
+	    len -= CILEN_ADDR; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCIDNS(opt, neg, addr) \
+    if (neg) { \
+	if (len >= CILEN_ADDR) { \
+	    u_int32_t l; \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_ADDR, ucp); \
+	    l = ntohl(addr); \
+	    PUTLONG(l, ucp); \
+	    len -= CILEN_ADDR; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCIWINS(opt, addr) \
+    if (addr) { \
+	if (len >= CILEN_ADDR) { \
+	    u_int32_t l; \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_ADDR, ucp); \
+	    l = ntohl(addr); \
+	    PUTLONG(l, ucp); \
+	    len -= CILEN_ADDR; \
+	} else \
+	    addr = 0; \
+    }
+
+    ADDCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
+	       go->hisaddr);
+
+    ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+	    go->maxslotindex, go->cflag);
+	warn("pppd: %s---->%d,%d", __FILE__, __LINE__, go->ouraddr);
+    ADDCIADDR(CI_ADDR, go->neg_addr, go->ouraddr);
+	
+    ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+	ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+	ADDCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+	ADDCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+    
+    *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ * Called by fsm_rconfack, Receive Configure ACK.
+ *
+ * Returns:
+ *	0 - Ack was bad.
+ *	1 - Ack was good.
+ */
+static int
+ipcp_ackci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    u_short cilen, citype, cishort;
+    u_int32_t cilong;
+    u_char cimaxslotindex, cicflag;
+
+    /*
+     * CIs must be in exactly the same order that we sent...
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+
+#define ACKCIADDRS(opt, neg, val1, val2) \
+    if (neg) { \
+	u_int32_t l; \
+	if ((len -= CILEN_ADDRS) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_ADDRS || \
+	    citype != opt) \
+	    goto bad; \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	if (val1 != cilong) \
+	    goto bad; \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	if (val2 != cilong) \
+	    goto bad; \
+    }
+
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+    if (neg) { \
+	int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+	if ((len -= vjlen) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != vjlen || \
+	    citype != opt)  \
+	    goto bad; \
+	GETSHORT(cishort, p); \
+	if (cishort != val) \
+	    goto bad; \
+	if (!old) { \
+	    GETCHAR(cimaxslotindex, p); \
+	    if (cimaxslotindex != maxslotindex) \
+		goto bad; \
+	    GETCHAR(cicflag, p); \
+	    if (cicflag != cflag) \
+		goto bad; \
+	} \
+    }
+
+#define ACKCIADDR(opt, neg, val) \
+    if (neg) { \
+	u_int32_t l; \
+	if ((len -= CILEN_ADDR) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_ADDR || \
+	    citype != opt) \
+	    goto bad; \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	if (val != cilong) \
+	    goto bad; \
+    }
+
+#define ACKCIDNS(opt, neg, addr) \
+    if (neg) { \
+	u_int32_t l; \
+	if ((len -= CILEN_ADDR) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_ADDR || citype != opt) \
+	    goto bad; \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	if (addr != cilong) \
+	    goto bad; \
+    }
+
+    ACKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
+	       go->hisaddr);
+
+    ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+	    go->maxslotindex, go->cflag);
+
+warn("pppd  ACKCIADDR: %s---->%d,%d", __FILE__, __LINE__, go->ouraddr);
+    ACKCIADDR(CI_ADDR, go->neg_addr, go->ouraddr);
+
+    ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+    ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+	goto bad;
+    return (1);
+
+bad:
+    IPCPDEBUG(("ipcp_ackci: received bad Ack!"));
+    return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the OPENED state.
+ * Calback from fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ *
+ * Returns:
+ *	0 - Nak was bad.
+ *	1 - Nak was good.
+ */
+static int
+ipcp_nakci(f, p, len, treat_as_reject)
+    fsm *f;
+    u_char *p;
+    int len;
+    int treat_as_reject;
+{
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    u_char cimaxslotindex, cicflag;
+    u_char citype, cilen, *next;
+    u_short cishort;
+    u_int32_t ciaddr1, ciaddr2, l, cidnsaddr;
+    ipcp_options no;		/* options we've seen Naks for */
+    ipcp_options try;		/* options to request next time */
+
+    BZERO(&no, sizeof(no));
+    try = *go;
+
+    /*
+     * Any Nak'd CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define NAKCIADDRS(opt, neg, code) \
+    if ((neg) && \
+	(cilen = p[1]) == CILEN_ADDRS && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETLONG(l, p); \
+	ciaddr1 = htonl(l); \
+	GETLONG(l, p); \
+	ciaddr2 = htonl(l); \
+	no.old_addrs = 1; \
+	code \
+    }
+
+#define NAKCIVJ(opt, neg, code) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	no.neg = 1; \
+        code \
+    }
+
+#define NAKCIADDR(opt, neg, code) \
+    if (go->neg && \
+	(cilen = p[1]) == CILEN_ADDR && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETLONG(l, p); \
+	ciaddr1 = htonl(l); \
+	no.neg = 1; \
+	code \
+    }
+
+#define NAKCIDNS(opt, neg, code) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_ADDR) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETLONG(l, p); \
+	cidnsaddr = htonl(l); \
+	no.neg = 1; \
+	code \
+    }
+
+    /*
+     * Accept the peer's idea of {our,his} address, if different
+     * from our idea, only if the accept_{local,remote} flag is set.
+     */
+    NAKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
+	       if (treat_as_reject) {
+		   try.old_addrs = 0;
+	       } else {
+		   if (go->accept_local && ciaddr1) {
+		       /* take his idea of our address */
+		       try.ouraddr = ciaddr1;
+		   }
+		   if (go->accept_remote && ciaddr2) {
+		       /* take his idea of his address */
+		       try.hisaddr = ciaddr2;
+		   }
+	       }
+	);
+
+    /*
+     * Accept the peer's value of maxslotindex provided that it
+     * is less than what we asked for.  Turn off slot-ID compression
+     * if the peer wants.  Send old-style compress-type option if
+     * the peer wants.
+     */
+    NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+	    if (treat_as_reject) {
+		try.neg_vj = 0;
+	    } else if (cilen == CILEN_VJ) {
+		GETCHAR(cimaxslotindex, p);
+		GETCHAR(cicflag, p);
+		if (cishort == IPCP_VJ_COMP) {
+		    try.old_vj = 0;
+		    if (cimaxslotindex < go->maxslotindex)
+			try.maxslotindex = cimaxslotindex;
+		    if (!cicflag)
+			try.cflag = 0;
+		} else {
+		    try.neg_vj = 0;
+		}
+	    } else {
+		if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+		    try.old_vj = 1;
+		    try.vj_protocol = cishort;
+		} else {
+		    try.neg_vj = 0;
+		}
+	    }
+	    );
+
+    NAKCIADDR(CI_ADDR, neg_addr,
+	      if (treat_as_reject) {
+		  try.neg_addr = 0;
+		  try.old_addrs = 0;
+	      } else if (go->accept_local && ciaddr1) {
+		  /* take his idea of our address */
+		  try.ouraddr = ciaddr1;
+	      }
+	      );
+
+    NAKCIDNS(CI_MS_DNS1, req_dns1,
+	     if (treat_as_reject) {
+		 try.req_dns1 = 0;
+	     } else {
+		 try.dnsaddr[0] = cidnsaddr;
+	     }
+	     );
+
+    NAKCIDNS(CI_MS_DNS2, req_dns2,
+	     if (treat_as_reject) {
+		 try.req_dns2 = 0;
+	     } else {
+		 try.dnsaddr[1] = cidnsaddr;
+	     }
+	     );
+
+    /*
+     * There may be remaining CIs, if the peer is requesting negotiation
+     * on an option that we didn't include in our request packet.
+     * If they want to negotiate about IP addresses, we comply.
+     * If they want us to ask for compression, we refuse.
+     * If they want us to ask for ms-dns, we do that, since some
+     * peers get huffy if we don't.
+     */
+    while (len >= CILEN_VOID) {
+	GETCHAR(citype, p);
+	GETCHAR(cilen, p);
+	if ( cilen < CILEN_VOID || (len -= cilen) < 0 )
+	    goto bad;
+	next = p + cilen - 2;
+
+	switch (citype) {
+	case CI_COMPRESSTYPE:
+	    if (go->neg_vj || no.neg_vj ||
+		(cilen != CILEN_VJ && cilen != CILEN_COMPRESS))
+		goto bad;
+	    no.neg_vj = 1;
+	    break;
+	case CI_ADDRS:
+	    if ((!go->neg_addr && go->old_addrs) || no.old_addrs
+		|| cilen != CILEN_ADDRS)
+		goto bad;
+	    try.neg_addr = 0;
+	    GETLONG(l, p);
+	    ciaddr1 = htonl(l);
+	    if (ciaddr1 && go->accept_local)
+		try.ouraddr = ciaddr1;
+	    GETLONG(l, p);
+	    ciaddr2 = htonl(l);
+	    if (ciaddr2 && go->accept_remote)
+		try.hisaddr = ciaddr2;
+	    no.old_addrs = 1;
+	    break;
+	case CI_ADDR:
+	    if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR)
+		goto bad;
+	    try.old_addrs = 0;
+	    GETLONG(l, p);
+	    ciaddr1 = htonl(l);
+	    if (ciaddr1 && go->accept_local)
+		try.ouraddr = ciaddr1;
+	    if (try.ouraddr != 0)
+		try.neg_addr = 1;
+	    no.neg_addr = 1;
+	    break;
+	case CI_MS_DNS1:
+	    if (go->req_dns1 || no.req_dns1 || cilen != CILEN_ADDR)
+		goto bad;
+	    GETLONG(l, p);
+	    try.dnsaddr[0] = htonl(l);
+	    try.req_dns1 = 1;
+	    no.req_dns1 = 1;
+	    break;
+	case CI_MS_DNS2:
+	    if (go->req_dns2 || no.req_dns2 || cilen != CILEN_ADDR)
+		goto bad;
+	    GETLONG(l, p);
+	    try.dnsaddr[1] = htonl(l);
+	    try.req_dns2 = 1;
+	    no.req_dns2 = 1;
+	    break;
+	case CI_MS_WINS1:
+	case CI_MS_WINS2:
+	    if (cilen != CILEN_ADDR)
+		goto bad;
+	    GETLONG(l, p);
+	    ciaddr1 = htonl(l);
+	    if (ciaddr1)
+		try.winsaddr[citype == CI_MS_WINS2] = ciaddr1;
+	    break;
+	}
+	p = next;
+    }
+
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     * If there are any remaining options, we ignore them.
+     */
+    if (f->state != OPENED)
+	*go = try;
+
+    return 1;
+
+bad:
+    IPCPDEBUG(("ipcp_nakci: received bad Nak!"));
+    return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ * Callback from fsm_rconfnakrej.
+ */
+static int
+ipcp_rejci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    u_char cimaxslotindex, ciflag, cilen;
+    u_short cishort;
+    u_int32_t cilong;
+    ipcp_options try;		/* options to request next time */
+
+    try = *go;
+    /*
+     * Any Rejected CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define REJCIADDRS(opt, neg, val1, val2) \
+    if ((neg) && \
+	(cilen = p[1]) == CILEN_ADDRS && \
+	len >= cilen && \
+	p[0] == opt) { \
+	u_int32_t l; \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	/* Check rejected value. */ \
+	if (cilong != val1) \
+	    goto bad; \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	/* Check rejected value. */ \
+	if (cilong != val2) \
+	    goto bad; \
+	try.old_addrs = 0; \
+    }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+    if (go->neg && \
+	p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+	len >= p[1] && \
+	p[0] == opt) { \
+	len -= p[1]; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	/* Check rejected value. */  \
+	if (cishort != val) \
+	    goto bad; \
+	if (!old) { \
+	   GETCHAR(cimaxslotindex, p); \
+	   if (cimaxslotindex != maxslot) \
+	     goto bad; \
+	   GETCHAR(ciflag, p); \
+	   if (ciflag != cflag) \
+	     goto bad; \
+        } \
+	try.neg = 0; \
+     }
+
+#define REJCIADDR(opt, neg, val) \
+    if (go->neg && \
+	(cilen = p[1]) == CILEN_ADDR && \
+	len >= cilen && \
+	p[0] == opt) { \
+	u_int32_t l; \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	/* Check rejected value. */ \
+	if (cilong != val) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
+#define REJCIDNS(opt, neg, dnsaddr) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_ADDR) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	u_int32_t l; \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	/* Check rejected value. */ \
+	if (cilong != dnsaddr) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
+#define REJCIWINS(opt, addr) \
+    if (addr && \
+	((cilen = p[1]) == CILEN_ADDR) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	u_int32_t l; \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	/* Check rejected value. */ \
+	if (cilong != addr) \
+	    goto bad; \
+	try.winsaddr[opt == CI_MS_WINS2] = 0; \
+    }
+
+warn("pppd  REJCIADDR: %s---->%d,%d", __FILE__, __LINE__, go->ouraddr);
+    REJCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
+	       go->ouraddr, go->hisaddr);
+
+    REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+	    go->maxslotindex, go->cflag);
+
+    REJCIADDR(CI_ADDR, neg_addr, go->ouraddr);
+
+    REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
+
+    REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
+
+    REJCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+    REJCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+	goto bad;
+    /*
+     * Now we can update state.
+     */
+    if (f->state != OPENED)
+	*go = try;
+    return 1;
+
+bad:
+    IPCPDEBUG(("ipcp_rejci: received bad Reject!"));
+    return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ * Callback from fsm_rconfreq, Receive Configure Request
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipcp_reqci(f, inp, len, reject_if_disagree)
+    fsm *f;
+    u_char *inp;		/* Requested CIs */
+    int *len;			/* Length of requested CIs */
+    int reject_if_disagree;
+{
+    ipcp_options *wo = &ipcp_wantoptions[f->unit];
+    ipcp_options *ho = &ipcp_hisoptions[f->unit];
+    ipcp_options *ao = &ipcp_allowoptions[f->unit];
+    u_char *cip, *next;		/* Pointer to current and next CIs */
+    u_short cilen, citype;	/* Parsed len, type */
+    u_short cishort;		/* Parsed short value */
+    u_int32_t tl, ciaddr1, ciaddr2;/* Parsed address values */
+    int rc = CONFACK;		/* Final packet return code */
+    int orc;			/* Individual option return code */
+    u_char *p;			/* Pointer to next char to parse */
+    u_char *ucp = inp;		/* Pointer to current output char */
+    int l = *len;		/* Length left */
+    u_char maxslotindex, cflag;
+    int d;
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+    
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    while (l) {
+	orc = CONFACK;			/* Assume success */
+	cip = p = next;			/* Remember begining of CI */
+	if (l < 2 ||			/* Not enough data for CI header or */
+	    p[1] < 2 ||			/*  CI length too small or */
+	    p[1] > l) {			/*  CI length too big? */
+	    IPCPDEBUG(("ipcp_reqci: bad CI length!"));
+	    orc = CONFREJ;		/* Reject bad CI */
+	    cilen = l;			/* Reject till end of packet */
+	    l = 0;			/* Don't loop again */
+	    goto endswitch;
+	}
+	GETCHAR(citype, p);		/* Parse CI type */
+	GETCHAR(cilen, p);		/* Parse CI length */
+	l -= cilen;			/* Adjust remaining length */
+	next += cilen;			/* Step to next CI */
+	 IPCPDEBUG(("ipcp:  citype is %d", citype));//wangming
+	switch (citype) {		/* Check CI type */
+	case CI_ADDRS:
+	    if (!ao->old_addrs || ho->neg_addr ||
+		cilen != CILEN_ADDRS) {	/* Check CI length */
+		orc = CONFREJ;		/* Reject CI */
+		break;
+	    }
+
+	    /*
+	     * If he has no address, or if we both have his address but
+	     * disagree about it, then NAK it with our idea.
+	     * In particular, if we don't know his address, but he does,
+	     * then accept it.
+	     */
+	    GETLONG(tl, p);		/* Parse source address (his) */
+	    ciaddr1 = htonl(tl);
+	    if (ciaddr1 != wo->hisaddr
+		&& (ciaddr1 == 0 || !wo->accept_remote)) {
+		orc = CONFNAK;
+		if (!reject_if_disagree) {
+		    DECPTR(sizeof(u_int32_t), p);
+		    tl = ntohl(wo->hisaddr);
+		    PUTLONG(tl, p);
+		}
+	    } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+		/*
+		 * If neither we nor he knows his address, reject the option.
+		 */
+		orc = CONFREJ;
+		wo->req_addr = 0;	/* don't NAK with 0.0.0.0 later */
+		break;
+	    }
+
+	    /*
+	     * If he doesn't know our address, or if we both have our address
+	     * but disagree about it, then NAK it with our idea.
+	     */
+	    GETLONG(tl, p);		/* Parse desination address (ours) */
+	    ciaddr2 = htonl(tl);
+	    if (ciaddr2 != wo->ouraddr) {
+		if (ciaddr2 == 0 || !wo->accept_local) {
+		    orc = CONFNAK;
+		    if (!reject_if_disagree) {
+			DECPTR(sizeof(u_int32_t), p);
+			tl = ntohl(wo->ouraddr);
+			PUTLONG(tl, p);
+		    }
+		} else {
+		    wo->ouraddr = ciaddr2;	/* accept peer's idea */
+		}
+	    }
+
+	    ho->old_addrs = 1;
+	    ho->hisaddr = ciaddr1;
+	    ho->ouraddr = ciaddr2;
+	    break;
+
+	case CI_ADDR:
+		/*wangming*/				
+	    if (!ao->neg_addr || ho->old_addrs ||
+		cilen != CILEN_ADDR) {	/* Check CI length */
+		orc = CONFREJ;		/* Reject CI */
+		break;
+	    }
+
+	    /*
+	     * If he has no address, or if we both have his address but
+	     * disagree about it, then NAK it with our idea.
+	     * In particular, if we don't know his address, but he does,
+	     * then accept it.
+	     */
+	    GETLONG(tl, p);	/* Parse source address (his) */
+	    ciaddr1 = htonl(tl);
+	    if (ciaddr1 != wo->hisaddr
+		&& (ciaddr1 == 0 || !wo->accept_remote)) {
+		orc = CONFNAK;
+		if (!reject_if_disagree) {
+			//wo->hisaddr = 0x6802a8c0;
+		    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;
+            }
+
+		orc = CONFNAK;
+            break;
+	
+	case CI_COMPRESSTYPE:
+	    if (!ao->neg_vj ||
+		(cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+		orc = CONFREJ;
+		break;
+	    }
+	    GETSHORT(cishort, p);
+
+	    if (!(cishort == IPCP_VJ_COMP ||
+		  (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+		orc = CONFREJ;
+		break;
+	    }
+
+	    ho->neg_vj = 1;
+	    ho->vj_protocol = cishort;
+	    if (cilen == CILEN_VJ) {
+		GETCHAR(maxslotindex, p);
+		if (maxslotindex > ao->maxslotindex) { 
+		    orc = CONFNAK;
+		    if (!reject_if_disagree){
+			DECPTR(1, p);
+			PUTCHAR(ao->maxslotindex, p);
+		    }
+		}
+		GETCHAR(cflag, p);
+		if (cflag && !ao->cflag) {
+		    orc = CONFNAK;
+		    if (!reject_if_disagree){
+			DECPTR(1, p);
+			PUTCHAR(wo->cflag, p);
+		    }
+		}
+		ho->maxslotindex = maxslotindex;
+		ho->cflag = cflag;
+	    } else {
+		ho->old_vj = 1;
+		ho->maxslotindex = MAX_STATES - 1;
+		ho->cflag = 1;
+	    }
+	    break;
+
+	default:
+	    orc = CONFREJ;
+	    break;
+	}
+endswitch:
+	if (orc == CONFACK &&		/* Good CI */
+	    rc != CONFACK)		/*  but prior CI wasnt? */
+	    continue;			/* Don't send this one */
+
+	if (orc == CONFNAK) {		/* Nak this CI? */
+	    if (reject_if_disagree)	/* Getting fed up with sending NAKs? */
+		orc = CONFREJ;		/* Get tough if so */
+	    else {
+		if (rc == CONFREJ)	/* Rejecting prior CI? */
+		    continue;		/* Don't send this one */
+		if (rc == CONFACK) {	/* Ack'd all prior CIs? */
+		    rc = CONFNAK;	/* Not anymore... */
+		    ucp = inp;		/* Backup */
+		}
+	    }
+	}
+
+	if (orc == CONFREJ &&		/* Reject this CI */
+	    rc != CONFREJ) {		/*  but no prior ones? */
+	    rc = CONFREJ;
+	    ucp = inp;			/* Backup */
+	}
+
+	/* Need to move CI? */
+	if (ucp != cip)
+	    BCOPY(cip, ucp, cilen);	/* Move it */
+
+	/* Update output pointer */
+	INCPTR(cilen, ucp);
+    }
+
+    /*
+     * If we aren't rejecting this packet, and we want to negotiate
+     * their address, and they didn't send their address, then we
+     * send a NAK with a CI_ADDR option appended.  We assume the
+     * input buffer is long enough that we can append the extra
+     * option safely.
+     */
+    if (rc != CONFREJ && !ho->neg_addr && !ho->old_addrs &&
+	wo->req_addr && !reject_if_disagree && !noremoteip) {
+	if (rc == CONFACK) {
+	    rc = CONFNAK;
+	    ucp = inp;			/* reset pointer */
+	    wo->req_addr = 0;		/* don't ask again */
+	}
+	PUTCHAR(CI_ADDR, ucp);
+	PUTCHAR(CILEN_ADDR, ucp);
+	tl = ntohl(wo->hisaddr);
+	PUTLONG(tl, ucp);
+    }
+
+    *len = ucp - inp;			/* Compute output length */
+    IPCPDEBUG(("ipcp: returning Configure-%s", CODENAME(rc)));
+    return (rc);			/* Return final code */
+}
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options()
+{
+    struct hostent *hp;
+    u_int32_t local;
+    ipcp_options *wo = &ipcp_wantoptions[0];
+
+    /*
+     * Default our local IP address based on our hostname.
+     * If local IP address already given, don't bother.
+     */
+    if (wo->ouraddr == 0 && !disable_defaultip) {
+	/*
+	 * Look up our hostname (possibly with domain name appended)
+	 * and take the first IP address as our local IP address.
+	 * If there isn't an IP address for our hostname, too bad.
+	 */
+	wo->accept_local = 1;	/* don't insist on this default value */
+	if ((hp = gethostbyname(hostname)) != NULL) {
+	    local = *(u_int32_t *)hp->h_addr;
+	    if (local != 0 && !bad_ip_adrs(local))
+		wo->ouraddr = local;
+	}
+    }
+    ask_for_local = wo->ouraddr != 0 || !disable_defaultip;
+}
+
+
+/*
+ * ip_demand_conf - configure the interface as though
+ * IPCP were up, for use with dial-on-demand.
+ */
+static int
+ip_demand_conf(u)
+    int u;
+{
+    ipcp_options *wo = &ipcp_wantoptions[u];
+
+    if (wo->hisaddr == 0 && !noremoteip) {
+	/* make up an arbitrary address for the peer */
+	wo->hisaddr = htonl(0x0a707070 + ifunit);
+	wo->accept_remote = 1;
+    }
+    if (wo->ouraddr == 0) {
+	/* make up an arbitrary address for us */
+	wo->ouraddr = htonl(0x0a404040 + ifunit);
+	wo->accept_local = 1;
+	ask_for_local = 0;	/* don't tell the peer this address */
+    }
+    if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
+	return 0;
+    ipcp_script(_PATH_IPPREUP, 1);
+    if (!sifup(u))
+	return 0;
+
+    rtmetricfixup(u, wo->hisaddr, metric);
+
+    if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
+	return 0;
+    if (wo->default_route)
+	if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr, drmetric, wo->force_default_route))
+	    default_route_set[u] = 1;
+    if (wo->proxy_arp)
+	if (sifproxyarp(u, wo->hisaddr))
+	    proxy_arp_set[u] = 1;
+
+    warn("local  IP address %I", wo->ouraddr);
+    if (wo->hisaddr)
+	warn("remote IP address %I", wo->hisaddr);
+
+    return 1;
+}
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(f)
+    fsm *f;
+{
+    u_int32_t mask;
+    ipcp_options *ho = &ipcp_hisoptions[f->unit];
+    ipcp_options *go = &ipcp_gotoptions[f->unit];
+    ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+    IPCPDEBUG(("ipcp: up"));
+
+    /*
+     * We must have a non-zero IP address for both ends of the link.
+     */
+    if (!ho->neg_addr && !ho->old_addrs)
+	ho->hisaddr = wo->hisaddr;
+
+    if (!(go->neg_addr || go->old_addrs) && (wo->neg_addr || wo->old_addrs)
+	&& wo->ouraddr != 0) {
+	error("Peer refused to agree to our IP address");
+	ipcp_close(f->unit, "Refused our IP address");
+	return;
+    }
+    if (go->ouraddr == 0) {
+	error("Could not determine local IP address");
+	ipcp_close(f->unit, "Could not determine local IP address");
+	return;
+    }
+    if (ho->hisaddr == 0 && !noremoteip) {
+	ho->hisaddr = htonl(0x0a404040 + ifunit);
+	warn("Could not determine remote IP address: defaulting to %I",
+	     ho->hisaddr);
+    }
+	warn("pppd  script_setenv: %s---->%d,%d", __FILE__, __LINE__, go->ouraddr);
+    script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
+    if (ho->hisaddr != 0)
+	script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
+
+    if (!go->req_dns1)
+	    go->dnsaddr[0] = 0;
+    if (!go->req_dns2)
+	    go->dnsaddr[1] = 0;
+    if (go->dnsaddr[0])
+	script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
+    if (go->dnsaddr[1])
+	script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
+    if (usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
+	script_setenv("USEPEERDNS", "1", 0);
+	create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
+    }
+
+    /*
+     * Check that the peer is allowed to use the IP address it wants.
+     */
+    if (ho->hisaddr != 0 && !auth_ip_addr(f->unit, ho->hisaddr)) {
+	error("Peer is not authorized to use remote address %I", ho->hisaddr);
+	ipcp_close(f->unit, "Unauthorized remote IP address");
+	return;
+    }
+
+    /* set tcp compression */
+    sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
+
+    /*
+     * If we are doing dial-on-demand, the interface is already
+     * configured, so we put out any saved-up packets, then set the
+     * interface to pass IP packets.
+     */
+    if (demand) {
+	if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
+	    ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr);
+	    if (go->ouraddr != wo->ouraddr) {
+		warn("Local IP address changed to %I", go->ouraddr);
+		script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
+		wo->ouraddr = go->ouraddr;
+	    } else
+		script_unsetenv("OLDIPLOCAL");
+	    if (ho->hisaddr != wo->hisaddr && wo->hisaddr != 0) {
+		warn("Remote IP address changed to %I", ho->hisaddr);
+		script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
+		wo->hisaddr = ho->hisaddr;
+	    } else
+		script_unsetenv("OLDIPREMOTE");
+
+	    /* Set the interface to the new addresses */
+	    mask = GetMask(go->ouraddr);
+	    if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+		if (debug)
+		    warn("Interface configuration failed");
+		ipcp_close(f->unit, "Interface configuration failed");
+		return;
+	    }
+
+	    rtmetricfixup(f->unit, ho->hisaddr, metric);
+
+	    /* assign a default route through the interface if required */
+	    if (ipcp_wantoptions[f->unit].default_route) 
+		if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr, drmetric, ipcp_wantoptions[f->unit].force_default_route))
+		    default_route_set[f->unit] = 1;
+
+	    /* Make a proxy ARP entry if requested. */
+	    if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp)
+		if (sifproxyarp(f->unit, ho->hisaddr))
+		    proxy_arp_set[f->unit] = 1;
+
+	}
+	demand_rexmit(PPP_IP);
+	sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+    } else {
+	/*
+	 * Set IP addresses and (if specified) netmask.
+	 */
+	mask = GetMask(go->ouraddr);
+
+#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+	if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+	    if (debug)
+		warn("Interface configuration failed");
+	    ipcp_close(f->unit, "Interface configuration failed");
+	    return;
+	}
+#endif
+
+	/* run the pre-up script, if any, and wait for it to finish */
+	ipcp_script(_PATH_IPPREUP, 1);
+
+	/* bring the interface up for IP */
+	if (!sifup(f->unit)) {
+	    if (debug)
+		warn("Interface failed to come up");
+	    ipcp_close(f->unit, "Interface configuration failed");
+	    return;
+	}
+
+#if (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+	if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+	    if (debug)
+		warn("Interface configuration failed");
+	    ipcp_close(f->unit, "Interface configuration failed");
+	    return;
+	}
+#endif
+	rtmetricfixup(f->unit, ho->hisaddr, metric);
+	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, drmetric, ipcp_wantoptions[f->unit].force_default_route))
+		default_route_set[f->unit] = 1;
+
+	/* Make a proxy ARP entry if requested. */
+	if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp)
+	    if (sifproxyarp(f->unit, ho->hisaddr))
+		proxy_arp_set[f->unit] = 1;
+
+	ipcp_wantoptions[0].ouraddr = go->ouraddr;
+
+	warn("local  IP address %I", go->ouraddr);
+	if (ho->hisaddr != 0)
+	    warn("remote IP address %I", ho->hisaddr);
+	if (go->dnsaddr[0])
+	    warn("primary   DNS address %I", go->dnsaddr[0]);
+	if (go->dnsaddr[1])
+	    warn("secondary DNS address %I", go->dnsaddr[1]);
+    }
+
+    reset_link_stats(f->unit);
+
+    np_up(f->unit, PPP_IP);
+    ipcp_is_up = 1;
+
+    notify(ip_up_notifier, 0);
+    if (ip_up_hook)
+	ip_up_hook();
+
+    /*
+     * Execute the ip-up script, like this:
+     *	/etc/ppp/ip-up interface tty speed local-IP remote-IP
+     */
+    if (ipcp_script_state == s_down && ipcp_script_pid == 0) {
+	ipcp_script_state = s_up;
+	ipcp_script_up();
+    }
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipcp_down(f)
+    fsm *f;
+{
+    IPCPDEBUG(("ipcp: down"));
+    /* XXX a bit IPv4-centric here, we only need to get the stats
+     * before the interface is marked down. */
+    /* XXX more correct: we must get the stats before running the notifiers,
+     * at least for the radius plugin */
+    update_link_stats(f->unit);
+    notify(ip_down_notifier, 0);
+    if (ip_down_hook)
+	ip_down_hook();
+    if (ipcp_is_up) {
+	ipcp_is_up = 0;
+	np_down(f->unit, PPP_IP);
+    }
+    sifvjcomp(f->unit, 0, 0, 0);
+
+    print_link_stats(); /* _after_ running the notifiers and ip_down_hook(),
+			 * because print_link_stats() sets link_stats_valid
+			 * to 0 (zero) */
+
+    /*
+     * If we are doing dial-on-demand, set the interface
+     * to queue up outgoing packets (for now).
+     */
+    if (demand) {
+	sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE);
+    } else {
+	sifnpmode(f->unit, PPP_IP, NPMODE_DROP);
+	sifdown(f->unit);
+	ipcp_clear_addrs(f->unit, ipcp_gotoptions[f->unit].ouraddr,
+			 ipcp_hisoptions[f->unit].hisaddr);
+    }
+
+    /* Execute the ip-down script */
+    if (ipcp_script_state == s_up && ipcp_script_pid == 0) {
+	ipcp_script_state = s_down;
+	ipcp_script_down();
+    }
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes,
+ * proxy arp entries, etc.
+ */
+static void
+ipcp_clear_addrs(unit, ouraddr, hisaddr)
+    int unit;
+    u_int32_t ouraddr;  /* local address */
+    u_int32_t hisaddr;  /* remote address */
+{
+    if (proxy_arp_set[unit]) {
+	cifproxyarp(unit, hisaddr);
+	proxy_arp_set[unit] = 0;
+    }
+    if (default_route_set[unit]) {
+	cifdefaultroute(unit, ouraddr, hisaddr);
+	default_route_set[unit] = 0;
+    }
+    cifaddr(unit, ouraddr, hisaddr);
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipcp_finished(f)
+    fsm *f;
+{
+	if (ipcp_is_open) {
+		ipcp_is_open = 0;
+		np_finished(f->unit, PPP_IP);
+	}
+}
+
+
+static void
+ipcp_script_up()
+{
+#ifdef PATH_ETC_CONFIG
+    if (ip_up)
+	ipcp_script(ip_up, 0);
+    else {
+	struct stat st;
+	if (stat(_PATH_IPUP, &st) == 0 && (st.st_mode & S_IXUSR))
+	    ipcp_script(_PATH_IPUP, 0);
+	else
+	    ipcp_script(_PATH_DEFAULT_IPUP, 0);
+    }       
+#else
+    ipcp_script(ip_up ? ip_up : _PATH_IPUP, 0);
+#endif
+}
+
+
+static void
+ipcp_script_down()
+{
+#ifdef PATH_ETC_CONFIG
+    if (ip_down)
+	ipcp_script(ip_down, 0);
+    else {
+	struct stat st;
+	if (stat(_PATH_IPDOWN, &st) == 0 && (st.st_mode & S_IXUSR))
+	    ipcp_script(_PATH_IPDOWN, 0);
+	else
+	    ipcp_script(_PATH_DEFAULT_IPDOWN, 0);
+    }       
+#else
+    ipcp_script(ip_down ? ip_down : _PATH_IPDOWN, 0);
+#endif
+}
+
+
+/*
+ * ipcp_script_done - called when the ip-up or ip-down script
+ * has finished.
+ */
+static void
+ipcp_script_done(arg)
+    void *arg;
+{
+    ipcp_script_pid = 0;
+    switch (ipcp_script_state) {
+    case s_up:
+	if (ipcp_fsm[0].state != OPENED) {
+	    ipcp_script_state = s_down;
+	    ipcp_script(_PATH_IPDOWN, 0);
+	}
+	break;
+    case s_down:
+	if (ipcp_fsm[0].state == OPENED) {
+	    ipcp_script_state = s_up;
+	    ipcp_script(_PATH_IPUP, 0);
+	}
+	break;
+    }
+}
+
+
+/*
+ * ipcp_script - Execute a script with arguments
+ * interface-name tty-name speed local-IP remote-IP.
+ */
+static void
+ipcp_script(script, wait)
+    char *script;
+    int wait;
+{
+    char strspeed[32], strlocal[32], strremote[32];
+    char *argv[8];
+
+    slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+    slprintf(strlocal, sizeof(strlocal), "%I", ipcp_gotoptions[0].ouraddr);
+    slprintf(strremote, sizeof(strremote), "%I", ipcp_hisoptions[0].hisaddr);
+
+    argv[0] = script;
+    argv[1] = ifname;
+    argv[2] = devnam;
+    argv[3] = strspeed;
+    argv[4] = strlocal;
+    argv[5] = strremote;
+    argv[6] = ipparam;
+    argv[7] = NULL;
+    if (wait)
+	run_program(script, argv, 0, NULL, NULL, 1);
+    else
+	ipcp_script_pid = run_program(script, argv, 0, ipcp_script_done,
+				      NULL, 0);
+}
+
+/*
+ * create_resolv - create the replacement resolv.conf file
+ */
+static void
+create_resolv(peerdns1, peerdns2)
+    u_int32_t peerdns1, peerdns2;
+{
+    FILE *f;
+    char pathname[MAXPATHLEN];
+
+    slprintf(pathname, sizeof(pathname), _PATH_RESOLV, ifname);
+
+    f = fopen(pathname, "w");
+    if (f == NULL) {
+	error("Failed to create %s: %m", pathname);
+	return;
+    }
+
+    if (peerdns1) {
+	syslog(LOG_INFO, "Adding resolv.conf for %s",ip_ntoa(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", pathname);
+
+    fclose(f);
+}
+
+/*
+ * ipcp_printpkt - print the contents of an IPCP packet.
+ */
+static char *ipcp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipcp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    int code, id, len, olen;
+    u_char *pstart, *optend;
+    u_short cishort;
+    u_int32_t cilong;
+
+    if (plen < HEADERLEN)
+	return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+	return 0;
+
+    if (code >= 1 && code <= sizeof(ipcp_codenames) / sizeof(char *))
+	printer(arg, " %s", ipcp_codenames[code-1]);
+    else
+	printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+	/* print option list */
+	while (len >= 2) {
+	    GETCHAR(code, p);
+	    GETCHAR(olen, p);
+	    p -= 2;
+	    if (olen < 2 || olen > len) {
+		break;
+	    }
+	    printer(arg, " <");
+	    len -= olen;
+	    optend = p + olen;
+	    switch (code) {
+	    case CI_ADDRS:
+		if (olen == CILEN_ADDRS) {
+		    p += 2;
+		    GETLONG(cilong, p);
+		    printer(arg, "addrs %I", htonl(cilong));
+		    GETLONG(cilong, p);
+		    printer(arg, " %I", htonl(cilong));
+		}
+		break;
+	    case CI_COMPRESSTYPE:
+		if (olen >= CILEN_COMPRESS) {
+		    p += 2;
+		    GETSHORT(cishort, p);
+		    printer(arg, "compress ");
+		    switch (cishort) {
+		    case IPCP_VJ_COMP:
+			printer(arg, "VJ");
+			break;
+		    case IPCP_VJ_COMP_OLD:
+			printer(arg, "old-VJ");
+			break;
+		    default:
+			printer(arg, "0x%x", cishort);
+		    }
+		}
+		break;
+	    case CI_ADDR:
+		if (olen == CILEN_ADDR) {
+		    p += 2;
+		    GETLONG(cilong, p);
+		    printer(arg, "addr %I", htonl(cilong));
+		}
+		break;
+	    case CI_MS_DNS1:
+	    case CI_MS_DNS2:
+	        p += 2;
+		GETLONG(cilong, p);
+		printer(arg, "ms-dns%d %I", (code == CI_MS_DNS1? 1: 2),
+			htonl(cilong));
+		break;
+	    case CI_MS_WINS1:
+	    case CI_MS_WINS2:
+	        p += 2;
+		GETLONG(cilong, p);
+		printer(arg, "ms-wins %I", htonl(cilong));
+		break;
+	    }
+	    while (p < optend) {
+		GETCHAR(code, p);
+		printer(arg, " %.2x", code);
+	    }
+	    printer(arg, ">");
+	}
+	break;
+
+    case TERMACK:
+    case TERMREQ:
+	if (len > 0 && *p >= ' ' && *p < 0x7f) {
+	    printer(arg, " ");
+	    print_string((char *)p, len, printer, arg);
+	    p += len;
+	    len = 0;
+	}
+	break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (; len > 0; --len) {
+	GETCHAR(code, p);
+	printer(arg, " %.2x", code);
+    }
+
+    return p - pstart;
+}
+
+/*
+ * ip_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP_HDRLEN	20	/* bytes */
+#define IP_OFFMASK	0x1fff
+#ifndef IPPROTO_TCP
+#define IPPROTO_TCP	6
+#endif
+#define TCP_HDRLEN	20
+#define TH_FIN		0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define net_short(x)	(((x)[0] << 8) + (x)[1])
+#define get_iphl(x)	(((unsigned char *)(x))[0] & 0xF)
+#define get_ipoff(x)	net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x)	(((unsigned char *)(x))[9])
+#define get_tcpoff(x)	(((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x)	(((unsigned char *)(x))[13])
+
+static int
+ip_active_pkt(pkt, len)
+    u_char *pkt;
+    int len;
+{
+    u_char *tcp;
+    int hlen;
+
+    len -= PPP_HDRLEN;
+    pkt += PPP_HDRLEN;
+    if (len < IP_HDRLEN)
+	return 0;
+    if ((get_ipoff(pkt) & IP_OFFMASK) != 0)
+	return 0;
+    if (get_ipproto(pkt) != IPPROTO_TCP)
+	return 1;
+    hlen = get_iphl(pkt) * 4;
+    if (len < hlen + TCP_HDRLEN)
+	return 0;
+    tcp = pkt + hlen;
+    if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4)
+	return 0;
+    return 1;
+}
diff --git a/ap/app/pppd/pppd/ipcp.h b/ap/app/pppd/pppd/ipcp.h
new file mode 100644
index 0000000..840e0db
--- /dev/null
+++ b/ap/app/pppd/pppd/ipcp.h
@@ -0,0 +1,97 @@
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipcp.h,v 1.14 2002/12/04 23:03:32 paulus Exp $
+ */
+
+/*
+ * Options.
+ */
+#define CI_ADDRS	1	/* IP Addresses */
+#define CI_COMPRESSTYPE	2	/* Compression Type */
+#define	CI_ADDR		3
+
+#define CI_MS_DNS1	129	/* Primary DNS value */
+#define CI_MS_WINS1	130	/* Primary WINS value */
+#define CI_MS_DNS2	131	/* Secondary DNS value */
+#define CI_MS_WINS2	132	/* Secondary WINS value */
+
+#define MAX_STATES 16		/* from slcompress.h */
+
+#define IPCP_VJMODE_OLD 1	/* "old" mode (option # = 0x0037) */
+#define IPCP_VJMODE_RFC1172 2	/* "old-rfc"mode (option # = 0x002d) */
+#define IPCP_VJMODE_RFC1332 3	/* "new-rfc"mode (option # = 0x002d, */
+                                /*  maxslot and slot number compression) */
+
+#define IPCP_VJ_COMP 0x002d	/* current value for VJ compression option*/
+#define IPCP_VJ_COMP_OLD 0x0037	/* "old" (i.e, broken) value for VJ */
+				/* compression option*/ 
+
+typedef struct ipcp_options {
+    bool neg_addr;		/* Negotiate IP Address? */
+    bool old_addrs;		/* Use old (IP-Addresses) option? */
+    bool req_addr;		/* Ask peer to send IP address? */
+    bool default_route;		/* Assign default route through interface? */
+    bool force_default_route;	/* Don't check for existing default route */
+    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/ap/app/pppd/pppd/ipv6cp.c b/ap/app/pppd/pppd/ipv6cp.c
new file mode 100644
index 0000000..cfe2979
--- /dev/null
+++ b/ap/app/pppd/pppd/ipv6cp.c
@@ -0,0 +1,1562 @@
+/*
+ * ipv6cp.c - PPP IPV6 Control Protocol.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*  Original version, based on RFC2023 :
+
+    Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+    Alain.Durand@imag.fr, IMAG,
+    Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+    Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+    Alain.Durand@imag.fr, IMAG,
+    Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+    Ce travail a ét?fait au sein du GIE DYADE (Groupement d'Intérêt
+    Économique ayant pour membres BULL S.A. et l'INRIA).
+
+    Ce logiciel informatique est disponible aux conditions
+    usuelles dans la recherche, c'est-?dire qu'il peut
+    être utilis? copi? modifi? distribu??l'unique
+    condition que ce texte soit conserv?afin que
+    l'origine de ce logiciel soit reconnue.
+
+    Le nom de l'Institut National de Recherche en Informatique
+    et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+    ou physique ayant particip??l'élaboration de ce logiciel ne peut
+    être utilis?sans son accord préalable explicite.
+
+    Ce logiciel est fourni tel quel sans aucune garantie,
+    support ou responsabilit?d'aucune sorte.
+    Ce logiciel est dériv?de sources d'origine
+    "University of California at Berkeley" et
+    "Digital Equipment Corporation" couvertes par des copyrights.
+
+    L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+    est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+    Polytechnique de Grenoble et de l'Universit?Joseph Fourier regroupant
+    sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+    This work has been done in the context of GIE DYADE (joint R & D venture
+    between BULL S.A. and INRIA).
+
+    This software is available with usual "research" terms
+    with the aim of retain credits of the software. 
+    Permission to use, copy, modify and distribute this software for any
+    purpose and without fee is hereby granted, provided that the above
+    copyright notice and this permission notice appear in all copies,
+    and the name of INRIA, IMAG, or any contributor not be used in advertising
+    or publicity pertaining to this material without the prior explicit
+    permission. The software is provided "as is" without any
+    warranties, support or liabilities of any kind.
+    This software is derived from source code from
+    "University of California at Berkeley" and
+    "Digital Equipment Corporation" protected by copyrights.
+
+    Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+    is a federation of seven research units funded by the CNRS, National
+    Polytechnic Institute of Grenoble and University Joseph Fourier.
+    The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $ 
+ */
+
+#define RCSID	"$Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $"
+
+/*
+ * TODO: 
+ *
+ * Proxy Neighbour Discovery.
+ *
+ * Better defines for selecting the ordering of
+ *   interface up / set address. (currently checks for __linux__,
+ *   since SVR4 && (SNI || __USLC__) didn't work properly)
+ */
+
+#include <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, int));/* Peer nak'd our CI */
+static int  ipv6cp_rejci __P((fsm *, u_char *, int));	/* Peer rej'd our CI */
+static int  ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipv6cp_up __P((fsm *));		/* We're UP */
+static void ipv6cp_down __P((fsm *));		/* We're DOWN */
+static void ipv6cp_finished __P((fsm *));	/* Don't need lower layer */
+
+fsm ipv6cp_fsm[NUM_PPP];		/* IPV6CP fsm structure */
+
+static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
+    ipv6cp_resetci,		/* Reset our Configuration Information */
+    ipv6cp_cilen,		/* Length of our Configuration Information */
+    ipv6cp_addci,		/* Add our Configuration Information */
+    ipv6cp_ackci,		/* ACK our Configuration Information */
+    ipv6cp_nakci,		/* NAK our Configuration Information */
+    ipv6cp_rejci,		/* Reject our Configuration Information */
+    ipv6cp_reqci,		/* Request peer's Configuration Information */
+    ipv6cp_up,			/* Called when fsm reaches OPENED state */
+    ipv6cp_down,		/* Called when fsm leaves OPENED state */
+    NULL,			/* Called when we want the lower layer up */
+    ipv6cp_finished,		/* Called when we want the lower layer down */
+    NULL,			/* Called when Protocol-Reject received */
+    NULL,			/* Retransmission is necessary */
+    NULL,			/* Called to handle protocol-specific codes */
+    "IPV6CP"			/* String name of protocol */
+};
+
+/*
+ * Command-line options.
+ */
+static int setifaceid __P((char **arg));
+static void printifaceid __P((option_t *,
+			      void (*)(void *, char *, ...), void *));
+
+static option_t ipv6cp_option_list[] = {
+    { "ipv6", o_special, (void *)setifaceid,
+      "Set interface identifiers for IPV6",
+      OPT_A2PRINTER, (void *)printifaceid },
+
+    { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+      "Enable IPv6 and IPv6CP", OPT_PRIO | 1 },
+    { "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
+      "Disable IPv6 and IPv6CP", OPT_PRIOSUB },
+    { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+      "Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS },
+
+    { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local,
+      "Accept peer's interface identifier for us", 1 },
+
+    { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
+      "Use (default) IPv4 address as interface identifier", 1 },
+
+#if defined(SOL2) || defined(__linux__)
+    { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
+      "Use uniquely-available persistent value for link local address", 1 },
+#endif /* defined(SOL2) */
+
+    { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
+      "Set timeout for IPv6CP", OPT_PRIO },
+    { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
+      "Set max #xmits for term-reqs", OPT_PRIO },
+    { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
+      "Set max #xmits for conf-reqs", OPT_PRIO },
+    { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
+      "Set max #conf-naks for IPv6CP", OPT_PRIO },
+
+   { NULL }
+};
+
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipv6cp_init __P((int));
+static void ipv6cp_open __P((int));
+static void ipv6cp_close __P((int, char *));
+static void ipv6cp_lowerup __P((int));
+static void ipv6cp_lowerdown __P((int));
+static void ipv6cp_input __P((int, u_char *, int));
+static void ipv6cp_protrej __P((int));
+static int  ipv6cp_printpkt __P((u_char *, int,
+			       void (*) __P((void *, char *, ...)), void *));
+static void ipv6_check_options __P((void));
+static int  ipv6_demand_conf __P((int));
+static int  ipv6_active_pkt __P((u_char *, int));
+
+struct protent ipv6cp_protent = {
+    PPP_IPV6CP,
+    ipv6cp_init,
+    ipv6cp_input,
+    ipv6cp_protrej,
+    ipv6cp_lowerup,
+    ipv6cp_lowerdown,
+    ipv6cp_open,
+    ipv6cp_close,
+    ipv6cp_printpkt,
+    NULL,
+    0,
+    "IPV6CP",
+    "IPV6",
+    ipv6cp_option_list,
+    ipv6_check_options,
+    ipv6_demand_conf,
+    ipv6_active_pkt
+};
+
+static void ipv6cp_clear_addrs __P((int, eui64_t, eui64_t));
+static void ipv6cp_script __P((char *));
+static void ipv6cp_script_done __P((void *));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID	2
+#define CILEN_COMPRESS	4	/* length for RFC2023 compress opt. */
+#define CILEN_IFACEID   10	/* RFC2472, interface identifier    */
+
+#define CODENAME(x)	((x) == CONFACK ? "ACK" : \
+			 (x) == CONFNAK ? "NAK" : "REJ")
+
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+    s_down,
+    s_up,
+} ipv6cp_script_state;
+static pid_t ipv6cp_script_pid;
+
+/*
+ * setifaceid - set the interface identifiers manually
+ */
+static int
+setifaceid(argv)
+    char **argv;
+{
+    char *comma, *arg, c;
+    ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+    struct in6_addr addr;
+    static int prio_local, prio_remote;
+
+#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
+			(((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
+    
+    arg = *argv;
+    if ((comma = strchr(arg, ',')) == NULL)
+	comma = arg + strlen(arg);
+    
+    /* 
+     * If comma first character, then no local identifier
+     */
+    if (comma != arg) {
+	c = *comma;
+	*comma = '\0';
+
+	if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) {
+	    option_error("Illegal interface identifier (local): %s", arg);
+	    return 0;
+	}
+
+	if (option_priority >= prio_local) {
+	    eui64_copy(addr.s6_addr32[2], wo->ourid);
+	    wo->opt_local = 1;
+	    prio_local = option_priority;
+	}
+	*comma = c;
+    }
+    
+    /*
+     * If comma last character, the no remote identifier
+     */
+    if (*comma != 0 && *++comma != '\0') {
+	if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) {
+	    option_error("Illegal interface identifier (remote): %s", comma);
+	    return 0;
+	}
+	if (option_priority >= prio_remote) {
+	    eui64_copy(addr.s6_addr32[2], wo->hisid);
+	    wo->opt_remote = 1;
+	    prio_remote = option_priority;
+	}
+    }
+
+    if (override_value("+ipv6", option_priority, option_source))
+	ipv6cp_protent.enabled_flag = 1;
+    return 1;
+}
+
+char *llv6_ntoa(eui64_t ifaceid);
+
+static void
+printifaceid(opt, printer, arg)
+    option_t *opt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+	if (wo->opt_local)
+		printer(arg, "%s", llv6_ntoa(wo->ourid));
+	printer(arg, ",");
+	if (wo->opt_remote)
+		printer(arg, "%s", llv6_ntoa(wo->hisid));
+}
+
+/*
+ * Make a string representation of a network address.
+ */
+char *
+llv6_ntoa(ifaceid)
+    eui64_t ifaceid;
+{
+    static char b[64];
+
+    sprintf(b, "fe80::%s", eui64_ntoa(ifaceid));
+    return b;
+}
+
+
+/*
+ * ipv6cp_init - Initialize IPV6CP.
+ */
+static void
+ipv6cp_init(unit)
+    int unit;
+{
+    fsm *f = &ipv6cp_fsm[unit];
+    ipv6cp_options *wo = &ipv6cp_wantoptions[unit];
+    ipv6cp_options *ao = &ipv6cp_allowoptions[unit];
+
+    f->unit = unit;
+    f->protocol = PPP_IPV6CP;
+    f->callbacks = &ipv6cp_callbacks;
+    fsm_init(&ipv6cp_fsm[unit]);
+
+    memset(wo, 0, sizeof(*wo));
+    memset(ao, 0, sizeof(*ao));
+
+    wo->accept_local = 1;
+    wo->neg_ifaceid = 1;
+    ao->neg_ifaceid = 1;
+
+#ifdef IPV6CP_COMP
+    wo->neg_vj = 1;
+    ao->neg_vj = 1;
+    wo->vj_protocol = IPV6CP_COMP;
+#endif
+
+}
+
+
+/*
+ * ipv6cp_open - IPV6CP is allowed to come up.
+ */
+static void
+ipv6cp_open(unit)
+    int unit;
+{
+    fsm_open(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_close - Take IPV6CP down.
+ */
+static void
+ipv6cp_close(unit, reason)
+    int unit;
+    char *reason;
+{
+    fsm_close(&ipv6cp_fsm[unit], reason);
+}
+
+
+/*
+ * ipv6cp_lowerup - The lower layer is up.
+ */
+static void
+ipv6cp_lowerup(unit)
+    int unit;
+{
+    fsm_lowerup(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_lowerdown - The lower layer is down.
+ */
+static void
+ipv6cp_lowerdown(unit)
+    int unit;
+{
+    fsm_lowerdown(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_input - Input IPV6CP packet.
+ */
+static void
+ipv6cp_input(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    fsm_input(&ipv6cp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipv6cp_protrej(unit)
+    int unit;
+{
+    fsm_lowerdown(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_resetci - Reset our CI.
+ */
+static void
+ipv6cp_resetci(f)
+    fsm *f;
+{
+    ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+    ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+    wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid;
+    
+    if (!wo->opt_local) {
+	eui64_magic_nz(wo->ourid);
+    }
+    
+    *go = *wo;
+    eui64_zero(go->hisid);	/* last proposed interface identifier */
+}
+
+
+/*
+ * ipv6cp_cilen - Return length of our CI.
+ */
+static int
+ipv6cp_cilen(f)
+    fsm *f;
+{
+    ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+#define LENCIVJ(neg)		(neg ? CILEN_COMPRESS : 0)
+#define LENCIIFACEID(neg)	(neg ? CILEN_IFACEID : 0)
+
+    return (LENCIIFACEID(go->neg_ifaceid) +
+	    LENCIVJ(go->neg_vj));
+}
+
+
+/*
+ * ipv6cp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipv6cp_addci(f, ucp, lenp)
+    fsm *f;
+    u_char *ucp;
+    int *lenp;
+{
+    ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+    int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val) \
+    if (neg) { \
+	int vjlen = CILEN_COMPRESS; \
+	if (len >= vjlen) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(vjlen, ucp); \
+	    PUTSHORT(val, ucp); \
+	    len -= vjlen; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCIIFACEID(opt, neg, val1) \
+    if (neg) { \
+	int idlen = CILEN_IFACEID; \
+	if (len >= idlen) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(idlen, ucp); \
+	    eui64_put(val1, ucp); \
+	    len -= idlen; \
+	} else \
+	    neg = 0; \
+    }
+
+    ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+    ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+    *lenp -= len;
+}
+
+
+/*
+ * ipv6cp_ackci - Ack our CIs.
+ *
+ * Returns:
+ *	0 - Ack was bad.
+ *	1 - Ack was good.
+ */
+static int
+ipv6cp_ackci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+    u_short cilen, citype, cishort;
+    eui64_t ifaceid;
+
+    /*
+     * CIs must be in exactly the same order that we sent...
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+
+#define ACKCIVJ(opt, neg, val) \
+    if (neg) { \
+	int vjlen = CILEN_COMPRESS; \
+	if ((len -= vjlen) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != vjlen || \
+	    citype != opt)  \
+	    goto bad; \
+	GETSHORT(cishort, p); \
+	if (cishort != val) \
+	    goto bad; \
+    }
+
+#define ACKCIIFACEID(opt, neg, val1) \
+    if (neg) { \
+	int idlen = CILEN_IFACEID; \
+	if ((len -= idlen) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != idlen || \
+	    citype != opt) \
+	    goto bad; \
+	eui64_get(ifaceid, p); \
+	if (! eui64_equals(val1, ifaceid)) \
+	    goto bad; \
+    }
+
+    ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+    ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+	goto bad;
+    return (1);
+
+bad:
+    IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
+    return (0);
+}
+
+/*
+ * ipv6cp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPV6CP is in the OPENED state.
+ *
+ * Returns:
+ *	0 - Nak was bad.
+ *	1 - Nak was good.
+ */
+static int
+ipv6cp_nakci(f, p, len, treat_as_reject)
+    fsm *f;
+    u_char *p;
+    int len;
+    int treat_as_reject;
+{
+    ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+    u_char citype, cilen, *next;
+    u_short cishort;
+    eui64_t ifaceid;
+    ipv6cp_options no;		/* options we've seen Naks for */
+    ipv6cp_options try;		/* options to request next time */
+
+    BZERO(&no, sizeof(no));
+    try = *go;
+
+    /*
+     * Any Nak'd CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define NAKCIIFACEID(opt, neg, code) \
+    if (go->neg && \
+	len >= (cilen = CILEN_IFACEID) && \
+	p[1] == cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	eui64_get(ifaceid, p); \
+	no.neg = 1; \
+	code \
+    }
+
+#define NAKCIVJ(opt, neg, code) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_COMPRESS) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	no.neg = 1; \
+        code \
+    }
+
+    /*
+     * Accept the peer's idea of {our,his} interface identifier, if different
+     * from our idea, only if the accept_{local,remote} flag is set.
+     */
+    NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
+		 if (treat_as_reject) {
+		     try.neg_ifaceid = 0;
+		 } else if (go->accept_local) {
+		     while (eui64_iszero(ifaceid) || 
+			    eui64_equals(ifaceid, go->hisid)) /* bad luck */
+			 eui64_magic(ifaceid);
+		     try.ourid = ifaceid;
+		     IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
+		 }
+		 );
+
+#ifdef IPV6CP_COMP
+    NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+	    {
+		if (cishort == IPV6CP_COMP && !treat_as_reject) {
+		    try.vj_protocol = cishort;
+		} else {
+		    try.neg_vj = 0;
+		}
+	    }
+	    );
+#else
+    NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+	    {
+		try.neg_vj = 0;
+	    }
+	    );
+#endif
+
+    /*
+     * There may be remaining CIs, if the peer is requesting negotiation
+     * on an option that we didn't include in our request packet.
+     * If they want to negotiate about interface identifier, we comply.
+     * If they want us to ask for compression, we refuse.
+     */
+    while (len >= CILEN_VOID) {
+	GETCHAR(citype, p);
+	GETCHAR(cilen, p);
+	if ( cilen < CILEN_VOID || (len -= cilen) < 0 )
+	    goto bad;
+	next = p + cilen - 2;
+
+	switch (citype) {
+	case CI_COMPRESSTYPE:
+	    if (go->neg_vj || no.neg_vj ||
+		(cilen != CILEN_COMPRESS))
+		goto bad;
+	    no.neg_vj = 1;
+	    break;
+	case CI_IFACEID:
+	    if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
+		goto bad;
+	    try.neg_ifaceid = 1;
+	    eui64_get(ifaceid, p);
+	    if (go->accept_local) {
+		while (eui64_iszero(ifaceid) || 
+		       eui64_equals(ifaceid, go->hisid)) /* bad luck */
+		    eui64_magic(ifaceid);
+		try.ourid = ifaceid;
+	    }
+	    no.neg_ifaceid = 1;
+	    break;
+	}
+	p = next;
+    }
+
+    /* If there is still anything left, this packet is bad. */
+    if (len != 0)
+	goto bad;
+
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     */
+    if (f->state != OPENED)
+	*go = try;
+
+    return 1;
+
+bad:
+    IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
+    return 0;
+}
+
+
+/*
+ * ipv6cp_rejci - Reject some of our CIs.
+ */
+static int
+ipv6cp_rejci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+    u_char cilen;
+    u_short cishort;
+    eui64_t ifaceid;
+    ipv6cp_options try;		/* options to request next time */
+
+    try = *go;
+    /*
+     * Any Rejected CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define REJCIIFACEID(opt, neg, val1) \
+    if (go->neg && \
+	len >= (cilen = CILEN_IFACEID) && \
+	p[1] == cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	eui64_get(ifaceid, p); \
+	/* Check rejected value. */ \
+	if (! eui64_equals(ifaceid, val1)) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
+#define REJCIVJ(opt, neg, val) \
+    if (go->neg && \
+	p[1] == CILEN_COMPRESS && \
+	len >= p[1] && \
+	p[0] == opt) { \
+	len -= p[1]; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	/* Check rejected value. */  \
+	if (cishort != val) \
+	    goto bad; \
+	try.neg = 0; \
+     }
+
+    REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
+
+    REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+	goto bad;
+    /*
+     * Now we can update state.
+     */
+    if (f->state != OPENED)
+	*go = try;
+    return 1;
+
+bad:
+    IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
+    return 0;
+}
+
+
+/*
+ * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipv6cp_reqci(f, inp, len, reject_if_disagree)
+    fsm *f;
+    u_char *inp;		/* Requested CIs */
+    int *len;			/* Length of requested CIs */
+    int reject_if_disagree;
+{
+    ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+    ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
+    ipv6cp_options *ao = &ipv6cp_allowoptions[f->unit];
+    ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+    u_char *cip, *next;		/* Pointer to current and next CIs */
+    u_short cilen, citype;	/* Parsed len, type */
+    u_short cishort;		/* Parsed short value */
+    eui64_t ifaceid;		/* Parsed interface identifier */
+    int rc = CONFACK;		/* Final packet return code */
+    int orc;			/* Individual option return code */
+    u_char *p;			/* Pointer to next char to parse */
+    u_char *ucp = inp;		/* Pointer to current output char */
+    int l = *len;		/* Length left */
+
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+    
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    while (l) {
+	orc = CONFACK;			/* Assume success */
+	cip = p = next;			/* Remember begining of CI */
+	if (l < 2 ||			/* Not enough data for CI header or */
+	    p[1] < 2 ||			/*  CI length too small or */
+	    p[1] > l) {			/*  CI length too big? */
+	    IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!"));
+	    orc = CONFREJ;		/* Reject bad CI */
+	    cilen = l;			/* Reject till end of packet */
+	    l = 0;			/* Don't loop again */
+	    goto endswitch;
+	}
+	GETCHAR(citype, p);		/* Parse CI type */
+	GETCHAR(cilen, p);		/* Parse CI length */
+	l -= cilen;			/* Adjust remaining length */
+	next += cilen;			/* Step to next CI */
+
+	switch (citype) {		/* Check CI type */
+	case CI_IFACEID:
+	    IPV6CPDEBUG(("ipv6cp: received interface identifier "));
+
+	    if (!ao->neg_ifaceid ||
+		cilen != CILEN_IFACEID) {	/* Check CI length */
+		orc = CONFREJ;		/* Reject CI */
+		break;
+	    }
+
+	    /*
+	     * If he has no interface identifier, or if we both have same 
+	     * identifier then NAK it with new idea.
+	     * In particular, if we don't know his identifier, but he does,
+	     * then accept it.
+	     */
+	    eui64_get(ifaceid, p);
+	    IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
+	    if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
+		orc = CONFREJ;		/* Reject CI */
+		break;
+	    }
+	    if (!eui64_iszero(wo->hisid) && 
+		!eui64_equals(ifaceid, wo->hisid) && 
+		eui64_iszero(go->hisid)) {
+		    
+		orc = CONFNAK;
+		ifaceid = wo->hisid;
+		go->hisid = ifaceid;
+		DECPTR(sizeof(ifaceid), p);
+		eui64_put(ifaceid, p);
+	    } else
+	    if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) {
+		orc = CONFNAK;
+		if (eui64_iszero(go->hisid))	/* first time, try option */
+		    ifaceid = wo->hisid;
+		while (eui64_iszero(ifaceid) || 
+		       eui64_equals(ifaceid, go->ourid)) /* bad luck */
+		    eui64_magic(ifaceid);
+		go->hisid = ifaceid;
+		DECPTR(sizeof(ifaceid), p);
+		eui64_put(ifaceid, p);
+	    }
+
+	    ho->neg_ifaceid = 1;
+	    ho->hisid = ifaceid;
+	    break;
+
+	case CI_COMPRESSTYPE:
+	    IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
+	    if (!ao->neg_vj ||
+		(cilen != CILEN_COMPRESS)) {
+		orc = CONFREJ;
+		break;
+	    }
+	    GETSHORT(cishort, p);
+	    IPV6CPDEBUG(("(%d)", cishort));
+
+#ifdef IPV6CP_COMP
+	    if (!(cishort == IPV6CP_COMP)) {
+		orc = CONFREJ;
+		break;
+	    }
+
+	    ho->neg_vj = 1;
+	    ho->vj_protocol = cishort;
+	    break;
+#else
+	    orc = CONFREJ;
+	    break;
+#endif
+
+	default:
+	    orc = CONFREJ;
+	    break;
+	}
+
+endswitch:
+	IPV6CPDEBUG((" (%s)\n", CODENAME(orc)));
+
+	if (orc == CONFACK &&		/* Good CI */
+	    rc != CONFACK)		/*  but prior CI wasnt? */
+	    continue;			/* Don't send this one */
+
+	if (orc == CONFNAK) {		/* Nak this CI? */
+	    if (reject_if_disagree)	/* Getting fed up with sending NAKs? */
+		orc = CONFREJ;		/* Get tough if so */
+	    else {
+		if (rc == CONFREJ)	/* Rejecting prior CI? */
+		    continue;		/* Don't send this one */
+		if (rc == CONFACK) {	/* Ack'd all prior CIs? */
+		    rc = CONFNAK;	/* Not anymore... */
+		    ucp = inp;		/* Backup */
+		}
+	    }
+	}
+
+	if (orc == CONFREJ &&		/* Reject this CI */
+	    rc != CONFREJ) {		/*  but no prior ones? */
+	    rc = CONFREJ;
+	    ucp = inp;			/* Backup */
+	}
+
+	/* Need to move CI? */
+	if (ucp != cip)
+	    BCOPY(cip, ucp, cilen);	/* Move it */
+
+	/* Update output pointer */
+	INCPTR(cilen, ucp);
+    }
+
+    /*
+     * If we aren't rejecting this packet, and we want to negotiate
+     * their identifier and they didn't send their identifier, then we
+     * send a NAK with a CI_IFACEID option appended.  We assume the
+     * input buffer is long enough that we can append the extra
+     * option safely.
+     */
+    if (rc != CONFREJ && !ho->neg_ifaceid &&
+	wo->req_ifaceid && !reject_if_disagree) {
+	if (rc == CONFACK) {
+	    rc = CONFNAK;
+	    ucp = inp;				/* reset pointer */
+	    wo->req_ifaceid = 0;		/* don't ask again */
+	}
+	PUTCHAR(CI_IFACEID, ucp);
+	PUTCHAR(CILEN_IFACEID, ucp);
+	eui64_put(wo->hisid, ucp);
+    }
+
+    *len = ucp - inp;			/* Compute output length */
+    IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc)));
+    return (rc);			/* Return final code */
+}
+
+
+/*
+ * ipv6_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ipv6_check_options()
+{
+    ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+    if (!ipv6cp_protent.enabled_flag)
+	return;
+
+#if defined(SOL2) || defined(__linux__)
+    /*
+     * Persistent link-local id is only used when user has not explicitly
+     * configure/hard-code the id
+     */
+    if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
+
+	/* 
+	 * On systems where there are no Ethernet interfaces used, there
+	 * may be other ways to obtain a persistent id. Right now, it
+	 * will fall back to using magic [see eui64_magic] below when
+	 * an EUI-48 from MAC address can't be obtained. Other possibilities
+	 * include obtaining EEPROM serial numbers, or some other unique
+	 * yet persistent number. On Sparc platforms, this is possible,
+	 * but too bad there's no standards yet for x86 machines.
+	 */
+	if (ether_to_eui64(&wo->ourid)) {
+	    wo->opt_local = 1;
+	}
+    }
+#endif
+
+    if (!wo->opt_local) {	/* init interface identifier */
+	if (wo->use_ip && eui64_iszero(wo->ourid)) {
+	    eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr));
+	    if (!eui64_iszero(wo->ourid))
+		wo->opt_local = 1;
+	}
+	
+	while (eui64_iszero(wo->ourid))
+	    eui64_magic(wo->ourid);
+    }
+
+    if (!wo->opt_remote) {
+	if (wo->use_ip && eui64_iszero(wo->hisid)) {
+	    eui64_setlo32(wo->hisid, ntohl(ipcp_wantoptions[0].hisaddr));
+	    if (!eui64_iszero(wo->hisid))
+		wo->opt_remote = 1;
+	}
+    }
+
+    if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
+	option_error("local/remote LL address required for demand-dialling\n");
+	exit(1);
+    }
+}
+
+
+/*
+ * ipv6_demand_conf - configure the interface as though
+ * IPV6CP were up, for use with dial-on-demand.
+ */
+static int
+ipv6_demand_conf(u)
+    int u;
+{
+    ipv6cp_options *wo = &ipv6cp_wantoptions[u];
+
+#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+#if defined(SOL2)
+    if (!sif6up(u))
+	return 0;
+#else
+    if (!sifup(u))
+	return 0;
+#endif /* defined(SOL2) */
+#endif    
+    if (!sif6addr(u, wo->ourid, wo->hisid))
+	return 0;
+#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+    if (!sifup(u))
+	return 0;
+#endif
+    if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
+	return 0;
+
+    warn("ipv6_demand_conf");
+    warn("local  LL address %s", llv6_ntoa(wo->ourid));
+    warn("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);
+
+	warn("local  LL address %s", llv6_ntoa(go->ourid));
+	warn("remote LL address %s", llv6_ntoa(ho->hisid));
+    }
+
+    np_up(f->unit, PPP_IPV6);
+    ipv6cp_is_up = 1;
+
+    /*
+     * Execute the ipv6-up script, like this:
+     *	/etc/ppp/ipv6-up interface tty speed local-LL remote-LL
+     */
+    if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
+	ipv6cp_script_state = s_up;
+	ipv6cp_script(_PATH_IPV6UP);
+    }
+}
+
+
+/*
+ * ipv6cp_down - IPV6CP has gone DOWN.
+ *
+ * Take the IPv6 network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipv6cp_down(f)
+    fsm *f;
+{
+    IPV6CPDEBUG(("ipv6cp: down"));
+    update_link_stats(f->unit);
+    if (ipv6cp_is_up) {
+	ipv6cp_is_up = 0;
+	np_down(f->unit, PPP_IPV6);
+    }
+#ifdef IPV6CP_COMP
+    sif6comp(f->unit, 0);
+#endif
+
+    /*
+     * If we are doing dial-on-demand, set the interface
+     * to queue up outgoing packets (for now).
+     */
+    if (demand) {
+	sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE);
+    } else {
+	sifnpmode(f->unit, PPP_IPV6, NPMODE_DROP);
+#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
+#if defined(SOL2)
+	sif6down(f->unit);
+#else
+	sifdown(f->unit);
+#endif /* defined(SOL2) */
+#endif
+	ipv6cp_clear_addrs(f->unit, 
+			   ipv6cp_gotoptions[f->unit].ourid,
+			   ipv6cp_hisoptions[f->unit].hisid);
+#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
+	sifdown(f->unit);
+#endif
+    }
+
+    /* Execute the ipv6-down script */
+    if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
+	ipv6cp_script_state = s_down;
+	ipv6cp_script(_PATH_IPV6DOWN);
+    }
+}
+
+
+/*
+ * ipv6cp_clear_addrs() - clear the interface addresses, routes,
+ * proxy neighbour discovery entries, etc.
+ */
+static void
+ipv6cp_clear_addrs(unit, ourid, hisid)
+    int unit;
+    eui64_t ourid;
+    eui64_t hisid;
+{
+    cif6addr(unit, ourid, hisid);
+}
+
+
+/*
+ * ipv6cp_finished - possibly shut down the lower layers.
+ */
+static void
+ipv6cp_finished(f)
+    fsm *f;
+{
+    np_finished(f->unit, PPP_IPV6);
+}
+
+
+/*
+ * ipv6cp_script_done - called when the ipv6-up or ipv6-down script
+ * has finished.
+ */
+static void
+ipv6cp_script_done(arg)
+    void *arg;
+{
+    ipv6cp_script_pid = 0;
+    switch (ipv6cp_script_state) {
+    case s_up:
+	if (ipv6cp_fsm[0].state != OPENED) {
+	    ipv6cp_script_state = s_down;
+	    ipv6cp_script(_PATH_IPV6DOWN);
+	}
+	break;
+    case s_down:
+	if (ipv6cp_fsm[0].state == OPENED) {
+	    ipv6cp_script_state = s_up;
+	    ipv6cp_script(_PATH_IPV6UP);
+	}
+	break;
+    }
+}
+
+
+/*
+ * ipv6cp_script - Execute a script with arguments
+ * interface-name tty-name speed local-LL remote-LL.
+ */
+static void
+ipv6cp_script(script)
+    char *script;
+{
+    char strspeed[32], strlocal[32], strremote[32];
+    char *argv[8];
+
+    sprintf(strspeed, "%d", baud_rate);
+    strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid));
+    strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid));
+
+    argv[0] = script;
+    argv[1] = ifname;
+    argv[2] = devnam;
+    argv[3] = strspeed;
+    argv[4] = strlocal;
+    argv[5] = strremote;
+    argv[6] = ipparam;
+    argv[7] = NULL;
+
+    ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done,
+				    NULL, 0);
+}
+
+/*
+ * ipv6cp_printpkt - print the contents of an IPV6CP packet.
+ */
+static char *ipv6cp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipv6cp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    int code, id, len, olen;
+    u_char *pstart, *optend;
+    u_short cishort;
+    eui64_t ifaceid;
+
+    if (plen < HEADERLEN)
+	return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+	return 0;
+
+    if (code >= 1 && code <= sizeof(ipv6cp_codenames) / sizeof(char *))
+	printer(arg, " %s", ipv6cp_codenames[code-1]);
+    else
+	printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+	/* print option list */
+	while (len >= 2) {
+	    GETCHAR(code, p);
+	    GETCHAR(olen, p);
+	    p -= 2;
+	    if (olen < 2 || olen > len) {
+		break;
+	    }
+	    printer(arg, " <");
+	    len -= olen;
+	    optend = p + olen;
+	    switch (code) {
+	    case CI_COMPRESSTYPE:
+		if (olen >= CILEN_COMPRESS) {
+		    p += 2;
+		    GETSHORT(cishort, p);
+		    printer(arg, "compress ");
+		    printer(arg, "0x%x", cishort);
+		}
+		break;
+	    case CI_IFACEID:
+		if (olen == CILEN_IFACEID) {
+		    p += 2;
+		    eui64_get(ifaceid, p);
+		    printer(arg, "addr %s", llv6_ntoa(ifaceid));
+		}
+		break;
+	    }
+	    while (p < optend) {
+		GETCHAR(code, p);
+		printer(arg, " %.2x", code);
+	    }
+	    printer(arg, ">");
+	}
+	break;
+
+    case TERMACK:
+    case TERMREQ:
+	if (len > 0 && *p >= ' ' && *p < 0x7f) {
+	    printer(arg, " ");
+	    print_string((char *)p, len, printer, arg);
+	    p += len;
+	    len = 0;
+	}
+	break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (; len > 0; --len) {
+	GETCHAR(code, p);
+	printer(arg, " %.2x", code);
+    }
+
+    return p - pstart;
+}
+
+/*
+ * ipv6_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP6_HDRLEN	40	/* bytes */
+#define IP6_NHDR_FRAG	44	/* fragment IPv6 header */
+#define TCP_HDRLEN	20
+#define TH_FIN		0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define get_ip6nh(x)	(((unsigned char *)(x))[6])
+#define get_tcpoff(x)	(((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x)	(((unsigned char *)(x))[13])
+
+static int
+ipv6_active_pkt(pkt, len)
+    u_char *pkt;
+    int len;
+{
+    u_char *tcp;
+
+    len -= PPP_HDRLEN;
+    pkt += PPP_HDRLEN;
+    if (len < IP6_HDRLEN)
+	return 0;
+    if (get_ip6nh(pkt) == IP6_NHDR_FRAG)
+	return 0;
+    if (get_ip6nh(pkt) != IPPROTO_TCP)
+	return 1;
+    if (len < IP6_HDRLEN + TCP_HDRLEN)
+	return 0;
+    tcp = pkt + IP6_HDRLEN;
+    if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4)
+	return 0;
+    return 1;
+}
diff --git a/ap/app/pppd/pppd/ipv6cp.h b/ap/app/pppd/pppd/ipv6cp.h
new file mode 100644
index 0000000..cc4568d
--- /dev/null
+++ b/ap/app/pppd/pppd/ipv6cp.h
@@ -0,0 +1,171 @@
+/*
+ * ipv6cp.h - PPP IPV6 Control Protocol.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*  Original version, based on RFC2023 :
+
+    Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+    Alain.Durand@imag.fr, IMAG,
+    Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+    Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+    Alain.Durand@imag.fr, IMAG,
+    Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+    Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+    Économique ayant pour membres BULL S.A. et l'INRIA).
+
+    Ce logiciel informatique est disponible aux conditions
+    usuelles dans la recherche, c'est-à-dire qu'il peut
+    être utilisé, copié, modifié, distribué à l'unique
+    condition que ce texte soit conservé afin que
+    l'origine de ce logiciel soit reconnue.
+
+    Le nom de l'Institut National de Recherche en Informatique
+    et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+    ou physique ayant participé à l'élaboration de ce logiciel ne peut
+    être utilisé sans son accord préalable explicite.
+
+    Ce logiciel est fourni tel quel sans aucune garantie,
+    support ou responsabilité d'aucune sorte.
+    Ce logiciel est dérivé de sources d'origine
+    "University of California at Berkeley" et
+    "Digital Equipment Corporation" couvertes par des copyrights.
+
+    L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+    est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+    Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+    sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+    This work has been done in the context of GIE DYADE (joint R & D venture
+    between BULL S.A. and INRIA).
+
+    This software is available with usual "research" terms
+    with the aim of retain credits of the software. 
+    Permission to use, copy, modify and distribute this software for any
+    purpose and without fee is hereby granted, provided that the above
+    copyright notice and this permission notice appear in all copies,
+    and the name of INRIA, IMAG, or any contributor not be used in advertising
+    or publicity pertaining to this material without the prior explicit
+    permission. The software is provided "as is" without any
+    warranties, support or liabilities of any kind.
+    This software is derived from source code from
+    "University of California at Berkeley" and
+    "Digital Equipment Corporation" protected by copyrights.
+
+    Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+    is a federation of seven research units funded by the CNRS, National
+    Polytechnic Institute of Grenoble and University Joseph Fourier.
+    The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipv6cp.h,v 1.7 2002/12/04 23:03:32 paulus Exp $
+ */
+
+/*
+ * Options.
+ */
+#define CI_IFACEID	1	/* Interface Identifier */
+#define CI_COMPRESSTYPE	2	/* Compression Type     */
+
+/* No compression types yet defined.
+ *#define IPV6CP_COMP	0x004f
+ */
+typedef struct ipv6cp_options {
+    int neg_ifaceid;		/* Negotiate interface identifier? */
+    int req_ifaceid;		/* Ask peer to send interface identifier? */
+    int accept_local;		/* accept peer's value for iface id? */
+    int opt_local;		/* ourtoken set by option */
+    int opt_remote;		/* histoken set by option */
+    int use_ip;			/* use IP as interface identifier */
+#if defined(SOL2) || defined(__linux__)
+    int use_persistent;		/* use uniquely persistent value for address */
+#endif /* defined(SOL2) */
+    int neg_vj;			/* Van Jacobson Compression? */
+    u_short vj_protocol;	/* protocol value to use in VJ option */
+    eui64_t ourid, hisid;	/* Interface identifiers */
+} ipv6cp_options;
+
+extern fsm ipv6cp_fsm[];
+extern ipv6cp_options ipv6cp_wantoptions[];
+extern ipv6cp_options ipv6cp_gotoptions[];
+extern ipv6cp_options ipv6cp_allowoptions[];
+extern ipv6cp_options ipv6cp_hisoptions[];
+
+extern struct protent ipv6cp_protent;
diff --git a/ap/app/pppd/pppd/ipxcp.c b/ap/app/pppd/pppd/ipxcp.c
new file mode 100644
index 0000000..3ccc3a7
--- /dev/null
+++ b/ap/app/pppd/pppd/ipxcp.c
@@ -0,0 +1,1598 @@
+/*
+ * ipxcp.c - PPP IPX Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef IPX_CHANGE
+
+#define RCSID	"$Id: ipxcp.c,v 1.2 2007-06-08 04:02:38 gerg Exp $"
+
+/*
+ * 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, int));/* Peer nak'd our CI */
+static int  ipxcp_rejci __P((fsm *, u_char *, int));	/* Peer rej'd our CI */
+static int  ipxcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipxcp_up __P((fsm *));		/* We're UP */
+static void ipxcp_down __P((fsm *));		/* We're DOWN */
+static void ipxcp_finished __P((fsm *));	/* Don't need lower layer */
+static void ipxcp_script __P((fsm *, char *)); /* Run an up/down script */
+
+fsm ipxcp_fsm[NUM_PPP];		/* IPXCP fsm structure */
+
+static fsm_callbacks ipxcp_callbacks = { /* IPXCP callback routines */
+    ipxcp_resetci,		/* Reset our Configuration Information */
+    ipxcp_cilen,		/* Length of our Configuration Information */
+    ipxcp_addci,		/* Add our Configuration Information */
+    ipxcp_ackci,		/* ACK our Configuration Information */
+    ipxcp_nakci,		/* NAK our Configuration Information */
+    ipxcp_rejci,		/* Reject our Configuration Information */
+    ipxcp_reqci,		/* Request peer's Configuration Information */
+    ipxcp_up,			/* Called when fsm reaches OPENED state */
+    ipxcp_down,			/* Called when fsm leaves OPENED state */
+    NULL,			/* Called when we want the lower layer up */
+    ipxcp_finished,		/* Called when we want the lower layer down */
+    NULL,			/* Called when Protocol-Reject received */
+    NULL,			/* Retransmission is necessary */
+    NULL,			/* Called to handle protocol-specific codes */
+    "IPXCP"			/* String name of protocol */
+};
+
+/*
+ * Command-line options.
+ */
+static int setipxnode __P((char **));
+static void printipxnode __P((option_t *,
+			      void (*)(void *, char *, ...), void *));
+static int setipxname __P((char **));
+
+static option_t ipxcp_option_list[] = {
+    { "ipx", o_bool, &ipxcp_protent.enabled_flag,
+      "Enable IPXCP (and IPX)", OPT_PRIO | 1 },
+    { "+ipx", o_bool, &ipxcp_protent.enabled_flag,
+      "Enable IPXCP (and IPX)", OPT_PRIOSUB | OPT_ALIAS | 1 },
+    { "noipx", o_bool, &ipxcp_protent.enabled_flag,
+      "Disable IPXCP (and IPX)", OPT_PRIOSUB },
+    { "-ipx", o_bool, &ipxcp_protent.enabled_flag,
+      "Disable IPXCP (and IPX)", OPT_PRIOSUB | OPT_ALIAS },
+
+    { "ipx-network", o_uint32, &ipxcp_wantoptions[0].our_network,
+      "Set our IPX network number", OPT_PRIO, &ipxcp_wantoptions[0].neg_nn },
+
+    { "ipxcp-accept-network", o_bool, &ipxcp_wantoptions[0].accept_network,
+      "Accept peer IPX network number", 1,
+      &ipxcp_allowoptions[0].accept_network },
+
+    { "ipx-node", o_special, (void *)setipxnode,
+      "Set IPX node number", OPT_A2PRINTER, (void *)printipxnode },
+
+    { "ipxcp-accept-local", o_bool, &ipxcp_wantoptions[0].accept_local,
+      "Accept our IPX address", 1,
+      &ipxcp_allowoptions[0].accept_local },
+
+    { "ipxcp-accept-remote", o_bool, &ipxcp_wantoptions[0].accept_remote,
+      "Accept peer's IPX address", 1,
+      &ipxcp_allowoptions[0].accept_remote },
+
+    { "ipx-routing", o_int, &ipxcp_wantoptions[0].router,
+      "Set IPX routing proto number", OPT_PRIO,
+      &ipxcp_wantoptions[0].neg_router },
+
+    { "ipx-router-name", o_special, setipxname,
+      "Set IPX router name", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC,
+       &ipxcp_wantoptions[0].name },
+
+    { "ipxcp-restart", o_int, &ipxcp_fsm[0].timeouttime,
+      "Set timeout for IPXCP", OPT_PRIO },
+    { "ipxcp-max-terminate", o_int, &ipxcp_fsm[0].maxtermtransmits,
+      "Set max #xmits for IPXCP term-reqs", OPT_PRIO },
+    { "ipxcp-max-configure", o_int, &ipxcp_fsm[0].maxconfreqtransmits,
+      "Set max #xmits for IPXCP conf-reqs", OPT_PRIO },
+    { "ipxcp-max-failure", o_int, &ipxcp_fsm[0].maxnakloops,
+      "Set max #conf-naks for IPXCP", OPT_PRIO },
+
+    { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+
+static void ipxcp_init __P((int));
+static void ipxcp_open __P((int));
+static void ipxcp_close __P((int, char *));
+static void ipxcp_lowerup __P((int));
+static void ipxcp_lowerdown __P((int));
+static void ipxcp_input __P((int, u_char *, int));
+static void ipxcp_protrej __P((int));
+static int  ipxcp_printpkt __P((u_char *, int,
+				void (*) __P((void *, char *, ...)), void *));
+
+struct protent ipxcp_protent = {
+    PPP_IPXCP,
+    ipxcp_init,
+    ipxcp_input,
+    ipxcp_protrej,
+    ipxcp_lowerup,
+    ipxcp_lowerdown,
+    ipxcp_open,
+    ipxcp_close,
+    ipxcp_printpkt,
+    NULL,
+    0,
+    "IPXCP",
+    "IPX",
+    ipxcp_option_list,
+    NULL,
+    NULL,
+    NULL
+};
+
+/*
+ * Lengths of configuration options.
+ */
+
+#define CILEN_VOID	2
+#define CILEN_COMPLETE	2	/* length of complete option */
+#define CILEN_NETN	6	/* network number length option */
+#define CILEN_NODEN	8	/* node number length option */
+#define CILEN_PROTOCOL	4	/* Minimum length of routing protocol */
+#define CILEN_NAME	3	/* Minimum length of router name */
+#define CILEN_COMPRESS	4	/* Minimum length of compression protocol */
+
+#define CODENAME(x)	((x) == CONFACK ? "ACK" : \
+			 (x) == CONFNAK ? "NAK" : "REJ")
+
+static int ipxcp_is_up;
+
+static char *ipx_ntoa __P((u_int32_t));
+
+/* Used in printing the node number */
+#define NODE(base) base[0], base[1], base[2], base[3], base[4], base[5]
+
+/* Used to generate the proper bit mask */
+#define BIT(num)   (1 << (num))
+
+/*
+ * Convert from internal to external notation
+ */
+
+static short int
+to_external(internal)
+short int internal;
+{
+    short int  external;
+
+    if (internal & BIT(IPX_NONE) )
+        external = IPX_NONE;
+    else
+        external = RIP_SAP;
+
+    return external;
+}
+
+/*
+ * Make a string representation of a network IP address.
+ */
+
+static char *
+ipx_ntoa(ipxaddr)
+u_int32_t ipxaddr;
+{
+    static char b[64];
+    slprintf(b, sizeof(b), "%x", ipxaddr);
+    return b;
+}
+
+
+static u_char *
+setipxnodevalue(src,dst)
+u_char *src, *dst;
+{
+    int indx;
+    int item;
+
+    for (;;) {
+        if (!isxdigit (*src))
+	    break;
+	
+	for (indx = 0; indx < 5; ++indx) {
+	    dst[indx] <<= 4;
+	    dst[indx] |= (dst[indx + 1] >> 4) & 0x0F;
+	}
+
+	item = toupper (*src) - '0';
+	if (item > 9)
+	    item -= 7;
+
+	dst[5] = (dst[5] << 4) | item;
+	++src;
+    }
+    return src;
+}
+
+static int ipx_prio_our, ipx_prio_his;
+
+static int
+setipxnode(argv)
+    char **argv;
+{
+    u_char *end;
+    int have_his = 0;
+    u_char our_node[6];
+    u_char his_node[6];
+
+    memset (our_node, 0, 6);
+    memset (his_node, 0, 6);
+
+    end = setipxnodevalue (*argv, our_node);
+    if (*end == ':') {
+	have_his = 1;
+	end = setipxnodevalue (++end, his_node);
+    }
+
+    if (*end == '\0') {
+        ipxcp_wantoptions[0].neg_node = 1;
+	if (option_priority >= ipx_prio_our) {
+	    memcpy(&ipxcp_wantoptions[0].our_node[0], our_node, 6);
+	    ipx_prio_our = option_priority;
+	}
+	if (have_his && option_priority >= ipx_prio_his) {
+	    memcpy(&ipxcp_wantoptions[0].his_node[0], his_node, 6);
+	    ipx_prio_his = option_priority;
+	}
+        return 1;
+    }
+
+    option_error("invalid parameter '%s' for ipx-node option", *argv);
+    return 0;
+}
+
+static void
+printipxnode(opt, printer, arg)
+    option_t *opt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	unsigned char *p;
+
+	p = ipxcp_wantoptions[0].our_node;
+	if (ipx_prio_our)
+		printer(arg, "%.2x%.2x%.2x%.2x%.2x%.2x",
+			p[0], p[1], p[2], p[3], p[4], p[5]);
+	printer(arg, ":");
+	p = ipxcp_wantoptions[0].his_node;
+	if (ipx_prio_his)
+		printer(arg, "%.2x%.2x%.2x%.2x%.2x%.2x",
+			p[0], p[1], p[2], p[3], p[4], p[5]);
+}
+
+static int
+setipxname (argv)
+    char **argv;
+{
+    u_char *dest = ipxcp_wantoptions[0].name;
+    char *src  = *argv;
+    int  count;
+    char ch;
+
+    ipxcp_wantoptions[0].neg_name  = 1;
+    ipxcp_allowoptions[0].neg_name = 1;
+    memset (dest, '\0', sizeof (ipxcp_wantoptions[0].name));
+
+    count = 0;
+    while (*src) {
+        ch = *src++;
+	if (! isalnum (ch) && ch != '_') {
+	    option_error("IPX router name must be alphanumeric or _");
+	    return 0;
+	}
+
+	if (count >= sizeof (ipxcp_wantoptions[0].name) - 1) {
+	    option_error("IPX router name is limited to %d characters",
+			 sizeof (ipxcp_wantoptions[0].name) - 1);
+	    return 0;
+	}
+
+	dest[count++] = toupper (ch);
+    }
+    dest[count] = 0;
+
+    return 1;
+}
+
+/*
+ * ipxcp_init - Initialize IPXCP.
+ */
+static void
+ipxcp_init(unit)
+    int unit;
+{
+    fsm *f = &ipxcp_fsm[unit];
+
+    f->unit	 = unit;
+    f->protocol	 = PPP_IPXCP;
+    f->callbacks = &ipxcp_callbacks;
+    fsm_init(&ipxcp_fsm[unit]);
+
+    memset (wo->name,	  0, sizeof (wo->name));
+    memset (wo->our_node, 0, sizeof (wo->our_node));
+    memset (wo->his_node, 0, sizeof (wo->his_node));
+
+    wo->neg_nn	       = 1;
+    wo->neg_complete   = 1;
+    wo->network	       = 0;
+
+    ao->neg_node       = 1;
+    ao->neg_nn	       = 1;
+    ao->neg_name       = 1;
+    ao->neg_complete   = 1;
+    ao->neg_router     = 1;
+
+    ao->accept_local   = 0;
+    ao->accept_remote  = 0;
+    ao->accept_network = 0;
+
+    wo->tried_rip      = 0;
+    wo->tried_nlsp     = 0;
+}
+
+/*
+ * Copy the node number
+ */
+
+static void
+copy_node (src, dst)
+u_char *src, *dst;
+{
+    memcpy (dst, src, sizeof (ipxcp_wantoptions[0].our_node));
+}
+
+/*
+ * Compare node numbers
+ */
+
+static int
+compare_node (src, dst)
+u_char *src, *dst;
+{
+    return memcmp (dst, src, sizeof (ipxcp_wantoptions[0].our_node)) == 0;
+}
+
+/*
+ * Is the node number zero?
+ */
+
+static int
+zero_node (node)
+u_char *node;
+{
+    int indx;
+    for (indx = 0; indx < sizeof (ipxcp_wantoptions[0].our_node); ++indx)
+	if (node [indx] != 0)
+	    return 0;
+    return 1;
+}
+
+/*
+ * Increment the node number
+ */
+
+static void
+inc_node (node)
+u_char *node;
+{
+    u_char   *outp;
+    u_int32_t magic_num;
+
+    outp      = node;
+    magic_num = magic();
+    *outp++   = '\0';
+    *outp++   = '\0';
+    PUTLONG (magic_num, outp);
+}
+
+/*
+ * ipxcp_open - IPXCP is allowed to come up.
+ */
+static void
+ipxcp_open(unit)
+    int unit;
+{
+    fsm_open(&ipxcp_fsm[unit]);
+}
+
+/*
+ * ipxcp_close - Take IPXCP down.
+ */
+static void
+ipxcp_close(unit, reason)
+    int unit;
+    char *reason;
+{
+    fsm_close(&ipxcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipxcp_lowerup - The lower layer is up.
+ */
+static void
+ipxcp_lowerup(unit)
+    int unit;
+{
+    fsm_lowerup(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_lowerdown - The lower layer is down.
+ */
+static void
+ipxcp_lowerdown(unit)
+    int unit;
+{
+    fsm_lowerdown(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_input - Input IPXCP packet.
+ */
+static void
+ipxcp_input(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    fsm_input(&ipxcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipxcp_protrej - A Protocol-Reject was received for IPXCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipxcp_protrej(unit)
+    int unit;
+{
+    fsm_lowerdown(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_resetci - Reset our CI.
+ */
+static void
+ipxcp_resetci(f)
+    fsm *f;
+{
+    wo->req_node = wo->neg_node && ao->neg_node;
+    wo->req_nn	 = wo->neg_nn	&& ao->neg_nn;
+
+    if (wo->our_network == 0) {
+	wo->neg_node	   = 1;
+	ao->accept_network = 1;
+    }
+/*
+ * If our node number is zero then change it.
+ */
+    if (zero_node (wo->our_node)) {
+	inc_node (wo->our_node);
+	ao->accept_local = 1;
+	wo->neg_node	 = 1;
+    }
+/*
+ * If his node number is zero then change it.
+ */
+    if (zero_node (wo->his_node)) {
+	inc_node (wo->his_node);
+	ao->accept_remote = 1;
+    }
+/*
+ * If no routing agent was specified then we do RIP/SAP according to the
+ * RFC documents. If you have specified something then OK. Otherwise, we
+ * do RIP/SAP.
+ */
+    if (ao->router == 0) {
+	ao->router |= BIT(RIP_SAP);
+	wo->router |= BIT(RIP_SAP);
+    }
+
+    /* Always specify a routing protocol unless it was REJected. */
+    wo->neg_router = 1;
+/*
+ * Start with these default values
+ */
+    *go = *wo;
+}
+
+/*
+ * ipxcp_cilen - Return length of our CI.
+ */
+
+static int
+ipxcp_cilen(f)
+    fsm *f;
+{
+    int len;
+
+    len	 = go->neg_nn	    ? CILEN_NETN     : 0;
+    len += go->neg_node	    ? CILEN_NODEN    : 0;
+    len += go->neg_name	    ? CILEN_NAME + strlen ((char *)go->name) - 1 : 0;
+
+    /* RFC says that defaults should not be included. */
+    if (go->neg_router && to_external(go->router) != RIP_SAP)
+        len += CILEN_PROTOCOL;
+
+    return (len);
+}
+
+
+/*
+ * ipxcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipxcp_addci(f, ucp, lenp)
+    fsm *f;
+    u_char *ucp;
+    int *lenp;
+{
+/*
+ * Add the options to the record.
+ */
+    if (go->neg_nn) {
+	PUTCHAR (IPX_NETWORK_NUMBER, ucp);
+	PUTCHAR (CILEN_NETN, ucp);
+	PUTLONG (go->our_network, ucp);
+    }
+
+    if (go->neg_node) {
+	int indx;
+	PUTCHAR (IPX_NODE_NUMBER, ucp);
+	PUTCHAR (CILEN_NODEN, ucp);
+	for (indx = 0; indx < sizeof (go->our_node); ++indx)
+	    PUTCHAR (go->our_node[indx], ucp);
+    }
+
+    if (go->neg_name) {
+	    int cilen = strlen ((char *)go->name);
+	int indx;
+	PUTCHAR (IPX_ROUTER_NAME, ucp);
+	PUTCHAR (CILEN_NAME + cilen - 1, ucp);
+	for (indx = 0; indx < cilen; ++indx)
+	    PUTCHAR (go->name [indx], ucp);
+    }
+
+    if (go->neg_router) {
+        short external = to_external (go->router);
+	if (external != RIP_SAP) {
+	    PUTCHAR  (IPX_ROUTER_PROTOCOL, ucp);
+	    PUTCHAR  (CILEN_PROTOCOL,      ucp);
+	    PUTSHORT (external,            ucp);
+	}
+    }
+}
+
+/*
+ * ipxcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ *	0 - Ack was bad.
+ *	1 - Ack was good.
+ */
+static int
+ipxcp_ackci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    u_short cilen, citype, cishort;
+    u_char cichar;
+    u_int32_t cilong;
+
+#define ACKCIVOID(opt, neg) \
+    if (neg) { \
+	if ((len -= CILEN_VOID) < 0) \
+	    break; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_VOID || \
+	    citype != opt) \
+	    break; \
+    }
+
+#define ACKCICOMPLETE(opt,neg)	ACKCIVOID(opt, neg)
+
+#define ACKCICHARS(opt, neg, val, cnt) \
+    if (neg) { \
+	int indx, count = cnt; \
+	len -= (count + 2); \
+	if (len < 0) \
+	    break; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != (count + 2) || \
+	    citype != opt) \
+	    break; \
+	for (indx = 0; indx < count; ++indx) {\
+	    GETCHAR(cichar, p); \
+	    if (cichar != ((u_char *) &val)[indx]) \
+	       break; \
+	}\
+	if (indx != count) \
+	    break; \
+    }
+
+#define ACKCINODE(opt,neg,val) ACKCICHARS(opt,neg,val,sizeof(val))
+#define ACKCINAME(opt,neg,val) ACKCICHARS(opt,neg,val,strlen((char *)val))
+
+#define ACKCINETWORK(opt, neg, val) \
+    if (neg) { \
+	if ((len -= CILEN_NETN) < 0) \
+	    break; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_NETN || \
+	    citype != opt) \
+	    break; \
+	GETLONG(cilong, p); \
+	if (cilong != val) \
+	    break; \
+    }
+
+#define ACKCIPROTO(opt, neg, val) \
+    if (neg) { \
+	if (len < 2) \
+	    break; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_PROTOCOL || citype != opt) \
+	    break; \
+	len -= cilen; \
+	if (len < 0) \
+	    break; \
+	GETSHORT(cishort, p); \
+	if (cishort != to_external (val) || cishort == RIP_SAP) \
+	    break; \
+      }
+/*
+ * Process the ACK frame in the order in which the frame was assembled
+ */
+    do {
+	ACKCINETWORK  (IPX_NETWORK_NUMBER,  go->neg_nn,	    go->our_network);
+	ACKCINODE     (IPX_NODE_NUMBER,	    go->neg_node,   go->our_node);
+	ACKCINAME     (IPX_ROUTER_NAME,	    go->neg_name,   go->name);
+	if (len > 0)
+		ACKCIPROTO    (IPX_ROUTER_PROTOCOL, go->neg_router, go->router);
+/*
+ * This is the end of the record.
+ */
+	if (len == 0)
+	    return (1);
+    } while (0);
+/*
+ * The frame is invalid
+ */
+    IPXCPDEBUG(("ipxcp_ackci: received bad Ack!"));
+    return (0);
+}
+
+/*
+ * ipxcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPXCP is in the OPENED state.
+ *
+ * Returns:
+ *	0 - Nak was bad.
+ *	1 - Nak was good.
+ */
+
+static int
+ipxcp_nakci(f, p, len, treat_as_reject)
+    fsm *f;
+    u_char *p;
+    int len;
+    int treat_as_reject;
+{
+    u_char citype, cilen, *next;
+    u_short s;
+    u_int32_t l;
+    ipxcp_options no;		/* options we've seen Naks for */
+    ipxcp_options try;		/* options to request next time */
+
+    BZERO(&no, sizeof(no));
+    try = *go;
+
+    while (len >= CILEN_VOID) {
+	GETCHAR (citype, p);
+	GETCHAR (cilen,	 p);
+	len -= cilen;
+	if (cilen < CILEN_VOID || len < 0)
+	    goto bad;
+	next = &p [cilen - CILEN_VOID];
+
+	switch (citype) {
+	case IPX_NETWORK_NUMBER:
+	    if (!go->neg_nn || no.neg_nn || (cilen != CILEN_NETN))
+		goto bad;
+	    no.neg_nn = 1;
+
+	    GETLONG(l, p);
+	    if (treat_as_reject)
+		try.neg_nn = 0;
+	    else if (l && ao->accept_network)
+		try.our_network = l;
+	    break;
+
+	case IPX_NODE_NUMBER:
+	    if (!go->neg_node || no.neg_node || (cilen != CILEN_NODEN))
+		goto bad;
+	    no.neg_node = 1;
+
+	    if (treat_as_reject)
+		try.neg_node = 0;
+	    else if (!zero_node (p) && ao->accept_local &&
+		     ! compare_node (p, ho->his_node))
+		copy_node (p, try.our_node);
+	    break;
+
+	    /* This has never been sent. Ignore the NAK frame */
+	case IPX_COMPRESSION_PROTOCOL:
+	    goto bad;
+
+	case IPX_ROUTER_PROTOCOL:
+	    if (!go->neg_router || (cilen < CILEN_PROTOCOL))
+		goto bad;
+
+	    GETSHORT (s, p);
+	    if (s > 15)         /* This is just bad, but ignore for now. */
+	        break;
+
+	    s = BIT(s);
+	    if (no.router & s)  /* duplicate NAKs are always bad */
+		goto bad;
+
+	    if (no.router == 0) /* Reset on first NAK only */
+		try.router = 0;
+
+	    no.router      |= s;
+	    try.router     |= s;
+	    try.neg_router  = 1;
+	    break;
+
+	    /* These, according to the RFC, must never be NAKed. */
+	case IPX_ROUTER_NAME:
+	case IPX_COMPLETE:
+	    goto bad;
+
+	    /* These are for options which we have not seen. */
+	default:
+	    break;
+	}
+	p = next;
+    }
+
+    /*
+     * Do not permit the peer to force a router protocol which we do not
+     * support. However, default to the condition that will accept "NONE".
+     */
+    try.router &= (ao->router | BIT(IPX_NONE));
+    if (try.router == 0 && ao->router != 0)
+	try.router = BIT(IPX_NONE);
+
+    if (try.router != 0)
+        try.neg_router = 1;
+    
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     * If there are any options left, we ignore them.
+     */
+    if (f->state != OPENED)
+	*go = try;
+
+    return 1;
+
+bad:
+    IPXCPDEBUG(("ipxcp_nakci: received bad Nak!"));
+    return 0;
+}
+
+/*
+ * ipxcp_rejci - Reject some of our CIs.
+ */
+static int
+ipxcp_rejci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    u_short cilen, citype, cishort;
+    u_char cichar;
+    u_int32_t cilong;
+    ipxcp_options try;		/* options to request next time */
+
+#define REJCINETWORK(opt, neg, val) \
+    if (neg && p[0] == opt) { \
+	if ((len -= CILEN_NETN) < 0) \
+	    break; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_NETN || \
+	    citype != opt) \
+	    break; \
+	GETLONG(cilong, p); \
+	if (cilong != val) \
+	    break; \
+	neg = 0; \
+    }
+
+#define REJCICHARS(opt, neg, val, cnt) \
+    if (neg && p[0] == opt) { \
+	int indx, count = cnt; \
+	len -= (count + 2); \
+	if (len < 0) \
+	    break; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != (count + 2) || \
+	    citype != opt) \
+	    break; \
+	for (indx = 0; indx < count; ++indx) {\
+	    GETCHAR(cichar, p); \
+	    if (cichar != ((u_char *) &val)[indx]) \
+	       break; \
+	}\
+	if (indx != count) \
+	    break; \
+	neg = 0; \
+    }
+
+#define REJCINODE(opt,neg,val) REJCICHARS(opt,neg,val,sizeof(val))
+#define REJCINAME(opt,neg,val) REJCICHARS(opt,neg,val,strlen((char *)val))
+
+#define REJCIVOID(opt, neg) \
+    if (neg && p[0] == opt) { \
+	if ((len -= CILEN_VOID) < 0) \
+	    break; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_VOID || citype != opt) \
+	    break; \
+	neg = 0; \
+    }
+
+/* a reject for RIP/SAP is invalid since we don't send it and you can't
+   reject something which is not sent. (You can NAK, but you can't REJ.) */
+#define REJCIPROTO(opt, neg, val, bit) \
+    if (neg && p[0] == opt) { \
+	if ((len -= CILEN_PROTOCOL) < 0) \
+	    break; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_PROTOCOL) \
+	    break; \
+	GETSHORT(cishort, p); \
+	if (cishort != to_external (val) || cishort == RIP_SAP) \
+	    break; \
+	neg = 0; \
+    }
+/*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+    try = *go;
+
+    do {
+	REJCINETWORK (IPX_NETWORK_NUMBER,  try.neg_nn,	   try.our_network);
+	REJCINODE    (IPX_NODE_NUMBER,	   try.neg_node,   try.our_node);
+	REJCINAME    (IPX_ROUTER_NAME,	   try.neg_name,   try.name);
+	REJCIPROTO   (IPX_ROUTER_PROTOCOL, try.neg_router, try.router, 0);
+/*
+ * This is the end of the record.
+ */
+	if (len == 0) {
+	    if (f->state != OPENED)
+		*go = try;
+	    return (1);
+	}
+    } while (0);
+/*
+ * The frame is invalid at this point.
+ */
+    IPXCPDEBUG(("ipxcp_rejci: received bad Reject!"));
+    return 0;
+}
+
+/*
+ * ipxcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipxcp_reqci(f, inp, len, reject_if_disagree)
+    fsm *f;
+    u_char *inp;		/* Requested CIs */
+    int *len;			/* Length of requested CIs */
+    int reject_if_disagree;
+{
+    u_char *cip, *next;		/* Pointer to current and next CIs */
+    u_short cilen, citype;	/* Parsed len, type */
+    u_short cishort;		/* Parsed short value */
+    u_int32_t cinetwork;	/* Parsed address values */
+    int rc = CONFACK;		/* Final packet return code */
+    int orc;			/* Individual option return code */
+    u_char *p;			/* Pointer to next char to parse */
+    u_char *ucp = inp;		/* Pointer to current output char */
+    int l = *len;		/* Length left */
+
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+    
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    while (l) {
+	orc = CONFACK;			/* Assume success */
+	cip = p = next;			/* Remember begining of CI */
+	if (l < 2 ||			/* Not enough data for CI header or */
+	    p[1] < 2 ||			/*  CI length too small or */
+	    p[1] > l) {			/*  CI length too big? */
+	    IPXCPDEBUG(("ipxcp_reqci: bad CI length!"));
+	    orc = CONFREJ;		/* Reject bad CI */
+	    cilen = l;			/* Reject till end of packet */
+	    l = 0;			/* Don't loop again */
+	    goto endswitch;
+	}
+	GETCHAR(citype, p);		/* Parse CI type */
+	GETCHAR(cilen, p);		/* Parse CI length */
+	l -= cilen;			/* Adjust remaining length */
+	next += cilen;			/* Step to next CI */
+
+	switch (citype) {		/* Check CI type */
+/*
+ * The network number must match. Choose the larger of the two.
+ */
+	case IPX_NETWORK_NUMBER:
+	    /* if we wont negotiate the network number or the length is wrong
+	       then reject the option */
+	    if ( !ao->neg_nn || cilen != CILEN_NETN ) {
+		orc = CONFREJ;
+		break;		
+	    }
+	    GETLONG(cinetwork, p);
+
+	    /* If the network numbers match then acknowledge them. */
+	    if (cinetwork != 0) {
+		ho->his_network = cinetwork;
+		ho->neg_nn	= 1;
+		if (wo->our_network == cinetwork)
+		    break;
+/*
+ * If the network number is not given or we don't accept their change or
+ * the network number is too small then NAK it.
+ */
+		if (! ao->accept_network || cinetwork < wo->our_network) {
+		    DECPTR (sizeof (u_int32_t), p);
+		    PUTLONG (wo->our_network, p);
+		    orc = CONFNAK;
+		}
+		break;
+	    }
+/*
+ * The peer sent '0' for the network. Give it ours if we have one.
+ */
+	    if (go->our_network != 0) {
+		DECPTR (sizeof (u_int32_t), p);
+		PUTLONG (wo->our_network, p);
+		orc = CONFNAK;
+/*
+ * We don't have one. Reject the value.
+ */
+	    } else
+		orc = CONFREJ;
+
+	    break;
+/*
+ * The node number is required
+ */
+	case IPX_NODE_NUMBER:
+	    /* if we wont negotiate the node number or the length is wrong
+	       then reject the option */
+	    if ( cilen != CILEN_NODEN ) {
+		orc = CONFREJ;
+		break;
+	    }
+
+	    copy_node (p, ho->his_node);
+	    ho->neg_node = 1;
+/*
+ * If the remote does not have a number and we do then NAK it with the value
+ * which we have for it. (We never have a default value of zero.)
+ */
+	    if (zero_node (ho->his_node)) {
+		orc = CONFNAK;
+		copy_node (wo->his_node, p);
+		INCPTR (sizeof (wo->his_node), p);
+		break;
+	    }
+/*
+ * If you have given me the expected network node number then I'll accept
+ * it now.
+ */
+	    if (compare_node (wo->his_node, ho->his_node)) {
+		orc = CONFACK;
+		ho->neg_node = 1;
+		INCPTR (sizeof (wo->his_node), p);
+		break;
+	    }
+/*
+ * If his node number is the same as ours then ask him to try the next
+ * value.
+ */
+	    if (compare_node (ho->his_node, go->our_node)) {
+		inc_node (ho->his_node);
+		orc = CONFNAK;
+		copy_node (ho->his_node, p);
+		INCPTR (sizeof (wo->his_node), p);
+		break;
+	    }
+/*
+ * If we don't accept a new value then NAK it.
+ */
+	    if (! ao->accept_remote) {
+		copy_node (wo->his_node, p);
+		INCPTR (sizeof (wo->his_node), p);
+		orc = CONFNAK;
+		break;
+	    }
+	    orc = CONFACK;
+	    ho->neg_node = 1;
+	    INCPTR (sizeof (wo->his_node), p);
+	    break;
+/*
+ * Compression is not desired at this time. It is always rejected.
+ */
+	case IPX_COMPRESSION_PROTOCOL:
+	    orc = CONFREJ;
+	    break;
+/*
+ * The routing protocol is a bitmask of various types. Any combination
+ * of the values RIP_SAP and NLSP are permissible. 'IPX_NONE' for no
+ * routing protocol must be specified only once.
+ */
+	case IPX_ROUTER_PROTOCOL:
+	    if ( !ao->neg_router || cilen < CILEN_PROTOCOL ) {
+		orc = CONFREJ;
+		break;		
+	    }
+
+	    GETSHORT (cishort, p);
+
+	    if (wo->neg_router == 0) {
+	        wo->neg_router = 1;
+		wo->router     = BIT(IPX_NONE);
+	    }
+
+	    if ((cishort == IPX_NONE && ho->router != 0) ||
+		(ho->router & BIT(IPX_NONE))) {
+		orc = CONFREJ;
+		break;
+	    }
+
+	    cishort = BIT(cishort);
+	    if (ho->router & cishort) {
+		orc = CONFREJ;
+		break;
+	    }
+
+	    ho->router	  |= cishort;
+	    ho->neg_router = 1;
+
+	    /* Finally do not allow a router protocol which we do not
+	       support. */
+
+	    if ((cishort & (ao->router | BIT(IPX_NONE))) == 0) {
+	        int protocol;
+
+		if (cishort == BIT(NLSP) &&
+		    (ao->router & BIT(RIP_SAP)) &&
+		    !wo->tried_rip) {
+		    protocol      = RIP_SAP;
+		    wo->tried_rip = 1;
+		} else
+		    protocol = IPX_NONE;
+
+		DECPTR (sizeof (u_int16_t), p);
+		PUTSHORT (protocol, p);
+		orc = CONFNAK;
+	    }
+	    break;
+/*
+ * The router name is advisorary. Just accept it if it is not too large.
+ */
+	case IPX_ROUTER_NAME:
+	    if (cilen >= CILEN_NAME) {
+		int name_size = cilen - CILEN_NAME;
+		if (name_size > sizeof (ho->name))
+		    name_size = sizeof (ho->name) - 1;
+		memset (ho->name, 0, sizeof (ho->name));
+		memcpy (ho->name, p, name_size);
+		ho->name [name_size] = '\0';
+		ho->neg_name = 1;
+		orc = CONFACK;
+		break;
+	    }
+	    orc = CONFREJ;
+	    break;
+/*
+ * This is advisorary.
+ */
+	case IPX_COMPLETE:
+	    if (cilen != CILEN_COMPLETE)
+		orc = CONFREJ;
+	    else {
+		ho->neg_complete = 1;
+		orc = CONFACK;
+	    }
+	    break;
+/*
+ * All other entries are not known at this time.
+ */
+	default:
+	    orc = CONFREJ;
+	    break;
+	}
+endswitch:
+	if (orc == CONFACK &&		/* Good CI */
+	    rc != CONFACK)		/*  but prior CI wasnt? */
+	    continue;			/* Don't send this one */
+
+	if (orc == CONFNAK) {		/* Nak this CI? */
+	    if (reject_if_disagree)	/* Getting fed up with sending NAKs? */
+		orc = CONFREJ;		/* Get tough if so */
+	    if (rc == CONFREJ)		/* Rejecting prior CI? */
+		continue;		/* Don't send this one */
+	    if (rc == CONFACK) {	/* Ack'd all prior CIs? */
+		rc  = CONFNAK;		/* Not anymore... */
+		ucp = inp;		/* Backup */
+	    }
+	}
+
+	if (orc == CONFREJ &&		/* Reject this CI */
+	    rc != CONFREJ) {		/*  but no prior ones? */
+	    rc = CONFREJ;
+	    ucp = inp;			/* Backup */
+	}
+
+	/* Need to move CI? */
+	if (ucp != cip)
+	    BCOPY(cip, ucp, cilen);	/* Move it */
+
+	/* Update output pointer */
+	INCPTR(cilen, ucp);
+    }
+
+    /*
+     * If we aren't rejecting this packet, and we want to negotiate
+     * their address, and they didn't send their address, then we
+     * send a NAK with a IPX_NODE_NUMBER option appended. We assume the
+     * input buffer is long enough that we can append the extra
+     * option safely.
+     */
+
+    if (rc != CONFREJ && !ho->neg_node &&
+	wo->req_nn && !reject_if_disagree) {
+	if (rc == CONFACK) {
+	    rc = CONFNAK;
+	    wo->req_nn = 0;		/* don't ask again */
+	    ucp = inp;			/* reset pointer */
+	}
+
+	if (zero_node (wo->his_node))
+	    inc_node (wo->his_node);
+
+	PUTCHAR (IPX_NODE_NUMBER, ucp);
+	PUTCHAR (CILEN_NODEN, ucp);
+	copy_node (wo->his_node, ucp);
+	INCPTR (sizeof (wo->his_node), ucp);
+    }
+
+    *len = ucp - inp;			/* Compute output length */
+    IPXCPDEBUG(("ipxcp: returning Configure-%s", CODENAME(rc)));
+    return (rc);			/* Return final code */
+}
+
+/*
+ * ipxcp_up - IPXCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+
+static void
+ipxcp_up(f)
+    fsm *f;
+{
+    int unit = f->unit;
+
+    IPXCPDEBUG(("ipxcp: up"));
+
+    /* The default router protocol is RIP/SAP. */
+    if (ho->router == 0)
+        ho->router = BIT(RIP_SAP);
+
+    if (go->router == 0)
+        go->router = BIT(RIP_SAP);
+
+    /* Fetch the network number */
+    if (!ho->neg_nn)
+	ho->his_network = wo->his_network;
+
+    if (!ho->neg_node)
+	copy_node (wo->his_node, ho->his_node);
+
+    if (!wo->neg_node && !go->neg_node)
+	copy_node (wo->our_node, go->our_node);
+
+    if (zero_node (go->our_node)) {
+        static char errmsg[] = "Could not determine local IPX node address";
+	if (debug)
+	    error(errmsg);
+	ipxcp_close(f->unit, errmsg);
+	return;
+    }
+
+    go->network = go->our_network;
+    if (ho->his_network != 0 && ho->his_network > go->network)
+	go->network = ho->his_network;
+
+    if (go->network == 0) {
+        static char errmsg[] = "Can not determine network number";
+	if (debug)
+	    error(errmsg);
+	ipxcp_close (unit, errmsg);
+	return;
+    }
+
+    /* bring the interface up */
+    if (!sifup(unit)) {
+	if (debug)
+	    warn("sifup failed (IPX)");
+	ipxcp_close(unit, "Interface configuration failed");
+	return;
+    }
+    ipxcp_is_up = 1;
+
+    /* set the network number for IPX */
+    if (!sipxfaddr(unit, go->network, go->our_node)) {
+	if (debug)
+	    warn("sipxfaddr failed");
+	ipxcp_close(unit, "Interface configuration failed");
+	return;
+    }
+
+    np_up(f->unit, PPP_IPX);
+
+    /*
+     * Execute the ipx-up script, like this:
+     *	/etc/ppp/ipx-up interface tty speed local-IPX remote-IPX
+     */
+
+    ipxcp_script (f, _PATH_IPXUP);
+}
+
+/*
+ * ipxcp_down - IPXCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+
+static void
+ipxcp_down(f)
+    fsm *f;
+{
+    IPXCPDEBUG(("ipxcp: down"));
+
+    if (!ipxcp_is_up)
+	return;
+    ipxcp_is_up = 0;
+    np_down(f->unit, PPP_IPX);
+    cipxfaddr(f->unit);
+    sifnpmode(f->unit, PPP_IPX, NPMODE_DROP);
+    sifdown(f->unit);
+    ipxcp_script (f, _PATH_IPXDOWN);
+}
+
+
+/*
+ * ipxcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipxcp_finished(f)
+    fsm *f;
+{
+    np_finished(f->unit, PPP_IPX);
+}
+
+
+/*
+ * ipxcp_script - Execute a script with arguments
+ * interface-name tty-name speed local-IPX remote-IPX networks.
+ */
+static void
+ipxcp_script(f, script)
+    fsm *f;
+    char *script;
+{
+    char strspeed[32],	 strlocal[32],	   strremote[32];
+    char strnetwork[32], strpid[32];
+    char *argv[14],	 strproto_lcl[32], strproto_rmt[32];
+
+    slprintf(strpid, sizeof(strpid), "%d", getpid());
+    slprintf(strspeed, sizeof(strspeed),"%d", baud_rate);
+
+    strproto_lcl[0] = '\0';
+    if (go->neg_router && ((go->router & BIT(IPX_NONE)) == 0)) {
+	if (go->router & BIT(RIP_SAP))
+	    strlcpy (strproto_lcl, "RIP ", sizeof(strproto_lcl));
+	if (go->router & BIT(NLSP))
+	    strlcat (strproto_lcl, "NLSP ", sizeof(strproto_lcl));
+    }
+
+    if (strproto_lcl[0] == '\0')
+	strlcpy (strproto_lcl, "NONE ", sizeof(strproto_lcl));
+
+    strproto_lcl[strlen (strproto_lcl)-1] = '\0';
+
+    strproto_rmt[0] = '\0';
+    if (ho->neg_router && ((ho->router & BIT(IPX_NONE)) == 0)) {
+	if (ho->router & BIT(RIP_SAP))
+	    strlcpy (strproto_rmt, "RIP ", sizeof(strproto_rmt));
+	if (ho->router & BIT(NLSP))
+	    strlcat (strproto_rmt, "NLSP ", sizeof(strproto_rmt));
+    }
+
+    if (strproto_rmt[0] == '\0')
+	strlcpy (strproto_rmt, "NONE ", sizeof(strproto_rmt));
+
+    strproto_rmt[strlen (strproto_rmt)-1] = '\0';
+
+    strlcpy (strnetwork, ipx_ntoa (go->network), sizeof(strnetwork));
+
+    slprintf (strlocal, sizeof(strlocal), "%0.6B", go->our_node);
+
+    slprintf (strremote, sizeof(strremote), "%0.6B", ho->his_node);
+
+    argv[0]  = script;
+    argv[1]  = ifname;
+    argv[2]  = devnam;
+    argv[3]  = strspeed;
+    argv[4]  = strnetwork;
+    argv[5]  = strlocal;
+    argv[6]  = strremote;
+    argv[7]  = strproto_lcl;
+    argv[8]  = strproto_rmt;
+    argv[9]  = (char *)go->name;
+    argv[10] = (char *)ho->name;
+    argv[11] = ipparam;
+    argv[12] = strpid;
+    argv[13] = NULL;
+    run_program(script, argv, 0, NULL, NULL, 0);
+}
+
+/*
+ * ipxcp_printpkt - print the contents of an IPXCP packet.
+ */
+static char *ipxcp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipxcp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    int code, id, len, olen;
+    u_char *pstart, *optend;
+    u_short cishort;
+    u_int32_t cilong;
+
+    if (plen < HEADERLEN)
+	return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+	return 0;
+
+    if (code >= 1 && code <= sizeof(ipxcp_codenames) / sizeof(char *))
+	printer(arg, " %s", ipxcp_codenames[code-1]);
+    else
+	printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+	/* print option list */
+	while (len >= 2) {
+	    GETCHAR(code, p);
+	    GETCHAR(olen, p);
+	    p -= 2;
+	    if (olen < CILEN_VOID || olen > len) {
+		break;
+	    }
+	    printer(arg, " <");
+	    len -= olen;
+	    optend = p + olen;
+	    switch (code) {
+	    case IPX_NETWORK_NUMBER:
+		if (olen == CILEN_NETN) {
+		    p += 2;
+		    GETLONG(cilong, p);
+		    printer (arg, "network %s", ipx_ntoa (cilong));
+		}
+		break;
+	    case IPX_NODE_NUMBER:
+		if (olen == CILEN_NODEN) {
+		    p += 2;
+		    printer (arg, "node ");
+		    while (p < optend) {
+			GETCHAR(code, p);
+			printer(arg, "%.2x", (int) (unsigned int) (unsigned char) code);
+		    }
+		}
+		break;
+	    case IPX_COMPRESSION_PROTOCOL:
+		if (olen == CILEN_COMPRESS) {
+		    p += 2;
+		    GETSHORT (cishort, p);
+		    printer (arg, "compression %d", (int) cishort);
+		}
+		break;
+	    case IPX_ROUTER_PROTOCOL:
+		if (olen == CILEN_PROTOCOL) {
+		    p += 2;
+		    GETSHORT (cishort, p);
+		    printer (arg, "router proto %d", (int) cishort);
+		}
+		break;
+	    case IPX_ROUTER_NAME:
+		if (olen >= CILEN_NAME) {
+		    p += 2;
+		    printer (arg, "router name \"");
+		    while (p < optend) {
+			GETCHAR(code, p);
+			if (code >= 0x20 && code <= 0x7E)
+			    printer (arg, "%c", (int) (unsigned int) (unsigned char) code);
+			else
+			    printer (arg, " \\%.2x", (int) (unsigned int) (unsigned char) code);
+		    }
+		    printer (arg, "\"");
+		}
+		break;
+	    case IPX_COMPLETE:
+		if (olen == CILEN_COMPLETE) {
+		    p += 2;
+		    printer (arg, "complete");
+		}
+		break;
+	    default:
+		break;
+	    }
+
+	    while (p < optend) {
+		GETCHAR(code, p);
+		printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
+	    }
+	    printer(arg, ">");
+	}
+	break;
+
+    case TERMACK:
+    case TERMREQ:
+	if (len > 0 && *p >= ' ' && *p < 0x7f) {
+	    printer(arg, " ");
+	    print_string((char *)p, len, printer, arg);
+	    p += len;
+	    len = 0;
+	}
+	break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (; len > 0; --len) {
+	GETCHAR(code, p);
+	printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
+    }
+
+    return p - pstart;
+}
+#endif /* ifdef IPX_CHANGE */
diff --git a/ap/app/pppd/pppd/ipxcp.h b/ap/app/pppd/pppd/ipxcp.h
new file mode 100644
index 0000000..a5fc70a
--- /dev/null
+++ b/ap/app/pppd/pppd/ipxcp.h
@@ -0,0 +1,94 @@
+/*
+ * ipxcp.h - IPX Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipxcp.h,v 1.2 2007-06-08 04:02:38 gerg Exp $
+ */
+
+/*
+ * Options.
+ */
+#define IPX_NETWORK_NUMBER        1   /* IPX Network Number */
+#define IPX_NODE_NUMBER           2
+#define IPX_COMPRESSION_PROTOCOL  3
+#define IPX_ROUTER_PROTOCOL       4
+#define IPX_ROUTER_NAME           5
+#define IPX_COMPLETE              6
+
+/* Values for the router protocol */
+#define IPX_NONE		  0
+#define RIP_SAP			  2
+#define NLSP			  4
+
+typedef struct ipxcp_options {
+    bool neg_node;		/* Negotiate IPX node number? */
+    bool req_node;		/* Ask peer to send IPX node number? */
+
+    bool neg_nn;		/* Negotiate IPX network number? */
+    bool req_nn;		/* Ask peer to send IPX network number */
+
+    bool neg_name;		/* Negotiate IPX router name */
+    bool neg_complete;		/* Negotiate completion */
+    bool neg_router;		/* Negotiate IPX router number */
+
+    bool accept_local;		/* accept peer's value for ournode */
+    bool accept_remote;		/* accept peer's value for hisnode */
+    bool accept_network;	/* accept network number */
+
+    bool tried_nlsp;		/* I have suggested NLSP already */
+    bool tried_rip;		/* I have suggested RIP/SAP already */
+
+    u_int32_t his_network;	/* base network number */
+    u_int32_t our_network;	/* our value for network number */
+    u_int32_t network;		/* the final network number */
+
+    u_char his_node[6];		/* peer's node number */
+    u_char our_node[6];		/* our node number */
+    u_char name [48];		/* name of the router */
+    int    router;		/* routing protocol */
+} ipxcp_options;
+
+extern fsm ipxcp_fsm[];
+extern ipxcp_options ipxcp_wantoptions[];
+extern ipxcp_options ipxcp_gotoptions[];
+extern ipxcp_options ipxcp_allowoptions[];
+extern ipxcp_options ipxcp_hisoptions[];
+
+extern struct protent ipxcp_protent;
diff --git a/ap/app/pppd/pppd/lcp.c b/ap/app/pppd/pppd/lcp.c
new file mode 100644
index 0000000..2947b74
--- /dev/null
+++ b/ap/app/pppd/pppd/lcp.c
@@ -0,0 +1,2369 @@
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: lcp.c,v 1.2 2007-06-08 04:02:38 gerg Exp $"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "chap-new.h"
+#include "magic.h"
+
+static const char rcsid[] = RCSID;
+
+/*
+ * When the link comes up we want to be able to wait for a short while,
+ * or until seeing some input from the peer, before starting to send
+ * configure-requests.  We do this by delaying the fsm_lowerup call.
+ */
+/* steal a bit in fsm flags word */
+#define DELAYED_UP	0x100
+
+static void lcp_delayed_up __P((void *));
+
+/*
+ * LCP-related command-line options.
+ */
+int	lcp_echo_interval = 0; 	/* Interval between LCP echo-requests */
+int	lcp_echo_fails = 0;	/* Tolerance to unanswered echo-requests */
+bool	lax_recv = 0;		/* accept control chars in asyncmap */
+bool	noendpoint = 0;		/* don't send/accept endpoint discriminator */
+
+static int noopt __P((char **));
+
+#ifdef HAVE_MULTILINK
+static int setendpoint __P((char **));
+static void printendpoint __P((option_t *, void (*)(void *, char *, ...),
+			       void *));
+#endif /* HAVE_MULTILINK */
+
+static option_t lcp_option_list[] = {
+    /* LCP options */
+    { "-all", o_special_noarg, (void *)noopt,
+      "Don't request/allow any LCP options" },
+
+    { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression,
+      "Disable address/control compression",
+      OPT_A2CLR, &lcp_allowoptions[0].neg_accompression },
+    { "-ac", o_bool, &lcp_wantoptions[0].neg_accompression,
+      "Disable address/control compression",
+      OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_accompression },
+
+    { "asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap,
+      "Set asyncmap (for received packets)",
+      OPT_OR, &lcp_wantoptions[0].neg_asyncmap },
+    { "-as", o_uint32, &lcp_wantoptions[0].asyncmap,
+      "Set asyncmap (for received packets)",
+      OPT_ALIAS | OPT_OR, &lcp_wantoptions[0].neg_asyncmap },
+    { "default-asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap,
+      "Disable asyncmap negotiation",
+      OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR,
+      &lcp_allowoptions[0].neg_asyncmap },
+    { "-am", o_uint32, &lcp_wantoptions[0].asyncmap,
+      "Disable asyncmap negotiation",
+      OPT_ALIAS | OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR,
+      &lcp_allowoptions[0].neg_asyncmap },
+
+    { "nomagic", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+      "Disable magic number negotiation (looped-back line detection)",
+      OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber },
+    { "-mn", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+      "Disable magic number negotiation (looped-back line detection)",
+      OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber },
+
+    { "mru", o_int, &lcp_wantoptions[0].mru,
+      "Set MRU (maximum received packet size) for negotiation",
+      OPT_PRIO, &lcp_wantoptions[0].neg_mru },
+    { "default-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+      "Disable MRU negotiation (use default 1500)",
+      OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru },
+    { "-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+      "Disable MRU negotiation (use default 1500)",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru },
+
+    { "mtu", o_int, &lcp_allowoptions[0].mru,
+      "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU },
+
+    { "nopcomp", o_bool, &lcp_wantoptions[0].neg_pcompression,
+      "Disable protocol field compression",
+      OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression },
+    { "-pc", o_bool, &lcp_wantoptions[0].neg_pcompression,
+      "Disable protocol field compression",
+      OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression },
+
+    { "passive", o_bool, &lcp_wantoptions[0].passive,
+      "Set passive mode", 1 },
+    { "-p", o_bool, &lcp_wantoptions[0].passive,
+      "Set passive mode", OPT_ALIAS | 1 },
+
+    { "silent", o_bool, &lcp_wantoptions[0].silent,
+      "Set silent mode", 1 },
+
+    { "lcp-echo-failure", o_int, &lcp_echo_fails,
+      "Set number of consecutive echo failures to indicate link failure",
+      OPT_PRIO },
+    { "lcp-echo-interval", o_int, &lcp_echo_interval,
+      "Set time in seconds between LCP echo requests", OPT_PRIO },
+    { "lcp-restart", o_int, &lcp_fsm[0].timeouttime,
+      "Set time in seconds between LCP retransmissions", OPT_PRIO },
+    { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits,
+      "Set maximum number of LCP terminate-request transmissions", OPT_PRIO },
+    { "lcp-max-configure", o_int, &lcp_fsm[0].maxconfreqtransmits,
+      "Set maximum number of LCP configure-request transmissions", OPT_PRIO },
+    { "lcp-max-failure", o_int, &lcp_fsm[0].maxnakloops,
+      "Set limit on number of LCP configure-naks", OPT_PRIO },
+
+    { "receive-all", o_bool, &lax_recv,
+      "Accept all received control characters", 1 },
+
+#ifdef HAVE_MULTILINK
+    { "mrru", o_int, &lcp_wantoptions[0].mrru,
+      "Maximum received packet size for multilink bundle",
+      OPT_PRIO, &lcp_wantoptions[0].neg_mrru },
+
+    { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+      "Use short sequence numbers in multilink headers",
+      OPT_PRIO | 1, &lcp_allowoptions[0].neg_ssnhf },
+    { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+      "Don't use short sequence numbers in multilink headers",
+      OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_ssnhf },
+
+    { "endpoint", o_special, (void *) setendpoint,
+      "Endpoint discriminator for multilink",
+      OPT_PRIO | OPT_A2PRINTER, (void *) printendpoint },
+#endif /* HAVE_MULTILINK */
+
+    { "noendpoint", o_bool, &noendpoint,
+      "Don't send or accept multilink endpoint discriminator", 1 },
+
+    {NULL}
+};
+
+/* global vars */
+fsm lcp_fsm[NUM_PPP];			/* LCP fsm structure (global)*/
+lcp_options lcp_wantoptions[NUM_PPP];	/* Options that we want to request */
+lcp_options lcp_gotoptions[NUM_PPP];	/* Options that peer ack'd */
+lcp_options lcp_allowoptions[NUM_PPP];	/* Options we allow peer to request */
+lcp_options lcp_hisoptions[NUM_PPP];	/* Options that we ack'd */
+
+static int lcp_echos_pending = 0;	/* Number of outstanding echo msgs */
+static int lcp_echo_number   = 0;	/* ID number of next echo frame */
+static int lcp_echo_timer_running = 0;  /* set if a timer is running */
+
+static u_char nak_buffer[PPP_MRU];	/* where we construct a nak packet */
+
+/*
+ * Callbacks for fsm code.  (CI = Configuration Information)
+ */
+static void lcp_resetci __P((fsm *));	/* Reset our CI */
+static int  lcp_cilen __P((fsm *));		/* Return length of our CI */
+static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */
+static int  lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int  lcp_nakci __P((fsm *, u_char *, int, int)); /* Peer nak'd our CI */
+static int  lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int  lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */
+static void lcp_up __P((fsm *));		/* We're UP */
+static void lcp_down __P((fsm *));		/* We're DOWN */
+static void lcp_starting __P((fsm *));	/* We need lower layer up */
+static void lcp_finished __P((fsm *));	/* We need lower layer down */
+static int  lcp_extcode __P((fsm *, int, int, u_char *, int));
+static void lcp_rprotrej __P((fsm *, u_char *, int));
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup __P((int));
+static void lcp_echo_lowerdown __P((int));
+static void LcpEchoTimeout __P((void *));
+static void lcp_received_echo_reply __P((fsm *, int, u_char *, int));
+static void LcpSendEchoRequest __P((fsm *));
+static void LcpLinkFailure __P((fsm *));
+static void LcpEchoCheck __P((fsm *));
+
+static fsm_callbacks lcp_callbacks = {	/* LCP callback routines */
+    lcp_resetci,		/* Reset our Configuration Information */
+    lcp_cilen,			/* Length of our Configuration Information */
+    lcp_addci,			/* Add our Configuration Information */
+    lcp_ackci,			/* ACK our Configuration Information */
+    lcp_nakci,			/* NAK our Configuration Information */
+    lcp_rejci,			/* Reject our Configuration Information */
+    lcp_reqci,			/* Request peer's Configuration Information */
+    lcp_up,			/* Called when fsm reaches OPENED state */
+    lcp_down,			/* Called when fsm leaves OPENED state */
+    lcp_starting,		/* Called when we want the lower layer up */
+    lcp_finished,		/* Called when we want the lower layer down */
+    NULL,			/* Called when Protocol-Reject received */
+    NULL,			/* Retransmission is necessary */
+    lcp_extcode,		/* Called to handle LCP-specific codes */
+    "LCP"			/* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ * Some of these are called directly.
+ */
+
+static void lcp_init __P((int));
+static void lcp_input __P((int, u_char *, int));
+static void lcp_protrej __P((int));
+static int  lcp_printpkt __P((u_char *, int,
+			      void (*) __P((void *, char *, ...)), void *));
+
+struct protent lcp_protent = {
+    PPP_LCP,
+    lcp_init,
+    lcp_input,
+    lcp_protrej,
+    lcp_lowerup,
+    lcp_lowerdown,
+    lcp_open,
+    lcp_close,
+    lcp_printpkt,
+    NULL,
+    1,
+    "LCP",
+    NULL,
+    lcp_option_list,
+    NULL,
+    NULL,
+    NULL
+};
+
+int lcp_loopbackfail = DEFLOOPBACKFAIL;
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID	2
+#define CILEN_CHAR	3
+#define CILEN_SHORT	4	/* CILEN_VOID + 2 */
+#define CILEN_CHAP	5	/* CILEN_VOID + 2 + 1 */
+#define CILEN_LONG	6	/* CILEN_VOID + 4 */
+#define CILEN_LQR	8	/* CILEN_VOID + 2 + 4 */
+#define CILEN_CBCP	3
+
+#define CODENAME(x)	((x) == CONFACK ? "ACK" : \
+			 (x) == CONFNAK ? "NAK" : "REJ")
+
+/*
+ * noopt - Disable all options (why?).
+ */
+static int
+noopt(argv)
+    char **argv;
+{
+    BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
+    BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
+
+    return (1);
+}
+
+#ifdef HAVE_MULTILINK
+static int
+setendpoint(argv)
+    char **argv;
+{
+    if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) {
+	lcp_wantoptions[0].neg_endpoint = 1;
+	return 1;
+    }
+    option_error("Can't parse '%s' as an endpoint discriminator", *argv);
+    return 0;
+}
+
+static void
+printendpoint(opt, printer, arg)
+    option_t *opt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	printer(arg, "%s", epdisc_to_str(&lcp_wantoptions[0].endpoint));
+}
+#endif /* HAVE_MULTILINK */
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+static void
+lcp_init(unit)
+    int unit;
+{
+    fsm *f = &lcp_fsm[unit];
+    lcp_options *wo = &lcp_wantoptions[unit];
+    lcp_options *ao = &lcp_allowoptions[unit];
+
+    f->unit = unit;
+    f->protocol = PPP_LCP;
+    f->callbacks = &lcp_callbacks;
+
+    fsm_init(f);
+
+    BZERO(wo, sizeof(*wo));
+    wo->neg_mru = 1;
+    wo->mru = DEFMRU;
+    wo->neg_asyncmap = 1;
+    wo->neg_magicnumber = 1;
+    wo->neg_pcompression = 1;
+    wo->neg_accompression = 1;
+
+    BZERO(ao, sizeof(*ao));
+    ao->neg_mru = 1;
+    ao->mru = MAXMRU;
+    ao->neg_asyncmap = 1;
+    ao->neg_chap = 1;
+    ao->chap_mdtype = chap_mdtype_all;
+    ao->neg_upap = 1;
+    ao->neg_eap = 1;
+    ao->neg_magicnumber = 1;
+    ao->neg_pcompression = 1;
+    ao->neg_accompression = 1;
+    ao->neg_endpoint = 1;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void
+lcp_open(unit)
+    int unit;
+{
+    fsm *f = &lcp_fsm[unit];
+    lcp_options *wo = &lcp_wantoptions[unit];
+
+    f->flags &= ~(OPT_PASSIVE | OPT_SILENT);
+    if (wo->passive)
+	f->flags |= OPT_PASSIVE;
+    if (wo->silent)
+	f->flags |= OPT_SILENT;
+    fsm_open(f);
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void
+lcp_close(unit, reason)
+    int unit;
+    char *reason;
+{
+    fsm *f = &lcp_fsm[unit];
+
+    if (phase != PHASE_DEAD && phase != PHASE_MASTER)
+	new_phase(PHASE_TERMINATE);
+    if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
+	/*
+	 * This action is not strictly according to the FSM in RFC1548,
+	 * but it does mean that the program terminates if you do a
+	 * lcp_close() in passive/silent mode when a connection hasn't
+	 * been established.
+	 */
+	f->state = CLOSED;
+	lcp_finished(f);
+
+    } else
+	fsm_close(f, reason);
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void
+lcp_lowerup(unit)
+    int unit;
+{
+    lcp_options *wo = &lcp_wantoptions[unit];
+    fsm *f = &lcp_fsm[unit];
+
+    /*
+     * Don't use A/C or protocol compression on transmission,
+     * but accept A/C and protocol compressed packets
+     * if we are going to ask for A/C and protocol compression.
+     */
+    if (ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0) < 0
+	|| ppp_recv_config(unit, PPP_MRU, (lax_recv? 0: 0xffffffff),
+			   wo->neg_pcompression, wo->neg_accompression) < 0)
+	    return;
+    peer_mru[unit] = PPP_MRU;
+
+    if (listen_time != 0) {
+	f->flags |= DELAYED_UP;
+	timeout(lcp_delayed_up, f, 0, listen_time * 1000);
+    } else
+	fsm_lowerup(f);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void
+lcp_lowerdown(unit)
+    int unit;
+{
+    fsm *f = &lcp_fsm[unit];
+
+    if (f->flags & DELAYED_UP)
+	f->flags &= ~DELAYED_UP;
+    else
+	fsm_lowerdown(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_delayed_up - Bring the lower layer up now.
+ */
+static void
+lcp_delayed_up(arg)
+    void *arg;
+{
+    fsm *f = arg;
+
+    if (f->flags & DELAYED_UP) {
+	f->flags &= ~DELAYED_UP;
+	fsm_lowerup(f);
+    }
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void
+lcp_input(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    fsm *f = &lcp_fsm[unit];
+
+    if (f->flags & DELAYED_UP) {
+	f->flags &= ~DELAYED_UP;
+	fsm_lowerup(f);
+    }
+    fsm_input(f, p, len);
+}
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int
+lcp_extcode(f, code, id, inp, len)
+    fsm *f;
+    int code, id;
+    u_char *inp;
+    int len;
+{
+    u_char *magp;
+
+    switch( code ){
+    case PROTREJ:
+	lcp_rprotrej(f, inp, len);
+	break;
+    
+    case ECHOREQ:
+	if (f->state != OPENED)
+	    break;
+	magp = inp;
+	PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
+	fsm_sdata(f, ECHOREP, id, inp, len);
+	break;
+    
+    case ECHOREP:
+	lcp_received_echo_reply(f, id, inp, len);
+	break;
+
+    case DISCREQ:
+    case IDENTIF:
+    case TIMEREM:
+	break;
+
+    default:
+	return 0;
+    }
+    return 1;
+}
+
+    
+/*
+ * lcp_rprotrej - Receive an Protocol-Reject.
+ *
+ * Figure out which protocol is rejected and inform it.
+ */
+static void
+lcp_rprotrej(f, inp, len)
+    fsm *f;
+    u_char *inp;
+    int len;
+{
+    int i;
+    struct protent *protp;
+    u_short prot;
+    const char *pname;
+
+    if (len < 2) {
+	LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
+	return;
+    }
+
+    GETSHORT(prot, inp);
+
+    /*
+     * Protocol-Reject packets received in any state other than the LCP
+     * OPENED state SHOULD be silently discarded.
+     */
+    if( f->state != OPENED ){
+	LCPDEBUG(("Protocol-Reject discarded: LCP in state %d", f->state));
+	return;
+    }
+
+    pname = protocol_name(prot);
+
+    /*
+     * Upcall the proper Protocol-Reject routine.
+     */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if (protp->protocol == prot && protp->enabled_flag) {
+	    if (pname == NULL)
+		warn("Protocol-Reject for 0x%x received", prot);
+	    else
+		warn("Protocol-Reject for '%s' (0x%x) received", pname,
+		       prot);
+	    (*protp->protrej)(f->unit);
+	    return;
+	}
+
+    if (pname == NULL)
+	warn("Protocol-Reject for unsupported protocol 0x%x", prot);
+    else
+	warn("Protocol-Reject for unsupported protocol '%s' (0x%x)", pname,
+	     prot);
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+/*ARGSUSED*/
+static void
+lcp_protrej(unit)
+    int unit;
+{
+    /*
+     * Can't reject LCP!
+     */
+    error("Received Protocol-Reject for LCP!");
+    fsm_protreject(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void
+lcp_sprotrej(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    /*
+     * Send back the protocol and the information field of the
+     * rejected packet.  We only get here if LCP is in the OPENED state.
+     */
+    p += 2;
+    len -= 2;
+
+    fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id,
+	      p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void
+lcp_resetci(f)
+    fsm *f;
+{
+    lcp_options *wo = &lcp_wantoptions[f->unit];
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    lcp_options *ao = &lcp_allowoptions[f->unit];
+
+    wo->magicnumber = magic();
+    wo->numloops = 0;
+    *go = *wo;
+    if (!multilink) {
+	go->neg_mrru = 0;
+	go->neg_ssnhf = 0;
+	go->neg_endpoint = 0;
+    }
+    if (noendpoint)
+	ao->neg_endpoint = 0;
+    peer_mru[f->unit] = PPP_MRU;
+    auth_reset(f->unit);
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int
+lcp_cilen(f)
+    fsm *f;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+
+#define LENCIVOID(neg)	((neg) ? CILEN_VOID : 0)
+#define LENCICHAP(neg)	((neg) ? CILEN_CHAP : 0)
+#define LENCISHORT(neg)	((neg) ? CILEN_SHORT : 0)
+#define LENCILONG(neg)	((neg) ? CILEN_LONG : 0)
+#define LENCILQR(neg)	((neg) ? CILEN_LQR: 0)
+#define LENCICBCP(neg)	((neg) ? CILEN_CBCP: 0)
+    /*
+     * NB: we only ask for one of CHAP, UPAP, or EAP, even if we will
+     * accept more than one.  We prefer EAP first, then CHAP, then
+     * PAP.
+     */
+    return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) +
+	    LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) +
+	    LENCISHORT(go->neg_eap) +
+	    LENCICHAP(!go->neg_eap && go->neg_chap) +
+	    LENCISHORT(!go->neg_eap && !go->neg_chap && go->neg_upap) +
+	    LENCILQR(go->neg_lqr) +
+	    LENCICBCP(go->neg_cbcp) +
+	    LENCILONG(go->neg_magicnumber) +
+	    LENCIVOID(go->neg_pcompression) +
+	    LENCIVOID(go->neg_accompression) +
+	    LENCISHORT(go->neg_mrru) +
+	    LENCIVOID(go->neg_ssnhf) +
+	    (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void
+lcp_addci(f, ucp, lenp)
+    fsm *f;
+    u_char *ucp;
+    int *lenp;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+    if (neg) { \
+	PUTCHAR(opt, ucp); \
+	PUTCHAR(CILEN_VOID, ucp); \
+    }
+#define ADDCISHORT(opt, neg, val) \
+    if (neg) { \
+	PUTCHAR(opt, ucp); \
+	PUTCHAR(CILEN_SHORT, ucp); \
+	PUTSHORT(val, ucp); \
+    }
+#define ADDCICHAP(opt, neg, val) \
+    if (neg) { \
+	PUTCHAR((opt), ucp); \
+	PUTCHAR(CILEN_CHAP, ucp); \
+	PUTSHORT(PPP_CHAP, ucp); \
+	PUTCHAR((CHAP_DIGEST(val)), ucp); \
+    }
+#define ADDCILONG(opt, neg, val) \
+    if (neg) { \
+	PUTCHAR(opt, ucp); \
+	PUTCHAR(CILEN_LONG, ucp); \
+	PUTLONG(val, ucp); \
+    }
+#define ADDCILQR(opt, neg, val) \
+    if (neg) { \
+	PUTCHAR(opt, ucp); \
+	PUTCHAR(CILEN_LQR, ucp); \
+	PUTSHORT(PPP_LQR, ucp); \
+	PUTLONG(val, ucp); \
+    }
+#define ADDCICHAR(opt, neg, val) \
+    if (neg) { \
+	PUTCHAR(opt, ucp); \
+	PUTCHAR(CILEN_CHAR, ucp); \
+	PUTCHAR(val, ucp); \
+    }
+#define ADDCIENDP(opt, neg, class, val, len) \
+    if (neg) { \
+	int i; \
+	PUTCHAR(opt, ucp); \
+	PUTCHAR(CILEN_CHAR + len, ucp); \
+	PUTCHAR(class, ucp); \
+	for (i = 0; i < len; ++i) \
+	    PUTCHAR(val[i], ucp); \
+    }
+
+    ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
+    ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+	      go->asyncmap);
+    ADDCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+    ADDCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+    ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap,
+	       PPP_PAP);
+    ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+    ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+    ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+    ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+    ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+    ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+    ADDCIVOID(CI_SSNHF, go->neg_ssnhf);
+    ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+	      go->endpoint.value, go->endpoint.length);
+
+    if (ucp - start_ucp != *lenp) {
+	/* this should never happen, because peer_mtu should be 1500 */
+	error("Bug in lcp_addci: wrong length");
+    }
+}
+
+
+/*
+ * lcp_ackci - Ack our CIs.
+ * This should not modify any state if the Ack is bad.
+ *
+ * Returns:
+ *	0 - Ack was bad.
+ *	1 - Ack was good.
+ */
+static int
+lcp_ackci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    u_char cilen, citype, cichar;
+    u_short cishort;
+    u_int32_t cilong;
+
+    /*
+     * CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define ACKCIVOID(opt, neg) \
+    if (neg) { \
+	if ((len -= CILEN_VOID) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_VOID || \
+	    citype != opt) \
+	    goto bad; \
+    }
+#define ACKCISHORT(opt, neg, val) \
+    if (neg) { \
+	if ((len -= CILEN_SHORT) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_SHORT || \
+	    citype != opt) \
+	    goto bad; \
+	GETSHORT(cishort, p); \
+	if (cishort != val) \
+	    goto bad; \
+    }
+#define ACKCICHAR(opt, neg, val) \
+    if (neg) { \
+	if ((len -= CILEN_CHAR) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_CHAR || \
+	    citype != opt) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (cichar != val) \
+	    goto bad; \
+    }
+#define ACKCICHAP(opt, neg, val) \
+    if (neg) { \
+	if ((len -= CILEN_CHAP) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_CHAP || \
+	    citype != (opt)) \
+	    goto bad; \
+	GETSHORT(cishort, p); \
+	if (cishort != PPP_CHAP) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (cichar != (CHAP_DIGEST(val))) \
+	  goto bad; \
+    }
+#define ACKCILONG(opt, neg, val) \
+    if (neg) { \
+	if ((len -= CILEN_LONG) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_LONG || \
+	    citype != opt) \
+	    goto bad; \
+	GETLONG(cilong, p); \
+	if (cilong != val) \
+	    goto bad; \
+    }
+#define ACKCILQR(opt, neg, val) \
+    if (neg) { \
+	if ((len -= CILEN_LQR) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_LQR || \
+	    citype != opt) \
+	    goto bad; \
+	GETSHORT(cishort, p); \
+	if (cishort != PPP_LQR) \
+	    goto bad; \
+	GETLONG(cilong, p); \
+	if (cilong != val) \
+	  goto bad; \
+    }
+#define ACKCIENDP(opt, neg, class, val, vlen) \
+    if (neg) { \
+	int i; \
+	if ((len -= CILEN_CHAR + vlen) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_CHAR + vlen || \
+	    citype != opt) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (cichar != class) \
+	    goto bad; \
+	for (i = 0; i < vlen; ++i) { \
+	    GETCHAR(cichar, p); \
+	    if (cichar != val[i]) \
+		goto bad; \
+	} \
+    }
+
+    ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
+    ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+	      go->asyncmap);
+    ACKCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+    ACKCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+    ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap,
+	       PPP_PAP);
+    ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+    ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+    ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+    ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+    ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+    ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+    ACKCIVOID(CI_SSNHF, go->neg_ssnhf);
+    ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+	      go->endpoint.value, go->endpoint.length);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+	goto bad;
+    return (1);
+bad:
+    LCPDEBUG(("lcp_acki: received bad Ack!"));
+    return (0);
+}
+
+
+/*
+ * lcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ *	0 - Nak was bad.
+ *	1 - Nak was good.
+ */
+static int
+lcp_nakci(f, p, len, treat_as_reject)
+    fsm *f;
+    u_char *p;
+    int len;
+    int treat_as_reject;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    lcp_options *wo = &lcp_wantoptions[f->unit];
+    u_char citype, cichar, *next;
+    u_short cishort;
+    u_int32_t cilong;
+    lcp_options no;		/* options we've seen Naks for */
+    lcp_options try;		/* options to request next time */
+    int looped_back = 0;
+    int cilen;
+
+    BZERO(&no, sizeof(no));
+    try = *go;
+
+    /*
+     * Any Nak'd CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define NAKCIVOID(opt, neg) \
+    if (go->neg && \
+	len >= CILEN_VOID && \
+	p[1] == CILEN_VOID && \
+	p[0] == opt) { \
+	len -= CILEN_VOID; \
+	INCPTR(CILEN_VOID, p); \
+	no.neg = 1; \
+	try.neg = 0; \
+    }
+#define NAKCICHAP(opt, neg, code) \
+    if (go->neg && \
+	len >= CILEN_CHAP && \
+	p[1] == CILEN_CHAP && \
+	p[0] == opt) { \
+	len -= CILEN_CHAP; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	GETCHAR(cichar, p); \
+	no.neg = 1; \
+	code \
+    }
+#define NAKCICHAR(opt, neg, code) \
+    if (go->neg && \
+	len >= CILEN_CHAR && \
+	p[1] == CILEN_CHAR && \
+	p[0] == opt) { \
+	len -= CILEN_CHAR; \
+	INCPTR(2, p); \
+	GETCHAR(cichar, p); \
+	no.neg = 1; \
+	code \
+    }
+#define NAKCISHORT(opt, neg, code) \
+    if (go->neg && \
+	len >= CILEN_SHORT && \
+	p[1] == CILEN_SHORT && \
+	p[0] == opt) { \
+	len -= CILEN_SHORT; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	no.neg = 1; \
+	code \
+    }
+#define NAKCILONG(opt, neg, code) \
+    if (go->neg && \
+	len >= CILEN_LONG && \
+	p[1] == CILEN_LONG && \
+	p[0] == opt) { \
+	len -= CILEN_LONG; \
+	INCPTR(2, p); \
+	GETLONG(cilong, p); \
+	no.neg = 1; \
+	code \
+    }
+#define NAKCILQR(opt, neg, code) \
+    if (go->neg && \
+	len >= CILEN_LQR && \
+	p[1] == CILEN_LQR && \
+	p[0] == opt) { \
+	len -= CILEN_LQR; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	GETLONG(cilong, p); \
+	no.neg = 1; \
+	code \
+    }
+#define NAKCIENDP(opt, neg) \
+    if (go->neg && \
+	len >= CILEN_CHAR && \
+	p[0] == opt && \
+	p[1] >= CILEN_CHAR && \
+	p[1] <= len) { \
+	len -= p[1]; \
+	INCPTR(p[1], p); \
+	no.neg = 1; \
+	try.neg = 0; \
+    }
+
+    /*
+     * NOTE!  There must be no assignments to individual fields of *go in
+     * the code below.  Any such assignment is a BUG!
+     */
+    /*
+     * We don't care if they want to send us smaller packets than
+     * we want.  Therefore, accept any MRU less than what we asked for,
+     * but then ignore the new value when setting the MRU in the kernel.
+     * If they send us a bigger MRU than what we asked, accept it, up to
+     * the limit of the default MRU we'd get if we didn't negotiate.
+     */
+    if (go->neg_mru && go->mru != DEFMRU) {
+	NAKCISHORT(CI_MRU, neg_mru,
+		   if (cishort <= wo->mru || cishort <= DEFMRU)
+		       try.mru = cishort;
+		   );
+    }
+
+    /*
+     * Add any characters they want to our (receive-side) asyncmap.
+     */
+    if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) {
+	NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
+		  try.asyncmap = go->asyncmap | cilong;
+		  );
+    }
+
+    /*
+     * If they've nak'd our authentication-protocol, check whether
+     * they are proposing a different protocol, or a different
+     * hash algorithm for CHAP.
+     */
+    if ((go->neg_chap || go->neg_upap || go->neg_eap)
+	&& len >= CILEN_SHORT
+	&& p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
+	cilen = p[1];
+	len -= cilen;
+	no.neg_chap = go->neg_chap;
+	no.neg_upap = go->neg_upap;
+	no.neg_eap = go->neg_eap;
+	INCPTR(2, p);
+	GETSHORT(cishort, p);
+	if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+	    /* If we were asking for EAP, then we need to stop that. */
+	    if (go->neg_eap)
+		try.neg_eap = 0;
+
+	    /* If we were asking for CHAP, then we need to stop that. */
+	    else if (go->neg_chap)
+		try.neg_chap = 0;
+	    /*
+	     * If we weren't asking for CHAP or EAP, then we were asking for
+	     * PAP, in which case this Nak is bad.
+	     */
+	    else
+		goto bad;
+
+	} else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
+	    GETCHAR(cichar, p);
+	    /* Stop asking for EAP, if we were. */
+	    if (go->neg_eap) {
+		try.neg_eap = 0;
+		/* Try to set up to use their suggestion, if possible */
+		if (CHAP_CANDIGEST(go->chap_mdtype, cichar))
+		    try.chap_mdtype = CHAP_MDTYPE_D(cichar);
+	    } else if (go->neg_chap) {
+		/*
+		 * We were asking for our preferred algorithm, they must
+		 * want something different.
+		 */
+		if (cichar != CHAP_DIGEST(go->chap_mdtype)) {
+		    if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) {
+			/* Use their suggestion if we support it ... */
+			try.chap_mdtype = CHAP_MDTYPE_D(cichar);
+		    } else {
+			/* ... otherwise, try our next-preferred algorithm. */
+			try.chap_mdtype &= ~(CHAP_MDTYPE(try.chap_mdtype));
+			if (try.chap_mdtype == MDTYPE_NONE) /* out of algos */
+			    try.neg_chap = 0;
+		    }
+		} else {
+		    /*
+		     * Whoops, they Nak'd our algorithm of choice
+		     * but then suggested it back to us.
+		     */
+		    goto bad;
+		}
+	    } else {
+		/*
+		 * Stop asking for PAP if we were asking for it.
+		 */
+		try.neg_upap = 0;
+	    }
+
+	} else {
+
+	    /*
+	     * If we were asking for EAP, and they're Conf-Naking EAP,
+	     * well, that's just strange.  Nobody should do that.
+	     */
+	    if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap)
+		warn("Unexpected Conf-Nak for EAP");
+
+	    /*
+	     * We don't recognize what they're suggesting.
+	     * Stop asking for what we were asking for.
+	     */
+	    if (go->neg_eap)
+		try.neg_eap = 0;
+	    else if (go->neg_chap)
+		try.neg_chap = 0;
+	    else
+		try.neg_upap = 0;
+	    p += cilen - CILEN_SHORT;
+	}
+    }
+
+    /*
+     * If they can't cope with our link quality protocol, we'll have
+     * to stop asking for LQR.  We haven't got any other protocol.
+     * If they Nak the reporting period, take their value XXX ?
+     */
+    NAKCILQR(CI_QUALITY, neg_lqr,
+	     if (cishort != PPP_LQR)
+		 try.neg_lqr = 0;
+	     else
+		 try.lqr_period = cilong;
+	     );
+
+    /*
+     * Only implementing CBCP...not the rest of the callback options
+     */
+    NAKCICHAR(CI_CALLBACK, neg_cbcp,
+              try.neg_cbcp = 0;
+              );
+
+    /*
+     * Check for a looped-back line.
+     */
+    NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
+	      try.magicnumber = magic();
+	      looped_back = 1;
+	      );
+
+    /*
+     * Peer shouldn't send Nak for protocol compression or
+     * address/control compression requests; they should send
+     * a Reject instead.  If they send a Nak, treat it as a Reject.
+     */
+    NAKCIVOID(CI_PCOMPRESSION, neg_pcompression);
+    NAKCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+    /*
+     * Nak for MRRU option - accept their value if it is smaller
+     * than the one we want.
+     */
+    if (go->neg_mrru) {
+	NAKCISHORT(CI_MRRU, neg_mrru,
+		   if (treat_as_reject)
+		       try.neg_mrru = 0;
+		   else if (cishort <= wo->mrru)
+		       try.mrru = cishort;
+		   );
+    }
+
+    /*
+     * Nak for short sequence numbers shouldn't be sent, treat it
+     * like a reject.
+     */
+    NAKCIVOID(CI_SSNHF, neg_ssnhf);
+
+    /*
+     * Nak of the endpoint discriminator option is not permitted,
+     * treat it like a reject.
+     */
+    NAKCIENDP(CI_EPDISC, neg_endpoint);
+
+    /*
+     * There may be remaining CIs, if the peer is requesting negotiation
+     * on an option that we didn't include in our request packet.
+     * If we see an option that we requested, or one we've already seen
+     * in this packet, then this packet is bad.
+     * If we wanted to respond by starting to negotiate on the requested
+     * option(s), we could, but we don't, because except for the
+     * authentication type and quality protocol, if we are not negotiating
+     * an option, it is because we were told not to.
+     * For the authentication type, the Nak from the peer means
+     * `let me authenticate myself with you' which is a bit pointless.
+     * For the quality protocol, the Nak means `ask me to send you quality
+     * reports', but if we didn't ask for them, we don't want them.
+     * An option we don't recognize represents the peer asking to
+     * negotiate some option we don't support, so ignore it.
+     */
+    while (len >= CILEN_VOID) {
+	GETCHAR(citype, p);
+	GETCHAR(cilen, p);
+	if (cilen < CILEN_VOID || (len -= cilen) < 0)
+	    goto bad;
+	next = p + cilen - 2;
+
+	switch (citype) {
+	case CI_MRU:
+	    if ((go->neg_mru && go->mru != DEFMRU)
+		|| no.neg_mru || cilen != CILEN_SHORT)
+		goto bad;
+	    GETSHORT(cishort, p);
+	    if (cishort < DEFMRU) {
+		try.neg_mru = 1;
+		try.mru = cishort;
+	    }
+	    break;
+	case CI_ASYNCMAP:
+	    if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF)
+		|| no.neg_asyncmap || cilen != CILEN_LONG)
+		goto bad;
+	    break;
+	case CI_AUTHTYPE:
+	    if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap ||
+		go->neg_eap || no.neg_eap)
+		goto bad;
+	    break;
+	case CI_MAGICNUMBER:
+	    if (go->neg_magicnumber || no.neg_magicnumber ||
+		cilen != CILEN_LONG)
+		goto bad;
+	    break;
+	case CI_PCOMPRESSION:
+	    if (go->neg_pcompression || no.neg_pcompression
+		|| cilen != CILEN_VOID)
+		goto bad;
+	    break;
+	case CI_ACCOMPRESSION:
+	    if (go->neg_accompression || no.neg_accompression
+		|| cilen != CILEN_VOID)
+		goto bad;
+	    break;
+	case CI_QUALITY:
+	    if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
+		goto bad;
+	    break;
+	case CI_MRRU:
+	    if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT)
+		goto bad;
+	    break;
+	case CI_SSNHF:
+	    if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID)
+		goto bad;
+	    try.neg_ssnhf = 1;
+	    break;
+	case CI_EPDISC:
+	    if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR)
+		goto bad;
+	    break;
+	}
+	p = next;
+    }
+
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     * If there are any options left we ignore them.
+     */
+    if (f->state != OPENED) {
+	if (looped_back) {
+	    if (++try.numloops >= lcp_loopbackfail) {
+		warn("Serial line is looped back.");
+		status = EXIT_LOOPBACK;
+		lcp_close(f->unit, "Loopback detected");
+	    }
+	} else
+	    try.numloops = 0;
+	*go = try;
+    }
+
+    return 1;
+
+bad:
+    LCPDEBUG(("lcp_nakci: received bad Nak!"));
+    return 0;
+}
+
+
+/*
+ * lcp_rejci - Peer has Rejected some of our CIs.
+ * This should not modify any state if the Reject is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ *	0 - Reject was bad.
+ *	1 - Reject was good.
+ */
+static int
+lcp_rejci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    u_char cichar;
+    u_short cishort;
+    u_int32_t cilong;
+    lcp_options try;		/* options to request next time */
+
+    try = *go;
+
+    /*
+     * Any Rejected CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define REJCIVOID(opt, neg) \
+    if (go->neg && \
+	len >= CILEN_VOID && \
+	p[1] == CILEN_VOID && \
+	p[0] == opt) { \
+	len -= CILEN_VOID; \
+	INCPTR(CILEN_VOID, p); \
+	try.neg = 0; \
+    }
+#define REJCISHORT(opt, neg, val) \
+    if (go->neg && \
+	len >= CILEN_SHORT && \
+	p[1] == CILEN_SHORT && \
+	p[0] == opt) { \
+	len -= CILEN_SHORT; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	/* Check rejected value. */ \
+	if (cishort != val) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+#define REJCICHAP(opt, neg, val) \
+    if (go->neg && \
+	len >= CILEN_CHAP && \
+	p[1] == CILEN_CHAP && \
+	p[0] == opt) { \
+	len -= CILEN_CHAP; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	GETCHAR(cichar, p); \
+	/* Check rejected value. */ \
+	if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
+	    goto bad; \
+	try.neg = 0; \
+	try.neg_eap = try.neg_upap = 0; \
+    }
+#define REJCILONG(opt, neg, val) \
+    if (go->neg && \
+	len >= CILEN_LONG && \
+	p[1] == CILEN_LONG && \
+	p[0] == opt) { \
+	len -= CILEN_LONG; \
+	INCPTR(2, p); \
+	GETLONG(cilong, p); \
+	/* Check rejected value. */ \
+	if (cilong != val) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+#define REJCILQR(opt, neg, val) \
+    if (go->neg && \
+	len >= CILEN_LQR && \
+	p[1] == CILEN_LQR && \
+	p[0] == opt) { \
+	len -= CILEN_LQR; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	GETLONG(cilong, p); \
+	/* Check rejected value. */ \
+	if (cishort != PPP_LQR || cilong != val) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+#define REJCICBCP(opt, neg, val) \
+    if (go->neg && \
+	len >= CILEN_CBCP && \
+	p[1] == CILEN_CBCP && \
+	p[0] == opt) { \
+	len -= CILEN_CBCP; \
+	INCPTR(2, p); \
+	GETCHAR(cichar, p); \
+	/* Check rejected value. */ \
+	if (cichar != val) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+#define REJCIENDP(opt, neg, class, val, vlen) \
+    if (go->neg && \
+	len >= CILEN_CHAR + vlen && \
+	p[0] == opt && \
+	p[1] == CILEN_CHAR + vlen) { \
+	int i; \
+	len -= CILEN_CHAR + vlen; \
+	INCPTR(2, p); \
+	GETCHAR(cichar, p); \
+	if (cichar != class) \
+	    goto bad; \
+	for (i = 0; i < vlen; ++i) { \
+	    GETCHAR(cichar, p); \
+	    if (cichar != val[i]) \
+		goto bad; \
+	} \
+	try.neg = 0; \
+    }
+
+    REJCISHORT(CI_MRU, neg_mru, go->mru);
+    REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+    REJCISHORT(CI_AUTHTYPE, neg_eap, PPP_EAP);
+    if (!go->neg_eap) {
+	REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype);
+	if (!go->neg_chap) {
+	    REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+	}
+    }
+    REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+    REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
+    REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+    REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+    REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+    REJCISHORT(CI_MRRU, neg_mrru, go->mrru);
+    REJCIVOID(CI_SSNHF, neg_ssnhf);
+    REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class,
+	      go->endpoint.value, go->endpoint.length);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+	goto bad;
+    /*
+     * Now we can update state.
+     */
+    if (f->state != OPENED)
+	*go = try;
+    return 1;
+
+bad:
+    LCPDEBUG(("lcp_rejci: received bad Reject!"));
+    return 0;
+}
+
+
+/*
+ * lcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+lcp_reqci(f, inp, lenp, reject_if_disagree)
+    fsm *f;
+    u_char *inp;		/* Requested CIs */
+    int *lenp;			/* Length of requested CIs */
+    int reject_if_disagree;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    lcp_options *ho = &lcp_hisoptions[f->unit];
+    lcp_options *ao = &lcp_allowoptions[f->unit];
+    u_char *cip, *next;		/* Pointer to current and next CIs */
+    int cilen, citype, cichar;	/* Parsed len, type, char value */
+    u_short cishort;		/* Parsed short value */
+    u_int32_t cilong;		/* Parse long value */
+    int rc = CONFACK;		/* Final packet return code */
+    int orc;			/* Individual option return code */
+    u_char *p;			/* Pointer to next char to parse */
+    u_char *rejp;		/* Pointer to next char in reject frame */
+    u_char *nakp;		/* Pointer to next char in Nak frame */
+    int l = *lenp;		/* Length left */
+
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    nakp = nak_buffer;
+    rejp = inp;
+    while (l) {
+	orc = CONFACK;			/* Assume success */
+	cip = p = next;			/* Remember begining of CI */
+	if (l < 2 ||			/* Not enough data for CI header or */
+	    p[1] < 2 ||			/*  CI length too small or */
+	    p[1] > l) {			/*  CI length too big? */
+	    LCPDEBUG(("lcp_reqci: bad CI length!"));
+	    orc = CONFREJ;		/* Reject bad CI */
+	    cilen = l;			/* Reject till end of packet */
+	    l = 0;			/* Don't loop again */
+	    citype = 0;
+	    goto endswitch;
+	}
+	GETCHAR(citype, p);		/* Parse CI type */
+	GETCHAR(cilen, p);		/* Parse CI length */
+	l -= cilen;			/* Adjust remaining length */
+	next += cilen;			/* Step to next CI */
+
+	switch (citype) {		/* Check CI type */
+	case CI_MRU:
+	    if (!ao->neg_mru ||		/* Allow option? */
+		cilen != CILEN_SHORT) {	/* Check CI length */
+		orc = CONFREJ;		/* Reject CI */
+		break;
+	    }
+	    GETSHORT(cishort, p);	/* Parse MRU */
+
+	    /*
+	     * He must be able to receive at least our minimum.
+	     * No need to check a maximum.  If he sends a large number,
+	     * we'll just ignore it.
+	     */
+	    if (cishort < MINMRU) {
+		orc = CONFNAK;		/* Nak CI */
+		PUTCHAR(CI_MRU, nakp);
+		PUTCHAR(CILEN_SHORT, nakp);
+		PUTSHORT(MINMRU, nakp);	/* Give him a hint */
+		break;
+	    }
+	    ho->neg_mru = 1;		/* Remember he sent MRU */
+	    ho->mru = cishort;		/* And remember value */
+	    break;
+
+	case CI_ASYNCMAP:
+	    if (!ao->neg_asyncmap ||
+		cilen != CILEN_LONG) {
+		orc = CONFREJ;
+		break;
+	    }
+	    GETLONG(cilong, p);
+
+	    /*
+	     * Asyncmap must have set at least the bits
+	     * which are set in lcp_allowoptions[unit].asyncmap.
+	     */
+	    if ((ao->asyncmap & ~cilong) != 0) {
+		orc = CONFNAK;
+		PUTCHAR(CI_ASYNCMAP, nakp);
+		PUTCHAR(CILEN_LONG, nakp);
+		PUTLONG(ao->asyncmap | cilong, nakp);
+		break;
+	    }
+	    ho->neg_asyncmap = 1;
+	    ho->asyncmap = cilong;
+	    break;
+
+	case CI_AUTHTYPE:
+	    if (cilen < CILEN_SHORT ||
+		!(ao->neg_upap || ao->neg_chap || ao->neg_eap)) {
+		/*
+		 * Reject the option if we're not willing to authenticate.
+		 */
+		warn("No auth is possible");
+		orc = CONFREJ;
+		break;
+	    }
+	    GETSHORT(cishort, p);
+
+	    /*
+	     * Authtype must be PAP, CHAP, or EAP.
+	     *
+	     * Note: if more than one of ao->neg_upap, ao->neg_chap, and
+	     * ao->neg_eap are set, and the peer sends a Configure-Request
+	     * with two or more authenticate-protocol requests, then we will
+	     * reject the second request.
+	     * Whether we end up doing CHAP, UPAP, or EAP depends then on
+	     * the ordering of the CIs in the peer's Configure-Request.
+             */
+
+	    if (cishort == PPP_PAP) {
+		/* we've already accepted CHAP or EAP */
+		if (ho->neg_chap || ho->neg_eap ||
+		    cilen != CILEN_SHORT) {
+		    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
+		    orc = CONFREJ;
+		    break;
+		}
+		if (!ao->neg_upap) {	/* we don't want to do PAP */
+		    orc = CONFNAK;	/* NAK it and suggest CHAP or EAP */
+		    PUTCHAR(CI_AUTHTYPE, nakp);
+		    if (ao->neg_eap) {
+			PUTCHAR(CILEN_SHORT, nakp);
+			PUTSHORT(PPP_EAP, nakp);
+		    } else {
+			PUTCHAR(CILEN_CHAP, nakp);
+			PUTSHORT(PPP_CHAP, nakp);
+			PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
+		    }
+		    break;
+		}
+		ho->neg_upap = 1;
+		break;
+	    }
+	    if (cishort == PPP_CHAP) {
+		/* we've already accepted PAP or EAP */
+		if (ho->neg_upap || ho->neg_eap ||
+		    cilen != CILEN_CHAP) {
+		    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
+		    orc = CONFREJ;
+		    break;
+		}
+		if (!ao->neg_chap) {	/* we don't want to do CHAP */
+		    orc = CONFNAK;	/* NAK it and suggest EAP or PAP */
+		    PUTCHAR(CI_AUTHTYPE, nakp);
+		    PUTCHAR(CILEN_SHORT, nakp);
+		    if (ao->neg_eap) {
+			PUTSHORT(PPP_EAP, nakp);
+		    } else {
+			PUTSHORT(PPP_PAP, nakp);
+		    }
+		    break;
+		}
+		GETCHAR(cichar, p);	/* get digest type */
+		if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) {
+		    /*
+		     * We can't/won't do the requested type,
+		     * suggest something else.
+		     */
+		    orc = CONFNAK;
+		    PUTCHAR(CI_AUTHTYPE, nakp);
+		    PUTCHAR(CILEN_CHAP, nakp);
+		    PUTSHORT(PPP_CHAP, nakp);
+		    PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
+		    break;
+		}
+		ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */
+		ho->neg_chap = 1;
+		break;
+	    }
+	    if (cishort == PPP_EAP) {
+		/* we've already accepted CHAP or PAP */
+		if (ho->neg_chap || ho->neg_upap || cilen != CILEN_SHORT) {
+		    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting..."));
+		    orc = CONFREJ;
+		    break;
+		}
+		if (!ao->neg_eap) {	/* we don't want to do EAP */
+		    orc = CONFNAK;	/* NAK it and suggest CHAP or PAP */
+		    PUTCHAR(CI_AUTHTYPE, nakp);
+		    if (ao->neg_chap) {
+			PUTCHAR(CILEN_CHAP, nakp);
+			PUTSHORT(PPP_CHAP, nakp);
+			PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
+		    } else {
+			PUTCHAR(CILEN_SHORT, nakp);
+			PUTSHORT(PPP_PAP, nakp);
+		    }
+		    break;
+		}
+		ho->neg_eap = 1;
+		break;
+	    }
+
+	    /*
+	     * We don't recognize the protocol they're asking for.
+	     * Nak it with something we're willing to do.
+	     * (At this point we know ao->neg_upap || ao->neg_chap ||
+	     * ao->neg_eap.)
+	     */
+	    orc = CONFNAK;
+	    PUTCHAR(CI_AUTHTYPE, nakp);
+	    if (ao->neg_eap) {
+		PUTCHAR(CILEN_SHORT, nakp);
+		PUTSHORT(PPP_EAP, nakp);
+	    } else if (ao->neg_chap) {
+		PUTCHAR(CILEN_CHAP, nakp);
+		PUTSHORT(PPP_CHAP, nakp);
+		PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
+	    } else {
+		PUTCHAR(CILEN_SHORT, nakp);
+		PUTSHORT(PPP_PAP, nakp);
+	    }
+	    break;
+
+	case CI_QUALITY:
+	    if (!ao->neg_lqr ||
+		cilen != CILEN_LQR) {
+		orc = CONFREJ;
+		break;
+	    }
+
+	    GETSHORT(cishort, p);
+	    GETLONG(cilong, p);
+
+	    /*
+	     * Check the protocol and the reporting period.
+	     * XXX When should we Nak this, and what with?
+	     */
+	    if (cishort != PPP_LQR) {
+		orc = CONFNAK;
+		PUTCHAR(CI_QUALITY, nakp);
+		PUTCHAR(CILEN_LQR, nakp);
+		PUTSHORT(PPP_LQR, nakp);
+		PUTLONG(ao->lqr_period, nakp);
+		break;
+	    }
+	    break;
+
+	case CI_MAGICNUMBER:
+	    if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
+		cilen != CILEN_LONG) {
+		orc = CONFREJ;
+		break;
+	    }
+	    GETLONG(cilong, p);
+
+	    /*
+	     * He must have a different magic number.
+	     */
+	    if (go->neg_magicnumber &&
+		cilong == go->magicnumber) {
+		cilong = magic();	/* Don't put magic() inside macro! */
+		orc = CONFNAK;
+		PUTCHAR(CI_MAGICNUMBER, nakp);
+		PUTCHAR(CILEN_LONG, nakp);
+		PUTLONG(cilong, nakp);
+		break;
+	    }
+	    ho->neg_magicnumber = 1;
+	    ho->magicnumber = cilong;
+	    break;
+
+
+	case CI_PCOMPRESSION:
+	    if (!ao->neg_pcompression ||
+		cilen != CILEN_VOID) {
+		orc = CONFREJ;
+		break;
+	    }
+	    ho->neg_pcompression = 1;
+	    break;
+
+	case CI_ACCOMPRESSION:
+	    if (!ao->neg_accompression ||
+		cilen != CILEN_VOID) {
+		orc = CONFREJ;
+		break;
+	    }
+	    ho->neg_accompression = 1;
+	    break;
+
+	case CI_MRRU:
+	    if (!ao->neg_mrru || !multilink ||
+		cilen != CILEN_SHORT) {
+		orc = CONFREJ;
+		break;
+	    }
+
+	    GETSHORT(cishort, p);
+	    /* possibly should insist on a minimum/maximum MRRU here */
+	    ho->neg_mrru = 1;
+	    ho->mrru = cishort;
+	    break;
+
+	case CI_SSNHF:
+	    if (!ao->neg_ssnhf || !multilink ||
+		cilen != CILEN_VOID) {
+		orc = CONFREJ;
+		break;
+	    }
+	    ho->neg_ssnhf = 1;
+	    break;
+
+	case CI_EPDISC:
+	    if (!ao->neg_endpoint ||
+		cilen < CILEN_CHAR ||
+		cilen > CILEN_CHAR + MAX_ENDP_LEN) {
+		orc = CONFREJ;
+		break;
+	    }
+	    GETCHAR(cichar, p);
+	    cilen -= CILEN_CHAR;
+	    ho->neg_endpoint = 1;
+	    ho->endpoint.class = cichar;
+	    ho->endpoint.length = cilen;
+	    BCOPY(p, ho->endpoint.value, cilen);
+	    INCPTR(cilen, p);
+	    break;
+
+	default:
+	    LCPDEBUG(("lcp_reqci: rcvd unknown option %d", citype));
+	    orc = CONFREJ;
+	    break;
+	}
+
+endswitch:
+	if (orc == CONFACK &&		/* Good CI */
+	    rc != CONFACK)		/*  but prior CI wasnt? */
+	    continue;			/* Don't send this one */
+
+	if (orc == CONFNAK) {		/* Nak this CI? */
+	    if (reject_if_disagree	/* Getting fed up with sending NAKs? */
+		&& citype != CI_MAGICNUMBER) {
+		orc = CONFREJ;		/* Get tough if so */
+	    } else {
+		if (rc == CONFREJ)	/* Rejecting prior CI? */
+		    continue;		/* Don't send this one */
+		rc = CONFNAK;
+	    }
+	}
+	if (orc == CONFREJ) {		/* Reject this CI */
+	    rc = CONFREJ;
+	    if (cip != rejp)		/* Need to move rejected CI? */
+		BCOPY(cip, rejp, cilen); /* Move it */
+	    INCPTR(cilen, rejp);	/* Update output pointer */
+	}
+    }
+
+    /*
+     * If we wanted to send additional NAKs (for unsent CIs), the
+     * code would go here.  The extra NAKs would go at *nakp.
+     * At present there are no cases where we want to ask the
+     * peer to negotiate an option.
+     */
+
+    switch (rc) {
+    case CONFACK:
+	*lenp = next - inp;
+	break;
+    case CONFNAK:
+	/*
+	 * Copy the Nak'd options from the nak_buffer to the caller's buffer.
+	 */
+	*lenp = nakp - nak_buffer;
+	BCOPY(nak_buffer, inp, *lenp);
+	break;
+    case CONFREJ:
+	*lenp = rejp - inp;
+	break;
+    }
+
+    LCPDEBUG(("lcp_reqci: returning CONF%s.", CODENAME(rc)));
+    return (rc);			/* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ */
+static void
+lcp_up(f)
+    fsm *f;
+{
+    lcp_options *wo = &lcp_wantoptions[f->unit];
+    lcp_options *ho = &lcp_hisoptions[f->unit];
+    lcp_options *go = &lcp_gotoptions[f->unit];
+    lcp_options *ao = &lcp_allowoptions[f->unit];
+    int mtu, mru;
+    if (!go->neg_magicnumber)
+	go->magicnumber = 0;
+    if (!ho->neg_magicnumber)
+	ho->magicnumber = 0;
+
+    /*
+     * Set our MTU to the smaller of the MTU we wanted and
+     * the MRU our peer wanted.  If we negotiated an MRU,
+     * set our MRU to the larger of value we wanted and
+     * the value we got in the negotiation.
+     * Note on the MTU: the link MTU can be the MRU the peer wanted,
+     * the interface MTU is set to the lowest of that, the
+     * MTU we want to use, and our link MRU.
+     */
+    mtu = ho->neg_mru? ho->mru: PPP_MRU;
+    mru = go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU;
+#ifdef HAVE_MULTILINK
+    if (!(multilink && go->neg_mrru && ho->neg_mrru))
+#endif /* HAVE_MULTILINK */
+	netif_set_mtu(f->unit, MIN(MIN(mtu, mru), ao->mru));
+    ppp_send_config(f->unit, mtu,
+		    (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
+		    ho->neg_pcompression, ho->neg_accompression);
+    ppp_recv_config(f->unit, mru,
+		    (lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff),
+		    go->neg_pcompression, go->neg_accompression);
+
+    if (ho->neg_mru)
+	peer_mru[f->unit] = ho->mru;
+
+    lcp_echo_lowerup(f->unit);  /* Enable echo messages */
+    link_established(f->unit);
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void
+lcp_down(f)
+    fsm *f;
+{
+    lcp_options *go = &lcp_gotoptions[f->unit];
+
+    lcp_echo_lowerdown(f->unit);
+
+    link_down(f->unit);
+
+    ppp_send_config(f->unit, PPP_MRU, 0xffffffff, 0, 0);
+    ppp_recv_config(f->unit, PPP_MRU,
+		    (go->neg_asyncmap? go->asyncmap: 0xffffffff),
+		    go->neg_pcompression, go->neg_accompression);
+    peer_mru[f->unit] = PPP_MRU;
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void
+lcp_starting(f)
+    fsm *f;
+{
+    link_required(f->unit);
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void
+lcp_finished(f)
+    fsm *f;
+{
+    link_terminated(f->unit);
+	
+}
+
+
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+static char *lcp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej", "ProtRej",
+    "EchoReq", "EchoRep", "DiscReq", "Ident",
+    "TimeRem"
+};
+
+static int
+lcp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    int code, id, len, olen, i;
+    u_char *pstart, *optend;
+    u_short cishort;
+    u_int32_t cilong;
+
+    if (plen < HEADERLEN)
+	return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+	return 0;
+
+    if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *))
+	printer(arg, " %s", lcp_codenames[code-1]);
+    else
+	printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+	/* print option list */
+	while (len >= 2) {
+	    GETCHAR(code, p);
+	    GETCHAR(olen, p);
+	    p -= 2;
+	    if (olen < 2 || olen > len) {
+		break;
+	    }
+	    printer(arg, " <");
+	    len -= olen;
+	    optend = p + olen;
+	    switch (code) {
+	    case CI_MRU:
+		if (olen == CILEN_SHORT) {
+		    p += 2;
+		    GETSHORT(cishort, p);
+		    printer(arg, "mru %d", cishort);
+		}
+		break;
+	    case CI_ASYNCMAP:
+		if (olen == CILEN_LONG) {
+		    p += 2;
+		    GETLONG(cilong, p);
+		    printer(arg, "asyncmap 0x%x", cilong);
+		}
+		break;
+	    case CI_AUTHTYPE:
+		if (olen >= CILEN_SHORT) {
+		    p += 2;
+		    printer(arg, "auth ");
+		    GETSHORT(cishort, p);
+		    switch (cishort) {
+		    case PPP_PAP:
+			printer(arg, "pap");
+			break;
+		    case PPP_CHAP:
+			printer(arg, "chap");
+			if (p < optend) {
+			    switch (*p) {
+			    case CHAP_MD5:
+				printer(arg, " MD5");
+				++p;
+				break;
+			    case CHAP_MICROSOFT:
+				printer(arg, " MS");
+				++p;
+				break;
+
+			    case CHAP_MICROSOFT_V2:
+				printer(arg, " MS-v2");
+				++p;
+				break;
+			    }
+			}
+			break;
+		    case PPP_EAP:
+			printer(arg, "eap");
+			break;
+		    default:
+			printer(arg, "0x%x", cishort);
+		    }
+		}
+		break;
+	    case CI_QUALITY:
+		if (olen >= CILEN_SHORT) {
+		    p += 2;
+		    printer(arg, "quality ");
+		    GETSHORT(cishort, p);
+		    switch (cishort) {
+		    case PPP_LQR:
+			printer(arg, "lqr");
+			break;
+		    default:
+			printer(arg, "0x%x", cishort);
+		    }
+		}
+		break;
+	    case CI_CALLBACK:
+		if (olen >= CILEN_CHAR) {
+		    p += 2;
+		    printer(arg, "callback ");
+		    GETCHAR(cishort, p);
+		    switch (cishort) {
+		    case CBCP_OPT:
+			printer(arg, "CBCP");
+			break;
+		    default:
+			printer(arg, "0x%x", cishort);
+		    }
+		}
+		break;
+	    case CI_MAGICNUMBER:
+		if (olen == CILEN_LONG) {
+		    p += 2;
+		    GETLONG(cilong, p);
+		    printer(arg, "magic 0x%x", cilong);
+		}
+		break;
+	    case CI_PCOMPRESSION:
+		if (olen == CILEN_VOID) {
+		    p += 2;
+		    printer(arg, "pcomp");
+		}
+		break;
+	    case CI_ACCOMPRESSION:
+		if (olen == CILEN_VOID) {
+		    p += 2;
+		    printer(arg, "accomp");
+		}
+		break;
+	    case CI_MRRU:
+		if (olen == CILEN_SHORT) {
+		    p += 2;
+		    GETSHORT(cishort, p);
+		    printer(arg, "mrru %d", cishort);
+		}
+		break;
+	    case CI_SSNHF:
+		if (olen == CILEN_VOID) {
+		    p += 2;
+		    printer(arg, "ssnhf");
+		}
+		break;
+	    case CI_EPDISC:
+#ifdef HAVE_MULTILINK
+		if (olen >= CILEN_CHAR) {
+		    struct epdisc epd;
+		    p += 2;
+		    GETCHAR(epd.class, p);
+		    epd.length = olen - CILEN_CHAR;
+		    if (epd.length > MAX_ENDP_LEN)
+			epd.length = MAX_ENDP_LEN;
+		    if (epd.length > 0) {
+			BCOPY(p, epd.value, epd.length);
+			p += epd.length;
+		    }
+		    printer(arg, "endpoint [%s]", epdisc_to_str(&epd));
+		}
+#else
+		printer(arg, "endpoint");
+#endif
+		break;
+	    }
+	    while (p < optend) {
+		GETCHAR(code, p);
+		printer(arg, " %.2x", code);
+	    }
+	    printer(arg, ">");
+	}
+	break;
+
+    case TERMACK:
+    case TERMREQ:
+	if (len > 0 && *p >= ' ' && *p < 0x7f) {
+	    printer(arg, " ");
+	    print_string((char *)p, len, printer, arg);
+	    p += len;
+	    len = 0;
+	}
+	break;
+
+    case ECHOREQ:
+    case ECHOREP:
+    case DISCREQ:
+	if (len >= 4) {
+	    GETLONG(cilong, p);
+	    printer(arg, " magic=0x%x", cilong);
+	    len -= 4;
+	}
+	break;
+
+    case IDENTIF:
+    case TIMEREM:
+	if (len >= 4) {
+	    GETLONG(cilong, p);
+	    printer(arg, " magic=0x%x", cilong);
+	    len -= 4;
+	}
+	if (code == TIMEREM) {
+	    if (len < 4)
+		break;
+	    GETLONG(cilong, p);
+	    printer(arg, " seconds=%u", cilong);
+	    len -= 4;
+	}
+	if (len > 0) {
+	    printer(arg, " ");
+	    print_string((char *)p, len, printer, arg);
+	    p += len;
+	    len = 0;
+	}
+	break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (i = 0; i < len && i < 32; ++i) {
+	GETCHAR(code, p);
+	printer(arg, " %.2x", code);
+    }
+    if (i < len) {
+	printer(arg, " ...");
+	p += len - i;
+    }
+
+    return p - pstart;
+}
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+
+static
+void LcpLinkFailure (f)
+    fsm *f;
+{
+    if (f->state == OPENED) {
+	warn("No response to %d echo-requests", lcp_echos_pending);
+        warn("Serial link appears to be disconnected.");
+	status = EXIT_PEER_DEAD;
+	lcp_close(f->unit, "Peer not responding");
+    }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+
+static void
+LcpEchoCheck (f)
+    fsm *f;
+{
+    LcpSendEchoRequest (f);
+    if (f->state != OPENED)
+	return;
+
+    /*
+     * Start the timer for the next interval.
+     */
+    if (lcp_echo_timer_running)
+	warn("assertion lcp_echo_timer_running==0 failed");
+    TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval);
+    lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+
+static void
+LcpEchoTimeout (arg)
+    void *arg;
+{
+    if (lcp_echo_timer_running != 0) {
+        lcp_echo_timer_running = 0;
+        LcpEchoCheck ((fsm *) arg);
+    }
+}
+
+/*
+ * LcpEchoReply - LCP has received a reply to the echo
+ */
+
+static void
+lcp_received_echo_reply (f, id, inp, len)
+    fsm *f;
+    int id;
+    u_char *inp;
+    int len;
+{
+    u_int32_t magic;
+
+    /* Check the magic number - don't count replies from ourselves. */
+    if (len < 4) {
+	warn("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/ap/app/pppd/pppd/lcp.h b/ap/app/pppd/pppd/lcp.h
new file mode 100644
index 0000000..bd69652
--- /dev/null
+++ b/ap/app/pppd/pppd/lcp.h
@@ -0,0 +1,135 @@
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: lcp.h,v 1.2 2007-06-08 04:02:38 gerg Exp $
+ */
+
+/*
+ * Options.
+ */
+#define CI_VENDOR	0	/* Vendor Specific */
+#define CI_MRU		1	/* Maximum Receive Unit */
+#define CI_ASYNCMAP	2	/* Async Control Character Map */
+#define CI_AUTHTYPE	3	/* Authentication Type */
+#define CI_QUALITY	4	/* Quality Protocol */
+#define CI_MAGICNUMBER	5	/* Magic Number */
+#define CI_PCOMPRESSION	7	/* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8	/* Address/Control Field Compression */
+#define CI_FCSALTERN	9	/* FCS-Alternatives */
+#define CI_SDP		10	/* Self-Describing-Pad */
+#define CI_NUMBERED	11	/* Numbered-Mode */
+#define CI_CALLBACK	13	/* callback */
+#define CI_MRRU		17	/* max reconstructed receive unit; multilink */
+#define CI_SSNHF	18	/* short sequence numbers for multilink */
+#define CI_EPDISC	19	/* endpoint discriminator */
+#define CI_MPPLUS	22	/* Multi-Link-Plus-Procedure */
+#define CI_LDISC	23	/* Link-Discriminator */
+#define CI_LCPAUTH	24	/* LCP Authentication */
+#define CI_COBS		25	/* Consistent Overhead Byte Stuffing */
+#define CI_PREFELIS	26	/* Prefix Elision */
+#define CI_MPHDRFMT	27	/* MP Header Format */
+#define CI_I18N		28	/* Internationalization */
+#define CI_SDL		29	/* Simple Data Link */
+
+/*
+ * LCP-specific packet types (code numbers).
+ */
+#define PROTREJ		8	/* Protocol Reject */
+#define ECHOREQ		9	/* Echo Request */
+#define ECHOREP		10	/* Echo Reply */
+#define DISCREQ		11	/* Discard Request */
+#define IDENTIF		12	/* Identification */
+#define TIMEREM		13	/* Time Remaining */
+
+/* Value used as data for CI_CALLBACK option */
+#define CBCP_OPT	6	/* Use callback control protocol */
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+    bool passive;		/* Don't die if we don't get a response */
+    bool silent;		/* Wait for the other end to start first */
+    bool restart;		/* Restart vs. exit after close */
+    bool neg_mru;		/* Negotiate the MRU? */
+    bool neg_asyncmap;		/* Negotiate the async map? */
+    bool neg_upap;		/* Ask for UPAP authentication? */
+    bool neg_chap;		/* Ask for CHAP authentication? */
+    bool neg_eap;		/* Ask for EAP authentication? */
+    bool neg_magicnumber;	/* Ask for magic number? */
+    bool neg_pcompression;	/* HDLC Protocol Field Compression? */
+    bool neg_accompression;	/* HDLC Address/Control Field Compression? */
+    bool neg_lqr;		/* Negotiate use of Link Quality Reports */
+    bool neg_cbcp;		/* Negotiate use of CBCP */
+    bool neg_mrru;		/* negotiate multilink MRRU */
+    bool neg_ssnhf;		/* negotiate short sequence numbers */
+    bool neg_endpoint;		/* negotiate endpoint discriminator */
+    int  mru;			/* Value of MRU */
+    int	 mrru;			/* Value of MRRU, and multilink enable */
+    u_char chap_mdtype;		/* which MD types (hashing algorithm) */
+    u_int32_t asyncmap;		/* Value of async map */
+    u_int32_t magicnumber;
+    int  numloops;		/* Number of loops during magic number neg. */
+    u_int32_t lqr_period;	/* Reporting period for LQR 1/100ths second */
+    struct epdisc endpoint;	/* endpoint discriminator */
+} lcp_options;
+
+extern fsm lcp_fsm[];
+extern lcp_options lcp_wantoptions[];
+extern lcp_options lcp_gotoptions[];
+extern lcp_options lcp_allowoptions[];
+extern lcp_options lcp_hisoptions[];
+
+#define DEFMRU	1500		/* Try for this */
+#define MINMRU	128		/* No MRUs below this */
+#define MAXMRU	16384		/* Normally limit MRU to this */
+
+void lcp_open __P((int));
+void lcp_close __P((int, char *));
+void lcp_lowerup __P((int));
+void lcp_lowerdown __P((int));
+void lcp_sprotrej __P((int, u_char *, int));	/* send protocol reject */
+
+extern struct protent lcp_protent;
+
+/* Default number of times we receive our magic number from the peer
+   before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL	10
diff --git a/ap/app/pppd/pppd/magic.c b/ap/app/pppd/pppd/magic.c
new file mode 100644
index 0000000..927d981
--- /dev/null
+++ b/ap/app/pppd/pppd/magic.c
@@ -0,0 +1,123 @@
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: magic.c,v 1.2 2007-06-08 04:02:38 gerg Exp $"
+
+#include <stdio.h>
+#include <stdlib.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();
+}
+
+/*
+ * random_bytes - Fill a buffer with random bytes.
+ */
+void
+random_bytes(unsigned char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; ++i)
+		buf[i] = mrand48() >> 24;
+}
+
+#ifdef NO_DRAND48
+/*
+ * Substitute procedures for those systems which don't have
+ * drand48 et al.
+ */
+
+double
+drand48()
+{
+    return (double)random() / (double)0x7fffffffL; /* 2**31-1 */
+}
+
+long
+mrand48()
+{
+    return random();
+}
+
+void
+srand48(seedval)
+long seedval;
+{
+    srandom((int)seedval);
+}
+
+#endif
diff --git a/ap/app/pppd/pppd/magic.h b/ap/app/pppd/pppd/magic.h
new file mode 100644
index 0000000..9338b82
--- /dev/null
+++ b/ap/app/pppd/pppd/magic.h
@@ -0,0 +1,49 @@
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: magic.h,v 1.2 2007-06-08 04:02:38 gerg Exp $
+ */
+
+void magic_init __P((void));	/* Initialize the magic number generator */
+u_int32_t magic __P((void));	/* Returns the next magic number */
+
+/* Fill buffer with random bytes */
+void random_bytes __P((unsigned char *buf, int len));
diff --git a/ap/app/pppd/pppd/main.c b/ap/app/pppd/pppd/main.c
new file mode 100644
index 0000000..174754e
--- /dev/null
+++ b/ap/app/pppd/pppd/main.c
@@ -0,0 +1,2437 @@
+/*
+ * main.c - Point-to-Point Protocol main module
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Copyright (c) 1999-2004 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: main.c,v 1.156 2008/06/23 11:47:18 paulus Exp $"
+
+#include <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-new.h"
+#include "eap.h"
+#include "ccp.h"
+#include "ecp.h"
+#include "pathnames.h"
+
+#ifdef USE_TDB
+#include "tdb.h"
+#endif
+
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#endif /* IPX_CHANGE */
+#ifdef AT_CHANGE
+#include "atcp.h"
+#endif
+
+static const char rcsid[] = RCSID;
+
+/* interface vars */
+char ifname[32];		/* Interface name */
+int ifunit;			/* Interface unit number */
+
+struct channel *the_channel;
+char PATH_SYSOPTIONS[30]={0};
+char PATH_UPAPFILE[30]={0};
+char PATH_CHAPFILE[30]={0};
+char PATH_TTYOPT[30]={0};
+char *progname;			/* Name of this program */
+char hostname[MAXNAMELEN];	/* Our hostname */
+static char pidfilename[MAXPATHLEN];	/* name of pid file */
+static char linkpidfile[MAXPATHLEN];	/* name of linkname pid file */
+char ppp_devnam[MAXPATHLEN];	/* name of PPP tty (maybe ttypx) */
+uid_t uid;			/* Our real user-id */
+struct notifier *pidchange = NULL;
+struct notifier *phasechange = NULL;
+struct notifier *exitnotify = NULL;
+struct notifier *sigreceived = NULL;
+struct notifier *fork_notifier = NULL;
+
+int hungup;			/* terminal has been hung up */
+int privileged;			/* we're running as real uid root */
+int need_holdoff;		/* need holdoff period before restarting */
+int detached;			/* have detached from terminal */
+volatile int status;		/* exit status for pppd */
+int unsuccess;			/* # unsuccessful connection attempts */
+int do_callback;		/* != 0 if we should do callback next */
+int doing_callback;		/* != 0 if we are doing callback */
+int ppp_session_number;		/* Session number, for channels with such a
+				   concept (eg PPPoE) */
+int childwait_done;		/* have timed out waiting for children */
+
+#ifdef USE_TDB
+TDB_CONTEXT *pppdb;		/* database for storing status etc. */
+#endif
+
+char db_key[32];
+
+int (*holdoff_hook) __P((void)) = NULL;
+int (*new_phase_hook) __P((int)) = NULL;
+void (*snoop_recv_hook) __P((unsigned char *p, int len)) = NULL;
+void (*snoop_send_hook) __P((unsigned char *p, int len)) = NULL;
+
+static int conn_running;	/* we have a [dis]connector running */
+static int fd_loop;		/* fd for getting demand-dial packets */
+
+int fd_devnull;			/* fd for /dev/null */
+int devfd = -1;			/* fd of underlying device */
+int fd_ppp = -1;		/* fd for talking PPP */
+int phase;			/* where the link is at */
+int kill_link;
+int asked_to_quit;
+int open_ccp_flag;
+int listen_time;
+int got_sigusr2;
+int got_sigterm;
+int got_sighup;
+
+static sigset_t signals_handled;
+static int waiting;
+static sigjmp_buf sigjmp;
+
+char **script_env;		/* Env. variable values for scripts */
+int s_env_nalloc;		/* # words avail at script_env */
+
+u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */
+u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
+
+static int n_children;		/* # child processes still running */
+static int got_sigchld;		/* set if we have received a SIGCHLD */
+
+int privopen;			/* don't lock, open device as root */
+
+char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
+
+#ifdef __uClinux__
+#define MAXGROUPS 16		/* Save a lot of bss RAM space */
+#else
+#define MAXGROUPS NGROUPS_MAX
+#endif
+GIDSET_TYPE groups[MAXGROUPS];	/* groups the user is in */
+int ngroups;			/* How many groups valid in groups */
+
+static struct timeval start_time;	/* Time when link was started. */
+
+static struct pppd_stats old_link_stats;
+struct pppd_stats link_stats;
+unsigned link_connect_time;
+int link_stats_valid;
+
+int error_count;
+
+bool bundle_eof;
+bool bundle_terminating;
+
+/*
+ * We maintain a list of child process pids and
+ * functions to call when they exit.
+ */
+struct subprocess {
+    pid_t	pid;
+    char	*prog;
+    void	(*done) __P((void *));
+    void	*arg;
+    int		killable;
+    struct subprocess *next;
+};
+
+static struct subprocess *children;
+
+/* Prototypes for procedures local to this file. */
+
+static void setup_signals __P((void));
+static void create_pidfile __P((int pid));
+static void create_linkpidfile __P((int pid));
+static void cleanup __P((void));
+static void get_input __P((void));
+static void calltimeout __P((void));
+static struct timeval *timeleft __P((struct timeval *));
+static void kill_my_pg __P((int));
+static void hup __P((int));
+static void term __P((int));
+static void chld __P((int));
+static void toggle_debug __P((int));
+static void open_ccp __P((int));
+static void bad_signal __P((int));
+static void holdoff_end __P((void *));
+static void forget_child __P((int pid, int status));
+static int reap_kids __P((void));
+static void childwait_end __P((void *));
+
+#ifdef USE_TDB
+static void update_db_entry __P((void));
+static void add_db_key __P((const char *));
+static void delete_db_key __P((const char *));
+static void cleanup_db __P((void));
+#endif
+
+static void handle_events __P((void));
+void print_link_stats __P((void));
+
+extern	char	*ttyname __P((int));
+extern	char	*getlogin __P((void));
+int main __P((int, char *[]));
+
+#ifdef ultrix
+#undef	O_NONBLOCK
+#define	O_NONBLOCK	O_NDELAY
+#endif
+
+#ifdef ULTRIX
+#define setlogmask(x)
+#endif
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+struct protent *protocols[] = {
+    &lcp_protent,
+    &pap_protent,
+    &chap_protent,
+#ifdef CBCP_SUPPORT
+    &cbcp_protent,
+#endif
+    &ipcp_protent,
+#ifdef INET6
+    &ipv6cp_protent,
+#endif
+    &ccp_protent,
+    &ecp_protent,
+#ifdef IPX_CHANGE
+    &ipxcp_protent,
+#endif
+#ifdef AT_CHANGE
+    &atcp_protent,
+#endif
+    &eap_protent,
+    NULL
+};
+
+/*
+ * If PPP_DRV_NAME is not defined, use the default "ppp" as the device name.
+ */
+#if !defined(PPP_DRV_NAME)
+#define PPP_DRV_NAME	"ppp"
+#endif /* !defined(PPP_DRV_NAME) */
+
+int
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    int i, t;
+    char *p;
+    struct passwd *pw;
+    struct protent *protp;
+    char numbuf[16];
+
+    link_stats_valid = 0;
+    new_phase(PHASE_INITIALIZE);
+
+    script_env = NULL;
+
+    /* Initialize syslog facilities */
+    reopen_log();
+	get_path_conf();
+    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(MAXGROUPS, groups);
+
+    /*
+     * Initialize magic number generator now so that protocols may
+     * use magic numbers in initialization.
+     */
+    magic_init();
+
+    /*
+     * Initialize each protocol.
+     */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+        (*protp->init)(0);
+
+    /*
+     * Initialize the default channel.
+     */
+    tty_init();
+
+    progname = *argv;
+
+    /*
+     * Parse, in order, the system options file, the user's options file,
+     * and the command line arguments.
+     */
+    if (!options_from_file(PATH_SYSOPTIONS, !privileged, 0, 1)
+	|| !options_from_user()
+	|| !parse_args(argc-1, argv+1))
+	exit(EXIT_OPTION_ERROR);
+    devnam_fixed = 1;		/* can no longer change device name */
+
+    /*
+     * Work out the device name, if it hasn't already been specified,
+     * and parse the tty's options file.
+     */
+    if (the_channel->process_extra_options)
+	(*the_channel->process_extra_options)();
+
+    if (debug)
+	setlogmask(LOG_UPTO(LOG_DEBUG));
+
+    /*
+     * Check that we are running as root.
+     */
+    if (geteuid() != 0) {
+	option_error("must be root to run %s, since it is not setuid-root",
+		     argv[0]);
+	exit(EXIT_NOT_ROOT);
+    }
+
+    if (!ppp_available()) {
+	option_error("%s", no_ppp_msg);
+	exit(EXIT_NO_KERNEL_SUPPORT);
+    }
+
+    /*
+     * Check that the options given are valid and consistent.
+     */
+    check_options();
+    if (!sys_check_options())
+	exit(EXIT_OPTION_ERROR);
+    auth_check_options();
+#ifdef HAVE_MULTILINK
+    mp_check_options();
+#endif
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if (protp->check_options != NULL)
+	    (*protp->check_options)();
+    if (the_channel->check_options)
+	(*the_channel->check_options)();
+
+
+    if (dump_options || dryrun) {
+	init_pr_log(NULL, LOG_INFO);
+	print_options(pr_log, NULL);
+	end_pr_log();
+    }
+
+    if (dryrun)
+	die(0);
+
+    /* Make sure fds 0, 1, 2 are open to somewhere. */
+    fd_devnull = open(_PATH_DEVNULL, O_RDWR);
+    if (fd_devnull < 0)
+	fatal("Couldn't open %s: %m", _PATH_DEVNULL);
+    while (fd_devnull <= 2) {
+	i = dup(fd_devnull);
+	if (i < 0)
+	    fatal("Critical shortage of file descriptors: dup failed: %m");
+	fd_devnull = i;
+    }
+
+    /*
+     * Initialize system-dependent stuff.
+     */
+    sys_init();
+
+#ifdef USE_TDB
+    pppdb = tdb_open(_PATH_PPPDB, 0, 0, O_RDWR|O_CREAT, 0644);
+    if (pppdb != NULL) {
+	slprintf(db_key, sizeof(db_key), "pppd%d", getpid());
+	update_db_entry();
+    } else {
+	if (multilink) {
+	    warn("Warning: disabling multilink");
+	    multilink = 0;
+	}
+    }
+#endif
+
+    /*
+     * Detach ourselves from the terminal, if required,
+     * and identify who is running us.
+     */
+    if (!nodetach && !updetach)
+	detach();
+    p = getlogin();
+    if (p == NULL) {
+	pw = getpwuid(uid);
+	if (pw != NULL && pw->pw_name != NULL)
+	    p = pw->pw_name;
+	else
+	    p = "(unknown)";
+    }
+    syslog(LOG_NOTICE, "pppd %s started by %s, uid %d", VERSION, p, uid);
+    script_setenv("PPPLOGNAME", p, 0);
+	
+    if (devnam[0])
+    	{
+	script_setenv("DEVICE", devnam, 1);
+    	}
+    slprintf(numbuf, sizeof(numbuf), "%d", getpid());
+    script_setenv("PPPD_PID", numbuf, 1);
+    setup_signals();
+    create_linkpidfile(getpid());
+    waiting = 0;
+
+    /*
+     * If we're doing dial-on-demand, set up the interface now.
+     */
+    if (demand) {
+	/*
+	 * Open the loopback channel and set it up to be the ppp interface.
+	 */
+	fd_loop = open_ppp_loopback();
+	set_ifunit(1);
+	/*
+	 * Configure the interface and mark it up, etc.
+	 */
+	demand_conf();
+    }
+
+    do_callback = 0;
+    for (;;) {
+
+	bundle_eof = 0;
+	bundle_terminating = 0;
+	listen_time = 0;
+	need_holdoff = 1;
+	devfd = -1;
+	status = EXIT_OK;
+	//++unsuccess;
+	doing_callback = do_callback;
+	do_callback = 0;
+	if (demand && !doing_callback) {
+	    /* did not go here
+	     * Don't do anything until we see some activity.
+	     */
+	    new_phase(PHASE_DORMANT);
+	    demand_unblock();
+	    add_fd(fd_loop);
+	    for (;;) {
+		handle_events();
+		if (asked_to_quit)
+		    break;
+		if (get_loop_output())
+		    break;
+	    }
+	    remove_fd(fd_loop);
+	    if (asked_to_quit)
+		break;
+
+	    /*
+	     * Now we want to bring up the link.
+	     */
+	    demand_block();
+	    warn("Starting link");
+	}
+
+	gettimeofday(&start_time, NULL);
+	script_unsetenv("CONNECT_TIME");
+	script_unsetenv("BYTES_SENT");
+	script_unsetenv("BYTES_RCVD");
+	lcp_open(0);		/* Start protocol */
+	start_link(0);
+	while (phase != PHASE_DEAD) {
+		warn("pppd: %s---->%d", __FILE__, __LINE__);
+	    handle_events();
+	    get_input();
+	    if (kill_link)
+		lcp_close(0, "User request");
+	    if (asked_to_quit) {
+		bundle_terminating = 1;
+		if (phase == PHASE_MASTER)
+		    mp_bundle_terminated();
+	    }
+	    if (open_ccp_flag) {
+		if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) {
+		    ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
+		    (*ccp_protent.open)(0);
+		}
+	    }
+		char ppp_pdpstate[100] = {0};
+		sc_cfg_get("ppp_pdpstate",ppp_pdpstate,sizeof(ppp_pdpstate));
+		warn("pppd: %s---->%d, %s", __FILE__, __LINE__, ppp_pdpstate);
+		if(strcmp(ppp_pdpstate, "dead") == 0)
+		{
+			sc_cfg_set("ppp_pdpstate", "");
+			lcp_close(0, "zhudeming");
+
+		}
+	}
+	/* restore FSMs to original state */
+	lcp_close(0, "");
+	if (!persist || asked_to_quit || (maxfail > 0 && unsuccess >= maxfail))
+		{
+	break;
+    	}
+	if (demand)
+	    demand_discard();
+	t = need_holdoff? holdoff: 0;
+	if (holdoff_hook)
+	    t = (*holdoff_hook)();
+	if (t > 0) {
+	    new_phase(PHASE_HOLDOFF);
+	    TIMEOUT(holdoff_end, NULL, t);
+	    do {
+		handle_events();
+		if (kill_link)
+		    new_phase(PHASE_DORMANT); /* allow signal to end holdoff */
+	    } while (phase == PHASE_HOLDOFF);
+	    if (!persist)
+	    	{
+		break;
+	    	}
+	}
+    }
+
+    /* Wait for scripts to finish */
+    reap_kids();
+    if (n_children > 0) {
+	if (child_wait > 0)
+	    TIMEOUT(childwait_end, NULL, child_wait);
+	if (debug) {
+	    struct subprocess *chp;
+	    warn("Waiting for %d child processes...", n_children);
+	    for (chp = children; chp != NULL; chp = chp->next)
+		warn("  script %s, pid %d", chp->prog, chp->pid);
+	}
+	while (n_children > 0 && !childwait_done) {
+	    handle_events();
+	    if (kill_link && !childwait_done)
+		childwait_end(NULL);
+	}
+    }
+
+    die(status);
+    return 0;
+}
+
+/*
+ * handle_events - wait for something to happen and respond to it.
+ */
+static void
+handle_events()
+{
+    struct timeval timo;
+
+    kill_link = open_ccp_flag = 0;
+    if (sigsetjmp(sigjmp, 1) == 0) {
+	sigprocmask(SIG_BLOCK, &signals_handled, NULL);
+	if (got_sighup || got_sigterm || got_sigusr2 || got_sigchld) {
+	    sigprocmask(SIG_UNBLOCK, &signals_handled, NULL);
+	} else {
+	    waiting = 1;
+	    sigprocmask(SIG_UNBLOCK, &signals_handled, NULL);
+	    wait_input(timeleft(&timo));
+	}
+    }
+    waiting = 0;
+    calltimeout();
+    if (got_sighup) {
+	warn("Hangup (SIGHUP)");
+	kill_link = 1;
+	got_sighup = 0;
+	if (status != EXIT_HANGUP)
+	    status = EXIT_USER_REQUEST;
+    }
+    if (got_sigterm) {
+	warn("Terminating on signal %d", got_sigterm);
+	kill_link = 1;
+	asked_to_quit = 1;
+	persist = 0;
+	status = EXIT_USER_REQUEST;
+	got_sigterm = 0;
+    }
+    if (got_sigchld) {
+	got_sigchld = 0;
+	reap_kids();	/* Don't leave dead kids lying around */
+    }
+    if (got_sigusr2) {
+	open_ccp_flag = 1;
+	got_sigusr2 = 0;
+    }
+}
+
+/*
+ * setup_signals - initialize signal handling.
+ */
+static void
+setup_signals()
+{
+    struct sigaction sa;
+
+    /*
+     * Compute mask of all interesting signals and install signal handlers
+     * for each.  Only one signal handler may be active at a time.  Therefore,
+     * all other signals should be masked when any handler is executing.
+     */
+    sigemptyset(&signals_handled);
+    sigaddset(&signals_handled, SIGHUP);
+    sigaddset(&signals_handled, SIGINT);
+    sigaddset(&signals_handled, SIGTERM);
+    sigaddset(&signals_handled, SIGCHLD);
+    sigaddset(&signals_handled, SIGUSR2);
+
+#define SIGNAL(s, handler)	do { \
+	sa.sa_handler = handler; \
+	if (sigaction(s, &sa, NULL) < 0) \
+	    fatal("Couldn't establish signal handler (%d): %m", s); \
+    } while (0)
+
+    sa.sa_mask = signals_handled;
+    sa.sa_flags = 0;
+    SIGNAL(SIGHUP, hup);		/* Hangup */
+    SIGNAL(SIGINT, term);		/* Interrupt */
+    SIGNAL(SIGTERM, term);		/* Terminate */
+    SIGNAL(SIGCHLD, chld);
+
+    SIGNAL(SIGUSR1, toggle_debug);	/* Toggle debug flag */
+    SIGNAL(SIGUSR2, open_ccp);		/* Reopen CCP */
+
+    /*
+     * Install a handler for other signals which would otherwise
+     * cause pppd to exit without cleaning up.
+     */
+    SIGNAL(SIGABRT, bad_signal);
+    SIGNAL(SIGALRM, bad_signal);
+    SIGNAL(SIGFPE, bad_signal);
+    SIGNAL(SIGILL, bad_signal);
+    SIGNAL(SIGPIPE, bad_signal);
+    SIGNAL(SIGQUIT, bad_signal);
+    //SIGNAL(SIGSEGV, bad_signal);
+#ifdef SIGBUS
+    SIGNAL(SIGBUS, bad_signal);
+#endif
+#ifdef SIGEMT
+    SIGNAL(SIGEMT, bad_signal);
+#endif
+#ifdef SIGPOLL
+    SIGNAL(SIGPOLL, bad_signal);
+#endif
+#ifdef SIGPROF
+    SIGNAL(SIGPROF, bad_signal);
+#endif
+#ifdef SIGSYS
+    SIGNAL(SIGSYS, bad_signal);
+#endif
+#ifdef SIGTRAP
+    SIGNAL(SIGTRAP, bad_signal);
+#endif
+#ifdef SIGVTALRM
+    SIGNAL(SIGVTALRM, bad_signal);
+#endif
+#ifdef SIGXCPU
+    SIGNAL(SIGXCPU, bad_signal);
+#endif
+#ifdef SIGXFSZ
+    SIGNAL(SIGXFSZ, bad_signal);
+#endif
+
+    /*
+     * Apparently we can get a SIGPIPE when we call syslog, if
+     * syslogd has died and been restarted.  Ignoring it seems
+     * be sufficient.
+     */
+    signal(SIGPIPE, SIG_IGN);
+}
+
+/*
+ * set_ifunit - do things we need to do once we know which ppp
+ * unit we are using.
+ */
+void
+set_ifunit(iskey)
+    int iskey;
+{
+    warn("Using interface %s%d", PPP_DRV_NAME, ifunit);
+    slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
+    script_setenv("IFNAME", ifname, iskey);
+    if (iskey) {
+	create_pidfile(getpid());	/* write pid to file */
+	create_linkpidfile(getpid());
+    }
+}
+
+/*
+ * detach - detach us from the controlling terminal.
+ */
+void
+detach()
+{
+    int pid;
+    char numbuf[16];
+    int pipefd[2];
+
+    if (detached)
+	return;
+    if (pipe(pipefd) == -1)
+	pipefd[0] = pipefd[1] = -1;
+#ifndef __uClinux__
+    if ((pid = fork()) < 0) {
+	error("Couldn't detach (fork failed: %m)");
+	die(1);			/* or just return? */
+    }
+    if (pid != 0) {
+	/* parent */
+	notify(pidchange, pid);
+	/* update pid files if they have been written already */
+	if (pidfilename[0])
+	    create_pidfile(pid);
+	if (linkpidfile[0])
+	    create_linkpidfile(pid);
+	exit(0);		/* parent dies */
+    }
+#endif /* __uClinux__ */
+    setsid();
+    chdir("/");
+    dup2(fd_devnull, 0);
+    dup2(fd_devnull, 1);
+    dup2(fd_devnull, 2);
+    detached = 1;
+    if (log_default)
+	log_to_fd = -1;
+    slprintf(numbuf, sizeof(numbuf), "%d", getpid());
+    script_setenv("PPPD_PID", numbuf, 1);
+
+    /* wait for parent to finish updating pid & lock files and die */
+    close(pipefd[1]);
+    complete_read(pipefd[0], numbuf, 1);
+    close(pipefd[0]);
+}
+
+/*
+ * reopen_log - (re)open our connection to syslog.
+ */
+void
+reopen_log()
+{
+    openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
+    setlogmask(LOG_UPTO(LOG_INFO));
+}
+
+/*
+ * Create a file containing our process ID.
+ */
+static void
+create_pidfile(pid)
+    int pid;
+{
+    FILE *pidfile;
+
+    if (pid_file[0])
+	slprintf(pidfilename, sizeof(pidfilename), "%s%s",
+			pid_file[0] == '/' ? "" : _PATH_VARRUN,
+			pid_file);
+    else
+	slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid",
+			_PATH_VARRUN, ifname);
+
+    if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+	fprintf(pidfile, "%d\n", pid);
+	if (ipparam)
+	    fprintf(pidfile, "%s\n", ipparam);
+	(void) fclose(pidfile);
+    } else {
+	error("Failed to create pid file %s: %m", pidfilename);
+	pidfilename[0] = 0;
+    }
+}
+
+void
+create_linkpidfile(pid)
+    int pid;
+{
+    FILE *pidfile;
+
+    if (linkname[0] == 0)
+	return;
+    script_setenv("LINKNAME", linkname, 1);
+    slprintf(linkpidfile, sizeof(linkpidfile), "%sppp-%s.pid",
+	     _PATH_VARRUN, linkname);
+    if ((pidfile = fopen(linkpidfile, "w")) != NULL) {
+	fprintf(pidfile, "%d\n", pid);
+	if (ifname[0])
+	    fprintf(pidfile, "%s\n", ifname);
+	(void) fclose(pidfile);
+    } else {
+	error("Failed to create pid file %s: %m", linkpidfile);
+	linkpidfile[0] = 0;
+    }
+}
+
+/*
+ * remove_pidfile - remove our pid files
+ */
+void remove_pidfiles()
+{
+    if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
+	warn("unable to delete pid file %s: %m", pidfilename);
+    pidfilename[0] = 0;
+    if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT)
+	warn("unable to delete pid file %s: %m", linkpidfile);
+    linkpidfile[0] = 0;
+}
+
+/*
+ * holdoff_end - called via a timeout when the holdoff period ends.
+ */
+static void
+holdoff_end(arg)
+    void *arg;
+{
+    new_phase(PHASE_DORMANT);
+}
+
+/* List of protocol names, to make our messages a little more informative. */
+struct protocol_list {
+    u_short	proto;
+    const char	*name;
+} protocol_list[] = {
+    { 0x21,	"IP" },
+    { 0x23,	"OSI Network Layer" },
+    { 0x25,	"Xerox NS IDP" },
+    { 0x27,	"DECnet Phase IV" },
+    { 0x29,	"Appletalk" },
+    { 0x2b,	"Novell IPX" },
+    { 0x2d,	"VJ compressed TCP/IP" },
+    { 0x2f,	"VJ uncompressed TCP/IP" },
+    { 0x31,	"Bridging PDU" },
+    { 0x33,	"Stream Protocol ST-II" },
+    { 0x35,	"Banyan Vines" },
+    { 0x39,	"AppleTalk EDDP" },
+    { 0x3b,	"AppleTalk SmartBuffered" },
+    { 0x3d,	"Multi-Link" },
+    { 0x3f,	"NETBIOS Framing" },
+    { 0x41,	"Cisco Systems" },
+    { 0x43,	"Ascom Timeplex" },
+    { 0x45,	"Fujitsu Link Backup and Load Balancing (LBLB)" },
+    { 0x47,	"DCA Remote Lan" },
+    { 0x49,	"Serial Data Transport Protocol (PPP-SDTP)" },
+    { 0x4b,	"SNA over 802.2" },
+    { 0x4d,	"SNA" },
+    { 0x4f,	"IP6 Header Compression" },
+    { 0x51,	"KNX Bridging Data" },
+    { 0x53,	"Encryption" },
+    { 0x55,	"Individual Link Encryption" },
+    { 0x57,	"IPv6" },
+    { 0x59,	"PPP Muxing" },
+    { 0x5b,	"Vendor-Specific Network Protocol" },
+    { 0x61,	"RTP IPHC Full Header" },
+    { 0x63,	"RTP IPHC Compressed TCP" },
+    { 0x65,	"RTP IPHC Compressed non-TCP" },
+    { 0x67,	"RTP IPHC Compressed UDP 8" },
+    { 0x69,	"RTP IPHC Compressed RTP 8" },
+    { 0x6f,	"Stampede Bridging" },
+    { 0x73,	"MP+" },
+    { 0xc1,	"NTCITS IPI" },
+    { 0xfb,	"single-link compression" },
+    { 0xfd,	"Compressed Datagram" },
+    { 0x0201,	"802.1d Hello Packets" },
+    { 0x0203,	"IBM Source Routing BPDU" },
+    { 0x0205,	"DEC LANBridge100 Spanning Tree" },
+    { 0x0207,	"Cisco Discovery Protocol" },
+    { 0x0209,	"Netcs Twin Routing" },
+    { 0x020b,	"STP - Scheduled Transfer Protocol" },
+    { 0x020d,	"EDP - Extreme Discovery Protocol" },
+    { 0x0211,	"Optical Supervisory Channel Protocol" },
+    { 0x0213,	"Optical Supervisory Channel Protocol" },
+    { 0x0231,	"Luxcom" },
+    { 0x0233,	"Sigma Network Systems" },
+    { 0x0235,	"Apple Client Server Protocol" },
+    { 0x0281,	"MPLS Unicast" },
+    { 0x0283,	"MPLS Multicast" },
+    { 0x0285,	"IEEE p1284.4 standard - data packets" },
+    { 0x0287,	"ETSI TETRA Network Protocol Type 1" },
+    { 0x0289,	"Multichannel Flow Treatment Protocol" },
+    { 0x2063,	"RTP IPHC Compressed TCP No Delta" },
+    { 0x2065,	"RTP IPHC Context State" },
+    { 0x2067,	"RTP IPHC Compressed UDP 16" },
+    { 0x2069,	"RTP IPHC Compressed RTP 16" },
+    { 0x4001,	"Cray Communications Control Protocol" },
+    { 0x4003,	"CDPD Mobile Network Registration Protocol" },
+    { 0x4005,	"Expand accelerator protocol" },
+    { 0x4007,	"ODSICP NCP" },
+    { 0x4009,	"DOCSIS DLL" },
+    { 0x400B,	"Cetacean Network Detection Protocol" },
+    { 0x4021,	"Stacker LZS" },
+    { 0x4023,	"RefTek Protocol" },
+    { 0x4025,	"Fibre Channel" },
+    { 0x4027,	"EMIT Protocols" },
+    { 0x405b,	"Vendor-Specific Protocol (VSP)" },
+    { 0x8021,	"Internet Protocol Control Protocol" },
+    { 0x8023,	"OSI Network Layer Control Protocol" },
+    { 0x8025,	"Xerox NS IDP Control Protocol" },
+    { 0x8027,	"DECnet Phase IV Control Protocol" },
+    { 0x8029,	"Appletalk Control Protocol" },
+    { 0x802b,	"Novell IPX Control Protocol" },
+    { 0x8031,	"Bridging NCP" },
+    { 0x8033,	"Stream Protocol Control Protocol" },
+    { 0x8035,	"Banyan Vines Control Protocol" },
+    { 0x803d,	"Multi-Link Control Protocol" },
+    { 0x803f,	"NETBIOS Framing Control Protocol" },
+    { 0x8041,	"Cisco Systems Control Protocol" },
+    { 0x8043,	"Ascom Timeplex" },
+    { 0x8045,	"Fujitsu LBLB Control Protocol" },
+    { 0x8047,	"DCA Remote Lan Network Control Protocol (RLNCP)" },
+    { 0x8049,	"Serial Data Control Protocol (PPP-SDCP)" },
+    { 0x804b,	"SNA over 802.2 Control Protocol" },
+    { 0x804d,	"SNA Control Protocol" },
+    { 0x804f,	"IP6 Header Compression Control Protocol" },
+    { 0x8051,	"KNX Bridging Control Protocol" },
+    { 0x8053,	"Encryption Control Protocol" },
+    { 0x8055,	"Individual Link Encryption Control Protocol" },
+    { 0x8057,	"IPv6 Control Protocol" },
+    { 0x8059,	"PPP Muxing Control Protocol" },
+    { 0x805b,	"Vendor-Specific Network Control Protocol (VSNCP)" },
+    { 0x806f,	"Stampede Bridging Control Protocol" },
+    { 0x8073,	"MP+ Control Protocol" },
+    { 0x80c1,	"NTCITS IPI Control Protocol" },
+    { 0x80fb,	"Single Link Compression Control Protocol" },
+    { 0x80fd,	"Compression Control Protocol" },
+    { 0x8207,	"Cisco Discovery Protocol Control" },
+    { 0x8209,	"Netcs Twin Routing" },
+    { 0x820b,	"STP - Control Protocol" },
+    { 0x820d,	"EDPCP - Extreme Discovery Protocol Ctrl Prtcl" },
+    { 0x8235,	"Apple Client Server Protocol Control" },
+    { 0x8281,	"MPLSCP" },
+    { 0x8285,	"IEEE p1284.4 standard - Protocol Control" },
+    { 0x8287,	"ETSI TETRA TNP1 Control Protocol" },
+    { 0x8289,	"Multichannel Flow Treatment Protocol" },
+    { 0xc021,	"Link Control Protocol" },
+    { 0xc023,	"Password Authentication Protocol" },
+    { 0xc025,	"Link Quality Report" },
+    { 0xc027,	"Shiva Password Authentication Protocol" },
+    { 0xc029,	"CallBack Control Protocol (CBCP)" },
+    { 0xc02b,	"BACP Bandwidth Allocation Control Protocol" },
+    { 0xc02d,	"BAP" },
+    { 0xc05b,	"Vendor-Specific Authentication Protocol (VSAP)" },
+    { 0xc081,	"Container Control Protocol" },
+    { 0xc223,	"Challenge Handshake Authentication Protocol" },
+    { 0xc225,	"RSA Authentication Protocol" },
+    { 0xc227,	"Extensible Authentication Protocol" },
+    { 0xc229,	"Mitsubishi Security Info Exch Ptcl (SIEP)" },
+    { 0xc26f,	"Stampede Bridging Authorization Protocol" },
+    { 0xc281,	"Proprietary Authentication Protocol" },
+    { 0xc283,	"Proprietary Authentication Protocol" },
+    { 0xc481,	"Proprietary Node ID Authentication Protocol" },
+    { 0,	NULL },
+};
+
+/*
+ * protocol_name - find a name for a PPP protocol.
+ */
+const char *
+protocol_name(proto)
+    int proto;
+{
+    struct protocol_list *lp;
+
+    for (lp = protocol_list; lp->proto != 0; ++lp)
+	if (proto == lp->proto)
+	    return lp->name;
+    return NULL;
+}
+
+/*
+ * get_input - called when incoming data is available.
+ */
+static void
+get_input()
+{
+    int len, i;
+    u_char *p;
+    u_short protocol;
+    struct protent *protp;
+
+    p = inpacket_buf;	/* point to beginning of packet buffer */
+	//warn("enter get_input");
+    len = read_packet(inpacket_buf);
+	warn("enter get_input %d", len);
+    if (len < 0)
+	return;
+
+    if (len == 0) {
+	if (bundle_eof && multilink_master) {
+	    warn("Last channel has disconnected");
+	    mp_bundle_terminated();
+	    return;
+	}
+	warn("Modem hangup");
+	hungup = 1;
+	status = EXIT_HANGUP;
+	lcp_lowerdown(0);	/* serial link is no longer available */
+	link_terminated(0);
+	return;
+    }
+
+    if (len < PPP_HDRLEN) {
+	warn("received short packet:%.*B", len, p);
+	return;
+    }
+
+    dump_packet("rcvd", p, len);
+    if (snoop_recv_hook) snoop_recv_hook(p, len);
+
+    p += 2;				/* Skip address and control */
+    GETSHORT(protocol, p);
+    len -= PPP_HDRLEN;
+
+    /*
+     * Toss all non-LCP packets unless LCP is OPEN.
+     */
+    if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
+	warn("Discarded non-LCP packet when LCP not open");
+	return;
+    }
+
+    /*
+     * Until we get past the authentication phase, toss all packets
+     * except LCP, LQR and authentication packets.
+     */
+    if (phase <= PHASE_AUTHENTICATE
+	&& !(protocol == PPP_LCP || protocol == PPP_LQR
+	     || protocol == PPP_PAP || protocol == PPP_CHAP ||
+		protocol == PPP_EAP)) {
+	warn("discarding proto 0x%x in phase %d",
+		   protocol, phase);
+	return;
+    }
+
+    /*
+     * Upcall the proper protocol input routine.
+     */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+	if (protp->protocol == protocol && protp->enabled_flag) {
+	    (*protp->input)(0, p, len);
+	    return;
+	}
+        if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag
+	    && protp->datainput != NULL) {
+	    (*protp->datainput)(0, p, len);
+	    return;
+	}
+    }
+
+    if (debug) {
+	const char *pname = protocol_name(protocol);
+	if (pname != NULL)
+	    warn("Unsupported protocol '%s' (0x%x) received", pname, protocol);
+	else
+	    warn("Unsupported protocol 0x%x received", protocol);
+    }
+    lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
+}
+
+/*
+ * ppp_send_config - configure the transmit-side characteristics of
+ * the ppp interface.  Returns -1, indicating an error, if the channel
+ * send_config procedure called error() (or incremented error_count
+ * itself), otherwise 0.
+ */
+int
+ppp_send_config(unit, mtu, accm, pcomp, accomp)
+    int unit, mtu;
+    u_int32_t accm;
+    int pcomp, accomp;
+{
+	int errs;
+
+	if (the_channel->send_config == NULL)
+		return 0;
+	errs = error_count;
+	(*the_channel->send_config)(mtu, accm, pcomp, accomp);
+	return (error_count != errs)? -1: 0;
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.  Returns -1, indicating an error, if the channel
+ * recv_config procedure called error() (or incremented error_count
+ * itself), otherwise 0.
+ */
+int
+ppp_recv_config(unit, mru, accm, pcomp, accomp)
+    int unit, mru;
+    u_int32_t accm;
+    int pcomp, accomp;
+{
+	int errs;
+
+	if (the_channel->recv_config == NULL)
+		return 0;
+	errs = error_count;
+	(*the_channel->recv_config)(mru, accm, pcomp, accomp);
+	return (error_count != errs)? -1: 0;
+}
+
+/*
+ * new_phase - signal the start of a new phase of pppd's operation.
+ */
+void
+new_phase(p)
+    int p;
+{
+    phase = p;
+    if (new_phase_hook)
+	(*new_phase_hook)(p);
+    notify(phasechange, p);
+}
+
+/*
+ * die - clean up state and exit with the specified status.
+ */
+void
+die(status)
+    int status;
+{
+    if (!doing_multilink || multilink_master)
+	print_link_stats();
+    cleanup();
+    notify(exitnotify, status);
+    syslog(LOG_INFO, "Exit.");
+    exit(status);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+/* ARGSUSED */
+static void
+cleanup()
+{
+    sys_cleanup();
+
+    if (fd_ppp >= 0)
+	the_channel->disestablish_ppp(devfd);
+    if (the_channel->cleanup)
+	(*the_channel->cleanup)();
+    remove_pidfiles();
+
+#ifdef USE_TDB
+    if (pppdb != NULL)
+	cleanup_db();
+#endif
+
+}
+
+void
+print_link_stats()
+{
+    /*
+     * Print connect time and statistics.
+     */
+    if (link_stats_valid) {
+       int t = (link_connect_time + 5) / 6;    /* 1/10ths of minutes */
+       warn("Connect time %d.%d minutes.", t/10, t%10);
+       warn("Sent %u bytes, received %u bytes.",
+	    link_stats.bytes_out, link_stats.bytes_in);
+       link_stats_valid = 0;
+    }
+}
+
+/*
+ * reset_link_stats - "reset" stats when link goes up.
+ */
+void
+reset_link_stats(u)
+    int u;
+{
+    if (!get_ppp_stats(u, &old_link_stats))
+	return;
+    gettimeofday(&start_time, NULL);
+}
+
+/*
+ * update_link_stats - get stats at link termination.
+ */
+void
+update_link_stats(u)
+    int u;
+{
+    struct timeval now;
+    char numbuf[32];
+
+    if (!get_ppp_stats(u, &link_stats)
+	|| gettimeofday(&now, NULL) < 0)
+	return;
+    link_connect_time = now.tv_sec - start_time.tv_sec;
+    link_stats_valid = 1;
+
+    link_stats.bytes_in  -= old_link_stats.bytes_in;
+    link_stats.bytes_out -= old_link_stats.bytes_out;
+    link_stats.pkts_in   -= old_link_stats.pkts_in;
+    link_stats.pkts_out  -= old_link_stats.pkts_out;
+
+    slprintf(numbuf, sizeof(numbuf), "%u", link_connect_time);
+    script_setenv("CONNECT_TIME", numbuf, 0);
+    slprintf(numbuf, sizeof(numbuf), "%u", link_stats.bytes_out);
+    script_setenv("BYTES_SENT", numbuf, 0);
+    slprintf(numbuf, sizeof(numbuf), "%u", link_stats.bytes_in);
+    script_setenv("BYTES_RCVD", numbuf, 0);
+}
+
+
+struct	callout {
+    struct timeval	c_time;		/* time at which to call routine */
+    void		*c_arg;		/* argument to routine */
+    void		(*c_func) __P((void *)); /* routine */
+    struct		callout *c_next;
+};
+
+static struct callout *callout = NULL;	/* Callout list */
+static struct timeval timenow;		/* Current time */
+
+/*
+ * timeout - Schedule a timeout.
+ */
+void
+timeout(func, arg, secs, usecs)
+    void (*func) __P((void *));
+    void *arg;
+    int secs, usecs;
+{
+    struct callout *newp, *p, **pp;
+
+    /*
+     * Allocate timeout.
+     */
+    if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL)
+	fatal("Out of memory in timeout()!");
+    newp->c_arg = arg;
+    newp->c_func = func;
+    gettimeofday(&timenow, NULL);
+    newp->c_time.tv_sec = timenow.tv_sec + secs;
+    newp->c_time.tv_usec = timenow.tv_usec + usecs;
+    if (newp->c_time.tv_usec >= 1000000) {
+	newp->c_time.tv_sec += newp->c_time.tv_usec / 1000000;
+	newp->c_time.tv_usec %= 1000000;
+    }
+
+    /*
+     * Find correct place and link it in.
+     */
+    for (pp = &callout; (p = *pp); pp = &p->c_next)
+	if (newp->c_time.tv_sec < p->c_time.tv_sec
+	    || (newp->c_time.tv_sec == p->c_time.tv_sec
+		&& newp->c_time.tv_usec < p->c_time.tv_usec))
+	    break;
+    newp->c_next = p;
+    *pp = newp;
+}
+
+
+/*
+ * untimeout - Unschedule a timeout.
+ */
+void
+untimeout(func, arg)
+    void (*func) __P((void *));
+    void *arg;
+{
+    struct callout **copp, *freep;
+
+    /*
+     * Find first matching timeout and remove it from the list.
+     */
+    for (copp = &callout; (freep = *copp); copp = &freep->c_next)
+	if (freep->c_func == func && freep->c_arg == arg) {
+	    *copp = freep->c_next;
+	    free((char *) freep);
+	    break;
+	}
+}
+
+
+/*
+ * calltimeout - Call any timeout routines which are now due.
+ */
+static void
+calltimeout()
+{
+    struct callout *p;
+
+    while (callout != NULL) {
+	p = callout;
+
+	if (gettimeofday(&timenow, NULL) < 0)
+	    fatal("Failed to get time of day: %m");
+	if (!(p->c_time.tv_sec < timenow.tv_sec
+	      || (p->c_time.tv_sec == timenow.tv_sec
+		  && p->c_time.tv_usec <= timenow.tv_usec)))
+	    break;		/* no, it's not time yet */
+
+	callout = p->c_next;
+	(*p->c_func)(p->c_arg);
+
+	free((char *) p);
+    }
+}
+
+
+/*
+ * timeleft - return the length of time until the next timeout is due.
+ */
+static struct timeval *
+timeleft(tvp)
+    struct timeval *tvp;
+{
+    if (callout == NULL)
+	return NULL;
+
+    gettimeofday(&timenow, NULL);
+    tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
+    tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec;
+    if (tvp->tv_usec < 0) {
+	tvp->tv_usec += 1000000;
+	tvp->tv_sec -= 1;
+    }
+    if (tvp->tv_sec < 0)
+	tvp->tv_sec = tvp->tv_usec = 0;
+
+    return tvp;
+}
+
+
+/*
+ * kill_my_pg - send a signal to our process group, and ignore it ourselves.
+ * We assume that sig is currently blocked.
+ */
+static void
+kill_my_pg(sig)
+    int sig;
+{
+    struct sigaction act, oldact;
+    struct subprocess *chp;
+
+    if (!detached) {
+	/*
+	 * There might be other things in our process group that we
+	 * didn't start that would get hit if we did a kill(0), so
+	 * just send the signal individually to our children.
+	 */
+	for (chp = children; chp != NULL; chp = chp->next)
+	    if (chp->killable)
+		kill(chp->pid, sig);
+	return;
+    }
+
+    /* We've done a setsid(), so we can just use a kill(0) */
+    sigemptyset(&act.sa_mask);		/* unnecessary in fact */
+    act.sa_handler = SIG_IGN;
+    act.sa_flags = 0;
+    kill(0, sig);
+    /*
+     * The kill() above made the signal pending for us, as well as
+     * the rest of our process group, but we don't want it delivered
+     * to us.  It is blocked at the moment.  Setting it to be ignored
+     * will cause the pending signal to be discarded.  If we did the
+     * kill() after setting the signal to be ignored, it is unspecified
+     * (by POSIX) whether the signal is immediately discarded or left
+     * pending, and in fact Linux would leave it pending, and so it
+     * would be delivered after the current signal handler exits,
+     * leading to an infinite loop.
+     */
+    sigaction(sig, &act, &oldact);
+    sigaction(sig, &oldact, NULL);
+}
+
+
+/*
+ * hup - Catch SIGHUP signal.
+ *
+ * Indicates that the physical layer has been disconnected.
+ * We don't rely on this indication; if the user has sent this
+ * signal, we just take the link down.
+ */
+static void
+hup(sig)
+    int sig;
+{
+    /* can't log a message here, it can deadlock */
+    got_sighup = 1;
+    if (conn_running)
+	/* Send the signal to the [dis]connector process(es) also */
+	kill_my_pg(sig);
+    notify(sigreceived, sig);
+    if (waiting)
+	siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * term - Catch SIGTERM signal and SIGINT signal (^C/del).
+ *
+ * Indicates that we should initiate a graceful disconnect and exit.
+ */
+/*ARGSUSED*/
+static void
+term(sig)
+    int sig;
+{
+    /* can't log a message here, it can deadlock */
+    got_sigterm = sig;
+    if (conn_running)
+	/* Send the signal to the [dis]connector process(es) also */
+	kill_my_pg(sig);
+    notify(sigreceived, sig);
+    if (waiting)
+	siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * chld - Catch SIGCHLD signal.
+ * Sets a flag so we will call reap_kids in the mainline.
+ */
+static void
+chld(sig)
+    int sig;
+{
+    got_sigchld = 1;
+    if (waiting)
+	siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * toggle_debug - Catch SIGUSR1 signal.
+ *
+ * Toggle debug flag.
+ */
+/*ARGSUSED*/
+static void
+toggle_debug(sig)
+    int sig;
+{
+    debug = !debug;
+    if (debug) {
+	setlogmask(LOG_UPTO(LOG_DEBUG));
+    } else {
+	setlogmask(LOG_UPTO(LOG_WARNING));
+    }
+}
+
+
+/*
+ * open_ccp - Catch SIGUSR2 signal.
+ *
+ * Try to (re)negotiate compression.
+ */
+/*ARGSUSED*/
+static void
+open_ccp(sig)
+    int sig;
+{
+    got_sigusr2 = 1;
+    if (waiting)
+	siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * bad_signal - We've caught a fatal signal.  Clean up state and exit.
+ */
+static void
+bad_signal(sig)
+    int sig;
+{
+    static int crashed = 0;
+
+    if (crashed)
+	_exit(127);
+    crashed = 1;
+    error("Fatal signal %d", sig);
+    if (conn_running)
+	kill_my_pg(SIGTERM);
+    notify(sigreceived, sig);
+    die(127);
+}
+
+#ifdef __uClinux__
+
+/*
+ * device_script - run a program to talk to the serial device
+ * (e.g. to run the connector or disconnector script).
+ */
+int
+device_script(program, in, out, dont_wait)
+    char *program;
+    int in, out;
+    int dont_wait;
+{
+    pid_t pid;
+    int status = -1;
+    int fd, errfd, pipefd[2];
+    char *argv[16], *sp;
+    int argc = 0, prevspace = 1;
+    char *prog_copy = strdup(program);
+
+    ++conn_running;
+    /* Close syslog BEFORE doing the fork since we can't do it after if we vfork() */
+    closelog();
+
+    /* make sure fds 0, 1, 2 are occupied (probably not necessary) */
+    while ((fd = dup(fd_devnull)) >= 0) {
+	if (fd > 2) {
+	    close(fd);
+	    break;
+	}
+    }
+
+    if (pipe(pipefd) == -1)
+	pipefd[0] = pipefd[1] = -1;
+
+	
+	
+    pid = vfork();
+
+    if (pid == 0) {
+	/* Executing in the child */
+	sys_close();
+
+	/* make sure in, out and errfd won't get tromped on below */
+	if (in == 1 || in == 2)	
+		in = dup(in);	
+	if (out == 0 || out == 2)
+		
+	out = dup(out);
+	if (errfd == 0 || errfd == 1)
+		errfd = dup(errfd);
+
+	/* dup the in, out, err fds to 0, 1, 2 */
+	if (in != 0)
+	
+		dup2(in, 0);
+
+	if (out != 1)
+		dup2(out, 1);
+
+	if (log_to_fd >= 0)
+	    errfd = log_to_fd;
+	else
+	    errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
+
+	if (errfd != 2)
+		dup2(errfd, 2);
+
+	if (log_to_fd > 2)
+		close(log_to_fd);
+	if (the_channel->close)
+		(*the_channel->close)();
+	else
+		close(devfd);	/* some plugins don't have a close function */
+	close(fd_ppp);
+	close(fd_devnull);
+	if (in != 0)
+		close(in);
+	if (out != 1)
+		close(out);
+	if (errfd != 2)
+		close(errfd);
+
+	close(pipefd[0]);
+	/* this close unblocks the read() call above in the parent */
+	close(pipefd[1]);
+
+	setuid(uid);
+	if (getuid() != uid) {
+	    error("setuid failed");
+	    exit(1);
+	}
+	setgid(getgid());
+
+	/*
+	 *	Save on memory/resources by not running a shell process
+	 *	and just running the program directly.
+	 *	We work on a copy of the program to run rather than
+	 *	change what was passed in.
+	 */
+	for (sp = prog_copy; (*sp != 0); ) {
+		if (prevspace && !isspace(*sp))
+			argv[argc++] = sp;
+		if ((prevspace = isspace(*sp)))
+			*sp = 0;
+		sp++;
+	}
+	argv[argc] = 0;
+
+	execv(argv[0], argv);
+
+	error("could not exec /bin/sh: %m");
+	exit(99);
+	/* NOTREACHED */
+    }
+
+    if (prog_copy)
+	free(prog_copy);
+    /* Reopen the log in the parent */
+    reopen_log();
+
+    if (pid < 0) {
+	--conn_running;
+	error("Failed to create child process: %m");
+	return -1;
+    }
+
+    /* parent */
+    close(pipefd[1]);
+    close(pipefd[0]);
+
+    if (dont_wait) {
+	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);
+}
+
+
+/*
+ * 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, wait)
+    char *prog;
+    char **args;
+    int must_exist;
+    void (*done) __P((void *));
+    void *arg;
+    int wait;
+{
+    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;
+    }
+
+    /* Close syslog BEFORE doing the fork since we can't do it after if we vfork() */
+    closelog();
+
+    pid = vfork();
+    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();
+	close (0);
+	close (1);
+	close (2);
+
+        /* 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);
+    }
+
+    /* Reopen the log in the parent */
+    reopen_log();
+
+    if (pid == -1) {
+	error("Failed to create child process for %s: %m", prog);
+	return -1;
+    }
+
+    if (debug)
+	warn("Script %s started (pid %d)", prog, pid);
+    record_child(pid, prog, done, arg, 0);
+
+    return pid;
+}
+
+#else
+
+/*
+ * safe_fork - Create a child process.  The child closes all the
+ * file descriptors that we don't want to leak to a script.
+ * The parent waits for the child to do this before returning.
+ * This also arranges for the specified fds to be dup'd to
+ * fds 0, 1, 2 in the child.
+ */
+pid_t
+safe_fork(int infd, int outfd, int errfd)
+{
+	pid_t pid;
+	int fd, pipefd[2];
+	char buf[1];
+
+	/* make sure fds 0, 1, 2 are occupied (probably not necessary) */
+	while ((fd = dup(fd_devnull)) >= 0) {
+		if (fd > 2) {
+			close(fd);
+			break;
+		}
+	}
+
+	if (pipe(pipefd) == -1)
+		pipefd[0] = pipefd[1] = -1;
+	pid = fork();
+	if (pid < 0) {
+		error("fork failed: %m");
+		return -1;
+	}
+	if (pid > 0) {
+		/* parent */
+		close(pipefd[1]);
+		/* this read() blocks until the close(pipefd[1]) below */
+		complete_read(pipefd[0], buf, 1);
+		close(pipefd[0]);
+		return pid;
+	}
+
+	/* Executing in the child */
+	sys_close();
+#ifdef USE_TDB
+	tdb_close(pppdb);
+#endif
+
+	/* make sure infd, outfd and errfd won't get tromped on below */
+	if (infd == 1 || infd == 2)
+		infd = dup(infd);
+	if (outfd == 0 || outfd == 2)
+		outfd = dup(outfd);
+	if (errfd == 0 || errfd == 1)
+		errfd = dup(errfd);
+
+	closelog();
+
+	/* dup the in, out, err fds to 0, 1, 2 */
+	if (infd != 0)
+		dup2(infd, 0);
+	if (outfd != 1)
+		dup2(outfd, 1);
+	if (errfd != 2)
+		dup2(errfd, 2);
+
+	if (log_to_fd > 2)
+		close(log_to_fd);
+	if (the_channel->close)
+		(*the_channel->close)();
+	else
+		close(devfd);	/* some plugins don't have a close function */
+	close(fd_ppp);
+	close(fd_devnull);
+	if (infd != 0)
+		close(infd);
+	if (outfd != 1)
+		close(outfd);
+	if (errfd != 2)
+		close(errfd);
+
+	notify(fork_notifier, 0);
+	close(pipefd[0]);
+	/* this close unblocks the read() call above in the parent */
+	close(pipefd[1]);
+
+	return 0;
+}
+
+/*
+ * device_script - run a program to talk to the specified fds
+ * (e.g. to run the connector or disconnector script).
+ * stderr gets connected to the log fd or to the _PATH_CONNERRS file.
+ */
+int
+device_script(program, in, out, dont_wait)
+    char *program;
+    int in, out;
+    int dont_wait;
+{
+    int pid;
+    int status = -1;
+    int errfd;
+
+    if (log_to_fd >= 0)
+	errfd = log_to_fd;
+    else
+	errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
+
+    ++conn_running;
+    pid = safe_fork(in, out, errfd);
+    if (pid != 0 && log_to_fd < 0)
+	close(errfd);
+
+    if (pid < 0) {
+	--conn_running;
+	error("Failed to create child process: %m");
+	return -1;
+    }
+
+    if (pid != 0) {
+	record_child(pid, program, NULL, NULL, 1);
+	status = 0;
+	if (!dont_wait) {
+	    while (waitpid(pid, &status, 0) < 0) {
+		if (errno == EINTR)
+		    continue;
+		fatal("error waiting for (dis)connection process: %m");
+	    }
+	    forget_child(pid, status);
+	    --conn_running;
+	}
+	return (status == 0 ? 0 : -1);
+    }
+
+    /* here we are executing in the child */
+
+    setgid(getgid());
+    setuid(uid);
+    if (getuid() != uid) {
+	fprintf(stderr, "pppd: setuid failed\n");
+	exit(1);
+    }
+    execl("/bin/sh", "sh", "-c", program, (char *)0);
+    perror("pppd: could not exec /bin/sh");
+    exit(99);
+    /* NOTREACHED */
+}
+
+
+/*
+ * run_program - execute a program with given arguments,
+ * but don't wait for it unless wait is non-zero.
+ * If the program can't be executed, logs an error unless
+ * must_exist is 0 and the program file doesn't exist.
+ * Returns -1 if it couldn't fork, 0 if the file doesn't exist
+ * or isn't an executable plain file, or the process ID of the child.
+ * If done != NULL, (*done)(arg) will be called later (within
+ * reap_kids) iff the return value is > 0.
+ */
+pid_t
+run_program(prog, args, must_exist, done, arg, wait)
+    char *prog;
+    char **args;
+    int must_exist;
+    void (*done) __P((void *));
+    void *arg;
+    int wait;
+{
+    int pid, status;
+    struct stat sbuf;
+
+    /*
+     * First check if the file exists and is executable.
+     * We don't use access() because that would use the
+     * real user-id, which might not be root, and the script
+     * might be accessible only to root.
+     */
+    errno = EINVAL;
+    if (stat(prog, &sbuf) < 0 || !S_ISREG(sbuf.st_mode)
+	|| (sbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0) {
+	if (must_exist || errno != ENOENT)
+	    warn("Can't execute %s: %m", prog);
+	return 0;
+    }
+
+    pid = safe_fork(fd_devnull, fd_devnull, fd_devnull);
+    if (pid == -1) {
+	error("Failed to create child process for %s: %m", prog);
+	return -1;
+    }
+    if (pid != 0) {
+	if (debug)
+	    warn("Script %s started (pid %d)", prog, pid);
+	record_child(pid, prog, done, arg, 0);
+	if (wait) {
+	    while (waitpid(pid, &status, 0) < 0) {
+		if (errno == EINTR)
+		    continue;
+		fatal("error waiting for script %s: %m", prog);
+	    }
+	    forget_child(pid, status);
+	}
+	return pid;
+    }
+
+    /* Leave the current location */
+    (void) setsid();	/* No controlling tty. */
+    (void) umask (S_IRWXG|S_IRWXO);
+    (void) chdir ("/");	/* no current directory. */
+    setuid(0);		/* set real UID = root */
+    setgid(getegid());
+
+#ifdef BSD
+    /* Force the priority back to zero if pppd is running higher. */
+    if (setpriority (PRIO_PROCESS, 0, 0) < 0)
+	warn("can't reset priority to 0: %m");
+#endif
+
+    /* run the program */
+    execve(prog, args, script_env);
+    if (must_exist || errno != ENOENT) {
+	/* have to reopen the log, there's nowhere else
+	   for the message to go. */
+	reopen_log();
+	syslog(LOG_ERR, "Can't execute %s: %m", prog);
+	closelog();
+    }
+    _exit(-1);
+}
+
+#endif /* __uClinux__ */
+
+/*
+ * record_child - add a child process to the list for reap_kids
+ * to use.
+ */
+void
+record_child(pid, prog, done, arg, killable)
+    int pid;
+    char *prog;
+    void (*done) __P((void *));
+    void *arg;
+    int killable;
+{
+    struct subprocess *chp;
+
+    ++n_children;
+
+    chp = (struct subprocess *) malloc(sizeof(struct subprocess));
+    if (chp == NULL) {
+	warn("losing track of %s process", prog);
+    } else {
+	chp->pid = pid;
+	chp->prog = prog;
+	chp->done = done;
+	chp->arg = arg;
+	chp->next = children;
+	chp->killable = killable;
+	children = chp;
+    }
+}
+
+/*
+ * childwait_end - we got fed up waiting for the child processes to
+ * exit, send them all a SIGTERM.
+ */
+static void
+childwait_end(arg)
+    void *arg;
+{
+    struct subprocess *chp;
+
+    for (chp = children; chp != NULL; chp = chp->next) {
+	if (debug)
+	    warn("sending SIGTERM to process %d", chp->pid);
+	kill(chp->pid, SIGTERM);
+    }
+    childwait_done = 1;
+}
+
+/*
+ * forget_child - clean up after a dead child
+ */
+static void
+forget_child(pid, status)
+    int pid, status;
+{
+    struct subprocess *chp, **prevp;
+
+    for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) {
+        if (chp->pid == pid) {
+	    --n_children;
+	    *prevp = chp->next;
+	    break;
+	}
+    }
+    if (WIFSIGNALED(status)) {
+        warn("Child process %s (pid %d) terminated with signal %d",
+	     (chp? chp->prog: "??"), pid, WTERMSIG(status));
+    } else if (debug)
+        warn("Script %s finished (pid %d), status = 0x%x",
+	       (chp? chp->prog: "??"), pid,
+	       WIFEXITED(status) ? WEXITSTATUS(status) : status);
+    if (chp && chp->done)
+        (*chp->done)(chp->arg);
+    if (chp)
+        free(chp);
+}
+
+/*
+ * reap_kids - get status from any dead child processes,
+ * and log a message for abnormal terminations.
+ */
+static int
+reap_kids()
+{
+    int pid, status;
+
+    if (n_children == 0)
+	return 0;
+    while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) {
+        forget_child(pid, status);
+    }
+    if (pid == -1) {
+	if (errno == ECHILD)
+	    return -1;
+	if (errno != EINTR)
+	    error("Error waiting for child process: %m");
+    }
+    return 0;
+}
+
+/*
+ * add_notifier - add a new function to be called when something happens.
+ */
+void
+add_notifier(notif, func, arg)
+    struct notifier **notif;
+    notify_func func;
+    void *arg;
+{
+    struct notifier *np;
+
+    np = malloc(sizeof(struct notifier));
+    if (np == 0)
+	novm("notifier struct");
+    np->next = *notif;
+    np->func = func;
+    np->arg = arg;
+    *notif = np;
+}
+
+/*
+ * remove_notifier - remove a function from the list of things to
+ * be called when something happens.
+ */
+void
+remove_notifier(notif, func, arg)
+    struct notifier **notif;
+    notify_func func;
+    void *arg;
+{
+    struct notifier *np;
+
+    for (; (np = *notif) != 0; notif = &np->next) {
+	if (np->func == func && np->arg == arg) {
+	    *notif = np->next;
+	    free(np);
+	    break;
+	}
+    }
+}
+
+/*
+ * notify - call a set of functions registered with add_notifier.
+ */
+void
+notify(notif, val)
+    struct notifier *notif;
+    int val;
+{
+    struct notifier *np;
+
+    while ((np = notif) != 0) {
+	notif = np->next;
+	(*np->func)(np->arg, val);
+    }
+}
+
+/*
+ * novm - log an error message saying we ran out of memory, and die.
+ */
+void
+novm(msg)
+    char *msg;
+{
+    fatal("Virtual memory exhausted allocating %s\n", msg);
+}
+
+/*
+ * script_setenv - set an environment variable value to be used
+ * for scripts that we run (e.g. ip-up, auth-up, etc.)
+ */
+void
+script_setenv(var, value, iskey)
+    char *var, *value;
+    int iskey;
+{
+    size_t varl = strlen(var);
+    size_t vl = varl + strlen(value) + 2;
+    int i;
+    char *p, *newstring;
+
+    newstring = (char *) malloc(vl+1);
+    if (newstring == 0)
+	return;
+    *newstring++ = iskey;
+    slprintf(newstring, vl, "%s=%s", var, value);
+
+    /* check if this variable is already set */
+    if (script_env != 0) {
+	for (i = 0; (p = script_env[i]) != 0; ++i) {
+	    if (strncmp(p, var, varl) == 0 && p[varl] == '=') {
+#ifdef USE_TDB
+		if (p[-1] && pppdb != NULL)
+		    delete_db_key(p);
+#endif
+		free(p-1);
+		script_env[i] = newstring;
+#ifdef USE_TDB
+		if (iskey && pppdb != NULL)
+		    add_db_key(newstring);
+		update_db_entry();
+#endif
+		return;
+	    }
+	}
+    } else {
+	/* no space allocated for script env. ptrs. yet */
+	i = 0;
+	script_env = (char **) malloc(16 * sizeof(char *));
+	if (script_env == 0)
+	    return;
+	s_env_nalloc = 16;
+    }
+
+    /* reallocate script_env with more space if needed */
+    if (i + 1 >= s_env_nalloc) {
+	int new_n = i + 17;
+	char **newenv = (char **) realloc((void *)script_env,
+					  new_n * sizeof(char *));
+	if (newenv == 0)
+	    return;
+	script_env = newenv;
+	s_env_nalloc = new_n;
+    }
+
+    script_env[i] = newstring;
+    script_env[i+1] = 0;
+
+#ifdef USE_TDB
+    if (pppdb != NULL) {
+	if (iskey)
+	    add_db_key(newstring);
+	update_db_entry();
+    }
+#endif
+}
+
+/*
+ * script_unsetenv - remove a variable from the environment
+ * for scripts.
+ */
+void
+script_unsetenv(var)
+    char *var;
+{
+    int vl = strlen(var);
+    int i;
+    char *p;
+
+    if (script_env == 0)
+	return;
+    for (i = 0; (p = script_env[i]) != 0; ++i) {
+	if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
+#ifdef USE_TDB
+	    if (p[-1] && pppdb != NULL)
+		delete_db_key(p);
+#endif
+	    free(p-1);
+	    while ((script_env[i] = script_env[i+1]) != 0)
+		++i;
+	    break;
+	}
+    }
+#ifdef USE_TDB
+    if (pppdb != NULL)
+	update_db_entry();
+#endif
+}
+
+void get_path_conf()    
+{
+	
+	char path_conf[50]={0};
+	
+	sc_cfg_get("path_conf", path_conf, sizeof(path_conf));
+	sprintf(PATH_SYSOPTIONS,"%s/options",path_conf);
+	sprintf(PATH_TTYOPT,"%s/options",path_conf);
+	sprintf(PATH_UPAPFILE,"%s/pap-secrets",path_conf);
+	sprintf(PATH_CHAPFILE,"%s/chap-secrets",path_conf);
+}
+
+/*
+ * Any arbitrary string used as a key for locking the database.
+ * It doesn't matter what it is as long as all pppds use the same string.
+ */
+#define PPPD_LOCK_KEY	"pppd lock"
+
+/*
+ * lock_db - get an exclusive lock on the TDB database.
+ * Used to ensure atomicity of various lookup/modify operations.
+ */
+void lock_db()
+{
+#ifdef USE_TDB
+	TDB_DATA key;
+
+	key.dptr = PPPD_LOCK_KEY;
+	key.dsize = strlen(key.dptr);
+	tdb_chainlock(pppdb, key);
+#endif
+}
+
+/*
+ * unlock_db - remove the exclusive lock obtained by lock_db.
+ */
+void unlock_db()
+{
+#ifdef USE_TDB
+	TDB_DATA key;
+
+	key.dptr = PPPD_LOCK_KEY;
+	key.dsize = strlen(key.dptr);
+	tdb_chainunlock(pppdb, key);
+#endif
+}
+
+#ifdef USE_TDB
+/*
+ * update_db_entry - update our entry in the database.
+ */
+static void
+update_db_entry()
+{
+    TDB_DATA key, dbuf;
+    int vlen, i;
+    char *p, *q, *vbuf;
+
+    if (script_env == NULL)
+	return;
+    vlen = 0;
+    for (i = 0; (p = script_env[i]) != 0; ++i)
+	vlen += strlen(p) + 1;
+    vbuf = malloc(vlen + 1);
+    if (vbuf == 0)
+	novm("database entry");
+    q = vbuf;
+    for (i = 0; (p = script_env[i]) != 0; ++i)
+	q += slprintf(q, vbuf + vlen - q, "%s;", p);
+
+    key.dptr = db_key;
+    key.dsize = strlen(db_key);
+    dbuf.dptr = vbuf;
+    dbuf.dsize = vlen;
+    if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+	error("tdb_store failed: %s", tdb_errorstr(pppdb));
+
+    if (vbuf)
+        free(vbuf);
+
+}
+
+/*
+ * add_db_key - add a key that we can use to look up our database entry.
+ */
+static void
+add_db_key(str)
+    const char *str;
+{
+    TDB_DATA key, dbuf;
+
+    key.dptr = (char *) str;
+    key.dsize = strlen(str);
+    dbuf.dptr = db_key;
+    dbuf.dsize = strlen(db_key);
+    if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+	error("tdb_store key failed: %s", tdb_errorstr(pppdb));
+}
+
+/*
+ * delete_db_key - delete a key for looking up our database entry.
+ */
+static void
+delete_db_key(str)
+    const char *str;
+{
+    TDB_DATA key;
+
+    key.dptr = (char *) str;
+    key.dsize = strlen(str);
+    tdb_delete(pppdb, key);
+}
+
+/*
+ * cleanup_db - delete all the entries we put in the database.
+ */
+static void
+cleanup_db()
+{
+    TDB_DATA key;
+    int i;
+    char *p;
+
+    key.dptr = db_key;
+    key.dsize = strlen(db_key);
+    tdb_delete(pppdb, key);
+    for (i = 0; (p = script_env[i]) != 0; ++i)
+	if (p[-1])
+	    delete_db_key(p);
+}
+#endif /* USE_TDB */
diff --git a/ap/app/pppd/pppd/md4.c b/ap/app/pppd/pppd/md4.c
new file mode 100644
index 0000000..d943e88
--- /dev/null
+++ b/ap/app/pppd/pppd/md4.c
@@ -0,0 +1,299 @@
+/*
+** ********************************************************************
+** 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 */
+    if (count)
+      for (i=0;i<=byte;i++) XX[i] = X[i];
+    for (i=byte+1;i<64;i++) XX[i] = 0;
+    /* Add padding '1' bit and low-order zeros in last byte */
+    mask = 1 << (7 - bit);
+    XX[byte] = (XX[byte] | mask) & ~( mask - 1);
+    /* If room for bit count, finish up with this block */
+    if (byte <= 55)
+    {
+      for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
+      MDblock(MDp,XX);
+    }
+    else /* need to do two blocks to finish up */
+    {
+      MDblock(MDp,XX);
+      for (i=0;i<56;i++) XX[i] = 0;
+      for (i=0;i<8;i++)  XX[56+i] = MDp->count[i];
+      MDblock(MDp,XX);
+    }
+    /* Set flag saying we're done with MD computation */
+    MDp->done = 1;
+  }
+}
+
+/*
+** Finish up MD4 computation and return message digest.
+*/
+void
+MD4Final(buf, MD)
+unsigned char *buf;
+MD4_CTX *MD;
+{
+  int i, j;
+  unsigned int w;
+
+  MD4Update(MD, NULL, 0);
+  for (i = 0; i < 4; ++i) {
+    w = MD->buffer[i];
+    for (j = 0; j < 4; ++j) {
+      *buf++ = w;
+      w >>= 8;
+    }
+  }
+}
+
+/*
+** End of md4.c
+****************************(cut)***********************************/
diff --git a/ap/app/pppd/pppd/md4.h b/ap/app/pppd/pppd/md4.h
new file mode 100644
index 0000000..80e8f9a
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/pppd/md5.c b/ap/app/pppd/pppd/md5.c
new file mode 100644
index 0000000..f1291ce
--- /dev/null
+++ b/ap/app/pppd/pppd/md5.c
@@ -0,0 +1,307 @@
+
+
+/*
+ ***********************************************************************
+ ** 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 <string.h>
+#include "md5.h"
+
+/*
+ ***********************************************************************
+ **  Message-digest routines:                                         **
+ **  To form the message digest for a message M                       **
+ **    (1) Initialize a context buffer mdContext using MD5_Init       **
+ **    (2) Call MD5_Update on mdContext and M                         **
+ **    (3) Call MD5_Final on mdContext                                **
+ **  The message digest is now in mdContext->digest[0...15]           **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform (UINT4 *buf, UINT4 *in);
+
+static unsigned char PADDING[64] = {
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+  {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) \
+  {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) \
+  {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) \
+  {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+
+#ifdef __STDC__
+#define UL(x)	x##U
+#else
+#define UL(x)	x
+#endif
+
+/* The routine MD5_Init initializes the message-digest context
+   mdContext. All fields are set to zero.
+ */
+void MD5_Init (mdContext)
+MD5_CTX *mdContext;
+{
+  mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+  /* Load magic initialization constants.
+   */
+  mdContext->buf[0] = (UINT4)0x67452301;
+  mdContext->buf[1] = (UINT4)0xefcdab89;
+  mdContext->buf[2] = (UINT4)0x98badcfe;
+  mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+/* The routine MD5Update updates the message-digest context to
+   account for the presence of each of the characters inBuf[0..inLen-1]
+   in the message whose digest is being computed.
+ */
+void MD5_Update (mdContext, inBuf, inLen)
+MD5_CTX *mdContext;
+unsigned char *inBuf;
+unsigned int inLen;
+{
+  UINT4 in[16];
+  int mdi;
+  unsigned int i, ii;
+
+  /* compute number of bytes mod 64 */
+  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+  /* update number of bits */
+  if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+    mdContext->i[1]++;
+  mdContext->i[0] += ((UINT4)inLen << 3);
+  mdContext->i[1] += ((UINT4)inLen >> 29);
+
+  while (inLen--) {
+    /* add new character to buffer, increment mdi */
+    mdContext->in[mdi++] = *inBuf++;
+
+    /* transform if necessary */
+    if (mdi == 0x40) {
+      for (i = 0, ii = 0; i < 16; i++, ii += 4)
+        in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+                (((UINT4)mdContext->in[ii+2]) << 16) |
+                (((UINT4)mdContext->in[ii+1]) << 8) |
+                ((UINT4)mdContext->in[ii]);
+      Transform (mdContext->buf, in);
+      mdi = 0;
+    }
+  }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+   ends with the desired message digest in mdContext->digest[0...15].
+ */
+void MD5_Final (hash, mdContext)
+unsigned char hash[];
+MD5_CTX *mdContext;
+{
+  UINT4 in[16];
+  int mdi;
+  unsigned int i, ii;
+  unsigned int padLen;
+
+  /* save number of bits */
+  in[14] = mdContext->i[0];
+  in[15] = mdContext->i[1];
+
+  /* compute number of bytes mod 64 */
+  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+  /* pad out to 56 mod 64 */
+  padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+  MD5_Update (mdContext, PADDING, padLen);
+
+  /* append length in bits and transform */
+  for (i = 0, ii = 0; i < 14; i++, ii += 4)
+    in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+            (((UINT4)mdContext->in[ii+2]) << 16) |
+            (((UINT4)mdContext->in[ii+1]) << 8) |
+            ((UINT4)mdContext->in[ii]);
+  Transform (mdContext->buf, in);
+
+  /* store buffer in digest */
+  for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+    mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+    mdContext->digest[ii+1] =
+      (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+    mdContext->digest[ii+2] =
+      (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+    mdContext->digest[ii+3] =
+      (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+  }
+  memcpy(hash, mdContext->digest, 16);
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void Transform (buf, in)
+UINT4 *buf;
+UINT4 *in;
+{
+  UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+  /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+  FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */
+  FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */
+  FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */
+  FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */
+  FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */
+  FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */
+  FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */
+  FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */
+  FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */
+  FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */
+  FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */
+  FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */
+  FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */
+  FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */
+  FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */
+  FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */
+
+  /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+  GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */
+  GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */
+  GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */
+  GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */
+  GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */
+  GG ( d, a, b, c, in[10], S22, UL(  38016083)); /* 22 */
+  GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */
+  GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */
+  GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */
+  GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */
+  GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */
+  GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */
+  GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */
+  GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */
+  GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */
+  GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */
+
+  /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+  HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */
+  HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */
+  HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */
+  HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */
+  HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */
+  HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */
+  HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */
+  HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */
+  HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */
+  HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */
+  HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */
+  HH ( b, c, d, a, in[ 6], S34, UL(  76029189)); /* 44 */
+  HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */
+  HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */
+  HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */
+  HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */
+
+  /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+  II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */
+  II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */
+  II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */
+  II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */
+  II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */
+  II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */
+  II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */
+  II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */
+  II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */
+  II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */
+  II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */
+  II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */
+  II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */
+  II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */
+  II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */
+  II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */
+
+  buf[0] += a;
+  buf[1] += b;
+  buf[2] += c;
+  buf[3] += d;
+}
+
+/*
+ ***********************************************************************
+ ** End of md5.c                                                      **
+ ******************************** (cut) ********************************
+ */
diff --git a/ap/app/pppd/pppd/md5.h b/ap/app/pppd/pppd/md5.h
new file mode 100644
index 0000000..71e8b00
--- /dev/null
+++ b/ap/app/pppd/pppd/md5.h
@@ -0,0 +1,65 @@
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5                    **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm              **
+ ** Created: 2/17/90 RLR                                              **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version               **
+ ** Revised (for MD5): RLR 4/27/91                                    **
+ **   -- G modified to have y&~z instead of y&z                       **
+ **   -- FF, GG, HH modified to add in last register done             **
+ **   -- Access pattern: round 2 works mod 5, round 3 works mod 3     **
+ **   -- distinct additive constant for each step                     **
+ **   -- round 4 added, working mod 7                                 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.  **
+ **                                                                   **
+ ** License to copy and use this software is granted provided that    **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message-     **
+ ** Digest Algorithm" in all material mentioning or referencing this  **
+ ** software or this function.                                        **
+ **                                                                   **
+ ** License is also granted to make and use derivative works          **
+ ** provided that such works are identified as "derived from the RSA  **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all          **
+ ** material mentioning or referencing the derived work.              **
+ **                                                                   **
+ ** RSA Data Security, Inc. makes no representations concerning       **
+ ** either the merchantability of this software or the suitability    **
+ ** of this software for any particular purpose.  It is provided "as  **
+ ** is" without express or implied warranty of any kind.              **
+ **                                                                   **
+ ** These notices must be retained in any copies of any part of this  **
+ ** documentation and/or software.                                    **
+ ***********************************************************************
+ */
+
+#ifndef __MD5_INCLUDE__
+
+/* typedef a 32-bit type */
+#ifdef _LP64
+typedef unsigned int UINT4;
+typedef int          INT4;
+#else
+typedef unsigned long UINT4;
+typedef long          INT4;
+#endif
+#define _UINT4_T
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+  UINT4 i[2];                   /* number of _bits_ handled mod 2^64 */
+  UINT4 buf[4];                                    /* scratch buffer */
+  unsigned char in[64];                              /* input buffer */
+  unsigned char digest[16];     /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5_Init (MD5_CTX *mdContext);
+void MD5_Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
+void MD5_Final (unsigned char hash[], MD5_CTX *mdContext);
+
+#define __MD5_INCLUDE__
+#endif /* __MD5_INCLUDE__ */
diff --git a/ap/app/pppd/pppd/mppe.h b/ap/app/pppd/pppd/mppe.h
new file mode 100644
index 0000000..5eb3b37
--- /dev/null
+++ b/ap/app/pppd/pppd/mppe.h
@@ -0,0 +1,121 @@
+/*
+ * mppe.h - Definitions for MPPE
+ *
+ * Copyright (c) 2008 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define MPPE_PAD		4	/* MPPE growth per frame */
+#define MPPE_MAX_KEY_LEN	16	/* largest key length (128-bit) */
+
+/* option bits for ccp_options.mppe */
+#define MPPE_OPT_40		0x01	/* 40 bit */
+#define MPPE_OPT_128		0x02	/* 128 bit */
+#define MPPE_OPT_STATEFUL	0x04	/* stateful mode */
+/* unsupported opts */
+#define MPPE_OPT_56		0x08	/* 56 bit */
+#define MPPE_OPT_MPPC		0x10	/* MPPC compression */
+#define MPPE_OPT_D		0x20	/* Unknown */
+#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
+#define MPPE_OPT_UNKNOWN	0x40	/* Bits !defined in RFC 3078 were set */
+
+/*
+ * This is not nice ... the alternative is a bitfield struct though.
+ * And unfortunately, we cannot share the same bits for the option
+ * names above since C and H are the same bit.  We could do a u_int32
+ * but then we have to do a htonl() all the time and/or we still need
+ * to know which octet is which.
+ */
+#define MPPE_C_BIT		0x01	/* MPPC */
+#define MPPE_D_BIT		0x10	/* Obsolete, usage unknown */
+#define MPPE_L_BIT		0x20	/* 40-bit */
+#define MPPE_S_BIT		0x40	/* 128-bit */
+#define MPPE_M_BIT		0x80	/* 56-bit, not supported */
+#define MPPE_H_BIT		0x01	/* Stateless (in a different byte) */
+
+/* Does not include H bit; used for least significant octet only. */
+#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
+
+/* Build a CI from mppe opts (see RFC 3078) */
+#define MPPE_OPTS_TO_CI(opts, ci)		\
+    do {					\
+	u_char *ptr = ci; /* u_char[4] */	\
+						\
+	/* H bit */				\
+	if (opts & MPPE_OPT_STATEFUL)		\
+	    *ptr++ = 0x0;			\
+	else					\
+	    *ptr++ = MPPE_H_BIT;		\
+	*ptr++ = 0;				\
+	*ptr++ = 0;				\
+						\
+	/* S,L bits */				\
+	*ptr = 0;				\
+	if (opts & MPPE_OPT_128)		\
+	    *ptr |= MPPE_S_BIT;			\
+	if (opts & MPPE_OPT_40)			\
+	    *ptr |= MPPE_L_BIT;			\
+	/* M,D,C bits not supported */		\
+    } while (/* CONSTCOND */ 0)
+
+/* The reverse of the above */
+#define MPPE_CI_TO_OPTS(ci, opts)		\
+    do {					\
+	u_char *ptr = ci; /* u_char[4] */	\
+						\
+	opts = 0;				\
+						\
+	/* H bit */				\
+	if (!(ptr[0] & MPPE_H_BIT))		\
+	    opts |= MPPE_OPT_STATEFUL;		\
+						\
+	/* S,L bits */				\
+	if (ptr[3] & MPPE_S_BIT)		\
+	    opts |= MPPE_OPT_128;		\
+	if (ptr[3] & MPPE_L_BIT)		\
+	    opts |= MPPE_OPT_40;		\
+						\
+	/* M,D,C bits */			\
+	if (ptr[3] & MPPE_M_BIT)		\
+	    opts |= MPPE_OPT_56;		\
+	if (ptr[3] & MPPE_D_BIT)		\
+	    opts |= MPPE_OPT_D;			\
+	if (ptr[3] & MPPE_C_BIT)		\
+	    opts |= MPPE_OPT_MPPC;		\
+						\
+	/* Other bits */			\
+	if (ptr[0] & ~MPPE_H_BIT)		\
+	    opts |= MPPE_OPT_UNKNOWN;		\
+	if (ptr[1] || ptr[2])			\
+	    opts |= MPPE_OPT_UNKNOWN;		\
+	if (ptr[3] & ~MPPE_ALL_BITS)		\
+	    opts |= MPPE_OPT_UNKNOWN;		\
+    } while (/* CONSTCOND */ 0)
diff --git a/ap/app/pppd/pppd/multilink.c b/ap/app/pppd/pppd/multilink.c
new file mode 100644
index 0000000..de1c45a
--- /dev/null
+++ b/ap/app/pppd/pppd/multilink.c
@@ -0,0 +1,592 @@
+/*
+ * multilink.c - support routines for multilink.
+ *
+ * Copyright (c) 2000-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <errno.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <unistd.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 */
+char *blinks_id;		/* key for the list of links */
+bool doing_multilink;		/* multilink was enabled and agreed to */
+bool multilink_master;		/* we own the multilink bundle */
+
+extern TDB_CONTEXT *pppdb;
+extern char db_key[];
+
+static void make_bundle_links __P((int append));
+static void remove_bundle_link __P((void));
+static void iterate_bundle_links __P((void (*func) __P((char *))));
+
+static int get_default_epdisc __P((struct epdisc *));
+static int parse_num __P((char *str, const char *key, int *valp));
+static int owns_unit __P((TDB_DATA pid, int unit));
+
+#define set_ip_epdisc(ep, addr) do {	\
+	ep->length = 4;			\
+	ep->value[0] = addr >> 24;	\
+	ep->value[1] = addr >> 16;	\
+	ep->value[2] = addr >> 8;	\
+	ep->value[3] = addr;		\
+} while (0)
+
+#define LOCAL_IP_ADDR(addr)						  \
+	(((addr) & 0xff000000) == 0x0a000000		/* 10.x.x.x */	  \
+	 || ((addr) & 0xfff00000) == 0xac100000		/* 172.16.x.x */  \
+	 || ((addr) & 0xffff0000) == 0xc0a80000)	/* 192.168.x.x */
+
+#define process_exists(n)	(kill((n), 0) == 0 || errno != ESRCH)
+
+void
+mp_check_options()
+{
+	lcp_options *wo = &lcp_wantoptions[0];
+	lcp_options *ao = &lcp_allowoptions[0];
+
+	doing_multilink = 0;
+	if (!multilink)
+		return;
+	/* if we're doing multilink, we have to negotiate MRRU */
+	if (!wo->neg_mrru) {
+		/* mrru not specified, default to mru */
+		wo->mrru = wo->mru;
+		wo->neg_mrru = 1;
+	}
+	ao->mrru = ao->mru;
+	ao->neg_mrru = 1;
+
+	if (!wo->neg_endpoint && !noendpoint) {
+		/* get a default endpoint value */
+		wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
+	}
+}
+
+/*
+ * Make a new bundle or join us to an existing bundle
+ * if we are doing multilink.
+ */
+int
+mp_join_bundle()
+{
+	lcp_options *go = &lcp_gotoptions[0];
+	lcp_options *ho = &lcp_hisoptions[0];
+	lcp_options *ao = &lcp_allowoptions[0];
+	int unit, pppd_pid;
+	int l, mtu;
+	char *p;
+	TDB_DATA key, pid, rec;
+
+	if (doing_multilink) {
+		/* have previously joined a bundle */
+		if (!go->neg_mrru || !ho->neg_mrru) {
+			warn("oops, didn't get multilink on renegotiation");
+			lcp_close(0, "multilink required");
+			return 0;
+		}
+		/* XXX should check the peer_authname and ho->endpoint
+		   are the same as previously */
+		return 0;
+	}
+
+	if (!go->neg_mrru || !ho->neg_mrru) {
+		/* not doing multilink */
+		if (go->neg_mrru)
+			warn("oops, multilink negotiated only for receive");
+		mtu = ho->neg_mru? ho->mru: PPP_MRU;
+		if (mtu > ao->mru)
+			mtu = ao->mru;
+		if (demand) {
+			/* already have a bundle */
+			cfg_bundle(0, 0, 0, 0);
+			netif_set_mtu(0, mtu);
+			return 0;
+		}
+		make_new_bundle(0, 0, 0, 0);
+		set_ifunit(1);
+		netif_set_mtu(0, mtu);
+		return 0;
+	}
+
+	doing_multilink = 1;
+
+	/*
+	 * Find the appropriate bundle or join a new one.
+	 * First we make up a name for the bundle.
+	 * The length estimate is worst-case assuming every
+	 * character has to be quoted.
+	 */
+	l = 4 * strlen(peer_authname) + 10;
+	if (ho->neg_endpoint)
+		l += 3 * ho->endpoint.length + 8;
+	if (bundle_name)
+		l += 3 * strlen(bundle_name) + 2;
+	bundle_id = malloc(l);
+	if (bundle_id == 0)
+		novm("bundle identifier");
+
+	p = bundle_id;
+	p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
+	if (ho->neg_endpoint || bundle_name)
+		*p++ = '/';
+	if (ho->neg_endpoint)
+		p += slprintf(p, bundle_id+l-p, "%s",
+			      epdisc_to_str(&ho->endpoint));
+	if (bundle_name)
+		p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
+
+	/* Make the key for the list of links belonging to the bundle */
+	l = p - bundle_id;
+	blinks_id = malloc(l + 7);
+	if (blinks_id == NULL)
+		novm("bundle links key");
+	slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7);
+
+	/*
+	 * For demand mode, we only need to configure the bundle
+	 * and attach the link.
+	 */
+	mtu = MIN(ho->mrru, ao->mru);
+	if (demand) {
+		cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+		netif_set_mtu(0, mtu);
+		script_setenv("BUNDLE", bundle_id + 7, 1);
+		return 0;
+	}
+
+	/*
+	 * Check if the bundle ID is already in the database.
+	 */
+	unit = -1;
+	lock_db();
+	key.dptr = bundle_id;
+	key.dsize = p - bundle_id;
+	pid = tdb_fetch(pppdb, key);
+	if (pid.dptr != NULL) {
+		/* bundle ID exists, see if the pppd record exists */
+		rec = tdb_fetch(pppdb, pid);
+		if (rec.dptr != NULL && rec.dsize > 0) {
+			/* make sure the string is null-terminated */
+			rec.dptr[rec.dsize-1] = 0;
+			/* parse the interface number */
+			parse_num(rec.dptr, "IFNAME=ppp", &unit);
+			/* check the pid value */
+			if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
+			    || !process_exists(pppd_pid)
+			    || !owns_unit(pid, unit))
+				unit = -1;
+			free(rec.dptr);
+		}
+		free(pid.dptr);
+	}
+
+	if (unit >= 0) {
+		/* attach to existing unit */
+		if (bundle_attach(unit)) {
+			set_ifunit(0);
+			script_setenv("BUNDLE", bundle_id + 7, 0);
+			make_bundle_links(1);
+			unlock_db();
+			warn("Link attached to %s", ifname);
+			return 1;
+		}
+		/* attach failed because bundle doesn't exist */
+	}
+
+	/* we have to make a new bundle */
+	make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+	set_ifunit(1);
+	netif_set_mtu(0, mtu);
+	script_setenv("BUNDLE", bundle_id + 7, 1);
+	make_bundle_links(0);
+	unlock_db();
+	warn("New bundle %s created", ifname);
+	multilink_master = 1;
+	return 0;
+}
+
+void mp_exit_bundle()
+{
+	lock_db();
+	remove_bundle_link();
+	unlock_db();
+}
+
+static void sendhup(char *str)
+{
+	int pid;
+
+	if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) {
+		if (debug)
+			warn("sending SIGHUP to process %d", pid);
+		kill(pid, SIGHUP);
+	}
+}
+
+void mp_bundle_terminated()
+{
+	TDB_DATA key;
+
+	bundle_terminating = 1;
+	upper_layers_down(0);
+	warn("Connection terminated.");
+	print_link_stats();
+	if (!demand) {
+		remove_pidfiles();
+		script_unsetenv("IFNAME");
+	}
+
+	lock_db();
+	destroy_bundle();
+	iterate_bundle_links(sendhup);
+	key.dptr = blinks_id;
+	key.dsize = strlen(blinks_id);
+	tdb_delete(pppdb, key);
+	unlock_db();
+
+	new_phase(PHASE_DEAD);
+
+	doing_multilink = 0;
+	multilink_master = 0;
+}
+
+static void make_bundle_links(int append)
+{
+	TDB_DATA key, rec;
+	char *p;
+	char entry[32];
+	int l;
+
+	key.dptr = blinks_id;
+	key.dsize = strlen(blinks_id);
+	slprintf(entry, sizeof(entry), "%s;", db_key);
+	p = entry;
+	if (append) {
+		rec = tdb_fetch(pppdb, key);
+		if (rec.dptr != NULL && rec.dsize > 0) {
+			rec.dptr[rec.dsize-1] = 0;
+			if (strstr(rec.dptr, db_key) != NULL) {
+				/* already in there? strange */
+				warn("link entry already exists in tdb");
+				return;
+			}
+			l = rec.dsize + strlen(entry);
+			p = malloc(l);
+			if (p == NULL)
+				novm("bundle link list");
+			slprintf(p, l, "%s%s", rec.dptr, entry);
+		} else {
+			warn("bundle link list not found");
+		}
+		if (rec.dptr != NULL)
+			free(rec.dptr);
+	}
+	rec.dptr = p;
+	rec.dsize = strlen(p) + 1;
+	if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+		error("couldn't %s bundle link list",
+		      append? "update": "create");
+	if (p != entry)
+		free(p);
+}
+
+static void remove_bundle_link()
+{
+	TDB_DATA key, rec;
+	char entry[32];
+	char *p, *q;
+	int l;
+
+	key.dptr = blinks_id;
+	key.dsize = strlen(blinks_id);
+	slprintf(entry, sizeof(entry), "%s;", db_key);
+
+	rec = tdb_fetch(pppdb, key);
+	if (rec.dptr == NULL || rec.dsize <= 0) {
+		if (rec.dptr != NULL)
+			free(rec.dptr);
+		return;
+	}
+	rec.dptr[rec.dsize-1] = 0;
+	p = strstr(rec.dptr, entry);
+	if (p != NULL) {
+		q = p + strlen(entry);
+		l = strlen(q) + 1;
+		memmove(p, q, l);
+		rec.dsize = p - rec.dptr + l;
+		if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+			error("couldn't update bundle link list (removal)");
+	}
+	free(rec.dptr);
+}
+
+static void iterate_bundle_links(void (*func)(char *))
+{
+	TDB_DATA key, rec, pp;
+	char *p, *q;
+
+	key.dptr = blinks_id;
+	key.dsize = strlen(blinks_id);
+	rec = tdb_fetch(pppdb, key);
+	if (rec.dptr == NULL || rec.dsize <= 0) {
+		error("bundle link list not found (iterating list)");
+		if (rec.dptr != NULL)
+			free(rec.dptr);
+		return;
+	}
+	p = rec.dptr;
+	p[rec.dsize-1] = 0;
+	while ((q = strchr(p, ';')) != NULL) {
+		*q = 0;
+		key.dptr = p;
+		key.dsize = q - p;
+		pp = tdb_fetch(pppdb, key);
+		if (pp.dptr != NULL && pp.dsize > 0) {
+			pp.dptr[pp.dsize-1] = 0;
+			func(pp.dptr);
+		}
+		if (pp.dptr != NULL)
+			free(pp.dptr);
+		p = q + 1;
+	}
+	free(rec.dptr);
+}
+
+static int
+parse_num(str, key, valp)
+     char *str;
+     const char *key;
+     int *valp;
+{
+	char *p, *endp;
+	int i;
+
+	p = strstr(str, key);
+	if (p != 0) {
+		p += strlen(key);
+		i = strtol(p, &endp, 10);
+		if (endp != p && (*endp == 0 || *endp == ';')) {
+			*valp = i;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Check whether the pppd identified by `key' still owns ppp unit `unit'.
+ */
+static int
+owns_unit(key, unit)
+     TDB_DATA key;
+     int unit;
+{
+	char ifkey[32];
+	TDB_DATA kd, vd;
+	int ret = 0;
+
+	slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
+	kd.dptr = ifkey;
+	kd.dsize = strlen(ifkey);
+	vd = tdb_fetch(pppdb, kd);
+	if (vd.dptr != NULL) {
+		ret = vd.dsize == key.dsize
+			&& memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
+		free(vd.dptr);
+	}
+	return ret;
+}
+
+static int
+get_default_epdisc(ep)
+     struct epdisc *ep;
+{
+	char *p;
+	struct hostent *hp;
+	u_int32_t addr;
+
+	/* First try for an ethernet MAC address */
+	p = get_first_ethernet();
+	if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) {
+		ep->class = EPD_MAC;
+		ep->length = 6;
+		return 1;
+	}
+
+	/* see if our hostname corresponds to a reasonable IP address */
+	hp = gethostbyname(hostname);
+	if (hp != NULL) {
+		addr = *(u_int32_t *)hp->h_addr;
+		if (!bad_ip_adrs(addr)) {
+			addr = ntohl(addr);
+			if (!LOCAL_IP_ADDR(addr)) {
+				ep->class = EPD_IP;
+				set_ip_epdisc(ep, addr);
+				return 1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * epdisc_to_str - make a printable string from an endpoint discriminator.
+ */
+
+static char *endp_class_names[] = {
+    "null", "local", "IP", "MAC", "magic", "phone"
+};
+
+char *
+epdisc_to_str(ep)
+     struct epdisc *ep;
+{
+	static char str[MAX_ENDP_LEN*3+8];
+	u_char *p = ep->value;
+	int i, mask = 0;
+	char *q, c, c2;
+
+	if (ep->class == EPD_NULL && ep->length == 0)
+		return "null";
+	if (ep->class == EPD_IP && ep->length == 4) {
+		u_int32_t addr;
+
+		GETLONG(addr, p);
+		slprintf(str, sizeof(str), "IP:%I", htonl(addr));
+		return str;
+	}
+
+	c = ':';
+	c2 = '.';
+	if (ep->class == EPD_MAC && ep->length == 6)
+		c2 = ':';
+	else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
+		mask = 3;
+	q = str;
+	if (ep->class <= EPD_PHONENUM)
+		q += slprintf(q, sizeof(str)-1, "%s",
+			      endp_class_names[ep->class]);
+	else
+		q += slprintf(q, sizeof(str)-1, "%d", ep->class);
+	c = ':';
+	for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
+		if ((i & mask) == 0) {
+			*q++ = c;
+			c = c2;
+		}
+		q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
+	}
+	return str;
+}
+
+static int hexc_val(int c)
+{
+	if (c >= 'a')
+		return c - 'a' + 10;
+	if (c >= 'A')
+		return c - 'A' + 10;
+	return c - '0';
+}
+
+int
+str_to_epdisc(ep, str)
+     struct epdisc *ep;
+     char *str;
+{
+	int i, l;
+	char *p, *endp;
+
+	for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
+		int sl = strlen(endp_class_names[i]);
+		if (strncasecmp(str, endp_class_names[i], sl) == 0) {
+			str += sl;
+			break;
+		}
+	}
+	if (i > EPD_PHONENUM) {
+		/* not a class name, try a decimal class number */
+		i = strtol(str, &endp, 10);
+		if (endp == str)
+			return 0;	/* can't parse class number */
+		str = endp;
+	}
+	ep->class = i;
+	if (*str == 0) {
+		ep->length = 0;
+		return 1;
+	}
+	if (*str != ':' && *str != '.')
+		return 0;
+	++str;
+
+	if (i == EPD_IP) {
+		u_int32_t addr;
+		i = parse_dotted_ip(str, &addr);
+		if (i == 0 || str[i] != 0)
+			return 0;
+		set_ip_epdisc(ep, addr);
+		return 1;
+	}
+	if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
+		ep->length = 6;
+		return 1;
+	}
+
+	p = str;
+	for (l = 0; l < MAX_ENDP_LEN; ++l) {
+		if (*str == 0)
+			break;
+		if (p <= str)
+			for (p = str; isxdigit(*p); ++p)
+				;
+		i = p - str;
+		if (i == 0)
+			return 0;
+		ep->value[l] = hexc_val(*str++);
+		if ((i & 1) == 0)
+			ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
+		if (*str == ':' || *str == '.')
+			++str;
+	}
+	if (*str != 0 || (ep->class == EPD_MAC && l != 6))
+		return 0;
+	ep->length = l;
+	return 1;
+}
+
diff --git a/ap/app/pppd/pppd/options.c b/ap/app/pppd/pppd/options.c
new file mode 100644
index 0000000..7b9f9d1
--- /dev/null
+++ b/ap/app/pppd/pppd/options.c
@@ -0,0 +1,1751 @@
+/*
+ * options.c - handles option processing for PPP.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: options.c,v 1.102 2008/06/15 06:53:06 paulus Exp $"
+
+#include <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_DYNAMIC
+#include <dlfcn.h>
+#endif
+
+#ifdef PPP_FILTER
+#include <pcap.h>
+/*
+ * There have been 3 or 4 different names for this in libpcap CVS, but
+ * this seems to be what they have settled on...
+ * For older versions of libpcap, use DLT_PPP - but that means
+ * we lose the inbound and outbound qualifiers.
+ */
+#ifndef DLT_PPP_PPPD
+#ifdef DLT_PPP_WITHDIRECTION
+#define DLT_PPP_PPPD	DLT_PPP_WITHDIRECTION
+#else
+#define DLT_PPP_PPPD	DLT_PPP
+#endif
+#endif
+#endif /* PPP_FILTER */
+
+#include "pppd.h"
+#include "pathnames.h"
+
+#if defined(ultrix) || defined(NeXT)
+char *strdup __P((char *));
+#endif
+
+static const char rcsid[] = RCSID;
+extern char PATH_TTYOPT[30];
+struct option_value {
+    struct option_value *next;
+    const char *source;
+    char value[1];
+};
+
+/*
+ * Option variables and default values.
+ */
+int	debug = 0;		/* Debug flag */
+int	kdebugflag = 0;		/* Tell kernel to print debug messages */
+int	default_device = 1;	/* Using /dev/tty or equivalent */
+char	devnam[MAXPATHLEN];	/* Device name */
+bool	nodetach = 0;		/* Don't detach from controlling tty */
+bool	updetach = 0;		/* Detach once link is up */
+int	maxconnect = 0;		/* Maximum connect time */
+char	user[MAXNAMELEN];	/* Username for PAP */
+char	passwd[MAXSECRETLEN];	/* Password for PAP */
+bool	persist = 0;		/* Reopen link after it goes down */
+char	our_name[MAXNAMELEN];	/* Our name for authentication purposes */
+bool	demand = 0;		/* do dial-on-demand */
+char	*ip_up = NULL;		/* user defined ip-up script */
+char	*ip_down = NULL;	/* user defined ip-down script */
+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 = 100;		/* max # of unsuccessful connection attempts */
+char	linkname[MAXPATHLEN];	/* logical name for link */
+bool	tune_kernel;		/* may alter kernel settings */
+int	connect_delay = 1000;	/* wait this many ms after connect script */
+int	req_unit = -1;		/* requested interface unit */
+bool	multilink = 0;		/* Enable multilink operation */
+char	*bundle_name = NULL;	/* bundle name for multilink */
+bool	dump_options;		/* print out option values */
+bool	dryrun;			/* print out option values and exit */
+char	*domain;		/* domain name set by domain option */
+int	child_wait = 5;		/* # seconds to wait for children at exit */
+
+u_int32_t	metric = 0;	/* the metric to set the host route to */
+u_int32_t	drmetric = 0;	/* the default route metric to set */
+char	pid_file[MAXNAMELEN];	/* name of our pid file */
+
+#ifdef MAXOCTETS
+unsigned int  maxoctets = 0;    /* default - no limit */
+int maxoctets_dir = 0;       /* default - sum of traffic */
+int maxoctets_timeout = 1;   /* default 1 second */ 
+#endif
+
+
+extern option_t auth_options[];
+extern struct stat devstat;
+
+#ifdef PPP_FILTER
+struct	bpf_program pass_filter;/* Filter program for packets to pass */
+struct	bpf_program active_filter; /* Filter program for link-active pkts */
+#endif
+
+char *current_option;		/* the name of the option being parsed */
+int  privileged_option;		/* set iff the current option came from root */
+char *option_source;		/* string saying where the option came from */
+int  option_priority = OPRIO_CFGFILE; /* priority of the current options */
+bool devnam_fixed;		/* can no longer change device name */
+
+static int logfile_fd = -1;	/* fd opened for log file */
+static char logfile_name[MAXPATHLEN];	/* name of log file */
+
+/*
+ * Prototypes
+ */
+static int setdomain __P((char **));
+static int readfile __P((char **));
+static int callfile __P((char **));
+static int showversion __P((char **));
+static int showhelp __P((char **));
+static void usage __P((void));
+static int setlogfile __P((char **));
+#ifdef PLUGIN
+static int loadplugin __P((char **));
+#endif
+
+#ifdef PPP_FILTER
+static int setpassfilter __P((char **));
+static int setactivefilter __P((char **));
+#endif
+
+#ifdef MAXOCTETS
+static int setmodir __P((char **));
+#endif
+
+static option_t *find_option __P((const char *name));
+static int process_option __P((option_t *, char *, char **));
+static int n_arguments __P((option_t *));
+static int number_option __P((char *, u_int32_t *, int));
+
+/*
+ * Structure to store extra lists of options.
+ */
+struct option_list {
+    option_t *options;
+    struct option_list *next;
+};
+
+static struct option_list *extra_options = NULL;
+
+/*
+ * Valid arguments.
+ */
+option_t general_options[] = {
+    { "metric", o_uint32, &metric,
+      "Set host route metric.", 1 },
+    { "drmetric", o_uint32, &drmetric,
+      "Set default route metric.", 1 },
+    { "pidfile", o_string, pid_file,
+      "File to write PPPD pid into.",
+      OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN },
+
+    { "debug", o_int, &debug,
+      "Increase debugging level", OPT_INC | OPT_NOARG | 1 },
+    { "-d", o_int, &debug,
+      "Increase debugging level",
+      OPT_ALIAS | OPT_INC | OPT_NOARG | 1 },
+
+    { "kdebug", o_int, &kdebugflag,
+      "Set kernel driver debug level", OPT_PRIO },
+
+    { "nodetach", o_bool, &nodetach,
+      "Don't detach from controlling tty", OPT_PRIO | 1 },
+    { "-detach", o_bool, &nodetach,
+      "Don't detach from controlling tty", OPT_ALIAS | OPT_PRIOSUB | 1 },
+    { "updetach", o_bool, &updetach,
+      "Detach from controlling tty once link is up",
+      OPT_PRIOSUB | OPT_A2CLR | 1, &nodetach },
+
+    { "holdoff", o_int, &holdoff,
+      "Set time in seconds before retrying connection",
+      OPT_PRIO, &holdoff_specified },
+
+    { "idle", o_int, &idle_time_limit,
+      "Set time in seconds before disconnecting idle link", OPT_PRIO },
+
+    { "maxconnect", o_int, &maxconnect,
+      "Set connection time limit",
+      OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF },
+
+    { "domain", o_special, (void *)setdomain,
+      "Add given domain name to hostname",
+      OPT_PRIO | OPT_PRIV | OPT_A2STRVAL, &domain },
+
+    { "file", o_special, (void *)readfile,
+      "Take options from a file", OPT_NOPRINT },
+    { "call", o_special, (void *)callfile,
+      "Take options from a privileged file", OPT_NOPRINT },
+
+    { "persist", o_bool, &persist,
+      "Keep on reopening connection after close", OPT_PRIO | 1 },
+    { "nopersist", o_bool, &persist,
+      "Turn off persist option", OPT_PRIOSUB },
+
+    { "demand", o_bool, &demand,
+      "Dial on demand", OPT_INITONLY | 1, &persist },
+
+    { "--version", o_special_noarg, (void *)showversion,
+      "Show version number" },
+    { "--help", o_special_noarg, (void *)showhelp,
+      "Show brief listing of options" },
+    { "-h", o_special_noarg, (void *)showhelp,
+      "Show brief listing of options", OPT_ALIAS },
+
+    { "logfile", o_special, (void *)setlogfile,
+      "Append log messages to this file",
+      OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, &logfile_name },
+    { "logfd", o_int, &log_to_fd,
+      "Send log messages to this file descriptor",
+      OPT_PRIOSUB | OPT_A2CLR, &log_default },
+    { "nolog", o_int, &log_to_fd,
+      "Don't send log messages to any file",
+      OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) },
+    { "nologfd", o_int, &log_to_fd,
+      "Don't send log messages to any file descriptor",
+      OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
+
+    { "linkname", o_string, linkname,
+      "Set logical name for link",
+      OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXPATHLEN },
+
+    { "maxfail", o_int, &maxfail,
+      "Maximum number of unsuccessful connection attempts to allow",
+      OPT_PRIO },
+
+    { "ktune", o_bool, &tune_kernel,
+      "Alter kernel settings as necessary", OPT_PRIO | 1 },
+    { "noktune", o_bool, &tune_kernel,
+      "Don't alter kernel settings", OPT_PRIOSUB },
+
+    { "connect-delay", o_int, &connect_delay,
+      "Maximum time (in ms) to wait after connect script finishes",
+      OPT_PRIO },
+
+    { "unit", o_int, &req_unit,
+      "PPP interface unit number to use if possible",
+      OPT_PRIO | OPT_LLIMIT, 0, 0 },
+
+    { "dump", o_bool, &dump_options,
+      "Print out option values after parsing all options", 1 },
+    { "dryrun", o_bool, &dryrun,
+      "Stop after parsing, printing, and checking options", 1 },
+
+    { "child-timeout", o_int, &child_wait,
+      "Number of seconds to wait for child processes at exit",
+      OPT_PRIO },
+
+#ifdef HAVE_MULTILINK
+    { "multilink", o_bool, &multilink,
+      "Enable multilink operation", OPT_PRIO | 1 },
+    { "mp", o_bool, &multilink,
+      "Enable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 1 },
+    { "nomultilink", o_bool, &multilink,
+      "Disable multilink operation", OPT_PRIOSUB | 0 },
+    { "nomp", o_bool, &multilink,
+      "Disable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 0 },
+
+    { "bundle", o_string, &bundle_name,
+      "Bundle name for multilink", OPT_PRIO },
+#endif /* HAVE_MULTILINK */
+
+#ifdef PLUGIN
+    { "plugin", o_special, (void *)loadplugin,
+      "Load a plug-in module into pppd", OPT_PRIV | OPT_A2LIST },
+#endif
+
+#ifdef PPP_FILTER
+    { "pass-filter", o_special, setpassfilter,
+      "set filter for packets to pass", OPT_PRIO },
+
+    { "active-filter", o_special, setactivefilter,
+      "set filter for active pkts", OPT_PRIO },
+#endif
+
+#ifdef MAXOCTETS
+    { "maxoctets", o_int, &maxoctets,
+      "Set connection traffic limit",
+      OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF },
+    { "mo", o_int, &maxoctets,
+      "Set connection traffic limit",
+      OPT_ALIAS | OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF },
+    { "mo-direction", o_special, setmodir,
+      "Set direction for limit traffic (sum,in,out,max)" },
+    { "mo-timeout", o_int, &maxoctets_timeout,
+      "Check for traffic limit every N seconds", OPT_PRIO | OPT_LLIMIT | 1 },
+#endif
+
+    { NULL }
+};
+
+#ifndef IMPLEMENTATION
+#define IMPLEMENTATION ""
+#endif
+
+static char *usage_string = "\
+pppd version %s\n\
+Usage: %s [ options ], where options are:\n\
+	<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;
+    uid_t euid;
+    char *argv[MAXARGS];
+    char args[MAXARGS][MAXWORDLEN];
+    char cmd[MAXWORDLEN];
+
+    euid = geteuid();
+    if (check_prot && seteuid(getuid()) == -1) {
+	option_error("unable to drop privileges to open %s: %m", filename);
+	return 0;
+    }
+    f = fopen(filename, "r");
+    err = errno;
+    if (check_prot && seteuid(euid) == -1)
+	fatal("unable to regain privileges");
+    if (f == NULL) {
+	errno = err;
+	if (!must_exist) {
+	    if (err != ENOENT && err != ENOTDIR)
+		warn("Warning: can't open options file %s: %m", filename);
+	    return 1;
+	}
+	option_error("Can't open options file %s: %m", filename);
+	return 0;
+    }
+
+    oldpriv = privileged_option;
+    privileged_option = priv;
+    oldsource = option_source;
+    option_source = strdup(filename);
+    if (option_source == NULL)
+	option_source = "file";
+    ret = 0;
+    while (getword(f, cmd, &newline, filename)) {
+	opt = find_option(cmd);
+	if (opt == NULL) {
+	    option_error("In file %s: unrecognized option '%s'",
+			 filename, cmd);
+	    goto err;
+	}
+	n = n_arguments(opt);
+	for (i = 0; i < n; ++i) {
+	    if (!getword(f, args[i], &newline, filename)) {
+		option_error(
+			"In file %s: too few parameters for option '%s'",
+			filename, cmd);
+		goto err;
+	    }
+	    argv[i] = args[i];
+	}
+	if (!process_option(opt, cmd, argv))
+	    goto err;
+    }
+    ret = 1;
+
+err:
+    fclose(f);
+    privileged_option = oldpriv;
+    option_source = oldsource;
+    return ret;
+}
+
+/*
+ * options_from_user - See if the use has a ~/.ppprc file,
+ * and if so, interpret options from it.
+ */
+int
+options_from_user()
+{
+    char *user, *path, *file;
+    int ret;
+    struct passwd *pw;
+    size_t pl;
+
+    pw = getpwuid(getuid());
+    if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0)
+	return 1;
+    file = _PATH_USEROPT;
+    pl = strlen(user) + strlen(file) + 2;
+    path = malloc(pl);
+    if (path == NULL)
+	novm("init file name");
+    slprintf(path, pl, "%s/%s", user, file);
+    option_priority = OPRIO_CFGFILE;
+    ret = options_from_file(path, 0, 1, privileged);
+    free(path);
+    return ret;
+}
+
+/*
+ * options_for_tty - See if an options file exists for the serial
+ * device, and if so, interpret options from it.
+ * We only allow the per-tty options file to override anything from
+ * the command line if it is something that the user can't override
+ * once it has been set by root; this is done by giving configuration
+ * files a lower priority than the command line.
+ */
+int
+options_for_tty()
+{
+    char *dev, *path, *p;
+    int ret;
+    size_t pl;
+
+    dev = devnam;
+    if ((p = strstr(dev, "/dev/")) != NULL)
+	dev = p + 5;
+    if (dev[0] == 0 || strcmp(dev, "tty") == 0)
+	return 1;		/* don't look for /etc/ppp/options.tty */
+    pl = strlen(PATH_TTYOPT) + strlen(dev) + 1;
+    path = malloc(pl);
+    if (path == NULL)
+	novm("tty init file name");
+    slprintf(path, pl, "%s%s", PATH_TTYOPT, dev);
+    /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
+    for (p = path + strlen(PATH_TTYOPT); *p != 0; ++p)
+	if (*p == '/')
+	    *p = '.';
+    option_priority = OPRIO_CFGFILE;
+    ret = options_from_file(path, 0, 0, 1);
+    free(path);
+    return ret;
+}
+
+/*
+ * options_from_list - process a string of options in a wordlist.
+ */
+int
+options_from_list(w, priv)
+    struct wordlist *w;
+    int priv;
+{
+    char *argv[MAXARGS];
+    option_t *opt;
+    int i, n, ret = 0;
+    struct wordlist *w0;
+
+    privileged_option = priv;
+    option_source = "secrets file";
+    option_priority = OPRIO_SECFILE;
+
+    while (w != NULL) {
+	opt = find_option(w->word);
+	if (opt == NULL) {
+	    option_error("In secrets file: unrecognized option '%s'",
+			 w->word);
+	    goto err;
+	}
+	n = n_arguments(opt);
+	w0 = w;
+	for (i = 0; i < n; ++i) {
+	    w = w->next;
+	    if (w == NULL) {
+		option_error(
+			"In secrets file: too few parameters for option '%s'",
+			w0->word);
+		goto err;
+	    }
+	    argv[i] = w->word;
+	}
+	if (!process_option(opt, w0->word, argv))
+	    goto err;
+	w = w->next;
+    }
+    ret = 1;
+
+err:
+    return ret;
+}
+
+/*
+ * match_option - see if this option matches an option_t structure.
+ */
+static int
+match_option(name, opt, dowild)
+    char *name;
+    option_t *opt;
+    int dowild;
+{
+	int (*match) __P((char *, char **, int));
+
+	if (dowild != (opt->type == o_wild))
+		return 0;
+	if (!dowild)
+		return strcmp(name, opt->name) == 0;
+	match = (int (*) __P((char *, char **, int))) opt->addr;
+	return (*match)(name, NULL, 0);
+}
+
+/*
+ * find_option - scan the option lists for the various protocols
+ * looking for an entry with the given name.
+ * This could be optimized by using a hash table.
+ */
+static option_t *
+find_option(name)
+    const char *name;
+{
+	option_t *opt;
+	struct option_list *list;
+	int i, dowild;
+
+	for (dowild = 0; dowild <= 1; ++dowild) {
+		for (opt = general_options; opt->name != NULL; ++opt)
+			if (match_option(name, opt, dowild))
+				return opt;
+		for (opt = auth_options; opt->name != NULL; ++opt)
+			if (match_option(name, opt, dowild))
+				return opt;
+		for (list = extra_options; list != NULL; list = list->next)
+			for (opt = list->options; opt->name != NULL; ++opt)
+				if (match_option(name, opt, dowild))
+					return opt;
+		for (opt = the_channel->options; opt->name != NULL; ++opt)
+			if (match_option(name, opt, dowild))
+				return opt;
+		for (i = 0; protocols[i] != NULL; ++i)
+			if ((opt = protocols[i]->options) != NULL)
+				for (; opt->name != NULL; ++opt)
+					if (match_option(name, opt, dowild))
+						return opt;
+	}
+	return NULL;
+}
+
+/*
+ * process_option - process one new-style option.
+ */
+static int
+process_option(opt, cmd, argv)
+    option_t *opt;
+    char *cmd;
+    char **argv;
+{
+    u_int32_t v;
+    int iv, a;
+    char *sv;
+    int (*parser) __P((char **));
+    int (*wildp) __P((char *, char **, int));
+    char *optopt = (opt->type == o_wild)? "": " option";
+    int prio = option_priority;
+    option_t *mainopt = opt;
+
+    current_option = opt->name;
+    if ((opt->flags & OPT_PRIVFIX) && privileged_option)
+	prio += OPRIO_ROOT;
+    while (mainopt->flags & OPT_PRIOSUB)
+	--mainopt;
+    if (mainopt->flags & OPT_PRIO) {
+	if (prio < mainopt->priority) {
+	    /* new value doesn't override old */
+	    if (prio == OPRIO_CMDLINE && mainopt->priority > OPRIO_ROOT) {
+		option_error("%s%s set in %s cannot be overridden\n",
+			     opt->name, optopt, mainopt->source);
+		return 0;
+	    }
+	    return 1;
+	}
+	if (prio > OPRIO_ROOT && mainopt->priority == OPRIO_CMDLINE)
+	    warn("%s%s from %s overrides command line",
+		 opt->name, optopt, option_source);
+    }
+
+    if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) {
+	option_error("%s%s cannot be changed after initialization",
+		     opt->name, optopt);
+	return 0;
+    }
+    if ((opt->flags & OPT_PRIV) && !privileged_option) {
+	option_error("using the %s%s requires root privilege",
+		     opt->name, optopt);
+	return 0;
+    }
+    if ((opt->flags & OPT_ENABLE) && *(bool *)(opt->addr2) == 0) {
+	option_error("%s%s is disabled", opt->name, optopt);
+	return 0;
+    }
+    if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) {
+	option_error("the %s%s may not be changed in %s",
+		     opt->name, optopt, option_source);
+	return 0;
+    }
+
+    switch (opt->type) {
+    case o_bool:
+	v = opt->flags & OPT_VALUE;
+	*(bool *)(opt->addr) = v;
+	if (opt->addr2 && (opt->flags & OPT_A2COPY))
+	    *(bool *)(opt->addr2) = v;
+	else if (opt->addr2 && (opt->flags & OPT_A2CLR))
+	    *(bool *)(opt->addr2) = 0;
+	else if (opt->addr2 && (opt->flags & OPT_A2CLRB))
+	    *(u_char *)(opt->addr2) &= ~v;
+	else if (opt->addr2 && (opt->flags & OPT_A2OR))
+	    *(u_char *)(opt->addr2) |= v;
+	break;
+
+    case o_int:
+	iv = 0;
+	if ((opt->flags & OPT_NOARG) == 0) {
+	    if (!int_option(*argv, &iv))
+		return 0;
+	    if ((((opt->flags & OPT_LLIMIT) && iv < opt->lower_limit)
+		 || ((opt->flags & OPT_ULIMIT) && iv > opt->upper_limit))
+		&& !((opt->flags & OPT_ZEROOK && iv == 0))) {
+		char *zok = (opt->flags & OPT_ZEROOK)? " zero or": "";
+		switch (opt->flags & OPT_LIMITS) {
+		case OPT_LLIMIT:
+		    option_error("%s value must be%s >= %d",
+				 opt->name, zok, opt->lower_limit);
+		    break;
+		case OPT_ULIMIT:
+		    option_error("%s value must be%s <= %d",
+				 opt->name, zok, opt->upper_limit);
+		    break;
+		case OPT_LIMITS:
+		    option_error("%s value must be%s between %d and %d",
+				opt->name, zok, opt->lower_limit, opt->upper_limit);
+		    break;
+		}
+		return 0;
+	    }
+	}
+	a = opt->flags & OPT_VALUE;
+	if (a >= 128)
+	    a -= 256;		/* sign extend */
+	iv += a;
+	if (opt->flags & OPT_INC)
+	    iv += *(int *)(opt->addr);
+	if ((opt->flags & OPT_NOINCR) && !privileged_option) {
+	    int oldv = *(int *)(opt->addr);
+	    if ((opt->flags & OPT_ZEROINF) ?
+		(oldv != 0 && (iv == 0 || iv > oldv)) : (iv > oldv)) {
+		option_error("%s value cannot be increased", opt->name);
+		return 0;
+	    }
+	}
+	*(int *)(opt->addr) = iv;
+	if (opt->addr2 && (opt->flags & OPT_A2COPY))
+	    *(int *)(opt->addr2) = iv;
+	break;
+
+    case o_uint32:
+	if (opt->flags & OPT_NOARG) {
+	    v = opt->flags & OPT_VALUE;
+	    if (v & 0x80)
+		    v |= 0xffffff00U;
+	} else if (!number_option(*argv, &v, 16))
+	    return 0;
+	if (opt->flags & OPT_OR)
+	    v |= *(u_int32_t *)(opt->addr);
+	*(u_int32_t *)(opt->addr) = v;
+	if (opt->addr2 && (opt->flags & OPT_A2COPY))
+	    *(u_int32_t *)(opt->addr2) = v;
+	break;
+
+    case o_string:
+	if (opt->flags & OPT_STATIC) {
+	    strlcpy((char *)(opt->addr), *argv, opt->upper_limit);
+	} else {
+	    sv = strdup(*argv);
+	    if (sv == NULL)
+		novm("option argument");
+	    *(char **)(opt->addr) = sv;
+	}
+	break;
+
+    case o_special_noarg:
+    case o_special:
+	parser = (int (*) __P((char **))) opt->addr;
+	if (!(*parser)(argv))
+	    return 0;
+	if (opt->flags & OPT_A2LIST) {
+	    struct option_value *ovp, *pp;
+
+	    ovp = malloc(sizeof(*ovp) + strlen(*argv));
+	    if (ovp != 0) {
+		strcpy(ovp->value, *argv);
+		ovp->source = option_source;
+		ovp->next = NULL;
+		if (opt->addr2 == NULL) {
+		    opt->addr2 = ovp;
+		} else {
+		    for (pp = opt->addr2; pp->next != NULL; pp = pp->next)
+			;
+		    pp->next = ovp;
+		}
+	    }
+	}
+	break;
+
+    case o_wild:
+	wildp = (int (*) __P((char *, char **, int))) opt->addr;
+	if (!(*wildp)(cmd, argv, 1))
+	    return 0;
+	break;
+    }
+
+    /*
+     * If addr2 wasn't used by any flag (OPT_A2COPY, etc.) but is set,
+     * treat it as a bool and set/clear it based on the OPT_A2CLR bit.
+     */
+    if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE
+		|OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST|OPT_A2OR)) == 0)
+	*(bool *)(opt->addr2) = !(opt->flags & OPT_A2CLR);
+
+    mainopt->source = option_source;
+    mainopt->priority = prio;
+    mainopt->winner = opt - mainopt;
+
+    return 1;
+}
+
+/*
+ * override_value - if the option priorities would permit us to
+ * override the value of option, return 1 and update the priority
+ * and source of the option value.  Otherwise returns 0.
+ */
+int
+override_value(option, priority, source)
+    const char *option;
+    int priority;
+    const char *source;
+{
+	option_t *opt;
+
+	opt = find_option(option);
+	if (opt == NULL)
+		return 0;
+	while (opt->flags & OPT_PRIOSUB)
+		--opt;
+	if ((opt->flags & OPT_PRIO) && priority < opt->priority)
+		return 0;
+	opt->priority = priority;
+	opt->source = source;
+	opt->winner = -1;
+	return 1;
+}
+
+/*
+ * n_arguments - tell how many arguments an option takes
+ */
+static int
+n_arguments(opt)
+    option_t *opt;
+{
+	return (opt->type == o_bool || opt->type == o_special_noarg
+		|| (opt->flags & OPT_NOARG))? 0: 1;
+}
+
+/*
+ * add_options - add a list of options to the set we grok.
+ */
+void
+add_options(opt)
+    option_t *opt;
+{
+    struct option_list *list;
+
+    list = malloc(sizeof(*list));
+    if (list == 0)
+	novm("option list entry");
+    list->options = opt;
+    list->next = extra_options;
+    extra_options = list;
+}
+
+/*
+ * check_options - check that options are valid and consistent.
+ */
+void
+check_options()
+{
+	if (logfile_fd >= 0 && logfile_fd != log_to_fd)
+		close(logfile_fd);
+}
+
+/*
+ * print_option - print out an option and its value
+ */
+static void
+print_option(opt, mainopt, printer, arg)
+    option_t *opt, *mainopt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	int i, v;
+	char *p;
+
+	if (opt->flags & OPT_NOPRINT)
+		return;
+	switch (opt->type) {
+	case o_bool:
+		v = opt->flags & OPT_VALUE;
+		if (*(bool *)opt->addr != v)
+			/* this can happen legitimately, e.g. lock
+			   option turned off for default device */
+			break;
+		printer(arg, "%s", opt->name);
+		break;
+	case o_int:
+		v = opt->flags & OPT_VALUE;
+		if (v >= 128)
+			v -= 256;
+		i = *(int *)opt->addr;
+		if (opt->flags & OPT_NOARG) {
+			printer(arg, "%s", opt->name);
+			if (i != v) {
+				if (opt->flags & OPT_INC) {
+					for (; i > v; i -= v)
+						printer(arg, " %s", opt->name);
+				} else
+					printer(arg, " # oops: %d not %d\n",
+						i, v);
+			}
+		} else {
+			printer(arg, "%s %d", opt->name, i);
+		}
+		break;
+	case o_uint32:
+		printer(arg, "%s", opt->name);
+		if ((opt->flags & OPT_NOARG) == 0)
+			printer(arg, " %x", *(u_int32_t *)opt->addr);
+		break;
+
+	case o_string:
+		if (opt->flags & OPT_HIDE) {
+			p = "??????";
+		} else {
+			p = (char *) opt->addr;
+			if ((opt->flags & OPT_STATIC) == 0)
+				p = *(char **)p;
+		}
+		printer(arg, "%s %q", opt->name, p);
+		break;
+
+	case o_special:
+	case o_special_noarg:
+	case o_wild:
+		if (opt->type != o_wild) {
+			printer(arg, "%s", opt->name);
+			if (n_arguments(opt) == 0)
+				break;
+			printer(arg, " ");
+		}
+		if (opt->flags & OPT_A2PRINTER) {
+			void (*oprt) __P((option_t *,
+					  void ((*)__P((void *, char *, ...))),
+					  void *));
+			oprt = (void (*) __P((option_t *,
+					 void ((*)__P((void *, char *, ...))),
+					 void *)))opt->addr2;
+			(*oprt)(opt, printer, arg);
+		} else if (opt->flags & OPT_A2STRVAL) {
+			p = (char *) opt->addr2;
+			if ((opt->flags & OPT_STATIC) == 0)
+				p = *(char **)p;
+			printer("%q", p);
+		} else if (opt->flags & OPT_A2LIST) {
+			struct option_value *ovp;
+
+			ovp = (struct option_value *) opt->addr2;
+			for (;;) {
+				printer(arg, "%q", ovp->value);
+				if ((ovp = ovp->next) == NULL)
+					break;
+				printer(arg, "\t\t# (from %s)\n%s ",
+					ovp->source, opt->name);
+			}
+		} else {
+			printer(arg, "xxx # [don't know how to print value]");
+		}
+		break;
+
+	default:
+		printer(arg, "# %s value (type %d\?\?)", opt->name, opt->type);
+		break;
+	}
+	printer(arg, "\t\t# (from %s)\n", mainopt->source);
+}
+
+/*
+ * print_option_list - print out options in effect from an
+ * array of options.
+ */
+static void
+print_option_list(opt, printer, arg)
+    option_t *opt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	while (opt->name != NULL) {
+		if (opt->priority != OPRIO_DEFAULT
+		    && opt->winner != (short int) -1)
+			print_option(opt + opt->winner, opt, printer, arg);
+		do {
+			++opt;
+		} while (opt->flags & OPT_PRIOSUB);
+	}
+}
+
+/*
+ * print_options - print out what options are in effect.
+ */
+void
+print_options(printer, arg)
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	struct option_list *list;
+	int i;
+
+	printer(arg, "pppd options in effect:\n");
+	print_option_list(general_options, printer, arg);
+	print_option_list(auth_options, printer, arg);
+	for (list = extra_options; list != NULL; list = list->next)
+		print_option_list(list->options, printer, arg);
+	print_option_list(the_channel->options, printer, arg);
+	for (i = 0; protocols[i] != NULL; ++i)
+		print_option_list(protocols[i]->options, printer, arg);
+}
+
+/*
+ * usage - print out a message telling how to use the program.
+ */
+static void
+usage()
+{
+    if (phase == PHASE_INITIALIZE)
+	fprintf(stderr, usage_string, VERSION, progname);
+}
+
+/*
+ * showhelp - print out usage message and exit.
+ */
+static int
+showhelp(argv)
+    char **argv;
+{
+    if (phase == PHASE_INITIALIZE) {
+	usage();
+	exit(0);
+    }
+    return 0;
+}
+
+/*
+ * showversion - print out the version number and exit.
+ */
+static int
+showversion(argv)
+    char **argv;
+{
+    if (phase == PHASE_INITIALIZE) {
+	fprintf(stderr, "pppd version %s\n", VERSION);
+	exit(0);
+    }
+    return 0;
+}
+
+/*
+ * option_error - print a message about an error in an option.
+ * The message is logged, and also sent to
+ * stderr if phase == PHASE_INITIALIZE.
+ */
+void
+option_error __V((char *fmt, ...))
+{
+    va_list args;
+    char buf[1024];
+
+#if defined(__STDC__)
+    va_start(args, fmt);
+#else
+    char *fmt;
+    va_start(args);
+    fmt = va_arg(args, char *);
+#endif
+    vslprintf(buf, sizeof(buf), fmt, args);
+    va_end(args);
+    if (phase == PHASE_INITIALIZE)
+	fprintf(stderr, "%s: %s\n", progname, buf);
+    syslog(LOG_ERR, "%s", buf);
+}
+
+#if 0
+/*
+ * readable - check if a file is readable by the real user.
+ */
+int
+readable(fd)
+    int fd;
+{
+    uid_t uid;
+    int i;
+    struct stat sbuf;
+
+    uid = getuid();
+    if (uid == 0)
+	return 1;
+    if (fstat(fd, &sbuf) != 0)
+	return 0;
+    if (sbuf.st_uid == uid)
+	return sbuf.st_mode & S_IRUSR;
+    if (sbuf.st_gid == getgid())
+	return sbuf.st_mode & S_IRGRP;
+    for (i = 0; i < ngroups; ++i)
+	if (sbuf.st_gid == groups[i])
+	    return sbuf.st_mode & S_IRGRP;
+    return sbuf.st_mode & S_IROTH;
+}
+#endif
+
+/*
+ * Read a word from a file.
+ * Words are delimited by white-space or by quotes (" or ').
+ * Quotes, white-space and \ may be escaped with \.
+ * \<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.
+	     */
+	     //hubÖÎÀí£ºCVE-2014-3158
+	    if (len < MAXWORDLEN){
+			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.
+	 */
+	 //hubÖÎÀí£ºCVE-2014-3158
+	if (len < MAXWORDLEN){
+		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;
+{
+    pcap_t *pc;
+    int ret = 1;
+
+    pc = pcap_open_dead(DLT_PPP_PPPD, 65535);
+    if (pcap_compile(pc, &pass_filter, *argv, 1, netmask) == -1) {
+	option_error("error in pass-filter expression: %s\n",
+		     pcap_geterr(pc));
+	ret = 0;
+    }
+    pcap_close(pc);
+
+    return ret;
+}
+
+/*
+ * setactivefilter - Set the active filter for packets
+ */
+static int
+setactivefilter(argv)
+    char **argv;
+{
+    pcap_t *pc;
+    int ret = 1;
+
+    pc = pcap_open_dead(DLT_PPP_PPPD, 65535);
+    if (pcap_compile(pc, &active_filter, *argv, 1, netmask) == -1) {
+	option_error("error in active-filter expression: %s\n",
+		     pcap_geterr(pc));
+	ret = 0;
+    }
+    pcap_close(pc);
+
+    return ret;
+}
+#endif
+
+/*
+ * setdomain - Set domain name to append to hostname 
+ */
+static int
+setdomain(argv)
+    char **argv;
+{
+    gethostname(hostname, MAXNAMELEN);
+    if (**argv != 0) {
+	if (**argv != '.')
+	    strncat(hostname, ".", MAXNAMELEN - strlen(hostname));
+	domain = hostname + strlen(hostname);
+	strncat(hostname, *argv, MAXNAMELEN - strlen(hostname));
+    }
+    hostname[MAXNAMELEN-1] = 0;
+    return (1);
+}
+
+static int
+setlogfile(argv)
+    char **argv;
+{
+    int fd, err;
+    uid_t euid;
+
+    euid = geteuid();
+    if (!privileged_option && seteuid(getuid()) == -1) {
+	option_error("unable to drop permissions to open %s: %m", *argv);
+	return 0;
+    }
+    fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644);
+    if (fd < 0 && errno == EEXIST)
+	fd = open(*argv, O_WRONLY | O_APPEND);
+    err = errno;
+    if (!privileged_option && seteuid(euid) == -1)
+	fatal("unable to regain privileges: %m");
+    if (fd < 0) {
+	errno = err;
+	option_error("Can't open log file %s: %m", *argv);
+	return 0;
+    }
+    strlcpy(logfile_name, *argv, sizeof(logfile_name));
+    if (logfile_fd >= 0)
+	close(logfile_fd);
+    logfile_fd = fd;
+    log_to_fd = fd;
+    log_default = 0;
+    return 1;
+}
+
+#ifdef MAXOCTETS
+static int
+setmodir(argv)
+    char **argv;
+{
+    if(*argv == NULL)
+	return 0;
+    if(!strcmp(*argv,"in")) {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_IN;
+    } else if (!strcmp(*argv,"out")) {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_OUT;
+    } else if (!strcmp(*argv,"max")) {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_MAXOVERAL;
+    } else {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_SUM;
+    }
+    return 1;
+}
+#endif
+
+#ifdef PLUGIN
+
+#ifdef PLUGIN_TACACS
+extern void tacacs_plugin_init __P((void));
+#endif
+#ifdef PLUGIN_RADIUS
+extern void radius_plugin_init __P((void));
+#endif
+#ifdef PLUGIN_PPPOE
+extern void pppoe_plugin_init __P((void));
+#endif
+#ifdef PLUGIN_PPPOA
+extern void pppoa_plugin_init __P((void));
+#endif
+#ifdef PLUGIN_PPPOL2TP
+extern void pppol2tp_plugin_init __P((void));
+extern void openl2tp_plugin_init __P((void));
+#endif
+#ifdef PLUGIN_PPTP
+extern void pptp_plugin_init __P((void));
+#endif
+#ifdef PLUGIN_MINCONN
+extern void minconn_plugin_init __P((void));
+#endif
+#ifdef PLUGIN_PASSPROMPT
+extern void passprompt_plugin_init __P((void));
+#endif
+#ifdef PLUGIN_PASSWORDFD
+extern void passwordfd_plugin_init __P((void));
+#endif
+ 
+static int
+loadplugin(argv)
+    char **argv;
+{
+#if 0
+#ifdef PLUGIN_DYNAMIC
+    char *arg = *argv;
+    void *handle;
+    const char *err;
+    void (*init) __P((void));
+    char *path = arg;
+    const char *vers;
+
+    if (strchr(arg, '/') == 0) {
+	const char *base = _PATH_PLUGIN;
+	int l = strlen(base) + strlen(arg) + 2;
+	path = malloc(l);
+	if (path == 0)
+	    novm("plugin file path");
+	strlcpy(path, base, l);
+	strlcat(path, "/", l);
+	strlcat(path, arg, l);
+    }
+    handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
+    if (handle == 0) {
+	err = dlerror();
+	if (err != 0)
+	    option_error("%s", err);
+	option_error("Couldn't load plugin %s", arg);
+	goto err;
+    }
+    init = (void (*)(void))dlsym(handle, "plugin_init");
+    if (init == 0) {
+	option_error("%s has no initialization entry point", arg);
+	goto errclose;
+    }
+    vers = (const char *) dlsym(handle, "pppd_version");
+    if (vers == 0) {
+	warn("Warning: plugin %s has no version information", arg);
+    } else if (strcmp(vers, VERSION) != 0) {
+	option_error("Plugin %s is for pppd version %s, this is %s",
+		     arg, vers, VERSION);
+	goto errclose;
+    }
+    warn("Plugin %s loaded.", arg);
+    (*init)();
+    return 1;
+
+ errclose:
+    dlclose(handle);
+ err:
+    if (path != arg)
+	free(path);
+    return 0;
+#else
+    char *arg = *argv;
+    void (*init) __P((void));
+    char *name;
+
+    /* Just get the basename of the library since we don't care
+     * about the path when statically linked. */
+    name = strrchr(arg, '/');
+    if (name != 0) {
+        arg = name+1;
+    }
+    if (strlen(arg) > 3 && strcmp(arg+strlen(arg)-3, ".so")==0) {
+        arg[strlen(arg)-3] = '\0';
+    }
+
+#ifdef PLUGIN_TACACS
+    if (strcmp(arg, "tacacs") == 0) {
+        init = tacacs_plugin_init;
+    } else
+#endif
+#ifdef PLUGIN_RADIUS
+    if (strcmp(arg, "radius") == 0) {
+        init = radius_plugin_init;
+    } else
+#endif
+#ifdef PLUGIN_PPPOE
+    if(strcmp(arg, "rp-pppoe") == 0 || strcmp(arg, "pppoe") == 0) {
+        init = pppoe_plugin_init;
+    } else
+#endif
+#ifdef PLUGIN_PPPOA
+    if(strcmp(arg, "pppoa") == 0) {
+        init = pppoa_plugin_init;
+    } else
+#endif
+#ifdef PLUGIN_PPPOL2TP
+    if(strcmp(arg, "pppol2tp") == 0) {
+        init = pppol2tp_plugin_init;
+    } else
+    if(strcmp(arg, "openl2tp") == 0) {
+        init = openl2tp_plugin_init;
+    } else
+#endif
+#ifdef PLUGIN_PPTP
+    if(strcmp(arg, "pptp") == 0) {
+        init = pptp_plugin_init;
+    } else
+#endif
+#ifdef PLUGIN_MINCONN
+    if(strcmp(arg, "minconn") == 0) {
+        init = minconn_plugin_init;
+    } else
+#endif
+#ifdef PLUGIN_PASSPROMPT
+    if(strcmp(arg, "passprompt") == 0) {
+        init = passprompt_plugin_init;
+    } else
+#endif
+#ifdef PLUGIN_PASSWORDFD
+    if(strcmp(arg, "passwordfd") == 0) {
+        init = passwordfd_plugin_init;
+    } else
+#endif
+#ifdef PLUGIN_WINBIND
+    if(strcmp(arg, "winbind") == 0) {
+        init = winbind_plugin_init;
+    } else
+#endif
+    {
+        option_error("Couldn't load plugin %s", arg);
+        return 0;
+    }
+
+    warn("Plugin %s loaded.", arg);
+    (*init)();
+    return 1;
+#endif /* PLUGIN_DYNAMIC */
+#endif
+}
+#endif /* PLUGIN */
diff --git a/ap/app/pppd/pppd/patchlevel.h b/ap/app/pppd/pppd/patchlevel.h
new file mode 100644
index 0000000..b7d6ce3
--- /dev/null
+++ b/ap/app/pppd/pppd/patchlevel.h
@@ -0,0 +1,2 @@
+#define VERSION		"2.4.5"
+#define DATE		"17 November 2009"
diff --git a/ap/app/pppd/pppd/pathnames.h b/ap/app/pppd/pppd/pathnames.h
new file mode 100644
index 0000000..f1bb0ce
--- /dev/null
+++ b/ap/app/pppd/pppd/pathnames.h
@@ -0,0 +1,84 @@
+/*
+ * define path names
+ *
+ * $Id: pathnames.h,v 1.17 2008-10-23 01:21:53 asallawa Exp $
+ */
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+
+#else /* HAVE_PATHS_H */
+#ifndef _PATH_VARRUN
+#define _PATH_VARRUN 	"/etc_rw/"
+#endif
+#define _PATH_DEVNULL	"/dev/null"
+#endif /* HAVE_PATHS_H */
+
+#ifndef _ROOT_PATH
+#define _ROOT_PATH
+#endif
+
+#ifndef PATH_CONFIG
+#define	PATH_CONFIG	"/etc_rw"
+#endif
+#ifndef PATH_AUTH
+#define	PATH_AUTH	"/etc_rw"
+#endif
+#ifndef PATH_LOG
+#define	PATH_LOG	"/etc_rw"
+#endif
+#ifndef PATH_RESOLV
+#define	PATH_RESOLV	"/etc_rw"
+#endif
+
+//#define _PATH_UPAPFILE 	 _ROOT_PATH PATH_CONFIG "/pap-secrets"
+//#define _PATH_CHAPFILE 	 _ROOT_PATH PATH_CONFIG "/chap-secrets"
+#define _PATH_SRPFILE 	 _ROOT_PATH PATH_CONFIG "/srp-secrets"
+//#define _PATH_SYSOPTIONS _ROOT_PATH PATH_CONFIG "/options"
+#define _PATH_IPUP	 _ROOT_PATH PATH_CONFIG "/ip-up"
+#define _PATH_IPDOWN	 _ROOT_PATH PATH_CONFIG "/ip-down"
+#define _PATH_IPPREUP	 _ROOT_PATH PATH_CONFIG "/ip-pre-up"
+#define _PATH_AUTHUP	 _ROOT_PATH PATH_AUTH   "/auth-up"
+#define _PATH_AUTHDOWN	 _ROOT_PATH PATH_AUTH   "/auth-down"
+//#define _PATH_TTYOPT	 _ROOT_PATH PATH_CONFIG "/options."
+#define _PATH_CONNERRS	 _ROOT_PATH PATH_LOG    "/connect-errors"
+#define _PATH_PEERFILES	 _ROOT_PATH PATH_CONFIG "/peers/"
+#define _PATH_RESOLV	 _ROOT_PATH PATH_RESOLV "/%s.resolv"
+
+#ifdef PATH_ETC_CONFIG
+#define _PATH_DEFAULT_IPUP	"/etc_rw/default/ip-up"
+#define _PATH_DEFAULT_IPDOWN	"/etc_rw/default/ip-down"
+#endif
+
+#define _PATH_USEROPT	 ".ppprc"
+#define	_PATH_PSEUDONYM	 ".ppp_pseudonym"
+
+#ifdef INET6
+#define _PATH_IPV6UP     _ROOT_PATH PATH_CONFIG "/ipv6-up"
+#define _PATH_IPV6DOWN   _ROOT_PATH PATH_CONFIG "/ipv6-down"
+#endif
+
+#ifdef IPX_CHANGE
+#define _PATH_IPXUP	 _ROOT_PATH "/etc_rw/ppp/ipx-up"
+#define _PATH_IPXDOWN	 _ROOT_PATH "/etc_rw/ppp/ipx-down"
+#endif /* IPX_CHANGE */
+
+#ifdef __STDC__
+//#define _PATH_PPPDB	_ROOT_PATH _PATH_VARRUN "pppd2.tdb"
+#define _PATH_PPPDB	"/etc_rw/pppd2.tdb"
+#else /* __STDC__ */
+#ifdef HAVE_PATHS_H
+#define _PATH_PPPDB	"/var/run/pppd2.tdb"
+#else
+#define _PATH_PPPDB	"/etc_rw/ppp/pppd2.tdb"
+#endif
+#endif /* __STDC__ */
+
+#ifdef PLUGIN
+#ifdef __STDC__
+#define _PATH_PLUGIN	DESTDIR "/lib/pppd/" VERSION
+#else /* __STDC__ */
+#define _PATH_PLUGIN	"/usr/lib/pppd"
+#endif /* __STDC__ */
+
+#endif /* PLUGIN */
diff --git a/ap/app/pppd/pppd/plugins/Makefile.linux b/ap/app/pppd/pppd/plugins/Makefile.linux
new file mode 100644
index 0000000..c54700e
--- /dev/null
+++ b/ap/app/pppd/pppd/plugins/Makefile.linux
@@ -0,0 +1,79 @@
+#CC	= gcc
+#COPTS	= -O2 -g
+CFLAGS	= $(COPTS) -I.. -I../../include -Itacc/include
+INSTALL	= install
+
+ifeq ($(CONFIG_USER_PPPD_WITH_DYNAMIC_PLUGINS),y)
+CFLAGS  += -fPIC -DDYNAMIC_PLUGINS=1
+LDFLAGS = -shared
+SO = so
+else
+SO = o
+endif
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+
+SUBDIRS := 
+ifeq ($(CONFIG_USER_PPPD_WITH_PPPOE),y)
+SUBDIRS += rp-pppoe
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PPPOA),y)
+SUBDIRS += pppoatm
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PPPOL2TP),y)
+SUBDIRS += pppol2tp
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_RADIUS),y)
+SUBDIRS += radius
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PPTP),y)
+SUBDIRS += pptp
+endif
+
+PLUGINS := 
+ifeq ($(CONFIG_USER_PPPD_WITH_MINCONN),y)
+PLUGINS += minconn.$(SO)
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PASSPROMPT),y)
+PLUGINS += passprompt.$(SO)
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_PASSWORDFD),y)
+PLUGINS += passwordfd.$(SO)
+endif
+ifeq ($(CONFIG_USER_PPPD_WITH_WINBIND),y)
+PLUGINS += winbind.$(SO)
+endif
+
+ifeq ($(CONFIG_USER_PPPD_WITH_TACACS),y)
+PLUGINS += tacacs.$(SO)
+SUBDIRS += tacc/lib
+endif
+
+# include dependencies if present
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+all:	$(PLUGINS)
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d all || exit 1; done
+
+%.so: %.c
+	$(CC) -o $@ $(LDFLAGS) $(CFLAGS) $^
+
+VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../patchlevel.h)
+
+install: $(PLUGINS)
+	$(INSTALL) -d $(LIBDIR)
+	$(INSTALL) $? $(LIBDIR)
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d install; done
+
+clean:
+	rm -f *.o *.so *.a
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d clean; done
+
+depend:
+	$(CPP) -M $(CFLAGS) *.c >.depend
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d depend; done
diff --git a/ap/app/pppd/pppd/plugins/Makefile.sol2 b/ap/app/pppd/pppd/plugins/Makefile.sol2
new file mode 100644
index 0000000..bc7d85d
--- /dev/null
+++ b/ap/app/pppd/pppd/plugins/Makefile.sol2
@@ -0,0 +1,27 @@
+#
+# Makefile for plugins on Solaris 2
+#
+# $Id: Makefile.sol2,v 1.3 2002/09/07 05:15:25 carlsonj Exp $
+#
+
+include ../../Makedefs.com
+
+CFLAGS	= -c -O -I.. -I../../include $(COPTS)
+LDFLAGS	= -G 
+
+all:	minconn.so
+
+minconn.so: minconn.o
+	ld -o $@ $(LDFLAGS) -h $@ minconn.o
+
+minconn.o: minconn.c
+	$(CC) $(CFLAGS) -c $? 
+
+passprompt.so: passprompt.o
+	ld -o $@ $(LDFLAGS) -h $@ passprompt.o
+
+passprompt.o: passprompt.c
+	$(CC) $(CFLAGS) -c $?
+
+clean:
+	rm -f *.o *.so
diff --git a/ap/app/pppd/pppd/ppp.pam b/ap/app/pppd/pppd/ppp.pam
new file mode 100644
index 0000000..c06f809
--- /dev/null
+++ b/ap/app/pppd/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
diff --git a/ap/app/pppd/pppd/pppcrypt.c b/ap/app/pppd/pppd/pppcrypt.c
new file mode 100644
index 0000000..8b85b13
--- /dev/null
+++ b/ap/app/pppd/pppd/pppcrypt.c
@@ -0,0 +1,193 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include "pppd.h"
+#include "pppcrypt.h"
+
+static u_char
+Get7Bits(input, startBit)
+u_char *input;
+int startBit;
+{
+	unsigned int word;
+
+	word  = (unsigned)input[startBit / 8] << 8;
+	word |= (unsigned)input[startBit / 8 + 1];
+
+	word >>= 15 - (startBit % 8 + 7);
+
+	return word & 0xFE;
+}
+
+static void
+MakeKey(key, des_key)
+u_char *key;		/* IN  56 bit DES key missing parity bits */
+u_char *des_key;	/* OUT 64 bit DES key with parity bits added */
+{
+	des_key[0] = Get7Bits(key,  0);
+	des_key[1] = Get7Bits(key,  7);
+	des_key[2] = Get7Bits(key, 14);
+	des_key[3] = Get7Bits(key, 21);
+	des_key[4] = Get7Bits(key, 28);
+	des_key[5] = Get7Bits(key, 35);
+	des_key[6] = Get7Bits(key, 42);
+	des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+	des_set_odd_parity((des_cblock *)des_key);
+#endif
+}
+
+#ifdef USE_CRYPT
+/*
+ * in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void
+Expand(in, out)
+u_char *in;
+u_char *out;
+{
+        int j, c;
+        int i;
+
+        for (i = 0; i < 64; in++){
+		c = *in;
+                for (j = 7; j >= 0; j--)
+                        *out++ = (c >> j) & 01;
+                i += 8;
+        }
+}
+
+/* The inverse of Expand
+ */
+static void
+Collapse(in, out)
+u_char *in;
+u_char *out;
+{
+        int j;
+        int i;
+	unsigned int c;
+
+	for (i = 0; i < 64; i += 8, out++) {
+	    c = 0;
+	    for (j = 7; j >= 0; j--, in++)
+		c |= *in << j;
+	    *out = c & 0xff;
+	}
+}
+
+bool
+DesSetkey(key)
+u_char *key;
+{
+	u_char des_key[8];
+	u_char crypt_key[66];
+
+	MakeKey(key, des_key);
+	Expand(des_key, crypt_key);
+	errno = 0;
+	setkey((const char *)crypt_key);
+	if (errno != 0)
+		return (0);
+	return (1);
+}
+
+bool
+DesEncrypt(clear, cipher)
+u_char *clear;	/* IN  8 octets */
+u_char *cipher;	/* OUT 8 octets */
+{
+	u_char des_input[66];
+
+	Expand(clear, des_input);
+	errno = 0;
+	encrypt((char *)des_input, 0);
+	if (errno != 0)
+		return (0);
+	Collapse(des_input, cipher);
+	return (1);
+}
+
+bool
+DesDecrypt(cipher, clear)
+u_char *cipher;	/* IN  8 octets */
+u_char *clear;	/* OUT 8 octets */
+{
+	u_char des_input[66];
+
+	Expand(cipher, des_input);
+	errno = 0;
+	encrypt((char *)des_input, 1);
+	if (errno != 0)
+		return (0);
+	Collapse(des_input, clear);
+	return (1);
+}
+
+#else /* USE_CRYPT */
+static des_key_schedule	key_schedule;
+
+bool
+DesSetkey(key)
+u_char *key;
+{
+	des_cblock des_key;
+	MakeKey(key, des_key);
+	des_set_key(&des_key, key_schedule);
+	return (1);
+}
+
+bool
+DesEncrypt(clear, key, cipher)
+u_char *clear;	/* IN  8 octets */
+u_char *cipher;	/* OUT 8 octets */
+{
+	des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher,
+	    key_schedule, 1);
+	return (1);
+}
+
+bool
+DesDecrypt(cipher, clear)
+u_char *cipher;	/* IN  8 octets */
+u_char *clear;	/* OUT 8 octets */
+{
+	des_ecb_encrypt((des_cblock *)cipher, (des_cblock *)clear,
+	    key_schedule, 0);
+	return (1);
+}
+
+#endif /* USE_CRYPT */
diff --git a/ap/app/pppd/pppd/pppcrypt.h b/ap/app/pppd/pppd/pppcrypt.h
new file mode 100644
index 0000000..adcdcbc
--- /dev/null
+++ b/ap/app/pppd/pppd/pppcrypt.h
@@ -0,0 +1,48 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef PPPCRYPT_H
+#define	PPPCRYPT_H
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#ifndef USE_CRYPT
+#include <des.h>
+#endif
+
+extern bool	DesSetkey __P((u_char *));
+extern bool	DesEncrypt __P((u_char *, u_char *));
+extern bool	DesDecrypt __P((u_char *, u_char *));
+
+#endif /* PPPCRYPT_H */
diff --git a/ap/app/pppd/pppd/pppd.8 b/ap/app/pppd/pppd/pppd.8
new file mode 100644
index 0000000..8ea8200
--- /dev/null
+++ b/ap/app/pppd/pppd/pppd.8
@@ -0,0 +1,1938 @@
+.\" manual page [] for pppd 2.4
+.\" $Id: pppd.8,v 1.90 2008/03/26 12:09:40 paulus Exp $
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.\" 
+.\" Copyright (c) 1993-2003 Paul Mackerras <paulus@samba.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.TH PPPD 8
+.SH NAME
+pppd \- Point-to-Point Protocol Daemon
+.SH SYNOPSIS
+.B pppd
+[
+.I options
+]
+.SH DESCRIPTION
+.LP
+PPP is the protocol used for establishing internet links over dial-up
+modems, DSL connections, and many other types of point-to-point
+links.  The \fIpppd\fR daemon works together with the kernel PPP
+driver to establish and maintain a PPP link with another system
+(called the \fIpeer\fR) and to negotiate Internet Protocol (IP)
+addresses for each end of the link.  Pppd can also authenticate the
+peer and/or supply authentication information to the peer.  PPP can be
+used with other network protocols besides IP, but such use is becoming
+increasingly rare.
+.SH FREQUENTLY USED OPTIONS
+.TP
+.I ttyname
+Use the serial port called \fIttyname\fR to communicate with the
+peer.  If \fIttyname\fR does not begin with a slash (/),
+the string "/dev/" is prepended to \fIttyname\fR to form the
+name of the device to open.  If no device name is given, or if the
+name of the terminal
+connected to the standard input is given, pppd will use that terminal,
+and will not fork to put itself in the background.  A value for this
+option from a privileged source cannot be overridden by a
+non-privileged user.
+.TP
+.I speed
+An option that is a decimal number is taken as the desired baud rate
+for the serial device.  On systems such as
+4.4BSD and NetBSD, any speed can be specified.  Other systems
+(e.g. Linux, SunOS) only support the commonly-used baud rates.
+.TP
+.B asyncmap \fImap
+This option sets the Async-Control-Character-Map (ACCM) for this end
+of the link.  The ACCM is a set of 32 bits, one for each of the
+ASCII control characters with values from 0 to 31, where a 1 bit
+indicates that the corresponding control character should not be used
+in PPP packets sent to this system.  The map is encoded as a
+hexadecimal number (without a leading 0x) where the least significant
+bit (00000001) represents character 0 and the most significant bit
+(80000000) represents character 31.
+Pppd will ask the peer to send these characters as a 2-byte
+escape sequence.
+If multiple \fIasyncmap\fR options are given, the values are ORed
+together.  If no \fIasyncmap\fR option is given, the default is zero,
+so pppd will ask the peer not to escape any control characters.
+To escape transmitted characters, use the \fIescape\fR option.
+.TP
+.B auth
+Require the peer to authenticate itself before allowing network
+packets to be sent or received.  This option is the default if the
+system has a default route.  If neither this option nor the
+\fInoauth\fR option is specified, pppd will only allow the peer to use
+IP addresses to which the system does not already have a route.
+.TP
+.B call \fIname
+Read additional options from the file /etc/ppp/peers/\fIname\fR.  This
+file may contain privileged options, such as \fInoauth\fR, even if pppd
+is not being run by root.  The \fIname\fR string may not begin with /
+or include .. as a pathname component.  The format of the options file
+is described below.
+.TP
+.B connect \fIscript
+Usually there is something which needs to be done to prepare the link
+before the PPP protocol can be started; for instance, with a dial-up
+modem, commands need to be sent to the modem to dial the appropriate
+phone number.  This option specifies an command for pppd to execute
+(by passing it to a shell) before attempting to start PPP negotiation.
+The chat (8) program is often useful here, as it provides a way to
+send arbitrary strings to a modem and respond to received characters.
+A value
+for this option from a privileged source cannot be overridden by a
+non-privileged user.
+.TP
+.B crtscts
+Specifies that pppd should set the serial port to use hardware flow
+control using the RTS and CTS signals in the RS-232 interface.
+If neither the \fIcrtscts\fR, the
+\fInocrtscts\fR, the \fIcdtrcts\fR nor the \fInocdtrcts\fR option
+is given, the hardware flow control setting for the serial port is
+left unchanged.
+Some serial ports (such as Macintosh serial ports) lack a true
+RTS output. Such serial ports use this mode to implement
+unidirectional flow control. The serial port will
+suspend transmission when requested by the modem (via CTS)
+but will be unable to request the modem to stop sending to the
+computer. This mode retains the ability to use DTR as
+a modem control line.
+.TP
+.B defaultroute
+Add a default route to the system routing tables, using the peer as
+the gateway, when IPCP negotiation is successfully completed.
+This entry is removed when the PPP connection is broken.  This option
+is privileged if the \fInodefaultroute\fR option has been specified.
+.TP
+.B disconnect \fIscript
+Execute the command specified by \fIscript\fR, by passing it to a
+shell, after
+pppd has terminated the link.  This command could, for example, issue
+commands to the modem to cause it to hang up if hardware modem control
+signals were not available.  The disconnect script is not run if the
+modem has already hung up.  A value for this option from a privileged
+source cannot be overridden by a non-privileged user.
+.TP
+.B escape \fIxx,yy,...
+Specifies that certain characters should be escaped on transmission
+(regardless of whether the peer requests them to be escaped with its
+async control character map).  The characters to be escaped are
+specified as a list of hex numbers separated by commas.  Note that
+almost any character can be specified for the \fIescape\fR option,
+unlike the \fIasyncmap\fR option which only allows control characters
+to be specified.  The characters which may not be escaped are those
+with hex values 0x20 - 0x3f or 0x5e.
+.TP
+.B file \fIname
+Read options from file \fIname\fR (the format is described below).
+The file must be readable by the user who has invoked pppd.
+.TP
+.B init \fIscript
+Execute the command specified by \fIscript\fR, by passing it to a shell, to
+initialize the serial line.  This script would typically use the
+chat(8) program to configure the modem to enable auto answer.  A value
+for this option from a privileged source cannot be overridden by a
+non-privileged user.
+.TP
+.B lock
+Specifies that pppd should create a UUCP-style lock file for the
+serial device to ensure exclusive access to the device.  By default,
+pppd will not create a lock file.
+.TP
+.B mru \fIn
+Set the MRU [Maximum Receive Unit] value to \fIn\fR. Pppd
+will ask the peer to send packets of no more than \fIn\fR bytes.
+The value of \fIn\fR must be between 128 and 16384; the default is 1500.
+A value of
+296 works well on very slow links (40 bytes for TCP/IP header + 256
+bytes of data).
+Note that for the IPv6 protocol, the MRU must be at least 1280.
+.TP
+.B mtu \fIn
+Set the MTU [Maximum Transmit Unit] value to \fIn\fR.  Unless the
+peer requests a smaller value via MRU negotiation, pppd will
+request that the kernel networking code send data packets of no more
+than \fIn\fR bytes through the PPP network interface.  Note that for 
+the IPv6 protocol, the MTU must be at least 1280.
+.TP
+.B passive
+Enables the "passive" option in the LCP.  With this option, pppd will
+attempt to initiate a connection; if no reply is received from the
+peer, pppd will then just wait passively for a valid LCP packet from
+the peer, instead of exiting, as it would without this option.
+.SH OPTIONS
+.TP
+.I <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 Linux, and requires that the kernel
+was configured to include PPP filtering support (CONFIG_PPP_FILTER).
+Note that it
+is possible to apply different constraints to incoming and outgoing
+packets using the \fBinbound\fR and \fBoutbound\fR qualifiers.
+.TP
+.B allow\-ip \fIaddress(es)
+Allow peers to use the given IP address or subnet without
+authenticating themselves.  The parameter is parsed as for each
+element of the list of allowed IP addresses in the secrets files (see
+the AUTHENTICATION section below).
+.TP
+.B allow\-number \fInumber
+Allow peers to connect from the given telephone number.  A trailing
+`*' character will match all numbers beginning with the leading part.
+.TP
+.B bsdcomp \fInr,nt
+Request that the peer compress packets that it sends, using the
+BSD-Compress scheme, with a maximum code size of \fInr\fR bits, and
+agree to compress packets sent to the peer with a maximum code size of
+\fInt\fR bits.  If \fInt\fR is not specified, it defaults to the value
+given for \fInr\fR.  Values in the range 9 to 15 may be used for
+\fInr\fR and \fInt\fR; larger values give better compression but
+consume more kernel memory for compression dictionaries.
+Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
+compression in the corresponding direction.  Use \fInobsdcomp\fR or
+\fIbsdcomp 0\fR to disable BSD-Compress compression entirely.
+.TP
+.B cdtrcts
+Use a non-standard hardware flow control (i.e. DTR/CTS) to control
+the flow of data on the serial port.  If neither the \fIcrtscts\fR,
+the \fInocrtscts\fR, the \fIcdtrcts\fR nor the \fInocdtrcts\fR
+option is given, the hardware flow control setting for the serial
+port is left unchanged.
+Some serial ports (such as Macintosh serial ports) lack a true
+RTS output. Such serial ports use this mode to implement true
+bi-directional flow control. The sacrifice is that this flow
+control mode does not permit using DTR as a modem control line.
+.TP
+.B chap\-interval \fIn
+If this option is given, pppd will rechallenge the peer every \fIn\fR
+seconds.
+.TP
+.B chap\-max\-challenge \fIn
+Set the maximum number of CHAP challenge transmissions to \fIn\fR
+(default 10).
+.TP
+.B chap\-restart \fIn
+Set the CHAP restart interval (retransmission timeout for challenges)
+to \fIn\fR seconds (default 3).
+.TP
+.B child\-timeout \fIn
+When exiting, wait for up to \fIn\fR seconds for any child processes
+(such as the command specified with the \fBpty\fR command) to exit
+before exiting.  At the end of the timeout, pppd will send a SIGTERM
+signal to any remaining child processes and exit.  A value of 0 means
+no timeout, that is, pppd will wait until all child processes have
+exited.
+.TP
+.B connect\-delay \fIn
+Wait for up to \fIn\fR milliseconds after the connect script finishes for
+a valid PPP packet from the peer.  At the end of this time, or when a
+valid PPP packet is received from the peer, pppd will commence
+negotiation by sending its first LCP packet.  The default value is
+1000 (1 second).  This wait period only applies if the \fBconnect\fR
+or \fBpty\fR option is used.
+.TP
+.B debug
+Enables connection debugging facilities.
+If this option is given, pppd will log the contents of all
+control packets sent or received in a readable form.  The packets are
+logged through syslog with facility \fIdaemon\fR and level
+\fIdebug\fR.  This information can be directed to a file by setting up
+/etc/syslog.conf appropriately (see syslog.conf(5)).
+.TP
+.B default\-asyncmap
+Disable asyncmap negotiation, forcing all control characters to be
+escaped for both the transmit and the receive direction.
+.TP
+.B default\-mru
+Disable MRU [Maximum Receive Unit] negotiation.  With this option,
+pppd will use the default MRU value of 1500 bytes for both the
+transmit and receive direction.
+.TP
+.B deflate \fInr,nt
+Request that the peer compress packets that it sends, using the
+Deflate scheme, with a maximum window size of \fI2**nr\fR bytes, and
+agree to compress packets sent to the peer with a maximum window size
+of \fI2**nt\fR bytes.  If \fInt\fR is not specified, it defaults to
+the value given for \fInr\fR.  Values in the range 9 to 15 may be used
+for \fInr\fR and \fInt\fR; larger values give better compression but
+consume more kernel memory for compression dictionaries.
+Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
+compression in the corresponding direction.  Use \fInodeflate\fR or
+\fIdeflate 0\fR to disable Deflate compression entirely.  (Note: pppd
+requests Deflate compression in preference to BSD-Compress if the peer
+can do either.)
+.TP
+.B demand
+Initiate the link only on demand, i.e. when data traffic is present.
+With this option, the remote IP address must be specified by the user
+on the command line or in an options file.  Pppd will initially
+configure the interface and enable it for IP traffic without
+connecting to the peer.  When traffic is available, pppd will
+connect to the peer and perform negotiation, authentication, etc.
+When this is completed, pppd will commence passing data packets
+(i.e., IP packets) across the link.
+
+The \fIdemand\fR option implies the \fIpersist\fR option.  If this
+behaviour is not desired, use the \fInopersist\fR option after the
+\fIdemand\fR option.  The \fIidle\fR and \fIholdoff\fR
+options are also useful in conjuction with the \fIdemand\fR option.
+.TP
+.B domain \fId
+Append the domain name \fId\fR to the local host name for authentication
+purposes.  For example, if gethostname() returns the name porsche, but
+the fully qualified domain name is porsche.Quotron.COM, you could
+specify \fIdomain Quotron.COM\fR.  Pppd would then use the name
+\fIporsche.Quotron.COM\fR for looking up secrets in the secrets file,
+and as the default name to send to the peer when authenticating itself
+to the peer.  This option is privileged.
+.TP
+.B dryrun
+With the \fBdryrun\fR option, pppd will print out all the option
+values which have been set and then exit, after parsing the command
+line and options files and checking the option values, but before
+initiating the link.  The option values are logged at level info, and
+also printed to standard output unless the device on standard output
+is the device that pppd would be using to communicate with the peer.
+.TP
+.B dump
+With the \fBdump\fR option, pppd will print out all the option values
+which have been set.  This option is like the \fBdryrun\fR option
+except that pppd proceeds as normal rather than exiting.
+.TP
+.B enable-session
+Enables session accounting via PAM or wtwp/wtmpx, as appropriate.
+When PAM is enabled, the PAM "account" and "session" module stacks
+determine behavior, and are enabled for all PPP authentication
+protocols.  When PAM is disabled, wtmp/wtmpx entries are recorded
+regardless of whether the peer name identifies a valid user on the
+local system, making peers visible in the last(1) log.  This feature
+is automatically enabled when the pppd \fBlogin\fR option is used.
+Session accounting is disabled by default.
+.TP
+.B endpoint \fI<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 eap\-interval \fIn
+If this option is given and pppd authenticates the peer with EAP
+(i.e., is the server), pppd will restart EAP authentication every
+\fIn\fR seconds.  For EAP SRP\-SHA1, see also the \fBsrp\-interval\fR
+option, which enables lightweight rechallenge.
+.TP
+.B eap\-max\-rreq \fIn
+Set the maximum number of EAP Requests to which pppd will respond (as
+a client) without hearing EAP Success or Failure.  (Default is 20.)
+.TP
+.B eap\-max\-sreq \fIn
+Set the maximum number of EAP Requests that pppd will issue (as a
+server) while attempting authentication.  (Default is 10.)
+.TP
+.B eap\-restart \fIn
+Set the retransmit timeout for EAP Requests when acting as a server
+(authenticator).  (Default is 3 seconds.)
+.TP
+.B eap\-timeout \fIn
+Set the maximum time to wait for the peer to send an EAP Request when
+acting as a client (authenticatee).  (Default is 20 seconds.)
+.TP
+.B hide\-password
+When logging the contents of PAP packets, this option causes pppd to
+exclude the password string from the log.  This is the default.
+.TP
+.B holdoff \fIn
+Specifies how many seconds to wait before re-initiating the link after
+it terminates.  This option only has any effect if the \fIpersist\fR
+or \fIdemand\fR option is used.  The holdoff period is not applied if
+the link was terminated because it was idle.
+.TP
+.B idle \fIn
+Specifies that pppd should disconnect if the link is idle for \fIn\fR
+seconds.  The link is idle when no data packets (i.e. IP packets) are
+being sent or received.  Note: it is not advisable to use this option
+with the \fIpersist\fR option without the \fIdemand\fR option.
+If the \fBactive\-filter\fR
+option is given, data packets which are rejected by the specified
+activity filter also count as the link being idle.
+.TP
+.B ipcp\-accept\-local
+With this option, pppd will accept the peer's idea of our local IP
+address, even if the local IP address was specified in an option.
+.TP
+.B ipcp\-accept\-remote
+With this option, pppd will accept the peer's idea of its (remote) IP
+address, even if the remote IP address was specified in an option.
+.TP
+.B ipcp\-max\-configure \fIn
+Set the maximum number of IPCP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B ipcp\-max\-failure \fIn
+Set the maximum number of IPCP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B ipcp\-max\-terminate \fIn
+Set the maximum number of IPCP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B ipcp\-restart \fIn
+Set the IPCP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B ipparam \fIstring
+Provides an extra parameter to the ip\-up, ip\-pre\-up and ip\-down
+scripts.  If this
+option is given, the \fIstring\fR supplied is given as the 6th
+parameter to those scripts.
+.TP
+.B ipv6cp\-max\-configure \fIn
+Set the maximum number of IPv6CP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B ipv6cp\-max\-failure \fIn
+Set the maximum number of IPv6CP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B ipv6cp\-max\-terminate \fIn
+Set the maximum number of IPv6CP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B ipv6cp\-restart \fIn
+Set the IPv6CP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B ipx
+Enable the IPXCP and IPX protocols.  This option is presently only
+supported under Linux, and only if your kernel has been configured to
+include IPX support.
+.TP
+.B ipx\-network \fIn
+Set the IPX network number in the IPXCP configure request frame to
+\fIn\fR, a hexadecimal number (without a leading 0x).  There is no
+valid default.  If this option is not specified, the network number is
+obtained from the peer.  If the peer does not have the network number,
+the IPX protocol will not be started.
+.TP
+.B ipx\-node \fIn\fB:\fIm
+Set the IPX node numbers. The two node numbers are separated from each
+other with a colon character. The first number \fIn\fR is the local
+node number. The second number \fIm\fR is the peer's node number. Each
+node number is a hexadecimal number, at most 10 digits long. The node
+numbers on the ipx\-network must be unique. There is no valid
+default. If this option is not specified then the node numbers are
+obtained from the peer.
+.TP
+.B ipx\-router\-name \fI<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.  This is
+the opposite of the \fBmodem\fR option.
+.TP
+.B logfd \fIn
+Send log messages to file descriptor \fIn\fR.  Pppd will send log
+messages to at most one file or file descriptor (as well as sending
+the log messages to syslog), so this option and the \fBlogfile\fR
+option are mutually exclusive.  The default is for pppd to send log
+messages to stdout (file descriptor 1), unless the serial port is
+already open on stdout.
+.TP
+.B logfile \fIfilename
+Append log messages to the file \fIfilename\fR (as well as sending the
+log messages to syslog).  The file is opened with the privileges of
+the user who invoked pppd, in append mode.
+.TP
+.B login
+Use the system password database for authenticating the peer using
+PAP, and record the user in the system wtmp file.  Note that the peer
+must have an entry in the /etc/ppp/pap\-secrets file as well as the
+system password database to be allowed access.  See also the
+\fBenable\-session\fR option.
+.TP
+.B maxconnect \fIn
+Terminate the connection when it has been available for network
+traffic for \fIn\fR seconds (i.e. \fIn\fR seconds after the first
+network control protocol comes up).
+.TP
+.B maxfail \fIn
+Terminate after \fIn\fR consecutive failed connection attempts.  A
+value of 0 means no limit.  The default value is 10.
+.TP
+.B modem
+Use the modem control lines.  This option is the default.  With this
+option, pppd will wait for the CD (Carrier Detect) signal from the
+modem to be asserted when opening the serial device (unless a connect
+script is specified), and it will drop the DTR (Data Terminal Ready)
+signal briefly when the connection is terminated and before executing
+the connect script.  On Ultrix, this option implies hardware flow
+control, as for the \fIcrtscts\fR option.  This is the opposite of the
+\fBlocal\fR option.
+.TP
+.B mp
+Enables the use of PPP multilink; this is an alias for the `multilink'
+option.  This option is currently only available under Linux.
+.TP
+.B mppe\-stateful
+Allow MPPE to use stateful mode.  Stateless mode is still attempted first.
+The default is to disallow stateful mode.  
+.TP
+.B mpshortseq
+Enables the use of short (12-bit) sequence numbers in multilink
+headers, as opposed to 24-bit sequence numbers.  This option is only
+available under Linux, and only has any effect if multilink is
+enabled (see the multilink option).
+.TP
+.B mrru \fIn
+Sets the Maximum Reconstructed Receive Unit to \fIn\fR.  The MRRU is
+the maximum size for a received packet on a multilink bundle, and is
+analogous to the MRU for the individual links.  This option is
+currently only available under Linux, and only has any effect if
+multilink is enabled (see the multilink option).
+.TP
+.B ms\-dns \fI<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 noaccomp
+Disable Address/Control compression in both directions (send and
+receive).
+.TP
+.B noauth
+Do not require the peer to authenticate itself.  This option is
+privileged.
+.TP
+.B nobsdcomp
+Disables BSD-Compress compression; \fBpppd\fR will not request or
+agree to compress packets using the BSD-Compress scheme.
+.TP
+.B noccp
+Disable CCP (Compression Control Protocol) negotiation.  This option
+should only be required if the peer is buggy and gets confused by
+requests from pppd for CCP negotiation.
+.TP
+.B nocrtscts
+Disable hardware flow control (i.e. RTS/CTS) on the serial port.
+If neither the \fIcrtscts\fR nor the \fInocrtscts\fR nor the
+\fIcdtrcts\fR nor the \fInocdtrcts\fR option is given, the hardware
+flow control setting for the serial port is left unchanged.
+.TP
+.B nocdtrcts
+This option is a synonym for \fInocrtscts\fR. Either of these options will
+disable both forms of hardware flow control.
+.TP
+.B nodefaultroute
+Disable the \fIdefaultroute\fR option.  The system administrator who
+wishes to prevent users from creating default routes with pppd
+can do so by placing this option in the /etc/ppp/options file.
+.TP
+.B nodeflate
+Disables Deflate compression; pppd will not request or agree to
+compress packets using the Deflate scheme.
+.TP
+.B nodetach
+Don't detach from the controlling terminal.  Without this option, if a
+serial device other than the terminal on the standard input is
+specified, pppd will fork to become a background process.
+.TP
+.B noendpoint
+Disables pppd from sending an endpoint discriminator to the peer or
+accepting one from the peer (see the MULTILINK section below).  This
+option should only be required if the peer is buggy.
+.TP
+.B noip
+Disable IPCP negotiation and IP communication.  This option should
+only be required if the peer is buggy and gets confused by requests
+from pppd for IPCP negotiation.
+.TP
+.B noipv6
+Disable IPv6CP negotiation and IPv6 communication. This option should
+only be required if the peer is buggy and gets confused by requests
+from pppd for IPv6CP negotiation.
+.TP
+.B noipdefault
+Disables the default behaviour when no local IP address is specified,
+which is to determine (if possible) the local IP address from the
+hostname.  With this option, the peer will have to supply the local IP
+address during IPCP negotiation (unless it specified explicitly on the
+command line or in an options file).
+.TP
+.B noipx
+Disable the IPXCP and IPX protocols.  This option should only be
+required if the peer is buggy and gets confused by requests from pppd
+for IPXCP negotiation.
+.TP
+.B noktune
+Opposite of the \fIktune\fR option; disables pppd from changing system
+settings.
+.TP
+.B nolock
+Opposite of the \fIlock\fR option; specifies that pppd should not
+create a UUCP-style lock file for the serial device.  This option is
+privileged.
+.TP
+.B nolog
+Do not send log messages to a file or file descriptor.  This option
+cancels the \fBlogfd\fR and \fBlogfile\fR options.
+.TP
+.B nomagic
+Disable magic number negotiation.  With this option, pppd cannot
+detect a looped-back line.  This option should only be needed if the
+peer is buggy.
+.TP
+.B nomp
+Disables the use of PPP multilink.  This option is currently only
+available under Linux.
+.TP
+.B nomppe
+Disables MPPE (Microsoft Point to Point Encryption).  This is the default.
+.TP
+.B nomppe\-40
+Disable 40-bit encryption with MPPE.
+.TP
+.B nomppe\-128
+Disable 128-bit encryption with MPPE.
+.TP
+.B nomppe\-stateful
+Disable MPPE stateful mode.  This is the default.
+.TP
+.B nompshortseq
+Disables the use of short (12-bit) sequence numbers in the PPP
+multilink protocol, forcing the use of 24-bit sequence numbers.  This
+option is currently only available under Linux, and only has any
+effect if multilink is enabled.
+.TP
+.B nomultilink
+Disables the use of PPP multilink.  This option is currently only
+available under Linux.
+.TP
+.B nopcomp
+Disable protocol field compression negotiation in both the receive and
+the transmit direction.
+.TP
+.B nopersist
+Exit once a connection has been made and terminated.  This is the
+default unless the \fIpersist\fR or \fIdemand\fR option has been
+specified.
+.TP
+.B nopredictor1
+Do not accept or agree to Predictor\-1 compression.
+.TP
+.B noproxyarp
+Disable the \fIproxyarp\fR option.  The system administrator who
+wishes to prevent users from creating proxy ARP entries with pppd can
+do so by placing this option in the /etc/ppp/options file.
+.TP
+.B noremoteip
+Allow pppd to operate without having an IP address for the peer.  This
+option is only available under Linux.  Normally, pppd will request the
+peer's IP address, and if the peer does not supply it, pppd will not
+bring up the link for IP traffic.  With this option, if the peer does
+not supply its IP address, pppd will not ask the peer for it, and will
+not set the destination address of the ppp interface.  In this
+situation, the ppp interface can be used for routing by creating
+device routes, but the peer itself cannot be addressed directly for IP
+traffic.
+.TP
+.B notty
+Normally, pppd requires a terminal device.  With this option, pppd
+will allocate itself a pseudo-tty master/slave pair and use the slave
+as its terminal device.  Pppd will create a child process to act as a
+`character shunt' to transfer characters between the pseudo-tty master
+and its standard input and output.  Thus pppd will transmit characters
+on its standard output and receive characters on its standard input
+even if they are not terminal devices.  This option increases the
+latency and CPU overhead of transferring data over the ppp interface
+as all of the characters sent and received must flow through the
+character shunt process.  An explicit device name may not be given if
+this option is used.
+.TP
+.B novj
+Disable Van Jacobson style TCP/IP header compression in both the
+transmit and the receive direction.
+.TP
+.B novjccomp
+Disable the connection-ID compression option in Van Jacobson style
+TCP/IP header compression.  With this option, pppd will not omit the
+connection-ID byte from Van Jacobson compressed TCP/IP headers, nor
+ask the peer to do so.
+.TP
+.B papcrypt
+Indicates that all secrets in the /etc/ppp/pap\-secrets file which are
+used for checking the identity of the peer are encrypted, and thus
+pppd should not accept a password which, before encryption, is
+identical to the secret from the /etc/ppp/pap\-secrets file.
+.TP
+.B pap\-max\-authreq \fIn
+Set the maximum number of PAP authenticate-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B pap\-restart \fIn
+Set the PAP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B pap\-timeout \fIn
+Set the maximum time that pppd will wait for the peer to authenticate
+itself with PAP to \fIn\fR seconds (0 means no limit).
+.TP
+.B pass\-filter \fIfilter\-expression
+Specifies a packet filter to applied to data packets being sent or
+received to determine which packets should be allowed to pass.
+Packets which are rejected by the filter are silently discarded.  This
+option can be used to prevent specific network daemons (such as
+routed) using up link bandwidth, or to provide a very basic firewall
+capability.
+The \fIfilter\-expression\fR syntax is as described for tcpdump(1),
+except that qualifiers which are inappropriate for a PPP link, such as
+\fBether\fR and \fBarp\fR, are not permitted.  Generally the filter
+expression should be enclosed in single-quotes to prevent whitespace
+in the expression from being interpreted by the shell.  Note that it
+is possible to apply different constraints to incoming and outgoing
+packets using the \fBinbound\fR and \fBoutbound\fR qualifiers. This
+option is currently only available under Linux, and requires that the
+kernel was configured to include PPP filtering support (CONFIG_PPP_FILTER).
+.TP
+.B password \fIpassword\-string
+Specifies the password to use for authenticating to the peer.  Use
+of this option is discouraged, as the password is likely to be visible
+to other users on the system (for example, by using ps(1)).
+.TP
+.B persist
+Do not exit after a connection is terminated; instead try to reopen
+the connection. The \fBmaxfail\fR option still has an effect on
+persistent connections.
+.TP
+.B plugin \fIfilename
+Load the shared library object file \fIfilename\fR as a plugin.  This
+is a privileged option.  If \fIfilename\fR does not contain a slash
+(/), pppd will look in the \fB/usr/lib/pppd/\fIversion\fR directory
+for the plugin, where
+\fIversion\fR is the version number of pppd (for example, 2.4.2).
+.TP
+.B predictor1
+Request that the peer compress frames that it sends using Predictor-1
+compression, and agree to compress transmitted frames with Predictor-1
+if requested.  This option has no effect unless the kernel driver
+supports Predictor-1 compression.
+.TP
+.B privgroup \fIgroup\-name
+Allows members of group \fIgroup\-name\fR to use privileged options.
+This is a privileged option.  Use of this option requires care as
+there is no guarantee that members of \fIgroup\-name\fR cannot use pppd
+to become root themselves.  Consider it equivalent to putting the
+members of \fIgroup\-name\fR in the kmem or disk group.
+.TP
+.B proxyarp
+Add an entry to this system's ARP [Address Resolution Protocol] table
+with the IP address of the peer and the Ethernet address of this
+system.  This will have the effect of making the peer appear to other
+systems to be on the local ethernet.
+.TP
+.B pty \fIscript
+Specifies that the command \fIscript\fR is to be used to communicate
+rather than a specific terminal device.  Pppd will allocate itself a
+pseudo-tty master/slave pair and use the slave as its terminal
+device.  The \fIscript\fR will be run in a child process with the
+pseudo-tty master as its standard input and output.  An explicit
+device name may not be given if this option is used.  (Note: if the
+\fIrecord\fR option is used in conjuction with the \fIpty\fR option,
+the child process will have pipes on its standard input and output.)
+.TP
+.B receive\-all
+With this option, pppd will accept all control characters from the
+peer, including those marked in the receive asyncmap.  Without this
+option, pppd will discard those characters as specified in RFC1662.
+This option should only be needed if the peer is buggy.
+.TP
+.B record \fIfilename
+Specifies that pppd should record all characters sent and received to
+a file named \fIfilename\fR.  This file is opened in append mode,
+using the user's user-ID and permissions.  This option is implemented
+using a pseudo-tty and a process to transfer characters between the
+pseudo-tty and the real serial device, so it will increase the latency
+and CPU overhead of transferring data over the ppp interface.  The
+characters are stored in a tagged format with timestamps, which can be
+displayed in readable form using the pppdump(8) program.
+.TP
+.B remotename \fIname
+Set the assumed name of the remote system for authentication purposes
+to \fIname\fR.
+.TP
+.B remotenumber \fInumber
+Set the assumed telephone number of the remote system for authentication
+purposes to \fInumber\fR.
+.TP
+.B refuse\-chap
+With this option, pppd will not agree to authenticate itself to the
+peer using CHAP.
+.TP
+.B refuse\-mschap
+With this option, pppd will not agree to authenticate itself to the
+peer using MS\-CHAP.
+.TP
+.B refuse\-mschap\-v2
+With this option, pppd will not agree to authenticate itself to the
+peer using MS\-CHAPv2.
+.TP
+.B refuse\-eap
+With this option, pppd will not agree to authenticate itself to the
+peer using EAP.
+.TP
+.B refuse\-pap
+With this option, pppd will not agree to authenticate itself to the
+peer using PAP.
+.TP
+.B require\-chap
+Require the peer to authenticate itself using CHAP [Challenge
+Handshake Authentication Protocol] authentication.
+.TP
+.B require\-mppe
+Require the use of MPPE (Microsoft Point to Point Encryption).  This
+option disables all other compression types.  This option enables
+both 40-bit and 128-bit encryption.  In order for MPPE to successfully
+come up, you must have authenticated with either MS\-CHAP or MS\-CHAPv2.
+This option is presently only supported under Linux, and only if your
+kernel has been configured to include MPPE support.
+.TP
+.B require\-mppe\-40
+Require the use of MPPE, with 40-bit encryption.
+.TP
+.B require\-mppe\-128
+Require the use of MPPE, with 128-bit encryption.
+.TP
+.B require\-mschap
+Require the peer to authenticate itself using MS\-CHAP [Microsoft Challenge
+Handshake Authentication Protocol] authentication.
+.TP
+.B require\-mschap\-v2
+Require the peer to authenticate itself using MS\-CHAPv2 [Microsoft Challenge
+Handshake Authentication Protocol, Version 2] authentication.
+.TP
+.B require\-eap
+Require the peer to authenticate itself using EAP [Extensible
+Authentication Protocol] authentication.
+.TP
+.B require\-pap
+Require the peer to authenticate itself using PAP [Password
+Authentication Protocol] authentication.
+.TP
+.B show\-password
+When logging the contents of PAP packets, this option causes pppd to
+show the password string in the log message.
+.TP
+.B silent
+With this option, pppd will not transmit LCP packets to initiate a
+connection until a valid LCP packet is received from the peer (as for
+the `passive' option with ancient versions of pppd).
+.TP
+.B srp\-interval \fIn
+If this parameter is given and pppd uses EAP SRP\-SHA1 to authenticate
+the peer (i.e., is the server), then pppd will use the optional
+lightweight SRP rechallenge mechanism at intervals of \fIn\fR
+seconds.  This option is faster than \fBeap\-interval\fR
+reauthentication because it uses a hash\-based mechanism and does not
+derive a new session key.
+.TP
+.B srp\-pn\-secret \fIstring
+Set the long-term pseudonym-generating secret for the server.  This
+value is optional and if set, needs to be known at the server
+(authenticator) side only, and should be different for each server (or
+poll of identical servers).  It is used along with the current date to
+generate a key to encrypt and decrypt the client's identity contained
+in the pseudonym.
+.TP
+.B srp\-use\-pseudonym
+When operating as an EAP SRP\-SHA1 client, attempt to use the pseudonym
+stored in ~/.ppp_psuedonym first as the identity, and save in this
+file any pseudonym offered by the peer during authentication.
+.TP
+.B sync
+Use synchronous HDLC serial encoding instead of asynchronous.
+The device used by pppd with this option must have sync support.
+Currently supports Microgate SyncLink adapters
+under Linux and FreeBSD 2.2.8 and later.
+.TP
+.B unit \fInum
+Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound
+connections.
+.TP
+.B updetach
+With this option, pppd will detach from its controlling terminal once
+it has successfully established the ppp connection (to the point where
+the first network control protocol, usually the IP control protocol,
+has come up).
+.TP
+.B usehostname
+Enforce the use of the hostname (with domain name appended, if given)
+as the name of the local system for authentication purposes (overrides
+the \fIname\fR option).  This option is not normally needed since the
+\fIname\fR option is privileged.
+.TP
+.B usepeerdns
+Ask the peer for up to 2 DNS server addresses.  The addresses supplied
+by the peer (if any) are passed to the /etc/ppp/ip\-up script in the
+environment variables DNS1 and DNS2, and the environment variable
+USEPEERDNS will be set to 1.  In addition, pppd will create an
+/etc/ppp/resolv.conf file containing one or two nameserver lines with
+the address(es) supplied by the peer.
+.TP
+.B user \fIname
+Sets the name used for authenticating the local system to the peer to
+\fIname\fR.
+.TP
+.B vj\-max\-slots \fIn
+Sets the number of connection slots to be used by the Van Jacobson
+TCP/IP header compression and decompression code to \fIn\fR, which
+must be between 2 and 16 (inclusive).
+.TP
+.B welcome \fIscript
+Run the executable or shell command specified by \fIscript\fR before
+initiating PPP negotiation, after the connect script (if any) has
+completed.  A value for this option from a privileged source cannot be
+overridden by a non-privileged user.
+.TP
+.B xonxoff
+Use software flow control (i.e. XON/XOFF) to control the flow of data on
+the serial port.
+.SH OPTIONS FILES
+Options can be taken from files as well as the command line.  Pppd
+reads options from the files /etc/ppp/options, ~/.ppprc and
+/etc/ppp/options.\fIttyname\fR (in that order) before processing the
+options on the command line.  (In fact, the command-line options are
+scanned to find the terminal name before the options.\fIttyname\fR
+file is read.)  In forming the name of the options.\fIttyname\fR file,
+the initial /dev/ is removed from the terminal name, and any remaining
+/ characters are replaced with dots.
+.PP
+An options file is parsed into a series of words, delimited by
+whitespace.  Whitespace can be included in a word by enclosing the
+word in double-quotes (").  A backslash (\\) quotes the following character.
+A hash (#) starts a comment, which continues until the end of the
+line.  There is no restriction on using the \fIfile\fR or \fIcall\fR
+options within an options file.
+.SH SECURITY
+.I pppd
+provides system administrators with sufficient access control that PPP
+access to a server machine can be provided to legitimate users without
+fear of compromising the security of the server or the network it's
+on.  This control is provided through restrictions on which IP
+addresses the peer may use, based on its authenticated identity (if
+any), and through restrictions on which options a non-privileged user
+may use.  Several of pppd's options are privileged, in particular
+those which permit potentially insecure configurations; these options
+are only accepted in files which are under the control of the system
+administrator, or if pppd is being run by root.
+.PP
+The default behaviour of pppd is to allow an unauthenticated peer to
+use a given IP address only if the system does not already have a
+route to that IP address.  For example, a system with a
+permanent connection to the wider internet will normally have a
+default route, and thus all peers will have to authenticate themselves
+in order to set up a connection.  On such a system, the \fIauth\fR
+option is the default.  On the other hand, a system where the
+PPP link is the only connection to the internet will not normally have
+a default route, so the peer will be able to use almost any IP address
+without authenticating itself.
+.PP
+As indicated above, some security-sensitive options are privileged,
+which means that they may not be used by an ordinary non-privileged
+user running a setuid-root pppd, either on the command line, in the
+user's ~/.ppprc file, or in an options file read using the \fIfile\fR
+option.  Privileged options may be used in /etc/ppp/options file or in
+an options file read using the \fIcall\fR option.  If pppd is being
+run by the root user, privileged options can be used without
+restriction.
+.PP
+When opening the device, pppd uses either the invoking user's user ID
+or the root UID (that is, 0), depending on whether the device name was
+specified by the user or the system administrator.  If the device name
+comes from a privileged source, that is, /etc/ppp/options or an
+options file read using the \fIcall\fR option, pppd uses full root
+privileges when opening the device.  Thus, by creating an appropriate
+file under /etc/ppp/peers, the system administrator can allow users to
+establish a ppp connection via a device which they would not normally
+have permission to access.  Otherwise pppd uses the invoking user's
+real UID when opening the device.
+.SH AUTHENTICATION
+Authentication is the process whereby one peer convinces the other of
+its identity.  This involves the first peer sending its name to the
+other, together with some kind of secret information which could only
+come from the genuine authorized user of that name.  In such an
+exchange, we will call the first peer the "client" and the other the
+"server".  The client has a name by which it identifies itself to the
+server, and the server also has a name by which it identifies itself
+to the client.  Generally the genuine client shares some secret (or
+password) with the server, and authenticates itself by proving that it
+knows that secret.  Very often, the names used for authentication
+correspond to the internet hostnames of the peers, but this is not
+essential.
+.LP
+At present, pppd supports three authentication protocols: the Password
+Authentication Protocol (PAP), Challenge Handshake Authentication
+Protocol (CHAP), and Extensible Authentication Protocol (EAP).  PAP
+involves the client sending its name and a cleartext password to the
+server to authenticate itself.  In contrast, the server initiates the
+CHAP authentication exchange by sending a challenge to the client (the
+challenge packet includes the server's name).  The client must respond
+with a response which includes its name plus a hash value derived from
+the shared secret and the challenge, in order to prove that it knows
+the secret.  EAP supports CHAP-style authentication, and also includes
+the SRP\-SHA1 mechanism, which is resistant to dictionary-based attacks
+and does not require a cleartext password on the server side.
+.LP
+The PPP protocol, being symmetrical, allows both peers to require the
+other to authenticate itself.  In that case, two separate and
+independent authentication exchanges will occur.  The two exchanges
+could use different authentication protocols, and in principle,
+different names could be used in the two exchanges.
+.LP
+The default behaviour of pppd is to agree to authenticate if
+requested, and to not require authentication from the peer.  However,
+pppd will not agree to authenticate itself with a particular protocol
+if it has no secrets which could be used to do so.
+.LP
+Pppd stores secrets for use in authentication in secrets
+files (/etc/ppp/pap\-secrets for PAP, /etc/ppp/chap\-secrets for CHAP,
+MS\-CHAP, MS\-CHAPv2, and EAP MD5-Challenge, and /etc/ppp/srp\-secrets
+for EAP SRP\-SHA1).
+All secrets files have the same format.  The secrets files can
+contain secrets for pppd to use in authenticating itself to other
+systems, as well as secrets for pppd to use when authenticating other
+systems to itself.
+.LP
+Each line in a secrets file contains one secret.  A given secret is
+specific to a particular combination of client and server - it can
+only be used by that client to authenticate itself to that server.
+Thus each line in a secrets file has at least 3 fields: the name of
+the client, the name of the server, and the secret.  These fields may
+be followed by a list of the IP addresses that the specified client
+may use when connecting to the specified server.
+.LP
+A secrets file is parsed into words as for a options file, so the
+client name, server name and secrets fields must each be one word,
+with any embedded spaces or other special characters quoted or
+escaped.  Note that case is significant in the client and server names
+and in the secret.
+.LP
+If the secret starts with an `@', what follows is assumed to be the
+name of a file from which to read the secret.  A "*" as the client or
+server name matches any name.  When selecting a secret, pppd takes the
+best match, i.e.  the match with the fewest wildcards.
+.LP
+Any following words on the same line are taken to be a list of
+acceptable IP addresses for that client.  If there are only 3 words on
+the line, or if the first word is "\-", then all IP addresses are
+disallowed.  To allow any address, use "*".  A word starting with "!"
+indicates that the specified address is \fInot\fR acceptable.  An
+address may be followed by "/" and a number \fIn\fR, to indicate a
+whole subnet, i.e. all addresses which have the same value in the most
+significant \fIn\fR bits.  In this form, the address may be followed
+by a plus sign ("+") to indicate that one address from the subnet is
+authorized, based on the ppp network interface unit number in use.
+In this case, the host part of the address will be set to the unit
+number plus one.
+.LP
+Thus a secrets file contains both secrets for use in authenticating
+other hosts, plus secrets which we use for authenticating ourselves to
+others.  When pppd is authenticating the peer (checking the peer's
+identity), it chooses a secret with the peer's name in the first
+field and the name of the local system in the second field.  The
+name of the local system defaults to the hostname, with the domain
+name appended if the \fIdomain\fR option is used.  This default can be
+overridden with the \fIname\fR option, except when the
+\fIusehostname\fR option is used.  (For EAP SRP\-SHA1, see the
+srp\-entry(8) utility for generating proper validator entries to be
+used in the "secret" field.)
+.LP
+When pppd is choosing a secret to use in authenticating itself to the
+peer, it first determines what name it is going to use to identify
+itself to the peer.  This name can be specified by the user with the
+\fIuser\fR option.  If this option is not used, the name defaults to
+the name of the local system, determined as described in the previous
+paragraph.  Then pppd looks for a secret with this name in the first
+field and the peer's name in the second field.  Pppd will know the
+name of the peer if CHAP or EAP authentication is being used, because
+the peer will have sent it in the challenge packet.  However, if PAP
+is being used, pppd will have to determine the peer's name from the
+options specified by the user.  The user can specify the peer's name
+directly with the \fIremotename\fR option.  Otherwise, if the remote
+IP address was specified by a name (rather than in numeric form), that
+name will be used as the peer's name.  Failing that, pppd will use the
+null string as the peer's name.
+.LP
+When authenticating the peer with PAP, the supplied password is first
+compared with the secret from the secrets file.  If the password
+doesn't match the secret, the password is encrypted using crypt() and
+checked against the secret again.  Thus secrets for authenticating the
+peer can be stored in encrypted form if desired.  If the
+\fIpapcrypt\fR option is given, the first (unencrypted) comparison is
+omitted, for better security.
+.LP
+Furthermore, if the \fIlogin\fR option was specified, the username and
+password are also checked against the system password database.  Thus,
+the system administrator can set up the pap\-secrets file to allow PPP
+access only to certain users, and to restrict the set of IP addresses
+that each user can use.  Typically, when using the \fIlogin\fR option,
+the secret in /etc/ppp/pap\-secrets would be "", which will match any
+password supplied by the peer.  This avoids the need to have the same
+secret in two places.
+.LP
+Authentication must be satisfactorily completed before IPCP (or any
+other Network Control Protocol) can be started.  If the peer is
+required to authenticate itself, and fails to do so, pppd will
+terminated the link (by closing LCP).  If IPCP negotiates an
+unacceptable IP address for the remote host, IPCP will be closed.  IP
+packets can only be sent or received when IPCP is open.
+.LP
+In some cases it is desirable to allow some hosts which can't
+authenticate themselves to connect and use one of a restricted set of
+IP addresses, even when the local host generally requires
+authentication.  If the peer refuses to authenticate itself when
+requested, pppd takes that as equivalent to authenticating with PAP
+using the empty string for the username and password.  Thus, by adding
+a line to the pap\-secrets file which specifies the empty string for
+the client and password, it is possible to allow restricted access to
+hosts which refuse to authenticate themselves.
+.SH ROUTING
+.LP
+When IPCP negotiation is completed successfully, pppd will inform the
+kernel of the local and remote IP addresses for the ppp interface.
+This is sufficient to create a host route to the remote end of the
+link, which will enable the peers to exchange IP packets.
+Communication with other machines generally requires further
+modification to routing tables and/or ARP (Address Resolution
+Protocol) tables.  In most cases the \fIdefaultroute\fR and/or
+\fIproxyarp\fR options are sufficient for this, but in some cases
+further intervention is required.  The /etc/ppp/ip\-up script can be
+used for this.
+.LP
+Sometimes it is desirable to add a default route through the remote
+host, as in the case of a machine whose only connection to the
+Internet is through the ppp interface.  The \fIdefaultroute\fR option
+causes pppd to create such a default route when IPCP comes up, and
+delete it when the link is terminated.
+.LP
+In some cases it is desirable to use proxy ARP, for example on a
+server machine connected to a LAN, in order to allow other hosts to
+communicate with the remote host.  The \fIproxyarp\fR option causes
+pppd to look for a network interface on the same subnet as the remote
+host (an interface supporting broadcast and ARP, which is up and not a
+point-to-point or loopback interface).  If found, pppd creates a
+permanent, published ARP entry with the IP address of the remote host
+and the hardware address of the network interface found.
+.LP
+When the \fIdemand\fR option is used, the interface IP addresses have
+already been set at the point when IPCP comes up.  If pppd has not
+been able to negotiate the same addresses that it used to configure
+the interface (for example when the peer is an ISP that uses dynamic
+IP address assignment), pppd has to change the interface IP addresses
+to the negotiated addresses.  This may disrupt existing connections,
+and the use of demand dialling with peers that do dynamic IP address
+assignment is not recommended.
+.SH MULTILINK
+Multilink PPP provides the capability to combine two or more PPP links
+between a pair of machines into a single `bundle', which appears as a
+single virtual PPP link which has the combined bandwidth of the
+individual links.  Currently, multilink PPP is only supported under
+Linux.
+.LP
+Pppd detects that the link it is controlling is connected to the same
+peer as another link using the peer's endpoint discriminator and the
+authenticated identity of the peer (if it authenticates itself).  The
+endpoint discriminator is a block of data which is hopefully unique
+for each peer.  Several types of data can be used, including
+locally-assigned strings of bytes, IP addresses, MAC addresses,
+randomly strings of bytes, or E\-164 phone numbers.  The endpoint
+discriminator sent to the peer by pppd can be set using the endpoint
+option.
+.LP
+In some circumstances the peer may send no endpoint discriminator or a
+non-unique value.  The bundle option adds an extra string which is
+added to the peer's endpoint discriminator and authenticated identity
+when matching up links to be joined together in a bundle.  The bundle
+option can also be used to allow the establishment of multiple bundles
+between the local system and the peer.  Pppd uses a TDB database in
+/var/run/pppd2.tdb to match up links.
+.LP
+Assuming that multilink is enabled and the peer is willing to
+negotiate multilink, then when pppd is invoked to bring up the first
+link to the peer, it will detect that no other link is connected to
+the peer and create a new bundle, that is, another ppp network
+interface unit.  When another pppd is invoked to bring up another link
+to the peer, it will detect the existing bundle and join its link to
+it.
+.LP
+If the first link terminates (for example, because of a hangup or a
+received LCP terminate-request) the bundle is not destroyed unless
+there are no other links remaining in the bundle.  Rather than
+exiting, the first pppd keeps running after its link terminates, until
+all the links in the bundle have terminated.  If the first pppd
+receives a SIGTERM or SIGINT signal, it will destroy the bundle and
+send a SIGHUP to the pppd processes for each of the links in the
+bundle.  If the first pppd receives a SIGHUP signal, it will terminate
+its link but not the bundle.
+.LP
+Note: demand mode is not currently supported with multilink.
+.SH EXAMPLES
+.LP
+The following examples assume that the /etc/ppp/options file contains
+the \fIauth\fR option (as in the default /etc/ppp/options file in the
+ppp distribution).
+.LP
+Probably the most common use of pppd is to dial out to an ISP.  This
+can be done with a command such as
+.IP
+pppd call isp
+.LP
+where the /etc/ppp/peers/isp file is set up by the system
+administrator to contain something like this:
+.IP
+ttyS0 19200 crtscts
+.br
+connect '/usr/sbin/chat \-v \-f /etc/ppp/chat\-isp'
+.br
+noauth
+.LP
+In this example, we are using chat to dial the ISP's modem and go
+through any logon sequence required.  The /etc/ppp/chat\-isp file
+contains the script used by chat; it could for example contain
+something like this:
+.IP
+ABORT "NO CARRIER"
+.br
+ABORT "NO DIALTONE"
+.br
+ABORT "ERROR"
+.br
+ABORT "NO ANSWER"
+.br
+ABORT "BUSY"
+.br
+ABORT "Username/Password Incorrect"
+.br
+"" "at"
+.br
+OK "at&d0&c1"
+.br
+OK "atdt2468135"
+.br
+"name:" "^Umyuserid"
+.br
+"word:" "\\qmypassword"
+.br
+"ispts" "\\q^Uppp"
+.br
+"~\-^Uppp\-~"
+.LP
+See the chat(8) man page for details of chat scripts.
+.LP
+Pppd can also be used to provide a dial-in ppp service for users.  If
+the users already have login accounts, the simplest way to set up the
+ppp service is to let the users log in to their accounts and run pppd
+(installed setuid-root) with a command such as
+.IP
+pppd proxyarp
+.LP
+To allow a user to use the PPP facilities, you need to allocate an IP
+address for that user's machine and create an entry in
+/etc/ppp/pap\-secrets, /etc/ppp/chap\-secrets, or /etc/ppp/srp\-secrets
+(depending on which authentication method the PPP implementation on
+the user's machine supports), so that the user's machine can
+authenticate itself.  For example, if Joe has a machine called
+"joespc" that is to be allowed to dial in to the machine called
+"server" and use the IP address joespc.my.net, you would add an entry
+like this to /etc/ppp/pap\-secrets or /etc/ppp/chap\-secrets:
+.IP
+joespc	server	"joe's secret"	joespc.my.net
+.LP
+(See srp\-entry(8) for a means to generate the server's entry when
+SRP\-SHA1 is in use.)
+Alternatively, you can create a username called (for example) "ppp",
+whose login shell is pppd and whose home directory is /etc/ppp.
+Options to be used when pppd is run this way can be put in
+/etc/ppp/.ppprc.
+.LP
+If your serial connection is any more complicated than a piece of
+wire, you may need to arrange for some control characters to be
+escaped.  In particular, it is often useful to escape XON (^Q) and
+XOFF (^S), using \fIasyncmap a0000\fR.  If the path includes a telnet,
+you probably should escape ^] as well (\fIasyncmap 200a0000\fR).  If
+the path includes an rlogin, you will need to use the \fIescape ff\fR
+option on the end which is running the rlogin client, since many
+rlogin implementations are not transparent; they will remove the
+sequence [0xff, 0xff, 0x73, 0x73, followed by any 8 bytes] from the
+stream.
+.SH DIAGNOSTICS
+.LP
+Messages are sent to the syslog daemon using facility LOG_DAEMON.
+(This can be overridden by recompiling pppd with the macro
+LOG_PPP defined as the desired facility.)  See the syslog(8)
+documentation for details of where the syslog daemon will write the
+messages.  On most systems, the syslog daemon uses the
+/etc/syslog.conf file to specify the destination(s) for syslog
+messages.  You may need to edit that file to suit.
+.LP
+The \fIdebug\fR option causes the contents of all control packets sent
+or received to be logged, that is, all LCP, PAP, CHAP, EAP, or IPCP packets.
+This can be useful if the PPP negotiation does not succeed or if
+authentication fails.
+If debugging is enabled at compile time, the \fIdebug\fR option also
+causes other debugging messages to be logged.
+.LP
+Debugging can also be enabled or disabled by sending a SIGUSR1 signal
+to the pppd process.  This signal acts as a toggle.
+.SH EXIT STATUS
+The exit status of pppd is set to indicate whether any error was
+detected, or the reason for the link being terminated.  The values
+used are:
+.TP
+.B 0
+Pppd has detached, or otherwise the connection was successfully
+established and terminated at the peer's request.
+.TP
+.B 1
+An immediately fatal error of some kind occurred, such as an essential
+system call failing, or running out of virtual memory.
+.TP
+.B 2
+An error was detected in processing the options given, such as two
+mutually exclusive options being used.
+.TP
+.B 3
+Pppd is not setuid-root and the invoking user is not root.
+.TP
+.B 4
+The kernel does not support PPP, for example, the PPP kernel driver is
+not included or cannot be loaded.
+.TP
+.B 5
+Pppd terminated because it was sent a SIGINT, SIGTERM or SIGHUP
+signal.
+.TP
+.B 6
+The serial port could not be locked.
+.TP
+.B 7
+The serial port could not be opened.
+.TP
+.B 8
+The connect script failed (returned a non-zero exit status).
+.TP
+.B 9
+The command specified as the argument to the \fIpty\fR option could
+not be run.
+.TP
+.B 10
+The PPP negotiation failed, that is, it didn't reach the point where
+at least one network protocol (e.g. IP) was running.
+.TP
+.B 11
+The peer system failed (or refused) to authenticate itself.
+.TP
+.B 12
+The link was established successfully and terminated because it was
+idle.
+.TP
+.B 13
+The link was established successfully and terminated because the
+connect time limit was reached.
+.TP
+.B 14
+Callback was negotiated and an incoming call should arrive shortly.
+.TP
+.B 15
+The link was terminated because the peer is not responding to echo
+requests.
+.TP
+.B 16
+The link was terminated by the modem hanging up.
+.TP
+.B 17
+The PPP negotiation failed because serial loopback was detected.
+.TP
+.B 18
+The init script failed (returned a non-zero exit status).
+.TP
+.B 19
+We failed to authenticate ourselves to the peer.
+.SH SCRIPTS
+Pppd invokes scripts at various stages in its processing which can be
+used to perform site-specific ancillary processing.  These scripts are
+usually shell scripts, but could be executable code files instead.
+Pppd does not wait for the scripts to finish (except for the ip-pre-up
+script).  The scripts are
+executed as root (with the real and effective user-id set to 0), so
+that they can do things such as update routing tables or run
+privileged daemons.  Be careful that the contents of these scripts do
+not compromise your system's security.  Pppd runs the scripts with
+standard input, output and error redirected to /dev/null, and with an
+environment that is empty except for some environment variables that
+give information about the link.  The environment variables that pppd
+sets are:
+.TP
+.B DEVICE
+The name of the serial tty device being used.
+.TP
+.B IFNAME
+The name of the network interface being used.
+.TP
+.B IPLOCAL
+The IP address for the local end of the link.  This is only set when
+IPCP has come up.
+.TP
+.B IPREMOTE
+The IP address for the remote end of the link.  This is only set when
+IPCP has come up.
+.TP
+.B PEERNAME
+The authenticated name of the peer.  This is only set if the peer
+authenticates itself.
+.TP
+.B SPEED
+The baud rate of the tty device.
+.TP
+.B ORIG_UID
+The real user-id of the user who invoked pppd.
+.TP
+.B PPPLOGNAME
+The username of the real user-id that invoked pppd. This is always set.
+.P
+For the ip-down and auth-down scripts, pppd also sets the following
+variables giving statistics for the connection:
+.TP
+.B CONNECT_TIME
+The number of seconds from when the PPP negotiation started until the
+connection was terminated.
+.TP
+.B BYTES_SENT
+The number of bytes sent (at the level of the serial port) during the
+connection.
+.TP
+.B BYTES_RCVD
+The number of bytes received (at the level of the serial port) during
+the connection.
+.TP
+.B LINKNAME
+The logical name of the link, set with the \fIlinkname\fR option.
+.TP
+.B DNS1
+If the peer supplies DNS server addresses, this variable is set to the
+first DNS server address supplied.
+.TP
+.B DNS2
+If the peer supplies DNS server addresses, this variable is set to the
+second DNS server address supplied.
+.P
+Pppd invokes the following scripts, if they exist.  It is not an error
+if they don't exist.
+.TP
+.B /etc/ppp/auth\-up
+A program or script which is executed after the remote system
+successfully authenticates itself.  It is executed with the parameters
+.IP
+\fIinterface\-name peer\-name user\-name tty\-device speed\fR
+.IP
+Note that this script is not executed if the peer doesn't authenticate
+itself, for example when the \fInoauth\fR option is used.
+.TP
+.B /etc/ppp/auth\-down
+A program or script which is executed when the link goes down, if
+/etc/ppp/auth\-up was previously executed.  It is executed in the same
+manner with the same parameters as /etc/ppp/auth\-up.
+.TP
+.B /etc/ppp/ip\-pre\-up
+A program or script which is executed just before the ppp network
+interface is brought up.  It is executed with the same parameters as
+the ip\-up script (below).  At this point the interface exists and has
+IP addresses assigned but is still down.  This can be used to
+add firewall rules before any IP traffic can pass through the
+interface.  Pppd will wait for this script to finish before bringing
+the interface up, so this script should run quickly.
+.TP
+.B /etc/ppp/ip\-up
+A program or script which is executed when the link is available for
+sending and receiving IP packets (that is, IPCP has come up).  It is
+executed with the parameters
+.IP
+\fIinterface\-name tty\-device speed local\-IP\-address
+remote\-IP\-address ipparam\fR
+.TP
+.B /etc/ppp/ip\-down
+A program or script which is executed when the link is no longer
+available for sending and receiving IP packets.  This script can be
+used for undoing the effects of the /etc/ppp/ip\-up and
+/etc/ppp/ip\-pre\-up scripts.  It is
+invoked in the same manner and with the same parameters as the ip\-up
+script.
+.TP
+.B /etc/ppp/ipv6\-up
+Like /etc/ppp/ip\-up, except that it is executed when the link is available 
+for sending and receiving IPv6 packets. It is executed with the parameters
+.IP
+\fIinterface\-name tty\-device speed local\-link\-local\-address
+remote\-link\-local\-address ipparam\fR
+.TP
+.B /etc/ppp/ipv6\-down
+Similar to /etc/ppp/ip\-down, but it is executed when IPv6 packets can no
+longer be transmitted on the link. It is executed with the same parameters 
+as the ipv6\-up script.
+.TP
+.B /etc/ppp/ipx\-up
+A program or script which is executed when the link is available for
+sending and receiving IPX packets (that is, IPXCP has come up).  It is
+executed with the parameters
+.IP
+\fIinterface\-name tty\-device speed network\-number local\-IPX\-node\-address
+remote\-IPX\-node\-address local\-IPX\-routing\-protocol remote\-IPX\-routing\-protocol
+local\-IPX\-router\-name remote\-IPX\-router\-name ipparam pppd\-pid\fR 
+.IP
+The local\-IPX\-routing\-protocol and remote\-IPX\-routing\-protocol field
+may be one of the following:
+.IP
+NONE      to indicate that there is no routing protocol
+.br
+RIP       to indicate that RIP/SAP should be used
+.br
+NLSP      to indicate that Novell NLSP should be used
+.br
+RIP NLSP  to indicate that both RIP/SAP and NLSP should be used
+.TP
+.B /etc/ppp/ipx\-down
+A program or script which is executed when the link is no longer
+available for sending and receiving IPX packets.  This script can be
+used for undoing the effects of the /etc/ppp/ipx\-up script.  It is
+invoked in the same manner and with the same parameters as the ipx\-up
+script.
+.SH FILES
+.TP
+.B /var/run/ppp\fIn\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp\fIn\fB.pid \fR(others)
+Process-ID for pppd process on ppp interface unit \fIn\fR.
+.TP
+.B /var/run/ppp\-\fIname\fB.pid \fR(BSD or Linux),
+\fB/etc/ppp/ppp\-\fIname\fB.pid \fR(others)
+Process-ID for pppd process for logical link \fIname\fR (see the
+\fIlinkname\fR option).
+.TP
+.B /var/run/pppd2.tdb
+Database containing information about pppd processes, interfaces and
+links, used for matching links to bundles in multilink operation.  May
+be examined by external programs to obtain information about running
+pppd instances, the interfaces and devices they are using, IP address
+assignments, etc.
+.B /etc/ppp/pap\-secrets
+Usernames, passwords and IP addresses for PAP authentication.  This
+file should be owned by root and not readable or writable by any other
+user.  Pppd will log a warning if this is not the case.
+.TP
+.B /etc/ppp/chap\-secrets
+Names, secrets and IP addresses for CHAP/MS\-CHAP/MS\-CHAPv2 authentication.
+As for /etc/ppp/pap\-secrets, this file should be owned by root and not
+readable or writable by any other user.  Pppd will log a warning if
+this is not the case.
+.TP
+.B /etc/ppp/srp\-secrets
+Names, secrets, and IP addresses for EAP authentication.  As for
+/etc/ppp/pap\-secrets, this file should be owned by root and not
+readable or writable by any other user.  Pppd will log a warning if
+this is not the case.
+.TP
+.B ~/.ppp_pseudonym
+Saved client-side SRP\-SHA1 pseudonym.  See the \fIsrp\-use\-pseudonym\fR
+option for details.
+.TP
+.B /etc/ppp/options
+System default options for pppd, read before user default options or
+command-line options.
+.TP
+.B ~/.ppprc
+User default options, read before /etc/ppp/options.\fIttyname\fR.
+.TP
+.B /etc/ppp/options.\fIttyname
+System default options for the serial port being used, read after
+~/.ppprc.  In forming the \fIttyname\fR part of this
+filename, an initial /dev/ is stripped from the port name (if
+present), and any slashes in the remaining part are converted to
+dots.
+.TP
+.B /etc/ppp/peers
+A directory containing options files which may contain privileged
+options, even if pppd was invoked by a user other than root.  The
+system administrator can create options files in this directory to
+permit non-privileged users to dial out without requiring the peer to
+authenticate, but only to certain trusted peers.
+.SH SEE ALSO
+.BR chat (8),
+.BR pppstats (8)
+.TP
+.B RFC1144
+Jacobson, V.
+\fICompressing TCP/IP headers for low-speed serial links.\fR
+February 1990.
+.TP
+.B RFC1321
+Rivest, R.
+.I The MD5 Message-Digest Algorithm.
+April 1992.
+.TP
+.B RFC1332
+McGregor, G.
+.I PPP Internet Protocol Control Protocol (IPCP).
+May 1992.
+.TP
+.B RFC1334
+Lloyd, B.; Simpson, W.A.
+.I PPP authentication protocols.
+October 1992.
+.TP
+.B RFC1661
+Simpson, W.A.
+.I The Point-to-Point Protocol (PPP).
+July 1994.
+.TP
+.B RFC1662
+Simpson, W.A.
+.I PPP in HDLC-like Framing.
+July 1994.
+.TP
+.B RFC2284
+Blunk, L.; Vollbrecht, J.,
+.I PPP Extensible Authentication Protocol (EAP).
+March 1998.
+.TP
+.B RFC2472
+Haskin, D.
+.I IP Version 6 over PPP
+December 1998.
+.TP
+.B RFC2945
+Wu, T.,
+.I The SRP Authentication and Key Exchange System
+September 2000.
+.TP
+.B draft\-ietf\-pppext\-eap\-srp\-03.txt
+Carlson, J.; et al.,
+.I EAP SRP\-SHA1 Authentication Protocol.
+July 2001.
+.SH NOTES
+Some limited degree of control can be exercised over a running pppd
+process by sending it a signal from the list below.
+.TP
+.B SIGINT, SIGTERM
+These signals cause pppd to terminate the link (by closing LCP),
+restore the serial device settings, and exit.  If a connector or
+disconnector process is currently running, pppd will send the same
+signal to its process group, so as to terminate the connector or
+disconnector process.
+.TP
+.B SIGHUP
+This signal causes pppd to terminate the link, restore the serial
+device settings, and close the serial device.  If the \fIpersist\fR or
+\fIdemand\fR option has been specified, pppd will try to reopen the
+serial device and start another connection (after the holdoff period).
+Otherwise pppd will exit.  If this signal is received during the
+holdoff period, it causes pppd to end the holdoff period immediately.
+If a connector or disconnector process is running, pppd will send the
+same signal to its process group.
+.TP
+.B SIGUSR1
+This signal toggles the state of the \fIdebug\fR option.
+.TP
+.B SIGUSR2
+This signal causes pppd to renegotiate compression.  This can be
+useful to re-enable compression after it has been disabled as a result
+of a fatal decompression error.  (Fatal decompression errors generally
+indicate a bug in one or other implementation.)
+
+.SH AUTHORS
+Paul Mackerras (paulus@samba.org), based on earlier work by
+Drew Perkins,
+Brad Clements,
+Karl Fox,
+Greg Christy,
+and
+Brad Parker.
+
+.SH COPYRIGHT
+Pppd is copyrighted and made available under conditions which provide
+that it may be copied and used in source or binary forms provided that
+the conditions listed below are met.  Portions of pppd are covered by
+the following copyright notices:
+.LP
+Copyright (c) 1984-2000 Carnegie Mellon University. All rights
+reserved.
+.br
+Copyright (c) 1993-2004 Paul Mackerras. All rights reserved.
+.br
+Copyright (c) 1995 Pedro Roque Marques.  All rights reserved.
+.br
+Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+.br
+Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+.br
+Copyright (C) Andrew Tridgell 1999
+.br
+Copyright (c) 2000 by Sun Microsystems, Inc.  All rights reserved.
+.br
+Copyright (c) 2001 by Sun Microsystems, Inc.  All rights reserved.
+.br
+Copyright (c) 2002 Google, Inc.  All rights reserved.
+.LP
+The copyright notices contain the following statements.
+.LP
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+.LP
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+.LP
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+.LP
+3. The name "Carnegie Mellon University" must not be used to
+   endorse or promote products derived from this software without
+   prior written permission. For permission or any legal
+   details, please contact
+.br
+     Office of Technology Transfer
+.br
+     Carnegie Mellon University
+.br
+     5000 Forbes Avenue
+.br
+     Pittsburgh, PA  15213-3890
+.br
+     (412) 268-4387, fax: (412) 268-7395
+.br
+     tech-transfer@andrew.cmu.edu
+.LP
+3b. The name(s) of the authors of this software must not be used to
+   endorse or promote products derived from this software without
+   prior written permission.
+.LP
+4. Redistributions of any form whatsoever must retain the following
+   acknowledgments:
+.br
+   "This product includes software developed by Computing Services
+    at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+.br
+   "This product includes software developed by Paul Mackerras
+    <paulus@samba.org>".
+.br
+   "This product includes software developed by Pedro Roque Marques
+    <pedro_m@yahoo.com>".
+.br
+   "This product includes software developed by Tommi Komulainen
+    <Tommi.Komulainen@iki.fi>".
+.LP
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.LP
+THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/ap/app/pppd/pppd/pppd.h b/ap/app/pppd/pppd/pppd.h
new file mode 100644
index 0000000..642ba1d
--- /dev/null
+++ b/ap/app/pppd/pppd/pppd.h
@@ -0,0 +1,934 @@
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: pppd.h,v 1.96 2008/06/23 11:47:18 paulus Exp $
+ */
+
+/*
+ * TODO:
+ */
+
+#ifndef __PPPD_H__
+#define __PPPD_H__
+
+#include <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"
+
+#include "../../../include/softap_api.h"
+//#include "../../../include/netapi.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;
+	unsigned int flags;
+	void	*addr2;
+	int	upper_limit;
+	int	lower_limit;
+	const char *source;
+	short int priority;
+	short int winner;
+} option_t;
+
+/* Values for flags */
+#define OPT_VALUE	0xff	/* mask for presupplied value */
+#define OPT_HEX		0x100	/* int option is in hex */
+#define OPT_NOARG	0x200	/* option doesn't take argument */
+#define OPT_OR		0x400	/* for u32, OR in argument to value */
+#define OPT_INC		0x400	/* for o_int, increment value */
+#define OPT_A2OR	0x800	/* for o_bool, OR arg to *(u_char *)addr2 */
+#define OPT_PRIV	0x1000	/* privileged option */
+#define OPT_STATIC	0x2000	/* string option goes into static array */
+#define OPT_NOINCR	0x2000	/* for o_int, value mustn't be increased */
+#define OPT_LLIMIT	0x4000	/* check value against lower limit */
+#define OPT_ULIMIT	0x8000	/* check value against upper limit */
+#define OPT_LIMITS	(OPT_LLIMIT|OPT_ULIMIT)
+#define OPT_ZEROOK	0x10000	/* 0 value is OK even if not within limits */
+#define OPT_HIDE	0x10000	/* for o_string, print value as ?????? */
+#define OPT_A2LIST	0x20000 /* for o_special, keep list of values */
+#define OPT_A2CLRB	0x20000 /* o_bool, clr val bits in *(u_char *)addr2 */
+#define OPT_ZEROINF	0x40000	/* with OPT_NOINCR, 0 == infinity */
+#define OPT_PRIO	0x80000	/* process option priorities for this option */
+#define OPT_PRIOSUB	0x100000 /* subsidiary member of priority group */
+#define OPT_ALIAS	0x200000 /* option is alias for previous option */
+#define OPT_A2COPY	0x400000 /* addr2 -> second location to rcv value */
+#define OPT_ENABLE	0x800000 /* use *addr2 as enable for option */
+#define OPT_A2CLR	0x1000000 /* clear *(bool *)addr2 */
+#define OPT_PRIVFIX	0x2000000 /* user can't override if set by root */
+#define OPT_INITONLY	0x4000000 /* option can only be set in init phase */
+#define OPT_DEVEQUIV	0x8000000 /* equiv to device name */
+#define OPT_DEVNAM	(OPT_INITONLY | OPT_DEVEQUIV)
+#define OPT_A2PRINTER	0x10000000 /* *addr2 is a fn for printing option */
+#define OPT_A2STRVAL	0x20000000 /* *addr2 points to current string value */
+#define OPT_NOPRINT	0x40000000 /* don't print this option at all */
+
+#define OPT_VAL(x)	((x) & OPT_VALUE)
+
+/* Values for priority */
+#define OPRIO_DEFAULT	0	/* a default value */
+#define OPRIO_CFGFILE	1	/* value from a configuration file */
+#define OPRIO_CMDLINE	2	/* value from the command line */
+#define OPRIO_SECFILE	3	/* value from options in a secrets file */
+#define OPRIO_ROOT	100	/* added to priority if OPT_PRIVFIX && root */
+
+#ifndef GIDSET_TYPE
+#define GIDSET_TYPE	gid_t
+#endif
+
+/* Structure representing a list of permitted IP addresses. */
+struct permitted_ip {
+    int		permit;		/* 1 = permit, 0 = forbid */
+    u_int32_t	base;		/* match if (addr & mask) == base */
+    u_int32_t	mask;		/* base and mask are in network byte order */
+};
+
+/*
+ * Unfortunately, the linux kernel driver uses a different structure
+ * for statistics from the rest of the ports.
+ * This structure serves as a common representation for the bits
+ * pppd needs.
+ */
+struct pppd_stats {
+    unsigned int	bytes_in;
+    unsigned int	bytes_out;
+    unsigned int	pkts_in;
+    unsigned int	pkts_out;
+};
+
+/* Used for storing a sequence of words.  Usually malloced. */
+struct wordlist {
+    struct wordlist	*next;
+    char		*word;
+};
+
+/* An endpoint discriminator, used with multilink. */
+#define MAX_ENDP_LEN	20	/* maximum length of discriminator value */
+struct epdisc {
+    unsigned char	class;
+    unsigned char	length;
+    unsigned char	value[MAX_ENDP_LEN];
+};
+
+/* values for epdisc.class */
+#define EPD_NULL	0	/* null discriminator, no data */
+#define EPD_LOCAL	1
+#define EPD_IP		2
+#define EPD_MAC		3
+#define EPD_MAGIC	4
+#define EPD_PHONENUM	5
+
+typedef void (*notify_func) __P((void *, int));
+
+struct notifier {
+    struct notifier *next;
+    notify_func	    func;
+    void	    *arg;
+};
+
+/*
+ * Global variables.
+ */
+
+extern int	hungup;		/* Physical layer has disconnected */
+extern int	ifunit;		/* Interface unit number */
+extern char	ifname[];	/* Interface name */
+extern char	hostname[];	/* Our hostname */
+extern u_char	outpacket_buf[]; /* Buffer for outgoing packets */
+extern int	devfd;		/* fd of underlying device */
+extern int	fd_ppp;		/* fd for talking PPP */
+extern int	phase;		/* Current state of link - see values below */
+extern int	baud_rate;	/* Current link speed in bits/sec */
+extern char	*progname;	/* Name of this program */
+extern int	redirect_stderr;/* Connector's stderr should go to file */
+extern char	peer_authname[];/* Authenticated name of peer */
+extern int	auth_done[NUM_PPP]; /* Methods actually used for auth */
+extern int	privileged;	/* We were run by real-uid root */
+extern int	need_holdoff;	/* Need holdoff period after link terminates */
+extern char	**script_env;	/* Environment variables for scripts */
+extern int	detached;	/* Have detached from controlling tty */
+extern GIDSET_TYPE groups[];	/* groups the user is in */
+extern int	ngroups;	/* How many groups valid in groups */
+extern struct pppd_stats link_stats; /* byte/packet counts etc. for link */
+extern int	link_stats_valid; /* set if link_stats is valid */
+extern unsigned	link_connect_time; /* time the link was up for */
+extern int	using_pty;	/* using pty as device (notty or pty opt.) */
+extern int	log_to_fd;	/* logging to this fd as well as syslog */
+extern bool	log_default;	/* log_to_fd is default (stdout) */
+extern char	*no_ppp_msg;	/* message to print if ppp not in kernel */
+extern volatile int status;	/* exit status for pppd */
+extern bool	devnam_fixed;	/* can no longer change devnam */
+extern int	unsuccess;	/* # unsuccessful connection attempts */
+extern int	do_callback;	/* set if we want to do callback next */
+extern int	doing_callback;	/* set if this is a callback */
+extern int	error_count;	/* # of times error() has been called */
+extern char	ppp_devnam[MAXPATHLEN];
+extern char     remote_number[MAXNAMELEN]; /* Remote telephone number, if avail. */
+extern int      ppp_session_number; /* Session number (eg PPPoE session) */
+extern int	fd_devnull;	/* fd open to /dev/null */
+
+extern int	listen_time;	/* time to listen first (ms) */
+extern bool	doing_multilink;
+extern bool	multilink_master;
+extern bool	bundle_eof;
+extern bool	bundle_terminating;
+extern bool external_auth; /* Set if we're using an external authenticator 
+				  			(radius, tacas etc) */
+
+extern struct notifier *pidchange;   /* for notifications of pid changing */
+extern struct notifier *phasechange; /* for notifications of phase changes */
+extern struct notifier *exitnotify;  /* for notification that we're exiting */
+extern struct notifier *sigreceived; /* notification of received signal */
+extern struct notifier *ip_up_notifier; /* IPCP has come up */
+extern struct notifier *ip_down_notifier; /* IPCP has gone down */
+extern struct notifier *auth_up_notifier; /* peer has authenticated */
+extern struct notifier *link_down_notifier; /* link has gone down */
+extern struct notifier *fork_notifier;	/* we are a new child process */
+
+/* Values for do_callback and doing_callback */
+#define CALLBACK_DIALIN		1	/* we are expecting the call back */
+#define CALLBACK_DIALOUT	2	/* we are dialling out to call back */
+
+/*
+ * Variables set by command-line options.
+ */
+
+extern int	debug;		/* Debug flag */
+extern int	kdebugflag;	/* Tell kernel to print debug messages */
+extern int	default_device;	/* Using /dev/tty or equivalent */
+extern char	devnam[MAXPATHLEN];	/* Device name */
+extern int	crtscts;	/* Use hardware flow control */
+extern bool	modem;		/* Use modem control lines */
+extern int	inspeed;	/* Input/Output speed requested */
+extern u_int32_t netmask;	/* IP netmask to set on interface */
+extern bool	lockflag;	/* Create lock file to lock the serial dev */
+extern bool	nodetach;	/* Don't detach from controlling tty */
+extern bool	updetach;	/* Detach from controlling tty when link up */
+extern char	*initializer;	/* Script to initialize physical link */
+extern char	*connect_script; /* Script to establish physical link */
+extern char	*disconnect_script; /* Script to disestablish physical link */
+extern char	*welcomer;	/* Script to welcome client after connection */
+extern char	*ptycommand;	/* Command to run on other side of pty */
+extern int	maxconnect;	/* Maximum connect time (seconds) */
+extern char	user[MAXNAMELEN];/* Our name for authenticating ourselves */
+extern char	passwd[MAXSECRETLEN];	/* Password for PAP or CHAP */
+extern bool	auth_required;	/* Peer is required to authenticate */
+extern bool	persist;	/* Reopen link after it goes down */
+extern bool	uselogin;	/* Use /etc/passwd for checking PAP */
+extern bool	session_mgmt;	/* Do session management (login records) */
+extern char	our_name[MAXNAMELEN];/* Our name for authentication purposes */
+extern char	remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+extern bool	explicit_remote;/* remote_name specified with remotename opt */
+extern const char *auth_group;	/* group provided by authenticator */
+extern bool	demand;		/* Do dial-on-demand */
+extern char	*ip_up;		/* user defined ip-up script */
+extern char	*ip_down;	/* user defined ip-down script */
+extern char	*ipparam;	/* Extra parameter for ip up/down scripts */
+extern bool	cryptpap;	/* Others' PAP passwords are encrypted */
+extern int	idle_time_limit;/* Shut down link if idle for this long */
+extern int	holdoff;	/* Dead time before restarting */
+extern bool	holdoff_specified; /* true if user gave a holdoff value */
+extern bool	notty;		/* Stdin/out is not a tty */
+extern char	*pty_socket;	/* Socket to connect to pty */
+extern char	*record_file;	/* File to record chars sent/received */
+extern bool	sync_serial;	/* Device is synchronous serial device */
+extern int	maxfail;	/* Max # of unsuccessful connection attempts */
+extern char	linkname[MAXPATHLEN]; /* logical name for link */
+extern bool	tune_kernel;	/* May alter kernel settings as necessary */
+extern int	connect_delay;	/* Time to delay after connect script */
+extern int	max_data_rate;	/* max bytes/sec through charshunt */
+extern int	req_unit;	/* interface unit number to use */
+extern bool	multilink;	/* enable multilink operation */
+extern bool	noendpoint;	/* don't send or accept endpt. discrim. */
+extern char	*bundle_name;	/* bundle name for multilink */
+extern bool	dump_options;	/* print out option values */
+extern bool	dryrun;		/* check everything, print options, exit */
+extern int	child_wait;	/* # seconds to wait for children at end */
+
+extern u_int32_t	metric;	/* the metric to set the host route to */
+extern u_int32_t	drmetric;	/* the default route metric to set */
+extern char	pid_file[MAXNAMELEN];	/* name of our pid file */
+
+#ifdef MAXOCTETS
+extern unsigned int maxoctets;	     /* Maximum octetes per session (in bytes) */
+extern int       maxoctets_dir;      /* Direction :
+				      0 - in+out (default)
+				      1 - in 
+				      2 - out
+				      3 - max(in,out) */
+extern int       maxoctets_timeout;  /* Timeout for check of octets limit */
+#define PPP_OCTETS_DIRECTION_SUM        0
+#define PPP_OCTETS_DIRECTION_IN         1
+#define PPP_OCTETS_DIRECTION_OUT        2
+#define PPP_OCTETS_DIRECTION_MAXOVERAL  3
+/* same as previos, but little different on RADIUS side */
+#define PPP_OCTETS_DIRECTION_MAXSESSION 4	
+#endif
+
+#ifdef PPP_FILTER
+extern struct	bpf_program pass_filter;   /* Filter for pkts to pass */
+extern struct	bpf_program active_filter; /* Filter for link-active pkts */
+#endif
+
+#ifdef MSLANMAN
+extern bool	ms_lanman;	/* Use LanMan password instead of NT */
+				/* Has meaning only with MS-CHAP challenges */
+#endif
+
+/* Values for auth_pending, auth_done */
+#define PAP_WITHPEER	0x1
+#define PAP_PEER	0x2
+#define CHAP_WITHPEER	0x4
+#define CHAP_PEER	0x8
+#define EAP_WITHPEER	0x10
+#define EAP_PEER	0x20
+
+/* Values for auth_done only */
+#define CHAP_MD5_WITHPEER	0x40
+#define CHAP_MD5_PEER		0x80
+#define CHAP_MS_SHIFT		8	/* LSB position for MS auths */
+#define CHAP_MS_WITHPEER	0x100
+#define CHAP_MS_PEER		0x200
+#define CHAP_MS2_WITHPEER	0x400
+#define CHAP_MS2_PEER		0x800
+
+extern char *current_option;	/* the name of the option being parsed */
+extern int  privileged_option;	/* set iff the current option came from root */
+extern char *option_source;	/* string saying where the option came from */
+extern int  option_priority;	/* priority of current options */
+
+/*
+ * Values for phase.
+ */
+#define PHASE_DEAD		0
+#define PHASE_INITIALIZE	1
+#define PHASE_SERIALCONN	2
+#define PHASE_DORMANT		3
+#define PHASE_ESTABLISH		4
+#define PHASE_AUTHENTICATE	5
+#define PHASE_CALLBACK		6
+#define PHASE_NETWORK		7
+#define PHASE_RUNNING		8
+#define PHASE_TERMINATE		9
+#define PHASE_DISCONNECT	10
+#define PHASE_HOLDOFF		11
+#define PHASE_MASTER		12
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular protocol.
+ */
+struct protent {
+    u_short protocol;		/* PPP protocol number */
+    /* Initialization procedure */
+    void (*init) __P((int unit));
+    /* Process a received packet */
+    void (*input) __P((int unit, u_char *pkt, int len));
+    /* Process a received protocol-reject */
+    void (*protrej) __P((int unit));
+    /* Lower layer has come up */
+    void (*lowerup) __P((int unit));
+    /* Lower layer has gone down */
+    void (*lowerdown) __P((int unit));
+    /* Open the protocol */
+    void (*open) __P((int unit));
+    /* Close the protocol */
+    void (*close) __P((int unit, char *reason));
+    /* Print a packet in readable form */
+    int  (*printpkt) __P((u_char *pkt, int len,
+			  void (*printer) __P((void *, char *, ...)),
+			  void *arg));
+    /* Process a received data packet */
+    void (*datainput) __P((int unit, u_char *pkt, int len));
+    bool enabled_flag;		/* 0 iff protocol is disabled */
+    char *name;			/* Text name of protocol */
+    char *data_name;		/* Text name of corresponding data protocol */
+    option_t *options;		/* List of command-line options */
+    /* Check requested options, assign defaults */
+    void (*check_options) __P((void));
+    /* Configure interface for demand-dial */
+    int  (*demand_conf) __P((int unit));
+    /* Say whether to bring up link for this pkt */
+    int  (*active_pkt) __P((u_char *pkt, int len));
+};
+
+/* Table of pointers to supported protocols */
+extern struct protent *protocols[];
+
+/*
+ * This struct contains pointers to a set of procedures for
+ * doing operations on a "channel".  A channel provides a way
+ * to send and receive PPP packets - the canonical example is
+ * a serial port device in PPP line discipline (or equivalently
+ * with PPP STREAMS modules pushed onto it).
+ */
+struct channel {
+	/* set of options for this channel */
+	option_t *options;
+	/* find and process a per-channel options file */
+	void (*process_extra_options) __P((void));
+	/* check all the options that have been given */
+	void (*check_options) __P((void));
+	/* get the channel ready to do PPP, return a file descriptor */
+	int  (*connect) __P((void));
+	/* we're finished with the channel */
+	void (*disconnect) __P((void));
+	/* put the channel into PPP `mode' */
+	int  (*establish_ppp) __P((int));
+	/* take the channel out of PPP `mode', restore loopback if demand */
+	void (*disestablish_ppp) __P((int));
+	/* set the transmit-side PPP parameters of the channel */
+	void (*send_config) __P((int, u_int32_t, int, int));
+	/* set the receive-side PPP parameters of the channel */
+	void (*recv_config) __P((int, u_int32_t, int, int));
+	/* cleanup on error or normal exit */
+	void (*cleanup) __P((void));
+	/* close the device, called in children after fork */
+	void (*close) __P((void));
+};
+
+extern struct channel *the_channel;
+
+/*
+ * Prototypes.
+ */
+
+/* Procedures exported from main.c. */
+void set_ifunit __P((int));	/* set stuff that depends on ifunit */
+void detach __P((void));	/* Detach from controlling tty */
+void die __P((int));		/* Cleanup and exit */
+void quit __P((void));		/* like die(1) */
+void novm __P((char *));	/* Say we ran out of memory, and die */
+void timeout __P((void (*func)(void *), void *arg, int s, int us));
+				/* Call func(arg) after s.us seconds */
+void untimeout __P((void (*func)(void *), void *arg));
+				/* Cancel call to func(arg) */
+void record_child __P((int, char *, void (*) (void *), void *, int));
+pid_t safe_fork __P((int, int, int));	/* Fork & close stuff in child */
+int  device_script __P((char *cmd, int in, int out, int dont_wait));
+				/* Run `cmd' with given stdin and stdout */
+pid_t run_program __P((char *prog, char **args, int must_exist,
+		       void (*done)(void *), void *arg, int wait));
+				/* Run program prog with args in child */
+void reopen_log __P((void));	/* (re)open the connection to syslog */
+void print_link_stats __P((void)); /* Print stats, if available */
+void reset_link_stats __P((int)); /* Reset (init) stats when link goes up */
+void update_link_stats __P((int)); /* Get stats at link termination */
+void script_setenv __P((char *, char *, int));	/* set script env var */
+void script_unsetenv __P((char *));		/* unset script env var */
+void new_phase __P((int));	/* signal start of new phase */
+void add_notifier __P((struct notifier **, notify_func, void *));
+void remove_notifier __P((struct notifier **, notify_func, void *));
+void notify __P((struct notifier *, int));
+int  ppp_send_config __P((int, int, u_int32_t, int, int));
+int  ppp_recv_config __P((int, int, u_int32_t, int, int));
+const char *protocol_name __P((int));
+void remove_pidfiles __P((void));
+void lock_db __P((void));
+void unlock_db __P((void));
+
+/* Procedures exported from tty.c. */
+void tty_init __P((void));
+
+/* Procedures exported from utils.c. */
+void log_packet __P((u_char *, int, char *, int));
+				/* Format a packet and log it with syslog */
+void print_string __P((char *, int,  void (*) (void *, char *, ...),
+		void *));	/* Format a string for output */
+int slprintf __P((char *, int, char *, ...));		/* sprintf++ */
+int vslprintf __P((char *, int, char *, va_list));	/* vsprintf++ */
+size_t strlcpy __P((char *, const char *, size_t));	/* safe strcpy */
+size_t strlcat __P((char *, const char *, size_t));	/* safe strncpy */
+void dbglog __P((char *, ...));	/* log a debug message */
+void info __P((char *, ...));	/* log an informational message */
+void notice __P((char *, ...));	/* log a notice-level message */
+void warn __P((char *, ...));	/* log a warning message */
+void error __P((char *, ...));	/* log an error message */
+void fatal __P((char *, ...));	/* log an error message and die(1) */
+void init_pr_log __P((const char *, int)); /* initialize for using pr_log */
+void pr_log __P((void *, char *, ...));	/* printer fn, output to syslog */
+void end_pr_log __P((void));	/* finish up after using pr_log */
+void dump_packet __P((const char *, u_char *, int));
+				/* dump packet to debug log if interesting */
+ssize_t complete_read __P((int, void *, size_t));
+				/* read a complete buffer */
+
+/* Procedures exported from auth.c */
+void link_required __P((int));	  /* we are starting to use the link */
+void start_link __P((int));	  /* bring the link up now */
+void link_terminated __P((int));  /* we are finished with the link */
+void link_down __P((int));	  /* the LCP layer has left the Opened state */
+void upper_layers_down __P((int));/* take all NCPs down */
+void link_established __P((int)); /* the link is up; authenticate now */
+void start_networks __P((int));   /* start all the network control protos */
+void continue_networks __P((int)); /* start network [ip, etc] control protos */
+void np_up __P((int, int));	  /* a network protocol has come up */
+void np_down __P((int, int));	  /* a network protocol has gone down */
+void np_finished __P((int, int)); /* a network protocol no longer needs link */
+void auth_peer_fail __P((int, int));
+				/* peer failed to authenticate itself */
+void auth_peer_success __P((int, int, int, char *, int));
+				/* peer successfully authenticated itself */
+void auth_withpeer_fail __P((int, int));
+				/* we failed to authenticate ourselves */
+void auth_withpeer_success __P((int, int, int));
+				/* we successfully authenticated ourselves */
+void auth_check_options __P((void));
+				/* check authentication options supplied */
+void auth_reset __P((int));	/* check what secrets we have */
+int  check_passwd __P((int, char *, int, char *, int, char **));
+				/* Check peer-supplied username/password */
+int  get_secret __P((int, char *, char *, char *, int *, int));
+				/* get "secret" for chap */
+int  get_srp_secret __P((int unit, char *client, char *server, char *secret,
+    int am_server));
+int  auth_ip_addr __P((int, u_int32_t));
+				/* check if IP address is authorized */
+int  auth_number __P((void));	/* check if remote number is authorized */
+int  bad_ip_adrs __P((u_int32_t));
+				/* check if IP address is unreasonable */
+#ifdef USE_PAM
+int check_pam_account_restrictions __P((const char *));
+				/* check PAM for user account restrictions */
+#endif
+#ifdef USE_EXTERNAL_STATS_PROG
+void notify_login_failure __P((const char *));
+                                /* Notify statsd of any failures */
+#endif
+
+/* Procedures exported from demand.c */
+void demand_conf __P((void));	/* config interface(s) for demand-dial */
+void demand_block __P((void));	/* set all NPs to queue up packets */
+void demand_unblock __P((void)); /* set all NPs to pass packets */
+void demand_discard __P((void)); /* set all NPs to discard packets */
+void demand_rexmit __P((int));	/* retransmit saved frames for an NP */
+int  loop_chars __P((unsigned char *, int)); /* process chars from loopback */
+int  loop_frame __P((unsigned char *, int)); /* should we bring link up? */
+
+/* Procedures exported from multilink.c */
+#ifdef HAVE_MULTILINK
+void mp_check_options __P((void)); /* Check multilink-related options */
+int  mp_join_bundle __P((void));  /* join our link to an appropriate bundle */
+void mp_exit_bundle __P((void));  /* have disconnected our link from bundle */
+void mp_bundle_terminated __P((void));
+char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */
+int  str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */
+#else
+#define mp_bundle_terminated()	/* nothing */
+#define mp_exit_bundle()	/* nothing */
+#define doing_multilink		0
+#define multilink_master	0
+#endif
+
+/* Procedures exported from sys-*.c */
+void sys_init __P((void));	/* Do system-dependent initialization */
+void sys_cleanup __P((void));	/* Restore system state before exiting */
+int  sys_check_options __P((void)); /* Check options specified */
+void sys_close __P((void));	/* Clean up in a child before execing */
+int  ppp_available __P((void));	/* Test whether ppp kernel support exists */
+int  get_pty __P((int *, int *, char *, int));	/* Get pty master/slave */
+int  open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */
+int  tty_establish_ppp __P((int));  /* Turn serial port into a ppp interface */
+void tty_disestablish_ppp __P((int)); /* Restore port to normal operation */
+void generic_disestablish_ppp __P((int dev_fd)); /* Restore device setting */
+int  generic_establish_ppp __P((int dev_fd)); /* Make a ppp interface */
+void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
+int  bundle_attach __P((int));	/* Attach link to existing bundle */
+void cfg_bundle __P((int, int, int, int)); /* Configure existing bundle */
+void destroy_bundle __P((void)); /* Tell driver to destroy bundle */
+void clean_check __P((void));	/* Check if line was 8-bit clean */
+void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */
+void restore_tty __P((int));	/* Restore port's original parameters */
+void setdtr __P((int, int));	/* Raise or lower port's DTR line */
+void output __P((int, u_char *, int)); /* Output a PPP packet */
+void wait_input __P((struct timeval *));
+				/* Wait for input, with timeout */
+void add_fd __P((int));		/* Add fd to set to wait for */
+void remove_fd __P((int));	/* Remove fd from set to wait for */
+int  read_packet __P((u_char *)); /* Read PPP packet */
+int  get_loop_output __P((void)); /* Read pkts from loopback */
+void tty_send_config __P((int, u_int32_t, int, int));
+				/* Configure i/f transmit parameters */
+void tty_set_xaccm __P((ext_accm));
+				/* Set extended transmit ACCM */
+void tty_recv_config __P((int, u_int32_t, int, int));
+				/* Configure i/f receive parameters */
+int  ccp_test __P((int, u_char *, int, int));
+				/* Test support for compression scheme */
+void ccp_flags_set __P((int, int, int));
+				/* Set kernel CCP state */
+int  ccp_fatal_error __P((int)); /* Test for fatal decomp error in kernel */
+int  get_idle_time __P((int, struct ppp_idle *));
+				/* Find out how long link has been idle */
+int  get_ppp_stats __P((int, struct pppd_stats *));
+				/* Return link statistics */
+void netif_set_mtu __P((int, int)); /* Set PPP interface MTU */
+int  netif_get_mtu __P((int));      /* Get PPP interface MTU */
+int  sifvjcomp __P((int, int, int, int));
+				/* Configure VJ TCP header compression */
+int  sifup __P((int));		/* Configure i/f up for one protocol */
+int  sifnpmode __P((int u, int proto, enum NPmode mode));
+				/* Set mode for handling packets for proto */
+int  sifdown __P((int));	/* Configure i/f down for one protocol */
+int  sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t));
+				/* Configure IPv4 addresses for i/f */
+int  cifaddr __P((int, u_int32_t, u_int32_t));
+				/* Reset i/f IP addresses */
+#ifdef INET6
+int  sif6addr __P((int, eui64_t, eui64_t));
+				/* Configure IPv6 addresses for i/f */
+int  cif6addr __P((int, eui64_t, eui64_t));
+				/* Remove an IPv6 address from i/f */
+#endif
+int  sifdefaultroute __P((int, u_int32_t, u_int32_t, u_int32_t, bool));
+				/* Create default route through i/f */
+int  cifdefaultroute __P((int, u_int32_t, u_int32_t));
+				/* Delete default route through i/f */
+int  rtmetricfixup __P((int, u_int32_t, u_int32_t));
+int  sifproxyarp __P((int, u_int32_t));
+				/* Add proxy ARP entry for peer */
+int  cifproxyarp __P((int, u_int32_t));
+				/* Delete proxy ARP entry for peer */
+u_int32_t GetMask __P((u_int32_t)); /* Get appropriate netmask for address */
+int  lock __P((char *));	/* Create lock file for device */
+int  relock __P((int));		/* Rewrite lock file with new pid */
+void unlock __P((void));	/* Delete previously-created lock file */
+void logwtmp __P((const char *, const char *, const char *));
+				/* Write entry to wtmp file */
+int  get_host_seed __P((void));	/* Get host-dependent random number seed */
+int  have_route_to __P((u_int32_t)); /* Check if route to addr exists */
+#ifdef PPP_FILTER
+int  set_filters __P((struct bpf_program *pass, struct bpf_program *active));
+				/* Set filter programs in kernel */
+#endif
+#ifdef IPX_CHANGE
+int  sipxfaddr __P((int, unsigned long, unsigned char *));
+int  cipxfaddr __P((int));
+#endif
+int  get_if_hwaddr __P((u_char *addr, char *name));
+char *get_first_ethernet __P((void));
+
+/* Procedures exported from options.c */
+int setipaddr __P((char *, char **, int)); /* Set local/remote ip addresses */
+int  parse_args __P((int argc, char **argv));
+				/* Parse options from arguments given */
+int  options_from_file __P((char *filename, int must_exist, int check_prot,
+			    int privileged));
+				/* Parse options from an options file */
+int  options_from_user __P((void)); /* Parse options from user's .ppprc */
+int  options_for_tty __P((void)); /* Parse options from /etc/ppp/options.tty */
+int  options_from_list __P((struct wordlist *, int privileged));
+				/* Parse options from a wordlist */
+int  getword __P((FILE *f, char *word, int *newlinep, char *filename));
+				/* Read a word from a file */
+void option_error __P((char *fmt, ...));
+				/* Print an error message about an option */
+int int_option __P((char *, int *));
+				/* Simplified number_option for decimal ints */
+void add_options __P((option_t *)); /* Add extra options */
+void check_options __P((void));	/* check values after all options parsed */
+int  override_value __P((const char *, int, const char *));
+				/* override value if permitted by priority */
+void print_options __P((void (*) __P((void *, char *, ...)), void *));
+				/* print out values of all options */
+
+int parse_dotted_ip __P((char *, u_int32_t *));
+
+/*
+ * Hooks to enable plugins to change various things.
+ */
+extern int (*new_phase_hook) __P((int));
+extern int (*idle_time_hook) __P((struct ppp_idle *));
+extern int (*holdoff_hook) __P((void));
+extern int (*pap_check_hook) __P((void));
+extern int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+				 struct wordlist **paddrs,
+				 struct wordlist **popts));
+extern void (*pap_logout_hook) __P((void));
+extern int (*pap_passwd_hook) __P((char *user, char *passwd));
+extern int (*allowed_address_hook) __P((u_int32_t addr));
+extern void (*ip_up_hook) __P((void));
+extern void (*ip_down_hook) __P((void));
+extern void (*ip_choose_hook) __P((u_int32_t *));
+
+extern int (*chap_check_hook) __P((void));
+extern int (*chap_passwd_hook) __P((char *user, char *passwd));
+extern void (*multilink_join_hook) __P((void));
+
+/* Let a plugin snoop sent and received packets.  Useful for L2TP */
+extern void (*snoop_recv_hook) __P((unsigned char *p, int len));
+extern void (*snoop_send_hook) __P((unsigned char *p, int len));
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+	(c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+	*(cp)++ = (u_char) (c); \
+}
+
+
+#define GETSHORT(s, cp) { \
+	(s) = *(cp)++ << 8; \
+	(s) |= *(cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+	*(cp)++ = (u_char) ((s) >> 8); \
+	*(cp)++ = (u_char) (s); \
+}
+
+#define GETLONG(l, cp) { \
+	(l) = *(cp)++ << 8; \
+	(l) |= *(cp)++; (l) <<= 8; \
+	(l) |= *(cp)++; (l) <<= 8; \
+	(l) |= *(cp)++; \
+}
+#define PUTLONG(l, cp) { \
+	*(cp)++ = (u_char) ((l) >> 24); \
+	*(cp)++ = (u_char) ((l) >> 16); \
+	*(cp)++ = (u_char) ((l) >> 8); \
+	*(cp)++ = (u_char) (l); \
+}
+
+#define INCPTR(n, cp)	((cp) += (n))
+#define DECPTR(n, cp)	((cp) -= (n))
+
+/*
+ * System dependent definitions for user-level 4.3BSD UNIX implementation.
+ */
+
+#define TIMEOUT(r, f, t)	timeout((r), (f), (t), 0)
+#define UNTIMEOUT(r, f)		untimeout((r), (f))
+
+#define BCOPY(s, d, l)		memcpy(d, s, l)
+#define BZERO(s, n)		memset(s, 0, n)
+#define	BCMP(s1, s2, l)		memcmp(s1, s2, l)
+
+#define PRINTMSG(m, l)		{ info("Remote message: %0.*v", l, m); }
+
+/*
+ * MAKEHEADER - Add Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+    PUTCHAR(PPP_ALLSTATIONS, p); \
+    PUTCHAR(PPP_UI, p); \
+    PUTSHORT(t, p); }
+
+/*
+ * Exit status values.
+ */
+#define EXIT_OK			0
+#define EXIT_FATAL_ERROR	1
+#define EXIT_OPTION_ERROR	2
+#define EXIT_NOT_ROOT		3
+#define EXIT_NO_KERNEL_SUPPORT	4
+#define EXIT_USER_REQUEST	5
+#define EXIT_LOCK_FAILED	6
+#define EXIT_OPEN_FAILED	7
+#define EXIT_CONNECT_FAILED	8
+#define EXIT_PTYCMD_FAILED	9
+#define EXIT_NEGOTIATION_FAILED	10
+#define EXIT_PEER_AUTH_FAILED	11
+#define EXIT_IDLE_TIMEOUT	12
+#define EXIT_CONNECT_TIME	13
+#define EXIT_CALLBACK		14
+#define EXIT_PEER_DEAD		15
+#define EXIT_HANGUP		16
+#define EXIT_LOOPBACK		17
+#define EXIT_INIT_FAILED	18
+#define EXIT_AUTH_TOPEER_FAILED	19
+#ifdef MAXOCTETS
+#define EXIT_TRAFFIC_LIMIT	20
+#endif
+#define EXIT_CNID_AUTH_FAILED	21
+
+/*
+ * Debug macros.  Slightly useful for finding bugs in pppd, not particularly
+ * useful for finding out why your connection isn't being established.
+ */
+//#ifdef DEBUGALL
+#define DEBUGMAIN	1
+#define DEBUGFSM	1
+#define DEBUGLCP	1
+#define DEBUGIPCP	1
+#define DEBUGIPV6CP	1
+#define DEBUGUPAP	1
+#define DEBUGCHAP	1
+//#endif
+
+#ifndef LOG_PPP			/* we use LOG_LOCAL2 for syslog by default */
+#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUGSYS) \
+  || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \
+  || defined(DEBUGCHAP) || defined(DEBUG) || defined(DEBUGIPV6CP)
+#define LOG_PPP LOG_LOCAL2
+#else
+#define LOG_PPP LOG_DAEMON
+#endif
+#endif /* LOG_PPP */
+
+#ifdef DEBUGMAIN
+#define MAINDEBUG(x)	if (debug) dbglog x
+#else
+#define MAINDEBUG(x)
+#endif
+
+#ifdef DEBUGSYS
+#define SYSDEBUG(x)	if (debug) dbglog x
+#else
+#define SYSDEBUG(x)
+#endif
+
+#ifdef DEBUGFSM
+#define FSMDEBUG(x)	if (debug) dbglog x
+#else
+#define FSMDEBUG(x)
+#endif
+
+#ifdef DEBUGLCP
+#define LCPDEBUG(x)	if (debug) dbglog x
+#else
+#define LCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPCP
+#define IPCPDEBUG(x)	if (debug) dbglog x
+#else
+#define IPCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPV6CP
+#define IPV6CPDEBUG(x)  if (debug) dbglog x
+#else
+#define IPV6CPDEBUG(x)
+#endif
+
+#ifdef DEBUGUPAP
+#define UPAPDEBUG(x)	if (debug) dbglog x
+#else
+#define UPAPDEBUG(x)
+#endif
+
+#ifdef DEBUGCHAP
+#define CHAPDEBUG(x)	if (debug) dbglog x
+#else
+#define CHAPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPXCP
+#define IPXCPDEBUG(x)	if (debug) dbglog x
+#else
+#define IPXCPDEBUG(x)
+#endif
+
+#ifndef SIGTYPE
+#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE)
+#define SIGTYPE void
+#else
+#define SIGTYPE int
+#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */
+#endif /* SIGTYPE */
+
+#ifndef MIN
+#define MIN(a, b)	((a) < (b)? (a): (b))
+#endif
+#ifndef MAX
+#define MAX(a, b)	((a) > (b)? (a): (b))
+#endif
+
+#ifndef offsetof
+#define offsetof(type, member) ((size_t) &((type *)0)->member)
+#endif
+
+
+
+
+
+#endif /* __PPP_H__ */
diff --git a/ap/app/pppd/pppd/session.c b/ap/app/pppd/pppd/session.c
new file mode 100644
index 0000000..69adca5
--- /dev/null
+++ b/ap/app/pppd/pppd/session.c
@@ -0,0 +1,421 @@
+/*
+ * session.c - PPP session control.
+ *
+ * Copyright (c) 2007 Diego Rivera. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from auth.c, which is:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <crypt.h>
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#endif
+#include <time.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "pppd.h"
+#include "session.h"
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#endif /* #ifdef USE_PAM */
+
+#define SET_MSG(var, msg) if (var != NULL) { var[0] = msg; }
+#define COPY_STRING(s) ((s) ? strdup(s) : NULL)
+
+#define SUCCESS_MSG "Session started successfully"
+#define ABORT_MSG "Session can't be started without a username"
+#define SERVICE_NAME "ppp"
+
+#define SESSION_FAILED  0
+#define SESSION_OK      1
+
+/* We have successfully started a session */
+static bool logged_in = 0;
+
+#ifdef USE_PAM
+/*
+ * Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static const char *PAM_username;
+static const char *PAM_password;
+static int   PAM_session = 0;
+static pam_handle_t *pamh = NULL;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static int conversation (int num_msg,
+#ifndef SOL2
+    const
+#endif
+    struct pam_message **msg,
+    struct pam_response **resp, void *appdata_ptr)
+{
+    int replies = 0;
+    struct pam_response *reply = NULL;
+
+    reply = malloc(sizeof(struct pam_response) * num_msg);
+    if (!reply) return PAM_CONV_ERR;
+
+    for (replies = 0; replies < num_msg; replies++) {
+        switch (msg[replies]->msg_style) {
+            case PAM_PROMPT_ECHO_ON:
+                reply[replies].resp_retcode = PAM_SUCCESS;
+                reply[replies].resp = COPY_STRING(PAM_username);
+                /* PAM frees resp */
+                break;
+            case PAM_PROMPT_ECHO_OFF:
+                reply[replies].resp_retcode = PAM_SUCCESS;
+                reply[replies].resp = COPY_STRING(PAM_password);
+                /* PAM frees resp */
+                break;
+            case PAM_TEXT_INFO:
+                /* fall through */
+            case PAM_ERROR_MSG:
+                /* ignore it, but pam still wants a NULL response... */
+                reply[replies].resp_retcode = PAM_SUCCESS;
+                reply[replies].resp = NULL;
+                break;
+            default:
+                /* Must be an error of some sort... */
+                free (reply);
+                return PAM_CONV_ERR;
+        }
+    }
+    *resp = reply;
+    return PAM_SUCCESS;
+}
+
+static struct pam_conv pam_conv_data = {
+    &conversation,
+    NULL
+};
+#endif /* #ifdef USE_PAM */
+
+int
+session_start(flags, user, passwd, ttyName, msg)
+    const int flags;
+    const char *user;
+    const char *passwd;
+    const char *ttyName;
+    char **msg;
+{
+#ifdef USE_PAM
+    bool ok = 1;
+    const char *usr;
+    int pam_error;
+    bool try_session = 0;
+#else /* #ifdef USE_PAM */
+    struct passwd *pw;
+#ifdef HAS_SHADOW
+    struct spwd *spwd;
+    struct spwd *getspnam();
+    long now = 0;
+#endif /* #ifdef HAS_SHADOW */
+#endif /* #ifdef USE_PAM */
+
+    SET_MSG(msg, SUCCESS_MSG);
+
+    /* If no verification is requested, then simply return an OK */
+    if (!(SESS_ALL & flags)) {
+        return SESSION_OK;
+    }
+
+    if (user == NULL) {
+       SET_MSG(msg, ABORT_MSG);
+       return SESSION_FAILED;
+    }
+
+#ifdef USE_PAM
+    /* Find the '\\' in the username */
+    /* This needs to be fixed to support different username schemes */
+    if ((usr = strchr(user, '\\')) == NULL)
+	usr = user;
+    else
+	usr++;
+
+    PAM_session = 0;
+    PAM_username = usr;
+    PAM_password = passwd;
+
+    warn("Initializing PAM (%d) for user %s", flags, usr);
+    pam_error = pam_start (SERVICE_NAME, usr, &pam_conv_data, &pamh);
+    warn("---> PAM INIT Result = %d", pam_error);
+    ok = (pam_error == PAM_SUCCESS);
+
+    if (ok) {
+        ok = (pam_set_item(pamh, PAM_TTY, ttyName) == PAM_SUCCESS) &&
+	    (pam_set_item(pamh, PAM_RHOST, ifname) == PAM_SUCCESS);
+    }
+
+    if (ok && (SESS_AUTH & flags)) {
+        warn("Attempting PAM authentication");
+        pam_error = pam_authenticate (pamh, PAM_SILENT);
+        if (pam_error == PAM_SUCCESS) {
+            /* PAM auth was OK */
+            warn("PAM Authentication OK for %s", user);
+        } else {
+            /* No matter the reason, we fail because we're authenticating */
+            ok = 0;
+            if (pam_error == PAM_USER_UNKNOWN) {
+                warn("User unknown, failing PAM authentication");
+                SET_MSG(msg, "User unknown - cannot authenticate via PAM");
+            } else {
+                /* Any other error means authentication was bad */
+                warn("PAM Authentication failed: %d: %s", pam_error,
+		       pam_strerror(pamh, pam_error));
+                SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
+            }
+        }
+    }
+
+    if (ok && (SESS_ACCT & flags)) {
+        warn("Attempting PAM account checks");
+        pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
+        if (pam_error == PAM_SUCCESS) {
+            /*
+	     * PAM account was OK, set the flag which indicates that we should
+	     * try to perform the session checks.
+	     */
+            try_session = 1;
+            warn("PAM Account OK for %s", user);
+        } else {
+            /*
+	     * If the account checks fail, then we should not try to perform
+	     * the session check, because they don't make sense.
+	     */
+            try_session = 0;
+            if (pam_error == PAM_USER_UNKNOWN) {
+                /*
+		 * We're checking the account, so it's ok to not have one
+		 * because the user might come from the secrets files, or some
+		 * other plugin.
+		 */
+                warn("User unknown, ignoring PAM restrictions");
+                SET_MSG(msg, "User unknown - ignoring PAM restrictions");
+            } else {
+                /* Any other error means session is rejected */
+                ok = 0;
+                warn("PAM Account checks failed: %d: %s", pam_error,
+		       pam_strerror(pamh, pam_error));
+                SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
+            }
+        }
+    }
+
+    if (ok && try_session && (SESS_ACCT & flags)) {
+        /* Only open a session if the user's account was found */
+        pam_error = pam_open_session (pamh, PAM_SILENT);
+        if (pam_error == PAM_SUCCESS) {
+            warn("PAM Session opened for user %s", user);
+            PAM_session = 1;
+        } else {
+            warn("PAM Session denied for user %s", user);
+            SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
+            ok = 0;
+        }
+    }
+
+    /* This is needed because apparently the PAM stuff closes the log */
+    reopen_log();
+
+    /* If our PAM checks have already failed, then we must return a failure */
+    if (!ok) return SESSION_FAILED;
+
+#else /* #ifdef USE_PAM */
+
+/*
+ * Use the non-PAM methods directly.  'pw' will remain NULL if the user
+ * has not been authenticated using local UNIX system services.
+ */
+
+    pw = NULL;
+    if ((SESS_AUTH & flags)) {
+	pw = getpwnam(user);
+
+	endpwent();
+	/*
+	 * Here, we bail if we have no user account, because there is nothing
+	 * to verify against.
+	 */
+	if (pw == NULL)
+	    return SESSION_FAILED;
+
+#ifdef HAS_SHADOW
+
+	spwd = getspnam(user);
+	endspent();
+
+	/*
+	 * If there is no shadow entry for the user, then we can't verify the
+	 * account.
+	 */
+	if (spwd == NULL)
+	    return SESSION_FAILED;
+
+	/*
+	 * We check validity all the time, because if the password has expired,
+	 * then clearly we should not authenticate against it (if we're being
+	 * called for authentication only).  Thus, in this particular instance,
+	 * there is no real difference between using the AUTH, SESS or ACCT
+	 * flags, or combinations thereof.
+	 */
+	now = time(NULL) / 86400L;
+	if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
+	    || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
+	    && spwd->sp_lstchg >= 0
+	    && now >= spwd->sp_lstchg + spwd->sp_max)) {
+	    warn("Password for %s has expired", user);
+	    return SESSION_FAILED;
+	}
+
+	/* We have a valid shadow entry, keep the password */
+	pw->pw_passwd = spwd->sp_pwdp;
+
+#endif /* #ifdef HAS_SHADOW */
+
+	/*
+	 * If no passwd, don't let them login if we're authenticating.
+	 */
+        if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2
+            || strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0)
+            return SESSION_FAILED;
+    }
+
+#endif /* #ifdef USE_PAM */
+
+    /*
+     * Write a wtmp entry for this user.
+     */
+
+    if (SESS_ACCT & flags) {
+	if (strncmp(ttyName, "/dev/", 5) == 0)
+	    ttyName += 5;
+	logwtmp(ttyName, user, ifname); /* Add wtmp login entry */
+	logged_in = 1;
+
+#if defined(_PATH_LASTLOG) && !defined(USE_PAM)
+	/*
+	 * Enter the user in lastlog only if he has been authenticated using
+	 * local system services.  If he has not, then we don't know what his
+	 * UID might be, and lastlog is indexed by UID.
+	 */
+	if (pw != NULL) {
+            struct lastlog ll;
+            int fd;
+	    time_t tnow;
+
+            if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
+                (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
+                memset((void *)&ll, 0, sizeof(ll));
+		(void)time(&tnow);
+                ll.ll_time = tnow;
+                (void)strncpy(ll.ll_line, ttyName, sizeof(ll.ll_line));
+                (void)strncpy(ll.ll_host, ifname, sizeof(ll.ll_host));
+                (void)write(fd, (char *)&ll, sizeof(ll));
+                (void)close(fd);
+            }
+	}
+#endif /* _PATH_LASTLOG and not USE_PAM */
+	warn("user %s logged in on tty %s intf %s", user, ttyName, ifname);
+    }
+
+    return SESSION_OK;
+}
+
+/*
+ * session_end - Logout the user.
+ */
+void
+session_end(const char* ttyName)
+{
+#ifdef USE_PAM
+    int pam_error = PAM_SUCCESS;
+
+    if (pamh != NULL) {
+        if (PAM_session) pam_error = pam_close_session (pamh, PAM_SILENT);
+        PAM_session = 0;
+        pam_end (pamh, pam_error);
+        pamh = NULL;
+	/* Apparently the pam stuff does closelog(). */
+	reopen_log();
+    }
+#endif
+    if (logged_in) {
+	if (strncmp(ttyName, "/dev/", 5) == 0)
+	    ttyName += 5;
+	logwtmp(ttyName, "", ""); /* Wipe out utmp logout entry */
+	logged_in = 0;
+    }
+}
diff --git a/ap/app/pppd/pppd/session.h b/ap/app/pppd/pppd/session.h
new file mode 100644
index 0000000..bee8c41
--- /dev/null
+++ b/ap/app/pppd/pppd/session.h
@@ -0,0 +1,91 @@
+/*
+ * session.c - PPP session control.
+ *
+ * Copyright (c) 2007 Diego Rivera. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __SESSION_H
+#define __SESSION_H
+
+#define SESS_AUTH  1	/* Check User Authentication */
+#define SESS_ACCT  2	/* Check Account Validity */
+
+/* Convenience parameter to do the whole enchilada */
+#define SESS_ALL   (SESS_AUTH | SESS_ACCT)
+
+/*
+ * int session_start(...)
+ *
+ * Start a session, performing any necessary validations.
+ *
+ * Parameters:
+ * 	const int flags :
+ * 		Any combination of the SESS_XXX flags, to indicate what the function
+ *		should do as part of its checks
+ *
+ *	const char* user :
+ *		The username to validate.  May safely be null.
+ *
+ *	const char* passwd :
+ *		The password to validate the user with. May safely be null.
+ *
+ *	const char* tty :
+ *		The TTY the user is connected on. May safely be null.
+ *
+ *	char** msg :
+ *		A char* to return an error or success message.  This message will be returned
+ *		regardless of the result.  May safely be null.
+ *
+ * Return Value:
+ * 	Zero value for failure, non-zero value for successful session verification.
+ */
+int
+session_start(const int flags, const char* user, const char* passwd, const char* tty, char** msg);
+
+/* Added these macros for convenience... */
+#define session_auth(user, pass, tty, msg) \
+	session_start(SESS_AUTH, user, pass, tty, msg)
+
+#define session_check(user, pass, tty, msg) \
+	session_start(SESS_ACCT, user, pass, tty, msg)
+
+#define session_full(user, pass, tty, msg) \
+	session_start(SESS_ALL, user, pass, tty, msg)
+
+/*
+ * void session_end(...)
+ *
+ * End a previously-started session.
+ *
+ * Parameters:
+ *	const char* tty :
+ *		The TTY the user is connected on. May safely be null.
+ */
+void
+session_end(const char* tty);
+
+#endif
diff --git a/ap/app/pppd/pppd/sha1.c b/ap/app/pppd/pppd/sha1.c
new file mode 100644
index 0000000..f4f975c
--- /dev/null
+++ b/ap/app/pppd/pppd/sha1.c
@@ -0,0 +1,170 @@
+/*
+ * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c
+ * 
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ * 
+ * Test Vectors (from FIPS PUB 180-1)
+ * "abc"
+ * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * A million repetitions of "a"
+ * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+ */
+
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <string.h>
+#include <netinet/in.h>	/* htonl() */
+#include <net/ppp_defs.h>
+#include "sha1.h"
+
+static void
+SHA1_Transform(u_int32_t[5], const unsigned char[64]);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#define blk0(i) (block->l[i] = htonl(block->l[i]))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void
+SHA1_Transform(u_int32_t state[5], const unsigned char buffer[64])
+{
+    u_int32_t a, b, c, d, e;
+    typedef union {
+	unsigned char c[64];
+	u_int32_t l[16];
+    } CHAR64LONG16;
+    CHAR64LONG16 *block;
+
+#ifdef SHA1HANDSOFF
+    static unsigned char workspace[64];
+    block = (CHAR64LONG16 *) workspace;
+    memcpy(block, buffer, 64);
+#else
+    block = (CHAR64LONG16 *) buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void
+SHA1_Init(SHA1_CTX *context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void
+SHA1_Update(SHA1_CTX *context, const unsigned char *data, unsigned int len)
+{
+    unsigned int i, j;
+
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+    context->count[1] += (len >> 29);
+    i = 64 - j;
+    while (len >= i) {
+	memcpy(&context->buffer[j], data, i);
+	SHA1_Transform(context->state, context->buffer);
+	data += i;
+	len -= i;
+	i = 64;
+	j = 0;
+    }
+
+    memcpy(&context->buffer[j], data, len);
+}
+
+
+/* Add padding and return the message digest. */
+
+void
+SHA1_Final(unsigned char digest[20], SHA1_CTX *context)
+{
+    u_int32_t i, j;
+    unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1_Update(context, (unsigned char *) "\200", 1);
+    while ((context->count[0] & 504) != 448) {
+	SHA1_Update(context, (unsigned char *) "\0", 1);
+    }
+    SHA1_Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+    for (i = 0; i < 20; i++) {
+	digest[i] = (unsigned char)
+		     ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    i = j = 0;
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
+    SHA1Transform(context->state, context->buffer);
+#endif
+}
+
diff --git a/ap/app/pppd/pppd/sha1.h b/ap/app/pppd/pppd/sha1.h
new file mode 100644
index 0000000..83f64df
--- /dev/null
+++ b/ap/app/pppd/pppd/sha1.h
@@ -0,0 +1,31 @@
+/* sha1.h */
+
+/* If OpenSSL is in use, then use that version of SHA-1 */
+#ifdef OPENSSL
+#include <t_sha.h>
+#define __SHA1_INCLUDE_
+#endif
+
+#ifndef __SHA1_INCLUDE_
+
+#ifndef SHA1_SIGNATURE_SIZE
+#ifdef SHA_DIGESTSIZE
+#define SHA1_SIGNATURE_SIZE SHA_DIGESTSIZE
+#else
+#define SHA1_SIGNATURE_SIZE 20
+#endif
+#endif
+
+typedef struct {
+    u_int32_t state[5];
+    u_int32_t count[2];
+    unsigned char buffer[64];
+} SHA1_CTX;
+
+extern void SHA1_Init(SHA1_CTX *);
+extern void SHA1_Update(SHA1_CTX *, const unsigned char *, unsigned int);
+extern void SHA1_Final(unsigned char[SHA1_SIGNATURE_SIZE], SHA1_CTX *);
+
+#define __SHA1_INCLUDE_
+#endif /* __SHA1_INCLUDE_ */
+
diff --git a/ap/app/pppd/pppd/spinlock.c b/ap/app/pppd/pppd/spinlock.c
new file mode 100644
index 0000000..4df7e47
--- /dev/null
+++ b/ap/app/pppd/pppd/spinlock.c
@@ -0,0 +1,473 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   trivial database library 
+
+   Copyright (C) Anton Blanchard                   2001
+   
+     ** NOTE! The following LGPL license applies to the tdb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+
+#define DEBUG
+
+#ifdef USE_SPINLOCKS
+
+/*
+ * ARCH SPECIFIC
+ */
+
+#if defined(SPARC_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+	unsigned int result;
+
+	asm volatile("ldstub    [%1], %0"
+		: "=r" (result)
+		: "r" (lock)
+		: "memory");
+
+	return (result == 0) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+	asm volatile("":::"memory");
+	*lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+	*lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+	return (*lock != 0);
+}
+
+#elif defined(POWERPC_SPINLOCKS) 
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+	unsigned int result;
+
+	__asm__ __volatile__(
+"1:	lwarx		%0,0,%1\n\
+	cmpwi		0,%0,0\n\
+	li		%0,0\n\
+	bne-		2f\n\
+	li		%0,1\n\
+	stwcx.		%0,0,%1\n\
+	bne-		1b\n\
+	isync\n\
+2:"	: "=&r"(result)
+	: "r"(lock)
+	: "cr0", "memory");
+
+	return (result == 1) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+	asm volatile("eieio":::"memory");
+	*lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+	*lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+	return (*lock != 0);
+}
+
+#elif defined(INTEL_SPINLOCKS) 
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+	int oldval;
+
+	asm volatile("xchgl %0,%1"
+		: "=r" (oldval), "=m" (*lock)
+		: "0" (0)
+		: "memory");
+
+	return oldval > 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+	asm volatile("":::"memory");
+	*lock = 1;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+	*lock = 1;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+	return (*lock != 1);
+}
+
+#elif defined(MIPS_SPINLOCKS) && defined(sgi) && (_COMPILER_VERSION >= 730)
+
+/* Implement spinlocks on IRIX using the MIPSPro atomic fetch operations. See
+ * sync(3) for the details of the intrinsic operations.
+ *
+ * "sgi" and "_COMPILER_VERSION" are always defined by MIPSPro.
+ */
+
+#ifdef STANDALONE
+
+/* MIPSPro 7.3 has "__inline" as an extension, but not "inline. */
+#define inline __inline
+
+#endif /* STANDALONE */
+
+/* Returns 0 if the lock is acquired, EBUSY otherwise. */
+static inline int __spin_trylock(spinlock_t *lock)
+{
+        unsigned int val;
+        val = __lock_test_and_set(lock, 1);
+        return val == 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+        __lock_release(lock);
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+        __lock_release(lock);
+}
+
+/* Returns 1 if the lock is held, 0 otherwise. */
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+        unsigned int val;
+        val = __add_and_fetch(lock, 0);
+	return val;
+}
+
+#elif defined(MIPS_SPINLOCKS) 
+
+static inline unsigned int load_linked(unsigned long addr)
+{
+	unsigned int res;
+
+	__asm__ __volatile__("ll\t%0,(%1)"
+		: "=r" (res)
+		: "r" (addr));
+
+	return res;
+}
+
+static inline unsigned int store_conditional(unsigned long addr, unsigned int value)
+{
+	unsigned int res;
+
+	__asm__ __volatile__("sc\t%0,(%2)"
+		: "=r" (res)
+		: "0" (value), "r" (addr));
+	return res;
+}
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+	unsigned int mw;
+
+	do {
+		mw = load_linked(lock);
+		if (mw) 
+			return EBUSY;
+	} while (!store_conditional(lock, 1));
+
+	asm volatile("":::"memory");
+
+	return 0;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+	asm volatile("":::"memory");
+	*lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+	*lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+	return (*lock != 0);
+}
+
+#else
+#error Need to implement spinlock code in spinlock.c
+#endif
+
+/*
+ * OS SPECIFIC
+ */
+
+static void yield_cpu(void)
+{
+	struct timespec tm;
+
+#ifdef USE_SCHED_YIELD
+	sched_yield();
+#else
+	/* Linux will busy loop for delays < 2ms on real time tasks */
+	tm.tv_sec = 0;
+	tm.tv_nsec = 2000000L + 1;
+	nanosleep(&tm, NULL);
+#endif
+}
+
+static int this_is_smp(void)
+{
+#if defined(HAVE_SYSCONF) && defined(SYSCONF_SC_NPROC_ONLN)
+        return (sysconf(_SC_NPROC_ONLN) > 1) ? 1 : 0;
+#else
+	return 0;
+#endif
+}
+
+/*
+ * GENERIC
+ */
+
+static int smp_machine = 0;
+
+static inline void __spin_lock(spinlock_t *lock)
+{
+	int ntries = 0;
+
+	while(__spin_trylock(lock)) {
+		while(__spin_is_locked(lock)) {
+			if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+				continue;
+			yield_cpu();
+		}
+	}
+}
+
+static void __read_lock(tdb_rwlock_t *rwlock)
+{
+	int ntries = 0;
+
+	while(1) {
+		__spin_lock(&rwlock->lock);
+
+		if (!(rwlock->count & RWLOCK_BIAS)) {
+			rwlock->count++;
+			__spin_unlock(&rwlock->lock);
+			return;
+		}
+	
+		__spin_unlock(&rwlock->lock);
+
+		while(rwlock->count & RWLOCK_BIAS) {
+			if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+				continue;
+			yield_cpu();
+		}
+	}
+}
+
+static void __write_lock(tdb_rwlock_t *rwlock)
+{
+	int ntries = 0;
+
+	while(1) {
+		__spin_lock(&rwlock->lock);
+
+		if (rwlock->count == 0) {
+			rwlock->count |= RWLOCK_BIAS;
+			__spin_unlock(&rwlock->lock);
+			return;
+		}
+
+		__spin_unlock(&rwlock->lock);
+
+		while(rwlock->count != 0) {
+			if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+				continue;
+			yield_cpu();
+		}
+	}
+}
+
+static void __write_unlock(tdb_rwlock_t *rwlock)
+{
+	__spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+	if (!(rwlock->count & RWLOCK_BIAS))
+		fprintf(stderr, "bug: write_unlock\n");
+#endif
+
+	rwlock->count &= ~RWLOCK_BIAS;
+	__spin_unlock(&rwlock->lock);
+}
+
+static void __read_unlock(tdb_rwlock_t *rwlock)
+{
+	__spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+	if (!rwlock->count)
+		fprintf(stderr, "bug: read_unlock\n");
+
+	if (rwlock->count & RWLOCK_BIAS)
+		fprintf(stderr, "bug: read_unlock\n");
+#endif
+
+	rwlock->count--;
+	__spin_unlock(&rwlock->lock);
+}
+
+/* TDB SPECIFIC */
+
+/* lock a list in the database. list -1 is the alloc list */
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+	tdb_rwlock_t *rwlocks;
+
+	if (!tdb->map_ptr) return -1;
+	rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+	switch(rw_type) {
+	case F_RDLCK:
+		__read_lock(&rwlocks[list+1]);
+		break;
+
+	case F_WRLCK:
+		__write_lock(&rwlocks[list+1]);
+		break;
+
+	default:
+		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+	}
+	return 0;
+}
+
+/* unlock the database. */
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+	tdb_rwlock_t *rwlocks;
+
+	if (!tdb->map_ptr) return -1;
+	rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+	switch(rw_type) {
+	case F_RDLCK:
+		__read_unlock(&rwlocks[list+1]);
+		break;
+
+	case F_WRLCK:
+		__write_unlock(&rwlocks[list+1]);
+		break;
+
+	default:
+		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+	}
+
+	return 0;
+}
+
+int tdb_create_rwlocks(int fd, unsigned int hash_size)
+{
+	unsigned size, i;
+	tdb_rwlock_t *rwlocks;
+
+	size = TDB_SPINLOCK_SIZE(hash_size);
+	rwlocks = malloc(size);
+	if (!rwlocks)
+		return -1;
+
+	for(i = 0; i < hash_size+1; i++) {
+		__spin_lock_init(&rwlocks[i].lock);
+		rwlocks[i].count = 0;
+	}
+
+	/* Write it out (appending to end) */
+	if (write(fd, rwlocks, size) != size) {
+		free(rwlocks);
+		return -1;
+	}
+	smp_machine = this_is_smp();
+	free(rwlocks);
+	return 0;
+}
+
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+	tdb_rwlock_t *rwlocks;
+	unsigned i;
+
+	if (tdb->header.rwlocks == 0) return 0;
+	if (!tdb->map_ptr) return -1;
+
+	/* We're mmapped here */
+	rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+	for(i = 0; i < tdb->header.hash_size+1; i++) {
+		__spin_lock_init(&rwlocks[i].lock);
+		rwlocks[i].count = 0;
+	}
+	return 0;
+}
+#else
+int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; }
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+
+/* Non-spinlock version: remove spinlock pointer */
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+	tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks
+				- (char *)&tdb->header);
+
+	tdb->header.rwlocks = 0;
+	if (lseek(tdb->fd, off, SEEK_SET) != off
+	    || write(tdb->fd, (void *)&tdb->header.rwlocks,
+		     sizeof(tdb->header.rwlocks)) 
+	    != sizeof(tdb->header.rwlocks))
+		return -1;
+	return 0;
+}
+#endif
diff --git a/ap/app/pppd/pppd/spinlock.h b/ap/app/pppd/pppd/spinlock.h
new file mode 100644
index 0000000..967fe37
--- /dev/null
+++ b/ap/app/pppd/pppd/spinlock.h
@@ -0,0 +1,59 @@
+#ifndef __SPINLOCK_H__
+#define __SPINLOCK_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "tdb.h"
+
+#ifdef USE_SPINLOCKS
+
+#define RWLOCK_BIAS 0x1000UL
+
+/* OS SPECIFIC */
+#define MAX_BUSY_LOOPS 1000
+#undef USE_SCHED_YIELD
+
+/* ARCH SPECIFIC */
+/* We should make sure these are padded to a cache line */
+#if defined(SPARC_SPINLOCKS)
+typedef volatile char spinlock_t;
+#elif defined(POWERPC_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#elif defined(INTEL_SPINLOCKS)
+typedef volatile int spinlock_t;
+#elif defined(MIPS_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#else
+#error Need to implement spinlock code in spinlock.h
+#endif
+
+typedef struct {
+	spinlock_t lock;
+	volatile int count;
+} tdb_rwlock_t;
+
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+
+#define TDB_SPINLOCK_SIZE(hash_size) (((hash_size) + 1) * sizeof(tdb_rwlock_t))
+
+#else /* !USE_SPINLOCKS */
+#if 0
+#define tdb_create_rwlocks(fd, hash_size) 0
+#define tdb_spinlock(tdb, list, rw_type) (-1)
+#define tdb_spinunlock(tdb, list, rw_type) (-1)
+#else
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+#endif
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+#define TDB_SPINLOCK_SIZE(hash_size) 0
+
+#endif
+
+#endif
diff --git a/ap/app/pppd/pppd/srp-entry.8 b/ap/app/pppd/pppd/srp-entry.8
new file mode 100644
index 0000000..097281a
--- /dev/null
+++ b/ap/app/pppd/pppd/srp-entry.8
@@ -0,0 +1,83 @@
+.\" manual page [] for srp-entry
+.\" $Id: srp-entry.8,v 1.2 2004/11/13 12:22:49 paulus Exp $
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH SRP-ENTRY 8
+.SH NAME
+srp\-entry \- Generate a SRP\-SHA1 Server Entry
+.SH SYNOPSIS
+.B srp\-entry
+[
+.I \-i index
+] [
+.I clientname
+]
+.SH DESCRIPTION
+.LP
+This utility generates an entry suitable for use in the
+/etc/ppp/srp\-secrets file on a PPP EAP SRP\-SHA1 authenticator
+("server").  This file has the same basic layout as the other pppd(8)
+authentication files, /etc/ppp/pap\-secrets and /etc/ppp/chap\-secrets.
+Thus, the entry generated has at least four main fields separated by
+spaces.  The first field is the authenticatee ("client") name.  The
+second is the server name.  The third is the secret.  The fourth is
+the allowed (or assigned) IP address for the client, and defaults to
+"*".  Additional fields can contain additional IP addresses or pppd
+options; see pppd(8) for details.
+.LP
+The third field has three subfields, separated by colons.  The first
+subfield is the index of the modulus and generator from SRP's
+/etc/tpasswd.conf.  The special value 0 is used to represent the
+well-known modulus and generator specified in the EAP SRP\-SHA1 draft.
+The second subfield is the password validator.  The third is the
+password salt.  These latter two values are encoded in base64 notation.
+.SH OPTIONS
+.TP
+.I \-i <index>
+Specifies the modulus/generator index in /etc/tpasswd.conf.  In order
+to use this option, you will need to run the "tconf" utility from the
+SRP package to generate local entries for this file.  Note that if
+these values are not known to the client, the client will be forced to
+run time-consuming safety tests on the values used.  For this reason,
+using the well-known values is recommended.
+.TP
+.I <clientname>
+Specifies the client name.  The password validator is a hashed
+combination of the client's name and password, and both are required.
+If the client name is not supplied on the command line, srp\-entry will
+prompt for the client name first.
+.SH FILES
+.TP
+.B /etc/ppp/srp\-secrets
+Usernames, passwords and IP addresses for SRP authentication.  This
+file should be owned by root and not readable or writable by any other
+user.  Pppd will log a warning if this is not the case.  Note that
+srp\-entry does not write to this file.  The user is responsible for
+copying the output of srp\-entry into this file.
+.TP
+.B /etc/tpasswd.conf
+Indexed copies of tested modulus/generator combinations; part of the
+SRP package.
+.SH SEE ALSO
+.TP
+pppd(8)
+.TP
+.B RFC2284
+Blunk, L., Vollbrecht, J.,
+.I PPP Extensible Authentication Protocol (EAP).
+March 1998.
+.TP
+.B draft\-ietf\-pppext\-eap\-srp\-03.txt
+Carlson, J., et al.,
+.I EAP SRP\-SHA1 Authentication Protocol.
+July 2001.
+.TP
+.B RFC2945
+Wu, T.,
+.I The SRP Authentication and Key Exchange System
+September 2000.
+.SH AUTHOR
+James Carlson (james.d.carlson@sun.com)
diff --git a/ap/app/pppd/pppd/srp-entry.c b/ap/app/pppd/pppd/srp-entry.c
new file mode 100644
index 0000000..8c0e297
--- /dev/null
+++ b/ap/app/pppd/pppd/srp-entry.c
@@ -0,0 +1,190 @@
+/*
+ * Utility program for generating entries in /etc/ppp/srp-secrets
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * Usage:
+ *	srp-entry [-i index] [clientname]
+ *
+ * Index, if supplied, is the modulus/generator index from
+ * /etc/tpasswd.conf.  If not supplied, then the last (highest
+ * numbered) entry from that file is used.  If the file doesn't exist,
+ * then the default "well known" EAP SRP-SHA1 modulus/generator is
+ * used.
+ *
+ * The default modulus/generator can be requested as index 0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <t_pwd.h>
+
+#ifndef	SOL2
+#define	getpassphrase	getpass
+#endif
+
+#define	HAS_SPACE	1
+#define	HAS_DQUOTE	2
+#define	HAS_SQUOTE	4
+#define	HAS_BACKSLASH	8
+
+static const u_char wkmodulus[] = {
+	0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
+	0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
+	0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
+	0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
+	0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
+	0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
+	0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
+	0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
+	0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
+	0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
+	0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
+	0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
+	0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
+	0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
+	0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
+	0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
+	0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
+	0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
+	0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
+	0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
+	0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
+	0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
+	0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
+	0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
+	0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
+	0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
+	0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
+	0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
+	0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
+	0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
+	0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
+	0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
+};
+
+static const char *myname;
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr, "Usage:\n\t%s [-i index] [clientname]\n",
+	    myname);
+	exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+	struct t_conf *tc;
+	struct t_confent *tcent, mytce;
+	struct t_pw pwval;
+	char *name;
+	char pname[256];
+	char *pass1, *pass2;
+	int flags, idx;
+	char *cp;
+	char delimit;
+	char strbuf[MAXB64PARAMLEN];
+	char saltbuf[MAXB64SALTLEN];
+
+	if ((myname = *argv) == NULL)
+		myname = "srp-entry";
+	else
+		argv++;
+
+	idx = -1;
+	if (*argv != NULL && strcmp(*argv, "-i") == 0) {
+		if (*++argv == NULL)
+			usage();
+		idx = atoi(*argv++);
+	}
+
+	tcent = NULL;
+	if (idx != 0 && (tc = t_openconf(NULL)) != NULL) {
+		if (idx == -1)
+			tcent = t_getconflast(tc);
+		else
+			tcent = t_getconfbyindex(tc, idx);
+	}
+	if (idx <= 0 && tcent == NULL) {
+		mytce.index = 0;
+		mytce.modulus.data = (u_char *)wkmodulus;
+		mytce.modulus.len = sizeof (wkmodulus);
+		mytce.generator.data = (u_char *)"\002";
+		mytce.generator.len = 1;
+		tcent = &mytce;
+	}
+	if (tcent == NULL) {
+		(void) fprintf(stderr, "SRP modulus/generator %d not found\n",
+		    idx);
+		exit(1);
+	}
+
+	if ((name = *argv) == NULL) {
+		(void) printf("Client name: ");
+		if (fgets(pname, sizeof (pname), stdin) == NULL)
+			exit(1);
+		if ((cp = strchr(pname, '\n')) != NULL)
+			*cp = '\0';
+		name = pname;
+	}
+
+	for (;;) {
+		if ((pass1 = getpassphrase("Pass phrase: ")) == NULL)
+			exit(1);
+		pass1 = strdup(pass1);
+		if ((pass2 = getpassphrase("Re-enter phrase: ")) == NULL)
+			exit(1);
+		if (strcmp(pass1, pass2) == 0)
+			break;
+		free(pass1);
+		(void) printf("Phrases don't match; try again.\n");
+	}
+
+	memset(&pwval, 0, sizeof (pwval));
+	t_makepwent(&pwval, name, pass1, NULL, tcent);
+	flags = 0;
+	for (cp = name; *cp != '\0'; cp++)
+		if (isspace(*cp))
+			flags |= HAS_SPACE;
+		else if (*cp == '"')
+			flags |= HAS_DQUOTE;
+		else if (*cp == '\'')
+			flags |= HAS_SQUOTE;
+		else if (*cp == '\\')
+			flags |= HAS_BACKSLASH;
+	delimit = flags == 0 ? '\0' : (flags & HAS_DQUOTE) ? '\'' : '"';
+	if (delimit != '\0')
+		(void) putchar(delimit);
+	for (cp = name; *cp != '\0'; cp++) {
+		if (*cp == delimit || *cp == '\\')
+			(void) putchar('\\');
+		(void) putchar(*cp);
+	}
+	if (delimit != '\0')
+		(void) putchar(delimit);
+	(void) printf(" * %d:%s:%s *\n",
+	    pwval.pebuf.index, t_tob64(strbuf,
+		(char *)pwval.pebuf.password.data, pwval.pebuf.password.len),
+	    t_tob64(saltbuf, (char *)pwval.pebuf.salt.data,
+		pwval.pebuf.salt.len));
+	return 0;
+}
diff --git a/ap/app/pppd/pppd/sys-linux.c b/ap/app/pppd/pppd/sys-linux.c
new file mode 100644
index 0000000..ed26de8
--- /dev/null
+++ b/ap/app/pppd/pppd/sys-linux.c
@@ -0,0 +1,3009 @@
+/*
+ * sys-linux.c - System-dependent procedures for setting up
+ * PPP interfaces on Linux systems
+ *
+ * Copyright (c) 1994-2004 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from main.c and pppd.h, which are:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <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 <pcap-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;	/* pty for old-style demand mode, slave */
+static int master_fd = -1;	/* pty for old-style demand mode, master */
+#ifdef INET6
+static int sock6_fd = -1;
+#endif /* INET6 */
+
+/*
+ * For the old-style kernel driver, this is the same as ppp_fd.
+ * For the new-style driver, it is the fd of an instance of /dev/ppp
+ * which is attached to the ppp unit and is used for controlling it.
+ */
+int ppp_dev_fd = -1;		/* fd for /dev/ppp (new style driver) */
+
+static int chindex;		/* channel index (new style driver) */
+
+static fd_set in_fds;		/* set of fds that wait_input waits for */
+static int max_in_fd;		/* highest fd set in in_fds */
+
+static int has_proxy_arp       = 0;
+static int driver_version      = 0;
+static int driver_modification = 0;
+static int driver_patch        = 0;
+static int driver_is_old       = 0;
+static int restore_term        = 0;	/* 1 => we've munged the terminal */
+static struct termios inittermios;	/* Initial TTY termios */
+
+int new_style_driver = 0;
+
+static char loop_name[20];
+static unsigned char inbuf[512]; /* buffer for chars read from loopback */
+
+static int	if_is_up;	/* Interface has been marked up */
+static int	have_default_route;	/* Gateway for default route added */
+static u_int32_t proxy_arp_addr;	/* Addr for proxy arp entry added */
+static char proxy_arp_dev[16];		/* Device for proxy arp entry */
+static u_int32_t our_old_addr;		/* for detecting address changes */
+static int	dynaddr_set;		/* 1 if ip_dynaddr set */
+static int	looped;			/* 1 if using loop */
+static int	link_mtu;		/* mtu for the link (not bundle) */
+
+static struct utsname utsname;	/* for the kernel version */
+static int kernel_version;
+#define KVERSION(j,n,p)	((j)*1000000 + (n)*1000 + (p))
+
+#define MAX_IFS		100
+
+#define FLAGS_GOOD (IFF_UP          | IFF_BROADCAST)
+#define FLAGS_MASK (IFF_UP          | IFF_BROADCAST | \
+		    IFF_POINTOPOINT | IFF_LOOPBACK  | IFF_NOARP)
+
+#define SIN_ADDR(x)	(((struct sockaddr_in *) (&(x)))->sin_addr.s_addr)
+
+/* Prototypes for procedures local to this file. */
+static int modify_flags(int fd, int clear_bits, int set_bits);
+static int translate_speed (int bps);
+static int baud_rate_of (int speed);
+static void close_route_table (void);
+static int open_route_table (void);
+static int read_route_table (struct rtentry *rt);
+static int defaultroute_exists (struct rtentry *rt);
+static int get_ether_addr (u_int32_t ipaddr, struct sockaddr *hwaddr,
+			   char *name, int namelen);
+static void decode_version (char *buf, int *version, int *mod, int *patch);
+static int set_kdebugflag(int level);
+static int ppp_registered(void);
+static int make_ppp_unit(void);
+
+extern u_char	inpacket_buf[];	/* borrowed from main.c */
+
+/*
+ * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
+ * if it exists.
+ */
+
+#define SET_SA_FAMILY(addr, family)			\
+    memset ((char *) &(addr), '\0', sizeof(addr));	\
+    addr.sa_family = (family);
+
+/*
+ * Determine if the PPP connection should still be present.
+ */
+
+extern int hungup;
+
+/* new_fd is the fd of a tty */
+static void set_ppp_fd (int new_fd)
+{
+	ppp_fd = new_fd;
+	if (!new_style_driver)
+		ppp_dev_fd = new_fd;
+}
+
+static int still_ppp(void)
+{
+	if (new_style_driver)
+		return !hungup && ppp_fd >= 0;
+	if (!hungup || ppp_fd == slave_fd)
+		return 1;
+	if (slave_fd >= 0) {
+		set_ppp_fd(slave_fd);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * modify_flags - set and clear flag bits controlling the kernel
+ * PPP driver.
+ */
+static int modify_flags(int fd, int clear_bits, int set_bits)
+{
+	int flags;
+
+	if (ioctl(fd, PPPIOCGFLAGS, &flags) == -1)
+		goto err;
+	flags = (flags & ~clear_bits) | set_bits;
+	if (ioctl(fd, PPPIOCSFLAGS, &flags) == -1)
+		goto err;
+
+	return 0;
+
+ err:
+	if (errno != EIO)
+		error("Failed to set PPP kernel option flags: %m");
+	return -1;
+}
+
+/********************************************************************
+ *
+ * sys_init - System-dependent initialization.
+ */
+
+void sys_init(void)
+{
+    /* Get an internet socket for doing socket ioctls. */
+    sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock_fd < 0)
+	fatal("Couldn't create IP socket: %m(%d)", errno);
+
+#ifdef INET6
+    sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (sock6_fd < 0)
+	sock6_fd = -errno;	/* save errno for later */
+#endif
+
+    FD_ZERO(&in_fds);
+    max_in_fd = 0;
+}
+
+/********************************************************************
+ *
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This shouldn't call die() because it's called from die().
+ */
+
+void sys_cleanup(void)
+{
+/*
+ * Take down the device
+ */
+    if (if_is_up) {
+	if_is_up = 0;
+	sifdown(0);
+    }
+/*
+ * Delete any routes through the device.
+ */
+    if (have_default_route)
+	cifdefaultroute(0, 0, 0);
+
+    if (has_proxy_arp)
+	cifproxyarp(0, proxy_arp_addr);
+}
+
+/********************************************************************
+ *
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close(void)
+{
+    if (new_style_driver && ppp_dev_fd >= 0)
+	close(ppp_dev_fd);
+    if (sock_fd >= 0)
+	close(sock_fd);
+#ifdef INET6
+    if (sock6_fd >= 0)
+	close(sock6_fd);
+#endif
+    if (slave_fd >= 0)
+	close(slave_fd);
+    if (master_fd >= 0)
+	close(master_fd);
+}
+
+/********************************************************************
+ *
+ * set_kdebugflag - Define the debugging level for the kernel
+ */
+
+static int set_kdebugflag (int requested_level)
+{
+    if (ppp_dev_fd < 0)
+	return 1;
+    if (ioctl(ppp_dev_fd, PPPIOCSDEBUG, &requested_level) < 0) {
+	if ( ! ok_error (errno) )
+	    error("ioctl(PPPIOCSDEBUG): %m (line %d)", __LINE__);
+	return (0);
+    }
+    return (1);
+}
+
+/********************************************************************
+ *
+ * tty_establish_ppp - Turn the serial port into a ppp interface.
+ */
+
+int tty_establish_ppp (int tty_fd)
+{
+    int ret_fd;
+
+/*
+ * Ensure that the tty device is in exclusive mode.
+ */
+    if (ioctl(tty_fd, TIOCEXCL, 0) < 0) {
+	if ( ! ok_error ( errno ))
+	    warn("Couldn't make tty exclusive: %m");
+    }
+/*
+ * Demand mode - prime the old ppp device to relinquish the unit.
+ */
+    if (!new_style_driver && looped
+	&& ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
+	error("ioctl(transfer ppp unit): %m, line %d", __LINE__);
+	return -1;
+    }
+/*
+ * Set the current tty to the PPP discpline
+ */
+
+#ifndef N_SYNC_PPP
+#define N_SYNC_PPP 14
+#endif
+    ppp_disc = (new_style_driver && sync_serial)? N_SYNC_PPP: N_PPP;
+    if (ioctl(tty_fd, TIOCSETD, &ppp_disc) < 0) {
+	if ( ! ok_error (errno) ) {
+	    error("Couldn't set tty to PPP discipline: %m");
+	    return -1;
+	}
+    }
+
+    ret_fd = generic_establish_ppp(tty_fd);
+
+#define SC_RCVB	(SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
+#define SC_LOGB	(SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
+		 | SC_LOG_FLUSH)
+
+    if (ret_fd >= 0) {
+	modify_flags(ppp_fd, SC_RCVB | SC_LOGB,
+		     (kdebugflag * SC_DEBUG) & SC_LOGB);
+    } else {
+	if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
+	    warn("Couldn't reset tty to normal line discipline: %m");
+    }
+
+    return ret_fd;
+}
+
+/********************************************************************
+ *
+ * generic_establish_ppp - Turn the fd into a ppp interface.
+ */
+int generic_establish_ppp (int fd)
+{
+    int x;
+
+    if (new_style_driver) {
+	int flags;
+
+	/* Open an instance of /dev/ppp and connect the channel to it */
+	if (ioctl(fd, PPPIOCGCHAN, &chindex) == -1) {
+	    error("Couldn't get channel number: %m");
+	    goto err;
+	}
+	warn("using channel %d", chindex);
+	fd = open("/dev/ppp", O_RDWR);
+	if (fd < 0) {
+	    error("Couldn't reopen /dev/ppp: %m");
+	    goto err;
+	}
+	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+	if (ioctl(fd, PPPIOCATTCHAN, &chindex) < 0) {
+	    error("Couldn't attach to channel %d: %m", chindex);
+	    goto err_close;
+	}
+	flags = fcntl(fd, F_GETFL);
+	if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+	    warn("Couldn't set /dev/ppp (channel) to nonblock: %m");
+	set_ppp_fd(fd);
+
+	if (!looped)
+	    ifunit = -1;
+	if (!looped && !multilink) {
+	    /*
+	     * Create a new PPP unit.
+	     */
+	    if (make_ppp_unit() < 0)
+		goto err_close;
+	}
+
+	if (looped)
+	    modify_flags(ppp_dev_fd, SC_LOOP_TRAFFIC, 0);
+
+	if (!multilink) {
+	    add_fd(ppp_dev_fd);
+	    if (ioctl(fd, PPPIOCCONNECT, &ifunit) < 0) {
+		error("Couldn't attach to PPP unit %d: %m", ifunit);
+		goto err_close;
+	    }
+	}
+
+    } else {
+	/*
+	 * Old-style driver: find out which interface we were given.
+	 */
+	set_ppp_fd (fd);
+	if (ioctl(fd, PPPIOCGUNIT, &x) < 0) {
+	    if (ok_error (errno))
+		goto err;
+	    fatal("ioctl(PPPIOCGUNIT): %m (line %d)", __LINE__);
+	}
+	/* Check that we got the same unit again. */
+	if (looped && x != ifunit)
+	    fatal("transfer_ppp failed: wanted unit %d, got %d", ifunit, x);
+	ifunit = x;
+
+	/*
+	 * Fetch the initial file flags and reset blocking mode on the file.
+	 */
+	initfdflags = fcntl(fd, F_GETFL);
+	if (initfdflags == -1 ||
+	    fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+	    if ( ! ok_error (errno))
+		warn("Couldn't set device to non-blocking mode: %m");
+	}
+    }
+
+    /*
+     * Enable debug in the driver if requested.
+     */
+    if (!looped)
+	set_kdebugflag (kdebugflag);
+
+    looped = 0;
+
+    return ppp_fd;
+
+ err_close:
+    close(fd);
+ err:
+    return -1;
+}
+
+/********************************************************************
+ *
+ * tty_disestablish_ppp - Restore the serial port to normal operation.
+ * This shouldn't call die() because it's called from die().
+ */
+
+void tty_disestablish_ppp(int tty_fd)
+{
+    if (!hungup) {
+/*
+ * Flush the tty output buffer so that the TIOCSETD doesn't hang.
+ */
+	if (tcflush(tty_fd, TCIOFLUSH) < 0)
+	{
+	    warn("tcflush failed: %m");
+	    goto flushfailed;
+	}
+/*
+ * Restore the previous line discipline
+ */
+	if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0) {
+	    if ( ! ok_error (errno))
+		error("ioctl(TIOCSETD, N_TTY): %m (line %d)", __LINE__);
+	}
+
+	if (ioctl(tty_fd, TIOCNXCL, 0) < 0) {
+	    if ( ! ok_error (errno))
+		warn("ioctl(TIOCNXCL): %m (line %d)", __LINE__);
+	}
+
+	/* Reset non-blocking mode on fd. */
+	if (initfdflags != -1 && fcntl(tty_fd, F_SETFL, initfdflags) < 0) {
+	    if ( ! ok_error (errno))
+		warn("Couldn't restore device fd flags: %m");
+	}
+    }
+flushfailed:
+    initfdflags = -1;
+
+    generic_disestablish_ppp(tty_fd);
+}
+
+/********************************************************************
+ *
+ * generic_disestablish_ppp - Restore device components to normal
+ * operation, and reconnect the ppp unit to the loopback if in demand
+ * mode.  This shouldn't call die() because it's called from die().
+ */
+void generic_disestablish_ppp(int dev_fd)
+{
+    if (new_style_driver) {
+	close(ppp_fd);
+	ppp_fd = -1;
+	if (demand) {
+	    modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC);
+	    looped = 1;
+	} else if (!doing_multilink && ppp_dev_fd >= 0) {
+	    close(ppp_dev_fd);
+	    remove_fd(ppp_dev_fd);
+	    ppp_dev_fd = -1;
+	}
+    } else {
+	/* old-style driver */
+	if (demand)
+	    set_ppp_fd(slave_fd);
+	else
+	    ppp_dev_fd = -1;
+    }
+}
+
+/*
+ * make_ppp_unit - make a new ppp unit for ppp_dev_fd.
+ * Assumes new_style_driver.
+ */
+static int make_ppp_unit()
+{
+	int x, flags;
+
+	if (ppp_dev_fd >= 0) {
+		warn("in make_ppp_unit, already had /dev/ppp open?");
+		close(ppp_dev_fd);
+	}
+	ppp_dev_fd = open("/dev/ppp", O_RDWR);
+	if (ppp_dev_fd < 0)
+		fatal("Couldn't open /dev/ppp: %m");
+	flags = fcntl(ppp_dev_fd, F_GETFL);
+	if (flags == -1
+	    || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+		warn("Couldn't set /dev/ppp to nonblock: %m");
+
+	ifunit = req_unit;
+	x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+	if (x < 0 && req_unit >= 0 && errno == EEXIST) {
+		warn("Couldn't allocate PPP unit %d as it is already in use", req_unit);
+		ifunit = -1;
+		x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+	}
+	if (x < 0)
+		error("Couldn't create new ppp unit: %m");
+	return x;
+}
+
+/*
+ * cfg_bundle - configure the existing bundle.
+ * Used in demand mode.
+ */
+void cfg_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+	if (!new_style_driver)
+		return;
+
+	/* set the mrru, mtu and flags */
+	if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
+		error("Couldn't set MRRU: %m");
+
+	modify_flags(ppp_dev_fd, SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ|SC_MULTILINK,
+		     ((rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0)
+		      | (mrru? SC_MULTILINK: 0)));
+
+	/* connect up the channel */
+	if (ioctl(ppp_fd, PPPIOCCONNECT, &ifunit) < 0)
+		fatal("Couldn't attach to PPP unit %d: %m", ifunit);
+	add_fd(ppp_dev_fd);
+}
+
+/*
+ * make_new_bundle - create a new PPP unit (i.e. a bundle)
+ * and connect our channel to it.  This should only get called
+ * if `multilink' was set at the time establish_ppp was called.
+ * In demand mode this uses our existing bundle instead of making
+ * a new one.
+ */
+void make_new_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+	if (!new_style_driver)
+		return;
+
+	/* make us a ppp unit */
+	if (make_ppp_unit() < 0)
+		die(1);
+
+	/* set the mrru and flags */
+	cfg_bundle(mrru, mtru, rssn, tssn);
+}
+
+/*
+ * bundle_attach - attach our link to a given PPP unit.
+ * We assume the unit is controlled by another pppd.
+ */
+int bundle_attach(int ifnum)
+{
+	int master_fd;
+
+	if (!new_style_driver)
+		return -1;
+
+	master_fd = open("/dev/ppp", O_RDWR);
+	if (master_fd < 0)
+		fatal("Couldn't open /dev/ppp: %m");
+	if (ioctl(master_fd, PPPIOCATTACH, &ifnum) < 0) {
+		if (errno == ENXIO) {
+			close(master_fd);
+			return 0;	/* doesn't still exist */
+		}
+		fatal("Couldn't attach to interface unit %d: %m\n", ifnum);
+	}
+	if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0)
+		fatal("Couldn't connect to interface unit %d: %m", ifnum);
+	modify_flags(master_fd, 0, SC_MULTILINK);
+	close(master_fd);
+
+	ifunit = ifnum;
+	return 1;
+}
+
+/*
+ * destroy_bundle - tell the driver to destroy our bundle.
+ */
+void destroy_bundle(void)
+{
+	if (ppp_dev_fd >= 0) {
+		close(ppp_dev_fd);
+		remove_fd(ppp_dev_fd);
+		ppp_dev_fd = -1;
+	}
+}
+
+/********************************************************************
+ *
+ * clean_check - Fetch the flags for the device and generate
+ * appropriate error messages.
+ */
+void clean_check(void)
+{
+    int x;
+    char *s;
+
+    if (still_ppp()) {
+	if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) {
+	    s = NULL;
+	    switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) {
+	    case SC_RCV_B7_0:
+		s = "all had bit 7 set to 1";
+		break;
+
+	    case SC_RCV_B7_1:
+		s = "all had bit 7 set to 0";
+		break;
+
+	    case SC_RCV_EVNP:
+		s = "all had odd parity";
+		break;
+
+	    case SC_RCV_ODDP:
+		s = "all had even parity";
+		break;
+	    }
+
+	    if (s != NULL) {
+		warn("Receive serial link is not 8-bit clean:");
+		warn("Problem: %s", s);
+	    }
+	}
+    }
+}
+
+
+/*
+ * List of valid speeds.
+ */
+
+struct speed {
+    int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+    { 50, B50 },
+#endif
+#ifdef B75
+    { 75, B75 },
+#endif
+#ifdef B110
+    { 110, B110 },
+#endif
+#ifdef B134
+    { 134, B134 },
+#endif
+#ifdef B150
+    { 150, B150 },
+#endif
+#ifdef B200
+    { 200, B200 },
+#endif
+#ifdef B300
+    { 300, B300 },
+#endif
+#ifdef B600
+    { 600, B600 },
+#endif
+#ifdef B1200
+    { 1200, B1200 },
+#endif
+#ifdef B1800
+    { 1800, B1800 },
+#endif
+#ifdef B2000
+    { 2000, B2000 },
+#endif
+#ifdef B2400
+    { 2400, B2400 },
+#endif
+#ifdef B3600
+    { 3600, B3600 },
+#endif
+#ifdef B4800
+    { 4800, B4800 },
+#endif
+#ifdef B7200
+    { 7200, B7200 },
+#endif
+#ifdef B9600
+    { 9600, B9600 },
+#endif
+#ifdef B19200
+    { 19200, B19200 },
+#endif
+#ifdef B38400
+    { 38400, B38400 },
+#endif
+#ifdef B57600
+    { 57600, B57600 },
+#endif
+#ifdef B76800
+    { 76800, B76800 },
+#endif
+#ifdef B115200
+    { 115200, B115200 },
+#endif
+#ifdef EXTA
+    { 19200, EXTA },
+#endif
+#ifdef EXTB
+    { 38400, EXTB },
+#endif
+#ifdef B230400
+    { 230400, B230400 },
+#endif
+#ifdef B460800
+    { 460800, B460800 },
+#endif
+#ifdef B921600
+    { 921600, B921600 },
+#endif
+#ifdef B1000000
+    { 1000000, B1000000 },
+#endif
+#ifdef B1152000
+    { 1152000, B1152000 },
+#endif
+#ifdef B1500000
+    { 1500000, B1500000 },
+#endif
+#ifdef B2000000
+    { 2000000, B2000000 },
+#endif
+#ifdef B2500000
+    { 2500000, B2500000 },
+#endif
+#ifdef B3000000
+    { 3000000, B3000000 },
+#endif
+#ifdef B3500000
+    { 3500000, B3500000 },
+#endif
+#ifdef B4000000
+    { 4000000, B4000000 },
+#endif
+    { 0, 0 }
+};
+
+/********************************************************************
+ *
+ * Translate from bits/second to a speed_t.
+ */
+
+static int translate_speed (int bps)
+{
+    struct speed *speedp;
+
+    if (bps != 0) {
+	for (speedp = speeds; speedp->speed_int; speedp++) {
+	    if (bps == speedp->speed_int)
+		return speedp->speed_val;
+	}
+	warn("speed %d not supported", bps);
+    }
+    return 0;
+}
+
+/********************************************************************
+ *
+ * Translate from a speed_t to bits/second.
+ */
+
+static int baud_rate_of (int speed)
+{
+    struct speed *speedp;
+
+    if (speed != 0) {
+	for (speedp = speeds; speedp->speed_int; speedp++) {
+	    if (speed == speedp->speed_val)
+		return speedp->speed_int;
+	}
+    }
+    return 0;
+}
+
+/********************************************************************
+ *
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc.  If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+
+void set_up_tty(int tty_fd, int local)
+{
+    int speed;
+    struct termios tios;
+
+    setdtr(tty_fd, 1);
+    if (tcgetattr(tty_fd, &tios) < 0) {
+	if (!ok_error(errno))
+	    fatal("tcgetattr: %m (line %d)", __LINE__);
+	return;
+    }
+
+    if (!restore_term)
+	inittermios = tios;
+
+    tios.c_cflag     &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+    tios.c_cflag     |= CS8 | CREAD | HUPCL;
+
+    tios.c_iflag      = IGNBRK | IGNPAR;
+    tios.c_oflag      = 0;
+    tios.c_lflag      = 0;
+    tios.c_cc[VMIN]   = 1;
+    tios.c_cc[VTIME]  = 0;
+
+    if (local || !modem)
+	tios.c_cflag ^= (CLOCAL | HUPCL);
+
+    switch (crtscts) {
+    case 1:
+	tios.c_cflag |= CRTSCTS;
+	break;
+
+    case -2:
+	tios.c_iflag     |= IXON | IXOFF;
+	tios.c_cc[VSTOP]  = 0x13;	/* DC3 = XOFF = ^S */
+	tios.c_cc[VSTART] = 0x11;	/* DC1 = XON  = ^Q */
+	break;
+
+    case -1:
+	tios.c_cflag &= ~CRTSCTS;
+	break;
+
+    default:
+	break;
+    }
+
+    speed = translate_speed(inspeed);
+    if (speed) {
+	cfsetospeed (&tios, speed);
+	cfsetispeed (&tios, speed);
+    }
+/*
+ * We can't proceed if the serial port speed is B0,
+ * since that implies that the serial port is disabled.
+ */
+    else {
+	speed = cfgetospeed(&tios);
+	if (speed == B0)
+	    fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
+    }
+
+    while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno))
+	if (errno != EINTR)
+	    fatal("tcsetattr: %m (line %d)", __LINE__);
+
+    baud_rate    = baud_rate_of(speed);
+    restore_term = 1;
+}
+
+/********************************************************************
+ *
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+
+void setdtr (int tty_fd, int on)
+{
+    int modembits = TIOCM_DTR;
+
+    ioctl(tty_fd, (on ? TIOCMBIS : TIOCMBIC), &modembits);
+}
+
+/********************************************************************
+ *
+ * restore_tty - restore the terminal to the saved settings.
+ */
+
+void restore_tty (int tty_fd)
+{
+    if (restore_term) {
+	restore_term = 0;
+/*
+ * Turn off echoing, because otherwise we can get into
+ * a loop with the tty and the modem echoing to each other.
+ * We presume we are the sole user of this tty device, so
+ * when we close it, it will revert to its defaults anyway.
+ */
+	if (!default_device)
+	    inittermios.c_lflag &= ~(ECHO | ECHONL);
+
+	if (tcsetattr(tty_fd, TCSAFLUSH, &inittermios) < 0) {
+	    if (! ok_error (errno))
+		warn("tcsetattr: %m (line %d)", __LINE__);
+	}
+    }
+}
+
+/********************************************************************
+ *
+ * output - Output PPP packet.
+ */
+
+void output (int unit, unsigned char *p, int len)
+{
+    int fd = ppp_fd;
+    int proto;
+
+    dump_packet("sent", p, len);
+    if (snoop_send_hook) snoop_send_hook(p, len);
+
+    if (len < PPP_HDRLEN)
+	return;
+    if (new_style_driver) {
+	p += 2;
+	len -= 2;
+	proto = (p[0] << 8) + p[1];
+	if (ppp_dev_fd >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
+	    fd = ppp_dev_fd;
+    }
+    if (write(fd, p, len) < 0) {
+	if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS
+	    || errno == ENXIO || errno == EIO || errno == EINTR)
+	    warn("write: warning: %m (%d)", errno);
+	else
+	    error("write: %m (%d)", errno);
+    }
+}
+
+/********************************************************************
+ *
+ * wait_input - wait until there is data available,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+#if 0
+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");
+}
+#endif
+
+struct timeval select_time = {5, 0};
+
+void wait_input(struct timeval *timo)
+{
+    fd_set ready, exc;
+    int n;
+	char ppp_pdpstate[100] = {0};
+	struct timeval temp= {0};
+
+	
+	sc_cfg_get("ppp_pdpstate",ppp_pdpstate,sizeof(ppp_pdpstate));
+	warn("pppd: %s---->%d, %s", __FILE__, __LINE__, ppp_pdpstate);
+	if(strcmp(ppp_pdpstate,"dead") == 0 )
+	{
+		warn("pppd: %s---->%d, %s", __FILE__, __LINE__, ppp_pdpstate);
+		return;
+	}
+	else
+	{
+		while(1)
+		{
+		    ready = in_fds;
+		    exc = in_fds;
+
+			if(timo == NULL)
+			{
+				temp.tv_usec = 0;
+				temp.tv_sec = 5;
+			}
+			else
+			{
+				temp.tv_sec = timo->tv_sec - select_time.tv_sec;
+				temp.tv_usec = timo->tv_usec - select_time.tv_usec;
+				if (temp.tv_usec < 0) {
+					temp.tv_usec += 1000000;
+					temp.tv_sec -= 1;
+				}
+				
+				if (temp.tv_sec < 0)
+				{
+					temp.tv_usec = timo->tv_usec;
+					temp.tv_sec  = timo->tv_sec;
+
+					timo->tv_usec = 0;
+					timo->tv_sec = 0;
+				}
+				else
+				{
+					timo->tv_usec = temp.tv_usec;
+					timo->tv_sec  = temp.tv_sec;
+					temp.tv_usec = select_time.tv_usec;
+					temp.tv_sec = select_time.tv_sec;
+				}
+			}
+			
+		    n = select(max_in_fd + 1, &ready, NULL, &exc, &temp);
+
+			if (n < 0 )
+		    {
+		    	if( errno != EINTR )
+		    	{
+					fatal("select: %m");
+		    	}
+				return;
+		    }
+
+
+			if(n > 0)
+			{
+				return;
+			}
+
+			if(timo != NULL && timo->tv_usec == 0 && timo->tv_sec == 0)
+			{
+				return;
+			}
+			
+			memset(ppp_pdpstate, 0x00, sizeof(ppp_pdpstate));
+			sc_cfg_get("ppp_pdpstate",ppp_pdpstate,sizeof(ppp_pdpstate));
+			if(strcmp(ppp_pdpstate,"dead") == 0)
+			{
+				warn("pppd: %s---->%d, %s", __FILE__, __LINE__, ppp_pdpstate);
+				return;
+			}
+
+			
+		}
+	}
+}
+/*
+ * add_fd - add an fd to the set that wait_input waits for.
+ */
+void add_fd(int fd)
+{
+    if (fd >= FD_SETSIZE)
+	fatal("internal error: file descriptor too large (%d)", fd);
+    FD_SET(fd, &in_fds);
+    if (fd > max_in_fd)
+	max_in_fd = fd;
+}
+
+/*
+ * remove_fd - remove an fd from the set that wait_input waits for.
+ */
+void remove_fd(int fd)
+{
+    FD_CLR(fd, &in_fds);
+}
+
+
+/********************************************************************
+ *
+ * read_packet - get a PPP packet from the serial device.
+ */
+
+int read_packet (unsigned char *buf)
+{
+    int len, nr;
+
+    len = PPP_MRU + PPP_HDRLEN;
+    if (new_style_driver) {
+	*buf++ = PPP_ALLSTATIONS;
+	*buf++ = PPP_UI;
+	len -= 2;
+    }
+    nr = -1;
+    if (ppp_fd >= 0) {
+	nr = read(ppp_fd, buf, len);
+	if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN
+	    && errno != EIO && errno != EINTR)
+	    error("read: %m");
+	if (nr < 0 && errno == ENXIO)
+	    return 0;
+    }
+    if (nr < 0 && new_style_driver && ppp_dev_fd >= 0 && !bundle_eof) {
+	/* N.B. we read ppp_fd first since LCP packets come in there. */
+	nr = read(ppp_dev_fd, buf, len);
+	if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN
+	    && errno != EIO && errno != EINTR)
+	    error("read /dev/ppp: %m");
+	if (nr < 0 && errno == ENXIO)
+	    nr = 0;
+	if (nr == 0 && doing_multilink) {
+	    remove_fd(ppp_dev_fd);
+	    bundle_eof = 1;
+	}
+    }
+    if (new_style_driver && ppp_fd < 0 && ppp_dev_fd < 0)
+	nr = 0;
+    return (new_style_driver && nr > 0)? nr+2: nr;
+}
+
+/********************************************************************
+ *
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output(void)
+{
+    int rv = 0;
+    int n;
+
+    if (new_style_driver) {
+	while ((n = read_packet(inpacket_buf)) > 0)
+	    if (loop_frame(inpacket_buf, n))
+		rv = 1;
+	return rv;
+    }
+
+    while ((n = read(master_fd, inbuf, sizeof(inbuf))) > 0)
+	if (loop_chars(inbuf, n))
+	    rv = 1;
+
+    if (n == 0)
+	fatal("eof on loopback");
+
+    if (errno != EWOULDBLOCK && errno != EAGAIN)
+	fatal("read from loopback: %m(%d)", errno);
+
+    return rv;
+}
+
+/*
+ * netif_set_mtu - set the MTU on the PPP network interface.
+ */
+void
+netif_set_mtu(int unit, int mtu)
+{
+    struct ifreq ifr;
+
+    memset (&ifr, '\0', sizeof (ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    ifr.ifr_mtu = mtu;
+
+    if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
+	error("ioctl(SIOCSIFMTU): %m (line %d)", __LINE__);
+}
+
+/*
+ * netif_get_mtu - get the MTU on the PPP network interface.
+ */
+int
+netif_get_mtu(int unit)
+{
+    struct ifreq ifr;
+
+    memset (&ifr, '\0', sizeof (ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+
+    if (ifunit >= 0 && ioctl(sock_fd, SIOCGIFMTU, (caddr_t) &ifr) < 0) {
+	error("ioctl(SIOCGIFMTU): %m (line %d)", __LINE__);
+	return 0;
+    }
+    return ifr.ifr_mtu;
+}
+
+/********************************************************************
+ *
+ * tty_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+
+void tty_send_config(int mtu, u_int32_t asyncmap, int pcomp, int accomp)
+{
+	int x;
+
+	if (!still_ppp())
+		return;
+	link_mtu = mtu;
+	if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
+		if (errno != EIO && errno != ENOTTY)
+			error("Couldn't set transmit async character map: %m");
+		++error_count;
+		return;
+	}
+
+	x = (pcomp? SC_COMP_PROT: 0) | (accomp? SC_COMP_AC: 0)
+	    | (sync_serial? SC_SYNC: 0);
+	modify_flags(ppp_fd, SC_COMP_PROT|SC_COMP_AC|SC_SYNC, x);
+}
+
+/********************************************************************
+ *
+ * tty_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+
+void tty_set_xaccm (ext_accm accm)
+{
+    if (!still_ppp())
+	return;
+    if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) {
+	if ( ! ok_error (errno))
+	    warn("ioctl(set extended ACCM): %m (line %d)", __LINE__);
+    }
+}
+
+/********************************************************************
+ *
+ * tty_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+
+void tty_recv_config(int mru, u_int32_t asyncmap, int pcomp, int accomp)
+{
+/*
+ * If we were called because the link has gone down then there is nothing
+ * which may be done. Just return without incident.
+ */
+	if (!still_ppp())
+		return;
+/*
+ * Set the receiver parameters
+ */
+	if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
+		if (errno != EIO && errno != ENOTTY)
+			error("Couldn't set channel receive MRU: %m");
+	}
+	if (new_style_driver && ppp_dev_fd >= 0
+	    && ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0)
+		error("Couldn't set MRU in generic PPP layer: %m");
+
+	if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
+		if (errno != EIO && errno != ENOTTY)
+			error("Couldn't set channel receive asyncmap: %m");
+	}
+}
+
+/********************************************************************
+ *
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+
+int
+ccp_test(int unit, u_char *opt_ptr, int opt_len, int for_transmit)
+{
+    struct ppp_option_data data;
+
+    memset (&data, '\0', sizeof (data));
+    data.ptr      = opt_ptr;
+    data.length   = opt_len;
+    data.transmit = for_transmit;
+
+    if (ioctl(ppp_dev_fd, PPPIOCSCOMPRESS, (caddr_t) &data) >= 0)
+	return 1;
+
+    return (errno == ENOBUFS)? 0: -1;
+}
+
+/********************************************************************
+ *
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+
+void ccp_flags_set (int unit, int isopen, int isup)
+{
+	int x;
+
+	x = (isopen? SC_CCP_OPEN: 0) | (isup? SC_CCP_UP: 0);
+	if (still_ppp() && ppp_dev_fd >= 0)
+		modify_flags(ppp_dev_fd, SC_CCP_OPEN|SC_CCP_UP, x);
+}
+
+#ifdef PPP_FILTER
+/*
+ * set_filters - set the active and pass filters in the kernel driver.
+ */
+int set_filters(struct bpf_program *pass, struct bpf_program *active)
+{
+	struct sock_fprog fp;
+
+	fp.len = pass->bf_len;
+	fp.filter = (struct sock_filter *) pass->bf_insns;
+	if (ioctl(ppp_dev_fd, PPPIOCSPASS, &fp) < 0) {
+		if (errno == ENOTTY)
+			warn("kernel does not support PPP filtering");
+		else
+			error("Couldn't set pass-filter in kernel: %m");
+		return 0;
+	}
+	fp.len = active->bf_len;
+	fp.filter = (struct sock_filter *) active->bf_insns;
+	if (ioctl(ppp_dev_fd, PPPIOCSACTIVE, &fp) < 0) {
+		error("Couldn't set active-filter in kernel: %m");
+		return 0;
+	}
+	return 1;
+}
+#endif /* PPP_FILTER */
+
+/********************************************************************
+ *
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+    int u;
+    struct ppp_idle *ip;
+{
+    return ioctl(ppp_dev_fd, PPPIOCGIDLE, ip) >= 0;
+}
+
+/********************************************************************
+ *
+ * get_ppp_stats - return statistics for the link.
+ */
+int
+get_ppp_stats(u, stats)
+    int u;
+    struct pppd_stats *stats;
+{
+    struct ifpppstatsreq req;
+
+    memset (&req, 0, sizeof (req));
+
+    req.stats_ptr = (caddr_t) &req.stats;
+    strlcpy(req.ifr__name, ifname, sizeof(req.ifr__name));
+    if (ioctl(sock_fd, SIOCGPPPSTATS, &req) < 0) {
+	error("Couldn't get PPP statistics: %m");
+	return 0;
+    }
+    stats->bytes_in = req.stats.p.ppp_ibytes;
+    stats->bytes_out = req.stats.p.ppp_obytes;
+    stats->pkts_in = req.stats.p.ppp_ipackets;
+    stats->pkts_out = req.stats.p.ppp_opackets;
+    return 1;
+}
+
+/********************************************************************
+ *
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise.  This is necessary because of patent nonsense.
+ */
+
+int ccp_fatal_error (int unit)
+{
+	int flags;
+
+	if (ioctl(ppp_dev_fd, PPPIOCGFLAGS, &flags) < 0) {
+		error("Couldn't read compression error flags: %m");
+		flags = 0;
+	}
+	return flags & SC_DC_FERROR;
+}
+
+/********************************************************************
+ *
+ * path_to_procfs - find the path to the proc file system mount point
+ */
+static char proc_path[MAXPATHLEN];
+static int proc_path_len;
+
+static char *path_to_procfs(const char *tail)
+{
+    struct mntent *mntent;
+    FILE *fp;
+
+    if (proc_path_len == 0) {
+	/* Default the mount location of /proc */
+	strlcpy (proc_path, "/proc", sizeof(proc_path));
+	proc_path_len = 5;
+	fp = fopen(MOUNTED, "r");
+	if (fp != NULL) {
+	    while ((mntent = getmntent(fp)) != NULL) {
+		if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0)
+		    continue;
+		if (strcmp(mntent->mnt_type, "proc") == 0) {
+		    strlcpy(proc_path, mntent->mnt_dir, sizeof(proc_path));
+		    proc_path_len = strlen(proc_path);
+		    break;
+		}
+	    }
+	    fclose (fp);
+	}
+    }
+
+    strlcpy(proc_path + proc_path_len, tail,
+	    sizeof(proc_path) - proc_path_len);
+    return proc_path;
+}
+
+/*
+ * /proc/net/route parsing stuff.
+ */
+#define ROUTE_MAX_COLS	12
+FILE *route_fd = (FILE *) 0;
+static char route_buffer[512];
+static int route_dev_col, route_dest_col, route_gw_col;
+static int route_flags_col, route_mask_col;
+static int route_num_cols;
+
+static int open_route_table (void);
+static void close_route_table (void);
+static int read_route_table (struct rtentry *rt);
+
+/********************************************************************
+ *
+ * close_route_table - close the interface to the route table
+ */
+
+static void close_route_table (void)
+{
+    if (route_fd != (FILE *) 0) {
+	fclose (route_fd);
+	route_fd = (FILE *) 0;
+    }
+}
+
+/********************************************************************
+ *
+ * open_route_table - open the interface to the route table
+ */
+static char route_delims[] = " \t\n";
+
+static int open_route_table (void)
+{
+    char *path;
+
+    close_route_table();
+
+    path = path_to_procfs("/net/route");
+    route_fd = fopen (path, "r");
+    if (route_fd == NULL) {
+	error("can't open routing table %s: %m", path);
+	return 0;
+    }
+
+    route_dev_col = 0;		/* default to usual columns */
+    route_dest_col = 1;
+    route_gw_col = 2;
+    route_flags_col = 3;
+    route_mask_col = 7;
+    route_num_cols = 8;
+
+    /* parse header line */
+    if (fgets(route_buffer, sizeof(route_buffer), route_fd) != 0) {
+	char *p = route_buffer, *q;
+	int col;
+	for (col = 0; col < ROUTE_MAX_COLS; ++col) {
+	    int used = 1;
+	    if ((q = strtok(p, route_delims)) == 0)
+		break;
+	    if (strcasecmp(q, "iface") == 0)
+		route_dev_col = col;
+	    else if (strcasecmp(q, "destination") == 0)
+		route_dest_col = col;
+	    else if (strcasecmp(q, "gateway") == 0)
+		route_gw_col = col;
+	    else if (strcasecmp(q, "flags") == 0)
+		route_flags_col = col;
+	    else if (strcasecmp(q, "mask") == 0)
+		route_mask_col = col;
+	    else
+		used = 0;
+	    if (used && col >= route_num_cols)
+		route_num_cols = col + 1;
+	    p = NULL;
+	}
+    }
+
+    return 1;
+}
+
+/********************************************************************
+ *
+ * read_route_table - read the next entry from the route table
+ */
+
+static int read_route_table(struct rtentry *rt)
+{
+    char *cols[ROUTE_MAX_COLS], *p;
+    int col;
+
+    memset (rt, '\0', sizeof (struct rtentry));
+
+    if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0)
+	return 0;
+
+    p = route_buffer;
+    for (col = 0; col < route_num_cols; ++col) {
+	cols[col] = strtok(p, route_delims);
+	if (cols[col] == NULL)
+	    return 0;		/* didn't get enough columns */
+	p = NULL;
+    }
+
+    SIN_ADDR(rt->rt_dst) = strtoul(cols[route_dest_col], NULL, 16);
+    SIN_ADDR(rt->rt_gateway) = strtoul(cols[route_gw_col], NULL, 16);
+    SIN_ADDR(rt->rt_genmask) = strtoul(cols[route_mask_col], NULL, 16);
+
+    rt->rt_flags = (short) strtoul(cols[route_flags_col], NULL, 16);
+    rt->rt_dev   = cols[route_dev_col];
+
+    return 1;
+}
+
+/********************************************************************
+ *
+ * defaultroute_exists - determine if there is a default route
+ */
+
+static int defaultroute_exists (struct rtentry *rt)
+{
+    int result = 0;
+
+    if (!open_route_table())
+	return 0;
+
+    while (read_route_table(rt) != 0) {
+	if ((rt->rt_flags & RTF_UP) == 0)
+	    continue;
+
+	if (kernel_version > KVERSION(2,1,0) && SIN_ADDR(rt->rt_genmask) != 0)
+	    continue;
+	if (SIN_ADDR(rt->rt_dst) == 0L) {
+	    result = 1;
+	    break;
+	}
+    }
+
+    close_route_table();
+    return result;
+}
+
+/*
+ * have_route_to - determine if the system has any route to
+ * a given IP address.  `addr' is in network byte order.
+ * Return value is 1 if yes, 0 if no, -1 if don't know.
+ * For demand mode to work properly, we have to ignore routes
+ * through our own interface.
+ */
+int have_route_to(u_int32_t addr)
+{
+    struct rtentry rt;
+    int result = 0;
+
+    if (!open_route_table())
+	return -1;		/* don't know */
+
+    while (read_route_table(&rt)) {
+	if ((rt.rt_flags & RTF_UP) == 0 || strcmp(rt.rt_dev, ifname) == 0)
+	    continue;
+	if ((addr & SIN_ADDR(rt.rt_genmask)) == SIN_ADDR(rt.rt_dst)) {
+	    result = 1;
+	    break;
+	}
+    }
+
+    close_route_table();
+    return result;
+}
+
+/********************************************************************
+ *
+ * sifdefaultroute - assign a default route through the address given.
+ */
+
+int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway, u_int32_t metric, bool force)
+{
+    struct rtentry rt;
+
+    if (!force && defaultroute_exists(&rt) && strcmp(rt.rt_dev, ifname) != 0) {
+	if (rt.rt_flags & RTF_GATEWAY)
+	    error("not replacing existing default route via %I",
+		  SIN_ADDR(rt.rt_gateway));
+	else
+	    error("not replacing existing default route through %s",
+		  rt.rt_dev);
+	return 0;
+    }
+
+    memset (&rt, 0, sizeof (rt));
+    SET_SA_FAMILY (rt.rt_dst, AF_INET);
+
+    rt.rt_dev = ifname;
+    rt.rt_metric = metric + 1;
+
+    if (kernel_version > KVERSION(2,1,0)) {
+	SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+	SIN_ADDR(rt.rt_genmask) = 0L;
+    }
+
+    rt.rt_flags = RTF_UP;
+    if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+	if ( ! ok_error ( errno ))
+	    error("default route ioctl(SIOCADDRT): %m");
+	return 0;
+    }
+
+    have_default_route = 1;
+    return 1;
+}
+
+/********************************************************************
+ *
+ * cifdefaultroute - delete a default route through the address given.
+ */
+
+int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
+{
+    struct rtentry rt;
+
+    have_default_route = 0;
+
+    memset (&rt, '\0', sizeof (rt));
+    SET_SA_FAMILY (rt.rt_dst,     AF_INET);
+    SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+
+    rt.rt_dev = ifname;
+
+    if (kernel_version > KVERSION(2,1,0)) {
+	SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+	SIN_ADDR(rt.rt_genmask) = 0L;
+    }
+
+    rt.rt_flags = RTF_UP;
+    if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+	if (still_ppp()) {
+	    if ( ! ok_error ( errno ))
+		error("default route ioctl(SIOCDELRT): %m");
+	    return 0;
+	}
+    }
+
+    return 1;
+}
+
+int rtmetricfixup(int unit, u_int32_t hisaddr, u_int32_t metric)
+{
+    struct rtentry rt;
+
+    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);
+	((struct sockaddr_in *) &rt.rt_genmask)->sin_addr.s_addr = ~0L;
+    }
+    rt.rt_dev = ifname;
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = 0L;
+    ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr     = hisaddr;
+    rt.rt_flags = RTF_UP | RTF_HOST;
+    if (ioctl(sock_fd, SIOCDELRT, &rt) < 0) {
+	error("ioctl(SIOCDELRT): %m(%d)", errno);
+    }
+    rt.rt_metric = metric + 1;
+    if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+	warn("ioctl(SIOCADDRT): %m(%d)", errno);
+    }
+    return 1;
+}
+
+/********************************************************************
+ *
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+
+int sifproxyarp (int unit, u_int32_t his_adr)
+{
+    struct arpreq arpreq;
+    char *forw_path;
+
+    if (has_proxy_arp == 0) {
+	memset (&arpreq, '\0', sizeof(arpreq));
+
+	SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+	SIN_ADDR(arpreq.arp_pa) = his_adr;
+	arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+/*
+ * Get the hardware address of an interface on the same subnet
+ * as our local address.
+ */
+	if (!get_ether_addr(his_adr, &arpreq.arp_ha, proxy_arp_dev,
+			    sizeof(proxy_arp_dev))) {
+	    error("Cannot determine ethernet address for proxy ARP");
+	    return 0;
+	}
+	strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev));
+
+	if (ioctl(sock_fd, SIOCSARP, (caddr_t)&arpreq) < 0) {
+	    if ( ! ok_error ( errno ))
+		error("ioctl(SIOCSARP): %m");
+	    return 0;
+	}
+	proxy_arp_addr = his_adr;
+	has_proxy_arp = 1;
+
+	if (tune_kernel) {
+	    forw_path = path_to_procfs("/sys/net/ipv4/ip_forward");
+	    if (forw_path != 0) {
+		int fd = open(forw_path, O_WRONLY);
+		if (fd >= 0) {
+		    if (write(fd, "1", 1) != 1)
+			error("Couldn't enable IP forwarding: %m");
+		    close(fd);
+		}
+	    }
+	}
+    }
+
+    return 1;
+}
+
+/********************************************************************
+ *
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+
+int cifproxyarp (int unit, u_int32_t his_adr)
+{
+    struct arpreq arpreq;
+
+    if (has_proxy_arp) {
+	has_proxy_arp = 0;
+	memset (&arpreq, '\0', sizeof(arpreq));
+	SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+	SIN_ADDR(arpreq.arp_pa) = his_adr;
+	arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+	strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev));
+
+	if (ioctl(sock_fd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+	    if ( ! ok_error ( errno ))
+		warn("ioctl(SIOCDARP): %m");
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+/********************************************************************
+ *
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+
+static int get_ether_addr (u_int32_t ipaddr,
+			   struct sockaddr *hwaddr,
+			   char *name, int namelen)
+{
+    struct ifreq *ifr, *ifend;
+    u_int32_t ina, mask;
+    char *aliasp;
+    struct ifreq ifreq, bestifreq;
+    struct ifconf ifc;
+    struct ifreq ifs[MAX_IFS];
+
+    u_int32_t bestmask=0;
+    int found_interface = 0;
+
+    ifc.ifc_len = sizeof(ifs);
+    ifc.ifc_req = ifs;
+    if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
+	if ( ! ok_error ( errno ))
+	    error("ioctl(SIOCGIFCONF): %m (line %d)", __LINE__);
+	return 0;
+    }
+
+/*
+ * Scan through looking for an interface with an Internet
+ * address on the same subnet as `ipaddr'.
+ */
+    ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+    for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
+	if (ifr->ifr_addr.sa_family == AF_INET) {
+	    ina = SIN_ADDR(ifr->ifr_addr);
+	    strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+/*
+ * Check that the interface is up, and not point-to-point
+ * nor loopback.
+ */
+	    if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
+		continue;
+
+	    if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
+		continue;
+/*
+ * Get its netmask and check that it's on the right subnet.
+ */
+	    if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0)
+		continue;
+
+	    mask = SIN_ADDR(ifreq.ifr_addr);
+
+	    if (((ipaddr ^ ina) & mask) != 0)
+		continue; /* no match */
+	    /* matched */
+	    if (mask >= bestmask) {
+		/* Compare using >= instead of > -- it is possible for
+		   an interface to have a netmask of 0.0.0.0 */
+		found_interface = 1;
+		bestifreq = ifreq;
+		bestmask = mask;
+	    }
+	}
+    }
+
+    if (!found_interface) return 0;
+
+    strlcpy(name, bestifreq.ifr_name, namelen);
+
+    /* trim off the :1 in eth0:1 */
+    aliasp = strchr(name, ':');
+    if (aliasp != 0)
+	*aliasp = 0;
+
+    warn("found interface %s for proxy arp", name);
+/*
+ * Now get the hardware address.
+ */
+    memset (&bestifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+    if (ioctl (sock_fd, SIOCGIFHWADDR, &bestifreq) < 0) {
+	error("SIOCGIFHWADDR(%s): %m", bestifreq.ifr_name);
+	return 0;
+    }
+
+    memcpy (hwaddr,
+	    &bestifreq.ifr_hwaddr,
+	    sizeof (struct sockaddr));
+
+    return 1;
+}
+
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *name)
+{
+	struct ifreq ifreq;
+	int ret, sock_fd;
+
+	sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock_fd < 0)
+		return 0;
+	memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr));
+	strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
+	ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq);
+	close(sock_fd);
+	if (ret >= 0)
+		memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6);
+	return ret;
+}
+
+/*
+ * get_first_ethernet - return the name of the first ethernet-style
+ * interface on this system.
+ */
+char *
+get_first_ethernet()
+{
+	return "eth0";
+}
+
+/********************************************************************
+ *
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'.  If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+
+u_int32_t GetMask (u_int32_t addr)
+{
+    u_int32_t mask, nmask, ina;
+    struct ifreq *ifr, *ifend, ifreq;
+    struct ifconf ifc;
+    struct ifreq ifs[MAX_IFS];
+
+    addr = ntohl(addr);
+
+    if (IN_CLASSA(addr))	/* determine network mask for address class */
+	nmask = IN_CLASSA_NET;
+    else if (IN_CLASSB(addr))
+	    nmask = IN_CLASSB_NET;
+    else
+	    nmask = IN_CLASSC_NET;
+
+    /* class D nets are disallowed by bad_ip_adrs */
+    mask = netmask | htonl(nmask);
+/*
+ * Scan through the system's network interfaces.
+ */
+    ifc.ifc_len = sizeof(ifs);
+    ifc.ifc_req = ifs;
+    if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
+	if ( ! ok_error ( errno ))
+	    warn("ioctl(SIOCGIFCONF): %m (line %d)", __LINE__);
+	return mask;
+    }
+
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
+/*
+ * Check the interface's internet address.
+ */
+	if (ifr->ifr_addr.sa_family != AF_INET)
+	    continue;
+	ina = SIN_ADDR(ifr->ifr_addr);
+	if (((ntohl(ina) ^ addr) & nmask) != 0)
+	    continue;
+/*
+ * Check that the interface is up, and not point-to-point nor loopback.
+ */
+	strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+	if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
+	    continue;
+
+	if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
+	    continue;
+/*
+ * Get its netmask and OR it into our mask.
+ */
+	if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0)
+	    continue;
+	mask |= SIN_ADDR(ifreq.ifr_addr);
+	break;
+    }
+    return mask;
+}
+
+/********************************************************************
+ *
+ * Internal routine to decode the version.modification.patch level
+ */
+
+static void decode_version (char *buf, int *version,
+			    int *modification, int *patch)
+{
+    char *endp;
+
+    *version      = (int) strtoul (buf, &endp, 10);
+    *modification = 0;
+    *patch        = 0;
+
+    if (endp != buf && *endp == '.') {
+	buf = endp + 1;
+	*modification = (int) strtoul (buf, &endp, 10);
+	if (endp != buf && *endp == '.') {
+	    buf = endp + 1;
+	    *patch = (int) strtoul (buf, &buf, 10);
+	}
+    }
+}
+
+/********************************************************************
+ *
+ * Procedure to determine if the PPP line discipline is registered.
+ */
+
+static int
+ppp_registered(void)
+{
+    int local_fd;
+    int mfd = -1;
+    int ret = 0;
+    char slave[16];
+
+    /*
+     * We used to open the serial device and set it to the ppp line
+     * discipline here, in order to create a ppp unit.  But that is
+     * not a good idea - the user might have specified a device that
+     * they can't open (permission, or maybe it doesn't really exist).
+     * So we grab a pty master/slave pair and use that.
+     */
+    if (!get_pty(&mfd, &local_fd, slave, 0)) {
+	no_ppp_msg = "Couldn't determine if PPP is supported (no free ptys)";
+	return 0;
+    }
+
+    /*
+     * Try to put the device into the PPP discipline.
+     */
+    if (ioctl(local_fd, TIOCSETD, &ppp_disc) < 0) {
+	error("ioctl(TIOCSETD(PPP)): %m (line %d)", __LINE__);
+    } else
+	ret = 1;
+
+    close(local_fd);
+    close(mfd);
+    return ret;
+}
+
+/********************************************************************
+ *
+ * ppp_available - check whether the system has any ppp interfaces
+ * (in fact we check whether we can do an ioctl on ppp0).
+ */
+
+int ppp_available(void)
+{
+    int s, ok, fd, err;
+    struct ifreq ifr;
+    int    size;
+    int    my_version, my_modification, my_patch;
+    int osmaj, osmin, ospatch;
+
+    /* get the kernel version now, since we are called before sys_init */
+    uname(&utsname);
+    osmaj = osmin = ospatch = 0;
+    sscanf(utsname.release, "%d.%d.%d", &osmaj, &osmin, &ospatch);
+    kernel_version = KVERSION(osmaj, osmin, ospatch);
+
+    fd = open("/dev/ppp", O_RDWR);
+    if (fd >= 0) {
+	new_style_driver = 1;
+
+	/* XXX should get from driver */
+	driver_version = 2;
+	driver_modification = 4;
+	driver_patch = 0;
+	close(fd);
+	return 1;
+    }
+    err = errno;
+
+    if (kernel_version >= KVERSION(2,3,13)) {
+	error("Couldn't open the /dev/ppp device: %m");
+	if (errno == ENOENT)
+	    no_ppp_msg =
+		"You need to create the /dev/ppp device node by\n"
+		"executing the following command as root:\n"
+		"	mknod /dev/ppp c 108 0\n";
+	else if (errno == ENODEV || errno == ENXIO)
+	    no_ppp_msg =
+		"Please load the ppp_generic kernel module.\n";
+	return 0;
+    }
+
+    /* we are running on a really really old kernel */
+    no_ppp_msg =
+	"This system lacks kernel support for PPP.  This could be because\n"
+	"the PPP kernel module could not be loaded, or because PPP was not\n"
+	"included in the kernel configuration.  If PPP was included as a\n"
+	"module, try `/sbin/modprobe -v ppp'.  If that fails, check that\n"
+	"ppp.o exists in /lib/modules/`uname -r`/net.\n"
+	"See README.linux file in the ppp distribution for more details.\n";
+
+/*
+ * Open a socket for doing the ioctl operations.
+ */
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0)
+	return 0;
+
+    strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+    ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+/*
+ * If the device did not exist then attempt to create one by putting the
+ * current tty into the PPP discipline. If this works then obtain the
+ * flags for the device again.
+ */
+    if (!ok) {
+	if (ppp_registered()) {
+	    strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+	    ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+	}
+    }
+/*
+ * Ensure that the hardware address is for PPP and not something else
+ */
+    if (ok)
+	ok = ioctl (s, SIOCGIFHWADDR, (caddr_t) &ifr) >= 0;
+
+    if (ok && ((ifr.ifr_hwaddr.sa_family & ~0xFF) != ARPHRD_PPP))
+	ok = 0;
+
+/*
+ *  This is the PPP device. Validate the version of the driver at this
+ *  point to ensure that this program will work with the driver.
+ */
+    if (ok) {
+	char   abBuffer [1024];
+
+	ifr.ifr_data = abBuffer;
+	size = ioctl (s, SIOCGPPPVER, (caddr_t) &ifr);
+	if (size < 0) {
+	    error("Couldn't read driver version: %m");
+	    ok = 0;
+	    no_ppp_msg = "Sorry, couldn't verify kernel driver version\n";
+
+	} else {
+	    decode_version(abBuffer,
+			   &driver_version,
+			   &driver_modification,
+			   &driver_patch);
+/*
+ * Validate the version of the driver against the version that we used.
+ */
+	    decode_version(VERSION,
+			   &my_version,
+			   &my_modification,
+			   &my_patch);
+
+	    /* The version numbers must match */
+	    if (driver_version != my_version)
+		ok = 0;
+
+	    /* The modification levels must be legal */
+	    if (driver_modification < 3) {
+		if (driver_modification >= 2) {
+		    /* we can cope with 2.2.0 and above */
+		    driver_is_old = 1;
+		} else {
+		    ok = 0;
+		}
+	    }
+
+	    close (s);
+	    if (!ok) {
+		slprintf(route_buffer, sizeof(route_buffer),
+			 "Sorry - PPP driver version %d.%d.%d is out of date\n",
+			 driver_version, driver_modification, driver_patch);
+
+		no_ppp_msg = route_buffer;
+	    }
+	}
+    }
+    return ok;
+}
+
+/********************************************************************
+ *
+ * Update the wtmp file with the appropriate user name and tty device.
+ */
+
+void logwtmp (const char *line, const char *name, const char *host)
+{
+    struct utmp ut, *utp;
+    pid_t  mypid = getpid();
+#if __GLIBC__ < 2
+    int    wtmp;
+#endif
+
+/*
+ * Update the signon database for users.
+ * Christoph Lameter: Copied from poeigl-1.36 Jan 3, 1996
+ */
+    utmpname(_PATH_UTMP);
+    setutent();
+    while ((utp = getutent()) && (utp->ut_pid != mypid))
+	/* nothing */;
+
+    if (utp)
+	memcpy(&ut, utp, sizeof(ut));
+    else
+	/* some gettys/telnetds don't initialize utmp... */
+	memset(&ut, 0, sizeof(ut));
+
+    if (ut.ut_id[0] == 0)
+	strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
+
+    strncpy(ut.ut_user, name, sizeof(ut.ut_user));
+    strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+
+    time(&ut.ut_time);
+
+    ut.ut_type = USER_PROCESS;
+    ut.ut_pid  = mypid;
+
+    /* Insert the host name if one is supplied */
+    if (*host)
+	strncpy (ut.ut_host, host, sizeof(ut.ut_host));
+
+    /* Insert the IP address of the remote system if IP is enabled */
+    if (ipcp_protent.enabled_flag && ipcp_hisoptions[0].neg_addr)
+	memcpy(&ut.ut_addr, (char *) &ipcp_hisoptions[0].hisaddr,
+		 sizeof(ut.ut_addr));
+
+    /* CL: Makes sure that the logout works */
+    if (*host == 0 && *name==0)
+	ut.ut_host[0]=0;
+
+    pututline(&ut);
+    endutent();
+/*
+ * Update the wtmp file.
+ */
+#if __GLIBC__ >= 2
+    updwtmp(_PATH_WTMP, &ut);
+#else
+    wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY);
+    if (wtmp >= 0) {
+	flock(wtmp, LOCK_EX);
+
+	if (write (wtmp, (char *)&ut, sizeof(ut)) != sizeof(ut))
+	    warn("error writing %s: %m", _PATH_WTMP);
+
+	flock(wtmp, LOCK_UN);
+
+	close (wtmp);
+    }
+#endif
+}
+
+
+/********************************************************************
+ *
+ * sifvjcomp - config tcp header compression
+ */
+
+int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid)
+{
+	u_int x;
+
+	if (vjcomp) {
+		if (ioctl(ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0)
+			error("Couldn't set up TCP header compression: %m");
+		vjcomp = 0;
+	}
+
+	x = (vjcomp? SC_COMP_TCP: 0) | (cidcomp? 0: SC_NO_TCP_CCID);
+	modify_flags(ppp_dev_fd, SC_COMP_TCP|SC_NO_TCP_CCID, x);
+
+	return 1;
+}
+
+/********************************************************************
+ *
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+
+int sifup(int u)
+{
+    struct ifreq ifr;
+
+    memset (&ifr, '\0', sizeof (ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+	if (! ok_error (errno))
+	    error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__);
+	return 0;
+    }
+
+    ifr.ifr_flags |= (IFF_UP | IFF_POINTOPOINT);
+    if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+	if (! ok_error (errno))
+	    error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__);
+	return 0;
+    }
+    if_is_up++;
+
+    return 1;
+}
+
+/********************************************************************
+ *
+ * sifdown - Disable the indicated protocol and config the interface
+ *	     down if there are no remaining protocols.
+ */
+
+int sifdown (int u)
+{
+    struct ifreq ifr;
+
+    if (if_is_up && --if_is_up > 0)
+	return 1;
+
+    memset (&ifr, '\0', sizeof (ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+	if (! ok_error (errno))
+	    error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__);
+	return 0;
+    }
+
+    ifr.ifr_flags &= ~IFF_UP;
+    ifr.ifr_flags |= IFF_POINTOPOINT;
+    if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+	if (! ok_error (errno))
+	    error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__);
+	return 0;
+    }
+    return 1;
+}
+
+/********************************************************************
+ *
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+
+int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
+	     u_int32_t net_mask)
+{
+    struct ifreq   ifr;
+    struct rtentry rt;
+
+    memset (&ifr, '\0', sizeof (ifr));
+    memset (&rt,  '\0', sizeof (rt));
+
+    SET_SA_FAMILY (ifr.ifr_addr,    AF_INET);
+    SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET);
+    SET_SA_FAMILY (ifr.ifr_netmask, AF_INET);
+
+    strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+/*
+ *  Set our IP address
+ */
+    SIN_ADDR(ifr.ifr_addr) = our_adr;
+    if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+	if (errno != EEXIST) {
+	    if (! ok_error (errno))
+		error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
+	}
+	else {
+	    warn("ioctl(SIOCSIFADDR): Address already exists");
+	}
+	return (0);
+    }
+/*
+ *  Set the gateway address
+ */
+    if (his_adr != 0) {
+	SIN_ADDR(ifr.ifr_dstaddr) = his_adr;
+	if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+	    if (! ok_error (errno))
+		error("ioctl(SIOCSIFDSTADDR): %m (line %d)", __LINE__);
+	    return (0);
+	}
+    }
+/*
+ *  Set the netmask.
+ *  For recent kernels, force the netmask to 255.255.255.255.
+ */
+    if (kernel_version >= KVERSION(2,1,16))
+	net_mask = ~0L;
+    if (net_mask != 0) {
+	SIN_ADDR(ifr.ifr_netmask) = net_mask;
+	if (ioctl(sock_fd, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) {
+	    if (! ok_error (errno))
+		error("ioctl(SIOCSIFNETMASK): %m (line %d)", __LINE__);
+	    return (0);
+	}
+    }
+/*
+ *  Add the device route
+ */
+    if (kernel_version < KVERSION(2,1,16)) {
+	SET_SA_FAMILY (rt.rt_dst,     AF_INET);
+	SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+	rt.rt_dev = ifname;
+
+	SIN_ADDR(rt.rt_gateway) = 0L;
+	SIN_ADDR(rt.rt_dst)     = his_adr;
+	rt.rt_flags = RTF_UP | RTF_HOST;
+
+	if (kernel_version > KVERSION(2,1,0)) {
+	    SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+	    SIN_ADDR(rt.rt_genmask) = -1L;
+	}
+
+	if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+	    if (! ok_error (errno))
+		error("ioctl(SIOCADDRT) device route: %m (line %d)", __LINE__);
+	    return (0);
+	}
+    }
+
+    /* set ip_dynaddr in demand mode if address changes */
+    if (demand && tune_kernel && !dynaddr_set
+	&& our_old_addr && our_old_addr != our_adr) {
+	/* set ip_dynaddr if possible */
+	char *path;
+	int fd;
+
+	path = path_to_procfs("/sys/net/ipv4/ip_dynaddr");
+	if (path != 0 && (fd = open(path, O_WRONLY)) >= 0) {
+	    if (write(fd, "1", 1) != 1)
+		error("Couldn't enable dynamic IP addressing: %m");
+	    close(fd);
+	}
+	dynaddr_set = 1;	/* only 1 attempt */
+    }
+    our_old_addr = 0;
+
+    return 1;
+}
+
+/********************************************************************
+ *
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+
+int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr)
+{
+    struct ifreq ifr;
+
+    if (kernel_version < KVERSION(2,1,16)) {
+/*
+ *  Delete the route through the device
+ */
+	struct rtentry rt;
+	memset (&rt, '\0', sizeof (rt));
+
+	SET_SA_FAMILY (rt.rt_dst,     AF_INET);
+	SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+	rt.rt_dev = ifname;
+
+	SIN_ADDR(rt.rt_gateway) = 0;
+	SIN_ADDR(rt.rt_dst)     = his_adr;
+	rt.rt_flags = RTF_UP | RTF_HOST;
+
+	if (kernel_version > KVERSION(2,1,0)) {
+	    SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+	    SIN_ADDR(rt.rt_genmask) = -1L;
+	}
+
+	if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+	    if (still_ppp() && ! ok_error (errno))
+		error("ioctl(SIOCDELRT) device route: %m (line %d)", __LINE__);
+	    return (0);
+	}
+    }
+
+    /* This way it is possible to have an IPX-only or IPv6-only interface */
+    memset(&ifr, 0, sizeof(ifr));
+    SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+    if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+	if (! ok_error (errno)) {
+	    error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
+	    return 0;
+	}
+    }
+
+    our_old_addr = our_adr;
+
+    return 1;
+}
+
+#ifdef INET6
+/********************************************************************
+ *
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+    struct in6_ifreq ifr6;
+    struct ifreq ifr;
+    struct in6_rtmsg rt6;
+
+    if (sock6_fd < 0) {
+	errno = -sock6_fd;
+	error("IPv6 socket creation failed: %m");
+	return 0;
+    }
+    memset(&ifr, 0, sizeof (ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
+	error("sif6addr: ioctl(SIOCGIFINDEX): %m (line %d)", __LINE__);
+	return 0;
+    }
+
+    /* Local interface */
+    memset(&ifr6, 0, sizeof(ifr6));
+    IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
+    ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+    ifr6.ifr6_prefixlen = 10;
+
+    if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6) < 0) {
+	error("sif6addr: ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
+	return 0;
+    }
+
+    /* Route to remote host */
+    memset(&rt6, 0, sizeof(rt6));
+    IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64);
+    rt6.rtmsg_flags = RTF_UP;
+    rt6.rtmsg_dst_len = 10;
+    rt6.rtmsg_ifindex = ifr.ifr_ifindex;
+    rt6.rtmsg_metric = 1;
+
+    if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) {
+	error("sif6addr: ioctl(SIOCADDRT): %m (line %d)", __LINE__);
+	return 0;
+    }
+
+    return 1;
+}
+
+
+/********************************************************************
+ *
+ * cif6addr - Remove IPv6 address from interface
+ */
+int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+    struct ifreq ifr;
+    struct in6_ifreq ifr6;
+
+    if (sock6_fd < 0) {
+	errno = -sock6_fd;
+	error("IPv6 socket creation failed: %m");
+	return 0;
+    }
+    memset(&ifr, 0, sizeof(ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
+	error("cif6addr: ioctl(SIOCGIFINDEX): %m (line %d)", __LINE__);
+	return 0;
+    }
+
+    memset(&ifr6, 0, sizeof(ifr6));
+    IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
+    ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+    ifr6.ifr6_prefixlen = 10;
+
+    if (ioctl(sock6_fd, SIOCDIFADDR, &ifr6) < 0) {
+	if (errno != EADDRNOTAVAIL) {
+	    if (! ok_error (errno))
+		error("cif6addr: ioctl(SIOCDIFADDR): %m (line %d)", __LINE__);
+	}
+	else {
+	    warn("cif6addr: ioctl(SIOCDIFADDR): No such address");
+	}
+	return (0);
+    }
+    return 1;
+}
+#endif /* INET6 */
+
+/*
+ * get_pty - get a pty master/slave pair and chown the slave side
+ * to the uid given.  Assumes slave_name points to >= 16 bytes of space.
+ */
+int
+get_pty(master_fdp, slave_fdp, slave_name, uid)
+    int *master_fdp;
+    int *slave_fdp;
+    char *slave_name;
+    int uid;
+{
+    int i, mfd, sfd = -1;
+    char pty_name[16];
+    struct termios tios;
+
+#ifdef TIOCGPTN
+    /*
+     * Try the unix98 way first.
+     */
+    mfd = open("/dev/ptmx", O_RDWR);
+    if (mfd >= 0) {
+	int ptn;
+	if (ioctl(mfd, TIOCGPTN, &ptn) >= 0) {
+	    slprintf(pty_name, sizeof(pty_name), "/dev/pts/%d", ptn);
+	    chmod(pty_name, S_IRUSR | S_IWUSR);
+#ifdef TIOCSPTLCK
+	    ptn = 0;
+	    if (ioctl(mfd, TIOCSPTLCK, &ptn) < 0)
+		warn("Couldn't unlock pty slave %s: %m", pty_name);
+#endif
+	    if ((sfd = open(pty_name, O_RDWR | O_NOCTTY)) < 0)
+		warn("Couldn't open pty slave %s: %m", pty_name);
+	}
+    }
+#endif /* TIOCGPTN */
+
+    if (sfd < 0) {
+	/* the old way - scan through the pty name space */
+	for (i = 0; i < 64; ++i) {
+	    slprintf(pty_name, sizeof(pty_name), "/dev/pty%c%x",
+		     'p' + i / 16, i % 16);
+	    mfd = open(pty_name, O_RDWR, 0);
+	    if (mfd >= 0) {
+		pty_name[5] = 't';
+		sfd = open(pty_name, O_RDWR | O_NOCTTY, 0);
+		if (sfd >= 0) {
+		    fchown(sfd, uid, -1);
+		    fchmod(sfd, S_IRUSR | S_IWUSR);
+		    break;
+		}
+		close(mfd);
+	    }
+	}
+    }
+
+    if (sfd < 0)
+	return 0;
+
+    strlcpy(slave_name, pty_name, 16);
+    *master_fdp = mfd;
+    *slave_fdp = sfd;
+    if (tcgetattr(sfd, &tios) == 0) {
+	tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+	tios.c_cflag |= CS8 | CREAD | CLOCAL;
+	tios.c_iflag  = IGNPAR;
+	tios.c_oflag  = 0;
+	tios.c_lflag  = 0;
+	if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0)
+	    warn("couldn't set attributes on pty: %m");
+    } else
+	warn("couldn't get attributes on pty: %m");
+
+    return 1;
+}
+
+/********************************************************************
+ *
+ * open_loopback - open the device we use for getting packets
+ * in demand mode.  Under Linux, we use a pty master/slave pair.
+ */
+int
+open_ppp_loopback(void)
+{
+    int flags;
+
+    looped = 1;
+    if (new_style_driver) {
+	/* allocate ourselves a ppp unit */
+	if (make_ppp_unit() < 0)
+	    die(1);
+	modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC);
+	set_kdebugflag(kdebugflag);
+	ppp_fd = -1;
+	return ppp_dev_fd;
+    }
+
+    if (!get_pty(&master_fd, &slave_fd, loop_name, 0))
+	fatal("No free pty for loopback");
+
+    set_ppp_fd(slave_fd);
+
+    flags = fcntl(master_fd, F_GETFL);
+    if (flags == -1 ||
+	fcntl(master_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+	warn("couldn't set master loopback to nonblock: %m");
+
+    flags = fcntl(ppp_fd, F_GETFL);
+    if (flags == -1 ||
+	fcntl(ppp_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+	warn("couldn't set slave loopback to nonblock: %m");
+
+    if (ioctl(ppp_fd, TIOCSETD, &ppp_disc) < 0)
+	fatal("ioctl(TIOCSETD): %m (line %d)", __LINE__);
+/*
+ * Find out which interface we were given.
+ */
+    if (ioctl(ppp_fd, PPPIOCGUNIT, &ifunit) < 0)
+	fatal("ioctl(PPPIOCGUNIT): %m (line %d)", __LINE__);
+/*
+ * Enable debug in the driver if requested.
+ */
+    set_kdebugflag (kdebugflag);
+
+    return master_fd;
+}
+
+/********************************************************************
+ *
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+
+int
+sifnpmode(u, proto, mode)
+    int u;
+    int proto;
+    enum NPmode mode;
+{
+    struct npioctl npi;
+
+    npi.protocol = proto;
+    npi.mode     = mode;
+    if (ioctl(ppp_dev_fd, PPPIOCSNPMODE, (caddr_t) &npi) < 0) {
+	if (! ok_error (errno))
+	    error("ioctl(PPPIOCSNPMODE, %d, %d): %m", proto, mode);
+	return 0;
+    }
+    return 1;
+}
+
+
+/********************************************************************
+ *
+ * sipxfaddr - Config the interface IPX networknumber
+ */
+
+int sipxfaddr (int unit, unsigned long int network, unsigned char * node )
+{
+    int    result = 1;
+
+#ifdef IPX_CHANGE
+    int    skfd;
+    struct ifreq         ifr;
+    struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+
+    skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+    if (skfd < 0) {
+	if (! ok_error (errno))
+	    warn("socket(AF_IPX): %m (line %d)", __LINE__);
+	result = 0;
+    }
+    else {
+	memset (&ifr, '\0', sizeof (ifr));
+	strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+	memcpy (sipx->sipx_node, node, IPX_NODE_LEN);
+	sipx->sipx_family  = AF_IPX;
+	sipx->sipx_port    = 0;
+	sipx->sipx_network = htonl (network);
+	sipx->sipx_type    = IPX_FRAME_ETHERII;
+	sipx->sipx_action  = IPX_CRTITF;
+/*
+ *  Set the IPX device
+ */
+	if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+	    result = 0;
+	    if (errno != EEXIST) {
+		if (! ok_error (errno))
+		    warn("ioctl(SIOCSIFADDR, CRTITF): %m (line %d)", __LINE__);
+	    }
+	    else {
+		warn("ioctl(SIOCSIFADDR, CRTITF): Address already exists");
+	    }
+	}
+	close (skfd);
+    }
+#endif
+    return result;
+}
+
+/********************************************************************
+ *
+ * cipxfaddr - Clear the information for the IPX network. The IPX routes
+ *	       are removed and the device is no longer able to pass IPX
+ *	       frames.
+ */
+
+int cipxfaddr (int unit)
+{
+    int    result = 1;
+
+#ifdef IPX_CHANGE
+    int    skfd;
+    struct ifreq         ifr;
+    struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+
+    skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+    if (skfd < 0) {
+	if (! ok_error (errno))
+	    warn("socket(AF_IPX): %m (line %d)", __LINE__);
+	result = 0;
+    }
+    else {
+	memset (&ifr, '\0', sizeof (ifr));
+	strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+	sipx->sipx_type    = IPX_FRAME_ETHERII;
+	sipx->sipx_action  = IPX_DLTITF;
+	sipx->sipx_family  = AF_IPX;
+/*
+ *  Set the IPX device
+ */
+	if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+	    if (! ok_error (errno))
+		warn("ioctl(SIOCSIFADDR, IPX_DLTITF): %m (line %d)", __LINE__);
+	    result = 0;
+	}
+	close (skfd);
+    }
+#endif
+    return result;
+}
+
+/*
+ * Use the hostname as part of the random number seed.
+ */
+int
+get_host_seed()
+{
+    int h;
+    char *p = hostname;
+
+    h = 407;
+    for (p = hostname; *p != 0; ++p)
+	h = h * 37 + *p;
+    return h;
+}
+
+/********************************************************************
+ *
+ * sys_check_options - check the options that the user specified
+ */
+
+int
+sys_check_options(void)
+{
+#ifdef IPX_CHANGE
+/*
+ * Disable the IPX protocol if the support is not present in the kernel.
+ */
+    char *path;
+
+    if (ipxcp_protent.enabled_flag) {
+	struct stat stat_buf;
+	if (  ((path = path_to_procfs("/net/ipx/interface")) == NULL
+	    && (path = path_to_procfs("/net/ipx_interface")) == NULL)
+	    || lstat(path, &stat_buf) < 0) {
+	    error("IPX support is not present in the kernel\n");
+	    ipxcp_protent.enabled_flag = 0;
+	}
+    }
+#endif
+    if (demand && driver_is_old) {
+	option_error("demand dialling is not supported by kernel driver "
+		     "version %d.%d.%d", driver_version, driver_modification,
+		     driver_patch);
+	return 0;
+    }
+    if (multilink && !new_style_driver) {
+	warn("Warning: multilink is not supported by the kernel driver");
+	multilink = 0;
+    }
+    return 1;
+}
+
+#ifdef INET6
+/*
+ * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI
+ *
+ * convert the 48-bit MAC address of eth0 into EUI 64. caller also assumes
+ * that the system has a properly configured Ethernet interface for this
+ * function to return non-zero.
+ */
+int
+ether_to_eui64(eui64_t *p_eui64)
+{
+    struct ifreq ifr;
+    int skfd;
+    const unsigned char *ptr;
+
+    skfd = socket(PF_INET6, SOCK_DGRAM, 0);
+    if(skfd == -1)
+    {
+        warn("could not open IPv6 socket");
+        return 0;
+    }
+
+    strcpy(ifr.ifr_name, "eth0");
+    if(ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
+    {
+        close(skfd);
+        warn("could not obtain hardware address for eth0");
+        return 0;
+    }
+    close(skfd);
+
+    /*
+     * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
+     */
+    ptr = ifr.ifr_hwaddr.sa_data;
+    p_eui64->e8[0] = ptr[0] | 0x02;
+    p_eui64->e8[1] = ptr[1];
+    p_eui64->e8[2] = ptr[2];
+    p_eui64->e8[3] = 0xFF;
+    p_eui64->e8[4] = 0xFE;
+    p_eui64->e8[5] = ptr[3];
+    p_eui64->e8[6] = ptr[4];
+    p_eui64->e8[7] = ptr[5];
+
+    return 1;
+}
+#endif
diff --git a/ap/app/pppd/pppd/sys-solaris.c b/ap/app/pppd/pppd/sys-solaris.c
new file mode 100644
index 0000000..50ce3df
--- /dev/null
+++ b/ap/app/pppd/pppd/sys-solaris.c
@@ -0,0 +1,2782 @@
+/*
+ * 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) 1995-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from main.c and pppd.h, which are:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: sys-solaris.c,v 1.16 2008/01/30 14:26:53 carlsonj Exp $"
+
+#include <limits.h>
+#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>
+#ifndef CRTSCTS
+#include <sys/termiox.h>
+#endif
+#include <signal.h>
+#include <utmpx.h>
+#include <stropts.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 = 64;			\
+	l.lifr_addr = laddr;			\
+	} while (0)
+
+#define IN6_LLADDR_FROM_EUI64(l, s, eui64)  \
+    _IN6_LLX_FROM_EUI64(l, s, eui64, 0xfe800000)
+
+#define IN6_LLTOKEN_FROM_EUI64(l, s, eui64) \
+    _IN6_LLX_FROM_EUI64(l, s, eui64, 0)
+
+#endif /* defined(INET6) && defined(SOL2) */
+
+#if defined(INET6) && defined(SOL2)
+static char	first_ether_name[LIFNAMSIZ];	/* Solaris 8 and above */
+#else
+static char	first_ether_name[IFNAMSIZ];	/* Before Solaris 8 */
+#define MAXIFS		256			/* Max # of interfaces */
+#endif /* defined(INET6) && defined(SOL2) */
+
+static int	restore_term;
+static struct termios inittermios;
+#ifndef CRTSCTS
+static struct termiox inittermiox;
+static int	termiox_ok;
+#endif
+static struct winsize wsinfo;	/* Initial window size info */
+static pid_t	tty_sid;	/* original session ID for terminal */
+
+extern u_char	inpacket_buf[];	/* borrowed from main.c */
+
+#define MAX_POLLFDS	32
+static struct pollfd pollfds[MAX_POLLFDS];
+static int n_pollfds;
+
+static int	link_mtu, link_mru;
+
+#define NMODULES	32
+static int	tty_nmodules;
+static char	tty_modules[NMODULES][FMNAMESZ+1];
+static int	tty_npushed;
+
+static int	if_is_up;	/* Interface has been marked up */
+static u_int32_t remote_addr;		/* IP address of peer */
+static u_int32_t default_route_gateway;	/* Gateway for default route added */
+static u_int32_t proxy_arp_addr;	/* Addr for proxy arp entry added */
+
+/* Prototypes for procedures local to this file. */
+static int translate_speed __P((int));
+static int baud_rate_of __P((int));
+static int get_ether_addr __P((u_int32_t, struct sockaddr *));
+static int get_hw_addr __P((char *, u_int32_t, struct sockaddr *));
+static int get_hw_addr_dlpi __P((char *, struct sockaddr *));
+static int dlpi_attach __P((int, int));
+static int dlpi_info_req __P((int));
+static int dlpi_get_reply __P((int, union DL_primitives *, int, int));
+static int strioctl __P((int, int, void *, int, int));
+
+#ifdef SOL2
+/*
+ * sifppa - Sets interface ppa
+ *
+ * without setting the ppa, ip module will return EINVAL upon setting the
+ * interface UP (SIOCSxIFFLAGS). This is because ip module in 2.8 expects
+ * two DLPI_INFO_REQ to be sent down to the driver (below ip) before
+ * IFF_UP can be set. Plumbing the device causes one DLPI_INFO_REQ to
+ * be sent down, and the second DLPI_INFO_REQ is sent upon receiving
+ * IF_UNITSEL (old) or SIOCSLIFNAME (new) ioctls. Such setting of the ppa
+ * is required because the ppp DLPI provider advertises itself as
+ * a DLPI style 2 type, which requires a point of attachment to be
+ * specified. The only way the user can specify a point of attachment
+ * is via SIOCSLIFNAME or IF_UNITSEL.
+ *
+ * Such changes in the behavior of ip module was made to meet new or
+ * evolving standards requirements.
+ *
+ */
+static int
+sifppa(fd, ppa)
+    int fd;
+    int ppa;
+{
+    return (int)ioctl(fd, IF_UNITSEL, (char *)&ppa);
+}
+#endif /* SOL2 */
+
+#if defined(SOL2) && defined(INET6)
+/*
+ * get_first_ethernet - returns the first Ethernet interface name found in 
+ * the system, or NULL if none is found
+ *
+ * NOTE: This is the lifreq version (Solaris 8 and above)
+ */
+char *
+get_first_ethernet()
+{
+    struct lifnum lifn;
+    struct lifconf lifc;
+    struct lifreq *plifreq;
+    struct lifreq lifr;
+    int	fd, num_ifs, i, found;
+    uint_t fl, req_size;
+    char *req;
+
+    fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (fd < 0) {
+	return 0;
+    }
+
+    /*
+     * Find out how many interfaces are running
+     */
+    lifn.lifn_family = AF_UNSPEC;
+    lifn.lifn_flags = LIFC_NOXMIT;
+    if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0) {
+	close(fd);
+	error("could not determine number of interfaces: %m");
+	return 0;
+    }
+
+    num_ifs = lifn.lifn_count;
+    req_size = num_ifs * sizeof(struct lifreq);
+    req = malloc(req_size);
+    if (req == NULL) {
+	close(fd);
+	error("out of memory");
+	return 0;
+    }
+
+    /*
+     * Get interface configuration info for all interfaces
+     */
+    lifc.lifc_family = AF_UNSPEC;
+    lifc.lifc_flags = LIFC_NOXMIT;
+    lifc.lifc_len = req_size;
+    lifc.lifc_buf = req;
+    if (ioctl(fd, SIOCGLIFCONF, &lifc) < 0) {
+	close(fd);
+	free(req);
+	error("SIOCGLIFCONF: %m");
+	return 0;
+    }
+
+    /*
+     * And traverse each interface to look specifically for the first
+     * occurence of an Ethernet interface which has been marked up
+     */
+    plifreq = lifc.lifc_req;
+    found = 0;
+    for (i = lifc.lifc_len / sizeof(struct lifreq); i > 0; i--, plifreq++) {
+
+	if (strchr(plifreq->lifr_name, ':') != NULL)
+	    continue;
+
+	memset(&lifr, 0, sizeof(lifr));
+	strncpy(lifr.lifr_name, plifreq->lifr_name, sizeof(lifr.lifr_name));
+	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+	    close(fd);
+	    free(req);
+	    error("SIOCGLIFFLAGS: %m");
+	    return 0;
+	}
+	fl = lifr.lifr_flags;
+
+	if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+		!= (IFF_UP | IFF_BROADCAST))
+	    continue;
+
+	found = 1;
+	break;
+    }
+    free(req);
+    close(fd);
+
+    if (found) {
+	strncpy(first_ether_name, lifr.lifr_name, sizeof(first_ether_name));
+	return (char *)first_ether_name;
+    } else
+	return NULL;
+}
+#else
+/*
+ * get_first_ethernet - returns the first Ethernet interface name found in 
+ * the system, or NULL if none is found
+ *
+ * NOTE: This is the ifreq version (before Solaris 8). 
+ */
+char *
+get_first_ethernet()
+{
+    struct ifconf ifc;
+    struct ifreq *pifreq;
+    struct ifreq ifr;
+    int	fd, num_ifs, i, found;
+    uint_t fl, req_size;
+    char *req;
+
+    fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (fd < 0) {
+	return 0;
+    }
+
+    /*
+     * Find out how many interfaces are running
+     */
+    if (ioctl(fd, SIOCGIFNUM, (char *)&num_ifs) < 0) {
+	num_ifs = MAXIFS;
+    }
+
+    req_size = num_ifs * sizeof(struct ifreq);
+    req = malloc(req_size);
+    if (req == NULL) {
+	close(fd);
+	error("out of memory");
+	return 0;
+    }
+
+    /*
+     * Get interface configuration info for all interfaces
+     */
+    ifc.ifc_len = req_size;
+    ifc.ifc_buf = req;
+    if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+	close(fd);
+	free(req);
+	error("SIOCGIFCONF: %m");
+	return 0;
+    }
+
+    /*
+     * And traverse each interface to look specifically for the first
+     * occurence of an Ethernet interface which has been marked up
+     */
+    pifreq = ifc.ifc_req;
+    found = 0;
+    for (i = ifc.ifc_len / sizeof(struct ifreq); i > 0; i--, pifreq++) {
+
+	if (strchr(pifreq->ifr_name, ':') != NULL)
+	    continue;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, pifreq->ifr_name, sizeof(ifr.ifr_name));
+	if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+	    close(fd);
+	    free(req);
+	    error("SIOCGIFFLAGS: %m");
+	    return 0;
+	}
+	fl = ifr.ifr_flags;
+
+	if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+		!= (IFF_UP | IFF_BROADCAST))
+	    continue;
+
+	found = 1;
+	break;
+    }
+    free(req);
+    close(fd);
+
+    if (found) {
+	strncpy(first_ether_name, ifr.ifr_name, sizeof(first_ether_name));
+	return (char *)first_ether_name;
+    } else
+	return NULL;
+}
+#endif /* defined(SOL2) && defined(INET6) */
+
+#if defined(SOL2)
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *if_name)
+{
+    struct sockaddr s_eth_addr;
+    struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data;
+
+    if (if_name == NULL)
+	return -1;
+
+    /*
+     * Send DL_INFO_REQ to the driver to solicit its MAC address
+     */
+    if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) {
+	error("could not obtain hardware address for %s", if_name);
+	return -1;
+    }
+
+    memcpy(addr, eth_addr->ether_addr_octet, 6);
+    return 1;
+}
+#endif /* SOL2 */
+
+#if defined(SOL2) && defined(INET6)
+/*
+ * slifname - Sets interface ppa and flags
+ *
+ * in addition to the comments stated in sifppa(), IFF_IPV6 bit must
+ * be set in order to declare this as an IPv6 interface
+ */
+static int
+slifname(fd, ppa)
+    int fd;
+    int ppa;
+{
+    struct  lifreq lifr;
+    int	    ret;
+
+    memset(&lifr, 0, sizeof(lifr));
+    ret = ioctl(fd, SIOCGLIFFLAGS, &lifr);
+    if (ret < 0)
+	goto slifname_done;
+
+    lifr.lifr_flags |= IFF_IPV6;
+    lifr.lifr_flags &= ~(IFF_BROADCAST | IFF_IPV4);
+    lifr.lifr_ppa = ppa;
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+
+    ret = ioctl(fd, SIOCSLIFNAME, &lifr);
+
+slifname_done:
+    return ret;
+
+
+}
+
+
+/*
+ * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI
+ *
+ * walks the list of valid ethernet interfaces, and convert the first
+ * found 48-bit MAC address into EUI 64. caller also assumes that
+ * the system has a properly configured Ethernet interface for this
+ * function to return non-zero.
+ */
+int
+ether_to_eui64(eui64_t *p_eui64)
+{
+    struct sockaddr s_eth_addr;
+    struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data;
+    char *if_name;
+
+    if ((if_name = get_first_ethernet()) == NULL) {
+	error("no persistent id can be found");
+	return 0;
+    }
+ 
+    /*
+     * Send DL_INFO_REQ to the driver to solicit its MAC address
+     */
+    if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) {
+	error("could not obtain hardware address for %s", if_name);
+	return 0;
+    }
+
+    /*
+     * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
+     */
+    p_eui64->e8[0] = (eth_addr->ether_addr_octet[0] & 0xFF) | 0x02;
+    p_eui64->e8[1] = (eth_addr->ether_addr_octet[1] & 0xFF);
+    p_eui64->e8[2] = (eth_addr->ether_addr_octet[2] & 0xFF);
+    p_eui64->e8[3] = 0xFF;
+    p_eui64->e8[4] = 0xFE;
+    p_eui64->e8[5] = (eth_addr->ether_addr_octet[3] & 0xFF);
+    p_eui64->e8[6] = (eth_addr->ether_addr_octet[4] & 0xFF);
+    p_eui64->e8[7] = (eth_addr->ether_addr_octet[5] & 0xFF);
+
+    return 1;
+}
+#endif /* defined(SOL2) && defined(INET6) */
+
+/*
+ * sys_init - System-dependent initialization.
+ */
+void
+sys_init()
+{
+    int ifd, x;
+    struct ifreq ifr;
+#if defined(INET6) && defined(SOL2)
+    int i6fd;
+    struct lifreq lifr;
+#endif /* defined(INET6) && defined(SOL2) */
+#if !defined(SOL2)
+    struct {
+	union DL_primitives prim;
+	char space[64];
+    } reply;
+#endif /* !defined(SOL2) */
+
+    ipfd = open(mux_dev_name, O_RDWR, 0);
+    if (ipfd < 0)
+	fatal("Couldn't open IP device: %m");
+
+#if defined(INET6) && defined(SOL2)
+    ip6fd = open(UDP6_DEV_NAME, O_RDWR, 0);
+    if (ip6fd < 0)
+	fatal("Couldn't open IP device (2): %m");
+#endif /* defined(INET6) && defined(SOL2) */
+
+    if (default_device && !notty)
+	tty_sid = getsid((pid_t)0);
+
+    pppfd = open(PPP_DEV_NAME, O_RDWR | O_NONBLOCK, 0);
+    if (pppfd < 0)
+	fatal("Can't open %s: %m", PPP_DEV_NAME);
+    if (kdebugflag & 1) {
+	x = PPPDBG_LOG + PPPDBG_DRIVER;
+	strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+
+    /* Assign a new PPA and get its unit number. */
+    if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0)
+	fatal("Can't create new PPP interface: %m");
+
+#if defined(SOL2)
+    /*
+     * Since sys_init() is called prior to ifname being set in main(),
+     * we need to get the ifname now, otherwise slifname(), and others,
+     * will fail, or maybe, I should move them to a later point ?
+     * <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;
+
+    dump_packet("sent", p, len);
+    if (snoop_send_hook) snoop_send_hook(p, len);
+
+    data.len = len;
+    data.buf = (caddr_t) p;
+    retries = 4;
+    while (putmsg(pppfd, NULL, &data, 0) < 0) {
+	if (--retries < 0 || (errno != EWOULDBLOCK && errno != EAGAIN)) {
+	    if (errno != ENXIO)
+		error("Couldn't send packet: %m");
+	    break;
+	}
+	pfd.fd = pppfd;
+	pfd.events = POLLOUT;
+	poll(&pfd, 1, 250);	/* wait for up to 0.25 seconds */
+    }
+}
+
+
+/*
+ * wait_input - wait until there is data available,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_input(timo)
+    struct timeval *timo;
+{
+    int t;
+
+    t = timo == NULL? -1: timo->tv_sec * 1000 + timo->tv_usec / 1000;
+    if (poll(pollfds, n_pollfds, t) < 0 && errno != EINTR)
+	fatal("poll: %m");
+}
+
+/*
+ * add_fd - add an fd to the set that wait_input waits for.
+ */
+void add_fd(fd)
+    int fd;
+{
+    int n;
+
+    for (n = 0; n < n_pollfds; ++n)
+	if (pollfds[n].fd == fd)
+	    return;
+    if (n_pollfds < MAX_POLLFDS) {
+	pollfds[n_pollfds].fd = fd;
+	pollfds[n_pollfds].events = POLLIN | POLLPRI | POLLHUP;
+	++n_pollfds;
+    } else
+	error("Too many inputs!");
+}
+
+/*
+ * remove_fd - remove an fd from the set that wait_input waits for.
+ */
+void remove_fd(fd)
+    int fd;
+{
+    int n;
+
+    for (n = 0; n < n_pollfds; ++n) {
+	if (pollfds[n].fd == fd) {
+	    while (++n < n_pollfds)
+		pollfds[n-1] = pollfds[n];
+	    --n_pollfds;
+	    break;
+	}
+    }
+}
+
+#if 0
+/*
+ * wait_loop_output - wait until there is data available on the
+ * loopback, for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_loop_output(timo)
+    struct timeval *timo;
+{
+    wait_input(timo);
+}
+
+/*
+ * wait_time - wait for a given length of time or until a
+ * signal is received.
+ */
+void
+wait_time(timo)
+    struct timeval *timo;
+{
+    int n;
+
+    n = select(0, NULL, NULL, NULL, timo);
+    if (n < 0 && errno != EINTR)
+	fatal("select: %m");
+}
+#endif
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+    u_char *buf;
+{
+    struct strbuf ctrl, data;
+    int flags, len;
+    unsigned char ctrlbuf[sizeof(union DL_primitives) + 64];
+
+    for (;;) {
+	data.maxlen = PPP_MRU + PPP_HDRLEN;
+	data.buf = (caddr_t) buf;
+	ctrl.maxlen = sizeof(ctrlbuf);
+	ctrl.buf = (caddr_t) ctrlbuf;
+	flags = 0;
+	len = getmsg(pppfd, &ctrl, &data, &flags);
+	if (len < 0) {
+	    if (errno == EAGAIN || errno == EINTR)
+		return -1;
+	    fatal("Error reading packet: %m");
+	}
+
+	if (ctrl.len <= 0)
+	    return data.len;
+
+	/*
+	 * Got a M_PROTO or M_PCPROTO message.  Interpret it
+	 * as a DLPI primitive??
+	 */
+	if (debug)
+	    dbglog("got dlpi prim 0x%x, len=%d",
+		   ((union DL_primitives *)ctrlbuf)->dl_primitive, ctrl.len);
+
+    }
+}
+
+/*
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output()
+{
+    int len;
+    int rv = 0;
+
+    while ((len = read_packet(inpacket_buf)) > 0) {
+	if (loop_frame(inpacket_buf, len))
+	    rv = 1;
+    }
+    return rv;
+}
+
+/*
+ * netif_set_mtu - set the MTU on the PPP network interface.
+ */
+void
+netif_set_mtu(unit, mtu)
+    int unit, mtu;
+{
+    struct ifreq ifr;
+#if defined(INET6) && defined(SOL2)
+    struct lifreq lifr;
+    int	fd;
+#endif /* defined(INET6) && defined(SOL2) */
+
+    memset(&ifr, 0, sizeof(ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    ifr.ifr_metric = link_mtu;
+    if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) {
+	error("Couldn't set IP MTU (%s): %m", ifr.ifr_name);
+    }
+
+#if defined(INET6) && defined(SOL2) 
+    fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (fd < 0)
+	error("Couldn't open IPv6 socket: %m");
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    lifr.lifr_mtu = link_mtu;
+    if (ioctl(fd, SIOCSLIFMTU, &lifr) < 0) {
+	close(fd);
+	error("Couldn't set IPv6 MTU (%s): %m", ifr.ifr_name);
+    }
+    close(fd);
+#endif /* defined(INET6) && defined(SOL2) */
+}
+
+/*
+ * tty_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+tty_send_config(mtu, asyncmap, pcomp, accomp)
+    int mtu;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+
+    link_mtu = mtu;
+    if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) {
+	if (hungup && errno == ENXIO) {
+	    ++error_count;
+	    return;
+	}
+	error("Couldn't set MTU: %m");
+    }
+    if (fdmuxid >= 0) {
+	if (!sync_serial) {
+	    if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0)
+		error("Couldn't set transmit ACCM: %m");
+	}
+	cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
+	cf[1] = COMP_PROT | COMP_AC;
+	if (any_compressions() &&
+	    strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0)
+	    error("Couldn't set prot/AC compression: %m");
+    }
+}
+
+/*
+ * tty_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+tty_set_xaccm(accm)
+    ext_accm accm;
+{
+    if (sync_serial)
+	return;
+
+    if (fdmuxid >= 0
+	&& strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) {
+	if (!hungup || errno != ENXIO)
+	    warn("Couldn't set extended ACCM: %m");
+    }
+}
+
+/*
+ * tty_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+tty_recv_config(mru, asyncmap, pcomp, accomp)
+    int mru;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+
+    link_mru = mru;
+    if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) {
+	if (hungup && errno == ENXIO) {
+	    ++error_count;
+	    return;
+	}
+	error("Couldn't set MRU: %m");
+    }
+    if (fdmuxid >= 0) {
+	if (!sync_serial) {
+	    if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0)
+		error("Couldn't set receive ACCM: %m");
+	}
+	cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0);
+	cf[1] = DECOMP_PROT | DECOMP_AC;
+	if (any_compressions() &&
+	    strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0)
+	    error("Couldn't set prot/AC decompression: %m");
+    }
+}
+
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+    int unit, opt_len, for_transmit;
+    u_char *opt_ptr;
+{
+    if (strioctl(pppfd, (for_transmit? PPPIO_XCOMP: PPPIO_RCOMP),
+		 opt_ptr, opt_len, 0) >= 0)
+	return 1;
+    return (errno == ENOSR)? 0: -1;
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(unit, isopen, isup)
+    int unit, isopen, isup;
+{
+    int cf[2];
+
+    cf[0] = (isopen? CCP_ISOPEN: 0) + (isup? CCP_ISUP: 0);
+    cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (!hungup || errno != ENXIO)
+	    error("Couldn't set kernel CCP state: %m");
+    }
+}
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+    int u;
+    struct ppp_idle *ip;
+{
+    return strioctl(pppfd, PPPIO_GIDLE, ip, 0, sizeof(struct ppp_idle)) >= 0;
+}
+
+/*
+ * get_ppp_stats - return statistics for the link.
+ */
+int
+get_ppp_stats(u, stats)
+    int u;
+    struct pppd_stats *stats;
+{
+    struct ppp_stats s;
+
+    if (!sync_serial && 
+	strioctl(pppfd, PPPIO_GETSTAT, &s, 0, sizeof(s)) < 0) {
+	error("Couldn't get link statistics: %m");
+	return 0;
+    }
+    stats->bytes_in = s.p.ppp_ibytes;
+    stats->bytes_out = s.p.ppp_obytes;
+    stats->pkts_in = s.p.ppp_ipackets;
+    stats->pkts_out = s.p.ppp_opackets;
+    return 1;
+}
+
+#if 0
+/*
+ * set_filters - transfer the pass and active filters to the kernel.
+ */
+int
+set_filters(pass, active)
+    struct bpf_program *pass, *active;
+{
+    int ret = 1;
+
+    if (pass->bf_len > 0) {
+	if (strioctl(pppfd, PPPIO_PASSFILT, pass,
+		     sizeof(struct bpf_program), 0) < 0) {
+	    error("Couldn't set pass-filter in kernel: %m");
+	    ret = 0;
+	}
+    }
+    if (active->bf_len > 0) {
+	if (strioctl(pppfd, PPPIO_ACTIVEFILT, active,
+		     sizeof(struct bpf_program), 0) < 0) {
+	    error("Couldn't set active-filter in kernel: %m");
+	    ret = 0;
+	}
+    }
+    return ret;
+}
+#endif
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise.  This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(unit)
+    int unit;
+{
+    int cf[2];
+
+    cf[0] = cf[1] = 0;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (errno != ENXIO && errno != EINVAL)
+	    error("Couldn't get compression flags: %m");
+	return 0;
+    }
+    return cf[0] & CCP_FATALERROR;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, xcidcomp, xmaxcid)
+    int u, vjcomp, xcidcomp, xmaxcid;
+{
+    int cf[2];
+    char maxcid[2];
+
+    if (vjcomp) {
+	maxcid[0] = xcidcomp;
+	maxcid[1] = 15;		/* XXX should be rmaxcid */
+	if (strioctl(pppfd, PPPIO_VJINIT, maxcid, sizeof(maxcid), 0) < 0) {
+	    error("Couldn't initialize VJ compression: %m");
+	}
+    }
+
+    cf[0] = (vjcomp? COMP_VJC + DECOMP_VJC: 0)	/* XXX this is wrong */
+	+ (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0);
+    cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (vjcomp)
+	    error("Couldn't enable VJ compression: %m");
+    }
+
+    return 1;
+}
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+	error("Couldn't mark interface up (get): %m");
+	return 0;
+    }
+    ifr.ifr_flags |= IFF_UP;
+    if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+	error("Couldn't mark interface up (set): %m");
+	return 0;
+    }
+    if_is_up = 1;
+    return 1;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    if (ipmuxid < 0)
+	return 1;
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+	error("Couldn't mark interface down (get): %m");
+	return 0;
+    }
+    ifr.ifr_flags &= ~IFF_UP;
+    if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+	error("Couldn't mark interface down (set): %m");
+	return 0;
+    }
+    if_is_up = 0;
+    return 1;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(u, proto, mode)
+    int u;
+    int proto;
+    enum NPmode mode;
+{
+    int npi[2];
+
+    npi[0] = proto;
+    npi[1] = (int) mode;
+    if (strioctl(pppfd, PPPIO_NPMODE, &npi, 2 * sizeof(int), 0) < 0) {
+	error("ioctl(set NP %d mode to %d): %m", proto, mode);
+	return 0;
+    }
+    return 1;
+}
+
+#if defined(SOL2) && defined(INET6)
+/*
+ * sif6up - Config the IPv6 interface up and enable IPv6 packets to pass.
+ */
+int
+sif6up(u)
+    int u;
+{
+    struct lifreq lifr;
+    int fd;
+
+    fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (fd < 0) {
+	return 0;
+    }
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+	close(fd);
+	return 0;
+    }
+
+    lifr.lifr_flags |= IFF_UP;
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    if (ioctl(fd, SIOCSLIFFLAGS, &lifr) < 0) {
+	close(fd);
+	return 0;
+    }
+
+    if6_is_up = 1;
+    close(fd);
+    return 1;
+}
+
+/*
+ * sifdown - Config the IPv6 interface down and disable IPv6.
+ */
+int
+sif6down(u)
+    int u;
+{
+    struct lifreq lifr;
+    int fd;
+
+    fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (fd < 0)
+	return 0;
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+	close(fd);
+	return 0;
+    }
+
+    lifr.lifr_flags &= ~IFF_UP;
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+	close(fd);
+	return 0;
+    }
+
+    if6_is_up = 0;
+    close(fd);
+    return 1;
+}
+
+/*
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int
+sif6addr(u, o, h)
+    int u;
+    eui64_t o, h;
+{
+    struct lifreq lifr;
+    struct sockaddr_storage laddr;
+    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&laddr;
+    int fd;
+
+    fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (fd < 0)
+	return 0;
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+
+    /*
+     * Do this because /dev/ppp responds to DL_PHYS_ADDR_REQ with
+     * zero values, hence the interface token came to be zero too,
+     * and without this, in.ndpd will complain
+     */
+    IN6_LLTOKEN_FROM_EUI64(lifr, sin6, o);
+    if (ioctl(fd, SIOCSLIFTOKEN, &lifr) < 0) {
+	close(fd);
+	return 0;
+    }
+
+    /*
+     * Set the interface address and destination address
+     */
+    IN6_LLADDR_FROM_EUI64(lifr, sin6, o);
+    if (ioctl(fd, SIOCSLIFADDR, &lifr) < 0) {
+	close(fd);
+	return 0;
+    }
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    IN6_LLADDR_FROM_EUI64(lifr, sin6, h);
+    if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) < 0) {
+	close(fd);
+	return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * cif6addr - Remove the IPv6 address from interface
+ */
+int
+cif6addr(u, o, h)
+    int u;
+    eui64_t o, h;
+{
+    return 1;
+}
+
+#endif /* defined(SOL2) && defined(INET6) */
+
+
+#define INET_ADDR(x)	(((struct sockaddr_in *) &(x))->sin_addr.s_addr)
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+    int u;
+    u_int32_t o, h, m;
+{
+    struct ifreq ifr;
+    int ret = 1;
+
+    memset(&ifr, 0, sizeof(ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    ifr.ifr_addr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_addr) = m;
+    if (ioctl(ipfd, SIOCSIFNETMASK, &ifr) < 0) {
+	error("Couldn't set IP netmask: %m");
+	ret = 0;
+    }
+    ifr.ifr_addr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_addr) = o;
+    if (ioctl(ipfd, SIOCSIFADDR, &ifr) < 0) {
+	error("Couldn't set local IP address: %m");
+	ret = 0;
+    }
+
+    /*
+     * On some systems, we have to explicitly set the point-to-point
+     * flag bit before we can set a destination address.
+     */
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) >= 0
+	&& (ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
+	ifr.ifr_flags |= IFF_POINTOPOINT;
+	if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+	    error("Couldn't mark interface pt-to-pt: %m");
+	    ret = 0;
+	}
+    }
+    ifr.ifr_dstaddr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_dstaddr) = h;
+    if (ioctl(ipfd, SIOCSIFDSTADDR, &ifr) < 0) {
+	error("Couldn't set remote IP address: %m");
+	ret = 0;
+    }
+
+    remote_addr = h;
+    return ret;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+    int u;
+    u_int32_t o, h;
+{
+#if defined(__USLC__)		/* was: #if 0 */
+    cifroute(unit, ouraddr, hisaddr);
+    if (ipmuxid >= 0) {
+	notice("Removing ppp interface unit");
+	if (ioctl(ipfd, I_UNLINK, ipmuxid) < 0) {
+	    error("Can't remove ppp interface unit: %m");
+	    return 0;
+	}
+	ipmuxid = -1;
+    }
+#endif
+    remote_addr = 0;
+    return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct rtentry rt;
+
+#if defined(__USLC__)
+    g = l;			/* use the local address as gateway */
+#endif
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = 0;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = g;
+    rt.rt_flags = RTF_GATEWAY;
+
+    if (ioctl(ipfd, SIOCADDRT, &rt) < 0) {
+	error("Can't add default route: %m");
+	return 0;
+    }
+
+    default_route_gateway = g;
+    return 1;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct rtentry rt;
+
+#if defined(__USLC__)
+    g = l;			/* use the local address as gateway */
+#endif
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = 0;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = g;
+    rt.rt_flags = RTF_GATEWAY;
+
+    if (ioctl(ipfd, SIOCDELRT, &rt) < 0) {
+	error("Can't delete default route: %m");
+	return 0;
+    }
+
+    default_route_gateway = 0;
+    return 1;
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    memset(&arpreq, 0, sizeof(arpreq));
+    if (!get_ether_addr(hisaddr, &arpreq.arp_ha))
+	return 0;
+
+    arpreq.arp_pa.sa_family = AF_INET;
+    INET_ADDR(arpreq.arp_pa) = hisaddr;
+    arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+    if (ioctl(ipfd, SIOCSARP, (caddr_t) &arpreq) < 0) {
+	error("Couldn't set proxy ARP entry: %m");
+	return 0;
+    }
+
+    proxy_arp_addr = hisaddr;
+    return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    memset(&arpreq, 0, sizeof(arpreq));
+    arpreq.arp_pa.sa_family = AF_INET;
+    INET_ADDR(arpreq.arp_pa) = hisaddr;
+    if (ioctl(ipfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+	error("Couldn't delete proxy ARP entry: %m");
+	return 0;
+    }
+
+    proxy_arp_addr = 0;
+    return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS		32
+
+static int
+get_ether_addr(ipaddr, hwaddr)
+    u_int32_t ipaddr;
+    struct sockaddr *hwaddr;
+{
+    struct ifreq *ifr, *ifend, ifreq;
+    int nif;
+    struct ifconf ifc;
+    u_int32_t ina, mask;
+
+    /*
+     * Scan through the system's network interfaces.
+     */
+#ifdef SIOCGIFNUM
+    if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0)
+#endif
+	nif = MAX_IFS;
+    ifc.ifc_len = nif * sizeof(struct ifreq);
+    ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len);
+    if (ifc.ifc_buf == 0)
+	return 0;
+    if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) {
+	warn("Couldn't get system interface list: %m");
+	free(ifc.ifc_buf);
+	return 0;
+    }
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+	if (ifr->ifr_addr.sa_family != AF_INET)
+	    continue;
+	/*
+	 * Check that the interface is up, and not point-to-point or loopback.
+	 */
+	strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+	if (ioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0)
+	    continue;
+	if ((ifreq.ifr_flags &
+	     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+	    != (IFF_UP|IFF_BROADCAST))
+	    continue;
+	/*
+	 * Get its netmask and check that it's on the right subnet.
+	 */
+	if (ioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0)
+	    continue;
+	ina = INET_ADDR(ifr->ifr_addr);
+	mask = INET_ADDR(ifreq.ifr_addr);
+	if ((ipaddr & mask) == (ina & mask))
+	    break;
+    }
+
+    if (ifr >= ifend) {
+	warn("No suitable interface found for proxy ARP");
+	free(ifc.ifc_buf);
+	return 0;
+    }
+
+    info("found interface %s for proxy ARP", ifr->ifr_name);
+    if (!get_hw_addr(ifr->ifr_name, ina, hwaddr)) {
+	error("Couldn't get hardware address for %s", ifr->ifr_name);
+	free(ifc.ifc_buf);
+	return 0;
+    }
+
+    free(ifc.ifc_buf);
+    return 1;
+}
+
+/*
+ * get_hw_addr_dlpi - obtain the hardware address using DLPI
+ */
+static int
+get_hw_addr_dlpi(name, hwaddr)
+    char *name;
+    struct sockaddr *hwaddr;
+{
+    char *q;
+    int unit, iffd, adrlen;
+    unsigned char *adrp;
+    char ifdev[24];
+    struct {
+	union DL_primitives prim;
+	char space[64];
+    } reply;
+
+    /*
+     * We have to open the device and ask it for its hardware address.
+     * First split apart the device name and unit.
+     */
+    slprintf(ifdev, sizeof(ifdev), "/dev/%s", name);
+    for (q = ifdev + strlen(ifdev); --q >= ifdev; )
+	if (!isdigit(*q))
+	    break;
+    unit = atoi(q+1);
+    q[1] = 0;
+
+    /*
+     * Open the device and do a DLPI attach and phys_addr_req.
+     */
+    iffd = open(ifdev, O_RDWR);
+    if (iffd < 0) {
+	error("Can't open %s: %m", ifdev);
+	return 0;
+    }
+    if (dlpi_attach(iffd, unit) < 0
+	|| dlpi_get_reply(iffd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0
+	|| dlpi_info_req(iffd) < 0
+	|| dlpi_get_reply(iffd, &reply.prim, DL_INFO_ACK, sizeof(reply)) < 0) {
+	close(iffd);
+	return 0;
+    }
+
+    adrlen = reply.prim.info_ack.dl_addr_length;
+    adrp = (unsigned char *)&reply + reply.prim.info_ack.dl_addr_offset;
+
+#if DL_CURRENT_VERSION >= 2
+    if (reply.prim.info_ack.dl_sap_length < 0)
+	adrlen += reply.prim.info_ack.dl_sap_length;
+    else
+	adrp += reply.prim.info_ack.dl_sap_length;
+#endif
+
+    hwaddr->sa_family = AF_UNSPEC;
+    memcpy(hwaddr->sa_data, adrp, adrlen);
+
+    return 1;
+}
+/*
+ * get_hw_addr - obtain the hardware address for a named interface.
+ */
+static int
+get_hw_addr(name, ina, hwaddr)
+    char *name;
+    u_int32_t ina;
+    struct sockaddr *hwaddr;
+{
+    /* New way - get the address by doing an arp request. */
+    int s;
+    struct arpreq req;
+
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0)
+	return 0;
+    memset(&req, 0, sizeof(req));
+    req.arp_pa.sa_family = AF_INET;
+    INET_ADDR(req.arp_pa) = ina;
+    if (ioctl(s, SIOCGARP, &req) < 0) {
+	error("Couldn't get ARP entry for %s: %m", ip_ntoa(ina));
+	return 0;
+    }
+    *hwaddr = req.arp_ha;
+    hwaddr->sa_family = AF_UNSPEC;
+
+    return 1;
+}
+
+static int
+dlpi_attach(fd, ppa)
+    int fd, ppa;
+{
+    dl_attach_req_t req;
+    struct strbuf buf;
+
+    req.dl_primitive = DL_ATTACH_REQ;
+    req.dl_ppa = ppa;
+    buf.len = sizeof(req);
+    buf.buf = (void *) &req;
+    return putmsg(fd, &buf, NULL, RS_HIPRI);
+}
+
+static int
+dlpi_info_req(fd)
+    int fd;
+{
+    dl_info_req_t req;
+    struct strbuf buf;
+
+    req.dl_primitive = DL_INFO_REQ;
+    buf.len = sizeof(req);
+    buf.buf = (void *) &req;
+    return putmsg(fd, &buf, NULL, RS_HIPRI);
+}
+
+static int
+dlpi_get_reply(fd, reply, expected_prim, maxlen)
+    union DL_primitives *reply;
+    int fd, expected_prim, maxlen;
+{
+    struct strbuf buf;
+    int flags, n;
+    struct pollfd pfd;
+
+    /*
+     * Use poll to wait for a message with a timeout.
+     */
+    pfd.fd = fd;
+    pfd.events = POLLIN | POLLPRI;
+    do {
+	n = poll(&pfd, 1, 1000);
+    } while (n == -1 && errno == EINTR);
+    if (n <= 0)
+	return -1;
+
+    /*
+     * Get the reply.
+     */
+    buf.maxlen = maxlen;
+    buf.buf = (void *) reply;
+    flags = 0;
+    if (getmsg(fd, &buf, NULL, &flags) < 0)
+	return -1;
+
+    if (buf.len < sizeof(ulong)) {
+	if (debug)
+	    dbglog("dlpi response short (len=%d)\n", buf.len);
+	return -1;
+    }
+
+    if (reply->dl_primitive == expected_prim)
+	return 0;
+
+    if (debug) {
+	if (reply->dl_primitive == DL_ERROR_ACK) {
+	    dbglog("dlpi error %d (unix errno %d) for prim %x\n",
+		   reply->error_ack.dl_errno, reply->error_ack.dl_unix_errno,
+		   reply->error_ack.dl_error_primitive);
+	} else {
+	    dbglog("dlpi unexpected response prim %x\n",
+		   reply->dl_primitive);
+	}
+    }
+
+    return -1;
+}
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'.  If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+    u_int32_t addr;
+{
+    u_int32_t mask, nmask, ina;
+    struct ifreq *ifr, *ifend, ifreq;
+    int nif;
+    struct ifconf ifc;
+
+    addr = ntohl(addr);
+    if (IN_CLASSA(addr))	/* determine network mask for address class */
+	nmask = IN_CLASSA_NET;
+    else if (IN_CLASSB(addr))
+	nmask = IN_CLASSB_NET;
+    else
+	nmask = IN_CLASSC_NET;
+    /* class D nets are disallowed by bad_ip_adrs */
+    mask = netmask | htonl(nmask);
+
+    /*
+     * Scan through the system's network interfaces.
+     */
+#ifdef SIOCGIFNUM
+    if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0)
+#endif
+	nif = MAX_IFS;
+    ifc.ifc_len = nif * sizeof(struct ifreq);
+    ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len);
+    if (ifc.ifc_buf == 0)
+	return mask;
+    if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) {
+	warn("Couldn't get system interface list: %m");
+	free(ifc.ifc_buf);
+	return mask;
+    }
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+	/*
+	 * Check the interface's internet address.
+	 */
+	if (ifr->ifr_addr.sa_family != AF_INET)
+	    continue;
+	ina = INET_ADDR(ifr->ifr_addr);
+	if ((ntohl(ina) & nmask) != (addr & nmask))
+	    continue;
+	/*
+	 * Check that the interface is up, and not point-to-point or loopback.
+	 */
+	strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+	if (ioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0)
+	    continue;
+	if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+	    != IFF_UP)
+	    continue;
+	/*
+	 * Get its netmask and OR it into our mask.
+	 */
+	if (ioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0)
+	    continue;
+	mask |= INET_ADDR(ifreq.ifr_addr);
+    }
+
+    free(ifc.ifc_buf);
+    return mask;
+}
+
+/*
+ * logwtmp - write an accounting record to the /var/adm/wtmp file.
+ */
+void
+logwtmp(line, name, host)
+    const char *line, *name, *host;
+{
+    static struct utmpx utmpx;
+
+    if (name[0] != 0) {
+	/* logging in */
+	strncpy(utmpx.ut_user, name, sizeof(utmpx.ut_user));
+	strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
+	strncpy(utmpx.ut_host, host, sizeof(utmpx.ut_host));
+	if (*host != '\0') {
+	    utmpx.ut_syslen = strlen(host) + 1;
+	    if (utmpx.ut_syslen > sizeof(utmpx.ut_host))
+		utmpx.ut_syslen = sizeof(utmpx.ut_host);
+	}
+	utmpx.ut_pid = getpid();
+	utmpx.ut_type = USER_PROCESS;
+    } else {
+	utmpx.ut_type = DEAD_PROCESS;
+    }
+    gettimeofday(&utmpx.ut_tv, NULL);
+    updwtmpx("/var/adm/wtmpx", &utmpx);
+}
+
+/*
+ * get_host_seed - return the serial number of this machine.
+ */
+int
+get_host_seed()
+{
+    char buf[32];
+
+    if (sysinfo(SI_HW_SERIAL, buf, sizeof(buf)) < 0) {
+	error("sysinfo: %m");
+	return 0;
+    }
+    return (int) strtoul(buf, NULL, 16);
+}
+
+static int
+strioctl(fd, cmd, ptr, ilen, olen)
+    int fd, cmd, ilen, olen;
+    void *ptr;
+{
+    struct strioctl str;
+
+    str.ic_cmd = cmd;
+    str.ic_timout = 0;
+    str.ic_len = ilen;
+    str.ic_dp = ptr;
+    if (ioctl(fd, I_STR, &str) == -1)
+	return -1;
+    if (str.ic_len != olen)
+	dbglog("strioctl: expected %d bytes, got %d for cmd %x\n",
+	       olen, str.ic_len, cmd);
+    return 0;
+}
+
+#if 0
+/*
+ * lock - create a lock file for the named lock device
+ */
+
+#define LOCK_PREFIX	"/var/spool/locks/LK."
+static char lock_file[40];	/* name of lock file created */
+
+int
+lock(dev)
+    char *dev;
+{
+    int n, fd, pid;
+    struct stat sbuf;
+    char ascii_pid[12];
+
+    if (stat(dev, &sbuf) < 0) {
+	error("Can't get device number for %s: %m", dev);
+	return -1;
+    }
+    if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
+	error("Can't lock %s: not a character device", dev);
+	return -1;
+    }
+    slprintf(lock_file, sizeof(lock_file), "%s%03d.%03d.%03d",
+	     LOCK_PREFIX, major(sbuf.st_dev),
+	     major(sbuf.st_rdev), minor(sbuf.st_rdev));
+
+    while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+	if (errno == EEXIST
+	    && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+	    /* Read the lock file to find out who has the device locked */
+	    n = read(fd, ascii_pid, 11);
+	    if (n <= 0) {
+		error("Can't read pid from lock file %s", lock_file);
+		close(fd);
+	    } else {
+		ascii_pid[n] = 0;
+		pid = atoi(ascii_pid);
+		if (pid > 0 && kill(pid, 0) == -1 && errno == ESRCH) {
+		    /* pid no longer exists - remove the lock file */
+		    if (unlink(lock_file) == 0) {
+			close(fd);
+			notice("Removed stale lock on %s (pid %d)",
+			       dev, pid);
+			continue;
+		    } else
+			warn("Couldn't remove stale lock on %s",
+			       dev);
+		} else
+		    notice("Device %s is locked by pid %d",
+			   dev, pid);
+	    }
+	    close(fd);
+	} else
+	    error("Can't create lock file %s: %m", lock_file);
+	lock_file[0] = 0;
+	return -1;
+    }
+
+    slprintf(ascii_pid, sizeof(ascii_pid), "%10d\n", getpid());
+    write(fd, ascii_pid, 11);
+
+    close(fd);
+    return 1;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+    if (lock_file[0]) {
+	unlink(lock_file);
+	lock_file[0] = 0;
+    }
+}
+#endif
+
+/*
+ * cifroute - delete a route through the addresses given.
+ */
+int
+cifroute(u, our, his)
+    int u;
+    u_int32_t our, his;
+{
+    struct rtentry rt;
+
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = his;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = our;
+    rt.rt_flags = RTF_HOST;
+
+    if (ioctl(ipfd, SIOCDELRT, &rt) < 0) {
+	error("Can't delete route: %m");
+	return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * have_route_to - determine if the system has a route to the specified
+ * IP address.  Returns 0 if not, 1 if so, -1 if we can't tell.
+ * `addr' is in network byte order.
+ * For demand mode to work properly, we have to ignore routes
+ * through our own interface.
+ */
+#ifndef T_CURRENT		/* needed for Solaris 2.5 */
+#define T_CURRENT	MI_T_CURRENT
+#endif
+
+int
+have_route_to(addr)
+    u_int32_t addr;
+{
+#ifdef SOL2
+    int fd, r, flags, i;
+    struct {
+	struct T_optmgmt_req req;
+	struct opthdr hdr;
+    } req;
+    union {
+	struct T_optmgmt_ack ack;
+	unsigned char space[64];
+    } ack;
+    struct opthdr *rh;
+    struct strbuf cbuf, dbuf;
+    int nroutes;
+    mib2_ipRouteEntry_t routes[8];
+    mib2_ipRouteEntry_t *rp;
+
+    fd = open(mux_dev_name, O_RDWR);
+    if (fd < 0) {
+	warn("have_route_to: couldn't open %s: %m", mux_dev_name);
+	return -1;
+    }
+
+    req.req.PRIM_type = T_OPTMGMT_REQ;
+    req.req.OPT_offset = (char *) &req.hdr - (char *) &req;
+    req.req.OPT_length = sizeof(req.hdr);
+    req.req.MGMT_flags = T_CURRENT;
+
+    req.hdr.level = MIB2_IP;
+    req.hdr.name = 0;
+    req.hdr.len = 0;
+
+    cbuf.buf = (char *) &req;
+    cbuf.len = sizeof(req);
+
+    if (putmsg(fd, &cbuf, NULL, 0) == -1) {
+	warn("have_route_to: putmsg: %m");
+	close(fd);
+	return -1;
+    }
+
+    for (;;) {
+	cbuf.buf = (char *) &ack;
+	cbuf.maxlen = sizeof(ack);
+	dbuf.buf = (char *) routes;
+	dbuf.maxlen = sizeof(routes);
+	flags = 0;
+	r = getmsg(fd, &cbuf, &dbuf, &flags);
+	if (r == -1) {
+	    warn("have_route_to: getmsg: %m");
+	    close(fd);
+	    return -1;
+	}
+
+	if (cbuf.len < sizeof(struct T_optmgmt_ack)
+	    || ack.ack.PRIM_type != T_OPTMGMT_ACK
+	    || ack.ack.MGMT_flags != T_SUCCESS
+	    || ack.ack.OPT_length < sizeof(struct opthdr)) {
+	    dbglog("have_route_to: bad message len=%d prim=%d",
+		   cbuf.len, ack.ack.PRIM_type);
+	    close(fd);
+	    return -1;
+	}
+
+	rh = (struct opthdr *) ((char *)&ack + ack.ack.OPT_offset);
+	if (rh->level == 0 && rh->name == 0)
+	    break;
+	if (rh->level != MIB2_IP || rh->name != MIB2_IP_21) {
+	    while (r == MOREDATA)
+		r = getmsg(fd, NULL, &dbuf, &flags);
+	    continue;
+	}
+
+	for (;;) {
+	    nroutes = dbuf.len / sizeof(mib2_ipRouteEntry_t);
+	    for (rp = routes, i = 0; i < nroutes; ++i, ++rp) {
+		if (rp->ipRouteMask != ~0) {
+		    dbglog("have_route_to: dest=%x gw=%x mask=%x\n",
+			   rp->ipRouteDest, rp->ipRouteNextHop,
+			   rp->ipRouteMask);
+		    if (((addr ^ rp->ipRouteDest) & rp->ipRouteMask) == 0
+			&& rp->ipRouteNextHop != remote_addr)
+			return 1;
+		}
+	    }
+	    if (r == 0)
+		break;
+	    r = getmsg(fd, NULL, &dbuf, &flags);
+	}
+    }
+    close(fd);
+    return 0;
+#else
+    return -1;
+#endif /* SOL2 */
+}
+
+/*
+ * get_pty - get a pty master/slave pair and chown the slave side to
+ * the uid given.  Assumes slave_name points to MAXPATHLEN bytes of space.
+ */
+int
+get_pty(master_fdp, slave_fdp, slave_name, uid)
+    int *master_fdp;
+    int *slave_fdp;
+    char *slave_name;
+    int uid;
+{
+    int mfd, sfd;
+    char *pty_name;
+
+    mfd = open("/dev/ptmx", O_RDWR);
+    if (mfd < 0) {
+	error("Couldn't open pty master: %m");
+	return 0;
+    }
+
+    pty_name = ptsname(mfd);
+    if (pty_name == NULL) {
+	error("Couldn't get name of pty slave");
+	close(mfd);
+	return 0;
+    }
+    if (chown(pty_name, uid, -1) < 0)
+	warn("Couldn't change owner of pty slave: %m");
+    if (chmod(pty_name, S_IRUSR | S_IWUSR) < 0)
+	warn("Couldn't change permissions on pty slave: %m");
+    if (unlockpt(mfd) < 0)
+	warn("Couldn't unlock pty slave: %m");
+
+    sfd = open(pty_name, O_RDWR);
+    if (sfd < 0) {
+	error("Couldn't open pty slave %s: %m", pty_name);
+	close(mfd);
+	return 0;
+    }
+    if (ioctl(sfd, I_PUSH, "ptem") < 0)
+	warn("Couldn't push ptem module on pty slave: %m");
+
+    dbglog("Using %s", pty_name);
+    strlcpy(slave_name, pty_name, MAXPATHLEN);
+    *master_fdp = mfd;
+    *slave_fdp = sfd;
+
+    return 1;
+}
diff --git a/ap/app/pppd/pppd/tdb.c b/ap/app/pppd/pppd/tdb.c
new file mode 100644
index 0000000..bdc5828
--- /dev/null
+++ b/ap/app/pppd/pppd/tdb.c
@@ -0,0 +1,2009 @@
+ /* 
+   Unix SMB/CIFS implementation.
+
+   trivial database library
+
+   Copyright (C) Andrew Tridgell              1999-2004
+   Copyright (C) Paul `Rusty' Russell		   2000
+   Copyright (C) Jeremy Allison			   2000-2003
+   
+     ** NOTE! The following LGPL license applies to the tdb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+/* NOTE: If you use tdbs under valgrind, and in particular if you run
+ * tdbtorture, you may get spurious "uninitialized value" warnings.  I
+ * think this is because valgrind doesn't understand that the mmap'd
+ * area may be written to by other processes.  Memory can, from the
+ * point of view of the grinded process, spontaneously become
+ * initialized.
+ *
+ * I can think of a few solutions.  [mbp 20030311]
+ *
+ * 1 - Write suppressions for Valgrind so that it doesn't complain
+ * about this.  Probably the most reasonable but people need to
+ * remember to use them.
+ *
+ * 2 - Use IO not mmap when running under valgrind.  Not so nice.
+ *
+ * 3 - Use the special valgrind macros to mark memory as valid at the
+ * right time.  Probably too hard -- the process just doesn't know.
+ */ 
+
+#include <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 <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+
+#define TDB_MAGIC_FOOD "TDB file\n"
+#define TDB_VERSION (0x26011967 + 6)
+#define TDB_MAGIC (0x26011999U)
+#define TDB_FREE_MAGIC (~TDB_MAGIC)
+#define TDB_DEAD_MAGIC (0xFEE1DEAD)
+#define TDB_ALIGNMENT 4
+#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT)
+#define DEFAULT_HASH_SIZE 131
+#define TDB_PAGE_SIZE 0x2000
+#define FREELIST_TOP (sizeof(struct tdb_header))
+#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1))
+#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24))
+#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC)
+#define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
+#define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off))
+#define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + TDB_SPINLOCK_SIZE(hash_size))
+
+
+/* NB assumes there is a local variable called "tdb" that is the
+ * current context, also takes doubly-parenthesized print-style
+ * argument. */
+#define TDB_LOG(x) (tdb->log_fn?((tdb->log_fn x),0) : 0)
+
+/* lock offsets */
+#define GLOBAL_LOCK 0
+#define ACTIVE_LOCK 4
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+/* free memory if the pointer is valid and zero the pointer */
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0)
+#endif
+
+#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+TDB_DATA tdb_null;
+
+/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
+static TDB_CONTEXT *tdbs = NULL;
+
+static int tdb_munmap(TDB_CONTEXT *tdb)
+{
+	if (tdb->flags & TDB_INTERNAL)
+		return 0;
+
+#ifdef HAVE_MMAP
+	if (tdb->map_ptr) {
+		int ret = munmap(tdb->map_ptr, tdb->map_size);
+		if (ret != 0)
+			return ret;
+	}
+#endif
+	tdb->map_ptr = NULL;
+	return 0;
+}
+
+static void tdb_mmap(TDB_CONTEXT *tdb)
+{
+	if (tdb->flags & TDB_INTERNAL)
+		return;
+
+#ifdef HAVE_MMAP
+	if (!(tdb->flags & TDB_NOMMAP)) {
+		tdb->map_ptr = mmap(NULL, tdb->map_size, 
+				    PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
+				    MAP_SHARED|MAP_FILE, tdb->fd, 0);
+
+		/*
+		 * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
+		 */
+
+		if (tdb->map_ptr == MAP_FAILED) {
+			tdb->map_ptr = NULL;
+			TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n", 
+				 tdb->map_size, strerror(errno)));
+		}
+	} else {
+		tdb->map_ptr = NULL;
+	}
+#else
+	tdb->map_ptr = NULL;
+#endif
+}
+
+/* Endian conversion: we only ever deal with 4 byte quantities */
+static void *convert(void *buf, u32 size)
+{
+	u32 i, *p = buf;
+	for (i = 0; i < size / 4; i++)
+		p[i] = TDB_BYTEREV(p[i]);
+	return buf;
+}
+#define DOCONV() (tdb->flags & TDB_CONVERT)
+#define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x)
+
+/* the body of the database is made of one list_struct for the free space
+   plus a separate data list for each hash value */
+struct list_struct {
+	tdb_off next; /* offset of the next record in the list */
+	tdb_len rec_len; /* total byte length of record */
+	tdb_len key_len; /* byte length of key */
+	tdb_len data_len; /* byte length of data */
+	u32 full_hash; /* the full 32 bit hash of the key */
+	u32 magic;   /* try to catch errors */
+	/* the following union is implied:
+		union {
+			char record[rec_len];
+			struct {
+				char key[key_len];
+				char data[data_len];
+			}
+			u32 totalsize; (tailer)
+		}
+	*/
+};
+
+/***************************************************************
+ Allow a caller to set a "alarm" flag that tdb can check to abort
+ a blocking lock on SIGALRM.
+***************************************************************/
+
+static sig_atomic_t *palarm_fired;
+
+void tdb_set_lock_alarm(sig_atomic_t *palarm)
+{
+	palarm_fired = palarm;
+}
+
+/* a byte range locking function - return 0 on success
+   this functions locks/unlocks 1 byte at the specified offset.
+
+   On error, errno is also set so that errors are passed back properly
+   through tdb_open(). */
+static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, 
+		      int rw_type, int lck_type, int probe)
+{
+	struct flock fl;
+	int ret;
+
+	if (tdb->flags & TDB_NOLOCK)
+		return 0;
+	if ((rw_type == F_WRLCK) && (tdb->read_only)) {
+		errno = EACCES;
+		return -1;
+	}
+
+	fl.l_type = rw_type;
+	fl.l_whence = SEEK_SET;
+	fl.l_start = offset;
+	fl.l_len = 1;
+	fl.l_pid = 0;
+
+	do {
+		ret = fcntl(tdb->fd,lck_type,&fl);
+		if (ret == -1 && errno == EINTR && palarm_fired && *palarm_fired)
+			break;
+	} while (ret == -1 && errno == EINTR);
+
+	if (ret == -1) {
+		if (!probe && lck_type != F_SETLK) {
+			/* Ensure error code is set for log fun to examine. */
+			if (errno == EINTR && palarm_fired && *palarm_fired)
+				tdb->ecode = TDB_ERR_LOCK_TIMEOUT;
+			else
+				tdb->ecode = TDB_ERR_LOCK;
+			TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n", 
+				 tdb->fd, offset, rw_type, lck_type));
+		}
+		/* Was it an alarm timeout ? */
+		if (errno == EINTR && palarm_fired && *palarm_fired) {
+			TDB_LOG((tdb, 5, "tdb_brlock timed out (fd=%d) at offset %d rw_type=%d lck_type=%d\n", 
+				 tdb->fd, offset, rw_type, lck_type));
+			return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1);
+		}
+		/* Otherwise - generic lock error. errno set by fcntl.
+		 * EAGAIN is an expected return from non-blocking
+		 * locks. */
+		if (errno != EAGAIN) {
+			TDB_LOG((tdb, 5, "tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d: %s\n", 
+				 tdb->fd, offset, rw_type, lck_type, 
+				 strerror(errno)));
+		}
+		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+	}
+	return 0;
+}
+
+/* lock a list in the database. list -1 is the alloc list */
+static int tdb_lock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+	if (list < -1 || list >= (int)tdb->header.hash_size) {
+		TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n", 
+			   list, ltype));
+		return -1;
+	}
+	if (tdb->flags & TDB_NOLOCK)
+		return 0;
+
+	/* Since fcntl locks don't nest, we do a lock for the first one,
+	   and simply bump the count for future ones */
+	if (tdb->locked[list+1].count == 0) {
+		if (!tdb->read_only && tdb->header.rwlocks) {
+			if (tdb_spinlock(tdb, list, ltype)) {
+				TDB_LOG((tdb, 0, "tdb_lock spinlock failed on list %d ltype=%d\n", 
+					   list, ltype));
+				return -1;
+			}
+		} else if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) {
+			TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n", 
+					   list, ltype, strerror(errno)));
+			return -1;
+		}
+		tdb->locked[list+1].ltype = ltype;
+	}
+	tdb->locked[list+1].count++;
+	return 0;
+}
+
+/* unlock the database: returns void because it's too late for errors. */
+	/* changed to return int it may be interesting to know there
+	   has been an error  --simo */
+static int tdb_unlock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+	int ret = -1;
+
+	if (tdb->flags & TDB_NOLOCK)
+		return 0;
+
+	/* Sanity checks */
+	if (list < -1 || list >= (int)tdb->header.hash_size) {
+		TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
+		return ret;
+	}
+
+	if (tdb->locked[list+1].count==0) {
+		TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n"));
+		return ret;
+	}
+
+	if (tdb->locked[list+1].count == 1) {
+		/* Down to last nested lock: unlock underneath */
+		if (!tdb->read_only && tdb->header.rwlocks) {
+			ret = tdb_spinunlock(tdb, list, ltype);
+		} else {
+			ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0);
+		}
+	} else {
+		ret = 0;
+	}
+	tdb->locked[list+1].count--;
+
+	if (ret)
+		TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n")); 
+	return ret;
+}
+
+/* check for an out of bounds access - if it is out of bounds then
+   see if the database has been expanded by someone else and expand
+   if necessary 
+   note that "len" is the minimum length needed for the db
+*/
+static int tdb_oob(TDB_CONTEXT *tdb, tdb_off len, int probe)
+{
+	struct stat st;
+	if (len <= tdb->map_size)
+		return 0;
+	if (tdb->flags & TDB_INTERNAL) {
+		if (!probe) {
+			/* Ensure ecode is set for log fn. */
+			tdb->ecode = TDB_ERR_IO;
+			TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n",
+				 (int)len, (int)tdb->map_size));
+		}
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
+	}
+
+	if (fstat(tdb->fd, &st) == -1)
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
+
+	if (st.st_size < (size_t)len) {
+		if (!probe) {
+			/* Ensure ecode is set for log fn. */
+			tdb->ecode = TDB_ERR_IO;
+			TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n",
+				 (int)len, (int)st.st_size));
+		}
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
+	}
+
+	/* Unmap, update size, remap */
+	if (tdb_munmap(tdb) == -1)
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
+	tdb->map_size = st.st_size;
+	tdb_mmap(tdb);
+	return 0;
+}
+
+/* write a lump of data at a specified offset */
+static int tdb_write(TDB_CONTEXT *tdb, tdb_off off, void *buf, tdb_len len)
+{
+	if (tdb_oob(tdb, off + len, 0) != 0)
+		return -1;
+
+	if (tdb->map_ptr)
+		memcpy(off + (char *)tdb->map_ptr, buf, len);
+#ifdef HAVE_PWRITE
+	else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+	else if (lseek(tdb->fd, off, SEEK_SET) != off
+		 || write(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_IO;
+		TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n",
+			   off, len, strerror(errno)));
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
+	}
+	return 0;
+}
+
+/* read a lump of data at a specified offset, maybe convert */
+static int tdb_read(TDB_CONTEXT *tdb,tdb_off off,void *buf,tdb_len len,int cv)
+{
+	if (tdb_oob(tdb, off + len, 0) != 0)
+		return -1;
+
+	if (tdb->map_ptr)
+		memcpy(buf, off + (char *)tdb->map_ptr, len);
+#ifdef HAVE_PREAD
+	else if (pread(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+	else if (lseek(tdb->fd, off, SEEK_SET) != off
+		 || read(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_IO;
+		TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d (%s)\n",
+			   off, len, strerror(errno)));
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
+	}
+	if (cv)
+		convert(buf, len);
+	return 0;
+}
+
+/* read a lump of data, allocating the space for it */
+static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
+{
+	char *buf;
+
+	if (!(buf = malloc(len))) {
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_OOM;
+		TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n",
+			   len, strerror(errno)));
+		return TDB_ERRCODE(TDB_ERR_OOM, buf);
+	}
+	if (tdb_read(tdb, offset, buf, len, 0) == -1) {
+		SAFE_FREE(buf);
+		return NULL;
+	}
+	return buf;
+}
+
+/* read/write a tdb_off */
+static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+	return tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
+}
+static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+	tdb_off off = *d;
+	return tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
+}
+
+/* read/write a record */
+static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+	if (tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
+		return -1;
+	if (TDB_BAD_MAGIC(rec)) {
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_CORRUPT;
+		TDB_LOG((tdb, 0,"rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
+		return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+	}
+	return tdb_oob(tdb, rec->next+sizeof(*rec), 0);
+}
+static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+	struct list_struct r = *rec;
+	return tdb_write(tdb, offset, CONVERT(r), sizeof(r));
+}
+
+/* read a freelist record and check for simple errors */
+static int rec_free_read(TDB_CONTEXT *tdb, tdb_off off, struct list_struct *rec)
+{
+	if (tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1)
+		return -1;
+
+	if (rec->magic == TDB_MAGIC) {
+		/* this happens when a app is showdown while deleting a record - we should
+		   not completely fail when this happens */
+		TDB_LOG((tdb, 0,"rec_free_read non-free magic 0x%x at offset=%d - fixing\n", 
+			 rec->magic, off));
+		rec->magic = TDB_FREE_MAGIC;
+		if (tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
+			return -1;
+	}
+
+	if (rec->magic != TDB_FREE_MAGIC) {
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_CORRUPT;
+		TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n", 
+			   rec->magic, off));
+		return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+	}
+	if (tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
+		return -1;
+	return 0;
+}
+
+/* update a record tailer (must hold allocation lock) */
+static int update_tailer(TDB_CONTEXT *tdb, tdb_off offset,
+			 const struct list_struct *rec)
+{
+	tdb_off totalsize;
+
+	/* Offset of tailer from record header */
+	totalsize = sizeof(*rec) + rec->rec_len;
+	return ofs_write(tdb, offset + totalsize - sizeof(tdb_off),
+			 &totalsize);
+}
+
+static tdb_off tdb_dump_record(TDB_CONTEXT *tdb, tdb_off offset)
+{
+	struct list_struct rec;
+	tdb_off tailer_ofs, tailer;
+
+	if (tdb_read(tdb, offset, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+		printf("ERROR: failed to read record at %u\n", offset);
+		return 0;
+	}
+
+	printf(" rec: offset=%u next=%d rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
+	       offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic);
+
+	tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off);
+	if (ofs_read(tdb, tailer_ofs, &tailer) == -1) {
+		printf("ERROR: failed to read tailer at %u\n", tailer_ofs);
+		return rec.next;
+	}
+
+	if (tailer != rec.rec_len + sizeof(rec)) {
+		printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n",
+				(unsigned)tailer, (unsigned)(rec.rec_len + sizeof(rec)));
+	}
+	return rec.next;
+}
+
+static int tdb_dump_chain(TDB_CONTEXT *tdb, int i)
+{
+	tdb_off rec_ptr, top;
+
+	top = TDB_HASH_TOP(i);
+
+	if (tdb_lock(tdb, i, F_WRLCK) != 0)
+		return -1;
+
+	if (ofs_read(tdb, top, &rec_ptr) == -1)
+		return tdb_unlock(tdb, i, F_WRLCK);
+
+	if (rec_ptr)
+		printf("hash=%d\n", i);
+
+	while (rec_ptr) {
+		rec_ptr = tdb_dump_record(tdb, rec_ptr);
+	}
+
+	return tdb_unlock(tdb, i, F_WRLCK);
+}
+
+void tdb_dump_all(TDB_CONTEXT *tdb)
+{
+	int i;
+	for (i=0;i<tdb->header.hash_size;i++) {
+		tdb_dump_chain(tdb, i);
+	}
+	printf("freelist:\n");
+	tdb_dump_chain(tdb, -1);
+}
+
+int tdb_printfreelist(TDB_CONTEXT *tdb)
+{
+	int ret;
+	long total_free = 0;
+	tdb_off offset, rec_ptr;
+	struct list_struct rec;
+
+	if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0)
+		return ret;
+
+	offset = FREELIST_TOP;
+
+	/* read in the freelist top */
+	if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+		tdb_unlock(tdb, -1, F_WRLCK);
+		return 0;
+	}
+
+	printf("freelist top=[0x%08x]\n", rec_ptr );
+	while (rec_ptr) {
+		if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+			tdb_unlock(tdb, -1, F_WRLCK);
+			return -1;
+		}
+
+		if (rec.magic != TDB_FREE_MAGIC) {
+			printf("bad magic 0x%08x in free list\n", rec.magic);
+			tdb_unlock(tdb, -1, F_WRLCK);
+			return -1;
+		}
+
+		printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)]\n", rec.next, rec.rec_len, rec.rec_len );
+		total_free += rec.rec_len;
+
+		/* move to the next record */
+		rec_ptr = rec.next;
+	}
+	printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, 
+               (int)total_free);
+
+	return tdb_unlock(tdb, -1, F_WRLCK);
+}
+
+/* Remove an element from the freelist.  Must have alloc lock. */
+static int remove_from_freelist(TDB_CONTEXT *tdb, tdb_off off, tdb_off next)
+{
+	tdb_off last_ptr, i;
+
+	/* read in the freelist top */
+	last_ptr = FREELIST_TOP;
+	while (ofs_read(tdb, last_ptr, &i) != -1 && i != 0) {
+		if (i == off) {
+			/* We've found it! */
+			return ofs_write(tdb, last_ptr, &next);
+		}
+		/* Follow chain (next offset is at start of record) */
+		last_ptr = i;
+	}
+	TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off));
+	return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+}
+
+/* Add an element into the freelist. Merge adjacent records if
+   neccessary. */
+static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+	tdb_off right, left;
+
+	/* Allocation and tailer lock */
+	if (tdb_lock(tdb, -1, F_WRLCK) != 0)
+		return -1;
+
+	/* set an initial tailer, so if we fail we don't leave a bogus record */
+	if (update_tailer(tdb, offset, rec) != 0) {
+		TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n"));
+		goto fail;
+	}
+
+	/* Look right first (I'm an Australian, dammit) */
+	right = offset + sizeof(*rec) + rec->rec_len;
+	if (right + sizeof(*rec) <= tdb->map_size) {
+		struct list_struct r;
+
+		if (tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) {
+			TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right));
+			goto left;
+		}
+
+		/* If it's free, expand to include it. */
+		if (r.magic == TDB_FREE_MAGIC) {
+			if (remove_from_freelist(tdb, right, r.next) == -1) {
+				TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right));
+				goto left;
+			}
+			rec->rec_len += sizeof(r) + r.rec_len;
+		}
+	}
+
+left:
+	/* Look left */
+	left = offset - sizeof(tdb_off);
+	if (left > TDB_DATA_START(tdb->header.hash_size)) {
+		struct list_struct l;
+		tdb_off leftsize;
+		
+		/* Read in tailer and jump back to header */
+		if (ofs_read(tdb, left, &leftsize) == -1) {
+			TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left));
+			goto update;
+		}
+		left = offset - leftsize;
+
+		/* Now read in record */
+		if (tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) {
+			TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize));
+			goto update;
+		}
+
+		/* If it's free, expand to include it. */
+		if (l.magic == TDB_FREE_MAGIC) {
+			if (remove_from_freelist(tdb, left, l.next) == -1) {
+				TDB_LOG((tdb, 0, "tdb_free: left free failed at %u\n", left));
+				goto update;
+			} else {
+				offset = left;
+				rec->rec_len += leftsize;
+			}
+		}
+	}
+
+update:
+	if (update_tailer(tdb, offset, rec) == -1) {
+		TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset));
+		goto fail;
+	}
+
+	/* Now, prepend to free list */
+	rec->magic = TDB_FREE_MAGIC;
+
+	if (ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 ||
+	    rec_write(tdb, offset, rec) == -1 ||
+	    ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
+		TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset));
+		goto fail;
+	}
+
+	/* And we're done. */
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return 0;
+
+ fail:
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return -1;
+}
+
+
+/* expand a file.  we prefer to use ftruncate, as that is what posix
+  says to use for mmap expansion */
+static int expand_file(TDB_CONTEXT *tdb, tdb_off size, tdb_off addition)
+{
+	char buf[1024];
+#if HAVE_FTRUNCATE_EXTEND
+	if (ftruncate(tdb->fd, size+addition) != 0) {
+		TDB_LOG((tdb, 0, "expand_file ftruncate to %d failed (%s)\n", 
+			   size+addition, strerror(errno)));
+		return -1;
+	}
+#else
+	char b = 0;
+
+#ifdef HAVE_PWRITE
+	if (pwrite(tdb->fd,  &b, 1, (size+addition) - 1) != 1) {
+#else
+	if (lseek(tdb->fd, (size+addition) - 1, SEEK_SET) != (size+addition) - 1 || 
+	    write(tdb->fd, &b, 1) != 1) {
+#endif
+		TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n", 
+			   size+addition, strerror(errno)));
+		return -1;
+	}
+#endif
+
+	/* now fill the file with something. This ensures that the file isn't sparse, which would be
+	   very bad if we ran out of disk. This must be done with write, not via mmap */
+	memset(buf, 0x42, sizeof(buf));
+	while (addition) {
+		int n = addition>sizeof(buf)?sizeof(buf):addition;
+#ifdef HAVE_PWRITE
+		int ret = pwrite(tdb->fd, buf, n, size);
+#else
+		int ret;
+		if (lseek(tdb->fd, size, SEEK_SET) != size)
+			return -1;
+		ret = write(tdb->fd, buf, n);
+#endif
+		if (ret != n) {
+			TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n", 
+				   n, strerror(errno)));
+			return -1;
+		}
+		addition -= n;
+		size += n;
+	}
+	return 0;
+}
+
+
+/* expand the database at least size bytes by expanding the underlying
+   file and doing the mmap again if necessary */
+static int tdb_expand(TDB_CONTEXT *tdb, tdb_off size)
+{
+	struct list_struct rec;
+	tdb_off offset;
+
+	if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
+		TDB_LOG((tdb, 0, "lock failed in tdb_expand\n"));
+		return -1;
+	}
+
+	/* must know about any previous expansions by another process */
+	tdb_oob(tdb, tdb->map_size + 1, 1);
+
+	/* always make room for at least 10 more records, and round
+           the database up to a multiple of TDB_PAGE_SIZE */
+	size = TDB_ALIGN(tdb->map_size + size*10, TDB_PAGE_SIZE) - tdb->map_size;
+
+	if (!(tdb->flags & TDB_INTERNAL))
+		tdb_munmap(tdb);
+
+	/*
+	 * We must ensure the file is unmapped before doing this
+	 * to ensure consistency with systems like OpenBSD where
+	 * writes and mmaps are not consistent.
+	 */
+
+	/* expand the file itself */
+	if (!(tdb->flags & TDB_INTERNAL)) {
+		if (expand_file(tdb, tdb->map_size, size) != 0)
+			goto fail;
+	}
+
+	tdb->map_size += size;
+
+	if (tdb->flags & TDB_INTERNAL)
+		tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
+	else {
+		/*
+		 * We must ensure the file is remapped before adding the space
+		 * to ensure consistency with systems like OpenBSD where
+		 * writes and mmaps are not consistent.
+		 */
+
+		/* We're ok if the mmap fails as we'll fallback to read/write */
+		tdb_mmap(tdb);
+	}
+
+	/* form a new freelist record */
+	memset(&rec,'\0',sizeof(rec));
+	rec.rec_len = size - sizeof(rec);
+
+	/* link it into the free list */
+	offset = tdb->map_size - size;
+	if (tdb_free(tdb, offset, &rec) == -1)
+		goto fail;
+
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return 0;
+ fail:
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return -1;
+}
+
+/* allocate some space from the free list. The offset returned points
+   to a unconnected list_struct within the database with room for at
+   least length bytes of total data
+
+   0 is returned if the space could not be allocated
+ */
+static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length,
+			    struct list_struct *rec)
+{
+	tdb_off rec_ptr, last_ptr, newrec_ptr;
+	struct list_struct newrec;
+
+	memset(&newrec, '\0', sizeof(newrec));
+
+	if (tdb_lock(tdb, -1, F_WRLCK) == -1)
+		return 0;
+
+	/* Extra bytes required for tailer */
+	length += sizeof(tdb_off);
+
+ again:
+	last_ptr = FREELIST_TOP;
+
+	/* read in the freelist top */
+	if (ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1)
+		goto fail;
+
+	/* keep looking until we find a freelist record big enough */
+	while (rec_ptr) {
+		if (rec_free_read(tdb, rec_ptr, rec) == -1)
+			goto fail;
+
+		if (rec->rec_len >= length) {
+			/* found it - now possibly split it up  */
+			if (rec->rec_len > length + MIN_REC_SIZE) {
+				/* Length of left piece */
+				length = TDB_ALIGN(length, TDB_ALIGNMENT);
+
+				/* Right piece to go on free list */
+				newrec.rec_len = rec->rec_len
+					- (sizeof(*rec) + length);
+				newrec_ptr = rec_ptr + sizeof(*rec) + length;
+
+				/* And left record is shortened */
+				rec->rec_len = length;
+			} else
+				newrec_ptr = 0;
+
+			/* Remove allocated record from the free list */
+			if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+				goto fail;
+
+			/* Update header: do this before we drop alloc
+                           lock, otherwise tdb_free() might try to
+                           merge with us, thinking we're free.
+                           (Thanks Jeremy Allison). */
+			rec->magic = TDB_MAGIC;
+			if (rec_write(tdb, rec_ptr, rec) == -1)
+				goto fail;
+
+			/* Did we create new block? */
+			if (newrec_ptr) {
+				/* Update allocated record tailer (we
+                                   shortened it). */
+				if (update_tailer(tdb, rec_ptr, rec) == -1)
+					goto fail;
+
+				/* Free new record */
+				if (tdb_free(tdb, newrec_ptr, &newrec) == -1)
+					goto fail;
+			}
+
+			/* all done - return the new record offset */
+			tdb_unlock(tdb, -1, F_WRLCK);
+			return rec_ptr;
+		}
+		/* move to the next record */
+		last_ptr = rec_ptr;
+		rec_ptr = rec->next;
+	}
+	/* we didn't find enough space. See if we can expand the
+	   database and if we can then try again */
+	if (tdb_expand(tdb, length + sizeof(*rec)) == 0)
+		goto again;
+ fail:
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return 0;
+}
+
+/* initialise a new database with a specified hash size */
+static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+{
+	struct tdb_header *newdb;
+	int size, ret = -1;
+
+	/* We make it up in memory, then write it out if not internal */
+	size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off);
+	if (!(newdb = calloc(size, 1)))
+		return TDB_ERRCODE(TDB_ERR_OOM, -1);
+
+	/* Fill in the header */
+	newdb->version = TDB_VERSION;
+	newdb->hash_size = hash_size;
+	if (tdb->flags & TDB_INTERNAL) {
+		tdb->map_size = size;
+		tdb->map_ptr = (char *)newdb;
+		memcpy(&tdb->header, newdb, sizeof(tdb->header));
+		/* Convert the `ondisk' version if asked. */
+		CONVERT(*newdb);
+		return 0;
+	}
+	if (lseek(tdb->fd, 0, SEEK_SET) == -1)
+		goto fail;
+
+	if (ftruncate(tdb->fd, 0) == -1)
+		goto fail;
+
+	/* This creates an endian-converted header, as if read from disk */
+	CONVERT(*newdb);
+	memcpy(&tdb->header, newdb, sizeof(tdb->header));
+	/* Don't endian-convert the magic food! */
+	memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
+	if (write(tdb->fd, newdb, size) != size)
+		ret = -1;
+	else
+		ret = tdb_create_rwlocks(tdb->fd, hash_size);
+
+  fail:
+	SAFE_FREE(newdb);
+	return ret;
+}
+
+/* Returns 0 on fail.  On success, return offset of record, and fills
+   in rec */
+static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash,
+			struct list_struct *r)
+{
+	tdb_off rec_ptr;
+	
+	/* read in the hash top */
+	if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
+		return 0;
+
+	/* keep looking until we find the right record */
+	while (rec_ptr) {
+		if (rec_read(tdb, rec_ptr, r) == -1)
+			return 0;
+
+		if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) {
+			char *k;
+			/* a very likely hit - read the key */
+			k = tdb_alloc_read(tdb, rec_ptr + sizeof(*r), 
+					   r->key_len);
+			if (!k)
+				return 0;
+
+			if (memcmp(key.dptr, k, key.dsize) == 0) {
+				SAFE_FREE(k);
+				return rec_ptr;
+			}
+			SAFE_FREE(k);
+		}
+		rec_ptr = r->next;
+	}
+	return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
+}
+
+/* As tdb_find, but if you succeed, keep the lock */
+static tdb_off tdb_find_lock_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, int locktype,
+			     struct list_struct *rec)
+{
+	u32 rec_ptr;
+
+	if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
+		return 0;
+	if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
+		tdb_unlock(tdb, BUCKET(hash), locktype);
+	return rec_ptr;
+}
+
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb)
+{
+	return tdb->ecode;
+}
+
+static struct tdb_errname {
+	enum TDB_ERROR ecode; const char *estring;
+} emap[] = { {TDB_SUCCESS, "Success"},
+	     {TDB_ERR_CORRUPT, "Corrupt database"},
+	     {TDB_ERR_IO, "IO Error"},
+	     {TDB_ERR_LOCK, "Locking error"},
+	     {TDB_ERR_OOM, "Out of memory"},
+	     {TDB_ERR_EXISTS, "Record exists"},
+	     {TDB_ERR_NOLOCK, "Lock exists on other keys"},
+	     {TDB_ERR_NOEXIST, "Record does not exist"} };
+
+/* Error string for the last tdb error */
+const char *tdb_errorstr(TDB_CONTEXT *tdb)
+{
+	u32 i;
+	for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)
+		if (tdb->ecode == emap[i].ecode)
+			return emap[i].estring;
+	return "Invalid error code";
+}
+
+/* update an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1.
+*/
+
+static int tdb_update_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf)
+{
+	struct list_struct rec;
+	tdb_off rec_ptr;
+
+	/* find entry */
+	if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
+		return -1;
+
+	/* must be long enough key, data and tailer */
+	if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) {
+		tdb->ecode = TDB_SUCCESS; /* Not really an error */
+		return -1;
+	}
+
+	if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+		      dbuf.dptr, dbuf.dsize) == -1)
+		return -1;
+
+	if (dbuf.dsize != rec.data_len) {
+		/* update size */
+		rec.data_len = dbuf.dsize;
+		return rec_write(tdb, rec_ptr, &rec);
+	}
+ 
+	return 0;
+}
+
+/* find an entry in the database given a key */
+/* If an entry doesn't exist tdb_err will be set to
+ * TDB_ERR_NOEXIST. If a key has no data attached
+ * tdb_err will not be set. Both will return a
+ * zero pptr and zero dsize.
+ */
+
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	tdb_off rec_ptr;
+	struct list_struct rec;
+	TDB_DATA ret;
+	u32 hash;
+
+	/* find which hash bucket it is in */
+	hash = tdb->hash_fn(&key);
+	if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))
+		return tdb_null;
+
+	if (rec.data_len)
+		ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+					  rec.data_len);
+	else
+		ret.dptr = NULL;
+	ret.dsize = rec.data_len;
+	tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+	return ret;
+}
+
+/* check if an entry in the database exists 
+
+   note that 1 is returned if the key is found and 0 is returned if not found
+   this doesn't match the conventions in the rest of this module, but is
+   compatible with gdbm
+*/
+static int tdb_exists_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash)
+{
+	struct list_struct rec;
+	
+	if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
+		return 0;
+	tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+	return 1;
+}
+
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	u32 hash = tdb->hash_fn(&key);
+	return tdb_exists_hash(tdb, key, hash);
+}
+
+/* record lock stops delete underneath */
+static int lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+	return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0;
+}
+/*
+  Write locks override our own fcntl readlocks, so check it here.
+  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+  an error to fail to get the lock here.
+*/
+ 
+static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+	struct tdb_traverse_lock *i;
+	for (i = &tdb->travlocks; i; i = i->next)
+		if (i->off == off)
+			return -1;
+	return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1);
+}
+
+/*
+  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+  an error to fail to get the lock here.
+*/
+
+static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+	return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0);
+}
+/* fcntl locks don't stack: avoid unlocking someone else's */
+static int unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+	struct tdb_traverse_lock *i;
+	u32 count = 0;
+
+	if (off == 0)
+		return 0;
+	for (i = &tdb->travlocks; i; i = i->next)
+		if (i->off == off)
+			count++;
+	return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0);
+}
+
+/* actually delete an entry in the database given the offset */
+static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec)
+{
+	tdb_off last_ptr, i;
+	struct list_struct lastrec;
+
+	if (tdb->read_only) return -1;
+
+	if (write_lock_record(tdb, rec_ptr) == -1) {
+		/* Someone traversing here: mark it as dead */
+		rec->magic = TDB_DEAD_MAGIC;
+		return rec_write(tdb, rec_ptr, rec);
+	}
+	if (write_unlock_record(tdb, rec_ptr) != 0)
+		return -1;
+
+	/* find previous record in hash chain */
+	if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)
+		return -1;
+	for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
+		if (rec_read(tdb, i, &lastrec) == -1)
+			return -1;
+
+	/* unlink it: next ptr is at start of record. */
+	if (last_ptr == 0)
+		last_ptr = TDB_HASH_TOP(rec->full_hash);
+	if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+		return -1;
+
+	/* recover the space */
+	if (tdb_free(tdb, rec_ptr, rec) == -1)
+		return -1;
+	return 0;
+}
+
+/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */
+static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock,
+			 struct list_struct *rec)
+{
+	int want_next = (tlock->off != 0);
+
+	/* Lock each chain from the start one. */
+	for (; tlock->hash < tdb->header.hash_size; tlock->hash++) {
+		if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1)
+			return -1;
+
+		/* No previous record?  Start at top of chain. */
+		if (!tlock->off) {
+			if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash),
+				     &tlock->off) == -1)
+				goto fail;
+		} else {
+			/* Otherwise unlock the previous record. */
+			if (unlock_record(tdb, tlock->off) != 0)
+				goto fail;
+		}
+
+		if (want_next) {
+			/* We have offset of old record: grab next */
+			if (rec_read(tdb, tlock->off, rec) == -1)
+				goto fail;
+			tlock->off = rec->next;
+		}
+
+		/* Iterate through chain */
+		while( tlock->off) {
+			tdb_off current;
+			if (rec_read(tdb, tlock->off, rec) == -1)
+				goto fail;
+			if (!TDB_DEAD(rec)) {
+				/* Woohoo: we found one! */
+				if (lock_record(tdb, tlock->off) != 0)
+					goto fail;
+				return tlock->off;
+			}
+			/* Try to clean dead ones from old traverses */
+			current = tlock->off;
+			tlock->off = rec->next;
+			if (!tdb->read_only && 
+			    do_delete(tdb, current, rec) != 0)
+				goto fail;
+		}
+		tdb_unlock(tdb, tlock->hash, F_WRLCK);
+		want_next = 0;
+	}
+	/* We finished iteration without finding anything */
+	return TDB_ERRCODE(TDB_SUCCESS, 0);
+
+ fail:
+	tlock->off = 0;
+	if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0)
+		TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n"));
+	return -1;
+}
+
+/* traverse the entire database - calling fn(tdb, key, data) on each element.
+   return -1 on error or the record count traversed
+   if fn is NULL then it is not called
+   a non-zero return value from fn() indicates that the traversal should stop
+  */
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *private)
+{
+	TDB_DATA key, dbuf;
+	struct list_struct rec;
+	struct tdb_traverse_lock tl = { NULL, 0, 0 };
+	int ret, count = 0;
+
+	/* This was in the initializaton, above, but the IRIX compiler
+	 * did not like it.  crh
+	 */
+	tl.next = tdb->travlocks.next;
+
+	/* fcntl locks don't stack: beware traverse inside traverse */
+	tdb->travlocks.next = &tl;
+
+	/* tdb_next_lock places locks on the record returned, and its chain */
+	while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) {
+		count++;
+		/* now read the full record */
+		key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec), 
+					  rec.key_len + rec.data_len);
+		if (!key.dptr) {
+			ret = -1;
+			if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0)
+				goto out;
+			if (unlock_record(tdb, tl.off) != 0)
+				TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
+			goto out;
+		}
+		key.dsize = rec.key_len;
+		dbuf.dptr = key.dptr + rec.key_len;
+		dbuf.dsize = rec.data_len;
+
+		/* Drop chain lock, call out */
+		if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) {
+			ret = -1;
+			goto out;
+		}
+		if (fn && fn(tdb, key, dbuf, private)) {
+			/* They want us to terminate traversal */
+			ret = count;
+			if (unlock_record(tdb, tl.off) != 0) {
+				TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));;
+				ret = -1;
+			}
+			tdb->travlocks.next = tl.next;
+			SAFE_FREE(key.dptr);
+			return count;
+		}
+		SAFE_FREE(key.dptr);
+	}
+out:
+	tdb->travlocks.next = tl.next;
+	if (ret < 0)
+		return -1;
+	else
+		return count;
+}
+
+/* find the first entry in the database and return its key */
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
+{
+	TDB_DATA key;
+	struct list_struct rec;
+
+	/* release any old lock */
+	if (unlock_record(tdb, tdb->travlocks.off) != 0)
+		return tdb_null;
+	tdb->travlocks.off = tdb->travlocks.hash = 0;
+
+	if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0)
+		return tdb_null;
+	/* now read the key */
+	key.dsize = rec.key_len;
+	key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);
+	if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0)
+		TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
+	return key;
+}
+
+/* find the next entry in the database, returning its key */
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey)
+{
+	u32 oldhash;
+	TDB_DATA key = tdb_null;
+	struct list_struct rec;
+	char *k = NULL;
+
+	/* Is locked key the old key?  If so, traverse will be reliable. */
+	if (tdb->travlocks.off) {
+		if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK))
+			return tdb_null;
+		if (rec_read(tdb, tdb->travlocks.off, &rec) == -1
+		    || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
+					    rec.key_len))
+		    || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {
+			/* No, it wasn't: unlock it and start from scratch */
+			if (unlock_record(tdb, tdb->travlocks.off) != 0)
+				return tdb_null;
+			if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+				return tdb_null;
+			tdb->travlocks.off = 0;
+		}
+
+		SAFE_FREE(k);
+	}
+
+	if (!tdb->travlocks.off) {
+		/* No previous element: do normal find, and lock record */
+		tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), F_WRLCK, &rec);
+		if (!tdb->travlocks.off)
+			return tdb_null;
+		tdb->travlocks.hash = BUCKET(rec.full_hash);
+		if (lock_record(tdb, tdb->travlocks.off) != 0) {
+			TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));
+			return tdb_null;
+		}
+	}
+	oldhash = tdb->travlocks.hash;
+
+	/* Grab next record: locks chain and returned record,
+	   unlocks old record */
+	if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) {
+		key.dsize = rec.key_len;
+		key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
+					  key.dsize);
+		/* Unlock the chain of this new record */
+		if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+			TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+	}
+	/* Unlock the chain of old record */
+	if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0)
+		TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+	return key;
+}
+
+/* delete an entry in the database given a key */
+static int tdb_delete_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash)
+{
+	tdb_off rec_ptr;
+	struct list_struct rec;
+	int ret;
+
+	if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec)))
+		return -1;
+	ret = do_delete(tdb, rec_ptr, &rec);
+	if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
+		TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n"));
+	return ret;
+}
+
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	u32 hash = tdb->hash_fn(&key);
+	return tdb_delete_hash(tdb, key, hash);
+}
+
+/* store an element in the database, replacing any existing element
+   with the same key 
+
+   return 0 on success, -1 on failure
+*/
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+{
+	struct list_struct rec;
+	u32 hash;
+	tdb_off rec_ptr;
+	char *p = NULL;
+	int ret = 0;
+
+	/* find which hash bucket it is in */
+	hash = tdb->hash_fn(&key);
+	if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+		return -1;
+
+	/* check for it existing, on insert. */
+	if (flag == TDB_INSERT) {
+		if (tdb_exists_hash(tdb, key, hash)) {
+			tdb->ecode = TDB_ERR_EXISTS;
+			goto fail;
+		}
+	} else {
+		/* first try in-place update, on modify or replace. */
+		if (tdb_update_hash(tdb, key, hash, dbuf) == 0)
+			goto out;
+		if (tdb->ecode == TDB_ERR_NOEXIST &&
+		    flag == TDB_MODIFY) {
+			/* if the record doesn't exist and we are in TDB_MODIFY mode then
+			 we should fail the store */
+			goto fail;
+	}
+	}
+	/* reset the error code potentially set by the tdb_update() */
+	tdb->ecode = TDB_SUCCESS;
+
+	/* delete any existing record - if it doesn't exist we don't
+           care.  Doing this first reduces fragmentation, and avoids
+           coalescing with `allocated' block before it's updated. */
+	if (flag != TDB_INSERT)
+		tdb_delete_hash(tdb, key, hash);
+
+	/* Copy key+value *before* allocating free space in case malloc
+	   fails and we are left with a dead spot in the tdb. */
+
+	if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
+		tdb->ecode = TDB_ERR_OOM;
+		goto fail;
+	}
+
+	memcpy(p, key.dptr, key.dsize);
+	if (dbuf.dsize)
+		memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
+
+	/* we have to allocate some space */
+	if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec)))
+		goto fail;
+
+	/* Read hash top into next ptr */
+	if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+		goto fail;
+
+	rec.key_len = key.dsize;
+	rec.data_len = dbuf.dsize;
+	rec.full_hash = hash;
+	rec.magic = TDB_MAGIC;
+
+	/* write out and point the top of the hash chain at it */
+	if (rec_write(tdb, rec_ptr, &rec) == -1
+	    || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
+	    || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+		/* Need to tdb_unallocate() here */
+		goto fail;
+	}
+ out:
+	SAFE_FREE(p); 
+	tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+	return ret;
+fail:
+	ret = -1;
+	goto out;
+}
+
+/* Attempt to append data to an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1. Record must be locked before calling.
+*/
+static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA new_dbuf)
+{
+	struct list_struct rec;
+	tdb_off rec_ptr;
+
+	/* find entry */
+	if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
+		return -1;
+
+	/* Append of 0 is always ok. */
+	if (new_dbuf.dsize == 0)
+		return 0;
+
+	/* must be long enough for key, old data + new data and tailer */
+	if (rec.rec_len < key.dsize + rec.data_len + new_dbuf.dsize + sizeof(tdb_off)) {
+		/* No room. */
+		tdb->ecode = TDB_SUCCESS; /* Not really an error */
+		return -1;
+	}
+
+	if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len + rec.data_len,
+		      new_dbuf.dptr, new_dbuf.dsize) == -1)
+		return -1;
+
+	/* update size */
+	rec.data_len += new_dbuf.dsize;
+	return rec_write(tdb, rec_ptr, &rec);
+}
+
+/* Append to an entry. Create if not exist. */
+
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
+{
+	struct list_struct rec;
+	u32 hash;
+	tdb_off rec_ptr;
+	char *p = NULL;
+	int ret = 0;
+	size_t new_data_size = 0;
+
+	/* find which hash bucket it is in */
+	hash = tdb->hash_fn(&key);
+	if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+		return -1;
+
+	/* first try in-place. */
+	if (tdb_append_inplace(tdb, key, hash, new_dbuf) == 0)
+		goto out;
+
+	/* reset the error code potentially set by the tdb_append_inplace() */
+	tdb->ecode = TDB_SUCCESS;
+
+	/* find entry */
+	if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
+		if (tdb->ecode != TDB_ERR_NOEXIST)
+			goto fail;
+
+		/* Not found - create. */
+
+		ret = tdb_store(tdb, key, new_dbuf, TDB_INSERT);
+		goto out;
+	}
+
+	new_data_size = rec.data_len + new_dbuf.dsize;
+
+	/* Copy key+old_value+value *before* allocating free space in case malloc
+	   fails and we are left with a dead spot in the tdb. */
+
+	if (!(p = (char *)malloc(key.dsize + new_data_size))) {
+		tdb->ecode = TDB_ERR_OOM;
+		goto fail;
+	}
+
+	/* Copy the key in place. */
+	memcpy(p, key.dptr, key.dsize);
+
+	/* Now read the old data into place. */
+	if (rec.data_len &&
+		tdb_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, p + key.dsize, rec.data_len, 0) == -1)
+			goto fail;
+
+	/* Finally append the new data. */
+	if (new_dbuf.dsize)
+		memcpy(p+key.dsize+rec.data_len, new_dbuf.dptr, new_dbuf.dsize);
+
+	/* delete any existing record - if it doesn't exist we don't
+           care.  Doing this first reduces fragmentation, and avoids
+           coalescing with `allocated' block before it's updated. */
+
+	tdb_delete_hash(tdb, key, hash);
+
+	if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec)))
+		goto fail;
+
+	/* Read hash top into next ptr */
+	if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+		goto fail;
+
+	rec.key_len = key.dsize;
+	rec.data_len = new_data_size;
+	rec.full_hash = hash;
+	rec.magic = TDB_MAGIC;
+
+	/* write out and point the top of the hash chain at it */
+	if (rec_write(tdb, rec_ptr, &rec) == -1
+	    || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+new_data_size)==-1
+	    || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+		/* Need to tdb_unallocate() here */
+		goto fail;
+	}
+
+ out:
+	SAFE_FREE(p); 
+	tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+	return ret;
+
+fail:
+	ret = -1;
+	goto out;
+}
+
+static int tdb_already_open(dev_t device,
+			    ino_t ino)
+{
+	TDB_CONTEXT *i;
+	
+	for (i = tdbs; i; i = i->next) {
+		if (i->device == device && i->inode == ino) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/* This is based on the hash algorithm from gdbm */
+static u32 default_tdb_hash(TDB_DATA *key)
+{
+	u32 value;	/* Used to compute the hash value.  */
+	u32   i;	/* Used to cycle through random values. */
+
+	/* Set the initial value from the key size. */
+	for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
+		value = (value + (key->dptr[i] << (i*5 % 24)));
+
+	return (1103515243 * value + 12345);  
+}
+
+/* open the database, creating it if necessary 
+
+   The open_flags and mode are passed straight to the open call on the
+   database file. A flags value of O_WRONLY is invalid. The hash size
+   is advisory, use zero for a default value.
+
+   Return is NULL on error, in which case errno is also set.  Don't 
+   try to call tdb_error or tdb_errname, just do strerror(errno).
+
+   @param name may be NULL for internal databases. */
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+		      int open_flags, mode_t mode)
+{
+	return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL);
+}
+
+
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+			 int open_flags, mode_t mode,
+			 tdb_log_func log_fn,
+			 tdb_hash_func hash_fn)
+{
+	TDB_CONTEXT *tdb;
+	struct stat st;
+	int rev = 0, locked = 0;
+	unsigned char *vp;
+	u32 vertest;
+
+	if (!(tdb = calloc(1, sizeof *tdb))) {
+		/* Can't log this */
+		errno = ENOMEM;
+		goto fail;
+	}
+	tdb->fd = -1;
+	tdb->name = NULL;
+	tdb->map_ptr = NULL;
+	tdb->flags = tdb_flags;
+	tdb->open_flags = open_flags;
+	tdb->log_fn = log_fn;
+	tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash;
+
+	if ((open_flags & O_ACCMODE) == O_WRONLY) {
+		TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n",
+			 name));
+		errno = EINVAL;
+		goto fail;
+	}
+	
+	if (hash_size == 0)
+		hash_size = DEFAULT_HASH_SIZE;
+	if ((open_flags & O_ACCMODE) == O_RDONLY) {
+		tdb->read_only = 1;
+		/* read only databases don't do locking or clear if first */
+		tdb->flags |= TDB_NOLOCK;
+		tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+	}
+
+	/* internal databases don't mmap or lock, and start off cleared */
+	if (tdb->flags & TDB_INTERNAL) {
+		tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
+		tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+		if (tdb_new_database(tdb, hash_size) != 0) {
+			TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!"));
+			goto fail;
+		}
+		goto internal;
+	}
+
+	if ((tdb->fd = open(name, open_flags, mode)) == -1) {
+		TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n",
+			 name, strerror(errno)));
+		goto fail;	/* errno set by open(2) */
+	}
+
+	/* ensure there is only one process initialising at once */
+	if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) {
+		TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n",
+			 name, strerror(errno)));
+		goto fail;	/* errno set by tdb_brlock */
+	}
+
+	/* we need to zero database if we are the only one with it open */
+	if ((tdb_flags & TDB_CLEAR_IF_FIRST) &&
+		(locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0))) {
+		open_flags |= O_CREAT;
+		if (ftruncate(tdb->fd, 0) == -1) {
+			TDB_LOG((tdb, 0, "tdb_open_ex: "
+				 "failed to truncate %s: %s\n",
+				 name, strerror(errno)));
+			goto fail; /* errno set by ftruncate */
+		}
+	}
+
+	if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
+	    || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0
+	    || (tdb->header.version != TDB_VERSION
+		&& !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) {
+		/* its not a valid database - possibly initialise it */
+		if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
+			errno = EIO; /* ie bad format or something */
+			goto fail;
+		}
+		rev = (tdb->flags & TDB_CONVERT);
+	}
+	vp = (unsigned char *)&tdb->header.version;
+	vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) |
+		  (((u32)vp[2]) << 8) | (u32)vp[3];
+	tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
+	if (!rev)
+		tdb->flags &= ~TDB_CONVERT;
+	else {
+		tdb->flags |= TDB_CONVERT;
+		convert(&tdb->header, sizeof(tdb->header));
+	}
+	if (fstat(tdb->fd, &st) == -1)
+		goto fail;
+
+	/* Is it already in the open list?  If so, fail. */
+	if (tdb_already_open(st.st_dev, st.st_ino)) {
+		TDB_LOG((tdb, 2, "tdb_open_ex: "
+			 "%s (%d,%d) is already open in this process\n",
+			 name, (int)st.st_dev, (int)st.st_ino));
+		errno = EBUSY;
+		goto fail;
+	}
+
+	if (!(tdb->name = (char *)strdup(name))) {
+		errno = ENOMEM;
+		goto fail;
+	}
+
+	tdb->map_size = st.st_size;
+	tdb->device = st.st_dev;
+	tdb->inode = st.st_ino;
+	tdb->locked = calloc(tdb->header.hash_size+1, sizeof(tdb->locked[0]));
+	if (!tdb->locked) {
+		TDB_LOG((tdb, 2, "tdb_open_ex: "
+			 "failed to allocate lock structure for %s\n",
+			 name));
+		errno = ENOMEM;
+		goto fail;
+	}
+	tdb_mmap(tdb);
+	if (locked) {
+		if (!tdb->read_only)
+			if (tdb_clear_spinlocks(tdb) != 0) {
+				TDB_LOG((tdb, 0, "tdb_open_ex: "
+				"failed to clear spinlock\n"));
+				goto fail;
+			}
+		if (tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) {
+			TDB_LOG((tdb, 0, "tdb_open_ex: "
+				 "failed to take ACTIVE_LOCK on %s: %s\n",
+				 name, strerror(errno)));
+			goto fail;
+		}
+
+	}
+
+	/* We always need to do this if the CLEAR_IF_FIRST flag is set, even if
+	   we didn't get the initial exclusive lock as we need to let all other
+	   users know we're using it. */
+
+	if (tdb_flags & TDB_CLEAR_IF_FIRST) {
+		/* leave this lock in place to indicate it's in use */
+		if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)
+			goto fail;
+	}
+
+
+ internal:
+	/* Internal (memory-only) databases skip all the code above to
+	 * do with disk files, and resume here by releasing their
+	 * global lock and hooking into the active list. */
+	if (tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1)
+		goto fail;
+	tdb->next = tdbs;
+	tdbs = tdb;
+	return tdb;
+
+ fail:
+	{ int save_errno = errno;
+
+	if (!tdb)
+		return NULL;
+	
+	if (tdb->map_ptr) {
+		if (tdb->flags & TDB_INTERNAL)
+			SAFE_FREE(tdb->map_ptr);
+		else
+			tdb_munmap(tdb);
+	}
+	SAFE_FREE(tdb->name);
+	if (tdb->fd != -1)
+		if (close(tdb->fd) != 0)
+			TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n"));
+	SAFE_FREE(tdb->locked);
+	SAFE_FREE(tdb);
+	errno = save_errno;
+	return NULL;
+	}
+}
+
+/**
+ * Close a database.
+ *
+ * @returns -1 for error; 0 for success.
+ **/
+int tdb_close(TDB_CONTEXT *tdb)
+{
+	TDB_CONTEXT **i;
+	int ret = 0;
+
+	if (tdb->map_ptr) {
+		if (tdb->flags & TDB_INTERNAL)
+			SAFE_FREE(tdb->map_ptr);
+		else
+			tdb_munmap(tdb);
+	}
+	SAFE_FREE(tdb->name);
+	if (tdb->fd != -1)
+		ret = close(tdb->fd);
+	SAFE_FREE(tdb->locked);
+
+	/* Remove from contexts list */
+	for (i = &tdbs; *i; i = &(*i)->next) {
+		if (*i == tdb) {
+			*i = tdb->next;
+			break;
+		}
+	}
+
+	memset(tdb, 0, sizeof(*tdb));
+	SAFE_FREE(tdb);
+
+	return ret;
+}
+
+/* lock/unlock entire database */
+int tdb_lockall(TDB_CONTEXT *tdb)
+{
+	u32 i;
+
+	/* There are no locks on read-only dbs */
+	if (tdb->read_only)
+		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+	for (i = 0; i < tdb->header.hash_size; i++) 
+		if (tdb_lock(tdb, i, F_WRLCK))
+			break;
+
+	/* If error, release locks we have... */
+	if (i < tdb->header.hash_size) {
+		u32 j;
+
+		for ( j = 0; j < i; j++)
+			tdb_unlock(tdb, j, F_WRLCK);
+		return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+	}
+
+	return 0;
+}
+void tdb_unlockall(TDB_CONTEXT *tdb)
+{
+	u32 i;
+	for (i=0; i < tdb->header.hash_size; i++)
+		tdb_unlock(tdb, i, F_WRLCK);
+}
+
+/* lock/unlock one hash chain. This is meant to be used to reduce
+   contention - it cannot guarantee how many records will be locked */
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
+}
+
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
+}
+
+int tdb_chainlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
+}
+
+int tdb_chainunlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
+}
+
+
+/* register a loging function */
+void tdb_logging_function(TDB_CONTEXT *tdb, void (*fn)(TDB_CONTEXT *, int , const char *, ...))
+{
+	tdb->log_fn = fn;
+}
+
+/* reopen a tdb - this can be used after a fork to ensure that we have an independent
+   seek pointer from our parent and to re-establish locks */
+int tdb_reopen(TDB_CONTEXT *tdb)
+{
+	struct stat st;
+
+	if (tdb->flags & TDB_INTERNAL)
+		return 0; /* Nothing to do. */
+	if (tdb_munmap(tdb) != 0) {
+		TDB_LOG((tdb, 0, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
+		goto fail;
+	}
+	if (close(tdb->fd) != 0)
+		TDB_LOG((tdb, 0, "tdb_reopen: WARNING closing tdb->fd failed!\n"));
+	tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0);
+	if (tdb->fd == -1) {
+		TDB_LOG((tdb, 0, "tdb_reopen: open failed (%s)\n", strerror(errno)));
+		goto fail;
+	}
+	if (fstat(tdb->fd, &st) != 0) {
+		TDB_LOG((tdb, 0, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
+		goto fail;
+	}
+	if (st.st_ino != tdb->inode || st.st_dev != tdb->device) {
+		TDB_LOG((tdb, 0, "tdb_reopen: file dev/inode has changed!\n"));
+		goto fail;
+	}
+	tdb_mmap(tdb);
+	if ((tdb->flags & TDB_CLEAR_IF_FIRST) && (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)) {
+		TDB_LOG((tdb, 0, "tdb_reopen: failed to obtain active lock\n"));
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	tdb_close(tdb);
+	return -1;
+}
+
+/* reopen all tdb's */
+int tdb_reopen_all(void)
+{
+	TDB_CONTEXT *tdb;
+
+	for (tdb=tdbs; tdb; tdb = tdb->next) {
+		/* Ensure no clear-if-first. */
+		tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+		if (tdb_reopen(tdb) != 0)
+			return -1;
+	}
+
+	return 0;
+}
diff --git a/ap/app/pppd/pppd/tdb.h b/ap/app/pppd/pppd/tdb.h
new file mode 100644
index 0000000..153b6e9
--- /dev/null
+++ b/ap/app/pppd/pppd/tdb.h
@@ -0,0 +1,164 @@
+#ifndef __TDB_H__
+#define __TDB_H__
+
+/* 
+   Unix SMB/CIFS implementation.
+   
+   trivial database library
+   
+   Copyright (C) Andrew Tridgell 1999-2004
+   
+     ** NOTE! The following LGPL license applies to the tdb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#ifndef PRINTF_ATTRIBUTE
+/** Use gcc attribute to check printf fns.  a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#if (__GNUC__ >= 3)
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+/* flags to tdb_store() */
+#define TDB_REPLACE 1
+#define TDB_INSERT 2
+#define TDB_MODIFY 3
+
+/* flags for tdb_open() */
+#define TDB_DEFAULT 0 /* just a readability place holder */
+#define TDB_CLEAR_IF_FIRST 1
+#define TDB_INTERNAL 2 /* don't store on disk */
+#define TDB_NOLOCK   4 /* don't do any locking */
+#define TDB_NOMMAP   8 /* don't use mmap */
+#define TDB_CONVERT 16 /* convert endian (internal use) */
+#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
+
+#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
+
+/* error codes */
+enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 
+		TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT,
+		TDB_ERR_NOEXIST};
+
+#ifndef u32
+#define u32 unsigned
+#endif
+
+typedef struct {
+	char *dptr;
+	size_t dsize;
+} TDB_DATA;
+
+typedef u32 tdb_len;
+typedef u32 tdb_off;
+
+/* this is stored at the front of every database */
+struct tdb_header {
+	char magic_food[32]; /* for /etc/magic */
+	u32 version; /* version of the code */
+	u32 hash_size; /* number of hash entries */
+	tdb_off rwlocks;
+	tdb_off reserved[31];
+};
+
+struct tdb_lock_type {
+	u32 count;
+	u32 ltype;
+};
+
+struct tdb_traverse_lock {
+	struct tdb_traverse_lock *next;
+	u32 off;
+	u32 hash;
+};
+
+/* this is the context structure that is returned from a db open */
+typedef struct tdb_context {
+	char *name; /* the name of the database */
+	void *map_ptr; /* where it is currently mapped */
+	int fd; /* open file descriptor for the database */
+	tdb_len map_size; /* how much space has been mapped */
+	int read_only; /* opened read-only */
+	struct tdb_lock_type *locked; /* array of chain locks */
+	enum TDB_ERROR ecode; /* error code for last tdb error */
+	struct tdb_header header; /* a cached copy of the header */
+	u32 flags; /* the flags passed to tdb_open */
+	struct tdb_traverse_lock travlocks; /* current traversal locks */
+	struct tdb_context *next; /* all tdbs to avoid multiple opens */
+	dev_t device;	/* uniquely identifies this tdb */
+	ino_t inode;	/* uniquely identifies this tdb */
+	void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...) PRINTF_ATTRIBUTE(3,4); /* logging function */
+	u32 (*hash_fn)(TDB_DATA *key);
+	int open_flags; /* flags used in the open - needed by reopen */
+} TDB_CONTEXT;
+
+typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *);
+typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...);
+typedef u32 (*tdb_hash_func)(TDB_DATA *key);
+
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+		      int open_flags, mode_t mode);
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+			 int open_flags, mode_t mode,
+			 tdb_log_func log_fn,
+			 tdb_hash_func hash_fn);
+
+int tdb_reopen(TDB_CONTEXT *tdb);
+int tdb_reopen_all(void);
+void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func);
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb);
+const char *tdb_errorstr(TDB_CONTEXT *tdb);
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf);
+int tdb_close(TDB_CONTEXT *tdb);
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *);
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]);
+void tdb_unlockkeys(TDB_CONTEXT *tdb);
+int tdb_lockall(TDB_CONTEXT *tdb);
+void tdb_unlockall(TDB_CONTEXT *tdb);
+
+/* Low level locking functions: use with care */
+void tdb_set_lock_alarm(sig_atomic_t *palarm);
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key);
+
+/* Debug functions. Not used in production. */
+void tdb_dump_all(TDB_CONTEXT *tdb);
+int tdb_printfreelist(TDB_CONTEXT *tdb);
+
+extern TDB_DATA tdb_null;
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* tdb.h */
diff --git a/ap/app/pppd/pppd/tty.c b/ap/app/pppd/pppd/tty.c
new file mode 100644
index 0000000..842c3ff
--- /dev/null
+++ b/ap/app/pppd/pppd/tty.c
@@ -0,0 +1,1272 @@
+/*
+ * tty.c - code for handling serial ports in pppd.
+ *
+ * Copyright (C) 2000-2004 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:d
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Portions derived from main.c, which is:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: tty.c,v 1.27 2008/07/01 12:27:56 paulus Exp $"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.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));
+#ifndef __uClinux__
+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 *));
+#endif
+static int open_socket __P((char *));
+static void maybe_relock __P((void *, int));
+
+static int pty_master;		/* fd for master side of pty */
+static int pty_slave;		/* fd for slave side of pty */
+static int real_ttyfd;		/* fd for actual serial port (not pty) */
+static int ttyfd;		/* Serial port file descriptor */
+static char speed_str[16];	/* Serial port speed as string */
+
+mode_t tty_mode = (mode_t)-1;	/* Original access permissions to tty */
+int baud_rate;			/* Actual bits/second for serial device */
+char *callback_script;		/* script for doing callback */
+int charshunt_pid;		/* Process ID for charshunt */
+int locked;			/* lock() has succeeded */
+struct stat devstat;		/* result of stat() on devnam */
+
+/* option variables */
+int	crtscts = 0;		/* Use hardware flow control */
+bool	modem = 1;		/* Use modem control lines */
+int	inspeed = 0;		/* Input/Output speed requested */
+bool	lockflag = 0;		/* Create lock file to lock the serial dev */
+char	*initializer = NULL;	/* Script to initialize physical link */
+char	*connect_script = NULL;	/* Script to establish physical link */
+char	*disconnect_script = NULL; /* Script to disestablish physical link */
+char	*welcomer = NULL;	/* Script to run after phys link estab. */
+char	*ptycommand = NULL;	/* Command to run on other side of pty */
+bool	notty = 0;		/* Stdin/out is not a tty */
+char	*record_file = NULL;	/* File to record chars sent/received */
+int	max_data_rate;		/* max bytes/sec through charshunt */
+bool	sync_serial = 0;	/* Device is synchronous serial device */
+char	*pty_socket = NULL;	/* Socket to connect to pty */
+int	using_pty = 0;		/* we're allocating a pty as the device */
+
+extern uid_t uid;
+extern int kill_link;
+extern int asked_to_quit;
+extern int got_sigterm;
+
+/* XXX */
+extern int privopen;		/* don't lock, open device as root */
+
+u_int32_t xmit_accm[8];		/* extended transmit ACCM */
+
+/* option descriptors */
+option_t tty_options[] = {
+    /* device name must be first, or change connect_tty() below! */
+    { "device name", o_wild, (void *) &setdevname,
+      "Serial port device name",
+      OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
+      devnam},
+
+    { "tty speed", o_wild, (void *) &setspeed,
+      "Baud rate for serial port",
+      OPT_PRIO | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, speed_str },
+
+    { "lock", o_bool, &lockflag,
+      "Lock serial device with UUCP-style lock file", OPT_PRIO | 1 },
+    { "nolock", o_bool, &lockflag,
+      "Don't lock serial device", OPT_PRIOSUB | OPT_PRIV },
+
+    { "init", o_string, &initializer,
+      "A program to initialize the device", OPT_PRIO | OPT_PRIVFIX },
+
+    { "connect", o_string, &connect_script,
+      "A program to set up a connection", OPT_PRIO | OPT_PRIVFIX },
+
+    { "disconnect", o_string, &disconnect_script,
+      "Program to disconnect serial device", OPT_PRIO | OPT_PRIVFIX },
+
+    { "welcome", o_string, &welcomer,
+      "Script to welcome client", OPT_PRIO | OPT_PRIVFIX },
+
+    { "pty", o_string, &ptycommand,
+      "Script to run on pseudo-tty master side",
+      OPT_PRIO | OPT_PRIVFIX | OPT_DEVNAM },
+
+    { "notty", o_bool, &notty,
+      "Input/output is not a tty", OPT_DEVNAM | 1 },
+
+    { "socket", o_string, &pty_socket,
+      "Send and receive over socket, arg is host:port",
+      OPT_PRIO | OPT_DEVNAM },
+
+    { "record", o_string, &record_file,
+      "Record characters sent/received to file", OPT_PRIO },
+
+    { "crtscts", o_int, &crtscts,
+      "Set hardware (RTS/CTS) flow control",
+      OPT_PRIO | OPT_NOARG | OPT_VAL(1) },
+    { "cdtrcts", o_int, &crtscts,
+      "Set alternate hardware (DTR/CTS) flow control",
+      OPT_PRIOSUB | OPT_NOARG | OPT_VAL(2) },
+    { "nocrtscts", o_int, &crtscts,
+      "Disable hardware flow control",
+      OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) },
+    { "-crtscts", o_int, &crtscts,
+      "Disable hardware flow control",
+      OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
+    { "nocdtrcts", o_int, &crtscts,
+      "Disable hardware flow control",
+      OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
+    { "xonxoff", o_special_noarg, (void *)setxonxoff,
+      "Set software (XON/XOFF) flow control", OPT_PRIOSUB },
+
+    { "modem", o_bool, &modem,
+      "Use modem control lines", OPT_PRIO | 1 },
+    { "local", o_bool, &modem,
+      "Don't use modem control lines", OPT_PRIOSUB | 0 },
+
+    { "sync", o_bool, &sync_serial,
+      "Use synchronous HDLC serial encoding", 1 },
+
+    { "datarate", o_int, &max_data_rate,
+      "Maximum data rate in bytes/sec (with pty, notty or record option)",
+      OPT_PRIO },
+
+    { "escape", o_special, (void *)setescape,
+      "List of character codes to escape on transmission",
+      OPT_A2PRINTER, (void *)printescape },
+
+    { NULL }
+};
+
+
+struct channel tty_channel = {
+	tty_options,
+	&tty_process_extra_options,
+	&tty_check_options,
+	&connect_tty,
+	&disconnect_tty,
+	&tty_establish_ppp,
+	&tty_disestablish_ppp,
+	&tty_do_send_config,
+	&tty_recv_config,
+	&cleanup_tty,
+	&tty_close_fds
+};
+
+/*
+ * setspeed - Set the serial port baud rate.
+ * If doit is 0, the call is to check whether this option is
+ * potentially a speed value.
+ */
+static int
+setspeed(arg, argv, doit)
+    char *arg;
+    char **argv;
+    int doit;
+{
+	char *ptr;
+	int spd;
+
+	spd = strtol(arg, &ptr, 0);
+	if (ptr == arg || *ptr != 0 || spd == 0)
+		return 0;
+	if (doit) {
+		inspeed = spd;
+		slprintf(speed_str, sizeof(speed_str), "%d", spd);
+	}
+	return 1;
+}
+
+
+/*
+ * setdevname - Set the device name.
+ * If doit is 0, the call is to check whether this option is
+ * potentially a device name.
+ */
+static int
+setdevname(cp, argv, doit)
+    char *cp;
+    char **argv;
+    int doit;
+{
+	struct stat statbuf;
+	char dev[MAXPATHLEN];
+
+	if (*cp == 0)
+		return 0;
+
+	if (*cp != '/') {
+		strlcpy(dev, "/dev/", sizeof(dev));
+		strlcat(dev, cp, sizeof(dev));
+		cp = dev;
+	}
+
+	/*
+	 * Check if there is a character device by this name.
+	 */
+	if (stat(cp, &statbuf) < 0) {
+		if (!doit)
+			return errno != ENOENT;
+		option_error("Couldn't stat %s: %m", cp);
+		return 0;
+	}
+	if (!S_ISCHR(statbuf.st_mode)) {
+		if (doit)
+			option_error("%s is not a character device", cp);
+		return 0;
+	}
+
+	if (doit) {
+		strlcpy(devnam, cp, sizeof(devnam));
+		devstat = statbuf;
+		default_device = 0;
+	}
+  
+	return 1;
+}
+
+static int
+setxonxoff(argv)
+    char **argv;
+{
+	lcp_wantoptions[0].asyncmap |= 0x000A0000;	/* escape ^S and ^Q */
+	lcp_wantoptions[0].neg_asyncmap = 1;
+
+	crtscts = -2;
+	return 1;
+}
+
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+static int
+setescape(argv)
+    char **argv;
+{
+    int n, ret;
+    char *p, *endp;
+
+    p = *argv;
+    ret = 1;
+    while (*p) {
+	n = strtol(p, &endp, 16);
+	if (p == endp) {
+	    option_error("escape parameter contains invalid hex number '%s'",
+			 p);
+	    return 0;
+	}
+	p = endp;
+	if (n < 0 || n == 0x5E || n > 0xFF) {
+	    option_error("can't escape character 0x%x", n);
+	    ret = 0;
+	} else
+	    xmit_accm[n >> 5] |= 1 << (n & 0x1F);
+	while (*p == ',' || *p == ' ')
+	    ++p;
+    }
+    lcp_allowoptions[0].asyncmap = xmit_accm[0];
+    return ret;
+}
+
+static void
+printescape(opt, printer, arg)
+    option_t *opt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	int n;
+	int first = 1;
+
+	for (n = 0; n < 256; ++n) {
+		if (n == 0x7d)
+			n += 2;		/* skip 7d, 7e */
+		if (xmit_accm[n >> 5] & (1 << (n & 0x1f))) {
+			if (!first)
+				printer(arg, ",");
+			else
+				first = 0;
+			printer(arg, "%x", n);
+		}
+	}
+	if (first)
+		printer(arg, "oops # nothing escaped");
+}
+
+/*
+ * tty_init - do various tty-related initializations.
+ */
+void tty_init()
+{
+    add_notifier(&pidchange, maybe_relock, 0);
+    the_channel = &tty_channel;
+    xmit_accm[3] = 0x60000000;
+}
+
+/*
+ * tty_process_extra_options - work out which tty device we are using
+ * and read its options file.
+ */
+void tty_process_extra_options()
+{
+	using_pty = notty || ptycommand != NULL || pty_socket != NULL;
+	if (using_pty)
+		return;
+	if (default_device) {
+		char *p;
+		if (!isatty(0) || (p = ttyname(0)) == NULL) {
+			option_error("no device specified and stdin is not a tty");
+			exit(EXIT_OPTION_ERROR);
+		}
+		strlcpy(devnam, p, sizeof(devnam));
+		if (stat(devnam, &devstat) < 0)
+			fatal("Couldn't stat default device %s: %m", devnam);
+	}
+
+
+	/*
+	 * Parse the tty options file.
+	 * The per-tty options file should not change
+	 * ptycommand, pty_socket, notty or devnam.
+	 * options_for_tty doesn't override options set on the command line,
+	 * except for some privileged options.
+	 */
+	if (!options_for_tty())
+		exit(EXIT_OPTION_ERROR);
+}
+
+/*
+ * tty_check_options - do consistency checks on the options we were given.
+ */
+void
+tty_check_options()
+{
+	struct stat statbuf;
+	int fdflags;
+
+	if (demand && notty) {
+		option_error("demand-dialling is incompatible with notty");
+		exit(EXIT_OPTION_ERROR);
+	}
+	if (demand && connect_script == 0 && ptycommand == NULL
+	    && pty_socket == NULL) {
+		option_error("connect script is required for demand-dialling\n");
+		exit(EXIT_OPTION_ERROR);
+	}
+	/* default holdoff to 0 if no connect script has been given */
+	if (connect_script == 0 && !holdoff_specified)
+		holdoff = 0;
+
+	if (using_pty) {
+		if (!default_device) {
+			option_error("%s option precludes specifying device name",
+				     pty_socket? "socket": notty? "notty": "pty");
+			exit(EXIT_OPTION_ERROR);
+		}
+		if (ptycommand != NULL && notty) {
+			option_error("pty option is incompatible with notty option");
+			exit(EXIT_OPTION_ERROR);
+		}
+		if (pty_socket != NULL && (ptycommand != NULL || notty)) {
+			option_error("socket option is incompatible with pty and notty");
+			exit(EXIT_OPTION_ERROR);
+		}
+		default_device = notty;
+		lockflag = 0;
+		modem = 0;
+		if (notty && log_to_fd <= 1)
+			log_to_fd = -1;
+	} else {
+		/*
+		 * If the user has specified a device which is the same as
+		 * the one on stdin, pretend they didn't specify any.
+		 * If the device is already open read/write on stdin,
+		 * we assume we don't need to lock it, and we can open it
+		 * as root.
+		 */
+		if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode)
+		    && statbuf.st_rdev == devstat.st_rdev) {
+			default_device = 1;
+			fdflags = fcntl(0, F_GETFL);
+			if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR)
+				privopen = 1;
+		}
+	}
+	if (default_device)
+		nodetach = 1;
+
+	/*
+	 * Don't send log messages to the serial port, it tends to
+	 * confuse the peer. :-)
+	 */
+	if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0
+	    && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
+		log_to_fd = -1;
+}
+
+/*
+ * connect_tty - get the serial port ready to start doing PPP.
+ * That is, open the serial port, set its speed and mode, and run
+ * the connector and/or welcomer.
+ */
+int connect_tty()
+{
+	char *connector;
+	int fdflags;
+#ifndef __linux__
+	struct stat statbuf;
+#endif
+	char numbuf[16];
+	/*
+	 * Get a pty master/slave pair if the pty, notty, socket,
+	 * or record options were specified.
+	 */
+	strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+	pty_master = -1;
+	pty_slave = -1;
+	real_ttyfd = -1;
+	if (using_pty || record_file != NULL) {
+		if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
+			error("Couldn't allocate pseudo-tty");
+			status = EXIT_FATAL_ERROR;
+			return -1;
+		}
+		set_up_tty(pty_slave, 1);
+	}
+
+	/*
+	 * Lock the device if we've been asked to.
+	 */
+	status = EXIT_LOCK_FAILED;
+	if (lockflag && !privopen) {
+		if (lock(devnam) < 0)
+			goto errret;
+		locked = 1;
+	}
+	/*
+	 * Open the serial device and set it up to be the ppp interface.
+	 * First we open it in non-blocking mode so we can set the
+	 * various termios flags appropriately.  If we aren't dialling
+	 * out and we want to use the modem lines, we reopen it later
+	 * in order to wait for the carrier detect signal from the modem.
+	 */
+	got_sigterm = 0;
+	connector = doing_callback? callback_script: connect_script;
+	if (devnam[0] != 0) {
+		for (;;) {
+			/* If the user specified the device name, become the
+			   user before opening it. */
+			int err, prio;
+
+			prio = privopen? OPRIO_ROOT: tty_options[0].priority;
+			if (prio < OPRIO_ROOT && seteuid(uid) == -1) {
+				error("Unable to drop privileges before opening %s: %m\n",
+				      devnam);
+				status = EXIT_OPEN_FAILED;
+				goto errret;
+			}
+			real_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
+			err = errno;
+			if (prio < OPRIO_ROOT && seteuid(0) == -1)
+				fatal("Unable to regain privileges");
+			if (real_ttyfd >= 0)
+				break;
+			errno = err;
+			if (err != EINTR) {
+				error("Failed to open %s: %m", devnam);
+				status = EXIT_OPEN_FAILED;
+			}
+			if (!persist || err != EINTR)
+				goto errret;
+		}
+		ttyfd = real_ttyfd;
+		if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
+		    || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+			warn("Couldn't reset non-blocking mode on device: %m");
+
+#ifndef __linux__
+		/*
+		 * Linux 2.4 and above blocks normal writes to the tty
+		 * when it is in PPP line discipline, so this isn't needed.
+		 */
+		/*
+		 * Do the equivalent of `mesg n' to stop broadcast messages.
+		 */
+		if (fstat(ttyfd, &statbuf) < 0
+		    || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
+			warn("Couldn't restrict write permissions to %s: %m", devnam);
+		} else
+			tty_mode = statbuf.st_mode;
+#endif /* __linux__ */
+
+		/*
+		 * Set line speed, flow control, etc.
+		 * If we have a non-null connection or initializer script,
+		 * on most systems we set CLOCAL for now so that we can talk
+		 * to the modem before carrier comes up.  But this has the
+		 * side effect that we might miss it if CD drops before we
+		 * get to clear CLOCAL below.  On systems where we can talk
+		 * successfully to the modem with CLOCAL clear and CD down,
+		 * we could clear CLOCAL at this point.
+		 */
+		set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
+				   || initializer != NULL));
+	}
+	/*
+	 * If the pty, socket, notty and/or record option was specified,
+	 * start up the character shunt now.
+	 */
+	status = EXIT_PTYCMD_FAILED;
+	if (ptycommand != NULL) {
+#ifndef __uClinux__
+		if (record_file != NULL) {
+			int ipipe[2], opipe[2], ok;
+
+			if (pipe(ipipe) < 0 || pipe(opipe) < 0)
+				fatal("Couldn't create pipes for record option: %m");
+
+			/* don't leak these to the ptycommand */
+			(void) fcntl(ipipe[0], F_SETFD, FD_CLOEXEC);
+			(void) fcntl(opipe[1], F_SETFD, FD_CLOEXEC);
+
+			ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0
+				&& start_charshunt(ipipe[0], opipe[1]);
+			close(ipipe[0]);
+			close(ipipe[1]);
+			close(opipe[0]);
+			close(opipe[1]);
+			if (!ok)
+				goto errret;
+		} else {
+#endif /* !__uClinux__ */
+			if (device_script(ptycommand, pty_master, pty_master, 1) < 0)
+				goto errret;
+#ifndef __uClinux__
+		}
+	} else if (pty_socket != NULL) {
+		int fd = open_socket(pty_socket);
+		if (fd < 0)
+			goto errret;
+		if (!start_charshunt(fd, fd))
+			goto errret;
+		close(fd);
+	} else if (notty) {
+		if (!start_charshunt(0, 1))
+			goto errret;
+		dup2(fd_devnull, 0);
+		dup2(fd_devnull, 1);
+		if (log_to_fd == 1)
+			log_to_fd = -1;
+		if (log_to_fd != 2)
+			dup2(fd_devnull, 2);
+	} else if (record_file != NULL) {
+		int fd = dup(ttyfd);
+		if (!start_charshunt(fd, fd))
+			goto errret;
+#endif /* !__uClinux__ */
+	}
+	if (using_pty || record_file != NULL) {
+		ttyfd = pty_slave;
+		close(pty_master);
+		pty_master = -1;
+	}
+	/* run connection script */
+	if ((connector && connector[0]) || initializer) {
+		if (real_ttyfd != -1) {
+			/* XXX do this if doing_callback == CALLBACK_DIALIN? */
+			if (!default_device && modem) {
+				setdtr(real_ttyfd, 0);	/* in case modem is off hook */
+				sleep(1);
+				setdtr(real_ttyfd, 1);
+			}
+		}
+
+		if (initializer && initializer[0]) {
+			sleep(5);
+			if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
+				error("Initializer script failed");
+				status = EXIT_INIT_FAILED;
+				goto errretf;
+			}
+			if (got_sigterm) {
+				disconnect_tty();
+				goto errretf;
+			}
+			warn("Serial port initialized.");
+		}
+
+		if (connector && connector[0]) {
+			if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
+				error("Connect script failed");
+				status = EXIT_CONNECT_FAILED;
+				goto errretf;
+			}
+			if (got_sigterm) {
+				disconnect_tty();
+				goto errretf;
+			}
+			warn("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=222;
+		for (;;) {
+			if ((i = open(devnam, O_RDWR | O_NOCTTY)) >= 0)
+				{
+				
+				break;
+				}
+			if (errno != EINTR) {
+				error("Failed to reopen %s: %m", devnam);
+				status = EXIT_OPEN_FAILED;
+			}
+			if (!persist || errno != EINTR || hungup || got_sigterm)
+				{
+				goto errret;
+				}
+		}
+		close(i);
+	}
+	slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
+	script_setenv("SPEED", numbuf, 0);
+
+	/* run welcome script, if any */
+	if (welcomer && welcomer[0]) {
+		if (device_script(welcomer, ttyfd, ttyfd, 0) < 0)
+			warn("Welcome script failed");
+	}
+
+	/*
+	 * If we are initiating this connection, wait for a short
+	 * time for something from the peer.  This can avoid bouncing
+	 * our packets off his tty before he has it set up.
+	 */
+	if (connector != NULL || ptycommand != NULL || pty_socket != NULL)
+		listen_time = connect_delay;
+	
+	return ttyfd;
+
+ errretf:
+	if (real_ttyfd >= 0)
+		tcflush(real_ttyfd, TCIOFLUSH);
+ errret:
+	if (pty_master >= 0) {
+		close(pty_master);
+		pty_master = -1;
+	}
+	ttyfd = -1;
+	if (got_sigterm)
+		asked_to_quit = 1;
+	return -1;
+}
+
+
+void disconnect_tty()
+{
+	if (disconnect_script == NULL || hungup)
+		return;
+	if (real_ttyfd >= 0)
+		set_up_tty(real_ttyfd, 1);
+	if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) {
+		warn("disconnect script failed");
+	} else {
+		warn("Serial link disconnected.");
+	}
+#ifndef __uClinux__
+	stop_charshunt(NULL, 0);
+#endif
+}
+
+void tty_close_fds()
+{
+	if (pty_slave >= 0)
+		close(pty_slave);
+	if (real_ttyfd >= 0) {
+		close(real_ttyfd);
+		real_ttyfd = -1;
+	}
+	/* N.B. ttyfd will == either pty_slave or real_ttyfd */
+}
+
+void cleanup_tty()
+{
+	if (real_ttyfd >= 0)
+		finish_tty();
+	tty_close_fds();
+	if (locked) {
+		unlock();
+		locked = 0;
+	}
+}
+
+/*
+ * tty_do_send_config - set transmit-side PPP configuration.
+ * We set the extended transmit ACCM here as well.
+ */
+void
+tty_do_send_config(mtu, accm, pcomp, accomp)
+    int mtu;
+    u_int32_t accm;
+    int pcomp, accomp;
+{
+	tty_set_xaccm(xmit_accm);
+	tty_send_config(mtu, accm, pcomp, accomp);
+}
+
+/*
+ * finish_tty - restore the terminal device to its original settings
+ */
+static void
+finish_tty()
+{
+	/* drop dtr to hang up */
+	if (!default_device && modem) {
+		setdtr(real_ttyfd, 0);
+		/*
+		 * This sleep is in case the serial port has CLOCAL set by default,
+		 * and consequently will reassert DTR when we close the device.
+		 */
+		sleep(1);
+	}
+
+	restore_tty(real_ttyfd);
+
+#ifndef __linux__
+	if (tty_mode != (mode_t) -1) {
+		if (fchmod(real_ttyfd, tty_mode) != 0)
+			error("Couldn't restore tty permissions");
+	}
+#endif /* __linux__ */
+
+	close(real_ttyfd);
+	real_ttyfd = -1;
+}
+
+/*
+ * maybe_relock - our PID has changed, maybe update the lock file.
+ */
+static void
+maybe_relock(arg, pid)
+    void *arg;
+    int pid;
+{
+    if (locked)
+	relock(pid);
+}
+
+/*
+ * open_socket - establish a stream socket connection to the nominated
+ * host and port.
+ */
+static int
+open_socket(dest)
+    char *dest;
+{
+    char *sep, *endp = NULL;
+    int sock, port = -1;
+    u_int32_t host;
+    struct hostent *hent;
+    struct sockaddr_in sad;
+
+    /* parse host:port and resolve host to an IP address */
+    sep = strchr(dest, ':');
+    if (sep != NULL)
+	port = strtol(sep+1, &endp, 10);
+    if (port < 0 || endp == sep+1 || sep == dest) {
+	error("Can't parse host:port for socket destination");
+	return -1;
+    }
+    *sep = 0;
+    host = inet_addr(dest);
+    if (host == (u_int32_t) -1) {
+	hent = gethostbyname(dest);
+	if (hent == NULL) {
+	    error("%s: unknown host in socket option", dest);
+	    *sep = ':';
+	    return -1;
+	}
+	host = *(u_int32_t *)(hent->h_addr_list[0]);
+    }
+    *sep = ':';
+
+    /* get a socket and connect it to the other end */
+    sock = socket(PF_INET, SOCK_STREAM, 0);
+    if (sock < 0) {
+	error("Can't create socket: %m");
+	return -1;
+    }
+    memset(&sad, 0, sizeof(sad));
+    sad.sin_family = AF_INET;
+    sad.sin_port = htons(port);
+    sad.sin_addr.s_addr = host;
+    if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
+	error("Can't connect to %s: %m", dest);
+	close(sock);
+	return -1;
+    }
+
+    return sock;
+}
+
+#ifndef __uClinux__
+
+/*
+ * start_charshunt - create a child process to run the character shunt.
+ */
+static int
+start_charshunt(ifd, ofd)
+    int ifd, ofd;
+{
+    int cpid;
+
+    cpid = safe_fork(ifd, ofd, (log_to_fd >= 0? log_to_fd: 2));
+    if (cpid == -1) {
+	error("Can't fork process for character shunt: %m");
+	return 0;
+    }
+    if (cpid == 0) {
+	/* child */
+	reopen_log();
+	if (!nodetach)
+	    log_to_fd = -1;
+	else if (log_to_fd >= 0)
+	    log_to_fd = 2;
+	setgid(getgid());
+	setuid(uid);
+	if (getuid() != uid)
+	    fatal("setuid failed");
+	charshunt(0, 1, record_file);
+	exit(0);
+    }
+    charshunt_pid = cpid;
+    record_child(cpid, "pppd (charshunt)", charshunt_done, NULL, 1);
+    return 1;
+}
+
+static void
+charshunt_done(arg)
+    void *arg;
+{
+	charshunt_pid = 0;
+}
+
+static void
+stop_charshunt(arg, sig)
+    void *arg;
+    int sig;
+{
+	if (charshunt_pid)
+		kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM));
+}
+
+/*
+ * charshunt - the character shunt, which passes characters between
+ * the pty master side and the serial port (or stdin/stdout).
+ * This runs as the user (not as root).
+ * (We assume ofd >= ifd which is true the way this gets called. :-).
+ */
+static void
+charshunt(ifd, ofd, record_file)
+    int ifd, ofd;
+    char *record_file;
+{
+    int n, nfds;
+    fd_set ready, writey;
+    u_char *ibufp, *obufp;
+    int nibuf, nobuf;
+    int flags;
+    int pty_readable, stdin_readable;
+    struct timeval lasttime;
+    FILE *recordf = NULL;
+    int ilevel, olevel, max_level;
+    struct timeval levelt, tout, *top;
+    extern u_char inpacket_buf[];
+
+    /*
+     * Reset signal handlers.
+     */
+    signal(SIGHUP, SIG_IGN);		/* Hangup */
+    signal(SIGINT, SIG_DFL);		/* Interrupt */
+    signal(SIGTERM, SIG_DFL);		/* Terminate */
+    signal(SIGCHLD, SIG_DFL);
+    signal(SIGUSR1, SIG_DFL);
+    signal(SIGUSR2, SIG_DFL);
+    signal(SIGABRT, SIG_DFL);
+    signal(SIGALRM, SIG_DFL);
+    signal(SIGFPE, SIG_DFL);
+    signal(SIGILL, SIG_DFL);
+    signal(SIGPIPE, SIG_DFL);
+    signal(SIGQUIT, SIG_DFL);
+    signal(SIGSEGV, SIG_DFL);
+#ifdef SIGBUS
+    signal(SIGBUS, SIG_DFL);
+#endif
+#ifdef SIGEMT
+    signal(SIGEMT, SIG_DFL);
+#endif
+#ifdef SIGPOLL
+    signal(SIGPOLL, SIG_DFL);
+#endif
+#ifdef SIGPROF
+    signal(SIGPROF, SIG_DFL);
+#endif
+#ifdef SIGSYS
+    signal(SIGSYS, SIG_DFL);
+#endif
+#ifdef SIGTRAP
+    signal(SIGTRAP, SIG_DFL);
+#endif
+#ifdef SIGVTALRM
+    signal(SIGVTALRM, SIG_DFL);
+#endif
+#ifdef SIGXCPU
+    signal(SIGXCPU, SIG_DFL);
+#endif
+#ifdef SIGXFSZ
+    signal(SIGXFSZ, SIG_DFL);
+#endif
+
+    /*
+     * Check that the fds won't overrun the fd_sets
+     */
+    if (ifd >= FD_SETSIZE || ofd >= FD_SETSIZE || pty_master >= FD_SETSIZE)
+	fatal("internal error: file descriptor too large (%d, %d, %d)",
+	      ifd, ofd, pty_master);
+
+    /*
+     * Open the record file if required.
+     */
+    if (record_file != NULL) {
+	recordf = fopen(record_file, "a");
+	if (recordf == NULL)
+	    error("Couldn't create record file %s: %m", record_file);
+    }
+
+    /* set all the fds to non-blocking mode */
+    flags = fcntl(pty_master, F_GETFL);
+    if (flags == -1
+	|| fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
+	warn("couldn't set pty master to nonblock: %m");
+    flags = fcntl(ifd, F_GETFL);
+    if (flags == -1
+	|| fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
+	warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
+    if (ofd != ifd) {
+	flags = fcntl(ofd, F_GETFL);
+	if (flags == -1
+	    || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
+	    warn("couldn't set stdout to nonblock: %m");
+    }
+
+    nibuf = nobuf = 0;
+    ibufp = obufp = NULL;
+    pty_readable = stdin_readable = 1;
+
+    ilevel = olevel = 0;
+    gettimeofday(&levelt, NULL);
+    if (max_data_rate) {
+	max_level = max_data_rate / 10;
+	if (max_level < 100)
+	    max_level = 100;
+    } else
+	max_level = PPP_MRU + PPP_HDRLEN + 1;
+
+    nfds = (ofd > pty_master? ofd: pty_master) + 1;
+    if (recordf != NULL) {
+	gettimeofday(&lasttime, NULL);
+	putc(7, recordf);	/* put start marker */
+	putc(lasttime.tv_sec >> 24, recordf);
+	putc(lasttime.tv_sec >> 16, recordf);
+	putc(lasttime.tv_sec >> 8, recordf);
+	putc(lasttime.tv_sec, recordf);
+	lasttime.tv_usec = 0;
+    }
+
+    while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
+	top = 0;
+	tout.tv_sec = 0;
+	tout.tv_usec = 10000;
+	FD_ZERO(&ready);
+	FD_ZERO(&writey);
+	if (nibuf != 0) {
+	    if (ilevel >= max_level)
+		top = &tout;
+	    else
+		FD_SET(pty_master, &writey);
+	} else if (stdin_readable)
+	    FD_SET(ifd, &ready);
+	if (nobuf != 0) {
+	    if (olevel >= max_level)
+		top = &tout;
+	    else
+		FD_SET(ofd, &writey);
+	} else if (pty_readable)
+	    FD_SET(pty_master, &ready);
+	if (select(nfds, &ready, &writey, NULL, top) < 0) {
+	    if (errno != EINTR)
+		fatal("select");
+	    continue;
+	}
+	if (max_data_rate) {
+	    double dt;
+	    int nbt;
+	    struct timeval now;
+
+	    gettimeofday(&now, NULL);
+	    dt = (now.tv_sec - levelt.tv_sec
+		  + (now.tv_usec - levelt.tv_usec) / 1e6);
+	    nbt = (int)(dt * max_data_rate);
+	    ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
+	    olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
+	    levelt = now;
+	} else
+	    ilevel = olevel = 0;
+	if (FD_ISSET(ifd, &ready)) {
+	    ibufp = inpacket_buf;
+	    nibuf = read(ifd, ibufp, PPP_MRU + PPP_HDRLEN);
+	    if (nibuf < 0 && errno == EIO)
+		nibuf = 0;
+	    if (nibuf < 0) {
+		if (!(errno == EINTR || errno == EAGAIN)) {
+		    error("Error reading standard input: %m");
+		    break;
+		}
+		nibuf = 0;
+	    } else if (nibuf == 0) {
+		/* end of file from stdin */
+		stdin_readable = 0;
+		if (recordf)
+		    if (!record_write(recordf, 4, NULL, 0, &lasttime))
+			recordf = NULL;
+	    } else {
+		FD_SET(pty_master, &writey);
+		if (recordf)
+		    if (!record_write(recordf, 2, ibufp, nibuf, &lasttime))
+			recordf = NULL;
+	    }
+	}
+	if (FD_ISSET(pty_master, &ready)) {
+	    obufp = outpacket_buf;
+	    nobuf = read(pty_master, obufp, PPP_MRU + PPP_HDRLEN);
+	    if (nobuf < 0 && errno == EIO)
+		nobuf = 0;
+	    if (nobuf < 0) {
+		if (!(errno == EINTR || errno == EAGAIN)) {
+		    error("Error reading pseudo-tty master: %m");
+		    break;
+		}
+		nobuf = 0;
+	    } else if (nobuf == 0) {
+		/* end of file from the pty - slave side has closed */
+		pty_readable = 0;
+		stdin_readable = 0;	/* pty is not writable now */
+		nibuf = 0;
+		close(ofd);
+		if (recordf)
+		    if (!record_write(recordf, 3, NULL, 0, &lasttime))
+			recordf = NULL;
+	    } else {
+		FD_SET(ofd, &writey);
+		if (recordf)
+		    if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
+			recordf = NULL;
+	    }
+	} else if (!stdin_readable)
+	    pty_readable = 0;
+	if (FD_ISSET(ofd, &writey)) {
+	    n = nobuf;
+	    if (olevel + n > max_level)
+		n = max_level - olevel;
+	    n = write(ofd, obufp, n);
+	    if (n < 0) {
+		if (errno == EIO) {
+		    pty_readable = 0;
+		    nobuf = 0;
+		} else if (errno != EAGAIN && errno != EINTR) {
+		    error("Error writing standard output: %m");
+		    break;
+		}
+	    } else {
+		obufp += n;
+		nobuf -= n;
+		olevel += n;
+	    }
+	}
+	if (FD_ISSET(pty_master, &writey)) {
+	    n = nibuf;
+	    if (ilevel + n > max_level)
+		n = max_level - ilevel;
+	    n = write(pty_master, ibufp, n);
+	    if (n < 0) {
+		if (errno == EIO) {
+		    stdin_readable = 0;
+		    nibuf = 0;
+		} else if (errno != EAGAIN && errno != EINTR) {
+		    error("Error writing pseudo-tty master: %m");
+		    break;
+		}
+	    } else {
+		ibufp += n;
+		nibuf -= n;
+		ilevel += n;
+	    }
+	}
+    }
+    exit(0);
+}
+
+static int
+record_write(f, code, buf, nb, tp)
+    FILE *f;
+    int code;
+    u_char *buf;
+    int nb;
+    struct timeval *tp;
+{
+    struct timeval now;
+    int diff;
+
+    gettimeofday(&now, NULL);
+    now.tv_usec /= 100000;	/* actually 1/10 s, not usec now */
+    diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec);
+    if (diff > 0) {
+	if (diff > 255) {
+	    putc(5, f);
+	    putc(diff >> 24, f);
+	    putc(diff >> 16, f);
+	    putc(diff >> 8, f);
+	    putc(diff, f);
+	} else {
+	    putc(6, f);
+	    putc(diff, f);
+	}
+	*tp = now;
+    }
+    putc(code, f);
+    if (buf != NULL) {
+	putc(nb >> 8, f);
+	putc(nb, f);
+	fwrite(buf, nb, 1, f);
+    }
+    fflush(f);
+    if (ferror(f)) {
+	error("Error writing record file: %m");
+	return 0;
+    }
+    return 1;
+}
+
+#endif /* !__uClinux__ */
diff --git a/ap/app/pppd/pppd/upap.c b/ap/app/pppd/pppd/upap.c
new file mode 100644
index 0000000..b95ca95
--- /dev/null
+++ b/ap/app/pppd/pppd/upap.c
@@ -0,0 +1,702 @@
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: upap.c,v 1.4 2008-06-04 03:50:22 kwilson Exp $"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pppd.h"
+#include "upap.h"
+//#include "netapi.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 */
+/*wangming added*/
+pppd_auth authinfo[NUM_PPPD_AUTH];		/* pppd_auth; one for each unit */
+
+static void upap_timeout __P((void *));
+static void upap_reqtimeout __P((void *));
+static void upap_rauthreq __P((upap_state *, u_char *, int, int));
+static void upap_rauthack __P((upap_state *, u_char *, int, int));
+static void upap_rauthnak __P((upap_state *, u_char *, int, int));
+static void upap_sauthreq __P((upap_state *));
+static void upap_sresp __P((upap_state *, int, int, char *, int));
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void
+upap_init(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    u->us_unit = unit;
+    u->us_user = NULL;
+    u->us_userlen = 0;
+    u->us_passwd = NULL;
+    u->us_passwdlen = 0;
+    u->us_clientstate = UPAPCS_INITIAL;
+    u->us_serverstate = UPAPSS_INITIAL;
+    u->us_id = 0;
+    u->us_timeouttime = UPAP_DEFTIMEOUT;
+    u->us_maxtransmits = 10;
+    u->us_reqtimeout = UPAP_DEFREQTIME;
+}
+
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(unit, user, password)
+    int unit;
+    char *user, *password;
+{
+    upap_state *u = &upap[unit];
+
+    /* Save the username and password we're given */
+    u->us_user = user;
+    u->us_userlen = strlen(user);
+    u->us_passwd = password;
+    u->us_passwdlen = strlen(password);
+    u->us_transmits = 0;
+
+    /* Lower layer up yet? */
+    if (u->us_clientstate == UPAPCS_INITIAL ||
+	u->us_clientstate == UPAPCS_PENDING) {
+	u->us_clientstate = UPAPCS_PENDING;
+	return;
+    }
+
+    upap_sauthreq(u);			/* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    /* Lower layer up yet? */
+    if (u->us_serverstate == UPAPSS_INITIAL ||
+	u->us_serverstate == UPAPSS_PENDING) {
+	u->us_serverstate = UPAPSS_PENDING;
+	return;
+    }
+
+    u->us_serverstate = UPAPSS_LISTEN;
+    if (u->us_reqtimeout > 0)
+	TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+}
+
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void
+upap_timeout(arg)
+    void *arg;
+{
+    upap_state *u = (upap_state *) arg;
+
+    if (u->us_clientstate != UPAPCS_AUTHREQ)
+	return;
+
+    if (u->us_transmits >= u->us_maxtransmits) {
+	/* give up in disgust */
+	error("No response to PAP authenticate-requests");
+	u->us_clientstate = UPAPCS_BADAUTH;
+	auth_withpeer_fail(u->us_unit, PPP_PAP);
+	return;
+    }
+
+    upap_sauthreq(u);		/* Send Authenticate-Request */
+}
+
+
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void
+upap_reqtimeout(arg)
+    void *arg;
+{
+    upap_state *u = (upap_state *) arg;
+
+    if (u->us_serverstate != UPAPSS_LISTEN)
+	return;			/* huh?? */
+
+    auth_peer_fail(u->us_unit, PPP_PAP);
+    u->us_serverstate = UPAPSS_BADAUTH;
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void
+upap_lowerup(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    if (u->us_clientstate == UPAPCS_INITIAL)
+	u->us_clientstate = UPAPCS_CLOSED;
+    else if (u->us_clientstate == UPAPCS_PENDING) {
+	upap_sauthreq(u);	/* send an auth-request */
+    }
+
+    if (u->us_serverstate == UPAPSS_INITIAL)
+	u->us_serverstate = UPAPSS_CLOSED;
+    else if (u->us_serverstate == UPAPSS_PENDING) {
+	u->us_serverstate = UPAPSS_LISTEN;
+	if (u->us_reqtimeout > 0)
+	    TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+    }
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+upap_lowerdown(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    if (u->us_clientstate == UPAPCS_AUTHREQ)	/* Timeout pending? */
+	UNTIMEOUT(upap_timeout, u);		/* Cancel timeout */
+    if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0)
+	UNTIMEOUT(upap_reqtimeout, u);
+
+    u->us_clientstate = UPAPCS_INITIAL;
+    u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen.  In any case, pretend lower layer went down.
+ */
+static void
+upap_protrej(unit)
+    int unit;
+{
+    upap_state *u = &upap[unit];
+
+    if (u->us_clientstate == UPAPCS_AUTHREQ) {
+	error("PAP authentication failed due to protocol-reject");
+	auth_withpeer_fail(unit, PPP_PAP);
+    }
+    if (u->us_serverstate == UPAPSS_LISTEN) {
+	error("PAP authentication of peer failed (protocol-reject)");
+	auth_peer_fail(unit, PPP_PAP);
+    }
+    upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void
+upap_input(unit, inpacket, l)
+    int unit;
+    u_char *inpacket;
+    int l;
+{
+    upap_state *u = &upap[unit];
+    u_char *inp;
+    u_char code, id;
+    int len;
+
+    /*
+     * Parse header (code, id and length).
+     * If packet too short, drop it.
+     */
+    inp = inpacket;
+    if (l < UPAP_HEADERLEN) {
+	UPAPDEBUG(("pap_input: rcvd short header."));
+	return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    GETSHORT(len, inp);
+    if (len < UPAP_HEADERLEN) {
+	UPAPDEBUG(("pap_input: rcvd illegal length."));
+	return;
+    }
+    if (len > l) {
+	UPAPDEBUG(("pap_input: rcvd short packet."));
+	return;
+    }
+    len -= UPAP_HEADERLEN;
+
+    /*
+     * Action depends on code.
+     */
+    switch (code) {
+    case UPAP_AUTHREQ:
+	upap_rauthreq(u, inp, id, len);
+	break;
+
+    case UPAP_AUTHACK:
+	upap_rauthack(u, inp, id, len);
+	break;
+
+    case UPAP_AUTHNAK:
+	upap_rauthnak(u, inp, id, len);
+	break;
+
+    default:				/* XXX Need code reject */
+	break;
+    }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(u, inp, id, len)
+    upap_state *u;
+    u_char *inp;
+    int id;
+    int len;
+{
+    u_char ruserlen, rpasswdlen;
+    char *ruser, *rpasswd;
+    char rhostname[256];
+    int retcode;
+    char *msg;
+    int msglen;
+
+    if (u->us_serverstate < UPAPSS_LISTEN)
+	return;
+
+    /*
+     * If we receive a duplicate authenticate-request, we are
+     * supposed to return the same status as for the first request.
+     */
+    if (u->us_serverstate == UPAPSS_OPEN) {
+	upap_sresp(u, UPAP_AUTHACK, id, "", 0);	/* return auth-ack */
+	return;
+    }
+    if (u->us_serverstate == UPAPSS_BADAUTH) {
+	upap_sresp(u, UPAP_AUTHNAK, id, "", 0);	/* return auth-nak */
+	return;
+    }
+
+    /*
+     * Parse user/passwd.
+     */
+    if (len < 1) {
+	UPAPDEBUG(("pap_rauth: rcvd short packet."));
+	return;
+    }
+    GETCHAR(ruserlen, inp);
+    len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+    if (len < 0) {
+	UPAPDEBUG(("pap_rauth: rcvd short packet."));
+	return;
+    }
+    ruser = (char *) inp;
+    INCPTR(ruserlen, inp);
+    GETCHAR(rpasswdlen, inp);
+    if (len < rpasswdlen) {
+	UPAPDEBUG(("pap_rauth: rcvd short packet."));
+	return;
+    }
+    rpasswd = (char *) inp;
+
+    /*
+     * Check the username and password given.
+     */
+	authinfo[0].auth_type 	= PPP_PAP_AUTH;
+	memcpy(authinfo[0].user, ruser, ruserlen);
+	authinfo[0].user_len	= ruserlen;
+	memcpy(authinfo[0].pw, rpasswd, rpasswdlen);
+	authinfo[0].pw_len		= rpasswdlen;
+	warn("pppd: %s---->%d, user is %s, pass is %s, %d, %d", __FILE__, __LINE__, authinfo[0].user, authinfo[0].pw, rpasswdlen, ruserlen);	
+    retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
+			   rpasswdlen, &msg);
+    BZERO(rpasswd, rpasswdlen);
+
+    /*
+     * Check remote number authorization.  A plugin may have filled in
+     * the remote number or added an allowed number, and rather than
+     * return an authenticate failure, is leaving it for us to verify.
+     */
+    if (retcode == UPAP_AUTHACK) {
+	if (!auth_number()) {
+	    /* We do not want to leak info about the pap result. */
+	    retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */
+	    warn("calling number %q is not authorized", remote_number);
+	}
+    }
+
+    msglen = strlen(msg);
+    if (msglen > 255)
+	msglen = 255;
+    upap_sresp(u, retcode, id, msg, msglen);
+
+    /* Null terminate and clean remote name. */
+    slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser);
+#ifdef USE_PAM
+    if (retcode == UPAP_AUTHACK) {
+		/* Check the pam account settings */
+		if (check_pam_account_restrictions(ruser)) {
+			warn("User %q failed PAM account check", ruser);
+			retcode - UPAP_AUTHNAK;
+		}
+    }
+#endif 
+    if (retcode == UPAP_AUTHACK) {
+	u->us_serverstate = UPAPSS_OPEN;
+	warn("PAP peer authentication succeeded for %q", rhostname);
+	auth_peer_success(u->us_unit, PPP_PAP, 0, ruser, ruserlen);
+    } else {
+	u->us_serverstate = UPAPSS_BADAUTH;
+	warn("PAP peer authentication failed for %q", rhostname);
+	auth_peer_fail(u->us_unit, PPP_PAP);
+#ifdef USE_EXTERNAL_STATS_PROG
+	notify_login_failure(ruser);
+#endif
+    }
+
+    if (u->us_reqtimeout > 0)
+	UNTIMEOUT(upap_reqtimeout, u);
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void
+upap_rauthack(u, inp, id, len)
+    upap_state *u;
+    u_char *inp;
+    int id;
+    int len;
+{
+    u_char msglen;
+    char *msg;
+
+    if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+	return;
+
+    /*
+     * Parse message.
+     */
+    if (len < 1) {
+	UPAPDEBUG(("pap_rauthack: ignoring missing msg-length."));
+    } else {
+	GETCHAR(msglen, inp);
+	if (msglen > 0) {
+	    len -= sizeof (u_char);
+	    if (len < msglen) {
+		UPAPDEBUG(("pap_rauthack: rcvd short packet."));
+		return;
+	    }
+	    msg = (char *) inp;
+	    PRINTMSG(msg, msglen);
+	}
+    }
+
+    u->us_clientstate = UPAPCS_OPEN;
+
+    auth_withpeer_success(u->us_unit, PPP_PAP, 0);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nak.
+ */
+static void
+upap_rauthnak(u, inp, id, len)
+    upap_state *u;
+    u_char *inp;
+    int id;
+    int len;
+{
+    u_char msglen;
+    char *msg;
+
+    if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+	return;
+
+    /*
+     * Parse message.
+     */
+    if (len < 1) {
+	UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length."));
+    } else {
+	GETCHAR(msglen, inp);
+	if (msglen > 0) {
+	    len -= sizeof (u_char);
+	    if (len < msglen) {
+		UPAPDEBUG(("pap_rauthnak: rcvd short packet."));
+		return;
+	    }
+	    msg = (char *) inp;
+	    PRINTMSG(msg, msglen);
+	}
+    }
+
+    u->us_clientstate = UPAPCS_BADAUTH;
+
+    error("PAP authentication failed");
+    auth_withpeer_fail(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(u)
+    upap_state *u;
+{
+    u_char *outp;
+    int outlen;
+
+    outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
+	u->us_userlen + u->us_passwdlen;
+    outp = outpacket_buf;
+    
+    MAKEHEADER(outp, PPP_PAP);
+
+    PUTCHAR(UPAP_AUTHREQ, outp);
+    PUTCHAR(++u->us_id, outp);
+    PUTSHORT(outlen, outp);
+    PUTCHAR(u->us_userlen, outp);
+    BCOPY(u->us_user, outp, u->us_userlen);
+    INCPTR(u->us_userlen, outp);
+    PUTCHAR(u->us_passwdlen, outp);
+    BCOPY(u->us_passwd, outp, u->us_passwdlen);
+
+    output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+    TIMEOUT(upap_timeout, u, u->us_timeouttime);
+    ++u->us_transmits;
+    u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(u, code, id, msg, msglen)
+    upap_state *u;
+    u_char code, id;
+    char *msg;
+    int msglen;
+{
+    u_char *outp;
+    int outlen;
+
+    outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+    outp = outpacket_buf;
+    MAKEHEADER(outp, PPP_PAP);
+
+    PUTCHAR(code, outp);
+    PUTCHAR(id, outp);
+    PUTSHORT(outlen, outp);
+    PUTCHAR(msglen, outp);
+    BCOPY(msg, outp, msglen);
+    output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static char *upap_codenames[] = {
+    "AuthReq", "AuthAck", "AuthNak"
+};
+
+static int
+upap_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    int code, id, len;
+    int mlen, ulen, wlen;
+    char *user, *pwd, *msg;
+    u_char *pstart;
+
+    if (plen < UPAP_HEADERLEN)
+	return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < UPAP_HEADERLEN || len > plen)
+	return 0;
+
+    if (code >= 1 && code <= sizeof(upap_codenames) / sizeof(char *))
+	printer(arg, " %s", upap_codenames[code-1]);
+    else
+	printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= UPAP_HEADERLEN;
+    switch (code) {
+    case UPAP_AUTHREQ:
+	if (len < 1)
+	    break;
+	ulen = p[0];
+	if (len < ulen + 2)
+	    break;
+	wlen = p[ulen + 1];
+	if (len < ulen + wlen + 2)
+	    break;
+	user = (char *) (p + 1);
+	pwd = (char *) (p + ulen + 2);
+	p += ulen + wlen + 2;
+	len -= ulen + wlen + 2;
+	printer(arg, " user=");
+	print_string(user, ulen, printer, arg);
+	printer(arg, " password=");
+	if (!hide_password)
+	    print_string(pwd, wlen, printer, arg);
+	else
+	    printer(arg, "<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/ap/app/pppd/pppd/upap.h b/ap/app/pppd/pppd/upap.h
new file mode 100644
index 0000000..e769177
--- /dev/null
+++ b/ap/app/pppd/pppd/upap.h
@@ -0,0 +1,110 @@
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: upap.h,v 1.2 2007-06-08 04:02:38 gerg Exp $
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN	4
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ	1	/* Authenticate-Request */
+#define UPAP_AUTHACK	2	/* Authenticate-Ack */
+#define UPAP_AUTHNAK	3	/* Authenticate-Nak */
+
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+    int us_unit;		/* Interface unit number */
+    char *us_user;		/* User */
+    int us_userlen;		/* User length */
+    char *us_passwd;		/* Password */
+    int us_passwdlen;		/* Password length */
+    int us_clientstate;		/* Client state */
+    int us_serverstate;		/* Server state */
+    u_char us_id;		/* Current id */
+    int us_timeouttime;		/* Timeout (seconds) for auth-req retrans. */
+    int us_transmits;		/* Number of auth-reqs sent */
+    int us_maxtransmits;	/* Maximum number of auth-reqs to send */
+    int us_reqtimeout;		/* Time to wait for auth-req from peer */
+} upap_state;
+
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL	0	/* Connection down */
+#define UPAPCS_CLOSED	1	/* Connection up, haven't requested auth */
+#define UPAPCS_PENDING	2	/* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ	3	/* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN	4	/* We've received an Ack */
+#define UPAPCS_BADAUTH	5	/* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL	0	/* Connection down */
+#define UPAPSS_CLOSED	1	/* Connection up, haven't requested auth */
+#define UPAPSS_PENDING	2	/* Connection down, have requested auth */
+#define UPAPSS_LISTEN	3	/* Listening for an Authenticate */
+#define UPAPSS_OPEN	4	/* We've sent an Ack */
+#define UPAPSS_BADAUTH	5	/* We've sent a Nak */
+
+
+/*
+ * Timeouts.
+ */
+#define UPAP_DEFTIMEOUT	3	/* Timeout (seconds) for retransmitting req */
+#define UPAP_DEFREQTIME	30	/* Time to wait for auth-req from peer */
+
+extern upap_state upap[];
+
+void upap_authwithpeer __P((int, char *, char *));
+void upap_authpeer __P((int));
+
+extern struct protent pap_protent;
diff --git a/ap/app/pppd/pppd/utils.c b/ap/app/pppd/pppd/utils.c
new file mode 100644
index 0000000..b673248
--- /dev/null
+++ b/ap/app/pppd/pppd/utils.c
@@ -0,0 +1,1058 @@
+/*
+ * utils.c - various utility functions used in pppd.
+ *
+ * Copyright (c) 1999-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RCSID	"$Id: utils.c,v 1.25 2008/06/03 12:06:37 paulus Exp $"
+
+#include <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 <time.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"
+#include "fsm.h"
+#include "lcp.h"
+
+static const char rcsid[] = RCSID;
+
+#if defined(SUNOS4)
+extern char *strerror();
+#endif
+
+static void logit __P((int, char *, va_list));
+static void log_write __P((int, char *));
+static void vslp_printer __P((void *, char *, ...));
+static void format_packet __P((u_char *, int, void (*) (void *, char *, ...),
+			       void *));
+
+struct buffer_info {
+    char *ptr;
+    int len;
+};
+
+/*
+ * strlcpy - like strcpy/strncpy, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t
+strlcpy(dest, src, len)
+    char *dest;
+    const char *src;
+    size_t len;
+{
+    size_t ret = strlen(src);
+
+    if (len != 0) {
+	if (ret < len)
+	    strcpy(dest, src);
+	else {
+	    strncpy(dest, src, len - 1);
+	    dest[len-1] = 0;
+	}
+    }
+    return ret;
+}
+
+/*
+ * strlcat - like strcat/strncat, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t
+strlcat(dest, src, len)
+    char *dest;
+    const char *src;
+    size_t len;
+{
+    size_t dlen = strlen(dest);
+
+    return dlen + strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0));
+}
+
+
+/*
+ * slprintf - format a message into a buffer.  Like sprintf except we
+ * also specify the length of the output buffer, and we handle
+ * %m (error message), %v (visible string),
+ * %q (quoted string), %t (current time) and %I (IP address) formats.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+int
+slprintf __V((char *buf, int buflen, char *fmt, ...))
+{
+    va_list args;
+    int n;
+
+#if defined(__STDC__)
+    va_start(args, fmt);
+#else
+    char *buf;
+    int buflen;
+    char *fmt;
+    va_start(args);
+    buf = va_arg(args, char *);
+    buflen = va_arg(args, int);
+    fmt = va_arg(args, char *);
+#endif
+    n = vslprintf(buf, buflen, fmt, args);
+    va_end(args);
+    return n;
+}
+
+/*
+ * vslprintf - like slprintf, takes a va_list instead of a list of args.
+ */
+#define OUTCHAR(c)	(buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int
+vslprintf(buf, buflen, fmt, args)
+    char *buf;
+    int buflen;
+    char *fmt;
+    va_list args;
+{
+    int c, i, n;
+    int width, prec, fillch;
+    int base, len, neg, quoted;
+    unsigned long val = 0;
+    char *str, *f, *buf0;
+    unsigned char *p;
+    char num[32];
+    time_t t;
+    u_int32_t ip;
+    static char hexchars[] = "0123456789abcdef";
+    struct buffer_info bufinfo;
+
+    buf0 = buf;
+    --buflen;
+    while (buflen > 0) {
+	for (f = fmt; *f != '%' && *f != 0; ++f)
+	    ;
+	if (f > fmt) {
+	    len = f - fmt;
+	    if (len > buflen)
+		len = buflen;
+	    memcpy(buf, fmt, len);
+	    buf += len;
+	    buflen -= len;
+	    fmt = f;
+	}
+	if (*fmt == 0)
+	    break;
+	c = *++fmt;
+	width = 0;
+	prec = -1;
+	fillch = ' ';
+	if (c == '0') {
+	    fillch = '0';
+	    c = *++fmt;
+	}
+	if (c == '*') {
+	    width = va_arg(args, int);
+	    c = *++fmt;
+	} else {
+	    while (isdigit(c)) {
+		width = width * 10 + c - '0';
+		c = *++fmt;
+	    }
+	}
+	if (c == '.') {
+	    c = *++fmt;
+	    if (c == '*') {
+		prec = va_arg(args, int);
+		c = *++fmt;
+	    } else {
+		prec = 0;
+		while (isdigit(c)) {
+		    prec = prec * 10 + c - '0';
+		    c = *++fmt;
+		}
+	    }
+	}
+	str = 0;
+	base = 0;
+	neg = 0;
+	++fmt;
+	switch (c) {
+	case 'l':
+	    c = *fmt++;
+	    switch (c) {
+	    case 'd':
+		val = va_arg(args, long);
+		if (val < 0) {
+		    neg = 1;
+		    val = -val;
+		}
+		base = 10;
+		break;
+	    case 'u':
+		val = va_arg(args, unsigned long);
+		base = 10;
+		break;
+	    default:
+		OUTCHAR('%');
+		OUTCHAR('l');
+		--fmt;		/* so %lz outputs %lz etc. */
+		continue;
+	    }
+	    break;
+	case 'd':
+	    i = va_arg(args, int);
+	    if (i < 0) {
+		neg = 1;
+		val = -i;
+	    } else
+		val = i;
+	    base = 10;
+	    break;
+	case 'u':
+	    val = va_arg(args, unsigned int);
+	    base = 10;
+	    break;
+	case 'o':
+	    val = va_arg(args, unsigned int);
+	    base = 8;
+	    break;
+	case 'x':
+	case 'X':
+	    val = va_arg(args, unsigned int);
+	    base = 16;
+	    break;
+	case 'p':
+	    val = (unsigned long) va_arg(args, void *);
+	    base = 16;
+	    neg = 2;
+	    break;
+	case 's':
+	    str = va_arg(args, char *);
+	    break;
+	case 'c':
+	    num[0] = va_arg(args, int);
+	    num[1] = 0;
+	    str = num;
+	    break;
+	case 'm':
+	    str = strerror(errno);
+	    break;
+	case 'I':
+	    ip = va_arg(args, u_int32_t);
+	    ip = ntohl(ip);
+	    slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff,
+		     (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
+	    str = num;
+	    break;
+#if 0	/* not used, and breaks on S/390, apparently */
+	case 'r':
+	    f = va_arg(args, char *);
+#ifndef __powerpc__
+	    n = vslprintf(buf, buflen + 1, f, va_arg(args, va_list));
+#else
+	    /* On the powerpc, a va_list is an array of 1 structure */
+	    n = vslprintf(buf, buflen + 1, f, va_arg(args, void *));
+#endif
+	    buf += n;
+	    buflen -= n;
+	    continue;
+#endif
+	case 't':
+	    time(&t);
+	    str = ctime(&t);
+	    str += 4;		/* chop off the day name */
+	    str[15] = 0;	/* chop off year and newline */
+	    break;
+	case 'v':		/* "visible" string */
+	case 'q':		/* quoted string */
+	    quoted = c == 'q';
+	    p = va_arg(args, unsigned char *);
+	    if (fillch == '0' && prec >= 0) {
+		n = prec;
+	    } else {
+		n = strlen((char *)p);
+		if (prec >= 0 && n > prec)
+		    n = prec;
+	    }
+	    while (n > 0 && buflen > 0) {
+		c = *p++;
+		--n;
+		if (!quoted && c >= 0x80) {
+		    OUTCHAR('M');
+		    OUTCHAR('-');
+		    c -= 0x80;
+		}
+		if (quoted && (c == '"' || c == '\\'))
+		    OUTCHAR('\\');
+		if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+		    if (quoted) {
+			OUTCHAR('\\');
+			switch (c) {
+			case '\t':	OUTCHAR('t');	break;
+			case '\n':	OUTCHAR('n');	break;
+			case '\b':	OUTCHAR('b');	break;
+			case '\f':	OUTCHAR('f');	break;
+			default:
+			    OUTCHAR('x');
+			    OUTCHAR(hexchars[c >> 4]);
+			    OUTCHAR(hexchars[c & 0xf]);
+			}
+		    } else {
+			if (c == '\t')
+			    OUTCHAR(c);
+			else {
+			    OUTCHAR('^');
+			    OUTCHAR(c ^ 0x40);
+			}
+		    }
+		} else
+		    OUTCHAR(c);
+	    }
+	    continue;
+	case 'P':		/* print PPP packet */
+	    bufinfo.ptr = buf;
+	    bufinfo.len = buflen + 1;
+	    p = va_arg(args, unsigned char *);
+	    n = va_arg(args, int);
+	    format_packet(p, n, vslp_printer, &bufinfo);
+	    buf = bufinfo.ptr;
+	    buflen = bufinfo.len - 1;
+	    continue;
+	case 'B':
+	    p = va_arg(args, unsigned char *);
+	    for (n = prec; n > 0; --n) {
+		c = *p++;
+		if (fillch == ' ')
+		    OUTCHAR(' ');
+		OUTCHAR(hexchars[(c >> 4) & 0xf]);
+		OUTCHAR(hexchars[c & 0xf]);
+	    }
+	    continue;
+	default:
+	    *buf++ = '%';
+	    if (c != '%')
+		--fmt;		/* so %z outputs %z etc. */
+	    --buflen;
+	    continue;
+	}
+	if (base != 0) {
+	    str = num + sizeof(num);
+	    *--str = 0;
+	    while (str > num + neg) {
+		*--str = hexchars[val % base];
+		val = val / base;
+		if (--prec <= 0 && val == 0)
+		    break;
+	    }
+	    switch (neg) {
+	    case 1:
+		*--str = '-';
+		break;
+	    case 2:
+		*--str = 'x';
+		*--str = '0';
+		break;
+	    }
+	    len = num + sizeof(num) - 1 - str;
+	} else {
+	    len = strlen(str);
+	    if (prec >= 0 && len > prec)
+		len = prec;
+	}
+	if (width > 0) {
+	    if (width > buflen)
+		width = buflen;
+	    if ((n = width - len) > 0) {
+		buflen -= n;
+		for (; n > 0; --n)
+		    *buf++ = fillch;
+	    }
+	}
+	if (len > buflen)
+	    len = buflen;
+	memcpy(buf, str, len);
+	buf += len;
+	buflen -= len;
+    }
+    *buf = 0;
+    return buf - buf0;
+}
+
+/*
+ * vslp_printer - used in processing a %P format
+ */
+static void
+vslp_printer __V((void *arg, char *fmt, ...))
+{
+    int n;
+    va_list pvar;
+    struct buffer_info *bi;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    void *arg;
+    char *fmt;
+    va_start(pvar);
+    arg = va_arg(pvar, void *);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    bi = (struct buffer_info *) arg;
+    n = vslprintf(bi->ptr, bi->len, fmt, pvar);
+    va_end(pvar);
+
+    bi->ptr += n;
+    bi->len -= n;
+}
+
+#ifdef unused
+/*
+ * log_packet - format a packet and log it.
+ */
+
+void
+log_packet(p, len, prefix, level)
+    u_char *p;
+    int len;
+    char *prefix;
+    int level;
+{
+	init_pr_log(prefix, level);
+	format_packet(p, len, pr_log, &level);
+	end_pr_log();
+}
+#endif /* unused */
+
+/*
+ * format_packet - make a readable representation of a packet,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void
+format_packet(p, len, printer, arg)
+    u_char *p;
+    int len;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    int i, n;
+    u_short proto;
+    struct protent *protp;
+
+    if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
+	p += 2;
+	GETSHORT(proto, p);
+	len -= PPP_HDRLEN;
+	for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	    if (proto == protp->protocol)
+		break;
+	if (protp != NULL) {
+	    printer(arg, "[%s", protp->name);
+	    n = (*protp->printpkt)(p, len, printer, arg);
+	    printer(arg, "]");
+	    p += n;
+	    len -= n;
+	} else {
+	    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+		if (proto == (protp->protocol & ~0x8000))
+		    break;
+	    if (protp != 0 && protp->data_name != 0) {
+		printer(arg, "[%s data]", protp->data_name);
+		if (len > 8)
+		    printer(arg, "%.8B ...", p);
+		else
+		    printer(arg, "%.*B", len, p);
+		len = 0;
+	    } else
+		printer(arg, "[proto=0x%x]", proto);
+	}
+    }
+
+    if (len > 32)
+	printer(arg, "%.32B ...", p);
+    else
+	printer(arg, "%.*B", len, p);
+}
+
+/*
+ * init_pr_log, end_pr_log - initialize and finish use of pr_log.
+ */
+
+static char line[256];		/* line to be logged accumulated here */
+static char *linep;		/* current pointer within line */
+static int llevel;		/* level for logging */
+
+void
+init_pr_log(prefix, level)
+     const char *prefix;
+     int level;
+{
+	linep = line;
+	if (prefix != NULL) {
+		strlcpy(line, prefix, sizeof(line));
+		linep = line + strlen(line);
+	}
+	llevel = level;
+}
+
+void
+end_pr_log()
+{
+	if (linep != line) {
+		*linep = 0;
+		log_write(llevel, line);
+	}
+}
+
+/*
+ * pr_log - printer routine for outputting to syslog
+ */
+void
+pr_log __V((void *arg, char *fmt, ...))
+{
+	int l, n;
+	va_list pvar;
+	char *p, *eol;
+	char buf[256];
+
+#if defined(__STDC__)
+	va_start(pvar, fmt);
+#else
+	void *arg;
+	char *fmt;
+	va_start(pvar);
+	arg = va_arg(pvar, void *);
+	fmt = va_arg(pvar, char *);
+#endif
+
+	n = vslprintf(buf, sizeof(buf), fmt, pvar);
+	va_end(pvar);
+
+	p = buf;
+	eol = strchr(buf, '\n');
+	if (linep != line) {
+		l = (eol == NULL)? n: eol - buf;
+		if (linep + l < line + sizeof(line)) {
+			if (l > 0) {
+				memcpy(linep, buf, l);
+				linep += l;
+			}
+			if (eol == NULL)
+				return;
+			p = eol + 1;
+			eol = strchr(p, '\n');
+		}
+		*linep = 0;
+		log_write(llevel, line);
+		linep = line;
+	}
+
+	while (eol != NULL) {
+		*eol = 0;
+		log_write(llevel, p);
+		p = eol + 1;
+		eol = strchr(p, '\n');
+	}
+
+	/* assumes sizeof(buf) <= sizeof(line) */
+	l = buf + n - p;
+	if (l > 0) {
+		memcpy(line, p, n);
+		linep = line + l;
+	}
+}
+
+/*
+ * print_string - print a readable representation of a string using
+ * printer.
+ */
+void
+print_string(p, len, printer, arg)
+    char *p;
+    int len;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    int c;
+
+    printer(arg, "\"");
+    for (; len > 0; --len) {
+	c = *p++;
+	if (' ' <= c && c <= '~') {
+	    if (c == '\\' || c == '"')
+		printer(arg, "\\");
+	    printer(arg, "%c", c);
+	} else {
+	    switch (c) {
+	    case '\n':
+		printer(arg, "\\n");
+		break;
+	    case '\r':
+		printer(arg, "\\r");
+		break;
+	    case '\t':
+		printer(arg, "\\t");
+		break;
+	    default:
+		printer(arg, "\\%.3o", c);
+	    }
+	}
+    }
+    printer(arg, "\"");
+}
+
+/*
+ * logit - does the hard work for fatal et al.
+ */
+static void
+logit(level, fmt, args)
+    int level;
+    char *fmt;
+    va_list args;
+{
+    int n;
+    char buf[1024];
+
+    n = vslprintf(buf, sizeof(buf), fmt, args);
+    log_write(level, buf);
+}
+
+static void
+log_write(level, buf)
+    int level;
+    char *buf;
+{
+#if 1
+	slog("[pppd]",SLOG_ERR,"%s\n", buf); 
+#else
+    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;
+    }
+#endif	
+}
+
+/*
+ * fatal - log an error message and die horribly.
+ */
+void
+fatal __V((char *fmt, ...))
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    logit(LOG_ERR, fmt, pvar);
+    va_end(pvar);
+
+    die(1);			/* as promised */
+}
+
+/*
+ * error - log an error message.
+ */
+void
+error __V((char *fmt, ...))
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    logit(LOG_ERR, fmt, pvar);
+    va_end(pvar);
+    ++error_count;
+}
+
+/*
+ * warn - log a warning message.
+ */
+void
+warn __V((char *fmt, ...))
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    logit(LOG_WARNING, fmt, pvar);
+    va_end(pvar);
+}
+
+/*
+ * notice - log a notice-level message.
+ */
+void
+notice __V((char *fmt, ...))
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    logit(LOG_NOTICE, fmt, pvar);
+    va_end(pvar);
+}
+
+/*
+ * info - log an informational message.
+ */
+void
+info __V((char *fmt, ...))
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    logit(LOG_INFO, fmt, pvar);
+    va_end(pvar);
+}
+
+/*
+ * dbglog - log a debug message.
+ */
+void
+dbglog __V((char *fmt, ...))
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    logit(LOG_DEBUG, fmt, pvar);
+    va_end(pvar);
+}
+
+/*
+ * dump_packet - print out a packet in readable form if it is interesting.
+ * Assumes len >= PPP_HDRLEN.
+ */
+void
+dump_packet(const char *tag, unsigned char *p, int len)
+{
+    int proto;
+
+    if (!debug)
+	return;
+
+    /*
+     * don't print LCP echo request/reply packets if debug <= 1
+     * and the link is up.
+     */
+    proto = (p[2] << 8) + p[3];
+    if (debug <= 1 && unsuccess == 0 && proto == PPP_LCP
+	&& len >= PPP_HDRLEN + HEADERLEN) {
+	unsigned char *lcp = p + PPP_HDRLEN;
+	int l = (lcp[2] << 8) + lcp[3];
+
+	if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP)
+	    && l >= HEADERLEN && l <= len - PPP_HDRLEN)
+	    return;
+    }
+
+    warn("%s %P", tag, p, len);
+}
+
+/*
+ * complete_read - read a full `count' bytes from fd,
+ * unless end-of-file or an error other than EINTR is encountered.
+ */
+ssize_t
+complete_read(int fd, void *buf, size_t count)
+{
+	size_t done;
+	ssize_t nb;
+	char *ptr = buf;
+
+	for (done = 0; done < count; ) {
+		nb = read(fd, ptr, count - done);
+		if (nb < 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		if (nb == 0)
+			break;
+		done += nb;
+		ptr += nb;
+	}
+	return done;
+}
+
+/* Procedures for locking the serial device using a lock file. */
+#ifndef LOCK_DIR
+#ifdef __linux__
+#define LOCK_DIR	"/var/lock"
+#else
+#ifdef SVR4
+#define LOCK_DIR	"/var/spool/locks"
+#else
+#define LOCK_DIR	"/var/spool/lock"
+#endif
+#endif
+#endif /* LOCK_DIR */
+
+static char lock_file[MAXPATHLEN];
+
+/*
+ * lock - create a lock file for the named device
+ */
+int
+lock(dev)
+    char *dev;
+{
+#ifdef LOCKLIB
+    int result;
+
+    result = mklock (dev, (void *) 0);
+    if (result == 0) {
+	strlcpy(lock_file, dev, sizeof(lock_file));
+	return 0;
+    }
+
+    if (result > 0)
+        warn("Device %s is locked by pid %d", dev, result);
+    else
+	error("Can't create lock file %s", lock_file);
+    return -1;
+
+#else /* LOCKLIB */
+
+    char lock_buffer[12];
+    int fd, pid, n;
+
+#ifdef SVR4
+    struct stat sbuf;
+
+    if (stat(dev, &sbuf) < 0) {
+	error("Can't get device number for %s: %m", dev);
+	return -1;
+    }
+    if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
+	error("Can't lock %s: not a character device", dev);
+	return -1;
+    }
+    slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d",
+	     LOCK_DIR, major(sbuf.st_dev),
+	     major(sbuf.st_rdev), minor(sbuf.st_rdev));
+#else
+    char *p;
+    char lockdev[MAXPATHLEN];
+
+    if ((p = strstr(dev, "dev/")) != NULL) {
+	dev = p + 4;
+	strncpy(lockdev, dev, MAXPATHLEN-1);
+	lockdev[MAXPATHLEN-1] = 0;
+	while ((p = strrchr(lockdev, '/')) != NULL) {
+	    *p = '_';
+	}
+	dev = lockdev;
+    } else
+	if ((p = strrchr(dev, '/')) != NULL)
+	    dev = p + 1;
+
+    slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev);
+#endif
+
+    while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+	if (errno != EEXIST) {
+	    error("Can't create lock file %s: %m", lock_file);
+	    break;
+	}
+
+	/* Read the lock file to find out who has the device locked. */
+	fd = open(lock_file, O_RDONLY, 0);
+	if (fd < 0) {
+	    if (errno == ENOENT) /* This is just a timing problem. */
+		continue;
+	    error("Can't open existing lock file %s: %m", lock_file);
+	    break;
+	}
+#ifndef LOCK_BINARY
+	n = read(fd, lock_buffer, 11);
+#else
+	n = read(fd, &pid, sizeof(pid));
+#endif /* LOCK_BINARY */
+	close(fd);
+	fd = -1;
+	if (n <= 0) {
+	    error("Can't read pid from lock file %s", lock_file);
+	    break;
+	}
+
+	/* See if the process still exists. */
+#ifndef LOCK_BINARY
+	lock_buffer[n] = 0;
+	pid = atoi(lock_buffer);
+#endif /* LOCK_BINARY */
+	if (pid == getpid())
+	    return 1;		/* somebody else locked it for us */
+	if (pid == 0
+	    || (kill(pid, 0) == -1 && errno == ESRCH)) {
+	    if (unlink (lock_file) == 0) {
+		warn("Removed stale lock on %s (pid %d)", dev, pid);
+		continue;
+	    }
+	    warn("Couldn't remove stale lock on %s", dev);
+	} else
+	    warn("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/ap/app/pppd/pppdump/Makefile.linux b/ap/app/pppd/pppdump/Makefile.linux
new file mode 100644
index 0000000..3d73710
--- /dev/null
+++ b/ap/app/pppd/pppdump/Makefile.linux
@@ -0,0 +1,22 @@
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+
+#CFLAGS= -O -I../include/net
+CFLAGS += -I../include/net
+OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
+
+INSTALL= install
+
+all:	pppdump
+
+pppdump: $(OBJS)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o pppdump $(OBJS) $(LDLIBS$(LDLIBS_$@))
+
+clean:
+	rm -f pppdump $(OBJS) *~
+
+install:
+	mkdir -p $(BINDIR) $(MANDIR)
+	$(INSTALL) -s -c pppdump $(BINDIR)
+	$(INSTALL) -c -m 444 pppdump.8 $(MANDIR)
diff --git a/ap/app/pppd/pppdump/Makefile.sol2 b/ap/app/pppd/pppdump/Makefile.sol2
new file mode 100644
index 0000000..d496873
--- /dev/null
+++ b/ap/app/pppd/pppdump/Makefile.sol2
@@ -0,0 +1,21 @@
+#
+# pppdump Makefile for SVR4 systems
+# $Id: Makefile.sol2,v 1.4 2007-06-08 04:52:52 gerg Exp $
+#
+
+include ../Makedefs.com
+
+CFLAGS= $(COPTS) -I../include/net
+OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
+
+all:	pppdump
+
+pppdump: $(OBJS)
+	$(CC) -o pppdump $(OBJS)
+
+clean:
+	rm -f $(OBJS) pppdump *~
+
+install:
+	$(INSTALL) -f $(BINDIR) pppdump
+	$(INSTALL) -m 444 -f $(MANDIR)/man8 pppdump.8
diff --git a/ap/app/pppd/pppdump/bsd-comp.c b/ap/app/pppd/pppdump/bsd-comp.c
new file mode 100644
index 0000000..f3a66c7
--- /dev/null
+++ b/ap/app/pppd/pppdump/bsd-comp.c
@@ -0,0 +1,752 @@
+/* Because this code is derived from the 4.3BSD compress source:
+ *
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: bsd-comp.c,v 1.2 2007-06-08 04:02:39 gerg Exp $
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.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/ap/app/pppd/pppdump/deflate.c b/ap/app/pppd/pppdump/deflate.c
new file mode 100644
index 0000000..fce5d72
--- /dev/null
+++ b/ap/app/pppd/pppdump/deflate.c
@@ -0,0 +1,354 @@
+/*
+ * ppp_deflate.c - interface the zlib procedures for Deflate compression
+ * and decompression (as used by gzip) to the PPP code.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: deflate.c,v 1.2 2007-06-08 04:02:39 gerg Exp $
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.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/ap/app/pppd/pppdump/ppp-comp.h b/ap/app/pppd/pppdump/ppp-comp.h
new file mode 100644
index 0000000..0c2a95b
--- /dev/null
+++ b/ap/app/pppd/pppdump/ppp-comp.h
@@ -0,0 +1,158 @@
+/*
+ * ppp-comp.h - Definitions for doing PPP packet compression.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp-comp.h,v 1.2 2007-06-08 04:02:39 gerg Exp $
+ */
+
+#ifndef _NET_PPP_COMP_H
+#define _NET_PPP_COMP_H
+
+/*
+ * The following symbols control whether we include code for
+ * various compression methods.
+ */
+#ifndef DO_BSD_COMPRESS
+#define DO_BSD_COMPRESS	1	/* by default, include BSD-Compress */
+#endif
+#ifndef DO_DEFLATE
+#define DO_DEFLATE	1	/* by default, include Deflate */
+#endif
+#define DO_PREDICTOR_1	0
+#define DO_PREDICTOR_2	0
+
+/*
+ * Structure giving methods for compression/decompression.
+ */
+struct compressor {
+	int	compress_proto;	/* CCP compression protocol number */
+
+	/* Allocate space for a decompressor (receive side) */
+	void	*(*decomp_alloc) __P((u_char *options, int opt_len));
+	/* Free space used by a decompressor */
+	void	(*decomp_free) __P((void *state));
+	/* Initialize a decompressor */
+	int	(*decomp_init) __P((void *state, u_char *options, int opt_len,
+				    int unit, int hdrlen, int mru, int debug));
+	/* Reset a decompressor */
+	void	(*decomp_reset) __P((void *state));
+	/* Decompress a packet. */
+	int	(*decompress) __P((void *state, u_char *mp, int inlen,
+				   u_char *dmp, int *outlen));
+	/* Update state for an incompressible packet received */
+	void	(*incomp) __P((void *state, u_char *mp, int len));
+	/* Return decompression statistics */
+	void	(*decomp_stat) __P((void *state, struct compstat *stats));
+};
+
+/*
+ * Return values for decompress routine.
+ * We need to make these distinctions so that we can disable certain
+ * useful functionality, namely sending a CCP reset-request as a result
+ * of an error detected after decompression.  This is to avoid infringing
+ * a patent held by Motorola.
+ * Don't you just lurve software patents.
+ */
+#define DECOMP_OK		0	/* everything went OK */
+#define DECOMP_ERROR		1	/* error detected before decomp. */
+#define DECOMP_FATALERROR	2	/* error detected after decomp. */
+
+/*
+ * CCP codes.
+ */
+#define CCP_CONFREQ	1
+#define CCP_CONFACK	2
+#define CCP_CONFNAK	3
+#define CCP_CONFREJ	4
+#define CCP_TERMREQ	5
+#define CCP_TERMACK	6
+#define CCP_RESETREQ	14
+#define CCP_RESETACK	15
+
+/*
+ * Max # bytes for a CCP option
+ */
+#define CCP_MAX_OPTION_LENGTH	32
+
+/*
+ * Parts of a CCP packet.
+ */
+#define CCP_CODE(dp)		((dp)[0])
+#define CCP_ID(dp)		((dp)[1])
+#define CCP_LENGTH(dp)		(((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN		4
+
+#define CCP_OPT_CODE(dp)	((dp)[0])
+#define CCP_OPT_LENGTH(dp)	((dp)[1])
+#define CCP_OPT_MINLEN		2
+
+/*
+ * Definitions for BSD-Compress.
+ */
+#define CI_BSD_COMPRESS		21	/* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS	3	/* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x)		((x) & 0x1F)	/* number of bits requested */
+#define BSD_VERSION(x)		((x) >> 5)	/* version of option format */
+#define BSD_CURRENT_VERSION	1		/* current version number */
+#define BSD_MAKE_OPT(v, n)	(((v) << 5) | (n))
+
+#define BSD_MIN_BITS		9	/* smallest code size supported */
+#define BSD_MAX_BITS		15	/* largest code size supported */
+
+/*
+ * Definitions for Deflate.
+ */
+#define CI_DEFLATE		26	/* config option for Deflate */
+#define CI_DEFLATE_DRAFT	24	/* value used in original draft RFC */
+#define CILEN_DEFLATE		4	/* length of its config option */
+
+#define DEFLATE_MIN_SIZE	8
+#define DEFLATE_MAX_SIZE	15
+#define DEFLATE_METHOD_VAL	8
+#define DEFLATE_SIZE(x)		(((x) >> 4) + DEFLATE_MIN_SIZE)
+#define DEFLATE_METHOD(x)	((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w)	((((w) - DEFLATE_MIN_SIZE) << 4) \
+				 + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE	0
+
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+#define CI_PREDICTOR_1		1	/* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1	2	/* length of its config option */
+#define CI_PREDICTOR_2		2	/* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2	2	/* length of its config option */
+
+#endif /* _NET_PPP_COMP_H */
diff --git a/ap/app/pppd/pppdump/pppdump.8 b/ap/app/pppd/pppdump/pppdump.8
new file mode 100644
index 0000000..8ff042d
--- /dev/null
+++ b/ap/app/pppd/pppdump/pppdump.8
@@ -0,0 +1,62 @@
+.\"	@(#) $Id: pppdump.8,v 1.2 2007-06-08 04:02:39 gerg Exp $
+.TH PPPDUMP 8 "1 April 1999"
+.SH NAME
+pppdump \- convert PPP record file to readable format
+.SH SYNOPSIS
+.B pppdump
+[
+.B \-h
+|
+.B \-p
+[
+.B \-d
+]] [
+.B \-r
+] [
+.B \-m \fImru
+] [
+.I file \fR...
+]
+.ti 12
+.SH DESCRIPTION
+The
+.B pppdump
+utility converts the files written using the \fIrecord\fR option of
+.B pppd
+into a human-readable format.  If one or more filenames are specified,
+.B pppdump
+will read each in turn; otherwise it will read its standard input.  In
+each case the result is written to standard output.
+.PP
+The options are as follows:
+.TP
+.B \-h
+Prints the bytes sent and received in hexadecimal.  If neither this
+option nor the \fB\-p\fR option is specified, the bytes are printed as
+the characters themselves, with non-printing and non-ASCII characters
+printed as escape sequences.
+.TP
+.B \-p
+Collects the bytes sent and received into PPP packets, interpreting
+the async HDLC framing and escape characters and checking the FCS
+(frame check sequence) of each packet.  The packets are printed as hex
+values and as characters (non-printable characters are printed as
+`.').
+.TP
+.B \-d
+With the \fB\-p\fR option, this option causes
+.B pppdump
+to decompress packets which have been compressed with the BSD-Compress
+or Deflate methods.
+.TP
+.B \-r
+Reverses the direction indicators, so that `sent' is printed for
+bytes or packets received, and `rcvd' is printed for bytes or packets
+sent.
+.TP
+.B \-m \fImru
+Use \fImru\fR as the MRU (maximum receive unit) for both directions of
+the link when checking for over-length PPP packets (with the \fB\-p\fR
+option).
+.SH SEE ALSO
+pppd(8)
diff --git a/ap/app/pppd/pppdump/pppdump.c b/ap/app/pppd/pppdump/pppdump.c
new file mode 100644
index 0000000..95e692c
--- /dev/null
+++ b/ap/app/pppd/pppdump/pppdump.c
@@ -0,0 +1,533 @@
+/*
+ * pppdump - print out the contents of a record file generated by
+ * pppd in readable form.
+ *
+ * Copyright (c) 1999 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.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;
+
+void dumplog();
+void dumpppp();
+void show_time();
+void handle_ccp();
+
+int
+main(ac, av)
+    int ac;
+    char **av;
+{
+    int i;
+    char *p;
+    FILE *f;
+
+    while ((i = getopt(ac, av, "hprdm:a")) != -1) {
+	switch (i) {
+	case 'h':
+	    hexmode = 1;
+	    break;
+	case 'p':
+	    pppmode = 1;
+	    break;
+	case 'r':
+	    reverse = 1;
+	    break;
+	case 'd':
+	    decompress = 1;
+	    break;
+	case 'm':
+	    mru = atoi(optarg);
+	    break;
+	case 'a':
+	    abs_times = 1;
+	    break;
+	default:
+	    fprintf(stderr, "Usage: %s [-h | -p[d]] [-r] [-m mru] [-a] [file ...]\n", av[0]);
+	    exit(1);
+	}
+    }
+    if (optind >= ac)
+	dumplog(stdin);
+    else {
+	for (i = optind; i < ac; ++i) {
+	    p = av[i];
+	    if ((f = fopen(p, "r")) == NULL) {
+		perror(p);
+		exit(1);
+	    }
+	    if (pppmode)
+		dumpppp(f);
+	    else
+		dumplog(f);
+	    fclose(f);
+	}
+    }
+    exit(0);
+}
+
+void
+dumplog(f)
+    FILE *f;
+{
+    int c, n, k, col;
+    int nb, c2;
+    unsigned char buf[16];
+
+    while ((c = getc(f)) != EOF) {
+	switch (c) {
+	case 1:
+	case 2:
+	    if (reverse)
+		c = 3 - c;
+	    printf("%s %c", c==1? "sent": "rcvd", hexmode? ' ': '"');
+	    col = 6;
+	    n = getc(f);
+	    n = (n << 8) + getc(f);
+	    *(c==1? &tot_sent: &tot_rcvd) += n;
+	    nb = 0;
+	    for (; n > 0; --n) {
+		c = getc(f);
+		if (c == EOF) {
+		    printf("\nEOF\n");
+		    exit(0);
+		}
+		if (hexmode) {
+		    if (nb >= 16) {
+			printf("  ");
+			for (k = 0; k < nb; ++k) {
+			    c2 = buf[k];
+			    putchar((' ' <= c2 && c2 <= '~')? c2: '.');
+			}
+			printf("\n      ");
+			nb = 0;
+		    }
+		    buf[nb++] = c;
+		    printf(" %.2x", c);
+		} else {
+		    k = (' ' <= c && c <= '~')? (c != '\\' && c != '"')? 1: 2: 3;
+		    if ((col += k) >= 78) {
+			printf("\n      ");
+			col = 6 + k;
+		    }
+		    switch (k) {
+		    case 1:
+			putchar(c);
+			break;
+		    case 2:
+			printf("\\%c", c);
+			break;
+		    case 3:
+			printf("\\%.2x", c);
+			break;
+		    }
+		}
+	    }
+	    if (hexmode) {
+		for (k = nb; k < 16; ++k)
+		    printf("   ");
+		printf("  ");
+		for (k = 0; k < nb; ++k) {
+		    c2 = buf[k];
+		    putchar((' ' <= c2 && c2 <= '~')? c2: '.');
+		}
+	    } else
+		putchar('"');
+	    printf("\n");
+	    break;
+	case 3:
+	case 4:
+	    printf("end %s\n", c==3? "send": "recv");
+	    break;
+	case 5:
+	case 6:
+	case 7:
+	    show_time(f, c);
+	    break;
+	default:
+	    printf("?%.2x\n");
+	}
+    }
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+	0x0000,	0x1189,	0x2312,	0x329b,	0x4624,	0x57ad,	0x6536,	0x74bf,
+	0x8c48,	0x9dc1,	0xaf5a,	0xbed3,	0xca6c,	0xdbe5,	0xe97e,	0xf8f7,
+	0x1081,	0x0108,	0x3393,	0x221a,	0x56a5,	0x472c,	0x75b7,	0x643e,
+	0x9cc9,	0x8d40,	0xbfdb,	0xae52,	0xdaed,	0xcb64,	0xf9ff,	0xe876,
+	0x2102,	0x308b,	0x0210,	0x1399,	0x6726,	0x76af,	0x4434,	0x55bd,
+	0xad4a,	0xbcc3,	0x8e58,	0x9fd1,	0xeb6e,	0xfae7,	0xc87c,	0xd9f5,
+	0x3183,	0x200a,	0x1291,	0x0318,	0x77a7,	0x662e,	0x54b5,	0x453c,
+	0xbdcb,	0xac42,	0x9ed9,	0x8f50,	0xfbef,	0xea66,	0xd8fd,	0xc974,
+	0x4204,	0x538d,	0x6116,	0x709f,	0x0420,	0x15a9,	0x2732,	0x36bb,
+	0xce4c,	0xdfc5,	0xed5e,	0xfcd7,	0x8868,	0x99e1,	0xab7a,	0xbaf3,
+	0x5285,	0x430c,	0x7197,	0x601e,	0x14a1,	0x0528,	0x37b3,	0x263a,
+	0xdecd,	0xcf44,	0xfddf,	0xec56,	0x98e9,	0x8960,	0xbbfb,	0xaa72,
+	0x6306,	0x728f,	0x4014,	0x519d,	0x2522,	0x34ab,	0x0630,	0x17b9,
+	0xef4e,	0xfec7,	0xcc5c,	0xddd5,	0xa96a,	0xb8e3,	0x8a78,	0x9bf1,
+	0x7387,	0x620e,	0x5095,	0x411c,	0x35a3,	0x242a,	0x16b1,	0x0738,
+	0xffcf,	0xee46,	0xdcdd,	0xcd54,	0xb9eb,	0xa862,	0x9af9,	0x8b70,
+	0x8408,	0x9581,	0xa71a,	0xb693,	0xc22c,	0xd3a5,	0xe13e,	0xf0b7,
+	0x0840,	0x19c9,	0x2b52,	0x3adb,	0x4e64,	0x5fed,	0x6d76,	0x7cff,
+	0x9489,	0x8500,	0xb79b,	0xa612,	0xd2ad,	0xc324,	0xf1bf,	0xe036,
+	0x18c1,	0x0948,	0x3bd3,	0x2a5a,	0x5ee5,	0x4f6c,	0x7df7,	0x6c7e,
+	0xa50a,	0xb483,	0x8618,	0x9791,	0xe32e,	0xf2a7,	0xc03c,	0xd1b5,
+	0x2942,	0x38cb,	0x0a50,	0x1bd9,	0x6f66,	0x7eef,	0x4c74,	0x5dfd,
+	0xb58b,	0xa402,	0x9699,	0x8710,	0xf3af,	0xe226,	0xd0bd,	0xc134,
+	0x39c3,	0x284a,	0x1ad1,	0x0b58,	0x7fe7,	0x6e6e,	0x5cf5,	0x4d7c,
+	0xc60c,	0xd785,	0xe51e,	0xf497,	0x8028,	0x91a1,	0xa33a,	0xb2b3,
+	0x4a44,	0x5bcd,	0x6956,	0x78df,	0x0c60,	0x1de9,	0x2f72,	0x3efb,
+	0xd68d,	0xc704,	0xf59f,	0xe416,	0x90a9,	0x8120,	0xb3bb,	0xa232,
+	0x5ac5,	0x4b4c,	0x79d7,	0x685e,	0x1ce1,	0x0d68,	0x3ff3,	0x2e7a,
+	0xe70e,	0xf687,	0xc41c,	0xd595,	0xa12a,	0xb0a3,	0x8238,	0x93b1,
+	0x6b46,	0x7acf,	0x4854,	0x59dd,	0x2d62,	0x3ceb,	0x0e70,	0x1ff9,
+	0xf78f,	0xe606,	0xd49d,	0xc514,	0xb1ab,	0xa022,	0x92b9,	0x8330,
+	0x7bc7,	0x6a4e,	0x58d5,	0x495c,	0x3de3,	0x2c6a,	0x1ef1,	0x0f78
+};
+
+struct pkt {
+    int	cnt;
+    int	esc;
+    int	flags;
+    struct compressor *comp;
+    void *state;
+    unsigned char buf[8192];
+} spkt, rpkt;
+
+/* Values for flags */
+#define CCP_ISUP	1
+#define CCP_ERROR	2
+#define CCP_FATALERROR	4
+#define CCP_ERR		(CCP_ERROR | CCP_FATALERROR)
+#define CCP_DECOMP_RUN	8
+
+unsigned char dbuf[8192];
+
+void
+dumpppp(f)
+    FILE *f;
+{
+    int c, n, k;
+    int nb, nl, dn, proto, rv;
+    char *dir, *q;
+    unsigned char *p, *r, *endp;
+    unsigned char *d;
+    unsigned short fcs;
+    struct pkt *pkt;
+
+    spkt.cnt = rpkt.cnt = 0;
+    spkt.esc = rpkt.esc = 0;
+    while ((c = getc(f)) != EOF) {
+	switch (c) {
+	case 1:
+	case 2:
+	    if (reverse)
+		c = 3 - c;
+	    dir = c==1? "sent": "rcvd";
+	    pkt = c==1? &spkt: &rpkt;
+	    n = getc(f);
+	    n = (n << 8) + getc(f);
+	    *(c==1? &tot_sent: &tot_rcvd) += n;
+	    for (; n > 0; --n) {
+		c = getc(f);
+		switch (c) {
+		case EOF:
+		    printf("\nEOF\n");
+		    if (spkt.cnt > 0)
+			printf("[%d bytes in incomplete send packet]\n",
+			       spkt.cnt);
+		    if (rpkt.cnt > 0)
+			printf("[%d bytes in incomplete recv packet]\n",
+			       rpkt.cnt);
+		    exit(0);
+		case '~':
+		    if (pkt->cnt > 0) {
+			q = dir;
+			if (pkt->esc) {
+			    printf("%s aborted packet:\n     ", dir);
+			    q = "    ";
+			}
+			nb = pkt->cnt;
+			p = pkt->buf;
+			pkt->cnt = 0;
+			pkt->esc = 0;
+			if (nb <= 2) {
+			    printf("%s short packet [%d bytes]:", q, nb);
+			    for (k = 0; k < nb; ++k)
+				printf(" %.2x", p[k]);
+			    printf("\n");
+			    break;
+			}
+			fcs = PPP_INITFCS;
+			for (k = 0; k < nb; ++k)
+			    fcs = PPP_FCS(fcs, p[k]);
+			fcs &= 0xFFFF;
+			nb -= 2;
+			endp = p + nb;
+			r = p;
+			if (r[0] == 0xff && r[1] == 3)
+			    r += 2;
+			if ((r[0] & 1) == 0)
+			    ++r;
+			++r;
+			if (endp - r > mru)
+			    printf("     ERROR: length (%d) > MRU (%d)\n",
+				   endp - r, mru);
+			if (decompress && fcs == PPP_GOODFCS) {
+			    /* See if this is a CCP or compressed packet */
+			    d = dbuf;
+			    r = p;
+			    if (r[0] == 0xff && r[1] == 3) {
+				*d++ = *r++;
+				*d++ = *r++;
+			    }
+			    proto = r[0];
+			    if ((proto & 1) == 0)
+				proto = (proto << 8) + r[1];
+			    if (proto == PPP_CCP) {
+				handle_ccp(pkt, r + 2, endp - r - 2);
+			    } else if (proto == PPP_COMP) {
+				if ((pkt->flags & CCP_ISUP)
+				    && (pkt->flags & CCP_DECOMP_RUN)
+				    && pkt->state
+				    && (pkt->flags & CCP_ERR) == 0) {
+				    rv = pkt->comp->decompress(pkt->state, r,
+							endp - r, d, &dn);
+				    switch (rv) {
+				    case DECOMP_OK:
+					p = dbuf;
+					nb = d + dn - p;
+					if ((d[0] & 1) == 0)
+					    --dn;
+					--dn;
+					if (dn > mru)
+					    printf("     ERROR: decompressed length (%d) > MRU (%d)\n", dn, mru);
+					break;
+				    case DECOMP_ERROR:
+					printf("     DECOMPRESSION ERROR\n");
+					pkt->flags |= CCP_ERROR;
+					break;
+				    case DECOMP_FATALERROR:
+					printf("     FATAL DECOMPRESSION ERROR\n");
+					pkt->flags |= CCP_FATALERROR;
+					break;
+				    }
+				}
+			    } else if (pkt->state
+				       && (pkt->flags & CCP_DECOMP_RUN)) {
+				pkt->comp->incomp(pkt->state, r, endp - r);
+			    }
+			}
+			do {
+			    nl = nb < 16? nb: 16;
+			    printf("%s ", q);
+			    for (k = 0; k < nl; ++k)
+				printf(" %.2x", p[k]);
+			    for (; k < 16; ++k)
+				printf("   ");
+			    printf("  ");
+			    for (k = 0; k < nl; ++k) {
+				c = p[k];
+				putchar((' ' <= c && c <= '~')? c: '.');
+			    }
+			    printf("\n");
+			    q = "    ";
+			    p += nl;
+			    nb -= nl;
+			} while (nb > 0);
+			if (fcs != PPP_GOODFCS)
+			    printf("     BAD FCS: (residue = %x)\n", fcs);
+		    }
+		    break;
+		case '}':
+		    if (!pkt->esc) {
+			pkt->esc = 1;
+			break;
+		    }
+		    /* else fall through */
+		default:
+		    if (pkt->esc) {
+			c ^= 0x20;
+			pkt->esc = 0;
+		    }
+		    pkt->buf[pkt->cnt++] = c;
+		    break;
+		}
+	    }
+	    break;
+	case 3:
+	case 4:
+	    if (reverse)
+		c = 7 - c;
+	    dir = c==3? "send": "recv";
+	    pkt = c==3? &spkt: &rpkt;
+	    printf("end %s", dir);
+	    if (pkt->cnt > 0)
+		printf("  [%d bytes in incomplete packet]", pkt->cnt);
+	    printf("\n");
+	    break;
+	case 5:
+	case 6:
+	case 7:
+	    show_time(f, c);
+	    break;
+	default:
+	    printf("?%.2x\n");
+	}
+    }
+}
+
+extern struct compressor ppp_bsd_compress, ppp_deflate;
+
+struct compressor *compressors[] = {
+#if DO_BSD_COMPRESS
+    &ppp_bsd_compress,
+#endif
+#if DO_DEFLATE
+    &ppp_deflate,
+#endif
+    NULL
+};
+
+void
+handle_ccp(cp, dp, len)
+    struct pkt *cp;
+    u_char *dp;
+    int len;
+{
+    int clen;
+    struct compressor **comp;
+
+    if (len < CCP_HDRLEN)
+	return;
+    clen = CCP_LENGTH(dp);
+    if (clen > len)
+	return;
+
+    switch (CCP_CODE(dp)) {
+    case CCP_CONFACK:
+	cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP);
+	if (clen < CCP_HDRLEN + CCP_OPT_MINLEN
+	    || clen < CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN))
+	    break;
+	dp += CCP_HDRLEN;
+	clen -= CCP_HDRLEN;
+	for (comp = compressors; *comp != NULL; ++comp) {
+	    if ((*comp)->compress_proto == dp[0]) {
+		if (cp->state != NULL) {
+		    (*cp->comp->decomp_free)(cp->state);
+		    cp->state = NULL;
+		}
+		cp->comp = *comp;
+		cp->state = (*comp)->decomp_alloc(dp, CCP_OPT_LENGTH(dp));
+		cp->flags |= CCP_ISUP;
+		if (cp->state != NULL
+		    && (*cp->comp->decomp_init)
+		        (cp->state, dp, clen, 0, 0, 8192, 1))
+		    cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
+		break;
+	    }
+	}
+	break;
+
+    case CCP_CONFNAK:
+    case CCP_CONFREJ:
+	cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP);
+	break;
+
+    case CCP_RESETACK:
+	if (cp->flags & CCP_ISUP) {
+	    if (cp->state && (cp->flags & CCP_DECOMP_RUN)) {
+		(*cp->comp->decomp_reset)(cp->state);
+		cp->flags &= ~CCP_ERROR;
+	    }
+	}
+	break;
+    }
+}
+
+void
+show_time(f, c)
+    FILE *f;
+    int c;
+{
+    time_t t;
+    int n;
+    struct tm *tm;
+
+    if (c == 7) {
+	t = getc(f);
+	t = (t << 8) + getc(f);
+	t = (t << 8) + getc(f);
+	t = (t << 8) + getc(f);
+	printf("start %s", ctime(&t));
+	start_time = t;
+	start_time_tenths = 0;
+	tot_sent = tot_rcvd = 0;
+    } else {
+	n = getc(f);
+	if (c == 5) {
+	    for (c = 3; c > 0; --c)
+		n = (n << 8) + getc(f);
+	}
+	if (abs_times) {
+	    n += start_time_tenths;
+	    start_time += n / 10;
+	    start_time_tenths = n % 10;
+	    tm = localtime(&start_time);
+	    printf("time  %.2d:%.2d:%.2d.%d", tm->tm_hour, tm->tm_min,
+		   tm->tm_sec, start_time_tenths);
+	    printf("  (sent %d, rcvd %d)\n", tot_sent, tot_rcvd);
+	} else
+	    printf("time  %.1fs\n", (double) n / 10);
+    }
+}
diff --git a/ap/app/pppd/pppdump/zlib.c b/ap/app/pppd/pppdump/zlib.c
new file mode 100644
index 0000000..d4f62b3
--- /dev/null
+++ b/ap/app/pppd/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: zlib.c,v 1.2 2007-06-08 04:02:39 gerg Exp $
+ */
+
+
+/*+++++*/
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */
+
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#ifdef STDC
+#  include <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/ap/app/pppd/pppdump/zlib.h b/ap/app/pppd/pppdump/zlib.h
new file mode 100644
index 0000000..cc90a7d
--- /dev/null
+++ b/ap/app/pppd/pppdump/zlib.h
@@ -0,0 +1,631 @@
+/*	$Id: zlib.h,v 1.2 2007-06-08 04:02:39 gerg Exp $	*/
+
+/*
+ * This file is derived from zlib.h and zconf.h from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.
+ */
+
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 0.95, Aug 16th, 1995.
+
+  Copyright (C) 1995 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  gzip@prep.ai.mit.edu    madler@alumni.caltech.edu
+ */
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+/* #include "zconf.h" */	/* included directly here */
+
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */
+
+/*
+     The library does not install any signal handler. It is recommended to
+  add at least a handler for SIGSEGV when decompressing; the library checks
+  the consistency of the input data whenever possible but may go nuts
+  for some forms of corrupted input.
+ */
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints
+ * at addresses which are not a multiple of their size.
+ * Under DOS, -DFAR=far or -DFAR=__far may be needed.
+ */
+
+#ifndef STDC
+#  if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus)
+#    define STDC
+#  endif
+#endif
+
+#ifdef	__MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */
+#  include <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/ap/app/pppd/pppstats/Makefile.linux b/ap/app/pppd/pppstats/Makefile.linux
new file mode 100644
index 0000000..1b62852
--- /dev/null
+++ b/ap/app/pppd/pppstats/Makefile.linux
@@ -0,0 +1,37 @@
+#
+# pppstats makefile
+# $Id: Makefile.linux,v 1.4 2007-06-20 06:11:29 gerg Exp $
+#
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+
+PPPSTATSRCS = pppstats.c
+PPPSTATOBJS = pppstats.o
+
+#CC = gcc
+#COPTS = -O
+COMPILE_FLAGS = -I../include
+#LIBS =
+
+INSTALL= install
+
+#CFLAGS = $(COPTS) $(COMPILE_FLAGS)
+CFLAGS += $(COPTS) $(COMPILE_FLAGS)
+
+all: pppstats
+
+install: pppstats
+	-mkdir -p $(MANDIR)
+	$(INSTALL) -s -c pppstats $(BINDIR)
+	$(INSTALL) -c -m 444 pppstats.8 $(MANDIR)
+
+pppstats: $(PPPSTATSRCS)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o pppstats pppstats.c $(LIBS) $(LDLIBS$(LDLIBS_$@))
+
+clean:
+	rm -f pppstats *~ #* core
+
+depend:
+	cpp -M $(CFLAGS) $(PPPSTATSRCS) >.depend
+#	makedepend $(CFLAGS) $(PPPSTATSRCS)
diff --git a/ap/app/pppd/pppstats/Makefile.sol2 b/ap/app/pppd/pppstats/Makefile.sol2
new file mode 100644
index 0000000..21caef4
--- /dev/null
+++ b/ap/app/pppd/pppstats/Makefile.sol2
@@ -0,0 +1,20 @@
+#
+# pppstats Makefile for SVR4 systems
+# $Id: Makefile.sol2,v 1.2 2007-06-08 04:02:39 gerg Exp $
+#
+
+include ../Makedefs.com
+
+CFLAGS = -DSTREAMS -I../include $(COPTS)
+
+all: pppstats
+
+pppstats: pppstats.c
+	$(CC) $(CFLAGS) -o pppstats pppstats.c
+
+install: pppstats
+	$(INSTALL) -f $(BINDIR) pppstats
+	$(INSTALL) -m 444 -f $(MANDIR)/man8 pppstats.8
+
+clean:
+	rm -f pppstats *~ core
diff --git a/ap/app/pppd/pppstats/pppstats.8 b/ap/app/pppd/pppstats/pppstats.8
new file mode 100644
index 0000000..402a8d8
--- /dev/null
+++ b/ap/app/pppd/pppstats/pppstats.8
@@ -0,0 +1,217 @@
+.\"	@(#) $Id: pppstats.8,v 1.2 2007-06-08 04:02:39 gerg Exp $
+.TH PPPSTATS 8 "26 June 1995"
+.SH NAME
+pppstats \- print PPP statistics
+.SH SYNOPSIS
+.B pppstats
+[
+.B \-a
+] [
+.B \-v
+] [
+.B \-r
+] [
+.B \-z
+] [
+.B \-c
+.I <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/ap/app/pppd/pppstats/pppstats.c b/ap/app/pppd/pppstats/pppstats.c
new file mode 100644
index 0000000..af2852c
--- /dev/null
+++ b/ap/app/pppd/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: pppstats.c,v 1.2 2007-06-08 04:02:39 gerg Exp $";
+#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/ap/app/pppd/ppp\346\250\241\345\235\227\344\277\256\346\224\271" "b/ap/app/pppd/ppp\346\250\241\345\235\227\344\277\256\346\224\271"
new file mode 100644
index 0000000..24e92c6
--- /dev/null
+++ "b/ap/app/pppd/ppp\346\250\241\345\235\227\344\277\256\346\224\271"
@@ -0,0 +1,8 @@
+auth.c network_phaseº¯ÊýÖе±Íê³ÉLCPЭÉ̽׶κóÉèÖÃcfg_set("lcp_finish","lcp_finish")£¬Ä¿µÄÊÇLCPЭÉ̺ó»á½øÈë¼øÈ¨ºÍNCP½×¶Î£¬¸Ã±ê־λ¿ØÖÆfsm_sdataÖÐLCP½×¶ÎµÄ°üÕý³£·¢ËÍ£»

+auth.c check_passwdÖмì²éUPAP¼øÈ¨µÄÃÜÂë£¬Ç¿ÖÆ·µ»ØACK£»

+chap-new.c chap_generate_challengeÖжÔauthinfo[0].challenge¸³Öµ£»

+chap-new.c chap_handle_responseÖжÔauthinfo[0].pw¸³Öµ£¬±£´æµÄÊÇCHAP¼øÈ¨µÄresponse´®£»

+chap-new.c chap_verify_response¶ÔCHAP¼øÈ¨µÄÆäËûÖµ¸³Öµ£¬²¢·¢ËÍÏûÏ¢¸øATServer£»

+fsm.c fsm_sdataÖпØÖÆPPPDÓë¿Í»§¶ËµÄ·¢ËÍʱ»ú£¬¼øÈ¨Ç°µÄÊý¾Ý¶¼·¢ËÍ£¬PPPDÒÑÐéÄâ¼øÈ¨µÈµ½ATserverµÄÕæÊµ¼øÈ¨£¬Ö»ÓÐÕæÊµ¼øÈ¨ºó²Å·¢ËÍ£¬ÆäËûµÈ´ý£»

+ipcp.c ipcp_resetci£¬µ±ATserver¼øÈ¨ºó²¢ÓÐÕæÊµµØÖ·µÄʱºò£¬¸³Öµ¶ÔÓÚµÄÖµ¸øpppdµÄÈ«¾Ö±äÁ¿£¬ÓÃÓÚ¸øÖÕ¶Ë·ÖÅäÕæÊµµØÖ·£»

+upap.c upap_rauthreq¶Ôupap½×¶ÎµÄ¼øÈ¨¸³Öµ
\ No newline at end of file
diff --git a/ap/app/pppd/scripts/README b/ap/app/pppd/scripts/README
new file mode 100644
index 0000000..00e032c
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/autopppd b/ap/app/pppd/scripts/autopppd
new file mode 100644
index 0000000..0730ef6
--- /dev/null
+++ b/ap/app/pppd/scripts/autopppd
@@ -0,0 +1,160 @@
+#!/usr/bin/perl -w
+
+# Auto dial script by Brian May <bam@snoopy.apana.org.au>
+
+use Proc::Daemon;
+use strict;
+use Sys::Syslog qw(:DEFAULT setlogsock);  # default set, plus setlogsock
+use Proc::WaitStat qw(:DEFAULT waitstat);
+
+
+Proc::Daemon::Init;
+open(PIDFILE,">/var/run/autopppd.pid");
+print(PIDFILE "$$");
+close(PIDFILE);
+
+sub toseconds($) {
+  my ($hours,$minutes,$seconds) = split(/:/,shift);
+  return ($hours*60+$minutes)*60+$seconds;
+}
+
+sub dseconds($) {
+  my ($total) = @_;
+
+  my $seconds = $total % 60; $total = ($total - $seconds)/60;
+  my $minutes = $total % 60; $total = ($total - $minutes)/60;
+  my $hours   = $total % 24; $total = ($total - $hours)/24;
+  my $days   = $total;
+  if ($days > 0) {
+    return(sprintf("%d-%02d:%02d:%02d",$days,$hours,$minutes,$seconds));
+  } else {
+    return(sprintf("%02d:%02d:%02d",$hours,$minutes,$seconds));
+  }
+}
+
+my $program="autopppd";
+
+setlogsock('unix');
+openlog($program, 'cons,pid', 'daemon');
+
+my $pppd_start_time;
+my $pppd_end_time;
+my $pppd_run_time;
+my $pppd_fail;
+my $delay=0;
+my $idelay=0;
+
+my @delays = (
+               toseconds("00:01:00"), # 1 minute
+               toseconds("00:07:00"), # 8 minutes
+               toseconds("00:07:00"), # 15 minutes
+               toseconds("00:15:00"), # 30 minutes
+               toseconds("00:30:00"), # 1 hour
+               toseconds("01:00:00"), # 2 hours
+               toseconds("01:00:00"), # 3 hours
+               toseconds("03:00:00"), # 6 hours
+               toseconds("06:00:00"), # 12 hours
+               toseconds("12:00:00"), # 24 hours
+               toseconds("24:00:00")  # 48 hours
+             );
+
+# action == 0 => immediate retry (!FIXME! needs to have some delay)
+# action == 1 => delayed retry
+# action == 2 => abort
+
+my $code = {
+  0 => { message=>"pppd detached",                              action=> 2 },
+  1 => { message=>"fatal error",                                action=> 2 },
+  2 => { message=>"options error",                              action=> 2 },
+  3 => { message=>"not setuid-root error",                      action=> 2 },
+  4 => { message=>"no kernel support for PPP",                  action=> 2 },
+  5 => { message=>"SIGINT or SIGTERM or SIGHUP",                action=> 1 },
+  6 => { message=>"Serial port locked",                         action=> 1 }, # should be 0
+  7 => { message=>"Serial port open error",                     action=> 1 },
+  8 => { message=>"Connect failed",                             action=> 1 },
+  9 => { message=>"Could not execute pty command",              action=> 1 },
+ 10 => { message=>"PPP negotiation failed",                     action=> 1 },
+ 11 => { message=>"Peer failed to authenticate",                action=> 1 },
+ 12 => { message=>"Link was idle",                              action=> 1 },
+ 13 => { message=>"Time limit exceeded",                        action=> 1 },
+ 14 => { message=>"call back not implemented",                  action=> 2 },
+ 15 => { message=>"peer not responding",                        action=> 1 },
+ 16 => { message=>"modem hang up",                              action=> 1 },
+ 17 => { message=>"Serial loopback detected",                   action=> 1 },
+ 18 => { message=>"Init script failed",                         action=> 1 },
+ 19 => { message=>"We failed to authenticate",                  action=> 1 },
+};
+
+while (1)
+{
+  $pppd_start_time=time;
+  syslog('info', 'restarting pppd');
+
+  # logging sometimes stopped working after ppp was running for
+  # some time. lets see if closing and reopening the log file helps...
+  closelog();
+
+  # run ppp
+  my $rc=system("pppd","-detach",@ARGV);
+
+  # reopon log file
+  openlog($program, 'cons,pid', 'daemon');
+
+  # calculate run time
+  $pppd_end_time=time;
+  $pppd_run_time=$pppd_end_time-$pppd_start_time;
+
+  my $pppd_code = ($? >> 8);
+  my $pppd_signal = $? & 127;
+  my $pppd_coredump = $? & 128;
+
+  $pppd_fail = 1;
+  if ($pppd_signal != 0) {
+    if ($pppd_coredump)
+    { syslog('err',"pppd died with signal $pppd_signal, coredump"); }
+    else
+    { syslog('err',"pppd died with signal $pppd_signal"); }
+  }
+  elsif ($pppd_coredump) {
+    syslog('err',"pppd died with coredump");
+  }
+  elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} == 0) {
+    syslog('err', "pppd returned: ".$code->{$pppd_code}{"message"}." ($pppd_code), immediate retry");
+    $pppd_fail = 0;
+  }
+  elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} == 1) {
+    syslog('err', "pppd returned: ".$code->{$pppd_code}{"message"}." ($pppd_code), delayed retry");
+    $pppd_fail = 1;
+  }
+  elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} >= 2) {
+    syslog('err', "pppd returned: ".$code->{$pppd_code}{"message"}." ($pppd_code), aborting");
+    exit(255);
+  }
+  elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} >= 2) {
+    syslog('err', "pppd returned: unknown error ($pppd_code), delayed retry");
+    $pppd_fail = 1;
+  }
+  # if it hasn't ran for at least an hour, then somthing went wrong
+  elsif ($pppd_run_time < toseconds("01:00:00")) {
+    syslog('err',"pppd session didn't last 1 hour, delayed retry");
+    $pppd_fail = 1;
+  }
+  else { $pppd_fail = 0; }
+
+  # if not failed, then reset delay.
+  if (!$pppd_fail) { $idelay = 0; }
+
+  # get next delay.
+  $delay = $delays[$idelay];
+
+  # log statistics.
+  syslog('info',"rc=".waitstat($rc)." runtime=".dseconds($pppd_run_time)." delay[$idelay]=".dseconds($delay)."");
+
+  # delay for desired time.
+  sleep($delay);
+
+  # increment delay for next time.
+  if (defined($delays[$idelay+1])) { $idelay++; }
+}
+
+closelog();
diff --git a/ap/app/pppd/scripts/callback b/ap/app/pppd/scripts/callback
new file mode 100644
index 0000000..1c3d3aa
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/chat-callback b/ap/app/pppd/scripts/chat-callback
new file mode 100644
index 0000000..d014d6a
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/chatchat/README b/ap/app/pppd/scripts/chatchat/README
new file mode 100644
index 0000000..88a4c69
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/chatchat/chatchat.c b/ap/app/pppd/scripts/chatchat/chatchat.c
new file mode 100644
index 0000000..4534fb9
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/ip-down.local.add b/ap/app/pppd/scripts/ip-down.local.add
new file mode 100644
index 0000000..b93590e
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/ip-up.local.add b/ap/app/pppd/scripts/ip-up.local.add
new file mode 100644
index 0000000..8017209
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/ipv6-down.sample b/ap/app/pppd/scripts/ipv6-down.sample
new file mode 100644
index 0000000..bf31574
--- /dev/null
+++ b/ap/app/pppd/scripts/ipv6-down.sample
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# This script is called with the following parameters:
+# interface tty speed local-address remote-address ipparam
+#
+
+
+# Kill the router advertisement daemon on this interface.
+# The killing procedure is copied from RedHat 6.0 initscripts.
+
+DEVICE="$1"
+
+PIDFILE="/var/run/radvd-$DEVICE.pid"
+
+[ -f "$PIDFILE" ] || exit 0
+
+PID="$(cat "$PIDFILE")"
+if [ "$PID" != "" ]; then
+    if ps h "$PID" >/dev/null 2>&1; then
+	kill -TERM "$PID"
+	usleep 10000
+	if ps h "$PID" >/dev/null 2>&1; then
+	    sleep 1
+	    if ps h "$PID" >/dev/null 2>&1; then
+		kill -KILL "$PID"
+	    fi
+	fi
+    fi
+fi
+
+rm -f "$PIDFILE"
diff --git a/ap/app/pppd/scripts/ipv6-up.sample b/ap/app/pppd/scripts/ipv6-up.sample
new file mode 100644
index 0000000..0974da9
--- /dev/null
+++ b/ap/app/pppd/scripts/ipv6-up.sample
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# This script is called with the following parameters:
+# interface tty speed local-address remote-address ipparam
+#
+
+
+# Start router advertisements on this link.
+# Based on radvd 0.5.0 behaviour
+
+DEVICE="$1"
+
+CFGFILE="/etc/radvd.conf-$DEVICE"
+PIDFILE="/var/run/radvd-$DEVICE.pid"
+EXEFILE="/usr/sbin/radvd"
+
+if [ -x "$EXEFILE" -a -f "$CFGFILE" ]; then
+    touch "$PIDFILE"
+    if [ ! -f "$PIDFILE" ]; then
+	echo "error: $PIDFILE is not a regular file. Aborting"
+	exit 0
+    fi
+
+    PID="$(cat "$PIDFILE")"
+    if [ -n "$PID" ]; then
+	ps h "$PID" >/dev/null 2>&1 && exit 0
+    fi
+
+    # radvd 0.5.0 doesn't write a pid-file so we do it here
+    # enabling debugging keeps radvd in foreground, putting it
+    # on background gives us the PID.
+    "$EXEFILE" -d 1 -C "$CFGFILE" &
+    echo $! >"$PIDFILE"
+fi
diff --git a/ap/app/pppd/scripts/options-rsh-loc b/ap/app/pppd/scripts/options-rsh-loc
new file mode 100644
index 0000000..b015b87
--- /dev/null
+++ b/ap/app/pppd/scripts/options-rsh-loc
@@ -0,0 +1 @@
+debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1460
diff --git a/ap/app/pppd/scripts/options-rsh-rem b/ap/app/pppd/scripts/options-rsh-rem
new file mode 100644
index 0000000..4b10bb9
--- /dev/null
+++ b/ap/app/pppd/scripts/options-rsh-rem
@@ -0,0 +1 @@
+notty debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1460
diff --git a/ap/app/pppd/scripts/options-ssh-loc b/ap/app/pppd/scripts/options-ssh-loc
new file mode 100644
index 0000000..add03d6
--- /dev/null
+++ b/ap/app/pppd/scripts/options-ssh-loc
@@ -0,0 +1 @@
+debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1400
diff --git a/ap/app/pppd/scripts/options-ssh-rem b/ap/app/pppd/scripts/options-ssh-rem
new file mode 100644
index 0000000..d690722
--- /dev/null
+++ b/ap/app/pppd/scripts/options-ssh-rem
@@ -0,0 +1 @@
+notty debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1400
diff --git a/ap/app/pppd/scripts/plog b/ap/app/pppd/scripts/plog
new file mode 100644
index 0000000..84d2c73
--- /dev/null
+++ b/ap/app/pppd/scripts/plog
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+if [ -s /var/log/ppp.log ]; then
+  exec tail "$@" /var/log/ppp.log
+else
+  exec tail "$@" /var/log/syslog | grep ' \(pppd\|chat\)\['
+fi
diff --git a/ap/app/pppd/scripts/poff b/ap/app/pppd/scripts/poff
new file mode 100644
index 0000000..659bfa7
--- /dev/null
+++ b/ap/app/pppd/scripts/poff
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+# $Id: poff,v 1.1 2002/11/24 23:30:44 etbe Exp $
+# Written by John Hasler <john@dhh.gt.org> and based on work 
+# by Phil Hands <phil@hands.com>.  Distributed under the GNU GPL
+
+if [ -x /usr/bin/kill ]; then
+  KILL="/usr/bin/kill"
+else
+  KILL="/bin/kill"
+fi
+SIG=TERM
+DONE="stopped"
+MODE=""
+
+usage ()
+{
+   cat <<!EOF!
+usage: $0 [option] [provider]
+options:
+  -r        Cause pppd to drop the line and redial.
+  -d        Toggle the state of pppd's debug option.
+  -c        Cause pppd to renegotiate compression.
+  -a        Stop all pppd's.  'provider' will be ignored.
+  -h        Print this help summary and exit.
+  -v        Print version and exit.
+  none      Stop pppd.
+
+Options may not be combined.
+
+If 'provider' is omitted pppd will be stopped or signalled if and only if
+there is exactly one running unless the '-a' option was given.  If
+'provider' is supplied the pppd controlling the connection to that
+provider will be stopped or signalled.
+!EOF!
+}
+
+# Get option.  If there are none replace the "?" that getopts puts in
+# FLAG on error with "null".
+getopts rdcavh FLAG
+if [ "$?" -ne 0 ]; then
+    FLAG="null"
+fi
+
+# Check for additional options.  Should be none.
+getopts :rdcavh DUMMY
+if [ "$?" -eq 0 ]; then
+    echo "$0: Illegal option -- ${OPTARG}."
+    exit 1
+fi
+
+case $FLAG in
+ "r") SIG=HUP;  DONE=signalled; shift ;;
+ "d") SIG=USR1; DONE=signalled; shift ;;
+ "c") SIG=USR2; DONE=signalled; shift ;;
+ "a") MODE="all"; shift ;;
+ "v") echo "$0$Revision: 1.1 $_TrickToPrint_RCS_Revision"; exit 0 ;;
+ "h") usage; exit 0 ;;
+ "?") exit 1;
+esac
+
+# Get the PIDs of all the pppds running.  Could also get these from
+# /var/run, but pppd doesn't create .pid files until ppp is up.
+PIDS=`pidof pppd`
+
+# poff is pointless if pppd isn't running.
+if test -z "$PIDS"; then
+    echo "$0: No pppd is running.  None ${DONE}."
+    exit 1
+fi
+
+# Find out how many pppd's are running.
+N=`echo "$PIDS" | wc -w`
+
+# If there are no arguments we can't do anything if there is more than one
+# pppd running.
+if test "$#" -eq 0 -a "$N" -gt 1 -a $FLAG != "a" ; then
+    echo "$0: More than one pppd running and no "-a" option and 
+no arguments supplied. Nothing ${DONE}."
+    exit 1
+fi
+
+# If either there are no arguments or '-a' was specified kill all the
+# pppd's.
+if test "$#" -eq 0 -o "$MODE" = "all" ; then
+    $KILL -$SIG $PIDS || {
+        echo "$0: $KILL failed.  None ${DONE}."
+        exit 1
+    }
+    exit 0
+fi
+
+# There is an argument, so kill the pppd started on that provider.
+PID=`ps axw | grep "[ /]pppd call $1 *\$" | awk '{print $1}'`
+if test -n "$PID" ; then
+    $KILL -$SIG $PID || {
+        echo "$0: $KILL failed.  None ${DONE}."
+        exit 1
+    }
+else
+   echo "$0: I could not find a pppd process for provider '$1'. None ${DONE}."
+   exit 1
+fi
+exit 0
diff --git a/ap/app/pppd/scripts/pon b/ap/app/pppd/scripts/pon
new file mode 100644
index 0000000..ef47518
--- /dev/null
+++ b/ap/app/pppd/scripts/pon
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+PPP_ON_BOOT=/etc/ppp/ppp_on_boot
+
+case "$1" in
+  -*) echo "
+Usage: pon [provider] [arguments]
+
+If pon is invoked without arguments, $PPP_ON_BOOT file will be
+run, presuming it exists and is executable. Otherwise, a PPP connection
+will be started using settings from /etc/ppp/peers/provider.
+If you specify one argument, a PPP connection will be started using
+settings from the appropriate file in the /etc/ppp/peers/ directory, and
+any additional arguments supplied will be passed as extra arguments to
+pppd.
+"
+      exit 0
+      ;;
+esac
+
+if [ -z "$1" -a -x "$PPP_ON_BOOT" ]; then
+  exec "$PPP_ON_BOOT"
+fi
+
+if [ -z "$1" -a ! -f /etc/ppp/peers/provider ]; then
+  echo "
+Please configure /etc/ppp/peers/provider or use a command line argument to
+use another file in /etc/ppp/peers/ directory.
+"
+  exit 1
+fi
+
+if [ "$1" -a ! -f "/etc/ppp/peers/$1" ]; then
+  echo "
+The file /etc/ppp/peers/$1 does not exist.
+"
+  exit 1
+fi
+
+exec /usr/sbin/pppd call ${@:-provider}
diff --git a/ap/app/pppd/scripts/pon.1 b/ap/app/pppd/scripts/pon.1
new file mode 100644
index 0000000..8c27f83
--- /dev/null
+++ b/ap/app/pppd/scripts/pon.1
@@ -0,0 +1,120 @@
+.\" This manual is published under the GPL.
+.\" All guidelines specified in the GPL apply here.
+.\" To get an ascii file:
+.\" groff -man -Tascii pon.1 > pon.txt
+.\"
+.TH PON 1 "July 2000" "Debian Project" "Debian PPPD"
+.SH NAME
+pon, poff, plog \- starts up, shuts down or lists the log of PPP connections
+.SH SYNOPSIS
+.B pon
+[ isp\-name [ options ] ]
+.br
+.B poff
+[ \-r ] [ \-d ] [ \-c ] [ \-a ] [ \-h ] [ isp\-name ]
+.br
+.B plog
+[ arguments ]
+.SH DESCRIPTION
+This manual page describes the \fBpon\fP, \fBplog\fP and \fBpoff\fP
+scripts, which allow users to control PPP connections.
+..
+.SS pon
+\fBpon\fP, invoked without arguments, runs the \fI/etc/ppp/ppp_on_boot\fP
+file, if it exists and is executable. Otherwise, a PPP connection will be
+started using configuration from \fI/etc/ppp/peers/provider\fP.
+This is the default behaviour unless an \fBisp\-name\fP argument is given.
+.PP
+For instance, to use ISP configuration "myisp" run:
+.IP
+pon myisp
+.PP
+\fBpon\fP will then use the options file \fI/etc/ppp/peers/myisp\fP.
+You can pass additional \fBoptions\fP after the ISP name, too.
+\fBpon\fP can be used to run multiple, simultaneous PPP connections.
+..
+.SS poff
+\fBpoff\fP closes a PPP connection. If more than one PPP connection exists,
+the one named in the argument to \fBpoff\fP will be killed, e.g.
+.IP
+poff myprovider2
+.PP
+will terminate the connection to myprovider2, and leave the PPP connections
+to e.g. "myprovider1" or "myprovider3" up and running.
+.PP
+\fBpoff\fP takes the following command line options:
+.RS
+.TP
+.B "\-r"
+causes the connection to be redialed after it is dropped.
+.TP
+.B "\-d"
+toggles the state of pppd's debug option.
+.TP
+.B "\-c"
+causes
+.BR pppd (8)
+to renegotiate compression.
+.TP
+.B "\-a"
+stops all running ppp connections. If the argument \fBisp\-name\fP
+is given it will be ignored.
+.TP
+.B "\-h"
+displays help information.
+.TP
+.B "\-v"
+prints the version and exits.
+.PP
+If no argument is given, \fBpoff\fP will stop or signal pppd if and only
+if there is exactly one running. If more than one connection is active,
+it will exit with an error code of 1.
+..
+.SS plog
+\fBplog\fP shows you the last few lines of \fI/var/log/ppp.log\fP. If that
+file doesn't exist, it shows you the last few lines of your
+\fI/var/log/syslog\fP file, but excluding the lines not generated by pppd.
+This script makes use of the
+.BR tail (1)
+command, so arguments that can be passed to
+.BR tail (1)
+can also be passed to \fBplog\fP.
+.PP
+Note: the \fBplog\fP script can only be used by root or another system
+administrator in group "adm", due to security reasons. Also, to have all
+pppd-generated information in one logfile, that plog can show, you need the
+following line in your \fI/etc/syslog.conf\fP file:
+.PP
+local2.*		\-/var/log/ppp.log
+.RE
+.SH FILES
+.TP
+.I /etc/ppp/options
+PPPd system options file.
+.TP
+.I /etc/ppp/pap\-secrets
+System PAP passwords file.
+.TP
+.I /etc/ppp/chap\-secrets
+System CHAP passwords file.
+.TP
+.I /etc/ppp/peers/
+Directory holding the peer options files. The default file is called
+\fIprovider\fP.
+.TP
+.I /etc/chatscripts/provider
+The chat script invoked from the default \fI/etc/ppp/peers/provider\fP.
+.TP
+.I /var/log/ppp.log
+The default PPP log file.
+.SH AUTHORS
+The p-commands were written by Christoph Lameter <clameter@debian.org>.
+Updated and revised by Philip Hands <phil@hands.com>.
+.br
+This manual was written by Othmar Pasteka <othmar@tron.at>. Modified
+by Rob Levin <lilo@openprojects.net>, with some extensions taken from
+the old p-commands manual written by John Hasler <jhasler@debian.org>.
+.SH "SEE ALSO"
+.BR pppd (8),
+.BR chat (8),
+.BR tail (1).
diff --git a/ap/app/pppd/scripts/ppp-off b/ap/app/pppd/scripts/ppp-off
new file mode 100644
index 0000000..a22b5ea
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/ppp-on b/ap/app/pppd/scripts/ppp-on
new file mode 100644
index 0000000..ab79db4
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/ppp-on-dialer b/ap/app/pppd/scripts/ppp-on-dialer
new file mode 100644
index 0000000..7d66765
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/ppp-on-rsh b/ap/app/pppd/scripts/ppp-on-rsh
new file mode 100644
index 0000000..30a50db
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/ppp-on-ssh b/ap/app/pppd/scripts/ppp-on-ssh
new file mode 100644
index 0000000..0e41aca
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/radiusclient/dictionary b/ap/app/pppd/scripts/radiusclient/dictionary
new file mode 100644
index 0000000..8172890
--- /dev/null
+++ b/ap/app/pppd/scripts/radiusclient/dictionary
@@ -0,0 +1,242 @@
+#
+# Updated 97/06/13 to livingston-radius-2.01 miquels@cistron.nl
+#
+#	This file contains dictionary translations for parsing
+#	requests and generating responses.  All transactions are
+#	composed of Attribute/Value Pairs.  The value of each attribute
+#	is specified as one of 4 data types.  Valid data types are:
+#
+#	string - 0-253 octets
+#	ipaddr - 4 octets in network byte order
+#	integer - 32 bit value in big endian order (high byte first)
+#	date - 32 bit value in big endian order - seconds since
+#					00:00:00 GMT,  Jan.  1,  1970
+#
+#	Enumerated values are stored in the user file with dictionary
+#	VALUE translations for easy administration.
+#
+#	Example:
+#
+#	ATTRIBUTE	  VALUE
+#	---------------   -----
+#	Framed-Protocol = PPP
+#	7		= 1	(integer encoding)
+#
+
+INCLUDE /etc/radiusclient/dictionary.ms
+INCLUDE /etc/radiusclient/dictionary.sg
+#
+#	Following are the proper new names. Use these.
+#
+ATTRIBUTE	User-Name		1	string
+ATTRIBUTE	Password		2	string
+ATTRIBUTE	CHAP-Password		3	string
+ATTRIBUTE	NAS-IP-Address		4	ipaddr
+ATTRIBUTE	NAS-Port-Id		5	integer
+ATTRIBUTE	Service-Type		6	integer
+ATTRIBUTE	Framed-Protocol		7	integer
+ATTRIBUTE	Framed-IP-Address	8	ipaddr
+ATTRIBUTE	Framed-IP-Netmask	9	ipaddr
+ATTRIBUTE	Framed-Routing		10	integer
+ATTRIBUTE	Filter-Id		11	string
+ATTRIBUTE	Framed-MTU		12	integer
+ATTRIBUTE	Framed-Compression	13	integer
+ATTRIBUTE	Login-IP-Host		14	ipaddr
+ATTRIBUTE	Login-Service		15	integer
+ATTRIBUTE	Login-TCP-Port		16	integer
+ATTRIBUTE	Reply-Message		18	string
+ATTRIBUTE	Callback-Number		19	string
+ATTRIBUTE	Callback-Id		20	string
+ATTRIBUTE	Framed-Route		22	string
+ATTRIBUTE	Framed-IPX-Network	23	ipaddr
+ATTRIBUTE	State			24	string
+ATTRIBUTE	Class			25	string
+ATTRIBUTE	Vendor-Specific		26	string
+ATTRIBUTE	Session-Timeout		27	integer
+ATTRIBUTE	Idle-Timeout		28	integer
+ATTRIBUTE	Termination-Action	29	integer
+ATTRIBUTE	Called-Station-Id	30	string
+ATTRIBUTE	Calling-Station-Id	31	string
+ATTRIBUTE	NAS-Identifier		32	string
+ATTRIBUTE	Proxy-State		33	string
+ATTRIBUTE	Login-LAT-Service	34	string
+ATTRIBUTE	Login-LAT-Node		35	string
+ATTRIBUTE	Login-LAT-Group		36	string
+ATTRIBUTE	Framed-AppleTalk-Link	37	integer
+ATTRIBUTE	Framed-AppleTalk-Network	38	integer
+ATTRIBUTE	Framed-AppleTalk-Zone	39	string
+ATTRIBUTE	Acct-Status-Type	40	integer
+ATTRIBUTE	Acct-Delay-Time		41	integer
+ATTRIBUTE	Acct-Input-Octets	42	integer
+ATTRIBUTE	Acct-Output-Octets	43	integer
+ATTRIBUTE	Acct-Session-Id		44	string
+ATTRIBUTE	Acct-Authentic		45	integer
+ATTRIBUTE	Acct-Session-Time	46	integer
+ATTRIBUTE	Acct-Input-Packets	47	integer
+ATTRIBUTE	Acct-Output-Packets	48	integer
+ATTRIBUTE	Acct-Terminate-Cause	49	integer
+ATTRIBUTE	Acct-Multi-Session-Id	50	string
+ATTRIBUTE	Acct-Link-Count		51	integer
+ATTRIBUTE	Event-Timestamp		55	integer
+ATTRIBUTE	CHAP-Challenge		60	string
+ATTRIBUTE	NAS-Port-Type		61	integer
+ATTRIBUTE	Port-Limit		62	integer
+ATTRIBUTE	Login-LAT-Port		63	integer
+ATTRIBUTE	Connect-Info		77	string
+
+#
+#	RFC3162 IPv6 attributes
+#
+ATTRIBUTE	NAS-IPv6-Address	95	string
+ATTRIBUTE	Framed-Interface-Id	96	string
+ATTRIBUTE	Framed-IPv6-Prefix	97	string
+ATTRIBUTE	Login-IPv6-Host		98	string
+ATTRIBUTE	Framed-IPv6-Route	99	string
+ATTRIBUTE	Framed-IPv6-Pool	100	string
+
+#
+#	Experimental Non Protocol Attributes used by Cistron-Radiusd
+#
+ATTRIBUTE	Huntgroup-Name		221	string
+ATTRIBUTE	User-Category		1029	string
+ATTRIBUTE	Group-Name		1030	string
+ATTRIBUTE	Simultaneous-Use	1034	integer
+ATTRIBUTE	Strip-User-Name		1035	integer
+ATTRIBUTE	Fall-Through		1036	integer
+ATTRIBUTE	Add-Port-To-IP-Address	1037	integer
+ATTRIBUTE	Exec-Program		1038	string
+ATTRIBUTE	Exec-Program-Wait	1039	string
+ATTRIBUTE	Hint			1040	string
+
+#
+#	Non-Protocol Attributes
+#	These attributes are used internally by the server
+#
+ATTRIBUTE	Expiration		  21	date
+ATTRIBUTE	Auth-Type		1000	integer
+ATTRIBUTE	Menu			1001	string
+ATTRIBUTE	Termination-Menu	1002	string
+ATTRIBUTE	Prefix			1003	string
+ATTRIBUTE	Suffix			1004	string
+ATTRIBUTE	Group			1005	string
+ATTRIBUTE	Crypt-Password		1006	string
+ATTRIBUTE	Connect-Rate		1007	integer
+
+#
+#	Integer Translations
+#
+
+#	User Types
+
+VALUE		Service-Type		Login-User		1
+VALUE		Service-Type		Framed-User		2
+VALUE		Service-Type		Callback-Login-User	3
+VALUE		Service-Type		Callback-Framed-User	4
+VALUE		Service-Type		Outbound-User		5
+VALUE		Service-Type		Administrative-User	6
+VALUE		Service-Type		NAS-Prompt-User		7
+
+#	Framed Protocols
+
+VALUE		Framed-Protocol		PPP			1
+VALUE		Framed-Protocol		SLIP			2
+
+#	Framed Routing Values
+
+VALUE		Framed-Routing		None			0
+VALUE		Framed-Routing		Broadcast		1
+VALUE		Framed-Routing		Listen			2
+VALUE		Framed-Routing		Broadcast-Listen	3
+
+#	Framed Compression Types
+
+VALUE		Framed-Compression	None			0
+VALUE		Framed-Compression	Van-Jacobson-TCP-IP	1
+
+#	Login Services
+
+VALUE		Login-Service		Telnet			0
+VALUE		Login-Service		Rlogin			1
+VALUE		Login-Service		TCP-Clear		2
+VALUE		Login-Service		PortMaster		3
+
+#	Status Types
+
+VALUE		Acct-Status-Type	Start			1
+VALUE		Acct-Status-Type	Stop			2
+VALUE		Acct-Status-Type	Alive			3
+VALUE		Acct-Status-Type	Accounting-On		7
+VALUE		Acct-Status-Type	Accounting-Off		8
+
+#	Authentication Types
+
+VALUE		Acct-Authentic		RADIUS			1
+VALUE		Acct-Authentic		Local			2
+VALUE		Acct-Authentic		PowerLink128		100
+
+#	Termination Options
+
+VALUE		Termination-Action	Default			0
+VALUE		Termination-Action	RADIUS-Request		1
+
+#	NAS Port Types, available in 3.3.1 and later
+
+VALUE		NAS-Port-Type		Async			0
+VALUE		NAS-Port-Type		Sync			1
+VALUE		NAS-Port-Type		ISDN			2
+VALUE		NAS-Port-Type		ISDN-V120		3
+VALUE		NAS-Port-Type		ISDN-V110		4
+
+#	Acct Terminate Causes, available in 3.3.2 and later
+
+VALUE           Acct-Terminate-Cause    User-Request            1
+VALUE           Acct-Terminate-Cause    Lost-Carrier            2
+VALUE           Acct-Terminate-Cause    Lost-Service            3
+VALUE           Acct-Terminate-Cause    Idle-Timeout            4
+VALUE           Acct-Terminate-Cause    Session-Timeout         5
+VALUE           Acct-Terminate-Cause    Admin-Reset             6
+VALUE           Acct-Terminate-Cause    Admin-Reboot            7
+VALUE           Acct-Terminate-Cause    Port-Error              8
+VALUE           Acct-Terminate-Cause    NAS-Error               9
+VALUE           Acct-Terminate-Cause    NAS-Request             10
+VALUE           Acct-Terminate-Cause    NAS-Reboot              11
+VALUE           Acct-Terminate-Cause    Port-Unneeded           12
+VALUE           Acct-Terminate-Cause    Port-Preempted          13
+VALUE           Acct-Terminate-Cause    Port-Suspended          14
+VALUE           Acct-Terminate-Cause    Service-Unavailable     15
+VALUE           Acct-Terminate-Cause    Callback                16
+VALUE           Acct-Terminate-Cause    User-Error              17
+VALUE           Acct-Terminate-Cause    Host-Request            18
+
+#
+#	Non-Protocol Integer Translations
+#
+
+VALUE		Auth-Type		Local			0
+VALUE		Auth-Type		System			1
+VALUE		Auth-Type		SecurID			2
+VALUE		Auth-Type		Crypt-Local		3
+VALUE		Auth-Type		Reject			4
+
+#
+#	Cistron extensions
+#
+VALUE		Auth-Type		Pam			253
+VALUE		Auth-Type		Accept			254
+
+#
+#	Experimental Non-Protocol Integer Translations for Cistron-Radiusd
+#
+VALUE		Fall-Through		No			0
+VALUE		Fall-Through		Yes			1
+VALUE		Add-Port-To-IP-Address	No			0
+VALUE		Add-Port-To-IP-Address	Yes			1
+
+#
+#	Configuration Values
+#	uncomment these two lines to turn account expiration on
+#
+
+#VALUE		Server-Config		Password-Expiration	30
+#VALUE		Server-Config		Password-Warning	5
+
diff --git a/ap/app/pppd/scripts/radiusclient/dictionary.ms b/ap/app/pppd/scripts/radiusclient/dictionary.ms
new file mode 100644
index 0000000..c8b0832
--- /dev/null
+++ b/ap/app/pppd/scripts/radiusclient/dictionary.ms
@@ -0,0 +1,81 @@
+#
+#       Microsoft's VSA's, from RFC 2548
+#
+#       $Id: dictionary.ms,v 1.1 2008-01-29 04:10:56 kwilson Exp $
+#
+
+VENDOR          Microsoft       311     Microsoft
+
+ATTRIBUTE       MS-CHAP-Response        1       string  Microsoft
+ATTRIBUTE       MS-CHAP-Error           2       string  Microsoft
+ATTRIBUTE       MS-CHAP-CPW-1           3       string  Microsoft
+ATTRIBUTE       MS-CHAP-CPW-2           4       string  Microsoft
+ATTRIBUTE       MS-CHAP-LM-Enc-PW       5       string  Microsoft
+ATTRIBUTE       MS-CHAP-NT-Enc-PW       6       string  Microsoft
+ATTRIBUTE       MS-MPPE-Encryption-Policy 7     string  Microsoft
+# This is referred to as both singular and plural in the RFC.
+# Plural seems to make more sense.
+ATTRIBUTE       MS-MPPE-Encryption-Type 8       string  Microsoft
+ATTRIBUTE       MS-MPPE-Encryption-Types  8     string  Microsoft
+ATTRIBUTE       MS-RAS-Vendor           9       integer Microsoft
+ATTRIBUTE       MS-CHAP-Domain          10      string  Microsoft
+ATTRIBUTE       MS-CHAP-Challenge       11      string  Microsoft
+ATTRIBUTE       MS-CHAP-MPPE-Keys       12      string  Microsoft encrypt=1
+ATTRIBUTE       MS-BAP-Usage            13      integer Microsoft
+ATTRIBUTE       MS-Link-Utilization-Threshold 14 integer        Microsoft
+ATTRIBUTE       MS-Link-Drop-Time-Limit 15      integer Microsoft
+ATTRIBUTE       MS-MPPE-Send-Key        16      string  Microsoft
+ATTRIBUTE       MS-MPPE-Recv-Key        17      string  Microsoft
+ATTRIBUTE       MS-RAS-Version          18      string  Microsoft
+ATTRIBUTE       MS-Old-ARAP-Password    19      string  Microsoft
+ATTRIBUTE       MS-New-ARAP-Password    20      string  Microsoft
+ATTRIBUTE       MS-ARAP-PW-Change-Reason 21     integer Microsoft
+
+ATTRIBUTE       MS-Filter               22      string  Microsoft
+ATTRIBUTE       MS-Acct-Auth-Type       23      integer Microsoft
+ATTRIBUTE       MS-Acct-EAP-Type        24      integer Microsoft
+
+ATTRIBUTE       MS-CHAP2-Response       25      string  Microsoft
+ATTRIBUTE       MS-CHAP2-Success        26      string  Microsoft
+ATTRIBUTE       MS-CHAP2-CPW            27      string  Microsoft
+
+ATTRIBUTE       MS-Primary-DNS-Server   28      ipaddr
+ATTRIBUTE       MS-Secondary-DNS-Server 29      ipaddr
+ATTRIBUTE       MS-Primary-NBNS-Server  30      ipaddr
+ATTRIBUTE       MS-Secondary-NBNS-Server 31     ipaddr
+
+#ATTRIBUTE      MS-ARAP-Challenge       33      string  Microsoft
+
+#
+#       Integer Translations
+#
+
+#       MS-BAP-Usage Values
+
+VALUE           MS-BAP-Usage            Not-Allowed     0
+VALUE           MS-BAP-Usage            Allowed         1
+VALUE           MS-BAP-Usage            Required        2
+
+#       MS-ARAP-Password-Change-Reason Values
+
+VALUE   MS-ARAP-PW-Change-Reason        Just-Change-Password            1
+VALUE   MS-ARAP-PW-Change-Reason        Expired-Password                2
+VALUE   MS-ARAP-PW-Change-Reason        Admin-Requires-Password-Change  3
+VALUE   MS-ARAP-PW-Change-Reason        Password-Too-Short              4
+
+#       MS-Acct-Auth-Type Values
+
+VALUE           MS-Acct-Auth-Type       PAP             1
+VALUE           MS-Acct-Auth-Type       CHAP            2
+VALUE           MS-Acct-Auth-Type       MS-CHAP-1       3
+VALUE           MS-Acct-Auth-Type       MS-CHAP-2       4
+VALUE           MS-Acct-Auth-Type       EAP             5
+
+#       MS-Acct-EAP-Type Values
+
+VALUE           MS-Acct-EAP-Type        MD5             4
+VALUE           MS-Acct-EAP-Type        OTP             5
+VALUE           MS-Acct-EAP-Type        Generic-Token-Card      6
+VALUE           MS-Acct-EAP-Type        TLS             13
+
+END-VENDOR Microsoft
diff --git a/ap/app/pppd/scripts/radiusclient/dictionary.sg b/ap/app/pppd/scripts/radiusclient/dictionary.sg
new file mode 100644
index 0000000..bdb74f6
--- /dev/null
+++ b/ap/app/pppd/scripts/radiusclient/dictionary.sg
@@ -0,0 +1,11 @@
+#
+#       Secure VSAs
+#
+#       $I$
+#
+
+VENDOR          Secure                  1573     Secure
+
+ATTRIBUTE       SG-GroupName            1        string  Secure
+
+END-VENDOR Secure
diff --git a/ap/app/pppd/scripts/redialer b/ap/app/pppd/scripts/redialer
new file mode 100644
index 0000000..5bbde4e
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/scripts/secure-card b/ap/app/pppd/scripts/secure-card
new file mode 100644
index 0000000..0002365
--- /dev/null
+++ b/ap/app/pppd/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/ttyS3 > /dev/ttyS3"
+
+#
+# These are the parameters for the program.
+#
+set user      Pxxxxxx
+set password  xxxxxxx 
+set modem     /dev/ttyS3
+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/ttyS3 19200 192.111.187.215: \
+	crtscts modem defaultroute debug 
diff --git a/ap/app/pppd/solaris/Makedefs b/ap/app/pppd/solaris/Makedefs
new file mode 100644
index 0000000..967e292
--- /dev/null
+++ b/ap/app/pppd/solaris/Makedefs
@@ -0,0 +1,14 @@
+#
+# defines common to several Makefiles
+#
+
+INSTALL= /usr/sbin/install
+
+BINDIR = @DESTDIR@/bin
+MANDIR = @DESTDIR@/man
+ETCDIR = @SYSCONF@/ppp
+
+CC = /opt/SUNWspro/bin/cc
+COPTS = -O -Xa
+
+LD = /usr/ccs/bin/ld
diff --git a/ap/app/pppd/solaris/Makedefs.gcc b/ap/app/pppd/solaris/Makedefs.gcc
new file mode 100644
index 0000000..ba9ce01
--- /dev/null
+++ b/ap/app/pppd/solaris/Makedefs.gcc
@@ -0,0 +1,14 @@
+#
+# defines common to several Makefiles
+#
+
+INSTALL= /usr/sbin/install
+
+BINDIR = @DESTDIR@/bin
+MANDIR = @DESTDIR@/man
+ETCDIR = @SYSCONF@/ppp
+
+CC = gcc
+COPTS = -O2
+
+LD = /usr/ccs/bin/ld
diff --git a/ap/app/pppd/solaris/Makedefs.sol2 b/ap/app/pppd/solaris/Makedefs.sol2
new file mode 100644
index 0000000..a0417bd
--- /dev/null
+++ b/ap/app/pppd/solaris/Makedefs.sol2
@@ -0,0 +1,59 @@
+#
+# Generic make definitions for Solaris 2
+#
+# $Id: Makedefs.sol2,v 1.2 2002/09/07 05:15:25 carlsonj Exp $
+#
+
+include ../Makedefs.com
+
+CPPFLAGS	= -D_KERNEL -DSVR4 -DSOL2 -DPRIOQ -DDEBUG -I../include
+CFLAGS		= $(CPPFLAGS) $(COPTS)
+
+# lint-specific variables
+LINT            = lint
+LINT_OPT_32     =
+LINT_OPT_64     = -Xarch=v9 -errchk=longptr64
+
+LINT_32     	=
+LINT_32     	+= -erroff=E_BAD_PTR_CAST_ALIGN
+LINT_32     	+= -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED
+LINT_32     	+= -erroff=E_SUSPICIOUS_COMPARISON
+LINT_32     	+= -erroff=E_CAST_UINT_TO_SIGNED_INT
+LINT_32     	+= -erroff=E_PASS_UINT_TO_SIGNED_INT
+LINT_32     	+= -erroff=E_INVALID_ANNOTATION_NAME
+LINT_32     	+= -erroff=E_FUNC_ARG_UNUSED
+# This might be needed, but zlib.c and vjcompress.c will squawk 
+# when not ignored
+LINT_32		+= -erroff=E_CASE_FALLTHRU
+LINT_32		+= -erroff=E_RET_INT_IMPLICITLY
+LINT_32		+= -erroff=E_FUNC_NO_RET_VAL
+# Some STREAMS macros will be noisy too when this isn't ignored
+LINT_32		+= -erroff=E_CONSTANT_CONDITION
+LINT_32		+= -erroff=E_CONST_EXPR
+
+# Extra noise suppressant for 64-bit
+EXTRA_OFF 	=
+EXTRA_OFF 	+= -erroff=E_CAST_INT_TO_SMALL_INT
+EXTRA_OFF 	+= -erroff=E_CAST_INT_CONST_TO_SMALL_INT
+EXTRA_OFF 	+= -erroff=E_CAST_TO_PTR_FROM_INT
+EXTRA_OFF 	+= -erroff=E_ASSIGN_INT_TO_SMALL_INT
+EXTRA_OFF 	+= -erroff=E_ASSIGN_INT_FROM_BIG_CONST
+EXTRA_OFF 	+= -erroff=E_CONST_PROMOTED_UNSIGNED_LL
+EXTRA_OFF 	+= -erroff=E_CONST_PROMOTED_LONG_LONG
+EXTRA_OFF 	+= -erroff=E_CONST_TRUNCATED_BY_ASSIGN
+EXTRA_OFF 	+= -erroff=E_PASS_INT_FROM_BIG_CONST
+EXTRA_OFF 	+= -erroff=E_COMP_INT_WITH_LARGE_INT
+EXTRA_OFF 	+= -erroff=E_ASSIGN_UINT_TO_SIGNED_INT
+EXTRA_OFF 	+= -erroff=E_ASSIGN_NARROW_CONV
+EXTRA_OFF 	+= -erroff=E_PASS_INT_TO_SMALL_INT
+EXTRA_OFF 	+= -erroff=E_PTR_CONV_LOSES_BITS
+
+LINT_64     	= $(LINT_32)
+LINT_64     	+= $(EXTRA_OFF)
+
+LINTFLAGS64     = -Xa -nsxmuF -errtags=yes $(LINT_OPT_64) $(LINT_64)
+LINT64          = $(LINT) -c $(LINTFLAGS64) $(CPPFLAGS)
+
+LINTFLAGS32     = -Xa -nsxmuF -errtags=yes $(LINT_OPT_32) $(LINT_32)
+LINT32          = $(LINT) -c $(LINTFLAGS32) $(CPPFLAGS)
+
diff --git a/ap/app/pppd/solaris/Makefile.sol2 b/ap/app/pppd/solaris/Makefile.sol2
new file mode 100644
index 0000000..c3b2577
--- /dev/null
+++ b/ap/app/pppd/solaris/Makefile.sol2
@@ -0,0 +1,68 @@
+#
+# Makefile for STREAMS modules for Solaris 2.
+#
+# $Id: Makefile.sol2,v 1.3 2004/11/15 00:57:54 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+COPTS += -xO2 -xspace -W0,-Lt
+
+COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \
+	ppp_comp_mod.o
+
+all:	ppp ppp_ahdl ppp_comp
+
+ppp:	ppp.o ppp_mod.o
+	$(LD) -r -o $@ ppp.o ppp_mod.o
+	chmod +x $@
+
+ppp_ahdl: ppp_ahdlc.o ppp_ahdlc_mod.o
+	$(LD) -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o
+	chmod +x $@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $@ $(COMP_OBJS)
+	chmod +x $@
+
+bsd-comp.o:	../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $?
+deflate.o:	../modules/deflate.c
+	$(CC) $(CFLAGS) -c $?
+ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $?
+ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $?
+ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $?
+ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $?
+ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $?
+ppp_comp_mod.o:	ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $?
+vjcompress.o:	../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $?
+zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $?
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT32) $(SRCS)
+
+clean:
+	rm -f ppp ppp_comp ppp_ahdl *.o *~ core
+	rm -f *.ln
diff --git a/ap/app/pppd/solaris/Makefile.sol2-64 b/ap/app/pppd/solaris/Makefile.sol2-64
new file mode 100644
index 0000000..9d614ae
--- /dev/null
+++ b/ap/app/pppd/solaris/Makefile.sol2-64
@@ -0,0 +1,88 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2.
+#
+# $Id: Makefile.sol2-64,v 1.3 2004/11/15 00:57:54 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+# Sun's cc flag for LP64 compilation / linkage
+COPTS 		+= -xchip=ultra -xarch=v9 -Wc,-xcode=abs32 -Wc,-Qiselect-regsym=0 -xO3 -xspace -W0,-Lt
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR		= sparcv9
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE	= Makefile.sol2
+
+COMP_OBJS	= $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+		$(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+		$(LP64DIR)/ppp_comp_mod.o
+
+all:	std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+	$(MAKE) -f $(STD_MAKE) all
+
+ppp:	$(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \
+		$(LP64DIR)/ppp_ahdlc_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+	chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+	mkdir -m 755 -p $@
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+	cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT64) $(SRCS)
+
+lint-32:
+	$(LINT32) $(SRCS)
+
+clean:
+	$(MAKE) -f $(STD_MAKE) clean
+	rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff --git a/ap/app/pppd/solaris/Makefile.sol2-64x b/ap/app/pppd/solaris/Makefile.sol2-64x
new file mode 100644
index 0000000..83f59ad
--- /dev/null
+++ b/ap/app/pppd/solaris/Makefile.sol2-64x
@@ -0,0 +1,89 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2 on x64
+#
+# $Id: Makefile.sol2-64x,v 1.1 2005/06/26 23:53:17 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+# Sun's cc flag for LP64 compilation / linkage
+COPTS 		+= -errwarn -xtarget=opteron -xarch=amd64 -xmodel=kernel \
+		   -Ui386 -U__i386 -D__amd64 -xO2
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR		= amd64
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE	= Makefile.sol2
+
+COMP_OBJS	= $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+		$(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+		$(LP64DIR)/ppp_comp_mod.o
+
+all:	std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+	$(MAKE) -f $(STD_MAKE) all
+
+ppp:	$(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \
+		$(LP64DIR)/ppp_ahdlc_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+	chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+	mkdir -m 755 -p $@
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+	cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT64) $(SRCS)
+
+lint-32:
+	$(LINT32) $(SRCS)
+
+clean:
+	$(MAKE) -f $(STD_MAKE) clean
+	rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff --git a/ap/app/pppd/solaris/Makefile.sol2gcc b/ap/app/pppd/solaris/Makefile.sol2gcc
new file mode 100644
index 0000000..7d7c943
--- /dev/null
+++ b/ap/app/pppd/solaris/Makefile.sol2gcc
@@ -0,0 +1,68 @@
+#
+# Makefile for STREAMS modules for Solaris 2.
+#
+# $Id: Makefile.sol2gcc,v 1.4 2004/11/15 00:57:54 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+COPTS += -fno-builtin
+
+COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \
+	ppp_comp_mod.o
+
+all:	ppp ppp_ahdl ppp_comp
+
+ppp:	ppp.o ppp_mod.o
+	$(LD) -r -o $@ ppp.o ppp_mod.o
+	chmod +x $@
+
+ppp_ahdl: ppp_ahdlc.o ppp_ahdlc_mod.o
+	$(LD) -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o
+	chmod +x $@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $@ $(COMP_OBJS)
+	chmod +x $@
+
+bsd-comp.o:	../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $?
+deflate.o:	../modules/deflate.c
+	$(CC) $(CFLAGS) -c $?
+ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $?
+ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $?
+ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $?
+ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $?
+ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $?
+ppp_comp_mod.o:	ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $?
+vjcompress.o:	../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $?
+zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $?
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT32) $(SRCS)
+
+clean:
+	rm -f ppp ppp_comp ppp_ahdl *.o *~ core
+	rm -f *.ln
diff --git a/ap/app/pppd/solaris/Makefile.sol2gcc-64 b/ap/app/pppd/solaris/Makefile.sol2gcc-64
new file mode 100644
index 0000000..2844a0d
--- /dev/null
+++ b/ap/app/pppd/solaris/Makefile.sol2gcc-64
@@ -0,0 +1,88 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2.
+#
+# $Id: Makefile.sol2gcc-64,v 1.3 2004/11/15 00:57:54 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+# gcc flags for LP64 compilation / linkage
+COPTS           += -mcpu=v9 -m64 -mcmodel=medlow -mstack-bias -fno-builtin
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR		= sparcv9
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE	= Makefile.sol2gcc
+
+COMP_OBJS	= $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+		$(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+		$(LP64DIR)/ppp_comp_mod.o
+
+all:	std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+	$(MAKE) -f $(STD_MAKE) all
+
+ppp:	$(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \
+		$(LP64DIR)/ppp_ahdlc_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+	chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+	mkdir -m 755 -p $@
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+	cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT64) $(SRCS)
+
+lint-32:
+	$(LINT32) $(SRCS)
+
+clean:
+	$(MAKE) -f $(STD_MAKE) clean
+	rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff --git a/ap/app/pppd/solaris/Makefile.sol2gcc-64x b/ap/app/pppd/solaris/Makefile.sol2gcc-64x
new file mode 100644
index 0000000..0eaef7d
--- /dev/null
+++ b/ap/app/pppd/solaris/Makefile.sol2gcc-64x
@@ -0,0 +1,94 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2 on x64 with gcc.
+#
+# $Id: Makefile.sol2gcc-64x,v 1.1 2005/06/26 23:53:17 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+# gcc flags for LP64 compilation / linkage
+COPTS           += -finline -fno-inline-functions -fno-builtin -fno-asm \
+		   -nodefaultlibs -D__sun -m64 -mtune=opteron -Ui386 \
+		   -U__i386 -fno-strict-aliasing -fno-unit-at-a-time \
+		   -fno-optimize-sibling-calls -O2 -D_ASM_INLINES \
+		   -ffreestanding -mcmodel=kernel -mno-red-zone -gdwarf-2 \
+		   -std=gnu89 -D_KERNEL -D_SYSCALL32 -D_SYSCALL32_IMPL \
+		   -D_ELF64 -Dsun -D__sun -D__SVR4
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR		= amd64
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE	= Makefile.sol2gcc
+
+COMP_OBJS	= $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+		$(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+		$(LP64DIR)/ppp_comp_mod.o
+
+all:	std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+	$(MAKE) -f $(STD_MAKE) all
+
+ppp:	$(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \
+		$(LP64DIR)/ppp_ahdlc_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+	chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+	mkdir -m 755 -p $@
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+	cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT64) $(SRCS)
+
+lint-32:
+	$(LINT32) $(SRCS)
+
+clean:
+	$(MAKE) -f $(STD_MAKE) clean
+	rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff --git a/ap/app/pppd/solaris/Makefile.top b/ap/app/pppd/solaris/Makefile.top
new file mode 100644
index 0000000..73edccd
--- /dev/null
+++ b/ap/app/pppd/solaris/Makefile.top
@@ -0,0 +1,55 @@
+#
+# ppp top level makefile for SVR4 and Solaris 2
+#
+# $Id: Makefile.top,v 1.3 2004/11/01 09:31:07 paulus Exp $
+#
+
+include Makedefs.com
+
+all:
+	cd chat; $(MAKE) all
+	cd pppd; $(MAKE) all
+	cd pppstats; $(MAKE) all
+	cd pppdump; $(MAKE) all
+	cd solaris; $(MAKE) all
+
+install: $(BINDIR) $(MANDIR)/man8 install-progs
+
+install-progs:
+	cd chat; $(MAKE) install
+	cd pppd; $(MAKE) install
+	cd pppstats; $(MAKE) install
+	cd pppdump; $(MAKE) install
+
+install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
+	$(ETCDIR)/chap-secrets
+
+install-modules:
+	cd solaris; $(MAKE) install
+
+$(ETCDIR)/options:
+	cp etc.ppp/options $@
+	chmod go-w $@
+$(ETCDIR)/pap-secrets:
+	$(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/pap-secrets
+$(ETCDIR)/chap-secrets:
+	$(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/chap-secrets
+
+$(BINDIR):
+	mkdir -m 755 -p $@
+$(MANDIR)/man8:
+	mkdir -m 755 -p $@
+$(ETCDIR):
+	mkdir -m 755 -p $@
+
+clean:
+	rm -f *~
+	cd chat; $(MAKE) clean
+	cd pppd; $(MAKE) clean
+	cd pppstats; $(MAKE) clean
+	cd pppdump; $(MAKE) clean
+	cd solaris; $(MAKE) clean
+
+# no tests yet, one day...
+installcheck:
+	true
diff --git a/ap/app/pppd/solaris/ppp.c b/ap/app/pppd/solaris/ppp.c
new file mode 100644
index 0000000..676bc26
--- /dev/null
+++ b/ap/app/pppd/solaris/ppp.c
@@ -0,0 +1,2519 @@
+/*
+ * ppp.c - STREAMS multiplexing pseudo-device driver for PPP.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp.c,v 1.4 2005/06/27 00:59:57 carlsonj Exp $
+ */
+
+/*
+ * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
+ */
+
+#include <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 *));
+#ifndef NO_DLPI
+static void detach_ppa __P((queue_t *, mblk_t *));
+#endif
+static void detach_lower __P((queue_t *, mblk_t *));
+static void debug_dump __P((queue_t *, mblk_t *));
+static upperstr_t *find_dest __P((upperstr_t *, int));
+#if defined(SOL2)
+static upperstr_t *find_promisc __P((upperstr_t *, int));
+static mblk_t *prepend_ether __P((upperstr_t *, mblk_t *, int));
+static mblk_t *prepend_udind __P((upperstr_t *, mblk_t *, int));
+static void promisc_sendup __P((upperstr_t *, mblk_t *, int, int));
+#endif /* defined(SOL2) */
+static int putctl2 __P((queue_t *, int, int, int));
+static int putctl4 __P((queue_t *, int, int, int));
+static int pass_packet __P((upperstr_t *ppa, mblk_t *mp, int outbound));
+#ifdef FILTER_PACKETS
+static int ip_hard_filter __P((upperstr_t *ppa, mblk_t *mp, int outbound));
+#endif /* FILTER_PACKETS */
+
+#define PPP_ID 0xb1a6
+static struct module_info ppp_info = {
+#ifdef PRIOQ
+    PPP_ID, "ppp", 0, 512, 512, 384
+#else
+    PPP_ID, "ppp", 0, 512, 512, 128
+#endif	/* PRIOQ */
+};
+
+static struct qinit pppurint = {
+    pppurput, pppursrv, pppopen, pppclose, NULL, &ppp_info, NULL
+};
+
+static struct qinit pppuwint = {
+    pppuwput, pppuwsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+static struct qinit ppplrint = {
+    ppplrput, ppplrsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+static struct qinit ppplwint = {
+    ppplwput, ppplwsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+#ifdef LACHTCP
+extern struct ifstats *ifstats;
+int pppdevflag = 0;
+#endif
+
+struct streamtab pppinfo = {
+    &pppurint, &pppuwint,
+    &ppplrint, &ppplwint
+};
+
+int ppp_count;
+
+/*
+ * How we maintain statistics.
+ */
+#ifdef SOL2
+#define INCR_IPACKETS(ppa)				\
+	if (ppa->kstats != 0) {				\
+	    KSTAT_NAMED_PTR(ppa->kstats)[0].value.ul++;	\
+	}
+#define INCR_IERRORS(ppa)				\
+	if (ppa->kstats != 0) {				\
+	    KSTAT_NAMED_PTR(ppa->kstats)[1].value.ul++;	\
+	}
+#define INCR_OPACKETS(ppa)				\
+	if (ppa->kstats != 0) {				\
+	    KSTAT_NAMED_PTR(ppa->kstats)[2].value.ul++;	\
+	}
+#define INCR_OERRORS(ppa)				\
+	if (ppa->kstats != 0) {				\
+	    KSTAT_NAMED_PTR(ppa->kstats)[3].value.ul++;	\
+	}
+#endif
+
+#ifdef LACHTCP
+#define INCR_IPACKETS(ppa)	ppa->ifstats.ifs_ipackets++;
+#define INCR_IERRORS(ppa)	ppa->ifstats.ifs_ierrors++;
+#define INCR_OPACKETS(ppa)	ppa->ifstats.ifs_opackets++;
+#define INCR_OERRORS(ppa)	ppa->ifstats.ifs_oerrors++;
+#endif
+
+/*
+ * STREAMS driver entry points.
+ */
+static int
+#ifdef SVR4
+pppopen(q, devp, oflag, sflag, credp)
+    queue_t *q;
+    dev_t *devp;
+    int oflag, sflag;
+    cred_t *credp;
+#else
+pppopen(q, dev, oflag, sflag)
+    queue_t *q;
+    int dev;			/* really dev_t */
+    int oflag, sflag;
+#endif
+{
+    upperstr_t *up;
+    upperstr_t **prevp;
+    minor_t mn;
+#ifdef PRIOQ
+    u_short *ptr;
+    u_char new_band;
+#endif	/* PRIOQ */
+
+    if (q->q_ptr)
+	DRV_OPEN_OK(dev);	/* device is already open */
+
+#ifdef PRIOQ
+    /* Calculate max_bband & def_band from definitions in prioq.h
+       This colud be done at some more approtiate time (less often)
+       but this way it works well so I'll just leave it here */
+
+    max_band = 1;
+    def_band = 0;
+    ptr = prioq_table;
+    while (*ptr) {
+        new_band = 1;
+        while (*ptr)
+	    if (*ptr++ == IPPORT_DEFAULT) {
+		new_band = 0;
+		def_band = max_band;
+	    }
+        max_band += new_band;
+        ptr++;
+    }
+    if (def_band)
+        def_band = max_band - def_band;
+    --max_band;
+#endif	/* PRIOQ */
+
+    if (sflag == CLONEOPEN) {
+	mn = 0;
+	for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) {
+	    if (up->mn != mn)
+		break;
+	    ++mn;
+	}
+    } else {
+#ifdef SVR4
+	mn = getminor(*devp);
+#else
+	mn = minor(dev);
+#endif
+	for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) {
+	    if (up->mn >= mn)
+		break;
+	}
+	if (up->mn == mn) {
+	    /* this can't happen */
+	    q->q_ptr = WR(q)->q_ptr = (caddr_t) up;
+	    DRV_OPEN_OK(dev);
+	}
+    }
+
+    /*
+     * Construct a new minor node.
+     */
+    up = (upperstr_t *) ALLOC_SLEEP(sizeof(upperstr_t));
+    bzero((caddr_t) up, sizeof(upperstr_t));
+    if (up == 0) {
+	DPRINT("pppopen: out of kernel memory\n");
+	OPEN_ERROR(ENXIO);
+    }
+    up->nextmn = *prevp;
+    *prevp = up;
+    up->mn = mn;
+#ifdef SVR4
+    *devp = makedevice(getmajor(*devp), mn);
+#endif
+    up->q = q;
+    if (NOTSUSER() == 0)
+	up->flags |= US_PRIV;
+#ifndef NO_DLPI
+    up->state = DL_UNATTACHED;
+#endif
+#ifdef LACHTCP
+    up->ifflags = IFF_UP | IFF_POINTOPOINT;
+#endif
+    up->sap = -1;
+    up->last_sent = up->last_recv = time;
+    up->npmode = NPMODE_DROP;
+    q->q_ptr = (caddr_t) up;
+    WR(q)->q_ptr = (caddr_t) up;
+    noenable(WR(q));
+#ifdef SOL2
+    mutex_init(&up->stats_lock, NULL, MUTEX_DRIVER, NULL);
+#endif
+    ++ppp_count;
+
+    qprocson(q);
+    DRV_OPEN_OK(makedev(major(dev), mn));
+}
+
+static int
+#ifdef SVR4
+pppclose(q, flag, credp)
+    queue_t *q;
+    int flag;
+    cred_t *credp;
+#else
+pppclose(q, flag)
+    queue_t *q;
+    int flag;
+#endif
+{
+    upperstr_t *up, **upp;
+    upperstr_t *as, *asnext;
+    upperstr_t **prevp;
+
+    qprocsoff(q);
+
+    up = (upperstr_t *) q->q_ptr;
+    if (up == 0) {
+	DPRINT("pppclose: q_ptr = 0\n");
+	return 0;
+    }
+    if (up->flags & US_DBGLOG)
+	DPRINT2("ppp/%d: close, flags=%x\n", up->mn, up->flags);
+    if (up->flags & US_CONTROL) {
+#ifdef LACHTCP
+	struct ifstats *ifp, *pifp;
+#endif
+	if (up->lowerq != 0) {
+	    /* Gack! the lower stream should have be unlinked earlier! */
+	    DPRINT1("ppp%d: lower stream still connected on close?\n",
+		    up->mn);
+	    LOCK_LOWER_W;
+	    up->lowerq->q_ptr = 0;
+	    RD(up->lowerq)->q_ptr = 0;
+	    up->lowerq = 0;
+	    UNLOCK_LOWER;
+	}
+
+	/*
+	 * This stream represents a PPA:
+	 * For all streams attached to the PPA, clear their
+	 * references to this PPA.
+	 * Then remove this PPA from the list of PPAs.
+	 */
+	for (as = up->next; as != 0; as = asnext) {
+	    asnext = as->next;
+	    as->next = 0;
+	    as->ppa = 0;
+	    if (as->flags & US_BLOCKED) {
+		as->flags &= ~US_BLOCKED;
+		flushq(WR(as->q), FLUSHDATA);
+	    }
+	}
+	for (upp = &ppas; *upp != 0; upp = &(*upp)->nextppa)
+	    if (*upp == up) {
+		*upp = up->nextppa;
+		break;
+	    }
+#ifdef LACHTCP
+	/* Remove the statistics from the active list.  */
+	for (ifp = ifstats, pifp = 0; ifp; ifp = ifp->ifs_next) {
+	    if (ifp == &up->ifstats) {
+		if (pifp)
+		    pifp->ifs_next = ifp->ifs_next;
+		else
+		    ifstats = ifp->ifs_next;
+		break;
+	    }
+	    pifp = ifp;
+	}
+#endif
+    } else {
+	/*
+	 * If this stream is attached to a PPA,
+	 * remove it from the PPA's list.
+	 */
+	if ((as = up->ppa) != 0) {
+	    for (; as->next != 0; as = as->next)
+		if (as->next == up) {
+		    as->next = up->next;
+		    break;
+		}
+	}
+    }
+
+#ifdef SOL2
+    if (up->kstats)
+	kstat_delete(up->kstats);
+    mutex_destroy(&up->stats_lock);
+#endif
+
+    q->q_ptr = NULL;
+    WR(q)->q_ptr = NULL;
+
+    for (prevp = &minor_devs; *prevp != 0; prevp = &(*prevp)->nextmn) {
+	if (*prevp == up) {
+	    *prevp = up->nextmn;
+	    break;
+	}
+    }
+    FREE(up, sizeof(upperstr_t));
+    --ppp_count;
+
+    return 0;
+}
+
+/*
+ * A message from on high.  We do one of three things:
+ *	- qreply()
+ *	- put the message on the lower write stream
+ *	- queue it for our service routine
+ */
+static int
+pppuwput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us, *ppa, *nps;
+    struct iocblk *iop;
+    struct linkblk *lb;
+#ifdef LACHTCP
+    struct ifreq *ifr;
+    int i;
+#endif
+    queue_t *lq;
+    int error, n, sap;
+    mblk_t *mq;
+    struct ppp_idle *pip;
+#ifdef PRIOQ
+    queue_t *tlq;
+#endif	/* PRIOQ */
+#ifdef NO_DLPI
+    upperstr_t *os;
+#endif
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+	DPRINT("pppuwput: q_ptr = 0!\n");
+	return 0;
+    }
+    if (mp == 0) {
+	DPRINT1("pppuwput/%d: mp = 0!\n", us->mn);
+	return 0;
+    }
+    if (mp->b_datap == 0) {
+	DPRINT1("pppuwput/%d: mp->b_datap = 0!\n", us->mn);
+	return 0;
+    }
+    switch (mp->b_datap->db_type) {
+#ifndef NO_DLPI
+    case M_PCPROTO:
+    case M_PROTO:
+	dlpi_request(q, mp, us);
+	break;
+#endif /* NO_DLPI */
+
+    case M_DATA:
+	if (us->flags & US_DBGLOG)
+	    DPRINT3("ppp/%d: uwput M_DATA len=%d flags=%x\n",
+		    us->mn, msgdsize(mp), us->flags);
+	if (us->ppa == 0 || msgdsize(mp) > us->ppa->mtu + PPP_HDRLEN
+#ifndef NO_DLPI
+	    || (us->flags & US_CONTROL) == 0
+#endif /* NO_DLPI */
+	    ) {
+	    DPRINT1("pppuwput: junk data len=%d\n", msgdsize(mp));
+	    freemsg(mp);
+	    break;
+	}
+#ifdef NO_DLPI
+	/* pass_packet frees the packet on returning 0 */
+	if ((us->flags & US_CONTROL) == 0 && !pass_packet(us, mp, 1))
+	    break;
+#endif
+	if (!send_data(mp, us) && !putq(q, mp))
+	    freemsg(mp);
+	break;
+
+    case M_IOCTL:
+	iop = (struct iocblk *) mp->b_rptr;
+	error = EINVAL;
+	if (us->flags & US_DBGLOG)
+	    DPRINT3("ppp/%d: ioctl %x count=%d\n",
+		    us->mn, iop->ioc_cmd, iop->ioc_count);
+	switch (iop->ioc_cmd) {
+#if defined(SOL2)
+	case DLIOCRAW:	    /* raw M_DATA mode */
+	    us->flags |= US_RAWDATA;
+	    error = 0;
+	    break;
+#endif /* defined(SOL2) */
+	case I_LINK:
+	    if ((us->flags & US_CONTROL) == 0 || us->lowerq != 0)
+		break;
+	    if (mp->b_cont == 0) {
+		DPRINT1("pppuwput/%d: ioctl I_LINK b_cont = 0!\n", us->mn);
+		break;
+	    }
+	    lb = (struct linkblk *) mp->b_cont->b_rptr;
+	    lq = lb->l_qbot;
+	    if (lq == 0) {
+		DPRINT1("pppuwput/%d: ioctl I_LINK l_qbot = 0!\n", us->mn);
+		break;
+	    }
+	    LOCK_LOWER_W;
+	    us->lowerq = lq;
+	    lq->q_ptr = (caddr_t) q;
+	    RD(lq)->q_ptr = (caddr_t) us->q;
+	    UNLOCK_LOWER;
+	    iop->ioc_count = 0;
+	    error = 0;
+	    us->flags &= ~US_LASTMOD;
+	    /* Unblock upper streams which now feed this lower stream. */
+	    qenable(q);
+	    /* Send useful information down to the modules which
+	       are now linked below us. */
+	    putctl2(lq, M_CTL, PPPCTL_UNIT, us->ppa_id);
+	    putctl4(lq, M_CTL, PPPCTL_MRU, us->mru);
+	    putctl4(lq, M_CTL, PPPCTL_MTU, us->mtu);
+#ifdef PRIOQ
+            /* Lower tty driver's queue hiwat/lowat from default 4096/128
+               to 256/128 since we don't want queueing of data on
+               output to physical device */
+
+            freezestr(lq);
+            for (tlq = lq; tlq->q_next != NULL; tlq = tlq->q_next)
+		;
+            strqset(tlq, QHIWAT, 0, 256);
+            strqset(tlq, QLOWAT, 0, 128);
+            unfreezestr(lq);
+#endif	/* PRIOQ */
+	    break;
+
+	case I_UNLINK:
+	    if (mp->b_cont == 0) {
+		DPRINT1("pppuwput/%d: ioctl I_UNLINK b_cont = 0!\n", us->mn);
+		break;
+	    }
+	    lb = (struct linkblk *) mp->b_cont->b_rptr;
+#if DEBUG
+	    if (us->lowerq != lb->l_qbot) {
+		DPRINT2("ppp unlink: lowerq=%x qbot=%x\n",
+			us->lowerq, lb->l_qbot);
+		break;
+	    }
+#endif
+	    iop->ioc_count = 0;
+	    qwriter(q, mp, detach_lower, PERIM_OUTER);
+	    /* mp is now gone */
+	    error = -1;
+	    break;
+
+	case PPPIO_NEWPPA:
+	    if (us->flags & US_CONTROL)
+		break;
+	    if ((us->flags & US_PRIV) == 0) {
+		error = EPERM;
+		break;
+	    }
+	    /* Arrange to return an int */
+	    if ((mq = mp->b_cont) == 0
+		|| mq->b_datap->db_lim - mq->b_rptr < sizeof(int)) {
+		mq = allocb(sizeof(int), BPRI_HI);
+		if (mq == 0) {
+		    error = ENOSR;
+		    break;
+		}
+		if (mp->b_cont != 0)
+		    freemsg(mp->b_cont);
+		mp->b_cont = mq;
+		mq->b_cont = 0;
+	    }
+	    iop->ioc_count = sizeof(int);
+	    mq->b_wptr = mq->b_rptr + sizeof(int);
+	    qwriter(q, mp, new_ppa, PERIM_OUTER);
+	    /* mp is now gone */
+	    error = -1;
+	    break;
+
+	case PPPIO_ATTACH:
+	    /* like dlpi_attach, for programs which can't write to
+	       the stream (like pppstats) */
+	    if (iop->ioc_count != sizeof(int) || us->ppa != 0)
+		break;
+	    if (mp->b_cont == 0) {
+		DPRINT1("pppuwput/%d: ioctl PPPIO_ATTACH b_cont = 0!\n", us->mn);
+		break;
+	    }
+	    n = *(int *)mp->b_cont->b_rptr;
+	    for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
+		if (ppa->ppa_id == n)
+		    break;
+	    if (ppa == 0)
+		break;
+	    us->ppa = ppa;
+	    iop->ioc_count = 0;
+	    qwriter(q, mp, attach_ppa, PERIM_OUTER);
+	    /* mp is now gone */
+	    error = -1;
+	    break;
+
+#ifdef NO_DLPI
+	case PPPIO_BIND:
+	    /* Attach to a given SAP. */
+	    if (iop->ioc_count != sizeof(int) || us->ppa == 0)
+		break;
+	    if (mp->b_cont == 0) {
+		DPRINT1("pppuwput/%d: ioctl PPPIO_BIND b_cont = 0!\n", us->mn);
+		break;
+	    }
+	    n = *(int *)mp->b_cont->b_rptr;
+	    /* n must be a valid PPP network protocol number. */
+	    if (n < 0x21 || n > 0x3fff || (n & 0x101) != 1)
+		break;
+	    /* check that no other stream is bound to this sap already. */
+	    for (os = us->ppa; os != 0; os = os->next)
+		if (os->sap == n)
+		    break;
+	    if (os != 0)
+		break;
+	    us->sap = n;
+	    iop->ioc_count = 0;
+	    error = 0;
+	    break;
+#endif /* NO_DLPI */
+
+	case PPPIO_MRU:
+	    if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0)
+		break;
+	    if (mp->b_cont == 0) {
+		DPRINT1("pppuwput/%d: ioctl PPPIO_MRU b_cont = 0!\n", us->mn);
+		break;
+	    }
+	    n = *(int *)mp->b_cont->b_rptr;
+	    if (n <= 0 || n > PPP_MAXMRU)
+		break;
+	    if (n < PPP_MRU)
+		n = PPP_MRU;
+	    us->mru = n;
+	    if (us->lowerq)
+		putctl4(us->lowerq, M_CTL, PPPCTL_MRU, n);
+	    error = 0;
+	    iop->ioc_count = 0;
+	    break;
+
+	case PPPIO_MTU:
+	    if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0)
+		break;
+	    if (mp->b_cont == 0) {
+		DPRINT1("pppuwput/%d: ioctl PPPIO_MTU b_cont = 0!\n", us->mn);
+		break;
+	    }
+	    n = *(int *)mp->b_cont->b_rptr;
+	    if (n <= 0 || n > PPP_MAXMTU)
+		break;
+	    us->mtu = n;
+#ifdef LACHTCP
+	    /* The MTU reported in netstat, not used as IP max packet size! */
+	    us->ifstats.ifs_mtu = n;
+#endif
+	    if (us->lowerq)
+		putctl4(us->lowerq, M_CTL, PPPCTL_MTU, n);
+	    error = 0;
+	    iop->ioc_count = 0;
+	    break;
+
+	case PPPIO_LASTMOD:
+	    us->flags |= US_LASTMOD;
+	    error = 0;
+	    break;
+
+	case PPPIO_DEBUG:
+	    if (iop->ioc_count != sizeof(int))
+		break;
+	    if (mp->b_cont == 0) {
+		DPRINT1("pppuwput/%d: ioctl PPPIO_DEBUG b_cont = 0!\n", us->mn);
+		break;
+	    }
+	    n = *(int *)mp->b_cont->b_rptr;
+	    if (n == PPPDBG_DUMP + PPPDBG_DRIVER) {
+		qwriter(q, mp, debug_dump, PERIM_OUTER);
+		/* mp is now gone */
+		error = -1;
+	    } else if (n == PPPDBG_LOG + PPPDBG_DRIVER) {
+		DPRINT1("ppp/%d: debug log enabled\n", us->mn);
+		us->flags |= US_DBGLOG;
+		iop->ioc_count = 0;
+		error = 0;
+	    } else {
+		if (us->ppa == 0 || us->ppa->lowerq == 0)
+		    break;
+		putnext(us->ppa->lowerq, mp);
+		/* mp is now gone */
+		error = -1;
+	    }
+	    break;
+
+	case PPPIO_NPMODE:
+	    if (iop->ioc_count != 2 * sizeof(int))
+		break;
+	    if ((us->flags & US_CONTROL) == 0)
+		break;
+	    if (mp->b_cont == 0) {
+		DPRINT1("pppuwput/%d: ioctl PPPIO_NPMODE b_cont = 0!\n", us->mn);
+		break;
+	    }
+	    sap = ((int *)mp->b_cont->b_rptr)[0];
+	    for (nps = us->next; nps != 0; nps = nps->next) {
+		if (us->flags & US_DBGLOG)
+		    DPRINT2("us = 0x%x, us->next->sap = 0x%x\n", nps, nps->sap);
+		if (nps->sap == sap)
+		    break;
+	    }
+	    if (nps == 0) {
+		if (us->flags & US_DBGLOG)
+		    DPRINT2("ppp/%d: no stream for sap %x\n", us->mn, sap);
+		break;
+	    }
+	    /* XXX possibly should use qwriter here */
+	    nps->npmode = (enum NPmode) ((int *)mp->b_cont->b_rptr)[1];
+	    if (nps->npmode != NPMODE_QUEUE && (nps->flags & US_BLOCKED) != 0)
+		qenable(WR(nps->q));
+	    iop->ioc_count = 0;
+	    error = 0;
+	    break;
+
+	case PPPIO_GIDLE:
+	    if ((ppa = us->ppa) == 0)
+		break;
+	    mq = allocb(sizeof(struct ppp_idle), BPRI_HI);
+	    if (mq == 0) {
+		error = ENOSR;
+		break;
+	    }
+	    if (mp->b_cont != 0)
+		freemsg(mp->b_cont);
+	    mp->b_cont = mq;
+	    mq->b_cont = 0;
+	    pip = (struct ppp_idle *) mq->b_wptr;
+	    pip->xmit_idle = time - ppa->last_sent;
+	    pip->recv_idle = time - ppa->last_recv;
+	    mq->b_wptr += sizeof(struct ppp_idle);
+	    iop->ioc_count = sizeof(struct ppp_idle);
+	    error = 0;
+	    break;
+
+#ifdef LACHTCP
+	case SIOCSIFNAME:
+	    /* Sent from IP down to us.  Attach the ifstats structure.  */
+	    if (iop->ioc_count != sizeof(struct ifreq) || us->ppa == 0)
+	        break;
+	    ifr = (struct ifreq *)mp->b_cont->b_rptr;
+	    /* Find the unit number in the interface name.  */
+	    for (i = 0; i < IFNAMSIZ; i++) {
+		if (ifr->ifr_name[i] == 0 ||
+		    (ifr->ifr_name[i] >= '0' &&
+		     ifr->ifr_name[i] <= '9'))
+		    break;
+		else
+		    us->ifname[i] = ifr->ifr_name[i];
+	    }
+	    us->ifname[i] = 0;
+
+	    /* Convert the unit number to binary.  */
+	    for (n = 0; i < IFNAMSIZ; i++) {
+		if (ifr->ifr_name[i] == 0) {
+		    break;
+		}
+	        else {
+		    n = n * 10 + ifr->ifr_name[i] - '0';
+		}
+	    }
+
+	    /* Verify the ppa.  */
+	    if (us->ppa->ppa_id != n)
+		break;
+	    ppa = us->ppa;
+
+	    /* Set up the netstat block.  */
+	    strncpy (ppa->ifname, us->ifname, IFNAMSIZ);
+
+	    ppa->ifstats.ifs_name = ppa->ifname;
+	    ppa->ifstats.ifs_unit = n;
+	    ppa->ifstats.ifs_active = us->state != DL_UNBOUND;
+	    ppa->ifstats.ifs_mtu = ppa->mtu;
+
+	    /* Link in statistics used by netstat.  */
+	    ppa->ifstats.ifs_next = ifstats;
+	    ifstats = &ppa->ifstats;
+
+	    iop->ioc_count = 0;
+	    error = 0;
+	    break;
+
+	case SIOCGIFFLAGS:
+	    if (!(us->flags & US_CONTROL)) {
+		if (us->ppa)
+		    us = us->ppa;
+	        else
+		    break;
+	    }
+	    ((struct iocblk_in *)iop)->ioc_ifflags = us->ifflags;
+	    error = 0;
+	    break;
+
+	case SIOCSIFFLAGS:
+	    if (!(us->flags & US_CONTROL)) {
+		if (us->ppa)
+		    us = us->ppa;
+		else
+		    break;
+	    }
+	    us->ifflags = ((struct iocblk_in *)iop)->ioc_ifflags;
+	    error = 0;
+	    break;
+
+	case SIOCSIFADDR:
+	    if (!(us->flags & US_CONTROL)) {
+		if (us->ppa)
+		    us = us->ppa;
+		else
+		    break;
+	    }
+	    us->ifflags |= IFF_RUNNING;
+	    ((struct iocblk_in *)iop)->ioc_ifflags |= IFF_RUNNING;
+	    error = 0;
+	    break;
+
+	case SIOCSIFMTU:
+	    /*
+	     * Vanilla SVR4 systems don't handle SIOCSIFMTU, rather
+	     * they take the MTU from the DL_INFO_ACK we sent in response
+	     * to their DL_INFO_REQ.  Fortunately, they will update the
+	     * MTU if we send an unsolicited DL_INFO_ACK up.
+	     */
+	    if ((mq = allocb(sizeof(dl_info_req_t), BPRI_HI)) == 0)
+		break;		/* should do bufcall */
+	    ((union DL_primitives *)mq->b_rptr)->dl_primitive = DL_INFO_REQ;
+	    mq->b_wptr = mq->b_rptr + sizeof(dl_info_req_t);
+	    dlpi_request(q, mq, us);
+	    /* mp is now gone */
+	    error = -1;
+	    break;
+
+	case SIOCGIFNETMASK:
+	case SIOCSIFNETMASK:
+	case SIOCGIFADDR:
+	case SIOCGIFDSTADDR:
+	case SIOCSIFDSTADDR:
+	case SIOCGIFMETRIC:
+	    error = 0;
+	    break;
+#endif /* LACHTCP */
+
+	default:
+	    if (us->ppa == 0 || us->ppa->lowerq == 0)
+		break;
+	    us->ioc_id = iop->ioc_id;
+	    error = -1;
+	    switch (iop->ioc_cmd) {
+	    case PPPIO_GETSTAT:
+	    case PPPIO_GETCSTAT:
+		if (us->flags & US_LASTMOD) {
+		    error = EINVAL;
+		    break;
+		}
+		putnext(us->ppa->lowerq, mp);
+		break;
+	    default:
+		if (us->flags & US_PRIV)
+		    putnext(us->ppa->lowerq, mp);
+		else {
+		    DPRINT1("ppp ioctl %x rejected\n", iop->ioc_cmd);
+		    error = EPERM;
+		}
+		break;
+	    }
+	    break;
+	}
+
+	if (error > 0) {
+	    iop->ioc_error = error;
+	    mp->b_datap->db_type = M_IOCNAK;
+	    qreply(q, mp);
+	} else if (error == 0) {
+	    mp->b_datap->db_type = M_IOCACK;
+	    qreply(q, mp);
+	}
+	break;
+
+    case M_FLUSH:
+	if (us->flags & US_DBGLOG)
+	    DPRINT2("ppp/%d: flush %x\n", us->mn, *mp->b_rptr);
+	if (*mp->b_rptr & FLUSHW)
+	    flushq(q, FLUSHDATA);
+	if (*mp->b_rptr & FLUSHR) {
+	    *mp->b_rptr &= ~FLUSHW;
+	    qreply(q, mp);
+	} else
+	    freemsg(mp);
+	break;
+
+    default:
+	freemsg(mp);
+	break;
+    }
+    return 0;
+}
+
+#ifndef NO_DLPI
+static void
+dlpi_request(q, mp, us)
+    queue_t *q;
+    mblk_t *mp;
+    upperstr_t *us;
+{
+    union DL_primitives *d = (union DL_primitives *) mp->b_rptr;
+    int size = mp->b_wptr - mp->b_rptr;
+    mblk_t *reply, *np;
+    upperstr_t *ppa, *os;
+    int sap, len;
+    dl_info_ack_t *info;
+    dl_bind_ack_t *ackp;
+#if DL_CURRENT_VERSION >= 2
+    dl_phys_addr_ack_t	*paddrack;
+    static struct ether_addr eaddr = {0};
+#endif
+
+    if (us->flags & US_DBGLOG)
+	DPRINT3("ppp/%d: dlpi prim %x len=%d\n", us->mn,
+		d->dl_primitive, size);
+    switch (d->dl_primitive) {
+    case DL_INFO_REQ:
+	if (size < sizeof(dl_info_req_t))
+	    goto badprim;
+	if ((reply = allocb(sizeof(dl_info_ack_t), BPRI_HI)) == 0)
+	    break;		/* should do bufcall */
+	reply->b_datap->db_type = M_PCPROTO;
+	info = (dl_info_ack_t *) reply->b_wptr;
+	reply->b_wptr += sizeof(dl_info_ack_t);
+	bzero((caddr_t) info, sizeof(dl_info_ack_t));
+	info->dl_primitive = DL_INFO_ACK;
+	info->dl_max_sdu = us->ppa? us->ppa->mtu: PPP_MAXMTU;
+	info->dl_min_sdu = 1;
+	info->dl_addr_length = sizeof(uint);
+	info->dl_mac_type = DL_ETHER;	/* a bigger lie */
+	info->dl_current_state = us->state;
+	info->dl_service_mode = DL_CLDLS;
+	info->dl_provider_style = DL_STYLE2;
+#if DL_CURRENT_VERSION >= 2
+	info->dl_sap_length = sizeof(uint);
+	info->dl_version = DL_CURRENT_VERSION;
+#endif
+	qreply(q, reply);
+	break;
+
+    case DL_ATTACH_REQ:
+	if (size < sizeof(dl_attach_req_t))
+	    goto badprim;
+	if (us->state != DL_UNATTACHED || us->ppa != 0) {
+	    dlpi_error(q, us, DL_ATTACH_REQ, DL_OUTSTATE, 0);
+	    break;
+	}
+	for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
+	    if (ppa->ppa_id == d->attach_req.dl_ppa)
+		break;
+	if (ppa == 0) {
+	    dlpi_error(q, us, DL_ATTACH_REQ, DL_BADPPA, 0);
+	    break;
+	}
+	us->ppa = ppa;
+	qwriter(q, mp, attach_ppa, PERIM_OUTER);
+	return;
+
+    case DL_DETACH_REQ:
+	if (size < sizeof(dl_detach_req_t))
+	    goto badprim;
+	if (us->state != DL_UNBOUND || us->ppa == 0) {
+	    dlpi_error(q, us, DL_DETACH_REQ, DL_OUTSTATE, 0);
+	    break;
+	}
+	qwriter(q, mp, detach_ppa, PERIM_OUTER);
+	return;
+
+    case DL_BIND_REQ:
+	if (size < sizeof(dl_bind_req_t))
+	    goto badprim;
+	if (us->state != DL_UNBOUND || us->ppa == 0) {
+	    dlpi_error(q, us, DL_BIND_REQ, DL_OUTSTATE, 0);
+	    break;
+	}
+#if 0
+	/* apparently this test fails (unnecessarily?) on some systems */
+	if (d->bind_req.dl_service_mode != DL_CLDLS) {
+	    dlpi_error(q, us, DL_BIND_REQ, DL_UNSUPPORTED, 0);
+	    break;
+	}
+#endif
+
+	/* saps must be valid PPP network protocol numbers,
+	   except that we accept ETHERTYPE_IP in place of PPP_IP. */
+	sap = d->bind_req.dl_sap;
+	us->req_sap = sap;
+
+#if defined(SOL2)
+	if (us->flags & US_DBGLOG)
+	    DPRINT2("DL_BIND_REQ: ip gives sap = 0x%x, us = 0x%x", sap, us);
+
+	if (sap == ETHERTYPE_IP)	    /* normal IFF_IPV4 */
+	    sap = PPP_IP;
+	else if (sap == ETHERTYPE_IPV6)	    /* when IFF_IPV6 is set */
+	    sap = PPP_IPV6;
+	else if (sap == ETHERTYPE_ALLSAP)   /* snoop gives sap of 0 */
+	    sap = PPP_ALLSAP;
+	else {
+	    DPRINT2("DL_BIND_REQ: unrecognized sap = 0x%x, us = 0x%x", sap, us);
+	    dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0);
+	    break;
+	}
+#else
+	if (sap == ETHERTYPE_IP)
+	    sap = PPP_IP;
+	if (sap < 0x21 || sap > 0x3fff || (sap & 0x101) != 1) {
+	    dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0);
+	    break;
+	}
+#endif /* defined(SOL2) */
+
+	/* check that no other stream is bound to this sap already. */
+	for (os = us->ppa; os != 0; os = os->next)
+	    if (os->sap == sap)
+		break;
+	if (os != 0) {
+	    dlpi_error(q, us, DL_BIND_REQ, DL_NOADDR, 0);
+	    break;
+	}
+
+	us->sap = sap;
+	us->state = DL_IDLE;
+
+	if ((reply = allocb(sizeof(dl_bind_ack_t) + sizeof(uint),
+			    BPRI_HI)) == 0)
+	    break;		/* should do bufcall */
+	ackp = (dl_bind_ack_t *) reply->b_wptr;
+	reply->b_wptr += sizeof(dl_bind_ack_t) + sizeof(uint);
+	reply->b_datap->db_type = M_PCPROTO;
+	bzero((caddr_t) ackp, sizeof(dl_bind_ack_t));
+	ackp->dl_primitive = DL_BIND_ACK;
+	ackp->dl_sap = sap;
+	ackp->dl_addr_length = sizeof(uint);
+	ackp->dl_addr_offset = sizeof(dl_bind_ack_t);
+	*(uint *)(ackp+1) = sap;
+	qreply(q, reply);
+	break;
+
+    case DL_UNBIND_REQ:
+	if (size < sizeof(dl_unbind_req_t))
+	    goto badprim;
+	if (us->state != DL_IDLE) {
+	    dlpi_error(q, us, DL_UNBIND_REQ, DL_OUTSTATE, 0);
+	    break;
+	}
+	us->sap = -1;
+	us->state = DL_UNBOUND;
+#ifdef LACHTCP
+	us->ppa->ifstats.ifs_active = 0;
+#endif
+	dlpi_ok(q, DL_UNBIND_REQ);
+	break;
+
+    case DL_UNITDATA_REQ:
+	if (size < sizeof(dl_unitdata_req_t))
+	    goto badprim;
+	if (us->state != DL_IDLE) {
+	    dlpi_error(q, us, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
+	    break;
+	}
+	if ((ppa = us->ppa) == 0) {
+	    cmn_err(CE_CONT, "ppp: in state dl_idle but ppa == 0?\n");
+	    break;
+	}
+	len = mp->b_cont == 0? 0: msgdsize(mp->b_cont);
+	if (len > ppa->mtu) {
+	    DPRINT2("dlpi data too large (%d > %d)\n", len, ppa->mtu);
+	    break;
+	}
+
+#if defined(SOL2)
+	/*
+	 * Should there be any promiscuous stream(s), send the data
+	 * up for each promiscuous stream that we recognize.
+	 */
+	if (mp->b_cont)
+	    promisc_sendup(ppa, mp->b_cont, us->sap, 0);
+#endif /* defined(SOL2) */
+
+	mp->b_band = 0;
+#ifdef PRIOQ
+        /* Extract s_port & d_port from IP-packet, the code is a bit
+           dirty here, but so am I, too... */
+        if (mp->b_datap->db_type == M_PROTO && us->sap == PPP_IP
+	    && mp->b_cont != 0) {
+	    u_char *bb, *tlh;
+	    int iphlen, len;
+	    u_short *ptr;
+	    u_char band_unset, cur_band, syn;
+	    u_short s_port, d_port;
+
+            bb = mp->b_cont->b_rptr; /* bb points to IP-header*/
+	    len = mp->b_cont->b_wptr - mp->b_cont->b_rptr;
+            syn = 0;
+	    s_port = IPPORT_DEFAULT;
+	    d_port = IPPORT_DEFAULT;
+	    if (len >= 20) {	/* 20 = minimum length of IP header */
+		iphlen = (bb[0] & 0x0f) * 4;
+		tlh = bb + iphlen;
+		len -= iphlen;
+		switch (bb[9]) {
+		case IPPROTO_TCP:
+		    if (len >= 20) {	      /* min length of TCP header */
+			s_port = (tlh[0] << 8) + tlh[1];
+			d_port = (tlh[2] << 8) + tlh[3];
+			syn = tlh[13] & 0x02;
+		    }
+		    break;
+		case IPPROTO_UDP:
+		    if (len >= 8) {	      /* min length of UDP header */
+			s_port = (tlh[0] << 8) + tlh[1];
+			d_port = (tlh[2] << 8) + tlh[3];
+		    }
+		    break;
+		}
+	    }
+
+            /*
+	     * Now calculate b_band for this packet from the
+	     * port-priority table.
+	     */
+            ptr = prioq_table;
+            cur_band = max_band;
+            band_unset = 1;
+            while (*ptr) {
+                while (*ptr && band_unset)
+                    if (s_port == *ptr || d_port == *ptr++) {
+                        mp->b_band = cur_band;
+                        band_unset = 0;
+                        break;
+		    }
+                ptr++;
+                cur_band--;
+	    }
+            if (band_unset)
+		mp->b_band = def_band;
+            /* It may be usable to urge SYN packets a bit */
+            if (syn)
+		mp->b_band++;
+	}
+#endif	/* PRIOQ */
+	/* this assumes PPP_HDRLEN <= sizeof(dl_unitdata_req_t) */
+	if (mp->b_datap->db_ref > 1) {
+	    np = allocb(PPP_HDRLEN, BPRI_HI);
+	    if (np == 0)
+		break;		/* gak! */
+	    np->b_cont = mp->b_cont;
+	    mp->b_cont = 0;
+	    freeb(mp);
+	    mp = np;
+	} else
+	    mp->b_datap->db_type = M_DATA;
+	/* XXX should use dl_dest_addr_offset/length here,
+	   but we would have to translate ETHERTYPE_IP -> PPP_IP */
+	mp->b_wptr = mp->b_rptr + PPP_HDRLEN;
+	mp->b_rptr[0] = PPP_ALLSTATIONS;
+	mp->b_rptr[1] = PPP_UI;
+	mp->b_rptr[2] = us->sap >> 8;
+	mp->b_rptr[3] = us->sap;
+	/* pass_packet frees the packet on returning 0 */
+	if (pass_packet(us, mp, 1)) {
+	    if (!send_data(mp, us) && !putq(q, mp))
+		freemsg(mp);
+	}
+	return;
+
+#if DL_CURRENT_VERSION >= 2
+    case DL_PHYS_ADDR_REQ:
+	if (size < sizeof(dl_phys_addr_req_t))
+	    goto badprim;
+
+	/*
+	 * Don't check state because ifconfig sends this one down too
+	 */
+
+	if ((reply = allocb(sizeof(dl_phys_addr_ack_t)+ETHERADDRL, 
+			BPRI_HI)) == 0)
+	    break;		/* should do bufcall */
+	reply->b_datap->db_type = M_PCPROTO;
+	paddrack = (dl_phys_addr_ack_t *) reply->b_wptr;
+	reply->b_wptr += sizeof(dl_phys_addr_ack_t);
+	bzero((caddr_t) paddrack, sizeof(dl_phys_addr_ack_t)+ETHERADDRL);
+	paddrack->dl_primitive = DL_PHYS_ADDR_ACK;
+	paddrack->dl_addr_length = ETHERADDRL;
+	paddrack->dl_addr_offset = sizeof(dl_phys_addr_ack_t);
+	bcopy(&eaddr, reply->b_wptr, ETHERADDRL);
+	reply->b_wptr += ETHERADDRL;
+	qreply(q, reply);
+	break;
+
+#if defined(SOL2)
+    case DL_PROMISCON_REQ:
+	if (size < sizeof(dl_promiscon_req_t))
+	    goto badprim;
+	us->flags |= US_PROMISC;
+	dlpi_ok(q, DL_PROMISCON_REQ);
+	break;
+
+    case DL_PROMISCOFF_REQ:
+	if (size < sizeof(dl_promiscoff_req_t))
+	    goto badprim;
+	us->flags &= ~US_PROMISC;
+	dlpi_ok(q, DL_PROMISCOFF_REQ);
+	break;
+#else
+    case DL_PROMISCON_REQ:	    /* fall thru */
+    case DL_PROMISCOFF_REQ:	    /* fall thru */
+#endif /* defined(SOL2) */
+#endif /* DL_CURRENT_VERSION >= 2 */
+
+#if DL_CURRENT_VERSION >= 2
+    case DL_SET_PHYS_ADDR_REQ:
+    case DL_SUBS_BIND_REQ:
+    case DL_SUBS_UNBIND_REQ:
+    case DL_ENABMULTI_REQ:
+    case DL_DISABMULTI_REQ:
+    case DL_XID_REQ:
+    case DL_TEST_REQ:
+    case DL_REPLY_UPDATE_REQ:
+    case DL_REPLY_REQ:
+    case DL_DATA_ACK_REQ:
+#endif
+    case DL_CONNECT_REQ:
+    case DL_TOKEN_REQ:
+	dlpi_error(q, us, d->dl_primitive, DL_NOTSUPPORTED, 0);
+	break;
+
+    case DL_CONNECT_RES:
+    case DL_DISCONNECT_REQ:
+    case DL_RESET_REQ:
+    case DL_RESET_RES:
+	dlpi_error(q, us, d->dl_primitive, DL_OUTSTATE, 0);
+	break;
+
+    case DL_UDQOS_REQ:
+	dlpi_error(q, us, d->dl_primitive, DL_BADQOSTYPE, 0);
+	break;
+
+#if DL_CURRENT_VERSION >= 2
+    case DL_TEST_RES:
+    case DL_XID_RES:
+	break;
+#endif
+
+    default:
+	if (us->flags & US_DBGLOG)
+	    DPRINT1("ppp: unknown dlpi prim 0x%x\n", d->dl_primitive);
+	/* fall through */
+    badprim:
+	dlpi_error(q, us, d->dl_primitive, DL_BADPRIM, 0);
+	break;
+    }
+    freemsg(mp);
+}
+
+static void
+dlpi_error(q, us, prim, err, uerr)
+    queue_t *q;
+    upperstr_t *us;
+    int prim, err, uerr;
+{
+    mblk_t *reply;
+    dl_error_ack_t *errp;
+
+    if (us->flags & US_DBGLOG)
+        DPRINT3("ppp/%d: dlpi error, prim=%x, err=%x\n", us->mn, prim, err);
+    reply = allocb(sizeof(dl_error_ack_t), BPRI_HI);
+    if (reply == 0)
+	return;			/* XXX should do bufcall */
+    reply->b_datap->db_type = M_PCPROTO;
+    errp = (dl_error_ack_t *) reply->b_wptr;
+    reply->b_wptr += sizeof(dl_error_ack_t);
+    errp->dl_primitive = DL_ERROR_ACK;
+    errp->dl_error_primitive = prim;
+    errp->dl_errno = err;
+    errp->dl_unix_errno = uerr;
+    qreply(q, reply);
+}
+
+static void
+dlpi_ok(q, prim)
+    queue_t *q;
+    int prim;
+{
+    mblk_t *reply;
+    dl_ok_ack_t *okp;
+
+    reply = allocb(sizeof(dl_ok_ack_t), BPRI_HI);
+    if (reply == 0)
+	return;			/* XXX should do bufcall */
+    reply->b_datap->db_type = M_PCPROTO;
+    okp = (dl_ok_ack_t *) reply->b_wptr;
+    reply->b_wptr += sizeof(dl_ok_ack_t);
+    okp->dl_primitive = DL_OK_ACK;
+    okp->dl_correct_primitive = prim;
+    qreply(q, reply);
+}
+#endif /* NO_DLPI */
+
+/*
+ * If return value is 0, then the packet has already been freed.
+ */
+static int
+pass_packet(us, mp, outbound)
+    upperstr_t *us;
+    mblk_t *mp;
+    int outbound;
+{
+    int pass;
+    upperstr_t *ppa;
+
+    if ((ppa = us->ppa) == 0) {
+	freemsg(mp);
+	return 0;
+    }
+
+#ifdef FILTER_PACKETS
+    pass = ip_hard_filter(us, mp, outbound);
+#else
+    /*
+     * Here is where we might, in future, decide whether to pass
+     * or drop the packet, and whether it counts as link activity.
+     */
+    pass = 1;
+#endif /* FILTER_PACKETS */
+
+    if (pass < 0) {
+	/* pass only if link already up, and don't update time */
+	if (ppa->lowerq == 0) {
+	    freemsg(mp);
+	    return 0;
+	}
+	pass = 1;
+    } else if (pass) {
+	if (outbound)
+	    ppa->last_sent = time;
+	else
+	    ppa->last_recv = time;
+    }
+
+    return pass;
+}
+
+/*
+ * We have some data to send down to the lower stream (or up the
+ * control stream, if we don't have a lower stream attached).
+ * Returns 1 if the message was dealt with, 0 if it wasn't able
+ * to be sent on and should therefore be queued up.
+ */
+static int
+send_data(mp, us)
+    mblk_t *mp;
+    upperstr_t *us;
+{
+    upperstr_t *ppa;
+
+    if ((us->flags & US_BLOCKED) || us->npmode == NPMODE_QUEUE)
+	return 0;
+    ppa = us->ppa;
+    if (ppa == 0 || us->npmode == NPMODE_DROP || us->npmode == NPMODE_ERROR) {
+	if (us->flags & US_DBGLOG)
+	    DPRINT2("ppp/%d: dropping pkt (npmode=%d)\n", us->mn, us->npmode);
+	freemsg(mp);
+	return 1;
+    }
+    if (ppa->lowerq == 0) {
+	/* try to send it up the control stream */
+        if (bcanputnext(ppa->q, mp->b_band)) {
+	    /*
+	     * The message seems to get corrupted for some reason if
+	     * we just send the message up as it is, so we send a copy.
+	     */
+	    mblk_t *np = copymsg(mp);
+	    freemsg(mp);
+	    if (np != 0)
+		putnext(ppa->q, np);
+	    return 1;
+	}
+    } else {
+        if (bcanputnext(ppa->lowerq, mp->b_band)) {
+	    MT_ENTER(&ppa->stats_lock);
+	    ppa->stats.ppp_opackets++;
+	    ppa->stats.ppp_obytes += msgdsize(mp);
+#ifdef INCR_OPACKETS
+	    INCR_OPACKETS(ppa);
+#endif
+	    MT_EXIT(&ppa->stats_lock);
+	    /*
+	     * The lower queue is only ever detached while holding an
+	     * exclusive lock on the whole driver.  So we can be confident
+	     * that the lower queue is still there.
+	     */
+	    putnext(ppa->lowerq, mp);
+	    return 1;
+	}
+    }
+    us->flags |= US_BLOCKED;
+    return 0;
+}
+
+/*
+ * Allocate a new PPA id and link this stream into the list of PPAs.
+ * This procedure is called with an exclusive lock on all queues in
+ * this driver.
+ */
+static void
+new_ppa(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us, *up, **usp;
+    int ppa_id;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+	DPRINT("new_ppa: q_ptr = 0!\n");
+	return;
+    }
+
+    usp = &ppas;
+    ppa_id = 0;
+    while ((up = *usp) != 0 && ppa_id == up->ppa_id) {
+	++ppa_id;
+	usp = &up->nextppa;
+    }
+    us->ppa_id = ppa_id;
+    us->ppa = us;
+    us->next = 0;
+    us->nextppa = *usp;
+    *usp = us;
+    us->flags |= US_CONTROL;
+    us->npmode = NPMODE_PASS;
+
+    us->mtu = PPP_MTU;
+    us->mru = PPP_MRU;
+
+#ifdef SOL2
+    /*
+     * Create a kstats record for our statistics, so netstat -i works.
+     */
+    if (us->kstats == 0) {
+	char unit[32];
+
+	sprintf(unit, "ppp%d", us->ppa->ppa_id);
+	us->kstats = kstat_create("ppp", us->ppa->ppa_id, unit,
+				  "net", KSTAT_TYPE_NAMED, 4, 0);
+	if (us->kstats != 0) {
+	    kstat_named_t *kn = KSTAT_NAMED_PTR(us->kstats);
+
+	    strcpy(kn[0].name, "ipackets");
+	    kn[0].data_type = KSTAT_DATA_ULONG;
+	    strcpy(kn[1].name, "ierrors");
+	    kn[1].data_type = KSTAT_DATA_ULONG;
+	    strcpy(kn[2].name, "opackets");
+	    kn[2].data_type = KSTAT_DATA_ULONG;
+	    strcpy(kn[3].name, "oerrors");
+	    kn[3].data_type = KSTAT_DATA_ULONG;
+	    kstat_install(us->kstats);
+	}
+    }
+#endif /* SOL2 */
+
+    *(int *)mp->b_cont->b_rptr = ppa_id;
+    mp->b_datap->db_type = M_IOCACK;
+    qreply(q, mp);
+}
+
+static void
+attach_ppa(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us, *t;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+	DPRINT("attach_ppa: q_ptr = 0!\n");
+	return;
+    }
+
+#ifndef NO_DLPI
+    us->state = DL_UNBOUND;
+#endif
+    for (t = us->ppa; t->next != 0; t = t->next)
+	;
+    t->next = us;
+    us->next = 0;
+    if (mp->b_datap->db_type == M_IOCTL) {
+	mp->b_datap->db_type = M_IOCACK;
+	qreply(q, mp);
+    } else {
+#ifndef NO_DLPI
+	dlpi_ok(q, DL_ATTACH_REQ);
+#endif
+	freemsg(mp);
+    }
+}
+
+#ifndef NO_DLPI
+static void
+detach_ppa(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us, *t;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+	DPRINT("detach_ppa: q_ptr = 0!\n");
+	return;
+    }
+
+    for (t = us->ppa; t->next != 0; t = t->next)
+	if (t->next == us) {
+	    t->next = us->next;
+	    break;
+	}
+    us->next = 0;
+    us->ppa = 0;
+    us->state = DL_UNATTACHED;
+    dlpi_ok(q, DL_DETACH_REQ);
+    freemsg(mp);
+}
+#endif
+
+/*
+ * We call this with qwriter in order to give the upper queue procedures
+ * the guarantee that the lower queue is not going to go away while
+ * they are executing.
+ */
+static void
+detach_lower(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+	DPRINT("detach_lower: q_ptr = 0!\n");
+	return;
+    }
+
+    LOCK_LOWER_W;
+    us->lowerq->q_ptr = 0;
+    RD(us->lowerq)->q_ptr = 0;
+    us->lowerq = 0;
+    UNLOCK_LOWER;
+
+    /* Unblock streams which now feed back up the control stream. */
+    qenable(us->q);
+
+    mp->b_datap->db_type = M_IOCACK;
+    qreply(q, mp);
+}
+
+static int
+pppuwsrv(q)
+    queue_t *q;
+{
+    upperstr_t *us, *as;
+    mblk_t *mp;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+	DPRINT("pppuwsrv: q_ptr = 0!\n");
+	return 0;
+    }
+
+    /*
+     * If this is a control stream, then this service procedure
+     * probably got enabled because of flow control in the lower
+     * stream being enabled (or because of the lower stream going
+     * away).  Therefore we enable the service procedure of all
+     * attached upper streams.
+     */
+    if (us->flags & US_CONTROL) {
+	for (as = us->next; as != 0; as = as->next)
+	    qenable(WR(as->q));
+    }
+
+    /* Try to send on any data queued here. */
+    us->flags &= ~US_BLOCKED;
+    while ((mp = getq(q)) != 0) {
+	if (!send_data(mp, us)) {
+	    putbq(q, mp);
+	    break;
+	}
+    }
+
+    return 0;
+}
+
+/* should never get called... */
+static int
+ppplwput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    putnext(q, mp);
+    return 0;
+}
+
+static int
+ppplwsrv(q)
+    queue_t *q;
+{
+    queue_t *uq;
+
+    /*
+     * Flow control has back-enabled this stream:
+     * enable the upper write service procedure for
+     * the upper control stream for this lower stream.
+     */
+    LOCK_LOWER_R;
+    uq = (queue_t *) q->q_ptr;
+    if (uq != 0)
+	qenable(uq);
+    UNLOCK_LOWER;
+    return 0;
+}
+
+/*
+ * This should only get called for control streams.
+ */
+static int
+pppurput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *ppa, *us;
+    int proto, len;
+    struct iocblk *iop;
+
+    ppa = (upperstr_t *) q->q_ptr;
+    if (ppa == 0) {
+	DPRINT("pppurput: q_ptr = 0!\n");
+	return 0;
+    }
+
+    switch (mp->b_datap->db_type) {
+    case M_CTL:
+	MT_ENTER(&ppa->stats_lock);
+	switch (*mp->b_rptr) {
+	case PPPCTL_IERROR:
+#ifdef INCR_IERRORS
+	    INCR_IERRORS(ppa);
+#endif
+	    ppa->stats.ppp_ierrors++;
+	    break;
+	case PPPCTL_OERROR:
+#ifdef INCR_OERRORS
+	    INCR_OERRORS(ppa);
+#endif
+	    ppa->stats.ppp_oerrors++;
+	    break;
+	}
+	MT_EXIT(&ppa->stats_lock);
+	freemsg(mp);
+	break;
+
+    case M_IOCACK:
+    case M_IOCNAK:
+	/*
+	 * Attempt to match up the response with the stream
+	 * that the request came from.
+	 */
+	iop = (struct iocblk *) mp->b_rptr;
+	for (us = ppa; us != 0; us = us->next)
+	    if (us->ioc_id == iop->ioc_id)
+		break;
+	if (us == 0)
+	    freemsg(mp);
+	else
+	    putnext(us->q, mp);
+	break;
+
+    case M_HANGUP:
+	/*
+	 * The serial device has hung up.  We don't want to send
+	 * the M_HANGUP message up to pppd because that will stop
+	 * us from using the control stream any more.  Instead we
+	 * send a zero-length message as an end-of-file indication.
+	 */
+	freemsg(mp);
+	mp = allocb(1, BPRI_HI);
+	if (mp == 0) {
+	    DPRINT1("ppp/%d: couldn't allocate eof message!\n", ppa->mn);
+	    break;
+	}
+	putnext(ppa->q, mp);
+	break;
+
+    case M_DATA:
+	len = msgdsize(mp);
+	if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN) {
+	    PULLUP(mp, PPP_HDRLEN);
+	    if (mp == 0) {
+		DPRINT1("ppp_urput: msgpullup failed (len=%d)\n", len);
+		break;
+	    }
+	}
+	MT_ENTER(&ppa->stats_lock);
+	ppa->stats.ppp_ipackets++;
+	ppa->stats.ppp_ibytes += len;
+#ifdef INCR_IPACKETS
+	INCR_IPACKETS(ppa);
+#endif
+	MT_EXIT(&ppa->stats_lock);
+
+	proto = PPP_PROTOCOL(mp->b_rptr);
+
+#if defined(SOL2)
+	/*
+	 * Should there be any promiscuous stream(s), send the data
+	 * up for each promiscuous stream that we recognize.
+	 */
+	promisc_sendup(ppa, mp, proto, 1);
+#endif /* defined(SOL2) */
+
+	if (proto < 0x8000 && (us = find_dest(ppa, proto)) != 0) {
+	    /*
+	     * A data packet for some network protocol.
+	     * Queue it on the upper stream for that protocol.
+	     * XXX could we just putnext it?  (would require thought)
+	     * The rblocked flag is there to ensure that we keep
+	     * messages in order for each network protocol.
+	     */
+	    /* pass_packet frees the packet on returning 0 */
+	    if (!pass_packet(us, mp, 0))
+		break;
+	    if (!us->rblocked && !canput(us->q))
+		us->rblocked = 1;
+	    if (!putq(us->rblocked ? q : us->q, mp))
+		freemsg(mp);
+	    break;
+	}
+
+	/* FALLTHROUGH */
+
+   default:
+	/*
+	 * A control frame, a frame for an unknown protocol,
+	 * or some other message type.
+	 * Send it up to pppd via the control stream.
+	 */
+	if (queclass(mp) == QPCTL || canputnext(ppa->q))
+	    putnext(ppa->q, mp);
+	else if (!putq(q, mp))
+	    freemsg(mp);
+	break;
+    }
+
+    return 0;
+}
+
+static int
+pppursrv(q)
+    queue_t *q;
+{
+    upperstr_t *us, *as;
+    mblk_t *mp, *hdr;
+#ifndef NO_DLPI
+    dl_unitdata_ind_t *ud;
+#endif
+    int proto;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+	DPRINT("pppursrv: q_ptr = 0!\n");
+	return 0;
+    }
+
+    if (us->flags & US_CONTROL) {
+	/*
+	 * A control stream.
+	 * If there is no lower queue attached, run the write service
+	 * routines of other upper streams attached to this PPA.
+	 */
+	if (us->lowerq == 0) {
+	    as = us;
+	    do {
+		if (as->flags & US_BLOCKED)
+		    qenable(WR(as->q));
+		as = as->next;
+	    } while (as != 0);
+	}
+
+	/*
+	 * Messages get queued on this stream's read queue if they
+	 * can't be queued on the read queue of the attached stream
+	 * that they are destined for.  This is for flow control -
+	 * when this queue fills up, the lower read put procedure will
+	 * queue messages there and the flow control will propagate
+	 * down from there.
+	 */
+	while ((mp = getq(q)) != 0) {
+	    proto = PPP_PROTOCOL(mp->b_rptr);
+	    if (proto < 0x8000 && (as = find_dest(us, proto)) != 0) {
+		if (!canput(as->q))
+		    break;
+		if (!putq(as->q, mp))
+		    freemsg(mp);
+	    } else {
+		if (!canputnext(q))
+		    break;
+		putnext(q, mp);
+	    }
+	}
+	if (mp) {
+	    putbq(q, mp);
+	} else {
+	    /* can now put stuff directly on network protocol streams again */
+	    for (as = us->next; as != 0; as = as->next)
+		as->rblocked = 0;
+	}
+
+	/*
+	 * If this stream has a lower stream attached,
+	 * enable the read queue's service routine.
+	 * XXX we should really only do this if the queue length
+	 * has dropped below the low-water mark.
+	 */
+	if (us->lowerq != 0)
+	    qenable(RD(us->lowerq));
+		
+    } else {
+	/*
+	 * A network protocol stream.  Put a DLPI header on each
+	 * packet and send it on.
+	 * (Actually, it seems that the IP module will happily
+	 * accept M_DATA messages without the DL_UNITDATA_IND header.)
+	 */
+	while ((mp = getq(q)) != 0) {
+	    if (!canputnext(q)) {
+		putbq(q, mp);
+		break;
+	    }
+#ifndef NO_DLPI
+	    proto = PPP_PROTOCOL(mp->b_rptr);
+	    mp->b_rptr += PPP_HDRLEN;
+	    hdr = allocb(sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint),
+			 BPRI_MED);
+	    if (hdr == 0) {
+		/* XXX should put it back and use bufcall */
+		freemsg(mp);
+		continue;
+	    }
+	    hdr->b_datap->db_type = M_PROTO;
+	    ud = (dl_unitdata_ind_t *) hdr->b_wptr;
+	    hdr->b_wptr += sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint);
+	    hdr->b_cont = mp;
+	    ud->dl_primitive = DL_UNITDATA_IND;
+	    ud->dl_dest_addr_length = sizeof(uint);
+	    ud->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+	    ud->dl_src_addr_length = sizeof(uint);
+	    ud->dl_src_addr_offset = ud->dl_dest_addr_offset + sizeof(uint);
+#if DL_CURRENT_VERSION >= 2
+	    ud->dl_group_address = 0;
+#endif
+	    /* Send the DLPI client the data with the SAP they requested,
+	       (e.g. ETHERTYPE_IP) rather than the PPP protocol number
+	       (e.g. PPP_IP) */
+	    ((uint *)(ud + 1))[0] = us->req_sap;	/* dest SAP */
+	    ((uint *)(ud + 1))[1] = us->req_sap;	/* src SAP */
+	    putnext(q, hdr);
+#else /* NO_DLPI */
+	    putnext(q, mp);
+#endif /* NO_DLPI */
+	}
+	/*
+	 * Now that we have consumed some packets from this queue,
+	 * enable the control stream's read service routine so that we
+	 * can process any packets for us that might have got queued
+	 * there for flow control reasons.
+	 */
+	if (us->ppa)
+	    qenable(us->ppa->q);
+    }
+
+    return 0;
+}
+
+static upperstr_t *
+find_dest(ppa, proto)
+    upperstr_t *ppa;
+    int proto;
+{
+    upperstr_t *us;
+
+    for (us = ppa->next; us != 0; us = us->next)
+	if (proto == us->sap)
+	    break;
+    return us;
+}
+
+#if defined (SOL2)
+/*
+ * Test upstream promiscuous conditions. As of now, only pass IPv4 and
+ * Ipv6 packets upstream (let PPP packets be decoded elsewhere).
+ */
+static upperstr_t *
+find_promisc(us, proto)
+    upperstr_t *us;
+    int proto;
+{
+
+    if ((proto != PPP_IP) && (proto != PPP_IPV6))
+	return (upperstr_t *)0;
+
+    for ( ; us; us = us->next) {
+	if ((us->flags & US_PROMISC) && (us->state == DL_IDLE))
+	    return us;
+    }
+
+    return (upperstr_t *)0;
+}
+
+/*
+ * Prepend an empty Ethernet header to msg for snoop, et al.
+ */
+static mblk_t *
+prepend_ether(us, mp, proto)
+    upperstr_t *us;
+    mblk_t *mp;
+    int proto;
+{
+    mblk_t *eh;
+    int type;
+
+    if ((eh = allocb(sizeof(struct ether_header), BPRI_HI)) == 0) {
+	freemsg(mp);
+	return (mblk_t *)0;
+    }
+
+    if (proto == PPP_IP)
+	type = ETHERTYPE_IP;
+    else if (proto == PPP_IPV6)
+	type = ETHERTYPE_IPV6;
+    else 
+	type = proto;	    /* What else? Let decoder decide */
+
+    eh->b_wptr += sizeof(struct ether_header);
+    bzero((caddr_t)eh->b_rptr, sizeof(struct ether_header));
+    ((struct ether_header *)eh->b_rptr)->ether_type = htons((short)type);
+    eh->b_cont = mp;
+    return (eh);
+}
+
+/*
+ * Prepend DL_UNITDATA_IND mblk to msg
+ */
+static mblk_t *
+prepend_udind(us, mp, proto)
+    upperstr_t *us;
+    mblk_t *mp;
+    int proto;
+{
+    dl_unitdata_ind_t *dlu;
+    mblk_t *dh;
+    size_t size;
+
+    size = sizeof(dl_unitdata_ind_t);
+    if ((dh = allocb(size, BPRI_MED)) == 0) {
+	freemsg(mp);
+	return (mblk_t *)0;
+    }
+
+    dh->b_datap->db_type = M_PROTO;
+    dh->b_wptr = dh->b_datap->db_lim;
+    dh->b_rptr = dh->b_wptr - size;
+
+    dlu = (dl_unitdata_ind_t *)dh->b_rptr;
+    dlu->dl_primitive = DL_UNITDATA_IND;
+    dlu->dl_dest_addr_length = 0;
+    dlu->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+    dlu->dl_src_addr_length = 0;
+    dlu->dl_src_addr_offset = sizeof(dl_unitdata_ind_t);
+    dlu->dl_group_address = 0;
+
+    dh->b_cont = mp;
+    return (dh);
+}
+
+/*
+ * For any recognized promiscuous streams, send data upstream
+ */
+static void
+promisc_sendup(ppa, mp, proto, skip)
+    upperstr_t *ppa;
+    mblk_t *mp;
+    int proto, skip;
+{
+    mblk_t *dup_mp, *dup_dup_mp;
+    upperstr_t *prus, *nprus;
+
+    if ((prus = find_promisc(ppa, proto)) != 0) {
+	if (dup_mp = dupmsg(mp)) {
+
+	    if (skip)
+		dup_mp->b_rptr += PPP_HDRLEN;
+
+	    for ( ; nprus = find_promisc(prus->next, proto); 
+		    prus = nprus) {
+
+		if (dup_dup_mp = dupmsg(dup_mp)) {
+		    if (canputnext(prus->q)) {
+			if (prus->flags & US_RAWDATA) {
+			    dup_dup_mp = prepend_ether(prus, dup_dup_mp, proto);
+			} else {
+			    dup_dup_mp = prepend_udind(prus, dup_dup_mp, proto);
+			}
+			if (dup_dup_mp == 0)
+			    continue;
+			putnext(prus->q, dup_dup_mp);
+		    } else {
+			DPRINT("ppp_urput: data to promisc q dropped\n");
+			freemsg(dup_dup_mp);
+		    }
+		}
+	    }
+
+	    if (canputnext(prus->q)) {
+		if (prus->flags & US_RAWDATA) {
+		    dup_mp = prepend_ether(prus, dup_mp, proto);
+		} else {
+		    dup_mp = prepend_udind(prus, dup_mp, proto);
+		}
+		if (dup_mp != 0)
+		    putnext(prus->q, dup_mp);
+	    } else {
+		DPRINT("ppp_urput: data to promisc q dropped\n");
+		freemsg(dup_mp);
+	    }
+	}
+    }
+}
+#endif /* defined(SOL2) */
+
+/*
+ * We simply put the message on to the associated upper control stream
+ * (either here or in ppplrsrv).  That way we enter the perimeters
+ * before looking through the list of attached streams to decide which
+ * stream it should go up.
+ */
+static int
+ppplrput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    queue_t *uq;
+    struct iocblk *iop;
+
+    switch (mp->b_datap->db_type) {
+    case M_IOCTL:
+	iop = (struct iocblk *) mp->b_rptr;
+	iop->ioc_error = EINVAL;
+	mp->b_datap->db_type = M_IOCNAK;
+	qreply(q, mp);
+	return 0;
+    case M_FLUSH:
+	if (*mp->b_rptr & FLUSHR)
+	    flushq(q, FLUSHDATA);
+	if (*mp->b_rptr & FLUSHW) {
+	    *mp->b_rptr &= ~FLUSHR;
+	    qreply(q, mp);
+	} else
+	    freemsg(mp);
+	return 0;
+    }
+
+    /*
+     * If we can't get the lower lock straight away, queue this one
+     * rather than blocking, to avoid the possibility of deadlock.
+     */
+    if (!TRYLOCK_LOWER_R) {
+	if (!putq(q, mp))
+	    freemsg(mp);
+	return 0;
+    }
+
+    /*
+     * Check that we're still connected to the driver.
+     */
+    uq = (queue_t *) q->q_ptr;
+    if (uq == 0) {
+	UNLOCK_LOWER;
+	DPRINT1("ppplrput: q = %x, uq = 0??\n", q);
+	freemsg(mp);
+	return 0;
+    }
+
+    /*
+     * Try to forward the message to the put routine for the upper
+     * control stream for this lower stream.
+     * If there are already messages queued here, queue this one so
+     * they don't get out of order.
+     */
+    if (queclass(mp) == QPCTL || (qsize(q) == 0 && canput(uq)))
+	put(uq, mp);
+    else if (!putq(q, mp))
+	freemsg(mp);
+
+    UNLOCK_LOWER;
+    return 0;
+}
+
+static int
+ppplrsrv(q)
+    queue_t *q;
+{
+    mblk_t *mp;
+    queue_t *uq;
+
+    /*
+     * Packets get queued here for flow control reasons
+     * or if the lrput routine couldn't get the lower lock
+     * without blocking.
+     */
+    LOCK_LOWER_R;
+    uq = (queue_t *) q->q_ptr;
+    if (uq == 0) {
+	UNLOCK_LOWER;
+	flushq(q, FLUSHALL);
+	DPRINT1("ppplrsrv: q = %x, uq = 0??\n", q);
+	return 0;
+    }
+    while ((mp = getq(q)) != 0) {
+	if (queclass(mp) == QPCTL || canput(uq))
+	    put(uq, mp);
+	else {
+	    putbq(q, mp);
+	    break;
+	}
+    }
+    UNLOCK_LOWER;
+    return 0;
+}
+
+static int
+putctl2(q, type, code, val)
+    queue_t *q;
+    int type, code, val;
+{
+    mblk_t *mp;
+
+    mp = allocb(2, BPRI_HI);
+    if (mp == 0)
+	return 0;
+    mp->b_datap->db_type = type;
+    mp->b_wptr[0] = code;
+    mp->b_wptr[1] = val;
+    mp->b_wptr += 2;
+    putnext(q, mp);
+    return 1;
+}
+
+static int
+putctl4(q, type, code, val)
+    queue_t *q;
+    int type, code, val;
+{
+    mblk_t *mp;
+
+    mp = allocb(4, BPRI_HI);
+    if (mp == 0)
+	return 0;
+    mp->b_datap->db_type = type;
+    mp->b_wptr[0] = code;
+    ((short *)mp->b_wptr)[1] = val;
+    mp->b_wptr += 4;
+    putnext(q, mp);
+    return 1;
+}
+
+static void
+debug_dump(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us;
+    queue_t *uq, *lq;
+
+    DPRINT("ppp upper streams:\n");
+    for (us = minor_devs; us != 0; us = us->nextmn) {
+	uq = us->q;
+	DPRINT3(" %d: q=%x rlev=%d",
+		us->mn, uq, (uq? qsize(uq): 0));
+	DPRINT3(" wlev=%d flags=0x%b", (uq? qsize(WR(uq)): 0),
+		us->flags, "\020\1priv\2control\3blocked\4last");
+	DPRINT3(" state=%x sap=%x req_sap=%x", us->state, us->sap,
+		us->req_sap);
+	if (us->ppa == 0)
+	    DPRINT(" ppa=?\n");
+	else
+	    DPRINT1(" ppa=%d\n", us->ppa->ppa_id);
+	if (us->flags & US_CONTROL) {
+	    lq = us->lowerq;
+	    DPRINT3("    control for %d lq=%x rlev=%d",
+		    us->ppa_id, lq, (lq? qsize(RD(lq)): 0));
+	    DPRINT3(" wlev=%d mru=%d mtu=%d\n",
+		    (lq? qsize(lq): 0), us->mru, us->mtu);
+	}
+    }
+    mp->b_datap->db_type = M_IOCACK;
+    qreply(q, mp);
+}
+
+#ifdef FILTER_PACKETS
+#include <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 */
+};
+
+
+/*
+ * Packet has already been freed if return value is 0.
+ */
+static int
+ip_hard_filter(us, mp, outbound)
+    upperstr_t *us;
+    mblk_t *mp;
+    int outbound;
+{
+    struct ip *ip;
+    struct pktfilt_tab *pft;
+    mblk_t *temp_mp;
+    int proto;
+    int len, hlen;
+
+
+    /* Note, the PPP header has already been pulled up in all cases */
+    proto = PPP_PROTOCOL(mp->b_rptr);
+    if (us->flags & US_DBGLOG)
+        DPRINT3("ppp/%d: filter, proto=0x%x, out=%d\n", us->mn, proto, outbound);
+
+    switch (proto)
+    {
+    case PPP_IP:
+	if ((mp->b_wptr - mp->b_rptr) == PPP_HDRLEN && mp->b_cont != 0) {
+	    temp_mp = mp->b_cont;
+    	    len = msgdsize(temp_mp);
+	    hlen = (len < MAX_IPHDR) ? len : MAX_IPHDR;
+	    PULLUP(temp_mp, hlen);
+	    if (temp_mp == 0) {
+		DPRINT2("ppp/%d: filter, pullup next failed, len=%d\n", 
+			us->mn, hlen);
+		mp->b_cont = 0;		/* PULLUP() freed the rest */
+	        freemsg(mp);
+	        return 0;
+	    }
+	    ip = (struct ip *)mp->b_cont->b_rptr;
+	}
+	else {
+	    len = msgdsize(mp);
+	    hlen = (len < (PPP_HDRLEN+MAX_IPHDR)) ? len : (PPP_HDRLEN+MAX_IPHDR);
+	    PULLUP(mp, hlen);
+	    if (mp == 0) {
+		DPRINT2("ppp/%d: filter, pullup failed, len=%d\n", 
+			us->mn, hlen);
+	        return 0;
+	    }
+	    ip = (struct ip *)(mp->b_rptr + PPP_HDRLEN);
+	}
+
+	/* For IP traffic, certain packets (e.g., RIP) may be either
+	 *   1.  ignored - dropped completely
+	 *   2.  will not initiate a connection, but
+	 *       will be passed if a connection is currently up.
+	 */
+	for (pft=pktfilt_tab; pft->proto != -1; pft++) {
+	    if (ip->ip_p == pft->proto) {
+		switch(pft->proto) {
+		case IPPROTO_UDP:
+		    if (((struct udphdr *) &((int *)ip)[ip->ip_hl])->uh_dport
+				== htons(pft->port)) goto endfor;
+		    break;
+		case IPPROTO_TCP:
+		    if (((struct tcphdr *) &((int *)ip)[ip->ip_hl])->th_dport
+				== htons(pft->port)) goto endfor;
+		    break;
+		}	
+	    }
+	}
+	endfor:
+	if (pft->proto != -1) {
+	    if (us->flags & US_DBGLOG)
+		DPRINT3("ppp/%d: found IP pkt, proto=0x%x (%d)\n", 
+				us->mn, pft->proto, pft->port);
+	    /* Discard if not connected, or if not pass_with_link_up */
+	    /* else, if link is up let go by, but don't update time */
+	    if (pft->ok_if_link_up)
+		return -1;
+	    freemsg(mp);
+	    return 0;
+	}
+        break;
+    } /* end switch (proto) */
+
+    return 1;
+}
+#endif /* FILTER_PACKETS */
+
diff --git a/ap/app/pppd/solaris/ppp.conf b/ap/app/pppd/solaris/ppp.conf
new file mode 100644
index 0000000..e443a7a
--- /dev/null
+++ b/ap/app/pppd/solaris/ppp.conf
@@ -0,0 +1 @@
+name="ppp" parent="pseudo" instance=0;
diff --git a/ap/app/pppd/solaris/ppp_ahdlc.c b/ap/app/pppd/solaris/ppp_ahdlc.c
new file mode 100644
index 0000000..28920f7
--- /dev/null
+++ b/ap/app/pppd/solaris/ppp_ahdlc.c
@@ -0,0 +1,833 @@
+/*
+ * 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 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp_ahdlc.c,v 1.5 2005/06/27 00:59:57 carlsonj Exp $
+ */
+
+/*
+ * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stream.h>
+#include <sys/stropts.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 */
+
+#ifdef USE_MUTEX
+#define	MUTEX_ENTER(x)	mutex_enter(x)
+#define	MUTEX_EXIT(x)	mutex_exit(x)
+#else
+#define	MUTEX_ENTER(x)
+#define	MUTEX_EXIT(x)
+#endif
+
+/*
+ * intpointer_t and uintpointer_t are signed and unsigned integer types 
+ * large enough to hold any data pointer; that is, data pointers can be 
+ * assigned into or from these integer types without losing precision.
+ * On recent Solaris releases, these types are defined in sys/int_types.h,
+ * but not on SunOS 4.x or the earlier Solaris versions.
+ */
+#if defined(_LP64) || defined(_I32LPx)
+typedef long                    intpointer_t;
+typedef unsigned long           uintpointer_t;
+#else
+typedef int                     intpointer_t;
+typedef unsigned int            uintpointer_t;
+#endif
+
+MOD_OPEN_DECL(ahdlc_open);
+MOD_CLOSE_DECL(ahdlc_close);
+static int ahdlc_wput __P((queue_t *, mblk_t *));
+static int ahdlc_rput __P((queue_t *, mblk_t *));
+static void ahdlc_encode __P((queue_t *, mblk_t *));
+static void ahdlc_decode __P((queue_t *, mblk_t *));
+static int msg_byte __P((mblk_t *, unsigned int));
+
+#if defined(SOL2)
+/*
+ * Don't send HDLC start flag is last transmit is within 1.5 seconds -
+ * FLAG_TIME is defined is microseconds
+ */
+#define FLAG_TIME   1500
+#define ABS(x)	    (x >= 0 ? x : (-x))
+#endif /* SOL2 */
+
+/*
+ * Extract byte i of message mp 
+ */
+#define MSG_BYTE(mp, i)	((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
+			 msg_byte((mp), (i)))
+
+/* 
+ * Is this LCP packet one we have to transmit using LCP defaults? 
+ */
+#define LCP_USE_DFLT(mp)	(1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
+
+/*
+ * Standard STREAMS declarations
+ */
+static struct module_info minfo = {
+    0x7d23, "ppp_ahdl", 0, INFPSZ, 32768, 512
+};
+
+static struct qinit rinit = {
+    ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
+};
+
+static struct qinit winit = {
+    ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
+};
+
+#if defined(SVR4) && !defined(SOL2)
+int phdldevflag = 0;
+#define ppp_ahdlcinfo phdlinfo
+#endif /* defined(SVR4) && !defined(SOL2) */
+
+struct streamtab ppp_ahdlcinfo = {
+    &rinit,			    /* ptr to st_rdinit */
+    &winit,			    /* ptr to st_wrinit */
+    NULL,			    /* ptr to st_muxrinit */
+    NULL,			    /* ptr to st_muxwinit */
+#if defined(SUNOS4)
+    NULL			    /* ptr to ptr to st_modlist */
+#endif /* SUNOS4 */
+};
+
+#if defined(SUNOS4)
+int ppp_ahdlc_count = 0;	    /* open counter */
+#endif /* SUNOS4 */
+
+/*
+ * Per-stream state structure
+ */
+typedef struct ahdlc_state {
+#if defined(USE_MUTEX)
+    kmutex_t	    lock;		    /* lock for this structure */
+#endif /* USE_MUTEX */
+    int		    flags;		    /* link flags */
+    mblk_t	    *rx_buf;		    /* ptr to receive buffer */
+    int		    rx_buf_size;	    /* receive buffer size */
+    ushort_t	    infcs;		    /* calculated rx HDLC FCS */
+    u_int32_t	    xaccm[8];		    /* 256-bit xmit ACCM */
+    u_int32_t	    raccm;		    /* 32-bit rcv ACCM */
+    int		    mtu;		    /* interface MTU */
+    int		    mru;		    /* link MRU */
+    int		    unit;		    /* current PPP unit number */
+    struct pppstat  stats;		    /* statistic structure */
+#if defined(SOL2)
+    clock_t	    flag_time;		    /* time in usec between flags */
+    clock_t	    lbolt;		    /* last updated lbolt */
+#endif /* SOL2 */
+} ahdlc_state_t;
+
+/*
+ * Values for flags 
+ */
+#define ESCAPED		0x100	/* last saw escape char on input */
+#define IFLUSH		0x200	/* flushing input due to error */
+
+/* 
+ * RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. 
+ */
+#define RCV_FLAGS	(RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+	0x0000,	0x1189,	0x2312,	0x329b,	0x4624,	0x57ad,	0x6536,	0x74bf,
+	0x8c48,	0x9dc1,	0xaf5a,	0xbed3,	0xca6c,	0xdbe5,	0xe97e,	0xf8f7,
+	0x1081,	0x0108,	0x3393,	0x221a,	0x56a5,	0x472c,	0x75b7,	0x643e,
+	0x9cc9,	0x8d40,	0xbfdb,	0xae52,	0xdaed,	0xcb64,	0xf9ff,	0xe876,
+	0x2102,	0x308b,	0x0210,	0x1399,	0x6726,	0x76af,	0x4434,	0x55bd,
+	0xad4a,	0xbcc3,	0x8e58,	0x9fd1,	0xeb6e,	0xfae7,	0xc87c,	0xd9f5,
+	0x3183,	0x200a,	0x1291,	0x0318,	0x77a7,	0x662e,	0x54b5,	0x453c,
+	0xbdcb,	0xac42,	0x9ed9,	0x8f50,	0xfbef,	0xea66,	0xd8fd,	0xc974,
+	0x4204,	0x538d,	0x6116,	0x709f,	0x0420,	0x15a9,	0x2732,	0x36bb,
+	0xce4c,	0xdfc5,	0xed5e,	0xfcd7,	0x8868,	0x99e1,	0xab7a,	0xbaf3,
+	0x5285,	0x430c,	0x7197,	0x601e,	0x14a1,	0x0528,	0x37b3,	0x263a,
+	0xdecd,	0xcf44,	0xfddf,	0xec56,	0x98e9,	0x8960,	0xbbfb,	0xaa72,
+	0x6306,	0x728f,	0x4014,	0x519d,	0x2522,	0x34ab,	0x0630,	0x17b9,
+	0xef4e,	0xfec7,	0xcc5c,	0xddd5,	0xa96a,	0xb8e3,	0x8a78,	0x9bf1,
+	0x7387,	0x620e,	0x5095,	0x411c,	0x35a3,	0x242a,	0x16b1,	0x0738,
+	0xffcf,	0xee46,	0xdcdd,	0xcd54,	0xb9eb,	0xa862,	0x9af9,	0x8b70,
+	0x8408,	0x9581,	0xa71a,	0xb693,	0xc22c,	0xd3a5,	0xe13e,	0xf0b7,
+	0x0840,	0x19c9,	0x2b52,	0x3adb,	0x4e64,	0x5fed,	0x6d76,	0x7cff,
+	0x9489,	0x8500,	0xb79b,	0xa612,	0xd2ad,	0xc324,	0xf1bf,	0xe036,
+	0x18c1,	0x0948,	0x3bd3,	0x2a5a,	0x5ee5,	0x4f6c,	0x7df7,	0x6c7e,
+	0xa50a,	0xb483,	0x8618,	0x9791,	0xe32e,	0xf2a7,	0xc03c,	0xd1b5,
+	0x2942,	0x38cb,	0x0a50,	0x1bd9,	0x6f66,	0x7eef,	0x4c74,	0x5dfd,
+	0xb58b,	0xa402,	0x9699,	0x8710,	0xf3af,	0xe226,	0xd0bd,	0xc134,
+	0x39c3,	0x284a,	0x1ad1,	0x0b58,	0x7fe7,	0x6e6e,	0x5cf5,	0x4d7c,
+	0xc60c,	0xd785,	0xe51e,	0xf497,	0x8028,	0x91a1,	0xa33a,	0xb2b3,
+	0x4a44,	0x5bcd,	0x6956,	0x78df,	0x0c60,	0x1de9,	0x2f72,	0x3efb,
+	0xd68d,	0xc704,	0xf59f,	0xe416,	0x90a9,	0x8120,	0xb3bb,	0xa232,
+	0x5ac5,	0x4b4c,	0x79d7,	0x685e,	0x1ce1,	0x0d68,	0x3ff3,	0x2e7a,
+	0xe70e,	0xf687,	0xc41c,	0xd595,	0xa12a,	0xb0a3,	0x8238,	0x93b1,
+	0x6b46,	0x7acf,	0x4854,	0x59dd,	0x2d62,	0x3ceb,	0x0e70,	0x1ff9,
+	0xf78f,	0xe606,	0xd49d,	0xc514,	0xb1ab,	0xa022,	0x92b9,	0x8330,
+	0x7bc7,	0x6a4e,	0x58d5,	0x495c,	0x3de3,	0x2c6a,	0x1ef1,	0x0f78
+};
+
+static u_int32_t paritytab[8] =
+{
+	0x96696996, 0x69969669, 0x69969669, 0x96696996,
+	0x69969669, 0x96696996, 0x96696996, 0x69969669
+};
+
+/*
+ * STREAMS module open (entry) point
+ */
+MOD_OPEN(ahdlc_open)
+{
+    ahdlc_state_t   *state;
+    mblk_t *mp;
+
+    /*
+     * Return if it's already opened
+     */
+    if (q->q_ptr) {
+	return 0;
+    }
+
+    /*
+     * This can only be opened as a module
+     */
+    if (sflag != MODOPEN) {
+	OPEN_ERROR(EINVAL);
+    }
+
+    state = (ahdlc_state_t *) ALLOC_NOSLEEP(sizeof(ahdlc_state_t));
+    if (state == 0)
+	OPEN_ERROR(ENOSR);
+    bzero((caddr_t) state, sizeof(ahdlc_state_t));
+
+    q->q_ptr	 = (caddr_t) state;
+    WR(q)->q_ptr = (caddr_t) state;
+
+#if defined(USE_MUTEX)
+    mutex_init(&state->lock, NULL, MUTEX_DEFAULT, NULL);
+#endif /* USE_MUTEX */
+
+    state->xaccm[0] = ~0;	    /* escape 0x00 through 0x1f */
+    state->xaccm[3] = 0x60000000;   /* escape 0x7d and 0x7e */
+    state->mru	    = PPP_MRU;	    /* default of 1500 bytes */
+#if defined(SOL2)
+    state->flag_time = drv_usectohz(FLAG_TIME);
+#endif /* SOL2 */
+
+#if defined(SUNOS4)
+    ppp_ahdlc_count++;
+#endif /* SUNOS4 */
+
+    qprocson(q);
+
+    if ((mp = allocb(1, BPRI_HI)) != NULL) {
+	    mp->b_datap->db_type = M_FLUSH;
+	    *mp->b_wptr++ = FLUSHR;
+	    putnext(q, mp);
+    }
+
+    return 0;
+}
+
+/*
+ * STREAMS module close (exit) point
+ */
+MOD_CLOSE(ahdlc_close)
+{
+    ahdlc_state_t   *state;
+
+    qprocsoff(q);
+
+    state = (ahdlc_state_t *) q->q_ptr;
+
+    if (state == 0) {
+	DPRINT("state == 0 in ahdlc_close\n");
+	return 0;
+    }
+
+    if (state->rx_buf != 0) {
+	freemsg(state->rx_buf);
+	state->rx_buf = 0;
+    }
+
+#if defined(USE_MUTEX)
+    mutex_destroy(&state->lock);
+#endif /* USE_MUTEX */
+
+    FREE(q->q_ptr, sizeof(ahdlc_state_t));
+    q->q_ptr	     = NULL;
+    OTHERQ(q)->q_ptr = NULL;
+
+#if defined(SUNOS4)
+    if (ppp_ahdlc_count)
+	ppp_ahdlc_count--;
+#endif /* SUNOS4 */
+    
+    return 0;
+}
+
+/*
+ * Write side put routine
+ */
+static int
+ahdlc_wput(q, mp)
+    queue_t	*q;
+    mblk_t	*mp;
+{
+    ahdlc_state_t  	*state;
+    struct iocblk  	*iop;
+    int		   	error;
+    mblk_t	   	*np;
+    struct ppp_stats	*psp;
+
+    state = (ahdlc_state_t *) q->q_ptr;
+    if (state == 0) {
+	DPRINT("state == 0 in ahdlc_wput\n");
+	freemsg(mp);
+	return 0;
+    }
+
+    switch (mp->b_datap->db_type) {
+    case M_DATA:
+	/*
+	 * A data packet - do character-stuffing and FCS, and
+	 * send it onwards.
+	 */
+	ahdlc_encode(q, mp);
+	freemsg(mp);
+	break;
+
+    case M_IOCTL:
+	iop = (struct iocblk *) mp->b_rptr;
+	error = EINVAL;
+	switch (iop->ioc_cmd) {
+	case PPPIO_XACCM:
+	    if ((iop->ioc_count < sizeof(u_int32_t)) || 
+		(iop->ioc_count > sizeof(ext_accm))) {
+		break;
+	    }
+	    if (mp->b_cont == 0) {
+		DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit);
+		break;
+	    }
+	    MUTEX_ENTER(&state->lock);
+	    bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
+		  iop->ioc_count);
+	    state->xaccm[2] &= ~0x40000000;	/* don't escape 0x5e */
+	    state->xaccm[3] |= 0x60000000;	/* do escape 0x7d, 0x7e */
+	    MUTEX_EXIT(&state->lock);
+	    iop->ioc_count = 0;
+	    error = 0;
+	    break;
+
+	case PPPIO_RACCM:
+	    if (iop->ioc_count != sizeof(u_int32_t))
+		break;
+	    if (mp->b_cont == 0) {
+		DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit);
+		break;
+	    }
+	    MUTEX_ENTER(&state->lock);
+	    bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
+		  sizeof(u_int32_t));
+	    MUTEX_EXIT(&state->lock);
+	    iop->ioc_count = 0;
+	    error = 0;
+	    break;
+
+	case PPPIO_GCLEAN:
+	    np = allocb(sizeof(int), BPRI_HI);
+	    if (np == 0) {
+		error = ENOSR;
+		break;
+	    }
+	    if (mp->b_cont != 0)
+		freemsg(mp->b_cont);
+	    mp->b_cont = np;
+	    MUTEX_ENTER(&state->lock);
+	    *(int *)np->b_wptr = state->flags & RCV_FLAGS;
+	    MUTEX_EXIT(&state->lock);
+	    np->b_wptr += sizeof(int);
+	    iop->ioc_count = sizeof(int);
+	    error = 0;
+	    break;
+
+	case PPPIO_GETSTAT:
+	    np = allocb(sizeof(struct ppp_stats), BPRI_HI);
+	    if (np == 0) {
+		error = ENOSR;
+		break;
+	    }
+	    if (mp->b_cont != 0)
+		freemsg(mp->b_cont);
+	    mp->b_cont = np;
+	    psp = (struct ppp_stats *) np->b_wptr;
+	    np->b_wptr += sizeof(struct ppp_stats);
+	    bzero((caddr_t)psp, sizeof(struct ppp_stats));
+	    psp->p = state->stats;
+	    iop->ioc_count = sizeof(struct ppp_stats);
+	    error = 0;
+	    break;
+
+	case PPPIO_LASTMOD:
+	    /* we knew this anyway */
+	    error = 0;
+	    break;
+
+	default:
+	    error = -1;
+	    break;
+	}
+
+	if (error < 0)
+	    putnext(q, mp);
+	else if (error == 0) {
+	    mp->b_datap->db_type = M_IOCACK;
+	    qreply(q, mp);
+	} else {
+	    mp->b_datap->db_type = M_IOCNAK;
+	    iop->ioc_count = 0;
+	    iop->ioc_error = error;
+	    qreply(q, mp);
+	}
+	break;
+
+    case M_CTL:
+	switch (*mp->b_rptr) {
+	case PPPCTL_MTU:
+	    MUTEX_ENTER(&state->lock);
+	    state->mtu = ((unsigned short *)mp->b_rptr)[1];
+	    MUTEX_EXIT(&state->lock);
+	    break;
+	case PPPCTL_MRU:
+	    MUTEX_ENTER(&state->lock);
+	    state->mru = ((unsigned short *)mp->b_rptr)[1];
+	    MUTEX_EXIT(&state->lock);
+	    break;
+	case PPPCTL_UNIT:
+	    MUTEX_ENTER(&state->lock);
+	    state->unit = mp->b_rptr[1];
+	    MUTEX_EXIT(&state->lock);
+	    break;
+	default:
+	    putnext(q, mp);
+	    return 0;
+	}
+	freemsg(mp);
+	break;
+
+    default:
+	putnext(q, mp);
+    }
+
+    return 0;
+}
+
+/*
+ * Read side put routine
+ */
+static int
+ahdlc_rput(q, mp)
+    queue_t *q;
+    mblk_t  *mp;
+{
+    ahdlc_state_t *state;
+
+    state = (ahdlc_state_t *) q->q_ptr;
+    if (state == 0) {
+	DPRINT("state == 0 in ahdlc_rput\n");
+	freemsg(mp);
+	return 0;
+    }
+
+    switch (mp->b_datap->db_type) {
+    case M_DATA:
+	ahdlc_decode(q, mp);
+	break;
+
+    case M_HANGUP:
+	MUTEX_ENTER(&state->lock);
+	if (state->rx_buf != 0) {
+	    /* XXX would like to send this up for debugging */
+	    freemsg(state->rx_buf);
+	    state->rx_buf = 0;
+	}
+	state->flags = IFLUSH;
+	MUTEX_EXIT(&state->lock);
+	putnext(q, mp);
+	break;
+
+    default:
+	putnext(q, mp);
+    }
+    return 0;
+}
+
+/*
+ * Extract bit c from map m, to determine if c needs to be escaped
+ */
+#define IN_TX_MAP(c, m)	((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
+
+static void
+ahdlc_encode(q, mp)
+    queue_t	*q;
+    mblk_t	*mp;
+{
+    ahdlc_state_t	*state;
+    u_int32_t		*xaccm, loc_xaccm[8];
+    ushort_t		fcs;
+    size_t		outmp_len;
+    mblk_t		*outmp, *tmp;
+    uchar_t		*dp, fcs_val;
+    int			is_lcp, code;
+#if defined(SOL2)
+    clock_t		lbolt;
+#endif /* SOL2 */
+
+    if (msgdsize(mp) < 4) {
+	return;
+    }
+
+    state = (ahdlc_state_t *)q->q_ptr;
+    MUTEX_ENTER(&state->lock);
+
+    /*
+     * Allocate an output buffer large enough to handle a case where all
+     * characters need to be escaped
+     */
+    outmp_len = (msgdsize(mp)	 << 1) +		/* input block x 2 */
+		(sizeof(fcs)	 << 2) +		/* HDLC FCS x 4 */
+		(sizeof(uchar_t) << 1);			/* HDLC flags x 2 */
+
+    outmp = allocb(outmp_len, BPRI_MED);
+    if (outmp == NULL) {
+	state->stats.ppp_oerrors++;
+	MUTEX_EXIT(&state->lock);
+	putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+	return;
+    }
+
+#if defined(SOL2)
+    /*
+     * Check if our last transmit happenned within flag_time, using
+     * the system's LBOLT value in clock ticks
+     */
+    if (drv_getparm(LBOLT, &lbolt) != -1) {
+	if (ABS((clock_t)lbolt - state->lbolt) > state->flag_time) {
+	    *outmp->b_wptr++ = PPP_FLAG;
+	} 
+	state->lbolt = lbolt;
+    } else {
+	*outmp->b_wptr++ = PPP_FLAG;
+    }
+#else
+    /*
+     * If the driver below still has a message to process, skip the
+     * HDLC flag, otherwise, put one in the beginning
+     */
+    if (qsize(q->q_next) == 0) {
+	*outmp->b_wptr++ = PPP_FLAG;
+    }
+#endif
+
+    /*
+     * All control characters must be escaped for LCP packets with code
+     * values between 1 (Conf-Req) and 7 (Code-Rej).
+     */
+    is_lcp = ((MSG_BYTE(mp, 0) == PPP_ALLSTATIONS) && 
+	      (MSG_BYTE(mp, 1) == PPP_UI) && 
+	      (MSG_BYTE(mp, 2) == (PPP_LCP >> 8)) &&
+	      (MSG_BYTE(mp, 3) == (PPP_LCP & 0xff)) &&
+	      LCP_USE_DFLT(mp));
+
+    xaccm = state->xaccm;
+    if (is_lcp) {
+	bcopy((caddr_t)state->xaccm, (caddr_t)loc_xaccm, sizeof(loc_xaccm));
+	loc_xaccm[0] = ~0;	/* force escape on 0x00 through 0x1f */
+	xaccm = loc_xaccm;
+    }
+
+    fcs = PPP_INITFCS;		/* Initial FCS is 0xffff */
+
+    /*
+     * Process this block and the rest (if any) attached to the this one
+     */
+    for (tmp = mp; tmp; tmp = tmp->b_cont) {
+	if (tmp->b_datap->db_type == M_DATA) {
+	    for (dp = tmp->b_rptr; dp < tmp->b_wptr; dp++) {
+		fcs = PPP_FCS(fcs, *dp);
+		if (IN_TX_MAP(*dp, xaccm)) {
+		    *outmp->b_wptr++ = PPP_ESCAPE;
+		    *outmp->b_wptr++ = *dp ^ PPP_TRANS;
+		} else {
+		    *outmp->b_wptr++ = *dp;
+		}
+	    }
+	} else {
+	    continue;	/* skip if db_type is something other than M_DATA */
+	}
+    }
+
+    /*
+     * Append the HDLC FCS, making sure that escaping is done on any
+     * necessary bytes
+     */
+    fcs_val = (fcs ^ 0xffff) & 0xff;
+    if (IN_TX_MAP(fcs_val, xaccm)) {
+	*outmp->b_wptr++ = PPP_ESCAPE;
+	*outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+    } else {
+	*outmp->b_wptr++ = fcs_val;
+    }
+
+    fcs_val = ((fcs ^ 0xffff) >> 8) & 0xff;
+    if (IN_TX_MAP(fcs_val, xaccm)) {
+	*outmp->b_wptr++ = PPP_ESCAPE;
+	*outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+    } else {
+	*outmp->b_wptr++ = fcs_val;
+    }
+
+    /*
+     * And finally, append the HDLC flag, and send it away
+     */
+    *outmp->b_wptr++ = PPP_FLAG;
+
+    state->stats.ppp_obytes += msgdsize(outmp);
+    state->stats.ppp_opackets++;
+
+    MUTEX_EXIT(&state->lock);
+
+    putnext(q, outmp);
+}
+
+/*
+ * Checks the 32-bit receive ACCM to see if the byte needs un-escaping
+ */
+#define IN_RX_MAP(c, m)	((((unsigned int) (uchar_t) (c)) < 0x20) && \
+			(m) & (1 << (c)))
+
+
+/*
+ * Process received characters.
+ */
+static void
+ahdlc_decode(q, mp)
+    queue_t *q;
+    mblk_t  *mp;
+{
+    ahdlc_state_t   *state;
+    mblk_t	    *om;
+    uchar_t	    *dp;
+
+    state = (ahdlc_state_t *) q->q_ptr;
+
+    MUTEX_ENTER(&state->lock);
+
+    state->stats.ppp_ibytes += msgdsize(mp);
+
+    for (; mp != 0; om = mp->b_cont, freeb(mp), mp = om)
+    for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) {
+
+	/*
+	 * This should detect the lack of 8-bit communication channel
+	 * which is necessary for PPP to work. In addition, it also
+	 * checks on the parity.
+	 */
+	if (*dp & 0x80)
+	    state->flags |= RCV_B7_1;
+	else
+	    state->flags |= RCV_B7_0;
+
+	if (paritytab[*dp >> 5] & (1 << (*dp & 0x1f)))
+	    state->flags |= RCV_ODDP;
+	else
+	    state->flags |= RCV_EVNP;
+
+	/*
+	 * So we have a HDLC flag ...
+	 */
+	if (*dp == PPP_FLAG) {
+
+	    /*
+	     * If we think that it marks the beginning of the frame,
+	     * then continue to process the next octects
+	     */
+	    if ((state->flags & IFLUSH) ||
+		(state->rx_buf == 0) ||
+		(msgdsize(state->rx_buf) == 0)) {
+
+		state->flags &= ~IFLUSH;
+		continue;
+	    }
+
+	    /*
+	     * We get here because the above condition isn't true,
+	     * in which case the HDLC flag was there to mark the end
+	     * of the frame (or so we think)
+	     */
+	    om = state->rx_buf;
+
+	    if (state->infcs == PPP_GOODFCS) {
+		state->stats.ppp_ipackets++;
+		adjmsg(om, -PPP_FCSLEN);
+		putnext(q, om);
+	    } else {
+		DPRINT2("ppp%d: bad fcs (len=%d)\n",
+                    state->unit, msgdsize(state->rx_buf));
+		freemsg(state->rx_buf);
+		state->flags &= ~(IFLUSH | ESCAPED);
+		state->stats.ppp_ierrors++;
+		putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+	    }
+
+	    state->rx_buf = 0;
+	    continue;
+	}
+
+	if (state->flags & IFLUSH) {
+	    continue;
+	}
+
+	/*
+	 * Allocate a receive buffer, large enough to store a frame (after
+	 * un-escaping) of at least 1500 octets. If MRU is negotiated to
+	 * be more than the default, then allocate that much. In addition,
+	 * we add an extra 32-bytes for a fudge factor
+	 */ 
+	if (state->rx_buf == 0) {
+	    state->rx_buf_size  = (state->mru < PPP_MRU ? PPP_MRU : state->mru);
+	    state->rx_buf_size += (sizeof(u_int32_t) << 3);
+	    state->rx_buf = allocb(state->rx_buf_size, BPRI_MED);
+
+	    /*
+	     * If allocation fails, try again on the next frame
+	     */
+	    if (state->rx_buf == 0) {
+		state->flags |= IFLUSH;
+		continue;
+	    }
+	    state->flags &= ~(IFLUSH | ESCAPED);
+	    state->infcs  = PPP_INITFCS;
+	}
+
+	if (*dp == PPP_ESCAPE) {
+	    state->flags |= ESCAPED;
+	    continue;
+	}
+
+	/*
+	 * Make sure we un-escape the necessary characters, as well as the
+	 * ones in our receive async control character map
+	 */
+	if (state->flags & ESCAPED) {
+	    *dp ^= PPP_TRANS;
+	    state->flags &= ~ESCAPED;
+	} else if (IN_RX_MAP(*dp, state->raccm)) 
+	    continue;
+
+	/*
+	 * Unless the peer lied to us about the negotiated MRU, we should
+	 * never get a frame which is too long. If it happens, toss it away
+	 * and grab the next incoming one
+	 */
+	if (msgdsize(state->rx_buf) < state->rx_buf_size) {
+	    state->infcs = PPP_FCS(state->infcs, *dp);
+	    *state->rx_buf->b_wptr++ = *dp;
+	} else {
+	    DPRINT2("ppp%d: frame too long (%d)\n",
+		state->unit, msgdsize(state->rx_buf));
+	    freemsg(state->rx_buf);
+	    state->rx_buf     = 0;
+	    state->flags     |= IFLUSH;
+	}
+    }
+
+    MUTEX_EXIT(&state->lock);
+}
+
+static int
+msg_byte(mp, i)
+    mblk_t *mp;
+    unsigned int i;
+{
+    while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
+	mp = mp->b_cont;
+    if (mp == 0)
+	return -1;
+    return mp->b_rptr[i];
+}
diff --git a/ap/app/pppd/solaris/ppp_ahdlc_mod.c b/ap/app/pppd/solaris/ppp_ahdlc_mod.c
new file mode 100644
index 0000000..f81be8a
--- /dev/null
+++ b/ap/app/pppd/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/ap/app/pppd/solaris/ppp_comp.c b/ap/app/pppd/solaris/ppp_comp.c
new file mode 100644
index 0000000..3420ebc
--- /dev/null
+++ b/ap/app/pppd/solaris/ppp_comp.c
@@ -0,0 +1,1134 @@
+/*
+ * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp_comp.c,v 1.3 2004/01/17 05:47:55 carlsonj Exp $
+ */
+
+/*
+ * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX.
+ */
+
+#include <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/ap/app/pppd/solaris/ppp_comp_mod.c b/ap/app/pppd/solaris/ppp_comp_mod.c
new file mode 100644
index 0000000..21ad9ae
--- /dev/null
+++ b/ap/app/pppd/solaris/ppp_comp_mod.c
@@ -0,0 +1,89 @@
+/*
+ * ppp_comp_mod.c - modload support for PPP compression STREAMS module.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp_comp_mod.c,v 1.2 2002/12/06 09:49:16 paulus Exp $
+ */
+
+/*
+ * This file is used under Solaris 2.
+ */
+#include <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/ap/app/pppd/solaris/ppp_mod.c b/ap/app/pppd/solaris/ppp_mod.c
new file mode 100644
index 0000000..b70674c
--- /dev/null
+++ b/ap/app/pppd/solaris/ppp_mod.c
@@ -0,0 +1,187 @@
+/*
+ * ppp_mod.c - modload support for PPP pseudo-device driver.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ppp_mod.c,v 1.3 2004/01/17 05:47:55 carlsonj Exp $
+ */
+
+/*
+ * This file is used under Solaris 2.
+ */
+
+#include <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;
+{
+    /* This entry point is not used as of Solaris 10 */
+#ifdef DDI_IDENTIFIED
+    return strcmp(ddi_get_name(dip), "ppp") == 0? DDI_IDENTIFIED:
+	DDI_NOT_IDENTIFIED;
+#else
+    return 0;
+#endif
+}
+
+static int
+ppp_attach(dip, cmd)
+    dev_info_t *dip;
+    ddi_attach_cmd_t cmd;
+{
+
+    if (cmd != DDI_ATTACH)
+	return DDI_FAILURE;
+    if (ddi_create_minor_node(dip, "ppp", S_IFCHR, 0, DDI_PSEUDO, CLONE_DEV)
+	== DDI_FAILURE) {
+	ddi_remove_minor_node(dip, NULL);
+	return DDI_FAILURE;
+    }
+    rw_init(&ppp_lower_lock, NULL, RW_DRIVER, NULL);
+    return DDI_SUCCESS;
+}
+
+static int
+ppp_detach(dip, cmd)
+    dev_info_t *dip;
+    ddi_detach_cmd_t cmd;
+{
+    rw_destroy(&ppp_lower_lock);
+    ddi_remove_minor_node(dip, NULL);
+    return DDI_SUCCESS;
+}
+
+static int
+ppp_devinfo(dip, cmd, arg, result)
+    dev_info_t *dip;
+    ddi_info_cmd_t cmd;
+    void *arg;
+    void **result;
+{
+    int error;
+
+    error = DDI_SUCCESS;
+    switch (cmd) {
+    case DDI_INFO_DEVT2DEVINFO:
+	if (ppp_dip == NULL)
+	    error = DDI_FAILURE;
+	else
+	    *result = (void *) ppp_dip;
+	break;
+    case DDI_INFO_DEVT2INSTANCE:
+	*result = NULL;
+	break;
+    default:
+	error = DDI_FAILURE;
+    }
+    return error;
+}
diff --git a/ap/app/pppd/solaris/ppp_mod.h b/ap/app/pppd/solaris/ppp_mod.h
new file mode 100644
index 0000000..f0af008
--- /dev/null
+++ b/ap/app/pppd/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 */