# HG changeset patch # User nickg # Date 1081955336 0 # Node ID e0d88dc6a92fd0cd6f89bbbd2c47551971b07380 # Parent eead27d1aa2f06b1cd34f1df20d664293024e4e1 Added PPP network support. diff --git a/packages/ChangeLog b/packages/ChangeLog --- a/packages/ChangeLog +++ b/packages/ChangeLog @@ -1,3 +1,7 @@ +2004-04-14 Nick Garnett + + * ecos.db: Added PPP package. + 2004-03-12 Jonathan Larmour * pkgconf/rules.mak: When linking, $LDFLAGS must be after target.ld diff --git a/packages/NEWS b/packages/NEWS --- a/packages/NEWS +++ b/packages/NEWS @@ -1,3 +1,4 @@ +* Added PPP network support. Contributed by Nick Garnett of eCosCentric. * Added support for network under vmWare using the LANCE chipset. Contributed by Iztok Zupet. * Added support for FAT12/16 filesystems. Contributed by diff --git a/packages/ecos.db b/packages/ecos.db --- a/packages/ecos.db +++ b/packages/ecos.db @@ -1321,6 +1321,13 @@ This package provides a SNTP client whic information and set the system clock." } +package CYGPKG_PPP { + alias { "PPP support" ppp } + directory net/ppp + script ppp.cdl + description "PPP support." +} + package CYGPKG_NET_IPSEC_LIBIPSEC { alias { "libipsec support" libipsec } directory net/ipsec/libipsec diff --git a/packages/net/ppp/current/ChangeLog b/packages/net/ppp/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/ChangeLog @@ -0,0 +1,344 @@ +2004-03-31 John Dallaway + + * cdl/ppp.cdl: Correct capitalisation in display strings. + +2004-03-24 Nick Garnett + + * cdl/ppp.cdl: Add CYGPKG_PPP_DEBUG_WARN_ONLY option to control + which syslog() messages actually get printed. + + * include/ppp.h: Widen kdebugflag option to 5 bits. + + * src/ppp_io.c: Converted some db_printf() calls to diag_printf(). + + * src/sys-ecos.c: Delay return of cyg_ppp_wait_up() until IP + addresses have been set. Add ifdef for CYGPKG_PPP_DEBUG_WARN_ONLY + to syslog(). + + * doc/ppp.sgml: Added documentation of CYGPKG_PPP_DEBUG_WARN_ONLY + option. Improved documentation of kdebugflag runtime option. + +2004-01-09 John Dallaway + + * cdl/ppp.cdl: Fix typographical error. + +2004-01-08 Nick Garnett + + * tests/test_server.sh: Added a dbecho of the PPPD command line. + +2003-12-23 Nick Garnett + + * doc/ppp.sgml: Added A&M Adder boards to list of those that + support hardware flow control. + +2003-12-22 Nick Garnett + + * src/pppd.c (vfmtmsg): Disabled PowerPC-specific ifndef, we don't + have the differences in va_list that this is coping with. + +2003-11-12 John Dallaway + + * doc/ppp.sgml: Addition comments concerning serial flow control from + Nick Garnett. + +2003-10-10 John Dallaway + + * doc/ppp.sgml: Fix documentation build error. + +2003-09-25 Nick Garnett + + * src/magic.c (magic_init): Added pseudo-pid to seed + initialization to add some variety. + + * include/pppd.h: + * include/names.h: + * src/pppd.c: Added flowctl global variable to control flow + control of serial line. Removed crtscts variable. + + * cdl/ppp.cdl: Fixed enable of CHAP support, it was zero. + + * src/ppp_io.h: + * src/ppp_io.c: + * src/sys-ecos.c (set_up_tty): Added switch to set flow control of + serial line depending on flowctl variable. Added ifdefs around use + of line status callbacks -- not all drivers support them. + + * include/ppp.h: Added flowctl field to cyg_ppp_options_t + structure plus a set of defines for its possible values. + + * doc/ppp.sgml: Added documentation of flowctl field. + + * tests/test_server.sh: + * tests/nc_test_slave.c: + * tests/tcp_echo.c: + Added ability to use software flow control in these tests and an + option on test_server commandline to select it for the host. + +2003-07-18 Nick Garnett + + * tests/test_server.sh: Changed to /bin/bash on first line, just + in case. Added "nodetach" option to pppd. It seems that some + versions of pppd fork into the background by default, and some, + like the one I tested all this with initially, do not. This option + should make them all behave the same. + +2003-07-15 Nick Garnett + + * src/magic.c (magic): Removed use of getpid(), since this is the + only dependency here on the POSIX library. + +2003-07-14 Nick Garnett + + * doc/ppp.sgml: Updated configuration section to mention + requirement for net and serial packages. Various other fixes. + + * cdl/ppp.cdl: Added requirements for CYGPKG_IO_SERIAL and + CYGPKG_IO_SERIAL_DEVICES. Various other tidies. + + * src/sys-ecos.c: Added dummy versions of various PAP and CHAP + functions to satisfy references in auth.c when PAP and CHAP have + been configured out. + + * tests/test_server.sh: Added some comments and tidied a little. + +2003-07-11 Nick Garnett + + * cdl/ppp.cdl: Added CYGPKG_PPP_TESTS_ALL to control which tests + get made. Changed default value of CYGPKG_PPP_TESTS_AUTOMATE to + build automated tests by default. + + * tests/ppp_updown.c: Changed ping timeout to seven seconds. The + larger packets can take a long time to exchange at the lower baud + rates. + + * doc/ppp.sgml: Updated to reflect latest changes. + +2003-07-08 Nick Garnett + + * cdl/ppp.cdl: Added CYGPKG_PPP_TESTS_AUTOMATE and + CYGDAT_PPP_TEST_BAUD_RATES options. + + * src/sys-ecos.c: Close sockfd correctly. + + * src/chat.c: Some small fixes to make system re-entrant. Fixed + success flag to comply with documentation. + + * tests/ppp_test_support.inl: File added to contain the common + parts for all PPP tests. This mostly involves the sending of + commands to the test_server.sh script. + + * tests/test_server.sh: Modified to respond to baud rate change + commands and to loop until a finish command is added. Added + various extra tests for authentication and chat testing. + + + * tests/ppp_auth.c: Test added to test PAP and CHAP + authentication. + + * doc/ppp.sgml: Updated to reflect latest test changes. + + * tests/ppp_up.c: + * tests/ppp_updown.c: + * tests/chat.c: + * tests/nc_test_slave.c: + * tests/tcp_echo.c: + Tests converted to use ppp_test_support.inl and to run multiple + tests in some instances. + +2003-07-03 Nick Garnett + + * src/ppp_io.c (pppasyncstart): Removed a couple of spurious + breaks. These were causing the TX thread to loop unnecessarily, + and also seemed to cause some occasional packet corruptions. + + * tests/ppp_up.c: Fixed loop to be more responsive to the PPP link + going down. + +2003-07-01 Nick Garnett + + * doc/ppp.sgml: Added further documentation on testing. + + * tests/test_server.sh: Various fixes. + +2003-06-26 Nick Garnett + + * cdl/ppp.cdl: Added CYGPKG_PPP_TEST_DEVICE to specify device used + by test programs. + Added requirement for CYGPKG_IO_SERIAL_FLOW_CONTROL. + + * doc/ppp.sgml: Added descriptions of test programs. Various + tidies and fixes. + + * src/chat.c: Added some local debug support. + + * tests/ppp_up.c: + * tests/ppp_updown.c: + * tests/chat.c: + * tests/nc_test_slave.c: + * tests/tcp_echo.c: + Added chat script (or modified existing one) to announce the name + of the test over the serial line. This is to interface to the + test_server.sh script. Use CYGPKG_PPP_TEST_DEVICE. ppp_updown now + interrogates the ppp0 interface for the remote end's IP + address. chat now switches to 115200 baud. + + * tests/isp.c: Use CYGPKG_PPP_TEST_DEVICE. + + * tests/test_server.sh: Shell script to act as a remote end for + PPP testing. Each test announces itself over the link and this + script executes the correct set of commands to run the test. + +2003-06-20 Nick Garnett + + * cdl/ppp.cdl: Removed CYGNUM_PPP_THREAD_COUNT. Updated + descriptions of other options. + + * src/chat.c: + * src/sys-ecos.c: + * include/ppp.h: Some minor tidying, and API semantic changes + mostly prompted by trying to document the API and discovering some + inconsistencies. + + * tests/ppp_up.c: Modified in line with API semantic changes. + + * doc/ppp.sgml: + * doc/makefile: + * doc/ppp_sa.sgml: + Added documentation. The actual documentation is in ppp.sgml. The + makefile and ppp_sa.sgml allow a stand-alone copy of the + documentation to be generated. + +2003-06-17 Nick Garnett + + * include/ppp.h: Removed some unused options from + cyg_ppp_options_t. + + * include/syslog.h: Tidied and commented. + + * src/auth.c: + * src/ipcp.c: + * src/lcp.c: + * src/ppp_io.c: + * src/pppd.c: + * src/sys-ecos.c: + Removed some defunct BSD code, tidied up a little. + + * src/chat.c: Some tidying. + + * cdl/ppp.cdl: + * tests/nc_test_slave.c: + * tests/nc_test_framework.h: + Added nc_test_slave test. + + * tests/ppp_up.c: Changed wait to print out TCP/IP tables every + minute. + + * tests/tcp_echo.c: Tidied up a little. + +2003-06-13 Nick Garnett + + * cdl/ppp.cdl: Added dialup number, control over compression, and + some extra tests. + + * include/pppd.h: Changed inspeed to be a cyg_serial_baud_rate_t + enum. + + * include/ppp.h: Added default_route, modem, baud and script + options to cyg_ppp_options_t. + + * include/names.h: Header added to redefine all external names in + the PPP package so that they do not pollute to application's + namespace. + + * src/chat.c: + * include/syslog.h: + * include/ppp_io.h: Tidied and commented. + + + * src/if_ppp.c: Added include of pkgconf/ppp.h. + + * src/ppp_io.c: Expunged some unnecessary BSD code, various tides. + + * src/pppd.c: Added handling of CHAT scripting during bring-up, + carrier detect and proper shutdown. + Expunged some unnecessary BSD code, various tides. + + * src/sys-ecos.c: Improved startup and shutdown synchronization, + added carrier detect handling. + Expunged some unnecessary BSD code, various tides. + + * tests/isp.c: + * tests/ppp_up.c: + * tests/ppp_updown.c: + Generally tidied and cleaned up a bit. Added ping functionality to + some tests to check routing. + + * tests/tcp_echo.c: + New test, an adaptation of the standard tcp_echo net test that + brings the PPP link up before starting the test. + + * include/zlib.h: + * src/bsd_comp.c: + * src/ppp_deflate.c: + * src/zlib.c: + Added these files to implement PPP compression. However at present + there seem to be problems with this code, so compression is + disabled by default. In any case, these algorithms need enormous + amounts of working memory, so may not be useful in an embedded + system. + +2003-06-10 Nick Garnett + + * cdl/ppp.cdl: + * include/cbcp.h: + * include/ccp.h: + * include/chap.h: + * include/chap_ms.h: + * include/fsm.h: + * include/ipcp.h: + * include/lcp.h: + * include/magic.h: + * include/ppp.h: + * include/ppp_io.h: + * include/pppd.h: + * include/slcompress.h: + * include/syslog.h: + * include/upap.h: + * include/net/bpf.h: + * include/net/if_ppp.h: + * include/net/if_pppvar.h: + * include/net/ppp_comp.h: + * include/net/ppp_defs.h: + * src/auth.c: + * src/cbcp.c: + * src/ccp.c: + * src/chap.c: + * src/chap_ms.c: + * src/chat.c: + * src/fsm.c: + * src/if_ppp.c: + * src/ipcp.c: + * src/lcp.c: + * src/magic.c: + * src/ppp_io.c: + * src/pppd.c: + * src/slcompress.c: + * src/sys-ecos.c: + * src/upap.c: + * tests/chat.c: + * tests/isp.c: + * tests/ppp_up.c: + * tests/ppp_updown.c: + Initial checkin of PPP code -- mainly as a safety/backup + measure. There is still a good deal of tidying up to be done + here. At present there is a lot of ifdeffed out BSD code present, + which is there as a reminder of what should be done in the eCos + code. The tests are incomplete, and there is no documentation. + +# Copyright (C) 2003 eCosCentric Ltd. +# All Rights Reserved. +# +# Permission is granted to use, copy, modify and redistribute this +# file. + + diff --git a/packages/net/ppp/current/cdl/ppp.cdl b/packages/net/ppp/current/cdl/ppp.cdl new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/cdl/ppp.cdl @@ -0,0 +1,315 @@ +# ==================================================================== +# +# ppp.cdl +# +# PPP configuration data +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2003, 2004 eCosCentric Limited +## +## eCos 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 or (at your option) any later version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): nickg +# Original data: nickg +# Contributors: +# Date: 2002-10-15 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_PPP { + display "PPP daemon" + parent CYGPKG_NET + doc ref/net-ppp.html + include_dir cyg/ppp + requires CYGPKG_IO + requires CYGPKG_IO_SERIAL + + requires CYGPKG_IO_SERIAL_DEVICES + requires CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING + requires CYGPKG_NET + requires CYGPKG_IO_SERIAL_FLOW_CONTROL + + description "PPP support for eCos. This package contains the PPP + daemon functionality, BSD kernel device drivers for + the network stack, plus other parts of the PPP + infrastructure." + + compile pppd.c sys-ecos.c ppp_io.c + compile auth.c ccp.c fsm.c magic.c + compile cbcp.c ipcp.c lcp.c + + compile if_ppp.c slcompress.c + + cdl_option CYGNUM_PPP_PPPD_THREAD_PRIORITY { + display "PPP thread priority" + flavor data + default_value { CYGNUM_KERNEL_SCHED_PRIORITIES/2 } + legal_values 0 to CYGNUM_KERNEL_SCHED_PRIORITIES + description "The PPP system contains two threads, One is used for receiving + data from the link and processing control packets. + The other is used to transmit data asynchronously to the link + when it cannot be completed synchronously. The receive thread + runs at the priority given here, and the transmit thread + runs at the next lower priority. + The exact priority needed here depends on the importance of the + PPP subsystem relative to the rest of the system. The default + is to put it in the middle of the priority range to provide + reasonable response without impacting genuine high + priority threads." + } + + cdl_component CYGPKG_PPP_DEBUG_WARN_ONLY { + display "PPP protocol debug error only" + default_value 1 + description "The runtime debug option enables logging of high + level debug messages. Too many of these can interfere + with the PPP device and may result in missed messages. + This is because these messages are emitted via the diag_printf() + mechanism, which disables interrupts while it prints. + By default, therefore, we only report errors and warnings, and not + all events. Setting this option to zero will enable the logging of + all events." + } + + cdl_component CYGPKG_PPP_AUTH { + display "PPP authentication defaults" + flavor none + no_define + + cdl_option CYGPKG_PPP_AUTH_DEFAULT_USER { + display "Default user name" + flavor data + default_value {"\"eCos\""} + description "This option gives the default value + for the user name used to intialize + the user field in the PPP options." + } + + cdl_option CYGPKG_PPP_AUTH_DEFAULT_PASSWD { + display "Default password" + flavor data + default_value {"\"secret\""} + description "This option gives the default value + for the password used to intialize + the passwd field in the PPP options." + } + + } + + cdl_option CYGPKG_PPP_DEFAULT_DIALUP_NUMBER { + display "Default dialup number" + flavor data + default_value { "\"5551234\""} + description "This option provides a default dialup number for use in + chat scripts. This value is not used anywhere + in the PPP package, but is provided to complete the information + needed, alongside the user name and password, for accessing a + typical dialup server." + } + + cdl_component CYGPKG_PPP_PAP { + display "PAP support" + default_value 1 + description "This component enables the inclusion of PAP authentication + support." + compile upap.c + + } + + cdl_component CYGPKG_PPP_CHAP { + display "CHAP support" + default_value 1 + description "This component enables the inclusion of CHAP authentication + support." + + compile chap.c chap_ms.c + + } + + cdl_component CYGPKG_PPP_COMPRESSION { + display "PPP compression options" + default_value 0 + description "This component provides control over + PPP compression features. WARNING: at + present there are problems with this, and + the compression code needs to allocate large + amounts of memory. Hence this is all currently + disabled." + + cdl_option PPP_BSDCOMP { + display "Enable BSD compression" + default_value 0 + compile bsd_comp.c + description "This option enables inclusion of BSD + compression into the PPP protocol." + } + + cdl_option PPP_DEFLATE { + display "Enable ZLIB compression" + default_value 0 + compile ppp_deflate.c zlib.c + description "This option enables inclusion of ZLIB + compression into the PPP protocol." + } + + } + + cdl_component CYGPKG_PPP_CHAT { + display "Chat script support" + default_value 1 + description "This component enables the inclusion of a + simple scripting system to bring up PPP connections. + It implements a subset of the CHAT scripting language." + + compile chat.c + + cdl_option CYGNUM_PPP_CHAT_ABORTS_MAX { + display "Maximum number of ABORT strings" + flavor data + default_value 10 + legal_values 5 to 500 + description "This option defines the maximum number of ABORT + strings that the CHAT system will store." + } + + cdl_option CYGNUM_PPP_CHAT_ABORTS_SIZE { + display "Maximum size of each ABORT string" + flavor data + default_value 20 + legal_values 10 to 100 + description "This option defines the maximum size of each ABORT + strings that the CHAT system will store." + } + + cdl_option CYGNUM_PPP_CHAT_STRING_LENGTH { + display "Maximum size of CHAT strings" + flavor data + default_value 256 + legal_values 32 to 32768 + description "This option defines the maximum size of any + expect or reply strings that the CHAT system + will be given." + } + + } + + + cdl_component CYGPKG_PPP_OPTIONS { + display "PPP build options" + flavor none + no_define + + cdl_option CYGPKG_PPP_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "-D__ECOS" } + description " + This option modifies the set of compiler flags for + building the PPP package. + These flags are used in addition + to the set of global flags." + } + + cdl_option CYGPKG_PPP_CFLAGS_REMOVE { + display "Suppressed compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building the PPP package. These flags are removed from + the set of global flags if present." + } + } + + cdl_option CYGPKG_PPP_TESTS { + display "PPP tests" + flavor data + no_define + calculated { "tests/ppp_up " . + "tests/ppp_updown " . + "tests/chat " . + "tests/ppp_auth " . + ((CYGPKG_PPP_TESTS_ALL) ? + "tests/isp " . + "tests/tcp_echo " . + "tests/nc_test_slave " + : + "" ) + } + description " + This option specifies the set of tests + for the PPP package." + } + + cdl_option CYGPKG_PPP_TESTS_ALL { + display "Include all PPP tests" + default_value 0 + description "This option enables the inclusion of extra PPP tests + that are not normally suitable for automated testing." + } + + cdl_option CYGPKG_PPP_TEST_DEVICE { + display "PPP test device" + flavor data + default_value { "\"/dev/ser0\"" } + description "This is the name of the device used by the PPP tests + to make the PPP connection." + } + + cdl_component CYGPKG_PPP_TESTS_AUTOMATE { + display "PPP test automation" + default_value 1 + description "This component controls the automation of PPP tests using the + test_server.sh script to control the remote end. Without this + option, PPP tests are configured to run stand-alone. With this + option they emit control strings to change the line baud rate + and announce the test being performed." + + cdl_option CYGDAT_PPP_TEST_BAUD_RATES { + display "PPP test automation baud rates" + flavor data + default_value { "CYGNUM_SERIAL_BAUD_19200," . + "CYGNUM_SERIAL_BAUD_38400," . + "CYGNUM_SERIAL_BAUD_57600," . + "CYGNUM_SERIAL_BAUD_115200" + } + description "This option gives the set of baud rates for which some + tests will execute." + } + } +} + +# EOF ppp.cdl diff --git a/packages/net/ppp/current/doc/ppp.sgml b/packages/net/ppp/current/doc/ppp.sgml new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/doc/ppp.sgml @@ -0,0 +1,1848 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<productname>eCos</productname> PPP User Guide + + + +This package provides support for PPP (Point-to-Point Protocol) in the +eCos FreeBSD TCP/IP networking stack. + + + + + + +Features + +The eCos PPP implementation provides the +following features: + + + + + +PPP line protocol including VJ compression. + + + + + +LCP, IPCP and CCP control protocols. + + + + + +PAP and CHAP authentication. + + + + + +CHAT subset connection scripting. + + + + + +Modem control line support. + + + + + + + + + + +Using PPP + +Before going into detail, let's look at a simple example of how the +eCos PPP package is used. Consider the +following example: + + + +static void ppp_up(void) +{ + cyg_ppp_options_t options; + cyg_ppp_handle_t ppp_handle; + + // Bring up the TCP/IP network + init_all_network_interfaces(); + + // Initialize the options + cyg_ppp_options_init( &options ); + + // Start up PPP + ppp_handle = cyg_ppp_up( "/dev/ser0", &options ); + + // Wait for it to get running + if( cyg_ppp_wait_up( ppp_handle ) == 0 ) + { + // Make use of PPP + use_ppp(); + + // Bring PPP link down + cyg_ppp_down( ppp_handle ); + + // Wait for connection to go down. + cyg_ppp_wait_down( ppp_handle ); + } +} + + + +This is a simple example of how to bring up a simple PPP connection to +another computer over a directly connected serial line. The other end +is assumed to already be running PPP on the line and waiting for a +connection. + + + +The first thing this code does is to call +init_all_network_interfaces() to bring up the +TCP/IP stack and initialize any other network interfaces. It then +calls cyg_ppp_options_init() to initialize the +PPP options structure to the defaults. As it happens, the default +options are exactly what we want for this example, so we don't need to +make any further changes. We go straight on to bring the PPP interface +up by calling cyg_ppp_up(). The arguments to this +function give the name of the serial device to use, in this case +"/dev/ser0", and a pointer to the options. + + + +When cyg_ppp_up() returns, it passes back a +handle to the PPP connection which is to be used in other calls. The +PPP link will not necessarily have been fully initialized at this +time. There is a certain amount of negotiation that goes on between +the ends of a PPP link before it is ready to pass packets. An +application can wait until the link is ready by calling +cyg_ppp_wait_up(), which returns +zero if the link is up and running, or +-1 if it has gone down or failed to come up. + + + +After a successful return from cyg_ppp_wait_up(), +the application may make use of the PPP connection. This is +represented here by the call to use_ppp() but +it may, of course, be accessed by any thread. While the connection is +up the application may use the standard socket calls to make or accept +network connections and transfer data in the normal way. + + + +Once the application has finished with the PPP link, it can bring it +down by calling cyg_ppp_down(). As with bringing +the connection up, this call is asynchronous, it simply informs the +PPP subsystem to start bringing the link down. The application can +wait for the link to go down fully by calling +cyg_ppp_wait_down(). + + + +That example showed how to use PPP to connect to a local peer. PPP is +more often used to connect via a modem to a remote server, such as an +ISP. The following example shows how this works: + + + + +static char *isp_script[] = +{ + "ABORT" , "BUSY" , + "ABORT" , "NO CARRIER" , + "ABORT" , "ERROR" , + "" , "ATZ" , + "OK" , "AT S7=45 S0=0 L1 V1 X4 &C1 E1 Q0" , + "OK" , "ATD" CYGPKG_PPP_DEFAULT_DIALUP_NUMBER , + "ogin:--ogin:" , CYGPKG_PPP_AUTH_DEFAULT_USER , + "assword:" , CYGPKG_PPP_AUTH_DEFAULT_PASSWD , + "otocol:" , "ppp" , + "HELLO" , "\\c" , + 0 +}; + +static void ppp_up(void) +{ + cyg_ppp_options_t options; + cyg_ppp_handle_t ppp_handle; + + // Bring up the TCP/IP network + init_all_network_interfaces(); + + // Initialize the options + cyg_ppp_options_init( &options ); + + options.script = isp_script; + options.modem = 1; + + // Start up PPP + ppp_handle = cyg_ppp_up( "/dev/ser0", &options ); + + // Wait for it to get running + if( cyg_ppp_wait_up( ppp_handle ) == 0 ) + { + // Make use of PPP + use_ppp(); + + // Bring PPP link down + cyg_ppp_down( ppp_handle ); + + // Wait for connection to go down. + cyg_ppp_wait_down( ppp_handle ); + } +} + + + +The majority of this code is exactly the same as the previous +example. The main difference is in the setting of a couple of options +before calling cyg_ppp_up(). The +script option is set to point to a CHAT +script to manage the setup of the connection. The +modem option is set to cause the PPP system +to make use of the modem control lines. + + + +During the PPP bring-up a call will be made to +cyg_ppp_chat() to run the CHAT script (see ). In the example this script sets up various modem +options and then dials a number supplied as part of the PPP package +configuration (see ). When the connection +has been established, the script log on to the server, using a name +and password also supplied by the configuration, and then starts PPP +on the remote end. If this script succeeds the PPP connection will be +brought up and will then function as expected. + + + +The modem option causes the PPP system to +make use of the modem control lines. In particular it waits for +Carrier Detect to be asserted, and will bring the +link down if it is lost. See +for more details. + + + + + + + + +PPP Interface + + + + + + + cyg_ppp_options_init() + + + + cyg_ppp_options_init + Initialize PPP link options + + + + + +#include <cyg/ppp/ppp.h> + + + cyg_int32 cyg_ppp_options_init + cyg_ppp_options_t *options + + + + + Description + +This function initializes the PPP options, pointed to by the +options parameter, to the default state. Once +the defaults have been initialized, application code may adjust them +by assigning new values to the the fields of the +cyg_ppp_options_t structure. + + + +This function returns zero if the options were initialized +successfully. It returns -1 if the options +argument is NULL, or the options could not be initialized. + + + +The option fields, their functions and default values are as follows: + + + + + + debug + + If set to 1 this enables the reporting of debug messages + from the PPP system. These will be generated using + diag_printf() and will appear on the standard + debug channel. Note that diag_printf() + disables interrupts during output: this may cause the PPP link + device to overrun and miss characters. It is quite possible for + this option to cause errors and even make the PPP link fail + completely. Consequently, this option should be used with care. + + + Default value: 0 + + + + + + kdebugflag + + This five bit field enables low level debugging messages from + the PPP device layer in the TCP/IP stack. As with the + debug option, this may result in missed + characters and cause errors. The bits of the field have the + following meanings: + + + + + + Bit + BSD Name + Description + + + + + 0x01 + SC_DEBUG + Enable debug messages + + + 0x02 + SC_LOG_INPKT + Log contents of good packets received + + + 0x04 + SC_LOG_OUTPKT + Log contents of packets sent + + + 0x08 + SC_LOG_RAWIN + Log all characters received + + + 0x10 + SC_LOG_FLUSH + Log all characters flushed + + + + + + Default value: 0 + + + + + + default_route + + If set to 1 this option causes the PPP subsystem to install + a default route in the TCP/IP stack's routing tables using the + peer as the gateway. This entry will be removed when the PPP link + is broken. If there is already an existing working network + connection, such as an ethernet device, then there may already be + a default route established. If this is the case, then this option + will have no effect. + + + Default value: 1 + + + + + + modem + + If this option is set to 1, then the modem lines will be + used during the connection. Specifically, the PPP subsystem will + wait until the carrier detect signal is + asserted before bringing up the PPP link, and will take the PPP + link down if this signal is de-asserted. + + + Default value: 0 + + + + + + flowctl + + This option is used to specify the mechanism used to + control data flow across the serial line. It can take one of the + following values: + + + + CYG_PPP_FLOWCTL_DEFAULT + + + The flow control mechanism is not changed and is left at + whatever value was set before bringing PPP up. This allows + a non-standard flow control mechanism to be used, or for it to + be chosen and set by some other means. + + + + + CYG_PPP_FLOWCTL_NONE + + + Flow control is turned off. It is not recommended that this + option be used unless the baud rate is set low or the two + communicating machines are particularly fast. + + + + + CYG_PPP_FLOWCTL_HARDWARE + + + Use hardware flow control via the RTS/CTS lines. This is the + most effective flow control mechanism and should always be + used if available. Availability of this mechanism depends on + whether the serial device hardware has the ability to control + these lines, whether they have been connected to the socket + pins and whether the device driver has the necessary support. + + + + + CYG_PPP_FLOWCTL_SOFTWARE + + + Use software flow control by embedding XON/XOFF characters in + the data stream. This is somewhat less effective that hardware + flow control since it is subject to the propagation time of + the serial cable and the latency of the communicating + devices. Since it does not rely on any hardware support, this + flow control mechanism is always available. + + + + + + Default value: CYG_PPP_FLOWCTL_HARDWARE + + + + + + refuse_pap + + If this option is set to 1, then the PPP subsystem will not + agree to authenticate itself to the peer with PAP. When dialling + in to a remote server it is normal to authenticate the + client. There are three ways this can be done, using a + straightforward login mechanism via the CHAT script, with the + Password Authentication Protocol (PAP), or with the Challenge + Handshake Authentication Protocol (CHAP). For PAP to work the + user and + passwd options must be set to the + expected values. If they are not, then this option should be set + to force CHAP authentication. + + + Default value: 0 + + + + + + refuse_chap + + If this option is set to 1, then the PPP subsystem will not + agree to authenticate itself to the peer with CHAP. CHAP + authentication will only work if the + passwd option has been set to the + required CHAP secret for the destination server. Otherwise this + option should be disabled. + + + If both refuse_pap and + refuse_chap are set, then either no + authentication will be carried out, or it is the responsibility of + the chat script to do it. If the peer does not + require any authentication, then the setting of these options is + irrelevant. + + + Default value: 0 + + + + + + baud + + This option is set to the baud rate at which the serial + connection should be run. The default value is the rate at which + modems conventionally operate. This field is an instance of the + cyg_serial_baud_rate_t enum defined in the + serialio.h header and may only take one of the + baud rate constants defined in there. + + + Default value: CYGNUM_SERIAL_BAUD_115200 + + + + + + idle_time_limit + + This is the number of seconds that the PPP connection may + be idle before it is shut down automatically. + + + Default value: 60 + + + + + + maxconnect + + This causes the connection to terminate when it has been up + for this number of seconds. The default value of zero means that + the connection will stay up indefinitely, until either end + explicitly brings it down, or the link is lost. + + + Default value: 0 + + + + + + our_address + + This is the IP address, in network byte order, to be + attached to the local end of the PPP connection. The default value + of INADDR_ANY causes the local address to be + obtained from the peer. + + + Default value: INADDR_ANY + + + + + + his_address + + This is the IP address, in network byte order, to be + attached to the remote end of the PPP connection. The default + value of INADDR_ANY causes the remote address + to be obtained from the peer. + + + Default value: INADDR_ANY + + + + + + script + + This is a pointer to a CHAT script suitable for passing to + cyg_ppp_chat(). See + for details of the format and contents of this script. + + + Default value: NULL + + + + + + user + + This array contains the user name to be used for PAP + authentication. This field is not used for CHAP authentication. By + default the value of this option is set from the + CYGPKG_PPP_AUTH_DEFAULT_USER configuration + option. + + + Default value: CYGPKG_PPP_AUTH_DEFAULT_USER + + + + + + passwd + + This array contains the password to be used for PAP + authentication, or the secret to be used during CHAP + authentication. By default the value of this option is set from + the CYGPKG_PPP_AUTH_DEFAULT_PASSWD + configuration option. + + + Default value: CYGPKG_PPP_AUTH_DEFAULT_PASSWD + + + + + + + + + + + + + + + + + cyg_ppp_up() + + + + cyg_ppp_up + Bring PPP connection up + + + + + +#include <cyg/ppp/ppp.h> + + + cyg_ppp_handle_t cyg_ppp_up + char *devnam + const cyg_ppp_options_t *options + + + + + Description + +This function starts up a PPP connection. The +devnam argument is the name of the device to be +used for the connection, typically "/dev/ser0" or +"/dev/ser1". The options +argument should point to an initialized +cyg_ppp_options_t object. + + + +The return value will either be zero, indicating a failure, or a +cyg_ppp_handle_t object that may be used as an argument +to other PPP functions. + + + + +Although the PPP API is designed to permit several simultaneous +connections to co-exist, at present only one PPP connection is +actually implemented. Any attempt to create a second connection while +there is already one open will fail. + + + + + + + + + + + + + + cyg_ppp_down() + + + + cyg_ppp_down + Bring PPP connection down + + + + + +#include <cyg/ppp/ppp.h> + + + cyg_int32 cyg_ppp_down + cyg_ppp_handle_t handle + + + + + Description + +This function brings the PPP connection down. The +handle argument is the result of a successful +call to cyg_ppp_up(). This function only signals +to the PPP subsystem that the link should be brought down. The link +will be terminated asynchronously. If the application needs to wait +for the link to terminate, then it should call +cyg_ppp_wait_down() after calling +cyg_ppp_down(). + + + +The function returns zero if it was able to start the termination of +the PPP connection successfully. It will return -1 if the connection +is not running, or if it could not otherwise start the termination. + + + + + + + + + + + + + cyg_ppp_wait_up() + + + + cyg_ppp_wait_up + Wait for PPP connection to come up + + + + + +#include <cyg/ppp/ppp.h> + + + cyg_int32 cyg_ppp_wait_up + cyg_ppp_handle_t handle + + + + + Description + +This function waits until the PPP connection is running and then +returns. This is needed because the actual bring up of the connection +happens mostly after the call to cyg_ppp_up() +returns, and may take some time to complete, especially if dialling a +remote server. + + + +The result of this call will be zero when the connection is running, +or -1 if the connection failed to start for some reason. If the +connection is already running when this call is made it will return +immediately with a zero result. If the connection is not in the +process of coming up, or has failed, or has terminated, then a result +of -1 will be returned immediately. Thus this function may also be +used to test that the connection is still running at any point. + + + + + + + + + + + + + cyg_ppp_wait_down() + + + + cyg_ppp_wait_down + Wait for PPP connection to terminate + + + + + +#include <cyg/ppp/ppp.h> + + + void cyg_ppp_wait_down + cyg_ppp_handle_t handle + + + + + Description + +This function waits for the PPP connection to terminate. The link may +be terminated with a call to cyg_ppp_down(), by +the remote end, or by the telephone line being dropped or lost. + + + +This function has no return value. If the PPP connection is not +running, or has terminated, it will return. Applications should use +cyg_ppp_wait_up() to test the link state. + + + + + + + + + + + + + cyg_ppp_chat() + + + + cyg_ppp_chat + Execute chat script + + + + + +#include <cyg/ppp/ppp.h> + + + cyg_int32 cyg_ppp_chat + const char *devname + const char *script[] + + + + + Description + +This function implements a subset of the automated conversational +scripting as defined by the chat program. The first +argument is the name of the serial device to be used, typically +"/dev/ser0" or "/dev/ser1". The +script argument is a pointer to a zero +terminated array of strings that comprise the chat script. See for an example script, and for full detail of the script used. + + + +Under normal use this function is called from the PPP subsystem if the +cyg_ppp_options_t +script field is set to a +non-NULL value. This function should only be used +directly if the application needs to undertake special processing +between running the chat script, and bringing up the PPP connections. + + + + + + + + + + + + + + + + + +Installing and Configuring PPP + + +Including PPP in a Configuration + + +PPP is contained entirely within a single +eCos package. So to include PPP in a +configuration all you need to do is add that package. + + + +In the GUI configuration tool use the +Build->Packages menu item, find the "PPP Support" +package in the left-hand pane and use the Add button +to add it to the list of packages in use in the right-hand pane. + + + +In the command-line tool ecosconfig, you can use the +following command during the configuration phase to add the PPP package: + + + + +$ ecosconfig add ppp + + + + +In addition to the PPP package you will also need to have the +"Network" package and the "Serial Device +Drivers" package in the configuration. The dependencies and +requirements of the networking package are such that it is strongly +recommended that you start with the net template. + + + +See the eCos User Guide for full details on +how to configure and build eCos. + + + + + + +Configuring PPP + +The PPP package contains a number of configuration options that may be +changed to affect its behaviour. + + + + + CYGNUM_PPP_PPPD_THREAD_PRIORITY + + + The PPP system contains two threads, One is used for receiving + data from the link and processing control packets. The other is + used to transmit data asynchronously to the link when it cannot be + completed synchronously. The receive thread runs at the priority + given here, and the transmit thread runs at the next lower + priority. The exact priority needed here depends on the + importance of the PPP subsystem relative to the rest of the + system. The default is to put it in the middle of the priority + range to provide reasonable response without impacting genuine + high priority threads. + + + Default value: CYGNUM_KERNEL_SCHED_PRIORITIES/2 + + + + + + CYGPKG_PPP_DEBUG_WARN_ONLY + + + The runtime debug option enables logging of + high level debug messages. Too many of these can interfere with + the PPP device and may result in missed messages. This is because + these messages are emitted via the diag_printf() mechanism, which + disables interrupts while it prints. By default, therefore, we + only report errors and warnings, and not all events. Setting this + option to zero will enable the logging of all events. + + + Default value: 1 + + + + + + CYGPKG_PPP_AUTH_DEFAULT_USER + + + This option gives the default value for the user name used to + initialize the user field in the PPP + options. + + + Default value: "eCos" + + + + + + CYGPKG_PPP_AUTH_DEFAULT_PASSWD + + + This option gives the default value for the password used to + initialize the passwd field in the PPP + options. + + + Default value: "secret" + + + + + + CYGPKG_PPP_DEFAULT_DIALUP_NUMBER + + + This option provides a default dialup number for use in + chat scripts. This value is not used anywhere + in the PPP package, but is provided to complete the information + needed, alongside the user name and password, for accessing a + typical dialup server. + + + Default value: "5551234" + + + + + + CYGPKG_PPP_PAP + + + This component enables the inclusion of PAP authentication + support. + + + Default value: 1 + + + + + + CYGPKG_PPP_CHAP + + + This component enables the inclusion of CHAT authentication + support. + + + Default value: 1 + + + + + + CYGPKG_PPP_COMPRESSION + + + This component provides control over PPP compression + features. WARNING: at present there are problems with this option, + and and in any case the compression code needs to allocate large + amounts of memory. Hence this option is currently disabled and + should remain so. + + + Default value: 0 + + + + + + PPP_BSDCOMP + + + This option enables inclusion of BSD compression into the PPP + protocol. + + + Default value: 0 + + + + + + PPP_DEFLATE + + + This option enables inclusion of ZLIB compression into the PPP + protocol. + + + Default value: 0 + + + + + + CYGPKG_PPP_CHAT + + + This component enables the inclusion of a simple scripting system + to bring up PPP connections. It implements a subset of the + chat scripting language. + + + Default value: 1 + + + + + + CYGNUM_PPP_CHAT_ABORTS_MAX + + + This option defines the maximum number of ABORT + strings that the CHAT system will store. + + + Default value: 10 + + + + + + CYGNUM_PPP_CHAT_ABORTS_SIZE + + + This option defines the maximum size of each + ABORT strings that the chat + system will store. + + + Default value: 20 + + + + + + CYGNUM_PPP_CHAT_STRING_LENGTH + + + This option defines the maximum size of any expect or reply + strings that the chat system will be given. + + + Default value: 256 + + + + + + CYGPKG_PPP_TEST_DEVICE + + + This option defines the serial device to be used for PPP test + programs. + + + Default value: "/dev/ser0" + + + + + + CYGPKG_PPP_TESTS_AUTOMATE + + + This option enables automated testing features in certain test + programs. These programs will interact with a test server at the + remote end of the serial link to run a variety of tests in + different conditions. Without this option most tests default to + running a single test instance and are suitable for being run by + hand for debugging purposes. + + + Default value: 0 + + + + + + CYGDAT_PPP_TEST_BAUD_RATES + + + This option supplies a list of baud rates at which certain tests + will run if the CYGPKG_PPP_TESTS_AUTOMATE + option is set. + + + Default value: "CYGNUM_SERIAL_BAUD_19200,CYGNUM_SERIAL_BAUD_38400,CYGNUM_SERIAL_BAUD_57600,CYGNUM_SERIAL_BAUD_115200" + + + + + + + + + + + + + + + + + + +CHAT Scripts + +The automated conversational scripting supported by the +eCos PPP package is a subset of the +scripting language provided by the chat command +found on most UNIX and Linux systems. + + + +Unlike the chat command, the +eCos cyg_ppp_chat() +function takes as a parameter a zero-terminated array of pointers to +strings. In most programs this will be defined by means of an +initializer for a static array, although there is nothing to stop the +application constructing it at runtime. A simple script would be +defined like this: + + + + +static char *chat_script[] = +{ + "ABORT" , "BUSY" , + "ABORT" , "NO CARRIER" , + "" , "ATD5551234" , + "ogin:--ogin:" , "ppp" , + "ssword:" , "hithere" , + 0 +}; + + + + +The following sections have been abstracted from the public domain +documentation for the chat command. + + + + +Chat Script + + 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: + + + + + "ogin:--ogin:" , "ppp" , + "ssword:" , "hello2u2" , + 0 + + + + + This script fragment indicates that the + cyg_ppp_chat() function should expect the + string "ogin:". If it fails to receive a login prompt within + the time interval allotted, it is to send a carriage return + to the remote and then expect the string "ogin:" again. If + the first "ogin:" is received then the carriage return is not + generated. + + + Once it received the login prompt the + cyg_ppp_chat() function 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". + + + 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. + + + The expect sequence should contain only what is needed to + identify the string. 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. + + + 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:". + + + A very simple script might look like this: + + + + "ogin:" , "ppp" , + "ssword:" , " hello2u2" , + 0 + + + + + In other words, expect "....ogin:", send "ppp", expect "...ssword:", + send "hello2u2". + + + In actual practice, simple scripts are rare. At the very least, + you should include sub-expect sequences should the original + string not be received. For example, consider the following + script: + + + + "ogin:--ogin:" , "ppp" , + "ssword:" , "hello2u2", + 0 + + + + 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. + + + + + +ABORT Strings + + + Many modems will report the status of the call as a + string. These strings may be CONNECTED or NO CARRIER or + BUSY. 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 BUSY while the next + time it may receive NO CARRIER. + + + These "abort" strings may be specified in the script using + the ABORT sequence. It is written in the script as in the + following example: + + + + "ABORT" , "BUSY" , + "ABORT" , "NO CARRIER" , + "" , "ATZ" , + "OK" , "ATDT5551212" , + "CONNECT" , ... + + + + + This sequence will expect nothing; and then send the string + ATZ. The expected response to this is the string OK. When it + receives OK, it sends the string ATDT5551212 to dial the + telephone. The expected string is CONNECT. If the string + CONNECT is received the remainder of the script is + executed. However, should the modem find a busy telephone, it + will send the string BUSY. 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 + NO CARRIER, it will abort for the same reason. Either string + may be received. Either string will terminate the chat script. + + + + + +TIMEOUT + + The initial timeout value is 45 seconds. + To change the timeout value for the next expect string, + the following example may be used: + + + + "" , "ATZ" , + "OK" , "ATDT5551212" , + "CONNECT" , "\\c" , + "TIMEOUT" , "10" , + "ogin:--ogin:" , "ppp" , + "TIMEOUT" , "5" , + "assword:" , "hello2u2" , + 0 + + + + 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. + + + The timeout, once changed, remains in effect until it is + changed again. + + + + + +Sending EOT + + The special reply string of EOT 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. The EOT sequence + may be embedded into the send string using the sequence + "\x04" (i.e. a Control-D character). + + + + +Escape Sequences + +Most standard chat escape sequences can be replaced +with standard C string escapes such as '\r', '\n', '\t' +etc. Additional escape sequences may be embedded in the expect or +reply strings by introducing them with two +backslashes. + + + + + +\\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. (not valid +in expect strings.) + + + + + + + + + + + + + + +PPP Enabled Device Drivers + +For PPP to function fully over a serial device, its driver must +implement certain features. At present not all +eCos serial drivers implement these +features. A driver indicates that it supports a certain feature by +including an "implements" line in its CDL for the +following interfaces: + + + + + +CYGINT_IO_SERIAL_FLOW_CONTROL_HW + + +This interface indicates that the driver implements hardware flow +control using the RTS and CTS lines. When data is being transferred +over high speed data lines, it is essential that flow control be used +to prevent buffer overrun. + + +The PPP subsystem functions best with hardware flow control. If this +is not available, then it can be configured to use software flow +control. Since software flow control is implemented by the device +independent part of the serial device infrastructure, it is available +for all serial devices. However, this will have an effect on the +performance and reliability of the PPP link. + + + + + + +CYGINT_IO_SERIAL_LINE_STATUS_HW + + +This interface indicates that the driver implements a callback +interface for indicating the status of various RS232 control lines. Of +particular interest here is the ability to detect changes in the +Carrier Detect (CD) line. Not all drivers that implement this +interface can indicate CD status. + + +This functionality is only needed if it is important that the link be +dropped immediately a telephone connection fails. Without it, a +connection will only be dropped after it times out. This may be +acceptable in many situations. + + + + + + + +At the time of writing, the serial device drivers for the following +platforms implement some or all of the required functionality: + + + + + + +All drivers that use the generic 16x5x driver implement all functions: + + +ARM CerfPDA +ARM IQ80321 +ARM PID +ARM IOP310 +i386 PC +MIPS Atlas +MIPS Ref4955 +SH3 SE77x9 + + + + + +The following drivers implement flow control but either do not support +line status callbacks, or do not report CD changes: + + +SH4 SCIF +A&M AdderI +A&M AdderII + + + + + +All other drivers can support software flow control only. + + + + + + + + + + + + +Testing + + + +Test Programs + + +There are a number of test programs supplied with the PPP +subsystem. By default all of these tests use the device configured by +CYGPKG_PPP_TEST_DEVICE as the PPP link device. + + + + + +ppp_up + + +This test just brings up the PPP link on +CYGPKG_PPP_TEST_DEVICE and waits until the remote end brings +it back down. No modem lines are used and the program expects a PPP +connection to be waiting on the other end of the line. Typically the +remote end will test the link using ping or access +the HTTP system monitor if it is present. + + +If CYGPKG_PPP_TESTS_AUTOMATE is set, then this test +attempts to bring PPP up at each of the baud rates specified in +CYGDAT_PPP_TEST_BAUD_RATES. If it is not set then +it will just bring the connection up at 115200 baud. + + + + + +ppp_updown + + +This test brings the PPP link up on +CYGPKG_PPP_TEST_DEVICE and attempts to +ping the remote end of the link. Once the pings +have finished, the link is then brought down. + + +If CYGPKG_PPP_TESTS_AUTOMATE is set, then this test +attempts to bring PPP up at each of the baud rates specified in +CYGDAT_PPP_TEST_BAUD_RATES. If it is not set then +it will just bring the connection up at 115200 baud. + + + + + +chat + + +This test does not bring the PPP link up but simply executes a chat +script. It expects a server at the remote end of the link to supply +the correct responses. + + +This program expects the test_server.sh script to +be running on the remote end and attempts several different tests, +expecting a variety of different responses for each. + + + + + +ppp_auth + + +This test attempts to bring up the PPP link under a variety of +different authentication conditions. This includes checking that both +PAP and CHAP authentication work, and that the connection is rejected +when the incorrect authentication protcol or secrets are used. + + +This test expects the test_server.sh script to be +running on the remote end. For this test to work the /etc/ppp/pap-secrets file on the remote +end should contain the following two lines: + + +eCos * secret * +eCosPAP * secretPAP * + + +The /etc/ppp/chap-secrets file should contain: + + +eCos * secret * +eCosCHAP * secretCHAP * + + + + + +isp + + +This test expects the serial test device to be connected to a Hayes +compatible modem. The test dials the telephone number given in +CYGPKG_PPP_DEFAULT_DIALUP_NUMBER and attempts to +log on to an ISP using the user name and password supplied in +CYGPKG_PPP_AUTH_DEFAULT_USER and +CYGPKG_PPP_AUTH_DEFAULT_PASSWD. Once the PPP +connection has been made, the program then attempts to ping a number +of well known addresses. + + +Since this test is designed to interact with an ISP, it does not run +within the automated testing system. + + + + + +tcp_echo + + +This is a version of the standard network tcp_echo +test that brings up the PPP connection before waiting for the +tcp_sink and tcp_source programs +to connect. It is expected that at least one of these programs will +connect via the PPP link. However, if another network interface is +present, such as an ethernet device, then one may connect via that +interface. + + +While this test is supported by the test_server.sh +script, it runs for such a long time that it should not normally be +used during automated testing. + + + + + +nc_test_slave + + +This is a version of the standard network +nc_test_slave test that brings up the PPP +connection before waiting for the nc_test_master +program to connect. It is expected that the master will connect via +the PPP link. + + +While this test is supported by the test_server.sh +script, it runs for such a long time that it should not normally be +used during automated testing. + + + + + + + + + +Test Script + + +The PPP package additionally contains a shell script +(test_server.sh) that may be used to operate the +remote end of a PPP test link. + + + +The script may be invoked with the following arguments: + + + + + +--dev=<devname> + + +This mandatory option gives the name of the device to be used for the +PPP link. Typically "/dev/ttyS0" or +"/dev/ttyS1". + + + + + +--myip=<ipaddress> + + +This mandatory option gives the IP address to be attached to this end +of the PPP link. + + + + + +--hisip=<ipaddress> + + +This mandatory option gives the IP address to be attached to the +remote (test target) end of the PPP link. + + + + + +--baud=<baud_rate> + + +This option gives the baud rate at which the PPP link is to be run. If +absent then the link will run at the value set for +--redboot-baud. + + + + + +--redboot + + +If this option is present then the script will look for a +"RedBoot>" prompt between test runs. This is +necessary if the serial device being used for testing is also used by +RedBoot. + + + + + +--redboot-baud=<baud_rate> + + +This option gives the baud rate at which the search for the RedBoot +prompt will be made. If absent then the link will run at 38400 baud. + + + + + +--debug + + +If this option is present, then the script will print out some +additional debug messages while it runs. + + + + + + + +This script operates as follows: If the --redboot +option is set it sets the device baud rate to the RedBoot baud rate +and waits until a "RedBoot>" prompt is encountered. +It then sets the baud rate to the value given by the +--baud option and reads lines from the device until +a recognizable test announce string is read. It then executes an +appropriate set of commands to satisfy the test. This usually means +bringing up the PPP link by running pppd and maybe +executing various commands. It then either terminates the link itself, +or waits for the target to terminate it. It then goes back to looking +for another test announce string. If a string of the form +"BAUD:XXX" is received then the baud rate is +changed depending on the XXX value. If a +"FINISH" string is received it returns to waiting +for a "RedBoot>" prompt. The script repeats this +process until it is terminated with a signal. + + + + + + + + + + diff --git a/packages/net/ppp/current/include/cbcp.h b/packages/net/ppp/current/include/cbcp.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/cbcp.h @@ -0,0 +1,75 @@ +//========================================================================== +// +// include/cbcp.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +#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/packages/net/ppp/current/include/ccp.h b/packages/net/ppp/current/include/ccp.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/ccp.h @@ -0,0 +1,99 @@ +//========================================================================== +// +// include/ccp.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * ccp.h - Definitions for PPP Compression Control Protocol. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + * + * $FreeBSD: src/usr.sbin/pppd/ccp.h,v 1.8 1999/08/28 01:19:00 peter Exp $ + */ + +typedef struct ccp_options { + u_int bsd_compress: 1; /* do BSD Compress? */ + u_int deflate: 1; /* do Deflate? (RFC code) */ + u_int baddeflate: 1; /* do Deflate? (Magnalink!) */ + u_int predictor_1: 1; /* do Predictor-1? */ + u_int predictor_2: 1; /* do Predictor-2? */ + u_int deflate_correct: 1; /* use correct code for deflate? */ + u_int deflate_draft: 1; /* use draft RFC code for deflate? */ + u_short bsd_bits; /* # bits/code for BSD Compress */ + u_short deflate_size; /* lg(window size) for Deflate */ + u_short baddeflate_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/packages/net/ppp/current/include/chap.h b/packages/net/ppp/current/include/chap.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/chap.h @@ -0,0 +1,173 @@ +//========================================================================== +// +// include/chap.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/usr.sbin/pppd/chap.h,v 1.7 1999/08/28 01:19:01 peter Exp $ + */ + +#ifndef __CHAP_INCLUDE__ + +/* Code + ID + length */ +#define CHAP_HEADERLEN 4 + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ +#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths (for challenges we send) and other limits. + */ +#define MIN_CHALLENGE_LENGTH 32 +#define MAX_CHALLENGE_LENGTH 64 +#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ + +/* + * Each interface is described by a chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u_char chal_len; /* challenge length */ + u_char chal_id; /* ID of last challenge */ + u_char chal_type; /* hash algorithm for challenges */ + u_char id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u_char resp_length; /* length of response */ + u_char resp_id; /* ID for response messages */ + u_char resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ +} chap_state; + + +/* + * Client (peer) states. + */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ + +/* + * Server (authenticator) states. + */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ + +/* + * Timeouts. + */ +#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */ +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ + +extern chap_state chap[]; + +void ChapAuthWithPeer __P((int, char *, int)); +void ChapAuthPeer __P((int, char *, int)); + +extern struct protent chap_protent; + +#define __CHAP_INCLUDE__ +#endif /* __CHAP_INCLUDE__ */ diff --git a/packages/net/ppp/current/include/chap_ms.h b/packages/net/ppp/current/include/chap_ms.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/chap_ms.h @@ -0,0 +1,82 @@ +//========================================================================== +// +// include/chap_ms.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/usr.sbin/pppd/chap_ms.h,v 1.5 1999/08/28 01:19:02 peter Exp $ + */ + +#ifndef __CHAPMS_INCLUDE__ + +#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */ +#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ + +void ChapMS __P((chap_state *, char *, int, char *, int)); + +#define __CHAPMS_INCLUDE__ +#endif /* __CHAPMS_INCLUDE__ */ diff --git a/packages/net/ppp/current/include/fsm.h b/packages/net/ppp/current/include/fsm.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/fsm.h @@ -0,0 +1,193 @@ +//========================================================================== +// +// include/fsm.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/usr.sbin/pppd/fsm.h,v 1.7 1999/08/28 01:19:03 peter Exp $ + */ + +/* + * Packet header = Code, id, length. + */ +#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + + +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + int unit; /* Interface unit number */ + int protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + struct fsm_callbacks *callbacks; /* Callback routines */ + char *term_reason; /* Reason for closing protocol */ + int term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci) /* Reset our Configuration Information */ + __P((fsm *)); + int (*cilen) /* Length of our Configuration Information */ + __P((fsm *)); + void (*addci) /* Add our Configuration Information */ + __P((fsm *, u_char *, int *)); + int (*ackci) /* ACK our Configuration Information */ + __P((fsm *, u_char *, int)); + int (*nakci) /* NAK our Configuration Information */ + __P((fsm *, u_char *, int)); + int (*rejci) /* Reject our Configuration Information */ + __P((fsm *, u_char *, int)); + int (*reqci) /* Request peer's Configuration Information */ + __P((fsm *, u_char *, int *, int)); + void (*up) /* Called when fsm reaches OPENED state */ + __P((fsm *)); + void (*down) /* Called when fsm leaves OPENED state */ + __P((fsm *)); + void (*starting) /* Called when we want the lower layer */ + __P((fsm *)); + void (*finished) /* Called when we don't want the lower layer */ + __P((fsm *)); + void (*protreject) /* Called when Protocol-Reject received */ + __P((int)); + void (*retransmit) /* Retransmission is necessary */ + __P((fsm *)); + int (*extcode) /* Called when unknown code received */ + __P((fsm *, int, int, u_char *, int)); + char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/* + * Link states. + */ +#define INITIAL 0 /* Down, hasn't been opened */ +#define STARTING 1 /* Down, been opened */ +#define CLOSED 2 /* Up, hasn't been opened */ +#define STOPPED 3 /* Open, waiting for down event */ +#define CLOSING 4 /* Terminating the connection, not open */ +#define STOPPING 5 /* Terminating, but open */ +#define REQSENT 6 /* We've sent a Config Request */ +#define ACKRCVD 7 /* We've received a Config Ack */ +#define ACKSENT 8 /* We've sent a Config Ack */ +#define OPENED 9 /* Connection available */ + + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/* + * Timeouts. + */ +#define DEFTIMEOUT 3 /* Timeout time in seconds */ +#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ + + +/* + * Prototypes + */ +void fsm_init __P((fsm *)); +void fsm_lowerup __P((fsm *)); +void fsm_lowerdown __P((fsm *)); +void fsm_open __P((fsm *)); +void fsm_close __P((fsm *, char *)); +void fsm_input __P((fsm *, u_char *, int)); +void fsm_protreject __P((fsm *)); +void fsm_sdata __P((fsm *, int, int, u_char *, int)); + + +/* + * Variables + */ +extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ diff --git a/packages/net/ppp/current/include/ipcp.h b/packages/net/ppp/current/include/ipcp.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/ipcp.h @@ -0,0 +1,119 @@ +//========================================================================== +// +// include/ipcp.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/usr.sbin/pppd/ipcp.h,v 1.10 1999/08/28 01:19:03 peter 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 { + int neg_addr : 1; /* Negotiate IP Address? */ + int old_addrs : 1; /* Use old (IP-Addresses) option? */ + int req_addr : 1; /* Ask peer to send IP address? */ + int default_route : 1; /* Assign default route through interface? */ + int proxy_arp : 1; /* Make proxy ARP entry for peer? */ + int neg_vj : 1; /* Van Jacobson Compression? */ + int old_vj : 1; /* use old (short) form of VJ option? */ + int accept_local : 1; /* accept peer's value for ouraddr */ + int accept_remote : 1; /* accept peer's value for hisaddr */ + u_short vj_protocol; /* protocol value to use in VJ option */ + u_char maxslotindex, cflag; /* values for RFC1332 VJ compression neg. */ + u_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/packages/net/ppp/current/include/lcp.h b/packages/net/ppp/current/include/lcp.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/lcp.h @@ -0,0 +1,137 @@ +//========================================================================== +// +// include/lcp.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/usr.sbin/pppd/lcp.h,v 1.7 1999/08/28 01:19:05 peter Exp $ + */ + +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_CALLBACK 13 /* callback */ + +/* + * LCP-specific packet types. + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define CBCP_OPT 6 /* Use callback control protocol */ + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + int passive : 1; /* Don't die if we don't get a response */ + int silent : 1; /* Wait for the other end to start first */ + int restart : 1; /* Restart vs. exit after close */ + int neg_mru : 1; /* Negotiate the MRU? */ + int neg_asyncmap : 1; /* Negotiate the async map? */ + int neg_upap : 1; /* Ask for UPAP authentication? */ + int neg_chap : 1; /* Ask for CHAP authentication? */ + int neg_magicnumber : 1; /* Ask for magic number? */ + int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ + int neg_lqr : 1; /* Negotiate use of Link Quality Reports */ + int neg_cbcp : 1; /* Negotiate use of CBCP */ + u_short mru; /* Value of MRU */ + u_char chap_mdtype; /* which MD type (hashing algorithm) */ + u_int32_t asyncmap; /* Value of async map */ + u_int32_t magicnumber; + int numloops; /* Number of loops during magic number neg. */ + u_int32_t lqr_period; /* Reporting period for LQR 1/100ths second */ +} lcp_options; + +extern fsm lcp_fsm[]; +extern lcp_options lcp_wantoptions[]; +extern lcp_options lcp_gotoptions[]; +extern lcp_options lcp_allowoptions[]; +extern lcp_options lcp_hisoptions[]; +extern u_int32_t xmit_accm[][8]; + +#define DEFMRU 1500 /* Try for this */ +#define MINMRU 128 /* No MRUs below this */ +#define MAXMRU 16384 /* Normally limit MRU to this */ + +void lcp_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/packages/net/ppp/current/include/magic.h b/packages/net/ppp/current/include/magic.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/magic.h @@ -0,0 +1,72 @@ +//========================================================================== +// +// include/magic.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/usr.sbin/pppd/magic.h,v 1.7 1999/08/28 01:19:05 peter Exp $ + */ + +void magic_init __P((void)); /* Initialize the magic number generator */ +u_int32_t magic __P((void)); /* Returns the next magic number */ diff --git a/packages/net/ppp/current/include/names.h b/packages/net/ppp/current/include/names.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/names.h @@ -0,0 +1,331 @@ +#ifndef CYGONCE_PPP_NAMES_H +#define CYGONCE_PPP_NAMES_H +// ==================================================================== +// +// names.h +// +// PPP name remapping +// +// ==================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting the +// copyright holder. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +// ==================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2003-06-01 +// Purpose: Name Remapping +// Description: This header contains redefinitions of all the +// external names defined in the PPP package. This +// avoids polluting the namespace with name that +// might clash with application symbols. +// +//####DESCRIPTIONEND#### +// +// ==================================================================== + +// net/ppp/current/src/auth.c: +#define auth_check_options cyg_ppp_auth_check_options +#define auth_ip_addr cyg_ppp_auth_ip_addr +#define auth_peer_fail cyg_ppp_auth_peer_fail +#define auth_peer_success cyg_ppp_auth_peer_success +#define auth_reset cyg_ppp_auth_reset +#define auth_withpeer_fail cyg_ppp_auth_withpeer_fail +#define auth_withpeer_success cyg_ppp_auth_withpeer_success +#define bad_ip_adrs cyg_ppp_bad_ip_adrs +#define check_access cyg_ppp_check_access +#define check_passwd cyg_ppp_check_passwd +#define get_secret cyg_ppp_get_secret +#define link_down cyg_ppp_link_down +#define link_established cyg_ppp_link_established +#define link_required cyg_ppp_link_required +#define link_terminated cyg_ppp_link_terminated +#define np_down cyg_ppp_np_down +#define np_finished cyg_ppp_np_finished +#define np_up cyg_ppp_np_up +#define peer_authname cyg_ppp_peer_authname + +// net/ppp/current/src/bsd_comp.c: +#define ppp_bsd_compress cyg_ppp_ppp_bsd_compress + +// net/ppp/current/src/cbcp.c: +#define cbcp cyg_ppp_cbcp +#define cbcp_codenames cyg_ppp_cbcp_codenames +#define cbcp_optionnames cyg_ppp_cbcp_optionnames +#define cbcp_protent cyg_ppp_cbcp_protent + +// net/ppp/current/src/ccp.c: +#define ccp_allowoptions cyg_ppp_ccp_allowoptions +#define ccp_fsm cyg_ppp_ccp_fsm +#define ccp_gotoptions cyg_ppp_ccp_gotoptions +#define ccp_hisoptions cyg_ppp_ccp_hisoptions +#define ccp_protent cyg_ppp_ccp_protent +#define ccp_wantoptions cyg_ppp_ccp_wantoptions + +// net/ppp/current/src/chap.c: +#define ChapAuthPeer cyg_ppp_ChapAuthPeer +#define ChapAuthWithPeer cyg_ppp_ChapAuthWithPeer +#define chap cyg_ppp_chap +#define chap_protent cyg_ppp_chap_protent + +// net/ppp/current/src/fsm.c: +#define fsm_close cyg_ppp_fsm_close +#define fsm_init cyg_ppp_fsm_init +#define fsm_input cyg_ppp_fsm_input +#define fsm_lowerdown cyg_ppp_fsm_lowerdown +#define fsm_lowerup cyg_ppp_fsm_lowerup +#define fsm_open cyg_ppp_fsm_open +#define fsm_protreject cyg_ppp_fsm_protreject +#define fsm_sdata cyg_ppp_fsm_sdata +#define peer_mru cyg_ppp_peer_mru + +// net/ppp/current/src/if_ppp.c: +#define ppp_dequeue cyg_ppp_ppp_dequeue +#define ppp_restart cyg_ppp_ppp_restart +#define ppp_softc cyg_ppp_ppp_softc +#define pppalloc cyg_ppp_pppalloc +#define pppattach cyg_ppp_pppattach +#define pppdealloc cyg_ppp_pppdealloc +#define pppioctl cyg_ppp_pppioctl +#define pppoutput cyg_ppp_pppoutput +#define ppppktin cyg_ppp_ppppktin + +// net/ppp/current/src/ipcp.c: +#define ip_ntoa cyg_ppp_ip_ntoa +#define ipcp_allowoptions cyg_ppp_ipcp_allowoptions +#define ipcp_fsm cyg_ppp_ipcp_fsm +#define ipcp_gotoptions cyg_ppp_ipcp_gotoptions +#define ipcp_hisoptions cyg_ppp_ipcp_hisoptions +#define ipcp_protent cyg_ppp_ipcp_protent +#define ipcp_wantoptions cyg_ppp_ipcp_wantoptions + +// net/ppp/current/src/lcp.c: +#define lcp_allowoptions cyg_ppp_lcp_allowoptions +#define lcp_close cyg_ppp_lcp_close +#define lcp_fsm cyg_ppp_lcp_fsm +#define lcp_gotoptions cyg_ppp_lcp_gotoptions +#define lcp_hisoptions cyg_ppp_lcp_hisoptions +#define lcp_loopbackfail cyg_ppp_lcp_loopbackfail +#define lcp_lowerdown cyg_ppp_lcp_lowerdown +#define lcp_lowerup cyg_ppp_lcp_lowerup +#define lcp_open cyg_ppp_lcp_open +#define lcp_protent cyg_ppp_lcp_protent +#define lcp_sprotrej cyg_ppp_lcp_sprotrej +#define lcp_wantoptions cyg_ppp_lcp_wantoptions +#define xmit_accm cyg_ppp_xmit_accm + +// net/ppp/current/src/net_ppp_magic.o: +#define magic cyg_ppp_magic +#define magic_init cyg_ppp_magic_init + +// net/ppp/current/src/ppp_deflate.c: +#define ppp_deflate cyg_ppp_ppp_deflate +#define ppp_deflate_draft cyg_ppp_ppp_deflate_draft + +// net/ppp/current/src/pppd.c: +#define auth_required cyg_ppp_auth_required +#define baud_rate cyg_ppp_baud_rate +#define connector cyg_ppp_connector +#define flowctl cyg_ppp_flowctl +#define cryptpap cyg_ppp_cryptpap +#define debug cyg_ppp_debug +#define default_device cyg_ppp_default_device +#define devnam cyg_ppp_devnam +#define die cyg_ppp_die +#define disable_defaultip cyg_ppp_disable_defaultip +#define disconnector cyg_ppp_disconnector +#define etime cyg_ppp_etime +#define explicit_remote cyg_ppp_explicit_remote +#define fmtmsg cyg_ppp_fmtmsg +#define format_packet cyg_ppp_format_packet +#define holdoff cyg_ppp_holdoff +#define hungup cyg_ppp_hungup +#define idle_time_limit cyg_ppp_idle_time_limit +#define ifname cyg_ppp_ifname +//#define ifunit cyg_ppp_ifunit +#define inpacket_buf cyg_ppp_inpacket_buf +#define inspeed cyg_ppp_inspeed +#define ipparam cyg_ppp_ipparam +#define kdebugflag cyg_ppp_kdebugflag +#define kill_link cyg_ppp_kill_link +#define lcp_echo_fails cyg_ppp_lcp_echo_fails +#define lcp_echo_interval cyg_ppp_lcp_echo_interval +#define line cyg_ppp_line +#define linep cyg_ppp_linep +#define lockflag cyg_ppp_lockflag +#define log_packet cyg_ppp_log_packet +#define max_con_attempts cyg_ppp_max_con_attempts +#define maxconnect cyg_ppp_maxconnect +#define minutes cyg_ppp_minutes +#define modem cyg_ppp_modem +#define need_holdoff cyg_ppp_need_holdoff +#define netmask cyg_ppp_netmask +#define no_ppp_msg cyg_ppp_no_ppp_msg +#define novm cyg_ppp_novm +#define open_ccp_flag cyg_ppp_open_ccp_flag +#define our_name cyg_ppp_our_name +#define outpacket_buf cyg_ppp_outpacket_buf +#define passwd cyg_ppp_passwd +#define persist cyg_ppp_persist +#define phase cyg_ppp_phase +#define print_string cyg_ppp_print_string +#define privileged cyg_ppp_privileged +#define progname cyg_ppp_progname +#define protocols cyg_ppp_protocols +#define proxyarp cyg_ppp_proxyarp +#define quit cyg_ppp_quit +#define refuse_chap cyg_ppp_refuse_chap +#define refuse_pap cyg_ppp_refuse_pap +#define remote_name cyg_ppp_remote_name +#define s_env_nalloc cyg_ppp_s_env_nalloc +#define script cyg_ppp_script +#define script_env cyg_ppp_script_env +#define stime cyg_ppp_stime +#define tty_handle cyg_ppp_tty_handle +#define tty_mode cyg_ppp_tty_mode +#define usehostname cyg_ppp_usehostname +#define uselogin cyg_ppp_uselogin +#define user cyg_ppp_user +#define vfmtmsg cyg_ppp_vfmtmsg +#define welcomer cyg_ppp_welcomer + +// net/ppp/current/src/slcompress.c: +#define sl_compress_init cyg_ppp_sl_compress_init +#define sl_compress_tcp cyg_ppp_sl_compress_tcp +#define sl_uncompress_tcp cyg_ppp_sl_uncompress_tcp +#define sl_uncompress_tcp_core cyg_ppp_sl_uncompress_tcp_core + +// net/ppp/current/src/sys-ecos.c: +#define GetMask cyg_ppp_GetMask +#define MD5Final cyg_MD5Final +#define MD5Init cyg_MD5Init +#define MD5Update cyg_MD5Update +#define ccp_fatal_error cyg_ppp_ccp_fatal_error +#define ccp_flags_set cyg_ppp_ccp_flags_set +#define ccp_test cyg_ppp_ccp_test +#define cifaddr cyg_ppp_cifaddr +#define cifdefaultroute cyg_ppp_cifdefaultroute +#define cifproxyarp cyg_ppp_cifproxyarp +#define clean_check cyg_ppp_clean_check +#define crypt cyg_ppp_crypt +#define disestablish_ppp cyg_ppp_disestablish_ppp +#define drand48 cyg_ppp_drand48 +#define establish_ppp cyg_ppp_establish_ppp +#define get_host_seed cyg_ppp_get_host_seed +#define get_idle_time cyg_ppp_get_idle_time +#define gettimeofday cyg_ppp_gettimeofday +#define mrand48 cyg_ppp_mrand48 +#define netmask cyg_ppp_netmask +#define output cyg_ppp_output +#define ppp_available cyg_ppp_ppp_available +#define ppp_recv_config cyg_ppp_ppp_recv_config +#define ppp_send_config cyg_ppp_ppp_send_config +#define ppp_set_xaccm cyg_ppp_ppp_set_xaccm +#define ppp_tty cyg_ppp_ppp_tty +#define read_packet cyg_ppp_read_packet +#define restore_loop cyg_ppp_restore_loop +#define restore_tty cyg_ppp_restore_tty +#define set_up_tty cyg_ppp_set_up_tty +#define setdtr cyg_ppp_setdtr +#define sifaddr cyg_ppp_sifaddr +#define sifdefaultroute cyg_ppp_sifdefaultroute +#define sifdown cyg_ppp_sifdown +#define sifnpmode cyg_ppp_sifnpmode +#define sifproxyarp cyg_ppp_sifproxyarp +#define sifup cyg_ppp_sifup +#define sifvjcomp cyg_ppp_sifvjcomp +#define srand48 cyg_ppp_srand48 +#define sys_check_options cyg_ppp_sys_check_options +#define sys_cleanup cyg_ppp_sys_cleanup +#define sys_close cyg_ppp_sys_close +#define sys_exit cyg_ppp_sys_exit +#define sys_init cyg_ppp_sys_init +#define syslog cyg_ppp_syslog +#define wait_input cyg_ppp_wait_input +#define wait_time cyg_ppp_wait_time + +// net/ppp/current/src/upap.c: +#define pap_protent cyg_ppp_pap_protent +#define upap cyg_ppp_upap +#define upap_authpeer cyg_ppp_upap_authpeer +#define upap_authwithpeer cyg_ppp_upap_authwithpeer + +// net/ppp/current/src/zlib.c: +#define _tr_align cyg_ppp__tr_align +#define _tr_flush_block cyg_ppp__tr_flush_block +#define _tr_init cyg_ppp__tr_init +#define _tr_stored_block cyg_ppp__tr_stored_block +#define _tr_stored_type_only cyg_ppp__tr_stored_type_only +#define _tr_tally cyg_ppp__tr_tally +#define adler32 cyg_ppp_adler32 +#define deflate cyg_ppp_deflate +#define deflateCopy cyg_ppp_deflateCopy +#define deflateEnd cyg_ppp_deflateEnd +#define deflateInit2_ cyg_ppp_deflateInit2_ +#define deflateInit_ cyg_ppp_deflateInit_ +#define deflateOutputPending cyg_ppp_deflateOutputPending +#define deflateParams cyg_ppp_deflateParams +#define deflateReset cyg_ppp_deflateReset +#define deflateSetDictionary cyg_ppp_deflateSetDictionary +#define deflate_copyright cyg_ppp_deflate_copyright +#define inflateEnd cyg_ppp_inflateEnd +#define inflateIncomp cyg_ppp_inflateIncomp +#define inflateInit2_ cyg_ppp_inflateInit2_ +#define inflateInit_ cyg_ppp_inflateInit_ +#define inflateReset cyg_ppp_inflateReset +#define inflateSetDictionary cyg_ppp_inflateSetDictionary +#define inflateSync cyg_ppp_inflateSync +#define inflate_addhistory cyg_ppp_inflate_addhistory +#define inflate_blocks cyg_ppp_inflate_blocks +#define inflate_blocks_free cyg_ppp_inflate_blocks_free +#define inflate_blocks_new cyg_ppp_inflate_blocks_new +#define inflate_blocks_reset cyg_ppp_inflate_blocks_reset +#define inflate_codes cyg_ppp_inflate_codes +#define inflate_codes_free cyg_ppp_inflate_codes_free +#define inflate_codes_new cyg_ppp_inflate_codes_new +#define inflate_copyright cyg_ppp_inflate_copyright +#define inflate_fast cyg_ppp_inflate_fast +#define inflate_flush cyg_ppp_inflate_flush +#define inflate_mask cyg_ppp_inflate_mask +#define inflate_packet_flush cyg_ppp_inflate_packet_flush +#define inflate_ppp cyg_ppp_inflate_ppp +#define inflate_set_dictionary cyg_ppp_inflate_set_dictionary +#define inflate_trees_bits cyg_ppp_inflate_trees_bits +#define inflate_trees_dynamic cyg_ppp_inflate_trees_dynamic +#define inflate_trees_fixed cyg_ppp_inflate_trees_fixed +#define inflate_trees_free cyg_ppp_inflate_trees_free +#define zlibVersion cyg_ppp_zlibVersion + +// ==================================================================== +#endif // CYGONCE_PPP_NAMES_H diff --git a/packages/net/ppp/current/include/net/bpf.h b/packages/net/ppp/current/include/net/bpf.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/net/bpf.h @@ -0,0 +1,371 @@ +//========================================================================== +// +// include/net/bpf.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * Copyright (c) 1990, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * 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. 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. + * + * @(#)bpf.h 8.1 (Berkeley) 6/10/93 + * @(#)bpf.h 1.34 (LBL) 6/16/96 + * + * $FreeBSD: src/sys/net/bpf.h,v 1.21.2.3 2001/08/01 00:23:13 fenner Exp $ + */ + +#ifndef _NET_BPF_H_ +#define _NET_BPF_H_ + +/* BSD style release date */ +#define BPF_RELEASE 199606 + +typedef int32_t bpf_int32; +typedef u_int32_t bpf_u_int32; + +/* + * Alignment macros. BPF_WORDALIGN rounds up to the next + * even multiple of BPF_ALIGNMENT. + */ +#define BPF_ALIGNMENT sizeof(long) +#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) + +#define BPF_MAXINSNS 512 +#define BPF_MAXBUFSIZE 0x80000 +#define BPF_MINBUFSIZE 32 + +/* + * Structure for BIOCSETF. + */ +struct bpf_program { + u_int bf_len; + struct bpf_insn *bf_insns; +}; + +/* + * Struct returned by BIOCGSTATS. + */ +struct bpf_stat { + u_int bs_recv; /* number of packets received */ + u_int bs_drop; /* number of packets dropped */ +}; + +/* + * Struct return by BIOCVERSION. This represents the version number of + * the filter language described by the instruction encodings below. + * bpf understands a program iff kernel_major == filter_major && + * kernel_minor >= filter_minor, that is, if the value returned by the + * running kernel has the same major number and a minor number equal + * equal to or less than the filter being downloaded. Otherwise, the + * results are undefined, meaning an error may be returned or packets + * may be accepted haphazardly. + * It has nothing to do with the source code version. + */ +struct bpf_version { + u_short bv_major; + u_short bv_minor; +}; +/* Current version number of filter architecture. */ +#define BPF_MAJOR_VERSION 1 +#define BPF_MINOR_VERSION 1 + +#define BIOCGBLEN _IOR('B',102, u_int) +#define BIOCSBLEN _IOWR('B',102, u_int) +#define BIOCSETF _IOW('B',103, struct bpf_program) +#define BIOCFLUSH _IO('B',104) +#define BIOCPROMISC _IO('B',105) +#define BIOCGDLT _IOR('B',106, u_int) +#define BIOCGETIF _IOR('B',107, struct ifreq) +#define BIOCSETIF _IOW('B',108, struct ifreq) +#define BIOCSRTIMEOUT _IOW('B',109, struct timeval) +#define BIOCGRTIMEOUT _IOR('B',110, struct timeval) +#define BIOCGSTATS _IOR('B',111, struct bpf_stat) +#define BIOCIMMEDIATE _IOW('B',112, u_int) +#define BIOCVERSION _IOR('B',113, struct bpf_version) +#define BIOCGRSIG _IOR('B',114, u_int) +#define BIOCSRSIG _IOW('B',115, u_int) +#define BIOCGHDRCMPLT _IOR('B',116, u_int) +#define BIOCSHDRCMPLT _IOW('B',117, u_int) +#define BIOCGSEESENT _IOR('B',118, u_int) +#define BIOCSSEESENT _IOW('B',119, u_int) + +/* + * Structure prepended to each packet. + */ +struct bpf_hdr { + struct timeval bh_tstamp; /* time stamp */ + bpf_u_int32 bh_caplen; /* length of captured portion */ + bpf_u_int32 bh_datalen; /* original length of packet */ + u_short bh_hdrlen; /* length of bpf header (this struct + plus alignment padding) */ +}; +/* + * Because the structure above is not a multiple of 4 bytes, some compilers + * will insist on inserting padding; hence, sizeof(struct bpf_hdr) won't work. + * Only the kernel needs to know about it; applications use bh_hdrlen. + */ +#ifdef _KERNEL +#define SIZEOF_BPF_HDR (sizeof(struct bpf_hdr) <= 20 ? 18 : \ + sizeof(struct bpf_hdr)) +#endif + +/* + * Data-link level type codes. + */ +#define DLT_NULL 0 /* no link-layer encapsulation */ +#define DLT_EN10MB 1 /* Ethernet (10Mb) */ +#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */ +#define DLT_AX25 3 /* Amateur Radio AX.25 */ +#define DLT_PRONET 4 /* Proteon ProNET Token Ring */ +#define DLT_CHAOS 5 /* Chaos */ +#define DLT_IEEE802 6 /* IEEE 802 Networks */ +#define DLT_ARCNET 7 /* ARCNET */ +#define DLT_SLIP 8 /* Serial Line IP */ +#define DLT_PPP 9 /* Point-to-point Protocol */ +#define DLT_FDDI 10 /* FDDI */ +#define DLT_ATM_RFC1483 11 /* LLC/SNAP encapsulated atm */ +#define DLT_RAW 12 /* raw IP */ + +/* + * These are values from BSD/OS's "bpf.h". + * These are not the same as the values from the traditional libpcap + * "bpf.h"; however, these values shouldn't be generated by any + * OS other than BSD/OS, so the correct values to use here are the + * BSD/OS values. + * + * Platforms that have already assigned these values to other + * DLT_ codes, however, should give these codes the values + * from that platform, so that programs that use these codes will + * continue to compile - even though they won't correctly read + * files of these types. + */ +#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */ +#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */ + +#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */ + +/* + * This value is defined by NetBSD; other platforms should refrain from + * using it for other purposes, so that NetBSD savefiles with a link + * type of 50 can be read as this type on all platforms. + */ +#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */ + +/* + * This value was defined by libpcap 0.5; platforms that have defined + * it with a different value should define it here with that value - + * a link type of 104 in a save file will be mapped to DLT_C_HDLC, + * whatever value that happens to be, so programs will correctly + * handle files with that link type regardless of the value of + * DLT_C_HDLC. + * + * The name DLT_C_HDLC was used by BSD/OS; we use that name for source + * compatibility with programs written for BSD/OS. + * + * libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well, + * for source compatibility with programs written for libpcap 0.5. + */ +#define DLT_C_HDLC 104 /* Cisco HDLC */ +#define DLT_CHDLC DLT_C_HDLC + +/* + * Reserved for future use. + * Do not pick other numerical value for these unless you have also + * picked up the tcpdump.org top-of-CVS-tree version of "savefile.c", + * which will arrange that capture files for these DLT_ types have + * the same "network" value on all platforms, regardless of what + * value is chosen for their DLT_ type (thus allowing captures made + * on one platform to be read on other platforms, even if the two + * platforms don't use the same numerical values for all DLT_ types). + */ +#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */ + +/* + * Values between 106 and 107 are used in capture file headers as + * link-layer types corresponding to DLT_ types that might differ + * between platforms; don't use those values for new DLT_ new types. + */ + +/* + * OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except + * that the AF_ type in the link-layer header is in network byte order. + * + * OpenBSD defines it as 12, but that collides with DLT_RAW, so we + * define it as 108 here. If OpenBSD picks up this file, it should + * define DLT_LOOP as 12 in its version, as per the comment above - + * and should not use 108 for any purpose. + */ +#define DLT_LOOP 108 + +/* + * Values between 109 and 112 are used in capture file headers as + * link-layer types corresponding to DLT_ types that might differ + * between platforms; don't use those values for new DLT_ new types. + */ + +/* + * This is for Linux cooked sockets. + */ +#define DLT_LINUX_SLL 113 + +/* + * The instruction encodings. + */ +/* instruction classes */ +#define BPF_CLASS(code) ((code) & 0x07) +#define BPF_LD 0x00 +#define BPF_LDX 0x01 +#define BPF_ST 0x02 +#define BPF_STX 0x03 +#define BPF_ALU 0x04 +#define BPF_JMP 0x05 +#define BPF_RET 0x06 +#define BPF_MISC 0x07 + +/* ld/ldx fields */ +#define BPF_SIZE(code) ((code) & 0x18) +#define BPF_W 0x00 +#define BPF_H 0x08 +#define BPF_B 0x10 +#define BPF_MODE(code) ((code) & 0xe0) +#define BPF_IMM 0x00 +#define BPF_ABS 0x20 +#define BPF_IND 0x40 +#define BPF_MEM 0x60 +#define BPF_LEN 0x80 +#define BPF_MSH 0xa0 + +/* alu/jmp fields */ +#define BPF_OP(code) ((code) & 0xf0) +#define BPF_ADD 0x00 +#define BPF_SUB 0x10 +#define BPF_MUL 0x20 +#define BPF_DIV 0x30 +#define BPF_OR 0x40 +#define BPF_AND 0x50 +#define BPF_LSH 0x60 +#define BPF_RSH 0x70 +#define BPF_NEG 0x80 +#define BPF_JA 0x00 +#define BPF_JEQ 0x10 +#define BPF_JGT 0x20 +#define BPF_JGE 0x30 +#define BPF_JSET 0x40 +#define BPF_SRC(code) ((code) & 0x08) +#define BPF_K 0x00 +#define BPF_X 0x08 + +/* ret - BPF_K and BPF_X also apply */ +#define BPF_RVAL(code) ((code) & 0x18) +#define BPF_A 0x10 + +/* misc */ +#define BPF_MISCOP(code) ((code) & 0xf8) +#define BPF_TAX 0x00 +#define BPF_TXA 0x80 + +/* + * The instruction data structure. + */ +struct bpf_insn { + u_short code; + u_char jt; + u_char jf; + bpf_u_int32 k; +}; + +/* + * Macros for insn array initializers. + */ +#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } +#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } + +#ifdef _KERNEL +int bpf_validate __P((const struct bpf_insn *, int)); +void bpf_tap __P((struct ifnet *, u_char *, u_int)); +void bpf_mtap __P((struct ifnet *, struct mbuf *)); +void bpfattach __P((struct ifnet *, u_int, u_int)); +void bpfdetach __P((struct ifnet *)); + +void bpfilterattach __P((int)); +u_int bpf_filter __P((const struct bpf_insn *, u_char *, u_int, u_int)); +#endif + +/* + * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). + */ +#define BPF_MEMWORDS 16 + +#endif diff --git a/packages/net/ppp/current/include/net/if_ppp.h b/packages/net/ppp/current/include/net/if_ppp.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/net/if_ppp.h @@ -0,0 +1,190 @@ +//========================================================================== +// +// include/net/if_ppp.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/sys/net/if_ppp.h,v 1.14 1999/08/28 00:48:20 peter Exp $ + */ + +#ifndef _IF_PPP_H_ +#define _IF_PPP_H_ + +#include + +#include + +/* XXX this used to be self-contained. */ +#include +#include + +/* + * Packet sizes + */ +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#define PPP_MAXMRU 65000 /* Largest MRU we allow */ +#define PPP_MAXMTU 16384 /* Largest MTU we allow */ + +/* + * 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_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 initiated */ +#define SC_DECOMP_RUN 0x00002000 /* decompressor has been initiated */ +#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 */ +#define PPPIOCSPASS _IOW('t', 71, struct bpf_program) /* set pass filter */ +#define PPPIOCSACTIVE _IOW('t', 70, struct bpf_program) /* set active filt */ + +/* 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 + +#endif /* _IF_PPP_H_ */ diff --git a/packages/net/ppp/current/include/net/if_pppvar.h b/packages/net/ppp/current/include/net/if_pppvar.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/net/if_pppvar.h @@ -0,0 +1,161 @@ +//========================================================================== +// +// include/net/if_pppvar.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * if_pppvar.h - private structures and declarations for PPP. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/sys/net/if_pppvar.h,v 1.15 2000/01/29 16:56:23 peter Exp $ + */ + +/* + * Supported network protocols. These values are used for + * indexing sc_npmode. + */ +#define NP_IP 0 /* Internet Protocol */ +#define NUM_NP 1 /* Number of NPs. */ + +/* + * Structure describing each ppp unit. + */ +struct ppp_softc { + struct ifnet sc_if; /* network-visible interface */ +/*hi*/ u_int sc_flags; /* control/status bits; see if_ppp.h */ +// struct callout_handle sc_ch; /* Used for scheduling timeouts */ + void *sc_devp; /* pointer to device-dep structure */ +// int sc_fd; /* TTY file descriptor */ + void (*sc_start) __P((struct ppp_softc *)); /* start output proc */ + void (*sc_ctlp) __P((struct ppp_softc *)); /* rcvd control pkt */ + void (*sc_relinq) __P((struct ppp_softc *)); /* relinquish ifunit */ + void (*sc_setmtu) __P((struct ppp_softc *)); /* set mtu */ + short sc_mru; /* max receive unit */ + pid_t sc_xfer; /* used in transferring unit */ +/*hi*/ struct ifqueue sc_rawq; /* received packets */ +/*net*/ struct ifqueue sc_inq; /* queue of input packets for daemon */ +/*net*/ struct ifqueue sc_fastq; /* interactive output packet q */ + struct mbuf *sc_npqueue; /* output packets not to be sent yet */ + struct mbuf **sc_npqtail; /* ptr to last next ptr in npqueue */ + struct pppstat sc_stats; /* count of bytes/pkts sent/rcvd */ + enum NPmode sc_npmode[NUM_NP]; /* what to do with each NP */ + struct compressor *sc_xcomp; /* transmit compressor */ + void *sc_xc_state; /* transmit compressor state */ + struct compressor *sc_rcomp; /* receive decompressor */ + void *sc_rc_state; /* receive decompressor state */ + time_t sc_last_sent; /* time (secs) last NP pkt sent */ + time_t sc_last_recv; /* time (secs) last NP pkt rcvd */ +#ifdef PPP_FILTER + struct bpf_program sc_pass_filt; /* filter for packets to pass */ + struct bpf_program sc_active_filt; /* filter for "non-idle" packets */ +#endif /* PPP_FILTER */ +#ifdef VJC + struct slcompress *sc_comp; /* vjc control buffer */ +#endif + + /* Device-dependent part for async lines. */ + ext_accm sc_asyncmap; /* async control character map */ + u_long sc_rasyncmap; /* receive async control char map */ + struct mbuf *sc_outm; /* mbuf chain currently being output */ + struct mbuf *sc_m; /* pointer to input mbuf chain */ + struct mbuf *sc_mc; /* pointer to current input mbuf */ + char *sc_mp; /* ptr to next char in input mbuf */ + short sc_ilen; /* length of input packet so far */ + u_short sc_fcs; /* FCS so far (input) */ + u_short sc_outfcs; /* FCS so far for output packet */ + u_char sc_rawin[16]; /* chars as received */ + int sc_rawin_count; /* # in sc_rawin */ +}; + +extern struct ppp_softc ppp_softc[]; + +struct ppp_softc *pppalloc __P((pid_t pid)); +void pppdealloc __P((struct ppp_softc *sc)); +int pppioctl __P((struct ppp_softc *sc, u_long cmd, caddr_t data, + int flag, struct proc *p)); +int pppoutput __P((struct ifnet *ifp, struct mbuf *m0, + struct sockaddr *dst, struct rtentry *rtp)); +void ppp_restart __P((struct ppp_softc *sc)); +void ppppktin __P((struct ppp_softc *sc, struct mbuf *m, int lost)); +struct mbuf *ppp_dequeue __P((struct ppp_softc *sc)); diff --git a/packages/net/ppp/current/include/net/ppp_comp.h b/packages/net/ppp/current/include/net/ppp_comp.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/net/ppp_comp.h @@ -0,0 +1,216 @@ +//========================================================================== +// +// include/net/ppp_comp.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * ppp_comp.h - Definitions for doing PPP packet compression. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + * + * $FreeBSD: src/sys/net/ppp_comp.h,v 1.8 1999/08/28 00:48:25 peter Exp $ + */ + +#ifndef _NET_PPP_COMP_H +#define _NET_PPP_COMP_H + +#include + +/* + * The following symbols control whether we include code for + * various compression methods. + */ +#ifndef DO_BSD_COMPRESS +#define DO_BSD_COMPRESS 1 /* by default, include BSD-Compress */ +#endif +#ifndef DO_DEFLATE +#define DO_DEFLATE 1 /* by default, include Deflate */ +#endif +#define DO_PREDICTOR_1 0 +#define DO_PREDICTOR_2 0 + +/* + * Structure giving methods for compression/decompression. + */ +#ifdef PACKETPTR +struct compressor { + int compress_proto; /* CCP compression protocol number */ + + /* Allocate space for a compressor (transmit side) */ + void *(*comp_alloc) __P((u_char *options, int opt_len)); + /* Free space used by a compressor */ + void (*comp_free) __P((void *state)); + /* Initialize a compressor */ + int (*comp_init) __P((void *state, u_char *options, int opt_len, + int unit, int hdrlen, int debug)); + /* Reset a compressor */ + void (*comp_reset) __P((void *state)); + /* Compress a packet */ + int (*compress) __P((void *state, PACKETPTR *mret, + PACKETPTR mp, int orig_len, int max_len)); + /* Return compression statistics */ + void (*comp_stat) __P((void *state, struct compstat *stats)); + + /* Allocate space for a decompressor (receive side) */ + void *(*decomp_alloc) __P((u_char *options, int opt_len)); + /* Free space used by a decompressor */ + void (*decomp_free) __P((void *state)); + /* Initialize a decompressor */ + int (*decomp_init) __P((void *state, u_char *options, int opt_len, + int unit, int hdrlen, int mru, int debug)); + /* Reset a decompressor */ + void (*decomp_reset) __P((void *state)); + /* Decompress a packet. */ + int (*decompress) __P((void *state, PACKETPTR mp, + PACKETPTR *dmpp)); + /* Update state for an incompressible packet received */ + void (*incomp) __P((void *state, PACKETPTR mp)); + /* Return decompression statistics */ + void (*decomp_stat) __P((void *state, struct compstat *stats)); +}; +#endif /* PACKETPTR */ + +/* + * Return values for decompress routine. + * We need to make these distinctions so that we can disable certain + * useful functionality, namely sending a CCP reset-request as a result + * of an error detected after decompression. This is to avoid infringing + * a patent held by Motorola. + * Don't you just lurve software patents. + */ +#define DECOMP_OK 0 /* everything went OK */ +#define DECOMP_ERROR 1 /* error detected before decomp. */ +#define DECOMP_FATALERROR 2 /* error detected after decomp. */ + +/* + * CCP codes. + */ +#define CCP_CONFREQ 1 +#define CCP_CONFACK 2 +#define CCP_TERMREQ 5 +#define CCP_TERMACK 6 +#define CCP_RESETREQ 14 +#define CCP_RESETACK 15 + +/* + * Max # bytes for a CCP option + */ +#define CCP_MAX_OPTION_LENGTH 32 + +/* + * Parts of a CCP packet. + */ +#define CCP_CODE(dp) ((dp)[0]) +#define CCP_ID(dp) ((dp)[1]) +#define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3]) +#define CCP_HDRLEN 4 + +#define CCP_OPT_CODE(dp) ((dp)[0]) +#define CCP_OPT_LENGTH(dp) ((dp)[1]) +#define CCP_OPT_MINLEN 2 + +/* + * Definitions for BSD-Compress. + */ +#define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */ +#define CILEN_BSD_COMPRESS 3 /* length of config. option */ + +/* Macros for handling the 3rd byte of the BSD-Compress config option. */ +#define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */ +#define BSD_VERSION(x) ((x) >> 5) /* version of option format */ +#define BSD_CURRENT_VERSION 1 /* current version number */ +#define BSD_MAKE_OPT(v, n) (((v) << 5) | (n)) + +#define BSD_MIN_BITS 9 /* smallest code size supported */ +#define BSD_MAX_BITS 15 /* largest code size supported */ + +/* + * Definitions for Deflate. + */ +#define CI_DEFLATE 26 /* config option for Deflate */ +#define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */ +#define CILEN_DEFLATE 4 /* length of its config option */ + +#define DEFLATE_MIN_SIZE 8 +#define DEFLATE_MAX_SIZE 15 +#define DEFLATE_METHOD_VAL 8 +#define DEFLATE_SIZE(x) (((x) >> 4) + DEFLATE_MIN_SIZE) +#define DEFLATE_METHOD(x) ((x) & 0x0F) +#define DEFLATE_MAKE_OPT(w) ((((w) - DEFLATE_MIN_SIZE) << 4) \ + + DEFLATE_METHOD_VAL) +#define DEFLATE_CHK_SEQUENCE 0 + +/* + * Definitions for other, as yet unsupported, compression methods. + */ +#define CI_PREDICTOR_1 1 /* config option for Predictor-1 */ +#define CILEN_PREDICTOR_1 2 /* length of its config option */ +#define CI_PREDICTOR_2 2 /* config option for Predictor-2 */ +#define CILEN_PREDICTOR_2 2 /* length of its config option */ + +#endif /* _NET_PPP_COMP_H */ diff --git a/packages/net/ppp/current/include/net/ppp_defs.h b/packages/net/ppp/current/include/net/ppp_defs.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/net/ppp_defs.h @@ -0,0 +1,206 @@ +//========================================================================== +// +// include/net/ppp_defs.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + * + * $FreeBSD: src/sys/net/ppp_defs.h,v 1.6.2.1 2001/07/03 11:01:41 ume Exp $ + */ + +#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) (((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_XNS 0x25 /* Xerox NS */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_IPX 0x2b /* IPX Datagram (RFC1552) */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#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 (RFC1552) */ +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ +#define PPP_IPV6 0x57 /* Internet Protocol version 6*/ +#define PPP_IPV6CP 0x8057 /* IPv6 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 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/packages/net/ppp/current/include/net/zlib.h b/packages/net/ppp/current/include/net/zlib.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/net/zlib.h @@ -0,0 +1,1064 @@ +//========================================================================== +// +// include/net/zlib.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* $FreeBSD: src/sys/net/zlib.h,v 1.7 1999/12/29 04:38:38 peter 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 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +++ zconf.h */ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-1996 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zconf.h,v 1.20 1996/07/02 15:09:28 me Exp $ */ + +#ifndef _ZCONF_H +#define _ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateReset z_inflateReset +# define compress z_compress +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table + +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif +#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) +# ifndef __32BIT__ +# define __32BIT__ +# endif +#endif +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#if defined(MSDOS) && !defined(__32BIT__) +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC) +# define STDC +#endif +#if (defined(__STDC__) || defined(__cplusplus)) && !defined(STDC) +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2 */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + 1 << (windowBits+2) + 1 << (memLevel+9) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR __far +# else +# define FAR far +# endif +#endif +#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) +# ifndef __32BIT__ +# define SMALL_MEDIUM +# define FAR __far +# endif +#endif +#ifndef FAR +# define FAR +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#if defined(__BORLANDC__) && defined(SMALL_MEDIUM) + /* Borland C/C++ ignores FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + + +/* Compile with -DZLIB_DLL for Windows DLL support */ +#if (defined(_WINDOWS) || defined(WINDOWS)) && defined(ZLIB_DLL) +# include +# define EXPORT WINAPI +#else +# define EXPORT +#endif + +#endif /* _ZCONF_H */ +/* --- zconf.h */ + +#define ZLIB_VERSION "1.0.4P" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms may be added later and will have the same + stream interface. + + For compression the application must provide the output buffer and + may optionally provide the input buffer for optimization. For decompression, + the application must provide the input buffer and may optionally provide + the output buffer for optimization. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The library does not install any signal handler. It is recommended to + add at least a handler for SIGSEGV when decompressing; the library checks + the consistency of the input data whenever possible but may go nuts + for some forms of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + const 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(). +*/ + +#if 1 //defined(__FreeBSD__) && defined(_KERNEL) +#define inflate inflate_ppp /* FreeBSD already has an inflate :-( */ +#endif + +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< +#include + +#include + +#include + +#define MAXNAMELEN 256 +#define MAXSECRETLEN 256 + +// ------------------------------------------------------------------------- +// PPP instance + +typedef CYG_ADDRWORD cyg_ppp_handle_t; + +// ------------------------------------------------------------------------- + +typedef struct +{ + unsigned int + debug : 1, /* Debug flag */ + kdebugflag : 5, /* Tell kernel to print debug messages */ + default_route : 1, /* Set up default route via PPP link */ + modem : 1, /* Use modem control lines */ + flowctl : 2, /* Flow control, see below */ + refuse_pap : 1, /* Don't wanna auth. ourselves with PAP */ + refuse_chap : 1 /* Don't wanna auth. ourselves with CHAP */ + ; + + cyg_serial_baud_rate_t baud; /* serial line baud rate */ + + int idle_time_limit; /* Shut down link if idle for this long */ + int maxconnect; /* Maximum connect time (seconds) */ + + cyg_uint32 our_address; /* Our IP address */ + cyg_uint32 his_address; /* His IP address */ + + char **script; /* CHAT connection script */ + + char user[MAXNAMELEN]; /* Our name for authenticating ourselves */ + char passwd[MAXSECRETLEN]; /* Password for PAP and secret for CHAP */ + +} cyg_ppp_options_t; + +// ------------------------------------------------------------------------- +/* Values for flowctl field */ + +#define CYG_PPP_FLOWCTL_DEFAULT 0 /* Default to current setting */ +#define CYG_PPP_FLOWCTL_NONE 1 /* No flow control - not recommended */ +#define CYG_PPP_FLOWCTL_HARDWARE 2 /* Hardware flow control - CTS/RTS */ +#define CYG_PPP_FLOWCTL_SOFTWARE 3 /* Software flow control - XON/XOFF */ + +// ------------------------------------------------------------------------- + +externC cyg_int32 cyg_ppp_options_init( cyg_ppp_options_t *options ); + +// ------------------------------------------------------------------------- + +externC cyg_ppp_handle_t cyg_ppp_up( const char *devnam, + const cyg_ppp_options_t *options ); + +// ------------------------------------------------------------------------- + +externC cyg_int32 cyg_ppp_down( const cyg_ppp_handle_t handle ); + + +// ------------------------------------------------------------------------- + +externC cyg_int32 cyg_ppp_wait_up( cyg_ppp_handle_t handle ); + +externC void cyg_ppp_wait_down( cyg_ppp_handle_t handle ); + +// ------------------------------------------------------------------------- + +externC cyg_int32 cyg_ppp_chat( const char *devname, + const char *script[] ); + + +// ------------------------------------------------------------------------- +#endif // CYGONCE_PPP_PPP_H multiple inclusion protection +// EOF ppp.h diff --git a/packages/net/ppp/current/include/ppp_io.h b/packages/net/ppp/current/include/ppp_io.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/ppp_io.h @@ -0,0 +1,156 @@ +#ifndef CYGONCE_PPP_PPP_IO_H +#define CYGONCE_PPP_PPP_IO_H +// ==================================================================== +// +// ppp_io.h +// +// PPP IO interface +// +// ==================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting the +// copyright holder. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +// ==================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2003-06-01 +// Purpose: Internal PPP interfaces +// Description: This header describes the interfaces between +// the PPP daemon system dependent code in sys-ecos.c +// and the PPP line-discipline-derived code in ppp_io.c. +// +//####DESCRIPTIONEND#### +// +// ==================================================================== + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include + +// ==================================================================== +// Fake TTY structure +// +// This is passed into the line discipline code and pretends to be a +// tty device. In actual fact we use it to store all the state for the +// connection. + +struct tty +{ + // IO device handle. + cyg_io_handle_t t_handle; + + // Pointer to softc structure. This is the only field that the + // ex-BSD code actually accesses. + void *t_sc; + + // Pointer to user's options + const cyg_ppp_options_t *options; + + // Parameters for PPPD thread + cyg_handle_t pppd_thread; + volatile int pppd_wakeup; + volatile cyg_bool_t pppd_thread_running; + + // Parameters for transmit thread + cyg_sem_t tx_sem; + cyg_handle_t tx_thread; + volatile cyg_bool_t tx_thread_running; + +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS + // Serial line control + cyg_serial_line_status_callback_t serial_callbacks; + volatile cyg_uint32 carrier_detected; +#endif + + // Alarm for implementing wait_input() timeout. + cyg_handle_t alarm; + cyg_alarm alarm_obj; + +}; + +externC struct tty ppp_tty; + +// ==================================================================== +// This is called at the correct time in the PPPD to set up the +// options from the user's request. This has to be done after the +// protocols etc. have intitialized. + +externC void cyg_ppp_options_install( const cyg_ppp_options_t *options ); + +// ==================================================================== +// Definition of serial callback function. Used for carrier detection +// etc. + +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS +externC void cyg_ppp_serial_callback( cyg_serial_line_status_t *s, + CYG_ADDRWORD priv ); +#endif + +// ==================================================================== +// These are all renamed versions of the PPP line discipline +// functions. The PPPD code calls these directly to control the PPP +// driver and to send and receive control packets. + +extern int cyg_ppp_pppopen __P((struct tty *tp)); + +extern int cyg_ppp_ppptioctl __P((struct tty *tp, u_long cmd, caddr_t data, int flag)); + +extern int cyg_ppp_pppinput __P((int c, struct tty *tp)); + +extern int cyg_ppp_pppwrite __P((struct tty *tp, struct uio *uio, int flag)); + +extern int cyg_ppp_pppread __P((struct tty *tp, struct uio *uio, int flag)); + +extern int cyg_ppp_pppclose __P((struct tty *tp, int flag)); + +// ==================================================================== +// Start PPP transmission. This is called from the TX thread to cause +// any pending packets to be sent. + +extern int cyg_ppp_pppstart __P((struct tty *tp)); + +// ==================================================================== +#endif // CYGONCE_PPP_PPP_IO_H diff --git a/packages/net/ppp/current/include/pppd.h b/packages/net/ppp/current/include/pppd.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/pppd.h @@ -0,0 +1,549 @@ +//========================================================================== +// +// include/pppd.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/usr.sbin/pppd/pppd.h,v 1.13 1999/08/28 01:19:08 peter Exp $ + */ + +/* + * TODO: + */ + +#ifndef __PPPD_H__ +#define __PPPD_H__ + +#include +#include + +#include + +#include /* for FILE */ +#include /* for MAXPATHLEN and BSD4_4, if defined */ +#include /* for u_int32_t, if defined */ +#include /* for struct timeval */ +#include + +#if __STDC__ +#include +#define __V(x) x +#else +#include +#define __V(x) (va_alist) va_dcl +#define const +#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 */ + +/* + * Global variables. + */ + +extern int hungup; /* Physical layer has disconnected */ +extern int ifunit; /* Interface unit number */ +extern char ifname[]; /* Interface name */ +extern int ttyfd; /* Serial device file descriptor */ +extern char cyg_ppp_hostname[]; /* Our hostname */ +extern u_char outpacket_buf[]; /* Buffer for outgoing packets */ +extern int phase; /* Current state of link - see values below */ +extern int baud_rate; /* Current link speed in bits/sec */ +extern char *progname; /* Name of this program */ +extern int redirect_stderr;/* Connector's stderr should go to file */ +extern char peer_authname[];/* Authenticated name of peer */ +extern int privileged; /* We were run by real-uid root */ +extern int need_holdoff; /* Need holdoff period after link terminates */ +extern char **script_env; /* Environment variables for scripts */ +extern int detached; /* Have detached from controlling tty */ + +/* + * 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[]; /* Device name */ +extern int flowctl; /* Flow control */ +extern int modem; /* Use modem control lines */ +extern cyg_serial_baud_rate_t inspeed; /* Input/Output speed requested */ +extern u_int32_t netmask; /* IP netmask to set on interface */ +extern int lockflag; /* Create lock file to lock the serial dev */ +extern int nodetach; /* Don't detach from controlling tty */ +extern char *connector; /* Script to establish physical link */ +extern char *disconnector; /* Script to disestablish physical link */ +extern char *welcomer; /* Script to welcome client after connection */ +extern int max_con_attempts;/* Maximum number of times to try dialing */ +extern int maxconnect; /* Maximum connect time (seconds) */ +extern char user[]; /* Our name for authenticating ourselves */ +extern char passwd[]; /* Password for PAP */ +extern int auth_required; /* Peer is required to authenticate */ +extern int proxyarp; /* Set up proxy ARP entry for peer */ +extern int persist; /* Reopen link after it goes down */ +extern int uselogin; /* Use /etc/passwd for checking PAP */ +extern int lcp_echo_interval; /* Interval between LCP echo-requests */ +extern int lcp_echo_fails; /* Tolerance to unanswered echo-requests */ +extern char our_name[]; /* Our name for authentication purposes */ +extern char remote_name[]; /* Peer's name for authentication */ +extern int explicit_remote;/* remote_name specified with remotename opt */ +extern int usehostname; /* Use hostname for our_name */ +extern int disable_defaultip; /* Don't use hostname for default IP adrs */ +//extern int demand; /* Do dial-on-demand */ +#define demand 0 +extern char *ipparam; /* Extra parameter for ip up/down scripts */ +extern int 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 int refuse_pap; /* Don't wanna auth. ourselves with PAP */ +extern int refuse_chap; /* Don't wanna auth. ourselves with CHAP */ +#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 int ms_lanman; /* Nonzero if use LanMan password instead of NT */ + /* Has meaning only with MS-CHAP challenges */ +#endif + +/* + * Values for phase. + */ +#define PHASE_DEAD 0 +#define PHASE_INITIALIZE 1 +#define PHASE_DORMANT 2 +#define PHASE_ESTABLISH 3 +#define PHASE_AUTHENTICATE 4 +#define PHASE_CALLBACK 5 +#define PHASE_NETWORK 6 +#define PHASE_TERMINATE 7 +#define PHASE_HOLDOFF 8 + +/* + * 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)); + int enabled_flag; /* 0 iff protocol is disabled */ + char *name; /* Text name of protocol */ + /* 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[]; + +/* + * Prototypes. + */ + +/* Procedures exported from main.c. */ +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 cyg_ppp_timeout __P((void (*func)(void *), void *arg, int t)); + /* Call func(arg) after t seconds */ +void cyg_ppp_untimeout __P((void (*func)(void *), void *arg)); + /* Cancel call to func(arg) */ +int run_program __P((char *prog, char **args, int must_exist)); + /* Run program prog with args in child */ +void demuxprotrej __P((int, int)); + /* Demultiplex a Protocol-Reject */ +void format_packet __P((u_char *, int, void (*) (void *, char *, ...), + void *)); /* Format a packet in human-readable form */ +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 fmtmsg __P((char *, int, char *, ...)); /* sprintf++ */ +int vfmtmsg __P((char *, int, char *, va_list)); /* vsprintf++ */ +void script_setenv __P((char *, char *)); /* set script env var */ +void script_unsetenv __P((char *)); /* unset script env var */ + +/* Procedures exported from auth.c */ +void link_required __P((int)); /* we are starting to use the link */ +void link_terminated __P((int)); /* we are finished with the link */ +void link_down __P((int)); /* the LCP layer has left the Opened state */ +void link_established __P((int)); /* the link is up; authenticate now */ +void np_up __P((int, int)); /* a network protocol has come up */ +void np_down __P((int, int)); /* a network protocol has gone down */ +void np_finished __P((int, int)); /* a network protocol no longer needs link */ +void auth_peer_fail __P((int, int)); + /* peer failed to authenticate itself */ +void auth_peer_success __P((int, int, char *, int)); + /* peer successfully authenticated itself */ +void auth_withpeer_fail __P((int, int)); + /* we failed to authenticate ourselves */ +void auth_withpeer_success __P((int, int)); + /* we successfully authenticated ourselves */ +void auth_check_options __P((void)); + /* check authentication options supplied */ +void auth_reset __P((int)); /* check what secrets we have */ +int check_passwd __P((int, char *, int, char *, int, char **, int *)); + /* Check peer-supplied username/password */ +int get_secret __P((int, char *, char *, char *, int *, int)); + /* get "secret" for chap */ +int auth_ip_addr __P((int, u_int32_t)); + /* check if IP address is authorized */ +int bad_ip_adrs __P((u_int32_t)); + /* check if IP address is unreasonable */ +void check_access __P((FILE *, char *)); + /* check permissions on secrets file */ + +/* 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)); /* process frame from loopback */ + +/* Procedures exported from sys-*.c */ +void sys_init __P((void)); /* Do system-dependent initialization */ +void sys_cleanup __P((void)); /* Restore system state before exiting */ +void 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 */ +void open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */ +void establish_ppp __P((cyg_io_handle_t)); /* Turn serial port into a ppp interface */ +void restore_loop __P((void)); /* Transfer ppp unit back to loopback */ +void disestablish_ppp __P((cyg_io_handle_t)); /* Restore port to normal operation */ +void clean_check __P((void)); /* Check if line was 8-bit clean */ +void set_up_tty __P((cyg_io_handle_t, int)); /* Set up port's speed, parameters, etc. */ +void restore_tty __P((cyg_io_handle_t)); /* 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 wait_loop_output __P((struct timeval *)); + /* Wait for pkt from loopback, with timeout */ +void wait_time __P((struct timeval *)); /* Wait for given length of time */ +int read_packet __P((u_char *)); /* Read PPP packet */ +int get_loop_output __P((void)); /* Read pkts from loopback */ +void ppp_send_config __P((int, int, u_int32_t, int, int)); + /* Configure i/f transmit parameters */ +void ppp_set_xaccm __P((int, ext_accm)); + /* Set extended transmit ACCM */ +void ppp_recv_config __P((int, 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 sifvjcomp __P((int, int, int, int)); + /* Configure VJ TCP header compression */ +int sifup __P((int)); /* Configure i/f up (for IP) */ +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 IP) */ +int sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t)); + /* Configure IP addresses for i/f */ +int cifaddr __P((int, u_int32_t, u_int32_t)); + /* Reset i/f IP addresses */ +int sifdefaultroute __P((int, u_int32_t, u_int32_t)); + /* Create default route through i/f */ +int cifdefaultroute __P((int, u_int32_t, u_int32_t)); + /* Delete default route through i/f */ +int sifproxyarp __P((int, u_int32_t)); + /* Add proxy ARP entry for peer */ +int cifproxyarp __P((int, u_int32_t)); + /* Delete proxy ARP entry for peer */ +u_int32_t GetMask __P((u_int32_t)); /* Get appropriate netmask for address */ +int lock __P((char *)); /* Create lock file for device */ +void unlock __P((void)); /* Delete previously-created lock file */ +int daemon __P((int, int)); /* Detach us from terminal session */ +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 */ +#ifdef PPP_FILTER +int set_filters __P((struct bpf_program *pass, struct bpf_program *active)); + /* Set filter programs in kernel */ +#endif + +/* Procedures exported from options.c */ +int parse_args __P((int argc, char **argv)); + /* Parse options from arguments given */ +void usage __P((void)); /* Print a usage message */ +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 */ +void scan_args __P((int argc, char **argv)); + /* Look for tty name in command-line args */ +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 setipaddr __P((char *)); /* set IP addresses */ + + +/* + * This structure is used to store information about certain + * options, such as where the option value came from (/etc/ppp/options, + * command line, etc.) and whether it came from a privileged source. + */ + +struct option_info { + int priv; /* was value set by sysadmin? */ + char *source; /* where option came from */ +}; + +extern struct option_info auth_req_info; +extern struct option_info connector_info; +extern struct option_info disconnector_info; +extern struct option_info welcomer_info; +extern struct option_info devnam_info; + +/* + * 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)) + +#undef FALSE +#define FALSE 0 +#undef TRUE +#define TRUE 1 + +/* + * System dependent definitions for user-level 4.3BSD UNIX implementation. + */ + +#define DEMUXPROTREJ(u, p) demuxprotrej(u, p) + +#define TIMEOUT(r, f, t) cyg_ppp_timeout((r), (f), (t)) +#define UNTIMEOUT(r, f) cyg_ppp_untimeout((r), (f)) + +#define BCOPY(s, d, l) memcpy(d, s, l) +#define BZERO(s, n) memset(s, 0, n) +#define EXIT(u) quit() + +#define PRINTMSG(m, l) { m[l] = '\0'; syslog(LOG_INFO, "Remote message: %s", m); } + +/* + * MAKEHEADER - Add Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +//#define DEBUGALL + +#ifdef DEBUGALL +#define DEBUGMAIN 1 +#define DEBUGFSM 1 +#define DEBUGLCP 1 +#define DEBUGIPCP 1 +#define DEBUGUPAP 1 +#define DEBUGCHAP 1 +#endif + +#ifndef LOG_PPP /* we use LOG_LOCAL2 for syslog by default */ +#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUGSYS) \ + || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \ + || defined(DEBUGCHAP) || defined(DEBUG) +#define LOG_PPP LOG_LOCAL2 +#else +#define LOG_PPP LOG_DAEMON +#endif +#endif /* LOG_PPP */ + +#ifdef DEBUGMAIN +#define MAINDEBUG(x) if (debug) syslog x +#else +#define MAINDEBUG(x) +#endif + +#ifdef DEBUGSYS +#define SYSDEBUG(x) if (debug) syslog x +#else +#define SYSDEBUG(x) +#endif + +#ifdef DEBUGFSM +#define FSMDEBUG(x) if (debug) syslog x +#else +#define FSMDEBUG(x) +#endif + +#ifdef DEBUGLCP +#define LCPDEBUG(x) if (debug) syslog x +#else +#define LCPDEBUG(x) +#endif + +#ifdef DEBUGIPCP +#define IPCPDEBUG(x) if (debug) syslog x +#else +#define IPCPDEBUG(x) +#endif + +#ifdef DEBUGUPAP +#define UPAPDEBUG(x) if (debug) syslog x +#else +#define UPAPDEBUG(x) +#endif + +#ifdef DEBUGCHAP +#define CHAPDEBUG(x) if (debug) syslog x +#else +#define CHAPDEBUG(x) +#endif + +#ifdef DEBUGIPXCP +#define IPXCPDEBUG(x) if (debug) syslog x +#else +#define IPXCPDEBUG(x) +#endif + +#ifndef SIGTYPE +#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) +#define SIGTYPE void +#else +#define SIGTYPE int +#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */ +#endif /* SIGTYPE */ + +#ifndef MIN +#define MIN(a, b) ((a) < (b)? (a): (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) > (b)? (a): (b)) +#endif + +#endif /* __PPP_H__ */ diff --git a/packages/net/ppp/current/include/slcompress.h b/packages/net/ppp/current/include/slcompress.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/slcompress.h @@ -0,0 +1,209 @@ +//========================================================================== +// +// include/slcompress.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * Definitions for tcp compression routines. + * + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + * $FreeBSD: src/sys/net/slcompress.h,v 1.14.2.1 2000/05/05 13:37:06 jlemon Exp $ + */ + +#ifndef _NET_SLCOMPRESS_H_ +#define _NET_SLCOMPRESS_H_ + +#include + +#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, + * acknowledgement, 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_int16_t 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_int16_t 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 *, 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 /* !_NET_SLCOMPRESS_H_ */ diff --git a/packages/net/ppp/current/include/syslog.h b/packages/net/ppp/current/include/syslog.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/syslog.h @@ -0,0 +1,97 @@ +#ifndef CYGONCE_PPP_SYSLOG_H +#define CYGONCE_PPP_SYSLOG_H +// ==================================================================== +// +// syslog.h +// +// PPP syslog stub +// +// ==================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting the +// copyright holder. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +// ==================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2003-06-01 +// Purpose: Syslog +// Description: This header contains definitions for using syslog +// in the PPP code. +// +//####DESCRIPTIONEND#### +// +// ==================================================================== + +#include + +#include + +// ==================================================================== +// Syslog functions +// +// We ignore setlogmask() but syslog() is a simple stub that goes to +// diag_printf(). + +#define setlogmask(x) + +void syslog( int level, char *fmt, ... ); + +// ==================================================================== +// Log level flags +// +// These are duplicates of the defines in sys/param.h. + +#define LOG_ERR 0x0001 +#define LOG_WARNING 0x0002 +#define LOG_NOTICE 0x0004 +#define LOG_INFO 0x0008 +#define LOG_DEBUG 0x0010 +#define LOG_MDEBUG 0x0020 +#define LOG_IOCTL 0x0040 +#define LOG_ADDR 0x0100 +#define LOG_FAIL 0x0200 +#define LOG_INIT 0x0080 +#define LOG_EMERG 0x4000 +#define LOG_CRIT 0x8000 + +// ==================================================================== +// Debug facility +// +// Switch these defines over to enable some debugging messages + +//#define db_printf diag_printf +#define db_printf(fmt, ... ) + +// ==================================================================== +#endif // CYGONCE_PPP_SYSLOG_H diff --git a/packages/net/ppp/current/include/upap.h b/packages/net/ppp/current/include/upap.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/include/upap.h @@ -0,0 +1,136 @@ +//========================================================================== +// +// include/upap.h +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $FreeBSD: src/usr.sbin/pppd/upap.h,v 1.7 1999/08/28 01:19:09 peter Exp $ + */ + +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + + +/* + * Each interface is described by upap structure. + */ +typedef struct upap_state { + int us_unit; /* Interface unit number */ + char *us_user; /* User */ + int us_userlen; /* User length */ + char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + u_char us_id; /* Current id */ + int us_timeouttime; /* Timeout (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/packages/net/ppp/current/src/auth.c b/packages/net/ppp/current/src/auth.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/auth.c @@ -0,0 +1,890 @@ +//========================================================================== +// +// src/auth.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/auth.c,v 1.24.2.1 2000/12/11 01:03:37 obrien Exp $"; +#endif + +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +//#include +#include +#if defined(_PATH_LASTLOG) && defined(_linux_) +#include +#endif + +#include +#include +#include +#include +//#include + +#ifdef USE_PAM +#include +#endif + +#ifdef HAS_SHADOW +#include +#ifndef PW_PPP +#define PW_PPP PW_LOGIN +#endif +#endif + +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/fsm.h" +#include "cyg/ppp/lcp.h" +#include "cyg/ppp/ipcp.h" +#include "cyg/ppp/upap.h" +#include "cyg/ppp/chap.h" +#ifdef CBCP_SUPPORT +#include "cyg/ppp/cbcp.h" +#endif +//#include "cyg/ppp/pathnames.h" + +/* Used for storing a sequence of words. Usually malloced. */ +struct wordlist { + struct wordlist *next; + char word[1]; +}; + +/* Bits in scan_authfile return value */ +#define NONWILD_SERVER 1 +#define NONWILD_CLIENT 2 + +#define ISWILD(word) (word[0] == '*' && word[1] == 0) + +#define FALSE 0 +#define TRUE 1 + +/* The name by which the peer authenticated itself to us. */ +char peer_authname[MAXNAMELEN]; + +/* Records which authentication operations haven't completed yet. */ +static int auth_pending[NUM_PPP]; + +/* Set if we have successfully called plogin() */ +//static int logged_in; + +/* Set if not wild or blank */ +//static int non_wildclient; + +/* Set if we have run the /etc/ppp/auth-up script. */ +//static int did_authup; + +/* List of addresses which the peer may use. */ +static struct wordlist *addresses[NUM_PPP]; + +/* 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; + +/* Bits in auth_pending[] */ +#define PAP_WITHPEER 1 +#define PAP_PEER 2 +#define CHAP_WITHPEER 4 +#define CHAP_PEER 8 + +extern char *crypt __P((const char *, const char *)); + +/* Prototypes for procedures local to this file. */ + +static void network_phase __P((int)); +static void check_idle __P((void *)); +static void connect_time_expired __P((void *)); +static int null_login __P((int)); +static int get_pap_passwd __P((char *)); +static int have_pap_secret __P((void)); +static int have_chap_secret __P((char *, char *, u_int32_t)); +static int ip_addr_check __P((u_int32_t, struct wordlist *)); + +static void auth_set_ip_addr __P((int)); + +/* + * An Open on LCP has requested a change from Dead to Establish phase. + * Do what's necessary to bring the physical layer up. + */ +void +link_required(unit) + int unit; +{ +} + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void +link_terminated(unit) + int unit; +{ + extern time_t etime, stime; + extern int minutes; + +db_printf("%s()\n",__PRETTY_FUNCTION__); + if (phase == PHASE_DEAD) + return; + phase = PHASE_DEAD; + etime = time((time_t *) NULL); + minutes = (etime-stime)/60; + syslog(LOG_NOTICE, "Connection terminated, connected for %d minutes\n", + minutes > 1 ? minutes : 1); +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void +link_down(unit) + int unit; +{ + int i; + struct protent *protp; + +db_printf("%s()\n",__PRETTY_FUNCTION__); + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (!protp->enabled_flag) + continue; + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) + (*protp->lowerdown)(unit); + if (protp->protocol < 0xC000 && protp->close != NULL) + (*protp->close)(unit, "LCP down"); + } + num_np_open = 0; + num_np_up = 0; + if (phase != PHASE_DEAD) + phase = PHASE_TERMINATE; +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void +link_established(unit) + int unit; +{ + int auth; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ho = &lcp_hisoptions[unit]; + int i; + struct protent *protp; + + /* + * Tell higher-level protocols that LCP is up. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol != PPP_LCP && protp->enabled_flag + && protp->lowerup != NULL) + (*protp->lowerup)(unit); + + if (auth_required && !(go->neg_chap || go->neg_upap)) { + /* + * We wanted the peer to authenticate itself, and it refused: + * treat it as though it authenticated with PAP using a username + * of "" and a password of "". If that's not OK, boot it out. + */ + if (!wo->neg_upap || !null_login(unit)) { + syslog(LOG_WARNING, "peer refused to authenticate"); + lcp_close(unit, "peer refused to authenticate"); + return; + } + } + + phase = PHASE_AUTHENTICATE; + auth = 0; + if (go->neg_chap) { + ChapAuthPeer(unit, our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } else if (go->neg_upap) { + upap_authpeer(unit); + auth |= PAP_PEER; + } + if (ho->neg_chap) { + ChapAuthWithPeer(unit, user, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } else if (ho->neg_upap) { + if (passwd[0] == 0) { + passwd_from_file = 1; + if (!get_pap_passwd(passwd)) + syslog(LOG_ERR, "No secret found for PAP login"); + } + upap_authwithpeer(unit, user, passwd); + auth |= PAP_WITHPEER; + } + auth_pending[unit] = auth; + + if (!auth) + network_phase(unit); +} + +/* + * Proceed to the network phase. + */ +static void +network_phase(unit) + int unit; +{ + int i; + struct protent *protp; + +#ifdef CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + phase = PHASE_CALLBACK; + (*cbcp_protent.open)(unit); + return; + } +#endif + + phase = PHASE_NETWORK; + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol < 0xC000 && protp->enabled_flag + && protp->open != NULL) { + (*protp->open)(unit); + if (protp->protocol != PPP_CCP) + ++num_np_open; + } + + if (num_np_open == 0) + /* nothing to do */ + lcp_close(0, "No network protocols running"); +} + +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void +auth_peer_fail(unit, protocol) + int unit, protocol; +{ + /* + * Authentication failure: take the link down + */ + lcp_close(unit, "Authentication failed"); +} + +/* + * The peer has been successfully authenticated using `protocol'. + */ +void +auth_peer_success(unit, protocol, name, namelen) + int unit, protocol; + char *name; + int namelen; +{ + int bit; + + switch (protocol) { + case PPP_CHAP: + bit = CHAP_PEER; + break; + case PPP_PAP: + bit = PAP_PEER; + break; + default: + syslog(LOG_WARNING, "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; + + /* + * If we have overridden addresses based on auth info + * then set that information now before continuing. + */ + auth_set_ip_addr(unit); + + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~bit) == 0) + network_phase(unit); +} + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void +auth_withpeer_fail(unit, protocol) + int unit, protocol; +{ + if (passwd_from_file) + BZERO(passwd, MAXSECRETLEN); + /* + * We've failed to authenticate ourselves to our peer. + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void +auth_withpeer_success(unit, protocol) + int unit, protocol; +{ + int bit; + + switch (protocol) { + case PPP_CHAP: + bit = CHAP_WITHPEER; + break; + case PPP_PAP: + if (passwd_from_file) + BZERO(passwd, MAXSECRETLEN); + bit = PAP_WITHPEER; + break; + default: + syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x", + protocol); + bit = 0; + } + + /* + * If we have overridden addresses based on auth info + * then set that information now before continuing. + */ + auth_set_ip_addr(unit); + + /* + * 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; +{ + if (num_np_up == 0) { + /* + * At this point we consider that the link has come up successfully. + */ + need_holdoff = 0; + + if (idle_time_limit > 0) + TIMEOUT(check_idle, NULL, idle_time_limit); + + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (maxconnect > 0) + TIMEOUT(connect_time_expired, 0, maxconnect); + + } + ++num_np_up; +} + +/* + * np_down - a network protocol has gone down. + */ +void +np_down(unit, proto) + int unit, proto; +{ + if (--num_np_up == 0 && idle_time_limit > 0) { + UNTIMEOUT(check_idle, NULL); + } +} + +/* + * np_finished - a network protocol has finished using the link. + */ +void +np_finished(unit, proto) + int unit, proto; +{ + if (--num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(0, "No network protocols running"); + } +} + +/* + * check_idle - check whether the link has been idle for long + * enough that we can shut it down. + */ +static void +check_idle(arg) + void *arg; +{ + struct ppp_idle idle; + time_t itime; + + if (!get_idle_time(0, &idle)) + return; + itime = MIN(idle.xmit_idle, idle.recv_idle); + if (itime >= idle_time_limit) { + /* link is idle: shut it down. */ + syslog(LOG_INFO, "Terminating connection due to lack of activity."); + lcp_close(0, "Link inactive"); + } else { + TIMEOUT(check_idle, NULL, idle_time_limit - itime); + } +} + +/* + * connect_time_expired - log a message and close the connection. + */ +static void +connect_time_expired(arg) + void *arg; +{ + syslog(LOG_INFO, "Connect time expired"); + 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; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u_int32_t remote; + + /* Default our_name to hostname, and user to our_name */ + if (our_name[0] == 0 || usehostname) + strcpy(our_name, cyg_ppp_hostname); + if (user[0] == 0) + strcpy(user, our_name); + + /* If authentication is required, ask peer for CHAP or PAP. */ + if (auth_required && !wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. + */ + can_auth = wo->neg_upap && (uselogin || have_pap_secret()); + if (!can_auth && wo->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + can_auth = have_chap_secret(remote_name, our_name, remote); + } + + if (auth_required && !can_auth) { + option_error("peer authentication required but no suitable secret(s) found\n"); + if (remote_name[0] == 0) + option_error("for authenticating any peer to us (%s)\n", our_name); + else + option_error("for authenticating peer %s to us (%s)\n", + remote_name, our_name); + exit(1); + } + + /* + * Check whether the user tried to override certain values + * set by root. + */ + if (!auth_required && auth_req_info.priv > 0) { + if (!default_device && devnam_info.priv == 0) { + option_error("can't override device name when noauth option used"); + exit(1); + } + if ((connector != NULL && connector_info.priv == 0) + || (disconnector != NULL && disconnector_info.priv == 0) + || (welcomer != NULL && welcomer_info.priv == 0)) { + option_error("can't override connect, disconnect or welcome"); + option_error("option values when noauth option used"); + exit(1); + } + } +} + +/* + * auth_reset - called when LCP is starting negotiations to recheck + * authentication options, i.e. whether we have appropriate secrets + * to use for authenticating ourselves and/or the peer. + */ +void +auth_reset(unit) + int unit; +{ + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ao = &lcp_allowoptions[0]; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u_int32_t remote; + ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL)); + ao->neg_chap = !refuse_chap + && have_chap_secret(user, remote_name, (u_int32_t)0); + if (go->neg_upap && !uselogin && !have_pap_secret()) + go->neg_upap = 0; + if (go->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + if (!have_chap_secret(remote_name, our_name, remote)) + go->neg_chap = 0; + } +} + + +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +int +check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen) + int unit; + char *auser; + int userlen; + char *apasswd; + int passwdlen; + char **msg; + int *msglen; +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); + return 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; +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); + return 0; +} + + +/* + * 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. + */ +static int +get_pap_passwd(passwd) + char *passwd; +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); + return 0; +} + + +/* + * 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() +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); + return 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, remote) + char *client; + char *server; + u_int32_t remote; +{ +// db_printf("%s(%s,%s,%d) called\n", __PRETTY_FUNCTION__,client,server,remote); + return 1; +} + + +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int +get_secret(unit, client, server, secret, secret_len, save_addrs) + int unit; + char *client; + char *server; + char *secret; + int *secret_len; + int save_addrs; +{ + db_printf("%s(%d,%s,%s,%08x,%d,%d) called\n", __PRETTY_FUNCTION__, + unit, client, server, secret, secret_len, save_addrs); + // We use the PAP password as the CHAP secret also. + strncpy( secret, passwd, MAXNAMELEN ); + *secret_len = strlen(secret); + return 1; +} + +static void +auth_set_ip_addr(unit) + int unit; +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); +} + +/* + * 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; +{ + return ip_addr_check(addr, addresses[unit]); +} + +static int +ip_addr_check(addr, addrs) + u_int32_t addr; + struct wordlist *addrs; +{ + int x, y; +// u_int32_t a, mask, ah; + u_int32_t a = -1, mask; + int accept; + char *ptr_word, *ptr_mask; +// struct hostent *hp; +// struct netent *np; + + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) + return 0; + + if (addrs == NULL) + return !auth_required; /* no addresses authorized */ + + x = y = 0; + for (; addrs != NULL; addrs = addrs->next) { + y++; + /* "-" means no addresses authorized, "*" means any address allowed */ + ptr_word = addrs->word; + if (strcmp(ptr_word, "-") == 0) + break; + if (strcmp(ptr_word, "*") == 0) + return 1; + + /* + * A colon in the string means that we wish to force a specific + * local:remote address, but we ignore these for now. + */ + if (strchr(addrs->word, ':') != NULL) + x++; + else { + + accept = 1; + if (*ptr_word == '!') { + accept = 0; + ++ptr_word; + } + + mask = ~ (u_int32_t) 0; + ptr_mask = strchr (ptr_word, '/'); + if (ptr_mask != NULL) { + int bit_count; + + bit_count = (int) strtol (ptr_mask+1, (char **) 0, 10); + if (bit_count <= 0 || bit_count > 32) { + syslog (LOG_WARNING, + "invalid address length %s in auth. address list", + ptr_mask); + continue; + } + *ptr_mask = '\0'; + mask <<= 32 - bit_count; + } + +#ifndef __ECOS + 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); + } + } +#endif + + if (ptr_mask != NULL) + *ptr_mask = '/'; + + if (a == (u_int32_t)-1L) + syslog (LOG_WARNING, + "unknown host %s in auth. address list", + addrs->word); + else + /* Here a and addr are in network byte order, + and mask is in host order. */ + if (((addr ^ a) & htonl(mask)) == 0) + return accept; + } /* else */ + } + return x == y; /* not in list => can't have it */ +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(addr) + u_int32_t addr; +{ + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + +/* + * check_access - complain if a secret file has too-liberal permissions. + */ +void +check_access(f, filename) + FILE *f; + char *filename; +{ + struct stat sbuf; + + if (fstat(fileno(f), &sbuf) < 0) { + syslog(LOG_WARNING, "cannot stat secret file %s: %m", filename); + } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + syslog(LOG_WARNING, "Warning - secret file %s has world and/or group access", filename); + } +} diff --git a/packages/net/ppp/current/src/bsd_comp.c b/packages/net/ppp/current/src/bsd_comp.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/bsd_comp.c @@ -0,0 +1,1160 @@ +//========================================================================== +// +// src/bsd_comp.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* 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. 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 mbufs on BSD-derived systems. + * + * $FreeBSD: src/sys/net/bsd_comp.c,v 1.11 1999/08/28 00:48:14 peter Exp $ + */ + +#define _KERNEL + +#include +//#include +#include +#include +#include + +#define PACKETPTR struct mbuf * +#include + +#if DO_BSD_COMPRESS +/* + * PPP "BSD compress" compression + * The differences between this compression and the classic BSD LZW + * source are obvious from the requirement that the classic code worked + * with files while this handles arbitrarily long streams that + * are broken into packets. They are: + * + * When the code size expands, a block of junk is not emitted by + * the compressor and not expected by the decompressor. + * + * New codes are not necessarily assigned every time an old + * code is output by the compressor. This is because a packet + * end forces a code to be emitted, but does not imply that a + * new sequence has been seen. + * + * The compression ratio is checked at the first end of a packet + * after the appropriate gap. Besides simplifying and speeding + * things up, this makes it more likely that the transmitter + * and receiver will agree when the dictionary is cleared when + * compression is not going well. + */ + +/* + * A dictionary for doing BSD compress. + */ +struct bsd_db { + int totlen; /* length of this structure */ + u_int hsize; /* size of the hash table */ + u_char hshift; /* used in hash function */ + u_char n_bits; /* current bits/code */ + u_char maxbits; + u_char debug; + u_char unit; + u_int16_t seqno; /* sequence # 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_int16_t *lens; /* array of lengths of codes */ + struct bsd_dict { + union { /* hash value */ + u_int32_t fcode; + struct { +#if BYTE_ORDER == LITTLE_ENDIAN + u_int16_t 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_int16_t prefix; /* preceding code */ +#endif + } hs; + } f; + u_int16_t codem1; /* output of hash table -1 */ + u_int16_t 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_clear __P((struct bsd_db *db)); +static int bsd_check __P((struct bsd_db *db)); +static void *bsd_alloc __P((u_char *options, int opt_len, int decomp)); +static int bsd_init __P((struct bsd_db *db, u_char *options, int opt_len, + int unit, int hdrlen, int mru, int debug, + int decomp)); +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, struct mbuf **mret, + struct mbuf *mp, int slen, int maxolen)); +static void bsd_incomp __P((void *state, struct mbuf *dmsg)); +static int bsd_decompress __P((void *state, struct mbuf *cmp, + struct mbuf **dmpp)); +static void bsd_reset __P((void *state)); +static void bsd_comp_stats __P((void *state, struct compstat *stats)); + +/* + * Procedures exported to if_ppp.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) + +/* + * 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 < CILEN_BSD_COMPRESS || options[0] != CI_BSD_COMPRESS + || options[1] != CILEN_BSD_COMPRESS + || 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])); + MALLOC(db, struct bsd_db *, newlen, M_DEVBUF, M_NOWAIT); + if (!db) + return NULL; + bzero(db, sizeof(*db) - sizeof(db->dict)); + + if (!decomp) { + db->lens = NULL; + } else { + MALLOC(db->lens, u_int16_t *, (maxmaxcode+1) * sizeof(db->lens[0]), + M_DEVBUF, M_NOWAIT); + if (!db->lens) { + FREE(db, M_DEVBUF); + 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, M_DEVBUF); + FREE(db, M_DEVBUF); +} + +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; +#ifndef DEBUG + if (debug) +#endif + 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. + */ +int /* new slen */ +bsd_compress(state, mret, mp, slen, maxolen) + void *state; + struct mbuf **mret; /* return compressed mbuf chain here */ + struct mbuf *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; + u_char *rptr, *wptr; + u_char *cp_end; + int olen; + struct mbuf *m; + +#define PUTBYTE(v) { \ + ++olen; \ + if (wptr) { \ + *wptr++ = (v); \ + if (wptr >= cp_end) { \ + m->m_len = wptr - mtod(m, u_char *); \ + MGET(m->m_next, M_DONTWAIT, MT_DATA); \ + m = m->m_next; \ + if (m) { \ + m->m_len = 0; \ + if (maxolen - olen > MLEN) \ + MCLGET(m, M_DONTWAIT); \ + wptr = mtod(m, u_char *); \ + cp_end = wptr + M_TRAILINGSPACE(m); \ + } else \ + wptr = NULL; \ + } \ + } \ +} + +#define OUTPUT(ent) { \ + bitno -= n_bits; \ + accm |= ((ent) << bitno); \ + do { \ + PUTBYTE(accm >> 24); \ + accm <<= 8; \ + bitno += 8; \ + } while (bitno <= 24); \ +} + + /* + * If the protocol is not in the range we're interested in, + * just return without compressing the packet. If it is, + * the protocol becomes the first byte to compress. + */ + rptr = mtod(mp, u_char *); + ent = PPP_PROTOCOL(rptr); + if (ent < 0x21 || ent > 0xf9) { + *mret = NULL; + return slen; + } + + /* Don't generate compressed packets which are larger than + the uncompressed packet. */ + if (maxolen > slen) + maxolen = slen; + + /* Allocate one mbuf to start with. */ + MGET(m, M_DONTWAIT, MT_DATA); + *mret = m; + if (m != NULL) { + m->m_len = 0; + if (maxolen + db->hdrlen > MLEN) + MCLGET(m, M_DONTWAIT); + m->m_data += db->hdrlen; + wptr = mtod(m, u_char *); + cp_end = wptr + M_TRAILINGSPACE(m); + } else + wptr = cp_end = NULL; + + /* + * Copy the PPP header over, changing the protocol, + * and install the 2-byte packet sequence number. + */ + if (wptr) { + *wptr++ = PPP_ADDRESS(rptr); /* assumes the ppp header is */ + *wptr++ = PPP_CONTROL(rptr); /* all in one mbuf */ + *wptr++ = 0; /* change the protocol */ + *wptr++ = PPP_COMP; + *wptr++ = db->seqno >> 8; + *wptr++ = db->seqno; + } + ++db->seqno; + + olen = 0; + rptr += PPP_HDRLEN; + slen = mp->m_len - PPP_HDRLEN; + ilen = slen + 1; + for (;;) { + if (slen <= 0) { + mp = mp->m_next; + if (!mp) + break; + rptr = mtod(mp, u_char *); + slen = mp->m_len; + 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); + + if (m != NULL) { + m->m_len = wptr - mtod(m, u_char *); + m->m_next = NULL; + } + + /* + * 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) { + /* throw away the compressed stuff if it is longer than uncompressed */ + if (*mret != NULL) { + m_freem(*mret); + *mret = NULL; + } + ++db->incomp_count; + db->incomp_bytes += ilen; + } else { + ++db->comp_count; + db->comp_bytes += olen + BSD_OVHD; + } + + 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; + struct mbuf *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; + u_int32_t hval, disp; + int slen, ilen; + u_int bitno = 7; + u_char *rptr; + u_int ent; + + /* + * If the protocol is not in the range we're interested in, + * just return without looking at the packet. If it is, + * the protocol becomes the first byte to "compress". + */ + rptr = mtod(dmsg, u_char *); + ent = PPP_PROTOCOL(rptr); + if (ent < 0x21 || ent > 0xf9) + return; + + db->seqno++; + ilen = 1; /* count the protocol as 1 byte */ + rptr += PPP_HDRLEN; + slen = dmsg->m_len - PPP_HDRLEN; + for (;;) { + if (slen <= 0) { + dmsg = dmsg->m_next; + if (!dmsg) + break; + rptr = mtod(dmsg, u_char *); + slen = dmsg->m_len; + continue; + } + 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. + */ +int +bsd_decompress(state, cmp, dmpp) + void *state; + struct mbuf *cmp, **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; + struct mbuf *m, *dmp, *mret; + int adrs, ctrl, ilen; + int space, codelen, extra; + + /* + * Save the address/control from the PPP header + * and then get the sequence number. + */ + *dmpp = NULL; + rptr = mtod(cmp, u_char *); + adrs = PPP_ADDRESS(rptr); + ctrl = PPP_CONTROL(rptr); + rptr += PPP_HDRLEN; + len = cmp->m_len - PPP_HDRLEN; + seq = 0; + for (i = 0; i < 2; ++i) { + while (len <= 0) { + cmp = cmp->m_next; + if (cmp == NULL) + return DECOMP_ERROR; + rptr = mtod(cmp, u_char *); + len = cmp->m_len; + } + seq = (seq << 8) + *rptr++; + --len; + } + + /* + * Check the sequence number and give up if it differs from + * the value we're expecting. + */ + 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; + } + ++db->seqno; + + /* + * Allocate one mbuf to start with. + */ + MGETHDR(dmp, M_DONTWAIT, MT_DATA); + if (dmp == NULL) + return DECOMP_ERROR; + mret = dmp; + dmp->m_len = 0; + dmp->m_next = NULL; + MCLGET(dmp, M_DONTWAIT); + dmp->m_data += db->hdrlen; + wptr = mtod(dmp, u_char *); + space = M_TRAILINGSPACE(dmp) - PPP_HDRLEN + 1; + + /* + * 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; + + ilen = len; + oldcode = CLEAR; + explen = 0; + for (;;) { + if (len == 0) { + cmp = cmp->m_next; + if (!cmp) /* quit at end of message */ + break; + rptr = mtod(cmp, u_char *); + len = cmp->m_len; + 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 mbuf at the end. + */ + if (len > 0 || cmp->m_next != NULL) { + while ((cmp = cmp->m_next) != NULL) + len += cmp->m_len; + if (len > 0) { + m_freem(mret); + if (db->debug) + printf("bsd_decomp%d: bad CLEAR\n", db->unit); + return DECOMP_FATALERROR; /* probably a bug */ + } + } + bsd_clear(db); + explen = ilen = 0; + break; + } + + if (incode > max_ent + 2 || incode > db->maxmaxcode + || (incode > max_ent && oldcode == CLEAR)) { + m_freem(mret); + if (db->debug) { + printf("bsd_decomp%d: bad code 0x%x oldcode=0x%x ", + db->unit, incode, oldcode); + printf("max_ent=0x%x explen=%d seqno=%d\n", + max_ent, explen, 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) { + m_freem(mret); + if (db->debug) { + printf("bsd_decomp%d: ran out of mru\n", db->unit); +#ifdef DEBUG + while ((cmp = cmp->m_next) != NULL) + len += cmp->m_len; + printf(" len=%d, finchar=0x%x, codelen=%d, explen=%d\n", + len, finchar, codelen, explen); +#endif + } + return DECOMP_FATALERROR; + } + + /* + * For simplicity, the decoded characters go in a single mbuf, + * so we allocate a single extra cluster mbuf if necessary. + */ + if ((space -= codelen + extra) < 0) { + dmp->m_len = wptr - mtod(dmp, u_char *); + MGET(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + m_freem(mret); + return DECOMP_ERROR; + } + m->m_len = 0; + m->m_next = NULL; + dmp->m_next = m; + MCLGET(m, M_DONTWAIT); + space = M_TRAILINGSPACE(m) - (codelen + extra); + if (space < 0) { + /* now that's what I call *compression*. */ + m_freem(mret); + return DECOMP_ERROR; + } + dmp = m; + wptr = mtod(dmp, u_char *); + } + + /* + * 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 + if (--codelen <= 0 || dictp->codem1 != finchar-1) + goto bad; +#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; + u_int32_t 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; + } + dmp->m_len = wptr - mtod(dmp, u_char *); + + /* + * 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; + +#ifdef DEBUG + bad: + 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); + } else 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); + } + m_freem(mret); + return DECOMP_FATALERROR; +#endif /* DEBUG */ +} +#endif /* DO_BSD_COMPRESS */ diff --git a/packages/net/ppp/current/src/cbcp.c b/packages/net/ppp/current/src/cbcp.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/cbcp.c @@ -0,0 +1,478 @@ +//========================================================================== +// +// src/cbcp.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * cbcp - Call Back Configuration Protocol. + * + * Copyright (c) 1995 Pedro Roque Marques + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Pedro Roque Marques. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/cbcp.c,v 1.4 1999/08/28 01:19:00 peter Exp $"; +#endif + +#include +#include +#include +#include +#include +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/cbcp.h" +#include "cyg/ppp/fsm.h" +#include "cyg/ppp/lcp.h" +#include "cyg/ppp/ipcp.h" + +/* + * 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, + NULL, + NULL +}; + +cbcp_state cbcp[NUM_PPP]; + +/* internal prototypes */ + +static void cbcp_recvreq __P((cbcp_state *us, char *pckt, int len)); +static void cbcp_resp __P((cbcp_state *us)); +static void cbcp_up __P((cbcp_state *us)); +static void cbcp_recvack __P((cbcp_state *us, char *pckt, int len)); +static void cbcp_send __P((cbcp_state *us, u_char code, u_char *buf, int len)); + +/* 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]; + + syslog(LOG_DEBUG, "cbcp_lowerup"); + syslog(LOG_DEBUG, "want: %d", us->us_type); + + if (us->us_type == CB_CONF_USER) + syslog(LOG_DEBUG, "phone no: %s", us->us_number); +} + +static void +cbcp_open(unit) + int unit; +{ + syslog(LOG_DEBUG, "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) { + syslog(LOG_ERR, "CBCP packet is too small"); + return; + } + + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + +#if 0 + if (len > pktlen) { + syslog(LOG_ERR, "CBCP packet: invalid length"); + return; + } +#endif + + len -= CBCP_MINLEN; + + switch(code) { + case CBCP_REQ: + us->us_id = id; + cbcp_recvreq(us, inp, len); + break; + + case CBCP_RESP: + syslog(LOG_DEBUG, "CBCP_RESP received"); + break; + + case CBCP_ACK: + if (id != us->us_id) + syslog(LOG_DEBUG, "id doesn't match: expected %d recv %d", + us->us_id, id); + + cbcp_recvack(us, inp, len); + break; + + default: + break; + } +} + +/* protocol was rejected by foe */ +void cbcp_protrej(int iface) +{ +} + +char *cbcp_codenames[] = { + "Request", "Response", "Ack" +}; + +char *cbcp_optionnames[] = { + "NoCallback", + "UserDefined", + "AdminDefined", + "List" +}; + +/* pretty print a packet */ +static int +cbcp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, opt, id, len, olen, delay; + u_char *pstart; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(cbcp_codenames) / sizeof(char *)) + printer(arg, " %s", cbcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + + switch (code) { + case CBCP_REQ: + case CBCP_RESP: + case CBCP_ACK: + while(len >= 2) { + GETCHAR(opt, p); + GETCHAR(olen, p); + + if (olen < 2 || olen > len) { + break; + } + + printer(arg, " <"); + len -= olen; + + if (opt >= 1 && opt <= sizeof(cbcp_optionnames) / sizeof(char *)) + printer(arg, " %s", cbcp_optionnames[opt-1]); + else + printer(arg, " option=0x%x", opt); + + if (olen > 2) { + GETCHAR(delay, p); + printer(arg, " delay = %d", delay); + } + + if (olen > 3) { + int addrt; + char str[256]; + + GETCHAR(addrt, p); + memcpy(str, p, olen - 4); + str[olen - 4] = 0; + printer(arg, " number = %s", str); + } + printer(arg, ">"); + break; + } + + default: + break; + } + + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} + +/* received CBCP request */ +static void +cbcp_recvreq(us, pckt, pcktlen) + cbcp_state *us; + char *pckt; + int pcktlen; +{ + u_char type, opt_len, delay, addr_type; + char address[256]; + int len = pcktlen; + + address[0] = 0; + + while (len) { + syslog(LOG_DEBUG, "length: %d", len); + + GETCHAR(type, pckt); + GETCHAR(opt_len, pckt); + + if (opt_len > 2) + GETCHAR(delay, pckt); + + us->us_allowed |= (1 << type); + + switch(type) { + case CB_CONF_NO: + syslog(LOG_DEBUG, "no callback allowed"); + break; + + case CB_CONF_USER: + syslog(LOG_DEBUG, "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]) + syslog(LOG_DEBUG, "address: %s", address); + } + break; + + case CB_CONF_ADMIN: + syslog(LOG_DEBUG, "user admin defined allowed"); + break; + + case CB_CONF_LIST: + break; + } + len -= opt_len; + } + + cbcp_resp(us); +} + +static void +cbcp_resp(us) + cbcp_state *us; +{ + u_char cb_type; + u_char buf[256]; + u_char *bufp = buf; + int len = 0; + + cb_type = us->us_allowed & us->us_type; + syslog(LOG_DEBUG, "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 ) ) { + syslog(LOG_DEBUG, "cbcp_resp CONF_USER"); + PUTCHAR(CB_CONF_USER, bufp); + len = 3 + 1 + strlen(us->us_number) + 1; + PUTCHAR(len , bufp); + PUTCHAR(5, bufp); /* delay */ + PUTCHAR(1, bufp); + BCOPY(us->us_number, bufp, strlen(us->us_number) + 1); + cbcp_send(us, CBCP_RESP, buf, len); + return; + } + + if (cb_type & ( 1 << CB_CONF_ADMIN ) ) { + syslog(LOG_DEBUG, "cbcp_resp CONF_ADMIN"); + PUTCHAR(CB_CONF_ADMIN, bufp); + len = 3 + 1; + PUTCHAR(len , bufp); + PUTCHAR(5, bufp); /* delay */ + PUTCHAR(0, bufp); + cbcp_send(us, CBCP_RESP, buf, len); + return; + } + + if (cb_type & ( 1 << CB_CONF_NO ) ) { + syslog(LOG_DEBUG, "cbcp_resp CONF_NO"); + PUTCHAR(CB_CONF_NO, bufp); + len = 3; + PUTCHAR(len , bufp); + PUTCHAR(0, bufp); + cbcp_send(us, CBCP_RESP, buf, len); + (*ipcp_protent.open)(us->us_unit); + return; + } +} + +static void +cbcp_send(us, code, buf, len) + cbcp_state *us; + u_char code; + u_char *buf; + int len; +{ + u_char *outp; + int outlen; + + outp = outpacket_buf; + + outlen = 4 + len; + + MAKEHEADER(outp, PPP_CBCP); + + PUTCHAR(code, outp); + PUTCHAR(us->us_id, outp); + PUTSHORT(outlen, outp); + + if (len) + BCOPY(buf, outp, len); + + output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN); +} + +static void +cbcp_recvack(us, pckt, len) + cbcp_state *us; + char *pckt; + int len; +{ + u_char type, delay, addr_type; + int opt_len; + char address[256]; + + if (len) { + GETCHAR(type, pckt); + GETCHAR(opt_len, pckt); + + if (opt_len > 2) + GETCHAR(delay, pckt); + + if (opt_len > 4) { + GETCHAR(addr_type, pckt); + memcpy(address, pckt, opt_len - 4); + address[opt_len - 4] = 0; + if (address[0]) + syslog(LOG_DEBUG, "peer will call: %s", address); + } + } + + cbcp_up(us); +} + +extern int persist; + +/* ok peer will do callback */ +static void +cbcp_up(us) + cbcp_state *us; +{ + persist = 0; + lcp_close(0, "Call me back, please"); +} diff --git a/packages/net/ppp/current/src/ccp.c b/packages/net/ppp/current/src/ccp.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/ccp.c @@ -0,0 +1,1162 @@ +//========================================================================== +// +// src/ccp.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * ccp.c - PPP Compression Control Protocol. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/ccp.c,v 1.10 1999/08/28 01:19:00 peter Exp $"; +#endif + +#include +#include +#include +#include + +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/fsm.h" +#include "cyg/ppp/ccp.h" +#include + +/* + * 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", + NULL, + NULL, + NULL +}; + +fsm ccp_fsm[NUM_PPP]; +ccp_options ccp_wantoptions[NUM_PPP]; /* what to request the peer to use */ +ccp_options ccp_gotoptions[NUM_PPP]; /* what the peer agreed to do */ +ccp_options ccp_allowoptions[NUM_PPP]; /* what we'll agree to do */ +ccp_options ccp_hisoptions[NUM_PPP]; /* what we agreed to do */ + +/* + * Callbacks for fsm code. + */ +static void ccp_resetci __P((fsm *)); +static int ccp_cilen __P((fsm *)); +static void ccp_addci __P((fsm *, u_char *, int *)); +static int ccp_ackci __P((fsm *, u_char *, int)); +static int ccp_nakci __P((fsm *, u_char *, int)); +static int ccp_rejci __P((fsm *, u_char *, int)); +static int ccp_reqci __P((fsm *, u_char *, int *, int)); +static void ccp_up __P((fsm *)); +static void ccp_down __P((fsm *)); +static int ccp_extcode __P((fsm *, int, int, u_char *, int)); +static void ccp_rack_timeout __P((void *)); +static char *method_name __P((ccp_options *, ccp_options *)); + +static fsm_callbacks ccp_callbacks = { + ccp_resetci, + ccp_cilen, + ccp_addci, + ccp_ackci, + ccp_nakci, + ccp_rejci, + ccp_reqci, + ccp_up, + ccp_down, + NULL, + NULL, + NULL, + NULL, + ccp_extcode, + "CCP" +}; + +/* + * Do we want / did we get any compression? + */ +#define ANY_COMPRESS(opt) ((opt).deflate || (opt).bsd_compress \ + || (opt).predictor_1 || (opt).predictor_2) + +/* + * Local state (mainly for handling reset-reqs and reset-acks). + */ +static int ccp_localstate[NUM_PPP]; +#define RACK_PENDING 1 /* waiting for reset-ack */ +#define RREQ_REPEAT 2 /* send another reset-req if no reset-ack */ + +#define RACKTIMEOUT 1 /* second */ + +static int all_rejected[NUM_PPP]; /* we rejected all peer's options */ + +/* + * 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) + syslog(LOG_NOTICE, "Compression disabled by peer."); + + /* + * If we get a terminate-ack and we're not asking for compression, + * close CCP. + */ + if (oldstate == REQSENT && p[0] == TERMACK + && !ANY_COMPRESS(ccp_gotoptions[unit])) + ccp_close(unit, "No compression negotiated"); +} + +/* + * Handle a CCP-specific code. + */ +static int +ccp_extcode(f, code, id, p, len) + fsm *f; + int code, id; + u_char *p; + int len; +{ + switch (code) { + case CCP_RESETREQ: + if (f->state != OPENED) + break; + /* send a reset-ack, which the transmitter will see and + reset its compression state. */ + fsm_sdata(f, CCP_RESETACK, id, NULL, 0); + break; + + case CCP_RESETACK: + if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) { + ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT); + UNTIMEOUT(ccp_rack_timeout, f); + } + break; + + default: + return 0; + } + + return 1; +} + +/* + * ccp_protrej - peer doesn't talk CCP. + */ +static void +ccp_protrej(unit) + int unit; +{ + ccp_flags_set(unit, 0, 0); + fsm_lowerdown(&ccp_fsm[unit]); +} + +/* + * ccp_resetci - initialize at start of negotiation. + */ +static void +ccp_resetci(f) + fsm *f; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + u_char opt_buf[16]; + + *go = ccp_wantoptions[f->unit]; + all_rejected[f->unit] = 0; + + /* + * Check whether the kernel knows about the various + * compression methods we might request. + */ + if (go->bsd_compress) { + opt_buf[0] = CI_BSD_COMPRESS; + opt_buf[1] = CILEN_BSD_COMPRESS; + opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, BSD_MIN_BITS); + if (ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0) <= 0) + go->bsd_compress = 0; + } + if (go->deflate) { + if (go->deflate_correct) { + opt_buf[0] = CI_DEFLATE; + opt_buf[1] = CILEN_DEFLATE; + opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_SIZE); + 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_SIZE); + opt_buf[3] = DEFLATE_CHK_SEQUENCE; + if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0) + go->deflate_draft = 0; + } + if (!go->deflate_correct && !go->deflate_draft) + go->deflate = 0; + } + if (go->predictor_1) { + opt_buf[0] = CI_PREDICTOR_1; + opt_buf[1] = CILEN_PREDICTOR_1; + if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_1, 0) <= 0) + go->predictor_1 = 0; + } + if (go->predictor_2) { + opt_buf[0] = CI_PREDICTOR_2; + opt_buf[1] = CILEN_PREDICTOR_2; + if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_2, 0) <= 0) + go->predictor_2 = 0; + } +} + +/* + * ccp_cilen - Return total length of our configuration info. + */ +static int +ccp_cilen(f) + fsm *f; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + + return (go->bsd_compress? CILEN_BSD_COMPRESS: 0) + + (go->deflate? CILEN_DEFLATE: 0) + + (go->predictor_1? CILEN_PREDICTOR_1: 0) + + (go->predictor_2? CILEN_PREDICTOR_2: 0); +} + +/* + * ccp_addci - put our requests in a packet. + */ +static void +ccp_addci(f, p, lenp) + fsm *f; + u_char *p; + int *lenp; +{ + int res; + ccp_options *go = &ccp_gotoptions[f->unit]; + u_char *p0 = p; + + /* + * Add the compression types that we can receive, in decreasing + * preference order. Get the kernel to allocate the first one + * in case it gets Acked. + */ + if (go->deflate) { + p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT; + p[1] = CILEN_DEFLATE; + p[2] = DEFLATE_MAKE_OPT(go->deflate_size); + p[3] = DEFLATE_CHK_SEQUENCE; + for (;;) { + res = ccp_test(f->unit, p, CILEN_DEFLATE, 0); + if (res > 0) { + p += CILEN_DEFLATE; + break; + } + if (res < 0 || go->deflate_size <= DEFLATE_MIN_SIZE) { + go->deflate = 0; + break; + } + --go->deflate_size; + p[2] = DEFLATE_MAKE_OPT(go->deflate_size); + } + if (p != p0 && go->deflate_correct && go->deflate_draft) { + p[0] = CI_DEFLATE_DRAFT; + p[1] = CILEN_DEFLATE; + p[2] = p[2 - CILEN_DEFLATE]; + p[3] = DEFLATE_CHK_SEQUENCE; + p += CILEN_DEFLATE; + } + } + if (go->bsd_compress) { + p[0] = CI_BSD_COMPRESS; + p[1] = CILEN_BSD_COMPRESS; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); + if (p != p0) { + p += CILEN_BSD_COMPRESS; /* not the first option */ + } else { + for (;;) { + res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0); + if (res > 0) { + p += CILEN_BSD_COMPRESS; + break; + } + if (res < 0 || go->bsd_bits <= BSD_MIN_BITS) { + go->bsd_compress = 0; + break; + } + --go->bsd_bits; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); + } + } + } + /* XXX Should Predictor 2 be preferable to Predictor 1? */ + if (go->predictor_1) { + p[0] = CI_PREDICTOR_1; + p[1] = CILEN_PREDICTOR_1; + if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 0) <= 0) { + go->predictor_1 = 0; + } else { + p += CILEN_PREDICTOR_1; + } + } + if (go->predictor_2) { + p[0] = CI_PREDICTOR_2; + p[1] = CILEN_PREDICTOR_2; + if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 0) <= 0) { + go->predictor_2 = 0; + } else { + p += CILEN_PREDICTOR_2; + } + } + + go->method = (p > p0)? p0[0]: -1; + + *lenp = p - p0; +} + +/* + * ccp_ackci - process a received configure-ack, and return + * 1 iff the packet was OK. + */ +static int +ccp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + u_char *p0 = p; + + if (go->deflate) { + if (len < CILEN_DEFLATE + || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + || p[1] != CILEN_DEFLATE + || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + /* XXX Cope with first/fast ack */ + if (len == 0) + return 1; + if (go->deflate_correct && go->deflate_draft) { + if (len < CILEN_DEFLATE + || p[0] != CI_DEFLATE_DRAFT + || p[1] != CILEN_DEFLATE + || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + } + if (go->bsd_compress) { + if (len < CILEN_BSD_COMPRESS + || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS + || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) + return 0; + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + if (go->predictor_1) { + if (len < CILEN_PREDICTOR_1 + || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1) + return 0; + p += CILEN_PREDICTOR_1; + len -= CILEN_PREDICTOR_1; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + if (go->predictor_2) { + if (len < CILEN_PREDICTOR_2 + || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2) + return 0; + p += CILEN_PREDICTOR_2; + len -= CILEN_PREDICTOR_2; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + + if (len != 0) + return 0; + return 1; +} + +/* + * ccp_nakci - process received configure-nak. + * Returns 1 iff the nak was OK. + */ +static int +ccp_nakci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + ccp_options no; /* options we've seen already */ + ccp_options try; /* options to ask for next time */ + + memset(&no, 0, sizeof(no)); + try = *go; + + if (go->deflate && len >= CILEN_DEFLATE + && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + && p[1] == CILEN_DEFLATE) { + no.deflate = 1; + /* + * Peer wants us to use a different code size or something. + * Stop asking for Deflate if we don't understand his suggestion. + */ + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL + || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_SIZE + || 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. + * + * XXX What should we do with any remaining options? + */ + + if (len != 0) + return 0; + + if (f->state != OPENED) + *go = try; + return 1; +} + +/* + * ccp_rejci - reject some of our suggested compression methods. + */ +static int +ccp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + ccp_options try; /* options to request next time */ + + try = *go; + + /* + * Cope with empty configure-rejects by ceasing to send + * configure-requests. + */ + if (len == 0 && all_rejected[f->unit]) + return -1; + + if (go->deflate && len >= CILEN_DEFLATE + && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + && p[1] == CILEN_DEFLATE) { + if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; /* Rej is bad */ + if (go->deflate_correct) + try.deflate_correct = 0; + else + try.deflate_draft = 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + if (go->deflate_correct && go->deflate_draft + && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT + && p[1] == CILEN_DEFLATE) { + if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; /* Rej is bad */ + try.deflate_draft = 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + if (!try.deflate_correct && !try.deflate_draft) + try.deflate = 0; + } + if (go->bsd_compress && len >= CILEN_BSD_COMPRESS + && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) { + if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) + return 0; + try.bsd_compress = 0; + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + } + if (go->predictor_1 && len >= CILEN_PREDICTOR_1 + && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) { + try.predictor_1 = 0; + p += CILEN_PREDICTOR_1; + len -= CILEN_PREDICTOR_1; + } + if (go->predictor_2 && len >= CILEN_PREDICTOR_2 + && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) { + try.predictor_2 = 0; + p += CILEN_PREDICTOR_2; + len -= CILEN_PREDICTOR_2; + } + + if (len != 0) + return 0; + + if (f->state != OPENED) + *go = try; + + return 1; +} + +/* + * ccp_reqci - processed a received configure-request. + * Returns CONFACK, CONFNAK or CONFREJ and the packet modified + * appropriately. + */ +static int +ccp_reqci(f, p, lenp, dont_nak) + fsm *f; + u_char *p; + int *lenp; + int dont_nak; +{ + int ret, newret, res; + u_char *p0, *retp; + int len, clen, type, nb; + ccp_options *ho = &ccp_hisoptions[f->unit]; + ccp_options *ao = &ccp_allowoptions[f->unit]; + + ret = CONFACK; + retp = p0 = p; + len = *lenp; + + memset(ho, 0, sizeof(ccp_options)); + ho->method = (len > 0)? p[0]: -1; + + while (len > 0) { + newret = CONFACK; + if (len < 2 || p[1] < 2 || p[1] > len) { + /* length is bad */ + clen = len; + newret = CONFREJ; + + } else { + type = p[0]; + clen = p[1]; + + switch (type) { + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (!ao->deflate || clen != CILEN_DEFLATE + || (!ao->deflate_correct && type == CI_DEFLATE) + || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) { + newret = CONFREJ; + break; + } + + ho->deflate = 1; + ho->deflate_size = nb = DEFLATE_SIZE(p[2]); + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL + || p[3] != DEFLATE_CHK_SEQUENCE + || nb > ao->deflate_size || nb < DEFLATE_MIN_SIZE) { + 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_SIZE || dont_nak) { + newret = CONFREJ; + p[2] = DEFLATE_MAKE_OPT(ho->deflate_size); + break; + } + newret = CONFNAK; + --nb; + p[2] = DEFLATE_MAKE_OPT(nb); + } + } + break; + + case CI_BSD_COMPRESS: + if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) { + newret = CONFREJ; + break; + } + + ho->bsd_compress = 1; + ho->bsd_bits = nb = BSD_NBITS(p[2]); + if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION + || nb > ao->bsd_bits || nb < BSD_MIN_BITS) { + newret = CONFNAK; + if (!dont_nak) { + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits); + /* fall through to test this #bits below */ + } else + break; + } + + /* + * Check whether we can do BSD-Compress with the code + * size they want. If the code size is too big, reduce + * it until the kernel can cope and nak with that. + * We only check this for the first option. + */ + if (p == p0) { + for (;;) { + res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 1); + if (res > 0) + break; + if (res < 0 || nb == BSD_MIN_BITS || dont_nak) { + newret = CONFREJ; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, + ho->bsd_bits); + break; + } + newret = CONFNAK; + --nb; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb); + } + } + break; + + case CI_PREDICTOR_1: + if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) { + newret = CONFREJ; + break; + } + + ho->predictor_1 = 1; + if (p == p0 + && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 1) <= 0) { + newret = CONFREJ; + } + break; + + case CI_PREDICTOR_2: + if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) { + newret = CONFREJ; + break; + } + + ho->predictor_2 = 1; + if (p == p0 + && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 1) <= 0) { + newret = CONFREJ; + } + break; + + default: + newret = CONFREJ; + } + } + + if (newret == CONFNAK && dont_nak) + newret = CONFREJ; + if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) { + /* we're returning this option */ + if (newret == CONFREJ && ret == CONFNAK) + retp = p0; + ret = newret; + if (p != retp) + BCOPY(p, retp, clen); + retp += clen; + } + + p += clen; + len -= clen; + } + + if (ret != CONFACK) { + if (ret == CONFREJ && *lenp == retp - p0) + all_rejected[f->unit] = 1; + else + *lenp = retp - p0; + } + return ret; +} + +/* + * Make a string name for a compression method (or 2). + */ +static char * +method_name(opt, opt2) + ccp_options *opt, *opt2; +{ + static char result[64]; + + if (!ANY_COMPRESS(*opt)) + return "(none)"; + switch (opt->method) { + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (opt2 != NULL && opt2->deflate_size != opt->deflate_size) + sprintf(result, "Deflate%s (%d/%d)", + (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""), + opt->deflate_size, opt2->deflate_size); + else + sprintf(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) + sprintf(result, "BSD-Compress (%d/%d)", opt->bsd_bits, + opt2->bsd_bits); + else + sprintf(result, "BSD-Compress (%d)", opt->bsd_bits); + break; + case CI_PREDICTOR_1: + return "Predictor 1"; + case CI_PREDICTOR_2: + return "Predictor 2"; + default: + sprintf(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) { + syslog(LOG_NOTICE, "%s compression enabled", + method_name(go, ho)); + } else { + strcpy(method1, method_name(go, NULL)); + syslog(LOG_NOTICE, "%s / %s compression enabled", + method1, method_name(ho, NULL)); + } + } else + syslog(LOG_NOTICE, "%s receive compression enabled", + method_name(go, NULL)); + } else if (ANY_COMPRESS(*ho)) + syslog(LOG_NOTICE, "%s transmit compression enabled", + method_name(ho, NULL)); +} + +/* + * CCP has gone down - inform the kernel driver. + */ +static void +ccp_down(f) + fsm *f; +{ + if (ccp_localstate[f->unit] & RACK_PENDING) + UNTIMEOUT(ccp_rack_timeout, f); + ccp_localstate[f->unit] = 0; + ccp_flags_set(f->unit, 1, 0); +} + +/* + * Print the contents of a CCP packet. + */ +static char *ccp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", + NULL, NULL, NULL, NULL, NULL, NULL, + "ResetReq", "ResetAck", +}; + +static int +ccp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + u_char *p0, *optend; + int code, id, len; + int optlen; + + p0 = p; + if (plen < HEADERLEN) + return 0; + code = p[0]; + id = p[1]; + len = (p[2] << 8) + p[3]; + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *) + && ccp_codenames[code-1] != NULL) + printer(arg, " %s", ccp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + p += HEADERLEN; + + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print list of possible compression methods */ + while (len >= 2) { + code = p[0]; + optlen = p[1]; + if (optlen < 2 || optlen > len) + break; + printer(arg, " <"); + len -= optlen; + optend = p + optlen; + switch (code) { + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (optlen >= CILEN_DEFLATE) { + printer(arg, "deflate%s %d", + (code == CI_DEFLATE_DRAFT? "(old#)": ""), + DEFLATE_SIZE(p[2])); + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL) + printer(arg, " method %d", DEFLATE_METHOD(p[2])); + if (p[3] != DEFLATE_CHK_SEQUENCE) + printer(arg, " check %d", p[3]); + p += CILEN_DEFLATE; + } + break; + case CI_BSD_COMPRESS: + if (optlen >= CILEN_BSD_COMPRESS) { + printer(arg, "bsd v%d %d", BSD_VERSION(p[2]), + BSD_NBITS(p[2])); + p += CILEN_BSD_COMPRESS; + } + break; + case CI_PREDICTOR_1: + if (optlen >= CILEN_PREDICTOR_1) { + printer(arg, "predictor 1"); + p += CILEN_PREDICTOR_1; + } + break; + case CI_PREDICTOR_2: + if (optlen >= CILEN_PREDICTOR_2) { + printer(arg, "predictor 2"); + p += CILEN_PREDICTOR_2; + } + break; + } + while (p < optend) + printer(arg, " %.2x", *p++); + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + print_string(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. + */ + syslog(LOG_ERR, "Lost compression sync: disabling compression"); + ccp_close(unit, "Lost compression sync"); + } else { + /* + * Send a reset-request to reset the peer's compressor. + * We don't do that if we are still waiting for an + * acknowledgement to a previous reset-request. + */ + if (!(ccp_localstate[f->unit] & RACK_PENDING)) { + fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0); + TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); + ccp_localstate[f->unit] |= RACK_PENDING; + } else + ccp_localstate[f->unit] |= RREQ_REPEAT; + } + } +} + +/* + * Timeout waiting for reset-ack. + */ +static void +ccp_rack_timeout(arg) + void *arg; +{ + fsm *f = arg; + + if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) { + fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0); + TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); + ccp_localstate[f->unit] &= ~RREQ_REPEAT; + } else + ccp_localstate[f->unit] &= ~RACK_PENDING; +} + diff --git a/packages/net/ppp/current/src/chap.c b/packages/net/ppp/current/src/chap.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/chap.c @@ -0,0 +1,919 @@ +//========================================================================== +// +// src/chap.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * chap.c - Challenge Handshake Authentication Protocol. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/chap.c,v 1.10 1999/08/28 01:19:01 peter Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include + +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/chap.h" +//#include "md5.h" +#ifdef CHAPMS +#include "cyg/ppp/chap_ms.h" +#endif + +/* + * Protocol entry points. + */ +static void ChapInit __P((int)); +static void ChapLowerUp __P((int)); +static void ChapLowerDown __P((int)); +static void ChapInput __P((int, u_char *, int)); +static void ChapProtocolReject __P((int)); +static int ChapPrintPkt __P((u_char *, int, + void (*) __P((void *, char *, ...)), void *)); + +struct protent chap_protent = { + PPP_CHAP, + ChapInit, + ChapInput, + ChapProtocolReject, + ChapLowerUp, + ChapLowerDown, + NULL, + NULL, + ChapPrintPkt, + NULL, + 1, + "CHAP", + NULL, + NULL, + NULL +}; + +chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ + +static void ChapChallengeTimeout __P((void *)); +static void ChapResponseTimeout __P((void *)); +static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int)); +static void ChapRechallenge __P((void *)); +static void ChapReceiveResponse __P((chap_state *, u_char *, int, int)); +static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int)); +static void ChapReceiveFailure __P((chap_state *, u_char *, int, int)); +static void ChapSendStatus __P((chap_state *, int)); +static void ChapSendChallenge __P((chap_state *)); +static void ChapSendResponse __P((chap_state *)); +static void ChapGenChallenge __P((chap_state *)); + +extern double drand48 __P((void)); +extern void srand48 __P((long)); + +/* + * ChapInit - Initialize a CHAP unit. + */ +static void +ChapInit(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + /* random number generator is initialized in magic_init */ +} + + +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void +ChapAuthWithPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void +ChapAuthPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; +} + + +/* + * ChapChallengeTimeout - Timeout expired on sending challenge. + */ +static void +ChapChallengeTimeout(arg) + void *arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) + return; + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + syslog(LOG_ERR, "Peer failed to respond to CHAP challenge"); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + return; + } + + ChapSendChallenge(cstate); /* Re-send challenge */ +} + + +/* + * ChapResponseTimeout - Timeout expired on sending response. + */ +static void +ChapResponseTimeout(arg) + void *arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) + return; + + ChapSendResponse(cstate); /* re-send response */ +} + + +/* + * ChapRechallenge - Time to challenge the peer again. + */ +static void +ChapRechallenge(arg) + void *arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) + return; + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +static void +ChapLowerUp(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) + cstate->clientstate = CHAPCS_CLOSED; + else if (cstate->clientstate == CHAPCS_PENDING) + cstate->clientstate = CHAPCS_LISTEN; + + if (cstate->serverstate == CHAPSS_INITIAL) + cstate->serverstate = CHAPSS_CLOSED; + else if (cstate->serverstate == CHAPSS_PENDING) { + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +ChapLowerDown(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) + UNTIMEOUT(ChapChallengeTimeout, cstate); + else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) + UNTIMEOUT(ChapRechallenge, cstate); + if (cstate->clientstate == CHAPCS_RESPONSE) + UNTIMEOUT(ChapResponseTimeout, cstate); + + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +static void +ChapProtocolReject(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) + auth_peer_fail(unit, PPP_CHAP); + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) + auth_withpeer_fail(unit, PPP_CHAP); + ChapLowerDown(unit); /* shutdown chap */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +static void +ChapInput(unit, inpacket, packet_len) + int unit; + u_char *inpacket; + int packet_len; +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length.")); + return; + } + if (len > packet_len) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet.")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge and send Response. + */ +static void +ChapReceiveChallenge(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + int id; + int len; +{ + int rchallenge_len; + u_char *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + MD5_CTX mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id)); + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d", + cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= sizeof(rhostname)) + len = sizeof(rhostname) - 1; + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'", + rhostname)); + + /* Microsoft doesn't send their name back in the PPP packet */ + if (remote_name[0] != 0 && (explicit_remote || rhostname[0] == 0)) { + strncpy(rhostname, remote_name, sizeof(rhostname)); + rhostname[sizeof(rhostname) - 1] = 0; + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name", + rhostname)); + } + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, + secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s", + rhostname); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) + UNTIMEOUT(ChapResponseTimeout, cstate); + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->resp_id, 1); + MD5Update(&mdContext, secret, secret_len); + MD5Update(&mdContext, rchallenge, rchallenge_len); + MD5Final(hash, &mdContext); + BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + +#ifdef CHAPMS + case CHAP_MICROSOFT: + ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); + break; +#endif + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type)); + return; + } + + BZERO(secret, sizeof(secret)); + ChapSendResponse(cstate); +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void +ChapReceiveResponse(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + int id; + int len; +{ + u_char *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + MD5_CTX mdContext; + char secret[MAXSECRETLEN]; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id)); + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d", + cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) + return; /* doesn't match ID of last challenge */ + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + ChapSendStatus(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + ChapSendStatus(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u_char) + remmd_len; + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")); + return; + } + + UNTIMEOUT(ChapChallengeTimeout, cstate); + + if (len >= sizeof(rhostname)) + len = sizeof(rhostname) - 1; + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s", + rhostname)); + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, rhostname, cstate->chal_name, + secret, &secret_len, 1)) { + syslog(LOG_WARNING, "No CHAP secret found for authenticating %s", + rhostname); + } else { + + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) + break; /* it's not even the right length */ + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->chal_id, 1); + MD5Update(&mdContext, secret, secret_len); + MD5Update(&mdContext, cstate->challenge, cstate->chal_len); + MD5Final(hash, &mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) + code = CHAP_SUCCESS; /* they are the same! */ + break; + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type)); + } + } + + BZERO(secret, sizeof(secret)); + ChapSendStatus(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + } + if (cstate->chal_interval != 0) + TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); + syslog(LOG_NOTICE, "CHAP peer authentication succeeded for %s", + rhostname); + + } else { + syslog(LOG_ERR, "CHAP peer authentication failed for remote host %s", + rhostname); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + } +} + +/* + * ChapReceiveSuccess - Receive Success + */ +static void +ChapReceiveSuccess(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + u_char id; + int len; +{ + + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id)); + + if (cstate->clientstate == CHAPCS_OPEN) + /* presumably an answer to a duplicate response */ + return; + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) + PRINTMSG(inp, len); + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, PPP_CHAP); +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +static void +ChapReceiveFailure(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + u_char id; + int len; +{ + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id)); + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) + PRINTMSG(inp, len); + + syslog(LOG_ERR, "CHAP authentication failed"); + auth_withpeer_fail(cstate->unit, PPP_CHAP); +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void +ChapSendChallenge(cstate) + chap_state *cstate; +{ + u_char *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id)); + + TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); + ++cstate->chal_transmits; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void +ChapSendStatus(cstate, code) + chap_state *cstate; + int code; +{ + u_char *outp; + int outlen, msglen; + char msg[256]; + + if (code == CHAP_SUCCESS) + sprintf(msg, "Welcome to %s.", cyg_ppp_hostname); + else + sprintf(msg, "I don't like you. Go 'way."); + msglen = strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code, + cstate->chal_id)); +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len. The challenge + * string and its length are stored in *cstate, and various other fields of + * *cstate are initialized. + */ + +static void +ChapGenChallenge(cstate) + chap_state *cstate; +{ + int chal_len; + u_char *ptr = cstate->challenge; + unsigned int i; + + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) ((drand48() * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) + + MIN_CHALLENGE_LENGTH); + cstate->chal_len = chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++ ) + *ptr++ = (char) (drand48() * 0xff); +} + +/* + * ChapSendResponse - send a response packet with values as specified + * in *cstate. + */ +/* ARGSUSED */ +static void +ChapSendResponse(cstate) + chap_state *cstate; +{ + u_char *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); + ++cstate->resp_transmits; +} + +/* + * ChapPrintPkt - print the contents of a CHAP packet. + */ +static char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; + +static int +ChapPrintPkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, id, len; + int clen, nlen; + u_char x; + + if (plen < CHAP_HEADERLEN) + return 0; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) + printer(arg, " %s", ChapCodenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) + break; + clen = p[0]; + if (len < clen + 1) + break; + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = "); + print_string((char *)p, nlen, printer, arg); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " "); + print_string((char *)p, len, printer, arg); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + + return len + CHAP_HEADERLEN; +} diff --git a/packages/net/ppp/current/src/chap_ms.c b/packages/net/ppp/current/src/chap_ms.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/chap_ms.c @@ -0,0 +1,384 @@ +//========================================================================== +// +// src/chap_ms.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 + * + * Implemented LANManager type password response to MS-CHAP challenges. + * Now pppd provides both NT style and LANMan style blocks, and the + * prefered is set by option "ms-lanman". Default is to use NT. + * The hash text (StdText) was taken from Win95 RASAPI32.DLL. + * + * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/chap_ms.c,v 1.8 2000/02/24 21:10:28 markm Exp $"; +#endif + +#ifdef CHAPMS + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_CRYPT_H +#include +#endif + +#include "pppd.h" +#include "chap.h" +#include "chap_ms.h" +#include "md4.h" + +#ifndef USE_CRYPT +#include +#endif + +typedef struct { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +} MS_ChapResponse; +/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), + in case this struct gets padded. */ + + +static void ChallengeResponse __P((u_char *, u_char *, u_char *)); +static void DesEncrypt __P((u_char *, u_char *, u_char *)); +static void MakeKey __P((u_char *, u_char *)); +static u_char Get7Bits __P((u_char *, int)); +static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *)); +#ifdef MSLANMAN +static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *)); +#endif + +#ifdef USE_CRYPT +static void Expand __P((u_char *, u_char *)); +static void Collapse __P((u_char *, u_char *)); +#endif + +static void +ChallengeResponse(challenge, pwHash, response) + u_char *challenge; /* IN 8 octets */ + u_char *pwHash; /* IN 16 octets */ + u_char *response; /* OUT 24 octets */ +{ + char ZPasswordHash[21]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE); + +#if 0 + log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); +#endif + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + +#if 0 + log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); +#endif +} + + +#ifdef USE_CRYPT +static void +DesEncrypt(clear, key, cipher) + u_char *clear; /* IN 8 octets */ + u_char *key; /* IN 7 octets */ + u_char *cipher; /* OUT 8 octets */ +{ + u_char des_key[8]; + u_char crypt_key[66]; + u_char des_input[66]; + + MakeKey(key, des_key); + + Expand(des_key, crypt_key); + setkey(crypt_key); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + Expand(clear, des_input); + encrypt(des_input, 0); + Collapse(des_input, cipher); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#else /* USE_CRYPT */ + +static void +DesEncrypt(clear, key, cipher) + u_char *clear; /* IN 8 octets */ + u_char *key; /* IN 7 octets */ + u_char *cipher; /* OUT 8 octets */ +{ + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + + des_set_key(&des_key, key_schedule); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#endif /* USE_CRYPT */ + + +static u_char Get7Bits(input, startBit) + u_char *input; + int startBit; +{ + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +#ifdef USE_CRYPT + +/* in == 8-byte string (expanded version of the 56-bit key) + * out == 64-byte string where each byte is either 1 or 0 + * Note that the low-order "bit" is always ignored by by setkey() + */ +static void Expand(in, out) + u_char *in; + u_char *out; +{ + int j, c; + int i; + + for(i = 0; i < 64; in++){ + c = *in; + for(j = 7; j >= 0; j--) + *out++ = (c >> j) & 01; + i += 8; + } +} + +/* The inverse of Expand + */ +static void Collapse(in, out) + u_char *in; + u_char *out; +{ + int j; + int i; + unsigned int c; + + for (i = 0; i < 64; i += 8, out++) { + c = 0; + for (j = 7; j >= 0; j--, in++) + c |= *in << j; + *out = c & 0xff; + } +} +#endif + +static void MakeKey(key, des_key) + u_char *key; /* IN 56 bit DES key missing parity bits */ + u_char *des_key; /* OUT 64 bit DES key with parity bits added */ +{ + des_key[0] = Get7Bits(key, 0); + des_key[1] = Get7Bits(key, 7); + des_key[2] = Get7Bits(key, 14); + des_key[3] = Get7Bits(key, 21); + des_key[4] = Get7Bits(key, 28); + des_key[5] = Get7Bits(key, 35); + des_key[6] = Get7Bits(key, 42); + des_key[7] = Get7Bits(key, 49); + +#ifndef USE_CRYPT + des_set_odd_parity((des_cblock *)des_key); +#endif + +#if 0 + CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X", + key[0], key[1], key[2], key[3], key[4], key[5], key[6])); + CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X", + des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); +#endif +} + +static void +ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response) + char *rchallenge; + int rchallenge_len; + char *secret; + int secret_len; + MS_ChapResponse *response; +{ + int i; + MD4_CTX md4Context; + u_char hash[MD4_SIGNATURE_SIZE]; + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + + /* Initialize the Unicode version of the secret (== password). */ + /* This implicitly supports 8-bit ISO8859/1 characters. */ + BZERO(unicodePassword, sizeof(unicodePassword)); + for (i = 0; i < secret_len; i++) + unicodePassword[i * 2] = (u_char)secret[i]; + + MD4Init(&md4Context); + MD4Update(&md4Context, unicodePassword, secret_len * 2); /* Unicode is 2 bytes/char */ + + MD4Final(hash, &md4Context); /* Tell MD4 we're done */ + + ChallengeResponse(rchallenge, hash, response->NTResp); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void +ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response) + char *rchallenge; + int rchallenge_len; + char *secret; + int secret_len; + MS_ChapResponse *response; +{ + int i; + u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) + UcasePassword[i] = (u_char)toupper(secret[i]); + DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); + DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); +} +#endif + +void +ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len) + chap_state *cstate; + char *rchallenge; + int rchallenge_len; + char *secret; + int secret_len; +{ + MS_ChapResponse response; +#ifdef MSLANMAN + extern int ms_lanman; +#endif + +#if 0 + CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret)); +#endif + BZERO(&response, sizeof(response)); + + /* Calculate both always */ + ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + + /* prefered method is set by option */ + response.UseNT = !ms_lanman; +#else + response.UseNT = 1; +#endif + + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; +} + +#endif /* CHAPMS */ diff --git a/packages/net/ppp/current/src/chat.c b/packages/net/ppp/current/src/chat.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/chat.c @@ -0,0 +1,448 @@ +//========================================================================== +// +// src/chat.c +// +// PPP CHAT scripting +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting the +// copyright holder. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2003-06-08 +// Purpose: Chat script handling +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +//===================================================================== + +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include "cyg/ppp/syslog.h" + +#include +#include + +#include + +#include + +//===================================================================== + +//#undef db_printf +//#define db_printf diag_printf + +//===================================================================== +// Expect return values + +#define CHAT_MATCH 0 +#define CHAT_ABORT 1 +#define CHAT_TIMEOUT 2 +#define CHAT_FAIL -1 + +//===================================================================== +// Configuration constants + +#define CHAT_MAX_ABORTS CYGNUM_PPP_CHAT_ABORTS_MAX +#define CHAT_ABORT_SIZE CYGNUM_PPP_CHAT_ABORTS_SIZE + +#define CHAT_STRING_LENGTH CYGNUM_PPP_CHAT_STRING_LENGTH + +//===================================================================== +// Local variables + +// Array of ABORT strings +static char cyg_ppp_chat_abort[CHAT_MAX_ABORTS][CHAT_ABORT_SIZE]; +static int cyg_ppp_chat_abort_count; + +// Response timeout +static int cyg_ppp_chat_timeout; +// timeout alarm +static cyg_alarm cyg_ppp_chat_alarm_obj; +static cyg_handle_t cyg_ppp_chat_alarm; +static cyg_handle_t cyg_ppp_chat_thread; + +// Handle on serial device +static cyg_io_handle_t cyg_ppp_chat_handle; + +// String buffers +static char cyg_ppp_chat_buffer[CHAT_STRING_LENGTH]; +static char cyg_ppp_chat_expect_buffer[CHAT_STRING_LENGTH]; + +// success indicator +static int cyg_ppp_chat_success; + +//===================================================================== +// Timeout alarm function +// +// The alarm is set whenever we start looking for a match, if the alarm +// goes off before we find a match, then it kicks the thread out of any +// wait it is in. + +static void chat_alarm(cyg_handle_t alarm, cyg_addrword_t data) +{ + db_printf("%s(%d)\n",__PRETTY_FUNCTION__,__LINE__); + cyg_thread_release( cyg_ppp_chat_thread ); +} + +//===================================================================== +// Match +// +// Looks for the supplied string in the input. Returns zero on timeout +// or other error, 1 if a match is found. + +static int cyg_ppp_chat_match( char *str ) +{ + cyg_uint64 trigger = (cyg_ppp_chat_timeout * 100) + cyg_current_time(); + char *s = cyg_ppp_chat_buffer; + int matchlen = strlen(str); + int matched = 0; + int aborted = 0; + + db_printf("%s(%d) timeout %d trigger %d\n",__PRETTY_FUNCTION__,__LINE__, + cyg_ppp_chat_timeout,(long)trigger); + db_printf(" current time %d\n",(long)cyg_current_time()); + + if( matchlen == 0 ) + return 1; + + cyg_alarm_initialize( cyg_ppp_chat_alarm, + trigger, + 0 ); + + while(!(matched || aborted)) + { + Cyg_ErrNo err; + char c; + cyg_uint32 len = 1; + int i; + + err = cyg_io_read( cyg_ppp_chat_handle, &c, &len ); + + if( err != 0 ) + { + db_printf("%s(%d) err %d\n",__PRETTY_FUNCTION__,__LINE__,err); + break; + } + + db_printf("%c",c); + + *s++ = c; + + // If we have enough characters to match the expect string, + // and the current character matches the last character of the + // expect string, then it is worth trying to compare the strings. + + if( s - cyg_ppp_chat_buffer >= matchlen && + c == str[matchlen - 1] && + strncmp( s - matchlen, str, matchlen ) == 0 ) + { + matched = 1; + break; + } + + for( i = 0 ; i < cyg_ppp_chat_abort_count; i++ ) + { + int abortlen = strlen( cyg_ppp_chat_abort[i] ); + + if( s - cyg_ppp_chat_buffer >= abortlen && + strncmp( s - abortlen, cyg_ppp_chat_abort[i], abortlen ) == 0 ) + { + syslog(LOG_ERR,"ABORT: %s\n",cyg_ppp_chat_abort[i]); + aborted = 1; + break; + } + } + + // If we have reached the end of the buffer, shuffle it down + // leaving the last matchlen characters in place. + if( s >= &cyg_ppp_chat_buffer[sizeof(cyg_ppp_chat_buffer)-1] ) + { + strncpy( cyg_ppp_chat_buffer, s-matchlen, matchlen ); + s = cyg_ppp_chat_buffer + matchlen; + } + } + + cyg_alarm_disable( cyg_ppp_chat_alarm ); + + return matched; +} + +//===================================================================== +// Send string +// +// Send the string to the remote end, dealing with any escapes or +// special values. We currently implement the "EOT" and "BREAK" +// strings (or would if the device drivers did), and just the \c +// escape sequence. + +static int cyg_ppp_chat_send( const char *s ) +{ + cyg_bool crlf = true; + + db_printf("%s(%d) %s\n",__PRETTY_FUNCTION__,__LINE__,s); + + if( strcmp( s, "EOT" ) == 0 ) + { + s = "\x04"; + crlf = false; + } + else if( strcmp( s, "BREAK" ) == 0 ) + { + // cannot do this at present + } + + for( ; *s != 0 ; s++ ) + { + char c = *s; + cyg_uint32 one = 1; + + // Look for escape sequences and process them + if( c == '\\' ) + { + c = *++s; + + if( c == 0 ) + break; + + switch( c ) + { + case 'c': + crlf = false; + continue; + } + } + + cyg_io_write( cyg_ppp_chat_handle, &c, &one ); + } + + if( crlf ) + { + cyg_uint32 len = 2; + s = "\r\n"; + cyg_io_write( cyg_ppp_chat_handle, s, &len ); + } + + return 0; +} + +//===================================================================== +// find_sep +// +// Split the pointed-to string at the next '-' character. + +static char *find_sep( char **str ) +{ + char *s = *str; + char *res = s; + + if( *s == 0 ) + return NULL; + + while( *s != 0 && *s != '-' ) + s++; + + if( *s == '-' ) + *s++ = 0; + + *str = s; + return res; +} + + +//===================================================================== +// expect processing +// +// Parse the expect string. The return value indicates what it is: a +// match string, an ABORT, a TIMEOUT or a failure. + +static int cyg_ppp_chat_expect( const char *str ) +{ + char *expect; + char *reply; + char *s; + + db_printf("%s(%d) %s\n",__PRETTY_FUNCTION__,__LINE__,str); + + // Look for command strings + if( strcmp( str, "ABORT" ) == 0 ) + return CHAT_ABORT; + + if( strcmp( str, "TIMEOUT" ) == 0 ) + return CHAT_TIMEOUT; + + // Copy the string over to a writable buffer, so we can handle the + // sub-expect strings + strncpy( cyg_ppp_chat_expect_buffer, str, sizeof(cyg_ppp_chat_expect_buffer) ); + s = cyg_ppp_chat_expect_buffer; + + while(1) + { + // Split the string at the next '-' + expect = find_sep( &s ); + + // All done, it looks like we succeeded! + if( expect == NULL ) + return CHAT_MATCH; + + db_printf("%s(%d) expect %s\n",__PRETTY_FUNCTION__,__LINE__,expect); + + // Get and response string in case the match fails. + reply = find_sep( &s ); + + db_printf("%s(%d) reply %s\n",__PRETTY_FUNCTION__,__LINE__,reply); + + // Try a match. + if( cyg_ppp_chat_match( expect ) ) + return CHAT_MATCH; + + // The match failed. If there is no reply string, then the + // whole thing has failed. + if( reply == NULL ) + break; + + // Send the reply string an loop around to pick up the next + // expect string. + cyg_ppp_chat_send( reply ); + } + + return CHAT_FAIL; +} + +//===================================================================== +// Chat entry point +// +// This is the main CHAT entry point. It is given the name of a device +// plus a pointer to the expect script in argv format. The device +// should already have been set up to the correct baud rate, bits, +// parity, flow control etc. + +externC cyg_int32 cyg_ppp_chat( const char *devname, + const char *script[] ) +{ + const char *s; + Cyg_ErrNo err; + + cyg_ppp_chat_success = 0; + cyg_ppp_chat_thread = cyg_thread_self(); + cyg_ppp_chat_abort_count = 0; + cyg_ppp_chat_timeout = 45; + + while ((err = cyg_io_lookup(devname, &cyg_ppp_chat_handle)) < 0) { + if (err != 0) + syslog(LOG_ERR, "Failed to open %s: %d", devname,err); + } + + // Set up timeout alarm. + cyg_alarm_create( cyg_real_time_clock(), + chat_alarm, + (cyg_addrword_t)0, + &cyg_ppp_chat_alarm, + &cyg_ppp_chat_alarm_obj); + + + // Now loop over script handling the elements in turn + while( (s = *script++) != NULL && cyg_ppp_chat_success == 0 ) + { + int what = cyg_ppp_chat_expect( s ); + + db_printf("%s(%d) what %d\n",__PRETTY_FUNCTION__,__LINE__,what); + + if( what == CHAT_FAIL ) + { + cyg_ppp_chat_success = 1; + break; + } + + if( (s = *script++) != NULL ) + { + if( what == CHAT_MATCH ) + cyg_ppp_chat_send( s ); + else switch( what ) + { + case CHAT_ABORT: + // An abort string, add it to the table. + if( cyg_ppp_chat_abort_count >= CHAT_MAX_ABORTS ) + { + syslog(LOG_ERR,"Too many ABORT strings\n"); + } + else if( strlen( s ) >= CHAT_ABORT_SIZE ) + syslog(LOG_ERR,"Abort string too long\n"); + else + { + strncpy( cyg_ppp_chat_abort[cyg_ppp_chat_abort_count++], + s, CHAT_ABORT_SIZE ); + } + break; + + case CHAT_TIMEOUT: + // A new timeout value + cyg_ppp_chat_timeout = atoi( s ); + db_printf("New timeout >%s< %d\n",s,cyg_ppp_chat_timeout); + break; + } + } + } + + // Finally, wait for the serial device to drain + { + cyg_uint32 zero = 0; + cyg_uint32 len = sizeof(zero); + + cyg_io_get_config( cyg_ppp_chat_handle, + CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN, + &zero, &len); + + } + + return cyg_ppp_chat_success; +} + +//===================================================================== +// End of chat.c + diff --git a/packages/net/ppp/current/src/fsm.c b/packages/net/ppp/current/src/fsm.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/fsm.c @@ -0,0 +1,847 @@ +//========================================================================== +// +// src/fsm.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/fsm.c,v 1.8 1999/08/28 01:19:02 peter Exp $"; +#endif + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include +#include +#include +#include + +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/fsm.h" + +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((LOG_INFO, "%s: Up event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void +fsm_lowerdown(f) + fsm *f; +{ + switch( f->state ){ + case CLOSED: + f->state = INITIAL; + break; + + case STOPPED: + f->state = STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case CLOSING: + f->state = INITIAL; + UNTIMEOUT(fsm_timeout, 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((LOG_INFO, "%s: Down event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void +fsm_open(f) + fsm *f; +{ + switch( f->state ){ + case INITIAL: + f->state = STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case CLOSED: + if( f->flags & OPT_SILENT ) + f->state = STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + } + break; + + case CLOSING: + f->state = STOPPING; + /* fall through */ + case STOPPED: + case OPENED: + if( f->flags & OPT_RESTART ){ + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } +} + + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the CLOSED state. + */ +void +fsm_close(f, reason) + fsm *f; + char *reason; +{ + f->term_reason = reason; + f->term_reason_len = (reason == NULL? 0: strlen(reason)); + switch( f->state ){ + case STARTING: + f->state = INITIAL; + break; + case STOPPED: + f->state = CLOSED; + break; + case STOPPING: + f->state = CLOSING; + break; + + case REQSENT: + case ACKRCVD: + case ACKSENT: + case OPENED: + if( f->state != OPENED ) + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + else if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = CLOSING; + break; + } +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void +fsm_timeout(arg) + void *arg; +{ + fsm *f = (fsm *) arg; + + switch (f->state) { + case CLOSING: + case STOPPING: + if( f->retransmits <= 0 ){ + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == CLOSING)? CLOSED: STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + } else { + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + } + break; + + case REQSENT: + case ACKRCVD: + case ACKSENT: + if (f->retransmits <= 0) { + syslog(LOG_WARNING, "%s: timeout sending Config-Requests", + PROTO_NAME(f)); + f->state = STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) + (*f->callbacks->finished)(f); + + } else { + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) + (*f->callbacks->retransmit)(f); + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == ACKRCVD ) + f->state = REQSENT; + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_input - Input packet. + */ +void +fsm_input(f, inpacket, l) + fsm *f; + u_char *inpacket; + int l; +{ + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < HEADERLEN) { + FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.", + f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.", + f->protocol)); + return; + } + if (len > l) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.", + f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == INITIAL || f->state == STARTING ){ + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d.", + f->protocol, f->state)); + return; + } + + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, 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; + + FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f), id)); + switch( f->state ){ + case CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case CLOSING: + case STOPPING: + return; + + case OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci){ /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) + code = CONFREJ; /* Reject all CI */ + else + code = CONFACK; + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, code, id, inp, len); + + if (code == CONFACK) { + if (f->state == ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = OPENED; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + } else + f->state = ACKSENT; + f->nakloops = 0; + + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != ACKRCVD) + f->state = REQSENT; + if( code == CONFNAK ) + ++f->nakloops; + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void +fsm_rconfack(f, id, inp, len) + fsm *f; + int id; + u_char *inp; + int len; +{ + FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d.", + PROTO_NAME(f), id)); + + if (id != f->reqid || 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 */ + log_packet(inp, len, "Received bad configure-ack: ", LOG_ERR); + FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)", + PROTO_NAME(f), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case CLOSED: + case STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case REQSENT: + f->state = ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + break; + + case ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + break; + + case OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void +fsm_rconfnakrej(f, code, id, inp, len) + fsm *f; + int code, id; + u_char *inp; + int len; +{ + int (*proc) __P((fsm *, u_char *, int)); + int ret; + + FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d.", + PROTO_NAME(f), id)); + + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !(ret = proc(f, inp, len))) { + /* Nak/reject is bad - ignore it */ + log_packet(inp, len, "Received bad configure-nak/rej: ", LOG_ERR); + FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)", + PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), 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; +{ + char str[80]; + + FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d.", + PROTO_NAME(f), id)); + + switch (f->state) { + case ACKRCVD: + case ACKSENT: + f->state = REQSENT; /* Start over but keep trying */ + break; + + case OPENED: + if (len > 0) { + fmtmsg(str, sizeof(str), "%0.*v", len, p); + syslog(LOG_INFO, "%s terminated by peer (%s)", PROTO_NAME(f), str); + } else + syslog(LOG_INFO, "%s terminated by peer", PROTO_NAME(f)); + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->retransmits = 0; + f->state = STOPPING; + TIMEOUT(fsm_timeout, f, f->timeouttime); + break; + } + + fsm_sdata(f, TERMACK, id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void +fsm_rtermack(f) + fsm *f; +{ + FSMDEBUG((LOG_INFO, "fsm_rtermack(%s).", PROTO_NAME(f))); + + switch (f->state) { + case CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + case STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case ACKRCVD: + f->state = REQSENT; + break; + + case OPENED: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); + break; + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void +fsm_rcoderej(f, inp, len) + fsm *f; + u_char *inp; + int len; +{ + u_char code, id; + + FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s).", PROTO_NAME(f))); + + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + syslog(LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d", + PROTO_NAME(f), code, id); + + if( f->state == ACKRCVD ) + f->state = REQSENT; +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void +fsm_protreject(f) + fsm *f; +{ + switch( f->state ){ + case CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case CLOSED: + f->state = CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case STOPPING: + case REQSENT: + case ACKRCVD: + case ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case STOPPED: + f->state = STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case OPENED: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = STOPPING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void +fsm_sconfreq(f, retransmit) + fsm *f; + int retransmit; +{ + u_char *outp; + int cilen; + + if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){ + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) + (*f->callbacks->resetci)(f); + f->nakloops = 0; + } + + if( !retransmit ){ + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + outp = outpacket_buf + PPP_HDRLEN + HEADERLEN; + if( f->callbacks->cilen && f->callbacks->addci ){ + cilen = (*f->callbacks->cilen)(f); + if( cilen > peer_mru[f->unit] - HEADERLEN ) + cilen = peer_mru[f->unit] - HEADERLEN; + if (f->callbacks->addci) + (*f->callbacks->addci)(f, outp, &cilen); + } else + cilen = 0; + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, f->timeouttime); + + FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d", + PROTO_NAME(f), f->reqid)); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void +fsm_sdata(f, code, id, data, datalen) + fsm *f; + u_char code, id; + u_char *data; + int datalen; +{ + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + outp = outpacket_buf; + if (datalen > peer_mru[f->unit] - HEADERLEN) + datalen = peer_mru[f->unit] - HEADERLEN; + if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) + BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); + outlen = datalen + HEADERLEN; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + output(f->unit, outpacket_buf, outlen + PPP_HDRLEN); + + FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d, id %d.", + PROTO_NAME(f), code, id)); +} diff --git a/packages/net/ppp/current/src/if_ppp.c b/packages/net/ppp/current/src/if_ppp.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/if_ppp.c @@ -0,0 +1,1626 @@ +//========================================================================== +// +// src/if_ppp.c +// +//========================================================================== +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * if_ppp.c - Point-to-Point Protocol (PPP) Asynchronous driver. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Drew D. Perkins + * Carnegie Mellon University + * 4910 Forbes Ave. + * Pittsburgh, PA 15213 + * (412) 268-8576 + * ddp@andrew.cmu.edu + * + * Based on: + * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89 + * + * Copyright (c) 1987 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, + * 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. + * + * Serial Line interface + * + * Rick Adams + * Center for Seismic Studies + * 1300 N 17th Street, Suite 1450 + * Arlington, Virginia 22209 + * (703)276-7900 + * rick@seismo.ARPA + * seismo!rick + * + * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). + * Converted to 4.3BSD Beta by Chris Torek. + * Other changes made at Berkeley, based in part on code by Kirk Smith. + * + * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com) + * Added VJ tcp header compression; more unified ioctls + * + * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au). + * Cleaned up a lot of the mbuf-related code to fix bugs that + * caused system crashes and packet corruption. Changed pppstart + * so that it doesn't just give up with a collision if the whole + * packet doesn't fit in the output ring buffer. + * + * Added priority queueing for interactive IP packets, following + * the model of if_sl.c, plus hooks for bpf. + * Paul Mackerras (paulus@cs.anu.edu.au). + */ + +/* $FreeBSD: src/sys/net/if_ppp.c,v 1.67.2.1 2001/09/11 09:49:54 kris Exp $ */ +/* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */ +/* from NetBSD: if_ppp.c,v 1.15.2.2 1994/07/28 05:17:58 cgd Exp */ + +#include +#include +#include + +//#include "ppp.h" +#define NPPP 1 +#define _KERNEL + +#if 0 +#include "opt_inet.h" +#include "opt_ipx.h" +#include "opt_ppp.h" +#endif + +#ifdef INET +#define VJC +#endif +#define PPP_COMPRESS + +#include +//#include +//#include +#define suser(x) 0 +#include +#include +//#include +#include +//#include +#include +#include + +#include +#include +#include +//#include + +#if INET +#include +#include +#include +#include +#endif + +#if 0 // IPX +#include +#include +#endif + +#ifdef VJC +#include +#endif + +#include +#include + +/* minimise diffs */ +#ifndef splsoftnet +#define splsoftnet splnet +#endif + +#ifdef PPP_COMPRESS +#define PACKETPTR struct mbuf * +#include +#endif + +struct ppp_softc ppp_softc[NPPP]; + +/* XXX layering violation */ +extern void pppasyncattach __P((void *)); + +//static void pppattach __P((void *)); +void pppattach __P((void *)); +PSEUDO_SET(pppattach, if_ppp); + +static int pppsioctl __P((struct ifnet *ifp, u_long cmd, caddr_t data)); +static void pppintr __P((void)); + +static void ppp_requeue __P((struct ppp_softc *)); +static void ppp_ccp __P((struct ppp_softc *, struct mbuf *m, int rcvd)); +static void ppp_ccp_closed __P((struct ppp_softc *)); +static void ppp_inproc __P((struct ppp_softc *, struct mbuf *)); +//static void pppdumpm __P((struct mbuf *m0)); + +/* + * Some useful mbuf macros not in mbuf.h. + */ +#define M_IS_CLUSTER(m) ((m)->m_flags & M_EXT) + +#define M_DATASTART(m) \ + (M_IS_CLUSTER(m) ? (m)->m_ext.ext_buf : \ + (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat) + +#define M_DATASIZE(m) \ + (M_IS_CLUSTER(m) ? (m)->m_ext.ext_size : \ + (m)->m_flags & M_PKTHDR ? MHLEN: MLEN) + +/* + * We steal two bits in the mbuf m_flags, to mark high-priority packets + * for output, and received packets following lost/corrupted packets. + */ +#define M_HIGHPRI 0x2000 /* output packet for sc_fastq */ +#define M_ERRMARK 0x4000 /* steal a bit in mbuf m_flags */ + + +#ifdef PPP_COMPRESS +/* + * List of compressors we know about. + * We leave some space so maybe we can modload compressors. + */ + +extern struct compressor ppp_bsd_compress; +extern struct compressor ppp_deflate, ppp_deflate_draft; + +static struct compressor *ppp_compressors[8] = { +#if DO_BSD_COMPRESS && defined(PPP_BSDCOMP) + &ppp_bsd_compress, +#endif +#if DO_DEFLATE && defined(PPP_DEFLATE) + &ppp_deflate, + &ppp_deflate_draft, +#endif + NULL +}; +#endif /* PPP_COMPRESS */ + +/* + * Called from boot code to establish ppp interfaces. + */ +//static void +void +pppattach(dummy) + void *dummy; +{ + register struct ppp_softc *sc; + register int i = 0; + + for (sc = ppp_softc; i < NPPP; sc++) { + sc->sc_if.if_name = "ppp"; + sc->sc_if.if_unit = i++; + sc->sc_if.if_mtu = PPP_MTU; + sc->sc_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; + sc->sc_if.if_type = IFT_PPP; + sc->sc_if.if_hdrlen = PPP_HDRLEN; + sc->sc_if.if_ioctl = pppsioctl; + sc->sc_if.if_output = pppoutput; + sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; + sc->sc_inq.ifq_maxlen = IFQ_MAXLEN; + sc->sc_fastq.ifq_maxlen = IFQ_MAXLEN; + sc->sc_rawq.ifq_maxlen = IFQ_MAXLEN; +// sc->sc_fd = -1; + if_attach(&sc->sc_if); +#ifdef BPF + bpfattach(&sc->sc_if, DLT_PPP, PPP_HDRLEN); +#endif + } + register_netisr(NETISR_PPP, pppintr); + /* + * XXX layering violation - if_ppp can work over any lower level + * transport that cares to attach to it. + */ +// pppasyncattach(dummy); +} + +/* + * Allocate a ppp interface unit and initialize it. + */ +struct ppp_softc * +pppalloc(pid) + pid_t pid; +{ + int nppp, i; + struct ppp_softc *sc; + +#ifndef __ECOS + for (nppp = 0, sc = ppp_softc; nppp < NPPP; nppp++, sc++) + if (sc->sc_xfer == pid) { + sc->sc_xfer = 0; + return sc; + } +#endif + for (nppp = 0, sc = ppp_softc; nppp < NPPP; nppp++, sc++) + if (sc->sc_devp == NULL) + break; + + if (nppp >= NPPP) + return NULL; + + sc->sc_flags = 0; + sc->sc_mru = PPP_MRU; + sc->sc_relinq = NULL; + bzero((char *)&sc->sc_stats, sizeof(sc->sc_stats)); +#ifdef VJC + MALLOC(sc->sc_comp, struct slcompress *, sizeof(struct slcompress), + M_DEVBUF, M_NOWAIT); + if (sc->sc_comp) + sl_compress_init(sc->sc_comp, -1); +#endif +#ifdef PPP_COMPRESS + sc->sc_xc_state = NULL; + sc->sc_rc_state = NULL; +#endif /* PPP_COMPRESS */ + for (i = 0; i < NUM_NP; ++i) + sc->sc_npmode[i] = NPMODE_ERROR; + sc->sc_npqueue = NULL; + sc->sc_npqtail = &sc->sc_npqueue; + sc->sc_last_sent = sc->sc_last_recv = time_second; + + return sc; +} + +/* + * Deallocate a ppp unit. Must be called at splsoftnet or higher. + */ +void +pppdealloc(sc) + struct ppp_softc *sc; +{ + struct mbuf *m; + + if_down(&sc->sc_if); + sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + getmicrotime(&sc->sc_if.if_lastchange); +#if 1 //def __NEVER__ + sc->sc_devp = NULL; +#else + sc->sc_fd = -1; +#endif + sc->sc_xfer = 0; + for (;;) { + IF_DEQUEUE(&sc->sc_rawq, m); + if (m == NULL) + break; + m_freem(m); + } + for (;;) { + IF_DEQUEUE(&sc->sc_inq, m); + if (m == NULL) + break; + m_freem(m); + } + for (;;) { + IF_DEQUEUE(&sc->sc_fastq, m); + if (m == NULL) + break; + m_freem(m); + } + while ((m = sc->sc_npqueue) != NULL) { + sc->sc_npqueue = m->m_nextpkt; + m_freem(m); + } +#ifdef PPP_COMPRESS + ppp_ccp_closed(sc); + sc->sc_xc_state = NULL; + sc->sc_rc_state = NULL; +#endif /* PPP_COMPRESS */ +#ifdef PPP_FILTER + if (sc->sc_pass_filt.bf_insns != 0) { + FREE(sc->sc_pass_filt.bf_insns, M_DEVBUF); + sc->sc_pass_filt.bf_insns = 0; + sc->sc_pass_filt.bf_len = 0; + } + if (sc->sc_active_filt.bf_insns != 0) { + FREE(sc->sc_active_filt.bf_insns, M_DEVBUF); + sc->sc_active_filt.bf_insns = 0; + sc->sc_active_filt.bf_len = 0; + } +#endif /* PPP_FILTER */ +#ifdef VJC + if (sc->sc_comp != 0) { + FREE(sc->sc_comp, M_DEVBUF); + sc->sc_comp = 0; + } +#endif +} + +/* + * Ioctl routine for generic ppp devices. + */ +int +pppioctl(sc, cmd, data, flag, p) + struct ppp_softc *sc; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int s, error, flags, mru, npx; + u_int nb; + struct ppp_option_data *odp; + struct compressor **cp; + struct npioctl *npi; + time_t t; +#ifdef PPP_FILTER + struct bpf_program *bp, *nbp; + struct bpf_insn *newcode, *oldcode; + int newcodelen; +#endif /* PPP_FILTER */ +#ifdef PPP_COMPRESS + u_char ccp_option[CCP_MAX_OPTION_LENGTH]; +#endif + + switch (cmd) { + case FIONREAD: + *(int *)data = sc->sc_inq.ifq_len; + break; + + case PPPIOCGUNIT: + *(int *)data = sc->sc_if.if_unit; + break; + + case PPPIOCGFLAGS: + *(u_int *)data = sc->sc_flags; + break; + + case PPPIOCSFLAGS: + if ((error = suser(p)) != 0) + return (error); + flags = *(int *)data & SC_MASK; + s = splsoftnet(); +#ifdef PPP_COMPRESS + if (sc->sc_flags & SC_CCP_OPEN && !(flags & SC_CCP_OPEN)) + ppp_ccp_closed(sc); +#endif + splimp(); + sc->sc_flags = (sc->sc_flags & ~SC_MASK) | flags; + splx(s); + break; + + case PPPIOCSMRU: + if ((error = suser(p)) != 0) + return (error); + mru = *(int *)data; + if (mru >= PPP_MRU && mru <= PPP_MAXMRU) + sc->sc_mru = mru; + break; + + case PPPIOCGMRU: + *(int *)data = sc->sc_mru; + break; + +#ifdef VJC + case PPPIOCSMAXCID: + if ((error = suser(p)) != 0) + return (error); + if (sc->sc_comp) { + s = splsoftnet(); + sl_compress_init(sc->sc_comp, *(int *)data); + splx(s); + } + break; +#endif + + case PPPIOCXFERUNIT: + if ((error = suser(p)) != 0) + return (error); +// sc->sc_xfer = p->p_pid; + sc->sc_xfer = 0; + break; + +#ifdef PPP_COMPRESS + case PPPIOCSCOMPRESS: + if ((error = suser(p)) != 0) + return (error); + odp = (struct ppp_option_data *) data; + nb = odp->length; + if (nb > sizeof(ccp_option)) + nb = sizeof(ccp_option); + if ((error = copyin(odp->ptr, ccp_option, nb)) != 0) + return (error); + if (ccp_option[1] < 2) /* preliminary check on the length byte */ + return (EINVAL); + for (cp = ppp_compressors; *cp != NULL; ++cp) + if ((*cp)->compress_proto == ccp_option[0]) { + /* + * Found a handler for the protocol - try to allocate + * a compressor or decompressor. + */ + error = 0; + if (odp->transmit) { + s = splsoftnet(); + if (sc->sc_xc_state != NULL) + (*sc->sc_xcomp->comp_free)(sc->sc_xc_state); + sc->sc_xcomp = *cp; + sc->sc_xc_state = (*cp)->comp_alloc(ccp_option, nb); + if (sc->sc_xc_state == NULL) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: comp_alloc failed\n", + sc->sc_if.if_unit); + error = ENOBUFS; + } + splimp(); + sc->sc_flags &= ~SC_COMP_RUN; + splx(s); + } else { + s = splsoftnet(); + if (sc->sc_rc_state != NULL) + (*sc->sc_rcomp->decomp_free)(sc->sc_rc_state); + sc->sc_rcomp = *cp; + sc->sc_rc_state = (*cp)->decomp_alloc(ccp_option, nb); + if (sc->sc_rc_state == NULL) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: decomp_alloc failed\n", + sc->sc_if.if_unit); + error = ENOBUFS; + } + splimp(); + sc->sc_flags &= ~SC_DECOMP_RUN; + splx(s); + } + return (error); + } + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: no compressor for [%x %x %x], %x\n", + sc->sc_if.if_unit, ccp_option[0], ccp_option[1], + ccp_option[2], nb); + return (EINVAL); /* no handler found */ +#endif /* PPP_COMPRESS */ + + case PPPIOCGNPMODE: + case PPPIOCSNPMODE: + npi = (struct npioctl *) data; + switch (npi->protocol) { + case PPP_IP: + npx = NP_IP; + break; + default: + return EINVAL; + } + if (cmd == PPPIOCGNPMODE) { + npi->mode = sc->sc_npmode[npx]; + } else { + if ((error = suser(p)) != 0) + return (error); + if (npi->mode != sc->sc_npmode[npx]) { + s = splsoftnet(); + sc->sc_npmode[npx] = npi->mode; + if (npi->mode != NPMODE_QUEUE) { + ppp_requeue(sc); + (*sc->sc_start)(sc); + } + splx(s); + } + } + break; + + case PPPIOCGIDLE: + s = splsoftnet(); + t = time_second; + ((struct ppp_idle *)data)->xmit_idle = t - sc->sc_last_sent; + ((struct ppp_idle *)data)->recv_idle = t - sc->sc_last_recv; + splx(s); + break; + +#ifdef PPP_FILTER + case PPPIOCSPASS: + case PPPIOCSACTIVE: + nbp = (struct bpf_program *) data; + if ((unsigned) nbp->bf_len > BPF_MAXINSNS) + return EINVAL; + newcodelen = nbp->bf_len * sizeof(struct bpf_insn); + if (newcodelen != 0) { + MALLOC(newcode, struct bpf_insn *, newcodelen, M_DEVBUF, M_WAITOK); + if (newcode == 0) { + return EINVAL; /* or sumpin */ + } + if ((error = copyin((caddr_t)nbp->bf_insns, (caddr_t)newcode, + newcodelen)) != 0) { + FREE(newcode, M_DEVBUF); + return error; + } + if (!bpf_validate(newcode, nbp->bf_len)) { + FREE(newcode, M_DEVBUF); + return EINVAL; + } + } else + newcode = 0; + bp = (cmd == PPPIOCSPASS)? &sc->sc_pass_filt: &sc->sc_active_filt; + oldcode = bp->bf_insns; + s = splimp(); + bp->bf_len = nbp->bf_len; + bp->bf_insns = newcode; + splx(s); + if (oldcode != 0) + FREE(oldcode, M_DEVBUF); + break; +#endif + + default: +// return (ENOIOCTL); + return ENOSYS; + } + return (0); +} + +/* + * Process an ioctl request to the ppp network interface. + */ +static int +pppsioctl(ifp, cmd, data) + register struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ +// struct proc *p = curproc; /* XXX */ + register struct ppp_softc *sc = &ppp_softc[ifp->if_unit]; + register struct ifaddr *ifa = (struct ifaddr *)data; + register struct ifreq *ifr = (struct ifreq *)data; + struct ppp_stats *psp; +#ifdef PPP_COMPRESS + struct ppp_comp_stats *pcp; +#endif + int s = splimp(), error = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_RUNNING) == 0) + ifp->if_flags &= ~IFF_UP; + break; + + case SIOCSIFADDR: + case SIOCAIFADDR: + switch(ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + break; +#endif +#ifdef IPX + case AF_IPX: + break; +#endif + default: + error = EAFNOSUPPORT; + break; + } + break; + + case SIOCSIFDSTADDR: + switch(ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + break; +#endif +#ifdef IPX + case AF_IPX: + break; +#endif + default: + error = EAFNOSUPPORT; + break; + } + break; + + case SIOCSIFMTU: + if ((error = suser(p)) != 0) + break; + if (ifr->ifr_mtu > PPP_MAXMTU) + error = EINVAL; + else { + sc->sc_if.if_mtu = ifr->ifr_mtu; + if (sc->sc_setmtu) + (*sc->sc_setmtu)(sc); + } + break; + + case SIOCGIFMTU: + ifr->ifr_mtu = sc->sc_if.if_mtu; + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifr == 0) { + error = EAFNOSUPPORT; + break; + } + switch(ifr->ifr_addr.sa_family) { +#ifdef INET + case AF_INET: + break; +#endif + default: + error = EAFNOSUPPORT; + break; + } + break; + + case SIOCGPPPSTATS: + psp = &((struct ifpppstatsreq *) data)->stats; + bzero(psp, sizeof(*psp)); + psp->p = sc->sc_stats; +#if defined(VJC) && !defined(SL_NO_STATS) + if (sc->sc_comp) { + psp->vj.vjs_packets = sc->sc_comp->sls_packets; + psp->vj.vjs_compressed = sc->sc_comp->sls_compressed; + psp->vj.vjs_searches = sc->sc_comp->sls_searches; + psp->vj.vjs_misses = sc->sc_comp->sls_misses; + psp->vj.vjs_uncompressedin = sc->sc_comp->sls_uncompressedin; + psp->vj.vjs_compressedin = sc->sc_comp->sls_compressedin; + psp->vj.vjs_errorin = sc->sc_comp->sls_errorin; + psp->vj.vjs_tossed = sc->sc_comp->sls_tossed; + } +#endif /* VJC */ + break; + +#ifdef PPP_COMPRESS + case SIOCGPPPCSTATS: + pcp = &((struct ifpppcstatsreq *) data)->stats; + bzero(pcp, sizeof(*pcp)); + if (sc->sc_xc_state != NULL) + (*sc->sc_xcomp->comp_stat)(sc->sc_xc_state, &pcp->c); + if (sc->sc_rc_state != NULL) + (*sc->sc_rcomp->decomp_stat)(sc->sc_rc_state, &pcp->d); + break; +#endif /* PPP_COMPRESS */ + + default: + error = ENOTTY; + } + splx(s); + return (error); +} + +/* + * Queue a packet. Start transmission if not active. + * Packet is placed in Information field of PPP frame. + * Called at splnet as the if->if_output handler. + * Called at splnet from pppwrite(). + */ +int +pppoutput(ifp, m0, dst, rtp) + struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; + struct rtentry *rtp; +{ + register struct ppp_softc *sc = &ppp_softc[ifp->if_unit]; + int protocol, address, control; + u_char *cp; + int s, error; + struct ip *ip; + struct ifqueue *ifq; + enum NPmode mode; + int len; + struct mbuf *m; + + if (sc->sc_devp == NULL || (ifp->if_flags & IFF_RUNNING) == 0 + || ((ifp->if_flags & IFF_UP) == 0 && dst->sa_family != AF_UNSPEC)) { + error = ENETDOWN; /* sort of */ + goto bad; + } + + /* + * Compute PPP header. + */ + m0->m_flags &= ~M_HIGHPRI; + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + address = PPP_ALLSTATIONS; + control = PPP_UI; + protocol = PPP_IP; + mode = sc->sc_npmode[NP_IP]; + + /* + * If this packet has the "low delay" bit set in the IP header, + * put it on the fastq instead. + */ + ip = mtod(m0, struct ip *); + if (ip->ip_tos & IPTOS_LOWDELAY) + m0->m_flags |= M_HIGHPRI; + break; +#endif +#ifdef IPX + case AF_IPX: + /* + * This is pretty bogus.. We dont have an ipxcp module in pppd + * yet to configure the link parameters. Sigh. I guess a + * manual ifconfig would do.... -Peter + */ + address = PPP_ALLSTATIONS; + control = PPP_UI; + protocol = PPP_IPX; + mode = NPMODE_PASS; + break; +#endif + case AF_UNSPEC: + address = PPP_ADDRESS(dst->sa_data); + control = PPP_CONTROL(dst->sa_data); + protocol = PPP_PROTOCOL(dst->sa_data); + mode = NPMODE_PASS; + break; + default: + diag_printf("ppp%d: af%d not supported\n", ifp->if_unit, dst->sa_family); + error = EAFNOSUPPORT; + goto bad; + } + + /* + * Drop this packet, or return an error, if necessary. + */ + if (mode == NPMODE_ERROR) { + error = ENETDOWN; + goto bad; + } + if (mode == NPMODE_DROP) { + error = 0; + goto bad; + } + + /* + * Add PPP header. If no space in first mbuf, allocate another. + * (This assumes M_LEADINGSPACE is always 0 for a cluster mbuf.) + */ + if (M_LEADINGSPACE(m0) < PPP_HDRLEN) { + m0 = m_prepend(m0, PPP_HDRLEN, M_DONTWAIT); + if (m0 == 0) { + error = ENOBUFS; + goto bad; + } + m0->m_len = 0; + } else + m0->m_data -= PPP_HDRLEN; + + cp = mtod(m0, u_char *); + *cp++ = address; + *cp++ = control; + *cp++ = protocol >> 8; + *cp++ = protocol & 0xff; + m0->m_len += PPP_HDRLEN; + + len = 0; + for (m = m0; m != 0; m = m->m_next) + len += m->m_len; + + if (sc->sc_flags & SC_LOG_OUTPKT) { + diag_printf("ppp%d output: ", ifp->if_unit); +// pppdumpm(m0); + } + + if ((protocol & 0x8000) == 0) { +#ifdef PPP_FILTER + /* + * Apply the pass and active filters to the packet, + * but only if it is a data packet. + */ + *mtod(m0, u_char *) = 1; /* indicates outbound */ + if (sc->sc_pass_filt.bf_insns != 0 + && bpf_filter(sc->sc_pass_filt.bf_insns, (u_char *) m0, + len, 0) == 0) { + error = 0; /* drop this packet */ + goto bad; + } + + /* + * Update the time we sent the most recent packet. + */ + if (sc->sc_active_filt.bf_insns == 0 + || bpf_filter(sc->sc_active_filt.bf_insns, (u_char *) m0, len, 0)) + sc->sc_last_sent = time_second; + + *mtod(m0, u_char *) = address; +#else + /* + * Update the time we sent the most recent data packet. + */ + sc->sc_last_sent = time_second; +#endif /* PPP_FILTER */ + } + +#ifdef BPF + /* + * See if bpf wants to look at the packet. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m0); +#endif + + /* + * Put the packet on the appropriate queue. + */ + s = splsoftnet(); /* redundant */ + if (mode == NPMODE_QUEUE) { + /* XXX we should limit the number of packets on this queue */ + *sc->sc_npqtail = m0; + m0->m_nextpkt = NULL; + sc->sc_npqtail = &m0->m_nextpkt; + } else { + /* fastq and if_snd are emptied at spl[soft]net now */ + ifq = (m0->m_flags & M_HIGHPRI)? &sc->sc_fastq: &ifp->if_snd; + if (IF_QFULL(ifq) && dst->sa_family != AF_UNSPEC) { + IF_DROP(ifq); + splx(s); + sc->sc_if.if_oerrors++; + sc->sc_stats.ppp_oerrors++; + error = ENOBUFS; + goto bad; + } + IF_ENQUEUE(ifq, m0); + (*sc->sc_start)(sc); + } + getmicrotime(&ifp->if_lastchange); + ifp->if_opackets++; + ifp->if_obytes += len; + + splx(s); + return (0); + +bad: + m_freem(m0); + return (error); +} + +/* + * After a change in the NPmode for some NP, move packets from the + * npqueue to the send queue or the fast queue as appropriate. + * Should be called at spl[soft]net. + */ +static void +ppp_requeue(sc) + struct ppp_softc *sc; +{ + struct mbuf *m, **mpp; + struct ifqueue *ifq; + enum NPmode mode; + + for (mpp = &sc->sc_npqueue; (m = *mpp) != NULL; ) { + switch (PPP_PROTOCOL(mtod(m, u_char *))) { + case PPP_IP: + mode = sc->sc_npmode[NP_IP]; + break; + default: + mode = NPMODE_PASS; + } + + switch (mode) { + case NPMODE_PASS: + /* + * This packet can now go on one of the queues to be sent. + */ + *mpp = m->m_nextpkt; + m->m_nextpkt = NULL; + ifq = (m->m_flags & M_HIGHPRI)? &sc->sc_fastq: &sc->sc_if.if_snd; + if (IF_QFULL(ifq)) { + IF_DROP(ifq); + sc->sc_if.if_oerrors++; + sc->sc_stats.ppp_oerrors++; + } else + IF_ENQUEUE(ifq, m); + break; + + case NPMODE_DROP: + case NPMODE_ERROR: + *mpp = m->m_nextpkt; + m_freem(m); + break; + + case NPMODE_QUEUE: + mpp = &m->m_nextpkt; + break; + } + } + sc->sc_npqtail = mpp; +} + +/* + * Transmitter has finished outputting some stuff; + * remember to call sc->sc_start later at splsoftnet. + */ +void +ppp_restart(sc) + struct ppp_softc *sc; +{ + int s = splimp(); + + sc->sc_flags &= ~SC_TBUSY; + schednetisr(NETISR_PPP); + splx(s); +} + + +/* + * Get a packet to send. This procedure is intended to be called at + * splsoftnet, since it may involve time-consuming operations such as + * applying VJ compression, packet compression, address/control and/or + * protocol field compression to the packet. + */ +struct mbuf * +ppp_dequeue(sc) + struct ppp_softc *sc; +{ + struct mbuf *m, *mp; + u_char *cp; + int address, control, protocol; + + /* + * Grab a packet to send: first try the fast queue, then the + * normal queue. + */ + IF_DEQUEUE(&sc->sc_fastq, m); + if (m == NULL) + IF_DEQUEUE(&sc->sc_if.if_snd, m); + if (m == NULL) + return NULL; + + ++sc->sc_stats.ppp_opackets; + + /* + * Extract the ppp header of the new packet. + * The ppp header will be in one mbuf. + */ + cp = mtod(m, u_char *); + address = PPP_ADDRESS(cp); + control = PPP_CONTROL(cp); + protocol = PPP_PROTOCOL(cp); + + switch (protocol) { + case PPP_IP: +#ifdef VJC + /* + * If the packet is a TCP/IP packet, see if we can compress it. + */ + if ((sc->sc_flags & SC_COMP_TCP) && sc->sc_comp != NULL) { + struct ip *ip; + int type; + + mp = m; + ip = (struct ip *) (cp + PPP_HDRLEN); + if (mp->m_len <= PPP_HDRLEN) { + mp = mp->m_next; + if (mp == NULL) + break; + ip = mtod(mp, struct ip *); + } + /* this code assumes the IP/TCP header is in one non-shared mbuf */ + if (ip->ip_p == IPPROTO_TCP) { + type = sl_compress_tcp(mp, ip, sc->sc_comp, + !(sc->sc_flags & SC_NO_TCP_CCID)); + switch (type) { + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + cp = mtod(m, u_char *); + cp[0] = address; /* header has moved */ + cp[1] = control; + cp[2] = 0; + break; + } + cp[3] = protocol; /* update protocol in PPP header */ + } + } +#endif /* VJC */ + break; + +#ifdef PPP_COMPRESS + case PPP_CCP: + ppp_ccp(sc, m, 0); + break; +#endif /* PPP_COMPRESS */ + } + +#ifdef PPP_COMPRESS + if (protocol != PPP_LCP && protocol != PPP_CCP + && sc->sc_xc_state && (sc->sc_flags & SC_COMP_RUN)) { + struct mbuf *mcomp = NULL; + int slen, clen; + + slen = 0; + for (mp = m; mp != NULL; mp = mp->m_next) + slen += mp->m_len; + clen = (*sc->sc_xcomp->compress) + (sc->sc_xc_state, &mcomp, m, slen, sc->sc_if.if_mtu + PPP_HDRLEN); + if (mcomp != NULL) { + if (sc->sc_flags & SC_CCP_UP) { + /* Send the compressed packet instead of the original. */ + m_freem(m); + m = mcomp; + cp = mtod(m, u_char *); + protocol = cp[3]; + } else { + /* Can't transmit compressed packets until CCP is up. */ + m_freem(mcomp); + } + } + } +#endif /* PPP_COMPRESS */ + + /* + * Compress the address/control and protocol, if possible. + */ + if (sc->sc_flags & SC_COMP_AC && address == PPP_ALLSTATIONS && + control == PPP_UI && protocol != PPP_ALLSTATIONS && + protocol != PPP_LCP) { + /* can compress address/control */ + m->m_data += 2; + m->m_len -= 2; + } + if (sc->sc_flags & SC_COMP_PROT && protocol < 0xFF) { + /* can compress protocol */ + if (mtod(m, u_char *) == cp) { + cp[2] = cp[1]; /* move address/control up */ + cp[1] = cp[0]; + } + ++m->m_data; + --m->m_len; + } + + return m; +} + +/* + * Software interrupt routine, called at spl[soft]net. + */ +static void +pppintr() +{ + struct ppp_softc *sc; + int i, s; + struct mbuf *m; + + sc = ppp_softc; + for (i = 0; i < NPPP; ++i, ++sc) { + s = splimp(); + if (!(sc->sc_flags & SC_TBUSY) + && (sc->sc_if.if_snd.ifq_head || sc->sc_fastq.ifq_head)) { + sc->sc_flags |= SC_TBUSY; + splx(s); + (*sc->sc_start)(sc); + } else + splx(s); + for (;;) { + s = splimp(); + IF_DEQUEUE(&sc->sc_rawq, m); + splx(s); + if (m == NULL) + break; + ppp_inproc(sc, m); + } + } +} + +#ifdef PPP_COMPRESS +/* + * Handle a CCP packet. `rcvd' is 1 if the packet was received, + * 0 if it is about to be transmitted. + */ +static void +ppp_ccp(sc, m, rcvd) + struct ppp_softc *sc; + struct mbuf *m; + int rcvd; +{ + u_char *dp, *ep; + struct mbuf *mp; + int slen, s; + + /* + * Get a pointer to the data after the PPP header. + */ + if (m->m_len <= PPP_HDRLEN) { + mp = m->m_next; + if (mp == NULL) + return; + dp = (mp != NULL)? mtod(mp, u_char *): NULL; + } else { + mp = m; + dp = mtod(mp, u_char *) + PPP_HDRLEN; + } + + ep = mtod(mp, u_char *) + mp->m_len; + if (dp + CCP_HDRLEN > ep) + return; + slen = CCP_LENGTH(dp); + if (dp + slen > ep) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("if_ppp/ccp: not enough data in mbuf (%p+%x > %p+%x)\n", + dp, slen, mtod(mp, u_char *), mp->m_len); + return; + } + + switch (CCP_CODE(dp)) { + case CCP_CONFREQ: + case CCP_TERMREQ: + case CCP_TERMACK: + /* CCP must be going down - disable compression */ + if (sc->sc_flags & SC_CCP_UP) { + s = splimp(); + sc->sc_flags &= ~(SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN); + splx(s); + } + break; + + case CCP_CONFACK: + if (sc->sc_flags & SC_CCP_OPEN && !(sc->sc_flags & SC_CCP_UP) + && slen >= CCP_HDRLEN + CCP_OPT_MINLEN + && slen >= CCP_OPT_LENGTH(dp + CCP_HDRLEN) + CCP_HDRLEN) { + if (!rcvd) { + /* we're agreeing to send compressed packets. */ + if (sc->sc_xc_state != NULL + && (*sc->sc_xcomp->comp_init) + (sc->sc_xc_state, dp + CCP_HDRLEN, slen - CCP_HDRLEN, + sc->sc_if.if_unit, 0, sc->sc_flags & SC_DEBUG)) { + s = splimp(); + sc->sc_flags |= SC_COMP_RUN; + splx(s); + } + } else { + /* peer is agreeing to send compressed packets. */ + if (sc->sc_rc_state != NULL + && (*sc->sc_rcomp->decomp_init) + (sc->sc_rc_state, dp + CCP_HDRLEN, slen - CCP_HDRLEN, + sc->sc_if.if_unit, 0, sc->sc_mru, + sc->sc_flags & SC_DEBUG)) { + s = splimp(); + sc->sc_flags |= SC_DECOMP_RUN; + sc->sc_flags &= ~(SC_DC_ERROR | SC_DC_FERROR); + splx(s); + } + } + } + break; + + case CCP_RESETACK: + if (sc->sc_flags & SC_CCP_UP) { + if (!rcvd) { + if (sc->sc_xc_state && (sc->sc_flags & SC_COMP_RUN)) + (*sc->sc_xcomp->comp_reset)(sc->sc_xc_state); + } else { + if (sc->sc_rc_state && (sc->sc_flags & SC_DECOMP_RUN)) { + (*sc->sc_rcomp->decomp_reset)(sc->sc_rc_state); + s = splimp(); + sc->sc_flags &= ~SC_DC_ERROR; + splx(s); + } + } + } + break; + } +} + +/* + * CCP is down; free (de)compressor state if necessary. + */ +static void +ppp_ccp_closed(sc) + struct ppp_softc *sc; +{ + if (sc->sc_xc_state) { + (*sc->sc_xcomp->comp_free)(sc->sc_xc_state); + sc->sc_xc_state = NULL; + } + if (sc->sc_rc_state) { + (*sc->sc_rcomp->decomp_free)(sc->sc_rc_state); + sc->sc_rc_state = NULL; + } +} +#endif /* PPP_COMPRESS */ + +/* + * PPP packet input routine. + * The caller has checked and removed the FCS and has inserted + * the address/control bytes and the protocol high byte if they + * were omitted. + */ +void +ppppktin(sc, m, lost) + struct ppp_softc *sc; + struct mbuf *m; + int lost; +{ + int s = splimp(); + + if (lost) + m->m_flags |= M_ERRMARK; + IF_ENQUEUE(&sc->sc_rawq, m); + schednetisr(NETISR_PPP); + splx(s); +} + +/* + * Process a received PPP packet, doing decompression as necessary. + * Should be called at splsoftnet. + */ +#define COMPTYPE(proto) ((proto) == PPP_VJC_COMP? TYPE_COMPRESSED_TCP: \ + TYPE_UNCOMPRESSED_TCP) + +static void +ppp_inproc(sc, m) + struct ppp_softc *sc; + struct mbuf *m; +{ + struct ifnet *ifp = &sc->sc_if; + struct ifqueue *inq; + int s, ilen = 0, xlen, proto, rv; + u_char *cp, adrs, ctrl; + struct mbuf *mp, *dmp = NULL; + u_char *iphdr; + u_int hlen; + + sc->sc_stats.ppp_ipackets++; + + if (sc->sc_flags & SC_LOG_INPKT) { + ilen = 0; + for (mp = m; mp != NULL; mp = mp->m_next) + ilen += mp->m_len; + diag_printf("ppp%d: got %d bytes\n", ifp->if_unit, ilen); +// pppdumpm(m); + } + + cp = mtod(m, u_char *); + adrs = PPP_ADDRESS(cp); + ctrl = PPP_CONTROL(cp); + proto = PPP_PROTOCOL(cp); + + if (m->m_flags & M_ERRMARK) { + m->m_flags &= ~M_ERRMARK; + s = splimp(); + sc->sc_flags |= SC_VJ_RESET; + splx(s); + } + +#ifdef PPP_COMPRESS + /* + * Decompress this packet if necessary, update the receiver's + * dictionary, or take appropriate action on a CCP packet. + */ + if (proto == PPP_COMP && sc->sc_rc_state && (sc->sc_flags & SC_DECOMP_RUN) + && !(sc->sc_flags & SC_DC_ERROR) && !(sc->sc_flags & SC_DC_FERROR)) { + /* decompress this packet */ + rv = (*sc->sc_rcomp->decompress)(sc->sc_rc_state, m, &dmp); + if (rv == DECOMP_OK) { + m_freem(m); + if (dmp == NULL) { + /* no error, but no decompressed packet produced */ + return; + } + m = dmp; + cp = mtod(m, u_char *); + proto = PPP_PROTOCOL(cp); + + } else { + /* + * An error has occurred in decompression. + * Pass the compressed packet up to pppd, which may take + * CCP down or issue a Reset-Req. + */ + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: decompress failed %d\n", ifp->if_unit, rv); + s = splimp(); + sc->sc_flags |= SC_VJ_RESET; + if (rv == DECOMP_ERROR) + sc->sc_flags |= SC_DC_ERROR; + else + sc->sc_flags |= SC_DC_FERROR; + splx(s); + } + + } else { + if (sc->sc_rc_state && (sc->sc_flags & SC_DECOMP_RUN)) { + (*sc->sc_rcomp->incomp)(sc->sc_rc_state, m); + } + if (proto == PPP_CCP) { + ppp_ccp(sc, m, 1); + } + } +#endif + + ilen = 0; + for (mp = m; mp != NULL; mp = mp->m_next) + ilen += mp->m_len; + +#ifdef VJC + if (sc->sc_flags & SC_VJ_RESET) { + /* + * If we've missed a packet, we must toss subsequent compressed + * packets which don't have an explicit connection ID. + */ + if (sc->sc_comp) + sl_uncompress_tcp(NULL, 0, TYPE_ERROR, sc->sc_comp); + s = splimp(); + sc->sc_flags &= ~SC_VJ_RESET; + splx(s); + } + + /* + * See if we have a VJ-compressed packet to uncompress. + */ + if (proto == PPP_VJC_COMP) { + if ((sc->sc_flags & SC_REJ_COMP_TCP) || sc->sc_comp == 0) + goto bad; + + xlen = sl_uncompress_tcp_core(cp + PPP_HDRLEN, m->m_len - PPP_HDRLEN, + ilen - PPP_HDRLEN, TYPE_COMPRESSED_TCP, + sc->sc_comp, &iphdr, &hlen); + + if (xlen <= 0) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: VJ uncompress failed on type comp\n", + ifp->if_unit); + goto bad; + } + + /* Copy the PPP and IP headers into a new mbuf. */ + MGETHDR(mp, M_DONTWAIT, MT_DATA); + if (mp == NULL) + goto bad; + mp->m_len = 0; + mp->m_next = NULL; + if (hlen + PPP_HDRLEN > MHLEN) { + MCLGET(mp, M_DONTWAIT); + if (M_TRAILINGSPACE(mp) < hlen + PPP_HDRLEN) { + m_freem(mp); + goto bad; /* lose if big headers and no clusters */ + } + } + cp = mtod(mp, u_char *); + cp[0] = adrs; + cp[1] = ctrl; + cp[2] = 0; + cp[3] = PPP_IP; + proto = PPP_IP; + bcopy(iphdr, cp + PPP_HDRLEN, hlen); + mp->m_len = hlen + PPP_HDRLEN; + + /* + * Trim the PPP and VJ headers off the old mbuf + * and stick the new and old mbufs together. + */ + m->m_data += PPP_HDRLEN + xlen; + m->m_len -= PPP_HDRLEN + xlen; + if (m->m_len <= M_TRAILINGSPACE(mp)) { + bcopy(mtod(m, u_char *), mtod(mp, u_char *) + mp->m_len, m->m_len); + mp->m_len += m->m_len; + MFREE(m, mp->m_next); + } else + mp->m_next = m; + m = mp; + ilen += hlen - xlen; + + } else if (proto == PPP_VJC_UNCOMP) { + if ((sc->sc_flags & SC_REJ_COMP_TCP) || sc->sc_comp == 0) + goto bad; + + xlen = sl_uncompress_tcp_core(cp + PPP_HDRLEN, m->m_len - PPP_HDRLEN, + ilen - PPP_HDRLEN, TYPE_UNCOMPRESSED_TCP, + sc->sc_comp, &iphdr, &hlen); + + if (xlen < 0) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: VJ uncompress failed on type uncomp\n", + ifp->if_unit); + goto bad; + } + + proto = PPP_IP; + cp[3] = PPP_IP; + } +#endif /* VJC */ + + /* + * If the packet will fit in a header mbuf, don't waste a + * whole cluster on it. + */ + if (ilen <= MHLEN && M_IS_CLUSTER(m)) { + MGETHDR(mp, M_DONTWAIT, MT_DATA); + if (mp != NULL) { + m_copydata(m, 0, ilen, mtod(mp, caddr_t)); + m_freem(m); + m = mp; + m->m_len = ilen; + } + } + m->m_pkthdr.len = ilen; + m->m_pkthdr.rcvif = ifp; + + if ((proto & 0x8000) == 0) { +#ifdef PPP_FILTER + /* + * See whether we want to pass this packet, and + * if it counts as link activity. + */ + adrs = *mtod(m, u_char *); /* save address field */ + *mtod(m, u_char *) = 0; /* indicate inbound */ + if (sc->sc_pass_filt.bf_insns != 0 + && bpf_filter(sc->sc_pass_filt.bf_insns, (u_char *) m, + ilen, 0) == 0) { + /* drop this packet */ + m_freem(m); + return; + } + if (sc->sc_active_filt.bf_insns == 0 + || bpf_filter(sc->sc_active_filt.bf_insns, (u_char *) m, ilen, 0)) + sc->sc_last_recv = time_second; + + *mtod(m, u_char *) = adrs; +#else + /* + * Record the time that we received this packet. + */ + sc->sc_last_recv = time_second; +#endif /* PPP_FILTER */ + } + +#ifdef BPF + /* See if bpf wants to look at the packet. */ + if (sc->sc_if.if_bpf) + bpf_mtap(&sc->sc_if, m); +#endif + + rv = 0; + switch (proto) { +#ifdef INET + case PPP_IP: + /* + * IP packet - take off the ppp header and pass it up to IP. + */ + if ((ifp->if_flags & IFF_UP) == 0 + || sc->sc_npmode[NP_IP] != NPMODE_PASS) { + /* interface is down - drop the packet. */ + m_freem(m); + return; + } + m->m_pkthdr.len -= PPP_HDRLEN; + m->m_data += PPP_HDRLEN; + m->m_len -= PPP_HDRLEN; + if (ipflow_fastforward(m)) { + sc->sc_last_recv = time_second; + return; + } + schednetisr(NETISR_IP); + inq = &ipintrq; + sc->sc_last_recv = time_second; /* update time of last pkt rcvd */ + break; +#endif +#ifdef IPX + case PPP_IPX: + /* + * IPX packet - take off the ppp header and pass it up to IPX. + */ + if ((sc->sc_if.if_flags & IFF_UP) == 0 + /* XXX: || sc->sc_npmode[NP_IPX] != NPMODE_PASS*/) { + /* interface is down - drop the packet. */ + m_freem(m); + return; + } + m->m_pkthdr.len -= PPP_HDRLEN; + m->m_data += PPP_HDRLEN; + m->m_len -= PPP_HDRLEN; + schednetisr(NETISR_IPX); + inq = &ipxintrq; + sc->sc_last_recv = time_second; /* update time of last pkt rcvd */ + break; +#endif + + default: + /* + * Some other protocol - place on input queue for read(). + */ + inq = &sc->sc_inq; + rv = 1; + break; + } + + /* + * Put the packet on the appropriate input queue. + */ + s = splimp(); + if (IF_QFULL(inq)) { + IF_DROP(inq); + splx(s); + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: input queue full\n", ifp->if_unit); + ifp->if_iqdrops++; + goto bad; + } + IF_ENQUEUE(inq, m); + splx(s); + ifp->if_ipackets++; + ifp->if_ibytes += ilen; + getmicrotime(&ifp->if_lastchange); + + if (rv) + (*sc->sc_ctlp)(sc); + + return; + + bad: + m_freem(m); + sc->sc_if.if_ierrors++; + sc->sc_stats.ppp_ierrors++; +} + +#if 0 + +#define MAX_DUMP_BYTES 128 + +static void +pppdumpm(m0) + struct mbuf *m0; +{ + char buf[3*MAX_DUMP_BYTES+4]; + char *bp = buf; + struct mbuf *m; + + for (m = m0; m; m = m->m_next) { + int l = m->m_len; + u_char *rptr = (u_char *)m->m_data; + + while (l--) { + if (bp > buf + sizeof(buf) - 4) + goto done; + *bp++ = hex2ascii(*rptr >> 4); + *bp++ = hex2ascii(*rptr++ & 0xf); + } + + if (m->m_next) { + if (bp > buf + sizeof(buf) - 3) + goto done; + *bp++ = '|'; + } else + *bp++ = ' '; + } +done: + if (m) + *bp++ = '>'; + *bp = 0; + diag_printf("%s\n", buf); +} + +#endif diff --git a/packages/net/ppp/current/src/ipcp.c b/packages/net/ppp/current/src/ipcp.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/ipcp.c @@ -0,0 +1,1534 @@ +//========================================================================== +// +// src/ipcp.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/ipcp.c,v 1.12 1999/08/28 01:19:03 peter Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/fsm.h" +#include "cyg/ppp/ipcp.h" + +#define option_error(msg) db_printf("Option error: %s\n", msg ) + +/* 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 */ + +/* local vars */ +static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ +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 */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci __P((fsm *)); /* Reset our CI */ +static int ipcp_cilen __P((fsm *)); /* Return length of our CI */ +static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */ +static int ipcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ +static int ipcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */ +static int ipcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ +static int ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */ +static void ipcp_up __P((fsm *)); /* We're UP */ +static void ipcp_down __P((fsm *)); /* We're DOWN */ +static void ipcp_finished __P((fsm *)); /* Don't need lower layer */ + +fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ + +static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches OPENED state */ + ipcp_down, /* Called when fsm leaves OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + +/* + * 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)); + +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_check_options, + ip_demand_conf, + ip_active_pkt +}; + +static void ipcp_clear_addrs __P((int)); + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + +/* + * Make a string representation of a network IP address. + */ +char * +ip_ntoa(ipaddr) +u_int32_t ipaddr; +{ + static char b[64]; + + ipaddr = ntohl(ipaddr); + + sprintf(b, "%d.%d.%d.%d", + (u_char)(ipaddr >> 24), + (u_char)(ipaddr >> 16), + (u_char)(ipaddr >> 8), + (u_char)(ipaddr)); + return b; +} + + +/* + * ipcp_init - Initialize IPCP. + */ +static void +ipcp_init(unit) + int unit; +{ + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(&ipcp_fsm[unit]); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = 1; + wo->neg_vj = 1; + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_STATES - 1; /* really max index */ + wo->cflag = 1; + + /* max slots and slot-id compression are currently hardwired in */ + /* ppp_if.c to 16 and 1, this needs to be changed (among other */ + /* things) gmc */ + + ao->neg_addr = 1; + ao->neg_vj = 1; + ao->maxslotindex = MAX_STATES - 1; + ao->cflag = 1; + + /* + * XXX These control whether the user may use the proxyarp + * and defaultroute options. + */ + ao->proxy_arp = 1; + ao->default_route = 1; +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +static void +ipcp_open(unit) + int unit; +{ + fsm_open(&ipcp_fsm[unit]); +} + + +/* + * ipcp_close - Take IPCP down. + */ +static void +ipcp_close(unit, reason) + int unit; + char *reason; +{ + fsm_close(&ipcp_fsm[unit], reason); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +static void +ipcp_lowerup(unit) + int unit; +{ + fsm_lowerup(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +static void +ipcp_lowerdown(unit) + int unit; +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +static void +ipcp_input(unit, p, len) + int unit; + u_char *p; + int len; +{ + fsm_input(&ipcp_fsm[unit], p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void +ipcp_protrej(unit) + int unit; +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_resetci - Reset our CI. + */ +static void +ipcp_resetci(f) + fsm *f; +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + if (wo->ouraddr == 0) + wo->accept_local = 1; + if (wo->hisaddr == 0) + wo->accept_remote = 1; + ipcp_gotoptions[f->unit] = *wo; + cis_received[f->unit] = 0; +} + + +/* + * ipcp_cilen - Return length of our CI. + */ +static int +ipcp_cilen(f) + fsm *f; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + + return (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj)); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + */ +static void +ipcp_addci(f, ucp, lenp) + fsm *f; + u_char *ucp; + int *lenp; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + int len = *lenp; + +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else \ + neg = 0; \ + } + +#define ADDCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u_int32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else \ + neg = 0; \ + } + + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +ipcp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_short cilen, citype, cishort; + u_int32_t cilong; + u_char cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) \ + goto bad; \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) \ + goto bad; \ + } \ + } + +#define ACKCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u_int32_t l; \ + if ((len -= addrlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) \ + goto bad; \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) \ + goto bad; \ + } \ + } + + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +ipcp_nakci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u_int32_t ciaddr1, ciaddr2, l; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDR(opt, neg, old, code) \ + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else \ + ciaddr2 = 0; \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "local IP address %s", + ip_ntoa(ciaddr1))); + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + try.hisaddr = ciaddr2; + IPCPDEBUG((LOG_INFO, "remote IP address %s", + ip_ntoa(ciaddr2))); + } + ); + + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) + try.maxslotindex = cimaxslotindex; + if (!cicflag) + try.cflag = 0; + } else { + try.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try.old_vj = 1; + try.vj_protocol = cishort; + } else { + try.neg_vj = 0; + } + } + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) + goto bad; + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) + goto bad; + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) + try.ouraddr = ciaddr1; + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) + try.hisaddr = ciaddr2; + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) + goto bad; + try.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) + try.ouraddr = ciaddr1; + if (try.ouraddr != 0) + try.neg_addr = 1; + no.neg_addr = 1; + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) + goto bad; + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != OPENED) + *go = try; + + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + */ +static int +ipcp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, ciflag, cilen; + u_short cishort; + u_int32_t cilong; + ipcp_options try; /* options to request next time */ + + try = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDR(opt, neg, old, val1, val2) \ + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == cilen && \ + p[0] == opt) { \ + u_int32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) \ + goto bad; \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) \ + goto bad; \ + } \ + try.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) \ + goto bad; \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) \ + goto bad; \ + } \ + try.neg = 0; \ + } + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != OPENED) + *go = try; + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +ipcp_reqci(f, inp, len, reject_if_disagree) + fsm *f; + u_char *inp; /* Requested CIs */ + int *len; /* Length of requested CIs */ + int reject_if_disagree; +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u_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; + + cis_received[f->unit] = 1; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_ADDRS: + IPCPDEBUG((LOG_INFO, "ipcp: received ADDRS ")); + if (!ao->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG((LOG_INFO, "(%s:", ip_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(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); + IPCPDEBUG((LOG_INFO, "%s)", ip_ntoa(ciaddr2))); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u_int32_t), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; + + case CI_ADDR: + IPCPDEBUG((LOG_INFO, "ipcp: received ADDR ")); + + if (!ao->neg_addr || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG((LOG_INFO, "(%s)", ip_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(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; + IPCPDEBUG((LOG_INFO, "ipcp: received DNS%d Request ", d+1)); + + /* 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; + IPCPDEBUG((LOG_INFO, "ipcp: received WINS%d Request ", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u_int32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_COMPRESSTYPE: + IPCPDEBUG((LOG_INFO, "ipcp: received COMPRESSTYPE ")); + if (!ao->neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + IPCPDEBUG((LOG_INFO, "(%d)", cishort)); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_STATES - 1; + ho->cflag = 1; + } + break; + + default: + orc = CONFREJ; + break; + } + +endswitch: + IPCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc))); + + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) /* Getting fed up with sending NAKs? */ + orc = CONFREJ; /* Get tough if so */ + else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) + BCOPY(cip, ucp, cilen); /* Move it */ + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && + wo->req_addr && !reject_if_disagree) { + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = ucp - inp; /* Compute output length */ + IPCPDEBUG((LOG_INFO, "ipcp: returning Configure-%s", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * 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(cyg_ppp_hostname)) != NULL) { + local = *(u_int32_t *)hp->h_addr; + if (local != 0 && !bad_ip_adrs(local)) + wo->ouraddr = local; + } + } + + if (demand && wo->hisaddr == 0) { + option_error("remote IP address required for demand-dialling\n"); + } +} + + +/* + * 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 (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr))) + return 0; + if (!sifup(u)) + return 0; + if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE)) + return 0; + if (wo->default_route) + if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr)) + default_route_set[u] = 1; + if (wo->proxy_arp) + if (sifproxyarp(u, wo->hisaddr)) + proxy_arp_set[u] = 1; + + syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(wo->ouraddr)); + syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(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]; + + np_up(f->unit, PPP_IP); + IPCPDEBUG((LOG_INFO, "ipcp: up")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr) + ho->hisaddr = wo->hisaddr; + + if (ho->hisaddr == 0) { + syslog(LOG_ERR, "Could not determine remote IP address"); + ipcp_close(f->unit, "Could not determine remote IP address"); + return; + } + if (go->ouraddr == 0) { + syslog(LOG_ERR, "Could not determine local IP address"); + ipcp_close(f->unit, "Could not determine local IP address"); + return; + } + + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (!auth_ip_addr(f->unit, ho->hisaddr)) { + syslog(LOG_ERR, "Peer is not authorized to use remote address %s", + ip_ntoa(ho->hisaddr)); + ipcp_close(f->unit, "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) { + if (go->ouraddr != wo->ouraddr) + syslog(LOG_WARNING, "Local IP address changed to %s", + ip_ntoa(go->ouraddr)); + if (ho->hisaddr != wo->hisaddr) + syslog(LOG_WARNING, "Remote IP address changed to %s", + ip_ntoa(ho->hisaddr)); + ipcp_clear_addrs(f->unit); + + /* Set the interface to the new addresses */ + mask = GetMask(go->ouraddr); + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) + default_route_set[f->unit] = 1; + + /* Make a proxy ARP entry if requested. */ + if (ipcp_wantoptions[f->unit].proxy_arp) + if (sifproxyarp(f->unit, ho->hisaddr)) + proxy_arp_set[f->unit] = 1; + + } + demand_rexmit(PPP_IP); + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + } else { + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + +#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } +#endif + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG((LOG_WARNING, "sifup failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + +#if (defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } +#endif + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) + default_route_set[f->unit] = 1; + + /* Make a proxy ARP entry if requested. */ + if (ipcp_wantoptions[f->unit].proxy_arp) + if (sifproxyarp(f->unit, ho->hisaddr)) + proxy_arp_set[f->unit] = 1; + + syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(go->ouraddr)); + syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(ho->hisaddr)); + } + +} + + +/* + * 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((LOG_INFO, "ipcp: down")); + np_down(f->unit, PPP_IP); + sifvjcomp(f->unit, 0, 0, 0); + + /* + * If we are doing dial-on-demand, set the interface + * to queue up outgoing packets (for now). + */ + if (demand) { + sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE); + } else { + sifdown(f->unit); + ipcp_clear_addrs(f->unit); + } + +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, + * proxy arp entries, etc. + */ +static void +ipcp_clear_addrs(unit) + int unit; +{ + u_int32_t ouraddr, hisaddr; + + ouraddr = ipcp_gotoptions[unit].ouraddr; + hisaddr = ipcp_hisoptions[unit].hisaddr; + if (proxy_arp_set[unit]) { + cifproxyarp(unit, hisaddr); + proxy_arp_set[unit] = 0; + } + if (default_route_set[unit]) { + cifdefaultroute(unit, ouraddr, hisaddr); + default_route_set[unit] = 0; + } + cifaddr(unit, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void +ipcp_finished(f) + fsm *f; +{ + np_finished(f->unit, PPP_IP); +} + +/* + * ipcp_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 %I", 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(p, len, printer, arg); + p += len; + len = 0; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} + +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#define IPPROTO_TCP 6 +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(pkt, len) + u_char *pkt; + int len; +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) + return 0; + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) + return 0; + if (get_ipproto(pkt) != IPPROTO_TCP) + return 1; + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) + return 0; + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) + return 0; + return 1; +} diff --git a/packages/net/ppp/current/src/lcp.c b/packages/net/ppp/current/src/lcp.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/lcp.c @@ -0,0 +1,1910 @@ +//========================================================================== +// +// src/lcp.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/lcp.c,v 1.9 1999/08/28 01:19:04 peter Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/fsm.h" +#include "cyg/ppp/lcp.h" +#include "cyg/ppp/chap.h" +#include "cyg/ppp/magic.h" + +/* 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 */ +u_int32_t xmit_accm[NUM_PPP][8]; /* extended transmit ACCM */ + +static u_int32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */ +static u_int32_t lcp_echo_number = 0; /* ID number of next echo frame */ +static u_int32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */ + +static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci __P((fsm *)); /* Reset our CI */ +static int lcp_cilen __P((fsm *)); /* Return length of our CI */ +static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */ +static int lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ +static int lcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */ +static int lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ +static int lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */ +static void lcp_up __P((fsm *)); /* We're UP */ +static void lcp_down __P((fsm *)); /* We're DOWN */ +static void lcp_starting __P((fsm *)); /* We need lower layer up */ +static void lcp_finished __P((fsm *)); /* We need lower layer down */ +static int lcp_extcode __P((fsm *, int, int, u_char *, int)); +static void lcp_rprotrej __P((fsm *, u_char *, int)); + +/* + * routines to send LCP echos to peer + */ + +static void lcp_echo_lowerup __P((int)); +static void lcp_echo_lowerdown __P((int)); +static void LcpEchoTimeout __P((void *)); +static void lcp_received_echo_reply __P((fsm *, int, u_char *, int)); +static void LcpSendEchoRequest __P((fsm *)); +static void LcpLinkFailure __P((fsm *)); +static void LcpEchoCheck __P((fsm *)); + +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches OPENED state */ + lcp_down, /* Called when fsm leaves OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +/* + * Protocol entry points. + * Some of these are called directly. + */ + +static void lcp_init __P((int)); +static void lcp_input __P((int, u_char *, int)); +static void lcp_protrej __P((int)); +static int lcp_printpkt __P((u_char *, int, + void (*) __P((void *, char *, ...)), void *)); + +struct protent lcp_protent = { + PPP_LCP, + lcp_init, + lcp_input, + lcp_protrej, + lcp_lowerup, + lcp_lowerdown, + lcp_open, + lcp_close, + lcp_printpkt, + NULL, + 1, + "LCP", + NULL, + 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 + sizeof(short) */ +#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ +#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ +#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ +#define CILEN_CBCP 3 + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + +/* + * 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); + + wo->passive = 0; + wo->silent = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line + implementations */ + wo->neg_mru = 1; + wo->mru = DEFMRU; + wo->neg_asyncmap = 0; + wo->asyncmap = 0; + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->chap_mdtype = CHAP_DIGEST_MD5; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + wo->neg_lqr = 0; /* no LQR implementation yet */ + wo->neg_cbcp = 0; + + ao->neg_mru = 1; + ao->mru = MAXMRU; + ao->neg_asyncmap = 1; + ao->asyncmap = 0; + ao->neg_chap = 1; + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->neg_upap = 1; + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_lqr = 0; /* no LQR implementation yet */ +#ifdef CBCP_SUPPORT + ao->neg_cbcp = 1; +#else + ao->neg_cbcp = 0; +#endif + + memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); + xmit_accm[unit][3] = 0x60000000; +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void +lcp_open(unit) + int unit; +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + + f->flags = 0; + if (wo->passive) + f->flags |= OPT_PASSIVE; + if (wo->silent) + f->flags |= OPT_SILENT; + fsm_open(f); +} + + +/* + * lcp_close - Take LCP down. + */ +void +lcp_close(unit, reason) + int unit; + char *reason; +{ + fsm *f = &lcp_fsm[unit]; +db_printf("%s()\n",__PRETTY_FUNCTION__); + + if (phase != PHASE_DEAD) + phase = PHASE_TERMINATE; + if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do a + * lcp_close() in passive/silent mode when a connection hasn't + * been established. + */ + f->state = CLOSED; + lcp_finished(f); + + } else + fsm_close(&lcp_fsm[unit], reason); +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void +lcp_lowerup(unit) + int unit; +{ + lcp_options *wo = &lcp_wantoptions[unit]; + + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ + ppp_set_xaccm(unit, xmit_accm[unit]); + ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0); + ppp_recv_config(unit, PPP_MRU, 0xffffffff, + wo->neg_pcompression, wo->neg_accompression); + peer_mru[unit] = PPP_MRU; + lcp_allowoptions[unit].asyncmap = xmit_accm[unit][0]; + + fsm_lowerup(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void +lcp_lowerdown(unit) + int unit; +{ + fsm_lowerdown(&lcp_fsm[unit]); +} + + +/* + * lcp_input - Input LCP packet. + */ +static void +lcp_input(unit, p, len) + int unit; + u_char *p; + int len; +{ + fsm *f = &lcp_fsm[unit]; + + 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; + LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id)); + magp = inp; + PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void +lcp_rprotrej(f, inp, len) + fsm *f; + u_char *inp; + int len; +{ + int i; + struct protent *protp; + u_short prot; + + LCPDEBUG((LOG_INFO, "lcp_rprotrej.")); + + if (len < sizeof (u_short)) { + LCPDEBUG((LOG_INFO, + "lcp_rprotrej: Rcvd short Protocol-Reject packet!")); + return; + } + + GETSHORT(prot, inp); + + LCPDEBUG((LOG_INFO, + "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!", + prot)); + + /* + * Protocol-Reject packets received in any state other than the LCP + * OPENED state SHOULD be silently discarded. + */ + if( f->state != OPENED ){ + LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d", + f->state)); + return; + } + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol == prot && protp->enabled_flag) { + (*protp->protrej)(f->unit); + return; + } + + syslog(LOG_WARNING, "Protocol-Reject for unsupported protocol 0x%x", + prot); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +/*ARGSUSED*/ +static void +lcp_protrej(unit) + int unit; +{ + /* + * Can't reject LCP! + */ + LCPDEBUG((LOG_WARNING, + "lcp_protrej: Received Protocol-Reject for LCP!")); + fsm_protreject(&lcp_fsm[unit]); +} + + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void +lcp_sprotrej(unit, p, len) + int unit; + u_char *p; + int len; +{ + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the OPENED state. + */ + p += 2; + len -= 2; + + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, + p, len); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void +lcp_resetci(f) + fsm *f; +{ + lcp_wantoptions[f->unit].magicnumber = magic(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; + peer_mru[f->unit] = PPP_MRU; + auth_reset(f->unit); +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int +lcp_cilen(f) + fsm *f; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!go->neg_chap && go->neg_upap) + + LENCILQR(go->neg_lqr) + + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void +lcp_addci(f, ucp, lenp) + fsm *f; + u_char *ucp; + int *lenp; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#define ADDCICHAP(opt, neg, val, digest) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCICHAR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, + go->asyncmap); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + syslog(LOG_ERR, "Bug in lcp_addci: wrong length"); + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +lcp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cilen, citype, cichar; + u_short cishort; + u_int32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || \ + citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || \ + citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } +#define ACKCICHAP(opt, neg, val, digest) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || \ + citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, + go->asyncmap); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); +bad: + LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +lcp_nakci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u_char 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, code) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define 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 \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort <= DEFMRU) + try.mru = cishort; + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((go->neg_chap || go->neg_upap) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; + no.neg_chap = go->neg_chap; + no.neg_upap = go->neg_upap; + INCPTR(2, p); + GETSHORT(cishort, p); + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { + /* + * If we were asking for CHAP, they obviously don't want to do it. + * If we weren't asking for CHAP, then we were asking for PAP, + * in which case this Nak is bad. + */ + if (!go->neg_chap) + goto bad; + try.neg_chap = 0; + + } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); + if (go->neg_chap) { + /* + * We were asking for CHAP/MD5; they must want a different + * algorithm. If they can't do MD5, we'll have to stop + * asking for CHAP. + */ + if (cichar != go->chap_mdtype) + try.neg_chap = 0; + } else { + /* + * Stop asking for PAP if we were asking for it. + */ + try.neg_upap = 0; + } + + } else { + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_chap) + try.neg_chap = 0; + else + try.neg_upap = 0; + p += cilen - CILEN_SHORT; + } + } + + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) + try.neg_lqr = 0; + else + try.lqr_period = cilong; + ); + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try.neg_cbcp = 0; + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try.magicnumber = magic(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, + try.neg_pcompression = 0; + ); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, + try.neg_accompression = 0; + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * 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.mru = cishort; + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + || no.neg_asyncmap || cilen != CILEN_LONG) + goto bad; + break; + case CI_AUTHTYPE: + if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) + goto bad; + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) + goto bad; + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) + goto bad; + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) + goto bad; + break; + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) + goto bad; + break; + } + 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) { + if (looped_back) { + if (++try.numloops >= lcp_loopbackfail) { + syslog(LOG_NOTICE, "Serial line is looped back."); + lcp_close(f->unit, "Loopback detected"); + } + } else + try.numloops = 0; + *go = try; + } + + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int +lcp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cichar; + u_short cishort; + u_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; \ + LCPDEBUG((LOG_INFO, "lcp_rejci rejected void opt %d", opt)); \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected short opt %d", opt)); \ + } +#define REJCICHAP(opt, neg, val, digest) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) \ + goto bad; \ + try.neg = 0; \ + try.neg_upap = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected chap opt %d", opt)); \ + } +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected long opt %d", opt)); \ + } +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected LQR opt %d", opt)); \ + } +#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; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected Callback opt %d", opt)); \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); + if (!go->neg_chap) { + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); + } + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != OPENED) + *go = try; + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!")); + 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((LOG_WARNING, "lcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + 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: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MRU")); + if (!ao->neg_mru || /* Allow option? */ + cilen != CILEN_SHORT) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + LCPDEBUG((LOG_INFO, "(%d)", cishort)); + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < MINMRU) { + orc = CONFNAK; /* Nak CI */ + 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: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ASYNCMAP")); + if (!ao->neg_asyncmap || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%x)", (unsigned int) cilong)); + + /* + * 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: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE")); + + if (cilen < CILEN_SHORT || + !(ao->neg_upap || ao->neg_chap)) { + /* + * Reject the option if we're not willing to authenticate. + */ + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + LCPDEBUG((LOG_INFO, "(%x)", cishort)); + + /* + * Authtype must be UPAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == PPP_PAP) { + if (ho->neg_chap || /* we've already accepted CHAP */ + cilen != CILEN_SHORT) { + LCPDEBUG((LOG_WARNING, + "lcp_reqci: rcvd AUTHTYPE PAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + orc = CONFNAK; /* NAK it and suggest CHAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->neg_upap = 1; + break; + } + if (cishort == PPP_CHAP) { + if (ho->neg_upap || /* we've already accepted PAP */ + cilen != CILEN_CHAP) { + LCPDEBUG((LOG_INFO, + "lcp_reqci: rcvd AUTHTYPE CHAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + orc = CONFNAK; /* NAK it and suggest PAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != CHAP_DIGEST_MD5 +#ifdef CHAPMS + && cichar != CHAP_MICROSOFT +#endif + ) { + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 1; + break; + } + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + if (ao->neg_chap) { + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + } else { + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + } + break; + + case CI_QUALITY: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd QUALITY")); + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + GETSHORT(cishort, p); + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%x %x)", cishort, (unsigned int) cilong)); + + /* + * 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: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MAGICNUMBER")); + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%x)", (unsigned int) cilong)); + + /* + * 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: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd PCOMPRESSION")); + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ACCOMPRESSION")); + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + default: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd unknown option %d", + citype)); + orc = CONFREJ; + break; + } + +endswitch: + LCPDEBUG((LOG_INFO, " (%s)", CODENAME(orc))); + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && 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((LOG_INFO, "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]; + + if (!go->neg_magicnumber) + go->magicnumber = 0; + if (!ho->neg_magicnumber) + ho->magicnumber = 0; + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + */ + ppp_send_config(f->unit, MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffff), + ho->neg_pcompression, ho->neg_accompression); + ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU), + (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; +{ +db_printf("%s()\n",__PRETTY_FUNCTION__); + 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; +{ +db_printf("%s()\n",__PRETTY_FUNCTION__); + link_terminated(f->unit); +} + + +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +static char *lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq" +}; + +static int +lcp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, id, len, olen; + 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"); + 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 "); + GETSHORT(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; + } + 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(p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + p += 4; + len -= 4; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} + +/* + * Time to shut down the link because there is nothing out there. + */ + +static +void LcpLinkFailure (f) + fsm *f; +{ + if (f->state == OPENED) { + syslog(LOG_INFO, "No response to %d echo-requests", lcp_echos_pending); + syslog(LOG_NOTICE, "Serial link appears to be disconnected."); + 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); + + /* + * Start the timer for the next interval. + */ + assert (lcp_echo_timer_running==0); + 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) { + syslog(LOG_DEBUG, "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) { + syslog(LOG_WARNING, "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/packages/net/ppp/current/src/magic.c b/packages/net/ppp/current/src/magic.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/magic.c @@ -0,0 +1,140 @@ +//========================================================================== +// +// src/magic.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/magic.c,v 1.8 1999/08/28 01:19:05 peter Exp $"; +#endif + +#include +#include +#include +#include + +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/magic.h" + +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); +#ifndef __ECOS + seed = get_host_seed() ^ t.tv_sec ^ t.tv_usec ^ getpid(); +#else + seed = get_host_seed() ^ t.tv_sec ^ t.tv_usec ^ 42; +#endif + srand48(seed); +} + +/* + * magic - Returns the next magic number. + */ +u_int32_t +magic() +{ + return (u_int32_t) mrand48(); +} + +#ifdef NO_DRAND48 +/* + * Substitute procedures for those systems which don't have + * drand48 et al. + */ + +double +drand48() +{ + return (double)random() / (double)0x7fffffffL; /* 2**31-1 */ +} + +long +mrand48() +{ + return random(); +} + +void +srand48(seedval) +long seedval; +{ + srandom((int)seedval); +} + +#endif diff --git a/packages/net/ppp/current/src/ppp_deflate.c b/packages/net/ppp/current/src/ppp_deflate.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/ppp_deflate.c @@ -0,0 +1,731 @@ +//========================================================================== +// +// src/ppp_deflate.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* $FreeBSD: src/sys/net/ppp_deflate.c,v 1.12 1999/08/28 00:48:26 peter Exp $ */ + +/* + * 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 mbufs on BSD-derived systems. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#define _KERNEL + +#include +//#include +#include +#include +#include +#include + +#define PACKETPTR struct mbuf * +#include + +#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)); +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, struct mbuf **mret, + struct mbuf *mp, int slen, int maxolen)); +static void z_incomp __P((void *state, struct mbuf *dmsg)); +static int z_decompress __P((void *state, struct mbuf *cmp, + struct mbuf **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 if_ppp.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 */ +}; + +/* + * Space allocation and freeing routines for use by zlib routines. + */ +void * +z_alloc(notused, items, size) + void *notused; + u_int items, size; +{ + void *ptr; + + MALLOC(ptr, void *, items * size, M_DEVBUF, M_NOWAIT); + return ptr; +} + +void +z_free(notused, ptr) + void *notused; + void *ptr; +{ + FREE(ptr, M_DEVBUF); +} + +/* + * 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]); + if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) + return NULL; + + MALLOC(state, struct deflate_state *, sizeof(struct deflate_state), + M_DEVBUF, M_NOWAIT); + if (state == NULL) + return NULL; + + state->strm.next_in = NULL; + state->strm.zalloc = z_alloc; + state->strm.zfree = z_free; + if (deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION, DEFLATE_METHOD_VAL, + -w_size, 8, Z_DEFAULT_STRATEGY) != Z_OK) { + FREE(state, M_DEVBUF); + return NULL; + } + + 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, M_DEVBUF); +} + +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); +} + +int +z_compress(arg, mret, mp, orig_len, maxolen) + void *arg; + struct mbuf **mret; /* compressed packet (out) */ + struct mbuf *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; + struct mbuf *m; + + /* + * Check that the protocol is in the range we handle. + */ + rptr = mtod(mp, u_char *); + proto = PPP_PROTOCOL(rptr); + if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) { + *mret = NULL; + return orig_len; + } + + /* Allocate one mbuf initially. */ + if (maxolen > orig_len) + maxolen = orig_len; + MGET(m, M_DONTWAIT, MT_DATA); + *mret = m; + if (m != NULL) { + m->m_len = 0; + if (maxolen + state->hdrlen > MLEN) + MCLGET(m, M_DONTWAIT); + wspace = M_TRAILINGSPACE(m); + if (state->hdrlen + PPP_HDRLEN + 2 < wspace) { + m->m_data += state->hdrlen; + wspace -= state->hdrlen; + } + wptr = mtod(m, u_char *); + + /* + * 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; + wptr = NULL; + wspace = 0; + } + ++state->seqno; + + rptr += (proto > 0xff)? 2: 3; /* skip 1st proto byte if 0 */ + state->strm.next_in = rptr; + state->strm.avail_in = mtod(mp, u_char *) + mp->m_len - rptr; + mp = mp->m_next; + 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 = mtod(mp, u_char *); + state->strm.avail_in = mp->m_len; + mp = mp->m_next; + if (mp == NULL) + flush = Z_PACKET_FLUSH; + } + if (state->strm.avail_out == 0) { + if (m != NULL) { + m->m_len = wspace; + olen += wspace; + MGET(m->m_next, M_DONTWAIT, MT_DATA); + m = m->m_next; + if (m != NULL) { + m->m_len = 0; + if (maxolen - olen > MLEN) + MCLGET(m, M_DONTWAIT); + state->strm.next_out = mtod(m, u_char *); + state->strm.avail_out = wspace = M_TRAILINGSPACE(m); + } + } + if (m == NULL) { + state->strm.next_out = NULL; + state->strm.avail_out = 1000000; + } + } + } + if (m != NULL) + olen += (m->m_len = wspace - state->strm.avail_out); + + /* + * See if we managed to reduce the size of the packet. + */ + if (m != NULL && olen < orig_len) { + state->stats.comp_bytes += olen; + state->stats.comp_packets++; + } else { + if (*mret != NULL) { + m_freem(*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->inc_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]); + if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) + return NULL; + + MALLOC(state, struct deflate_state *, sizeof(struct deflate_state), + M_DEVBUF, M_NOWAIT); + if (state == NULL) + return NULL; + + state->strm.next_out = NULL; + state->strm.zalloc = z_alloc; + state->strm.zfree = z_free; + if (inflateInit2(&state->strm, -w_size) != Z_OK) { + FREE(state, M_DEVBUF); + return NULL; + } + + 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, M_DEVBUF); +} + +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. + */ +int +z_decompress(arg, mi, mop) + void *arg; + struct mbuf *mi, **mop; +{ + struct deflate_state *state = (struct deflate_state *) arg; + struct mbuf *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 = mtod(mi, u_char *); + rlen = mi->m_len; + for (i = 0; i < PPP_HDRLEN + DEFLATE_OVHD; ++i) { + while (rlen <= 0) { + mi = mi->m_next; + if (mi == NULL) + return DECOMP_ERROR; + rptr = mtod(mi, u_char *); + rlen = mi->m_len; + } + hdr[i] = *rptr++; + --rlen; + } + + /* Check the sequence number. */ + seq = (hdr[PPP_HDRLEN] << 8) + hdr[PPP_HDRLEN+1]; + if (seq != state->seqno) { + if (state->debug) + printf("z_decompress%d: bad seq # %d, expected %d\n", + state->unit, seq, state->seqno); + return DECOMP_ERROR; + } + ++state->seqno; + + /* Allocate an output mbuf. */ + MGETHDR(mo, M_DONTWAIT, MT_DATA); + if (mo == NULL) + return DECOMP_ERROR; + mo_head = mo; + mo->m_len = 0; + mo->m_next = NULL; + MCLGET(mo, M_DONTWAIT); + ospace = M_TRAILINGSPACE(mo); + if (state->hdrlen + PPP_HDRLEN < ospace) { + mo->m_data += state->hdrlen; + ospace -= state->hdrlen; + } + + /* + * Fill in the first part of the PPP header. The protocol field + * comes from the decompressed data. + */ + wptr = mtod(mo, u_char *); + 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 = rlen; + mi = mi->m_next; + flush = (mi == NULL)? Z_PACKET_FLUSH: Z_NO_FLUSH; + rlen += PPP_HDRLEN + DEFLATE_OVHD; + state->strm.next_out = wptr + 3; + state->strm.avail_out = 1; + decode_proto = 1; + olen = PPP_HDRLEN; + + /* + * 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: "")); + m_freem(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 = mtod(mi, u_char *); + state->strm.avail_in = mi->m_len; + rlen += mi->m_len; + mi = mi->m_next; + 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; + --olen; + } + decode_proto = 0; + } else { + mo->m_len = ospace; + olen += ospace; + MGET(mo->m_next, M_DONTWAIT, MT_DATA); + mo = mo->m_next; + if (mo == NULL) { + m_freem(mo_head); + return DECOMP_ERROR; + } + MCLGET(mo, M_DONTWAIT); + state->strm.next_out = mtod(mo, u_char *); + state->strm.avail_out = ospace = M_TRAILINGSPACE(mo); + } + } + } + if (decode_proto) { + m_freem(mo_head); + return DECOMP_ERROR; + } + olen += (mo->m_len = ospace - state->strm.avail_out); +#if DEFLATE_DEBUG + if (state->debug && 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; + struct mbuf *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 = mtod(mi, u_char *); + proto = PPP_PROTOCOL(rptr); + if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) + return; + + ++state->seqno; + + /* + * Iterate through the mbufs, adding the characters in them + * to the decompressor's history. For the first mbuf, we start + * at the either the 1st or 2nd byte of the protocol field, + * depending on whether the protocol value is compressible. + */ + rlen = mi->m_len; + 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->m_next; + if (mi == NULL) + break; + state->strm.next_in = mtod(mi, u_char *); + state->strm.avail_in = mi->m_len; + rlen += mi->m_len; + } + + /* + * 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/packages/net/ppp/current/src/ppp_io.c b/packages/net/ppp/current/src/ppp_io.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/ppp_io.c @@ -0,0 +1,1107 @@ +//========================================================================== +// +// src/ppp_io.c +// +//========================================================================== +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003, 2004 eCosCentric Ltd. +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * ppp_tty.c - Point-to-Point Protocol (PPP) driver for asynchronous + * tty devices. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Drew D. Perkins + * Carnegie Mellon University + * 4910 Forbes Ave. + * Pittsburgh, PA 15213 + * (412) 268-8576 + * ddp@andrew.cmu.edu + * + * Based on: + * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89 + * + * Copyright (c) 1987 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, + * 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. + * + * Serial Line interface + * + * Rick Adams + * Center for Seismic Studies + * 1300 N 17th Street, Suite 1450 + * Arlington, Virginia 22209 + * (703)276-7900 + * rick@seismo.ARPA + * seismo!rick + * + * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). + * Converted to 4.3BSD Beta by Chris Torek. + * Other changes made at Berkeley, based in part on code by Kirk Smith. + * + * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com) + * Added VJ tcp header compression; more unified ioctls + * + * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au). + * Cleaned up a lot of the mbuf-related code to fix bugs that + * caused system crashes and packet corruption. Changed pppstart + * so that it doesn't just give up with a "collision" if the whole + * packet doesn't fit in the output ring buffer. + * + * Added priority queueing for interactive IP packets, following + * the model of if_sl.c, plus hooks for bpf. + * Paul Mackerras (paulus@cs.anu.edu.au). + */ + +/* $FreeBSD: src/sys/net/ppp_tty.c,v 1.43 2000/01/29 16:56:24 peter Exp $ */ + +#include +#include +#include + +#define _KERNEL + +#define VJC /* XXX for ppp_defs.h */ + +#include +#define suser(x) 0 +#include +#include +#include + +#ifdef PPP_FILTER +#include +#endif +#include +#include + +//========================================================================== + +#ifdef __ECOS + +#include "cyg/ppp/ppp_io.h" + +//#define db_printf diag_printf +#define db_printf(fmt, ... ) + +#define spltty(x) splsoftnet(x) + +#define ENOIOCTL ENOSYS + +#endif + +//========================================================================== + +static u_short pppfcs __P((u_short fcs, u_char *cp, int len)); +static void pppasyncstart __P((struct ppp_softc *)); +static void pppasyncctlp __P((struct ppp_softc *)); +static void pppasyncrelinq __P((struct ppp_softc *)); +static void pppasyncsetmtu __P((struct ppp_softc *)); +static void pppgetm __P((struct ppp_softc *sc)); +static void ppplogchar __P((struct ppp_softc *, int)); + +/* XXX called from if_ppp.c - layering violation */ +void pppasyncattach __P((void *)); + +//========================================================================== +/* + * Some useful mbuf macros not in mbuf.h. + */ +#define M_IS_CLUSTER(m) ((m)->m_flags & M_EXT) + +#define M_DATASTART(m) \ + (M_IS_CLUSTER(m) ? (m)->m_ext.ext_buf : \ + (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat) + +#define M_DATASIZE(m) \ + (M_IS_CLUSTER(m) ? (m)->m_ext.ext_size : \ + (m)->m_flags & M_PKTHDR ? MHLEN: MLEN) + +//========================================================================== +/* + * Does c need to be escaped? + */ +#define ESCAPE_P(c) (sc->sc_asyncmap[(c) >> 5] & (1 << ((c) & 0x1F))) + +//========================================================================== +/* + * Procedures for using an async tty interface for PPP. + */ + +/* This is a FreeBSD-2.X kernel. */ +//#define CCOUNT(q) ((q)->c_cc) +//#define PPP_LOWAT 100 /* Process more output when < LOWAT on queue */ +//#define PPP_HIWAT 400 /* Don't start a new packet if HIWAT on que */ + + +//========================================================================== +/* + * Line specific open routine for async tty devices. + * Attach the given tty to the first available ppp unit. + * Called from device open routine or ttioctl() at >= splsofttty() + */ +/* ARGSUSED */ + +int +cyg_ppp_pppopen(tp) + struct tty *tp; +{ + register struct ppp_softc *sc; + int error, s; + + if ((error = suser(p)) != 0) + return (error); + + s = spltty(); + + if ((sc = pppalloc(0)) == NULL) { + splx(s); + return ENXIO; + } + + if (sc->sc_relinq) + (*sc->sc_relinq)(sc); /* get previous owner to relinquish the unit */ + + sc->sc_ilen = 0; + sc->sc_m = NULL; + bzero(sc->sc_asyncmap, sizeof(sc->sc_asyncmap)); + sc->sc_asyncmap[0] = 0xffffffff; + sc->sc_asyncmap[3] = 0x60000000; + sc->sc_rasyncmap = 0; + sc->sc_devp = (void *) tp; + sc->sc_start = pppasyncstart; + sc->sc_ctlp = pppasyncctlp; + sc->sc_relinq = pppasyncrelinq; + sc->sc_setmtu = pppasyncsetmtu; + sc->sc_outm = NULL; + pppgetm(sc); + sc->sc_if.if_flags |= IFF_RUNNING; + getmicrotime(&sc->sc_if.if_lastchange); + tp->t_sc = (caddr_t) sc; + + splx(s); + + return (0); +} + +//========================================================================== +/* + * Line specific close routine, called from device close routine + * and from ttioctl at >= splsofttty(). + * Detach the tty from the ppp unit. + * Mimics part of ttyclose(). + */ + +int +cyg_ppp_pppclose(tp, flag) + struct tty *tp; + int flag; +{ + register struct ppp_softc *sc; + int s; + + db_printf("%s called\n", __PRETTY_FUNCTION__); + s = spltty(); + + sc = (struct ppp_softc *) tp->t_sc; + if (sc != NULL) { + tp->t_sc = NULL; + if (tp == (struct tty *) sc->sc_devp) { + pppasyncrelinq(sc); + pppdealloc(sc); + } + } + splx(s); + return 0; +} + +//========================================================================== +/* + * Relinquish the interface unit to another device. + */ + +static void +pppasyncrelinq(sc) + struct ppp_softc *sc; +{ + int s; + + s = spltty(); + if (sc->sc_outm) { + m_freem(sc->sc_outm); + sc->sc_outm = NULL; + } + if (sc->sc_m) { + m_freem(sc->sc_m); + sc->sc_m = NULL; + } + if (sc->sc_flags & SC_TIMEOUT) { + sc->sc_flags &= ~SC_TIMEOUT; + } + splx(s); +} + +//========================================================================== +/* + * This gets called from the upper layer to notify a mtu change + */ + +static void +pppasyncsetmtu(sc) +register struct ppp_softc *sc; +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); +} + +//========================================================================== +/* + * Line specific (tty) read routine. + * called at zero spl from the device driver in the response to user-level + * reads on the tty file descriptor (ie: pppd). + */ + +int +cyg_ppp_pppread(tp, uio, flag) + register struct tty *tp; + struct uio *uio; + int flag; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + struct mbuf *m, *m0; + register int s; + int error = 0; + + db_printf("%s called\n", __PRETTY_FUNCTION__); + + if (sc == NULL) + return 0; + + // Look for input. If there is none return EWOULDBLOCK, otherwise + // dequeue the next packet and return it. + + s = spltty(); + + if (tp != (struct tty *) sc->sc_devp ) { + splx(s); + return 0; + } + if (sc->sc_inq.ifq_head == NULL) + { + splx(s); + return EWOULDBLOCK; + } + + /* Get the packet from the input queue */ + IF_DEQUEUE(&sc->sc_inq, m0); + splx(s); + + + for (m = m0; m && uio->uio_resid; m = m->m_next) + { + if ((error = uiomove(mtod(m, u_char *), m->m_len, uio)) != 0) + break; + } + m_freem(m0); + + return (error); +} + +//========================================================================== +/* + * Line specific (tty) write routine. + * called at zero spl from the device driver in the response to user-level + * writes on the tty file descriptor (ie: pppd). + */ + +int +cyg_ppp_pppwrite(tp, uio, flag) + register struct tty *tp; + struct uio *uio; + int flag; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + struct mbuf *m, *m0, **mp; + struct sockaddr dst; + int len, error, s; + + s = spltty(); + for (mp = &m0; uio->uio_resid; mp = &m->m_next) { + MGET(m, M_WAIT, MT_DATA); + if ((*mp = m) == NULL) { + m_freem(m0); + splx(s); + return (ENOBUFS); + } + m->m_len = 0; + if (uio->uio_resid >= MCLBYTES / 2) + MCLGET(m, M_DONTWAIT); + len = M_TRAILINGSPACE(m); + if (len > uio->uio_resid) + len = uio->uio_resid; + if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) { + m_freem(m0); + splx(s); + return (error); + } + m->m_len = len; + } + dst.sa_family = AF_UNSPEC; + bcopy(mtod(m0, u_char *), dst.sa_data, PPP_HDRLEN); + m0->m_data += PPP_HDRLEN; + m0->m_len -= PPP_HDRLEN; + + /* call the upper layer to "transmit" it... */ + error = pppoutput(&sc->sc_if, m0, &dst, (struct rtentry *)0); + splx(s); + return (error); +} + +//========================================================================== +/* + * Line specific (tty) ioctl routine. + * This discipline requires that tty device drivers call + * the line specific l_ioctl routine from their ioctl routines. + */ +/* ARGSUSED */ +int +cyg_ppp_ppptioctl(tp, cmd, data, flag) + struct tty *tp; + u_long cmd; + caddr_t data; + int flag; +// struct proc *p; +{ + struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc; + int error, s; + + if (sc == NULL || tp != (struct tty *) sc->sc_devp) + return (ENOIOCTL); + + error = 0; + switch (cmd) { + case PPPIOCSASYNCMAP: + sc->sc_asyncmap[0] = *(u_int *)data; + break; + + case PPPIOCGASYNCMAP: + *(u_int *)data = sc->sc_asyncmap[0]; + break; + + case PPPIOCSRASYNCMAP: + if ((error = suser(p)) != 0) + break; + sc->sc_rasyncmap = *(u_int *)data; + break; + + case PPPIOCGRASYNCMAP: + *(u_int *)data = sc->sc_rasyncmap; + break; + + case PPPIOCSXASYNCMAP: + s = spltty(); + bcopy(data, sc->sc_asyncmap, sizeof(sc->sc_asyncmap)); + sc->sc_asyncmap[1] = 0; /* mustn't escape 0x20 - 0x3f */ + sc->sc_asyncmap[2] &= ~0x40000000; /* mustn't escape 0x5e */ + sc->sc_asyncmap[3] |= 0x60000000; /* must escape 0x7d, 0x7e */ + splx(s); + break; + + case PPPIOCGXASYNCMAP: + bcopy(sc->sc_asyncmap, data, sizeof(sc->sc_asyncmap)); + break; + + default: + error = pppioctl(sc, cmd, data, flag, 0); + if (error == 0 && cmd == PPPIOCSMRU) + pppgetm(sc); + } + + return error; +} + +//========================================================================== +/* + * 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 +}; + +//========================================================================== +/* + * Calculate a new FCS given the current FCS and the new data. + */ +static u_short +pppfcs(u_short fcs, u_char *cp, int len) +{ + while (len--) + fcs = PPP_FCS(fcs, *cp++); + return (fcs); +} + +//========================================================================== + +#ifdef __ECOS + +// Call pppasyncstart() to kick transmission into life. +int cyg_ppp_pppstart( struct tty *tp ) +{ + struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc; + + while( sc->sc_outm != NULL ) + { + // We wait for the output buffer to drain of all characters + // before we call pppasyncstart(). That way we know there will + // be room for some data. We would really like to just drain + // down to some low water mark, but the drivers don't provide + // that functionality. However, since most devices have + // hardware FIFO's, we have a grace period to get more + // characters into the device, so hopefully there won't be a + // gap on the line. + + cyg_uint32 nb = 0; + cyg_uint32 len = sizeof(nb); + int s; + + cyg_io_get_config( tp->t_handle, + CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN, + &nb, &len); + + s = splsoftnet(); + + pppasyncstart( sc ); + + splx(s); + } + + return 0; +} + +#endif + + +//========================================================================== +/* + * This gets called at splsoftnet from if_ppp.c at various times + * when there is data ready to be sent. + */ +static void +pppasyncstart(sc) + register struct ppp_softc *sc; +{ + register struct tty *tp = (struct tty *) sc->sc_devp; + register struct mbuf *m; + register int len; + register u_char *start, *stop, *cp; + int n, ndone, done, idle; + struct mbuf *m2; + int s; + + idle = 0; + + while (1) { + /* + * See if we have an existing packet partly sent. + * If not, get a new packet and start sending it. + */ + m = sc->sc_outm; + if (m == NULL) { + /* + * Get another packet to be sent. + */ + m = ppp_dequeue(sc); + if (m == NULL) { + idle = 1; + break; + } + + /* Calculate the FCS for the first mbuf's worth. */ + sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len); + getmicrotime(&sc->sc_if.if_lastchange); + } + + for (;;) { + start = mtod(m, u_char *); + len = m->m_len; + stop = start + len; + while (len > 0) { + /* + * Find out how many bytes in the string we can + * handle without doing something special. + */ + for (cp = start; cp < stop; cp++) + if (ESCAPE_P(*cp)) + break; + n = cp - start; + if (n) { +#ifndef __ECOS + /* NetBSD (0.9 or later), 4.3-Reno or similar. */ + ndone = n - b_to_q(start, n, &tp->t_outq); +#else + ndone = 0; +// db_printf("send: data %d\n",ndone); +// db_dump_buf(start,ndone); + { + cyg_serial_buf_info_t info; + cyg_uint32 ilen = sizeof(info); + if( cyg_io_get_config( tp->t_handle, CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO, &info, &ilen ) == 0 ) + { + ndone = info.tx_bufsize-info.tx_count; + if( ndone > n ) + ndone = n; + cyg_io_write( tp->t_handle, start, &ndone); + } + } +#endif + len -= ndone; + start += ndone; + sc->sc_stats.ppp_obytes += ndone; + + if (ndone < n) + break; /* packet doesn't fit */ + } + /* + * If there are characters left in the mbuf, + * the first one must be special. + * Put it out in a different form. + */ + if (len) { + s = spltty(); +#ifndef __ECOS + if (putc(PPP_ESCAPE, &tp->t_outq)) { + splx(s); + break; + } + if (putc(*start ^ PPP_TRANS, &tp->t_outq)) { + (void) unputc(&tp->t_outq); + splx(s); + break; + } +#else + { + cyg_serial_buf_info_t info; + cyg_uint32 ilen = sizeof(info); + if( cyg_io_get_config( tp->t_handle, CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO, &info, &ilen ) == 0 && + info.tx_bufsize-info.tx_count >= 2 ) + { + char buf[2]; + int blen = 2; + buf[0] = PPP_ESCAPE; + buf[1] = *start ^ PPP_TRANS; + if( cyg_io_write( tp->t_handle, buf, &blen) != 0 ) { + splx(s); + break; + } + } + else + { + splx(s); + break; + } + } +#endif + splx(s); + sc->sc_stats.ppp_obytes += 2; + start++; + len--; + } // if( len ) + } // while (len > 0) + + /* + * If we didn't empty this mbuf, remember where we're up to. + * If we emptied the last mbuf, try to add the FCS and closing + * flag, and if we can't, leave sc_outm pointing to m, but with + * m->m_len == 0, to remind us to output the FCS and flag later. + */ + done = len == 0; + if (done && m->m_next == NULL) { + u_char *p, *q; + int c; + u_char endseq[8]; + + /* + * We may have to escape the bytes in the FCS. + */ + p = endseq; + c = ~sc->sc_outfcs & 0xFF; + if (ESCAPE_P(c)) { + *p++ = PPP_ESCAPE; + *p++ = c ^ PPP_TRANS; + } else + *p++ = c; + c = (~sc->sc_outfcs >> 8) & 0xFF; + if (ESCAPE_P(c)) { + *p++ = PPP_ESCAPE; + *p++ = c ^ PPP_TRANS; + } else + *p++ = c; + *p++ = PPP_FLAG; + + /* + * Try to output the FCS and flag. If the bytes + * don't all fit, back out. + */ + s = spltty(); +#ifndef __ECOS + for (q = endseq; q < p; ++q) + if (putc(*q, &tp->t_outq)) { + done = 0; + for (; q > endseq; --q) + unputc(&tp->t_outq); + break; + } +#else + { + int elen = p-endseq; + cyg_serial_buf_info_t info; + cyg_uint32 ilen = sizeof(info); + q = p; + if( (cyg_io_get_config( tp->t_handle, CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO, &info, &ilen ) == 0) && + ((info.tx_bufsize-info.tx_count) >= elen) ) + { + if( cyg_io_write( tp->t_handle, endseq, &elen) != 0 ) { + done = 0; + } + } + else + { + done = 0; + } + } +#endif + splx(s); + if (done) + sc->sc_stats.ppp_obytes += q - endseq; + } // if (done && m->m_next == NULL) + + if (!done) { + /* remember where we got to */ + m->m_data = start; + m->m_len = len; + break; + } + + /* Finished with this mbuf; free it and move on. */ + MFREE(m, m2); + m = m2; + if (m == NULL) { + /* Finished a packet */ + break; + } + sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, u_char *), m->m_len); + } // for(;;) + + /* + * If m == NULL, we have finished a packet. + * If m != NULL, we've either done as much work this time + * as we need to, or else we've filled up the output queue. + */ + sc->sc_outm = m; + if (m) + break; + } + +#ifndef __ECOS + /* Call pppstart to start output again if necessary. */ + s = spltty(); + pppstart(tp); + + /* + * This timeout is needed for operation on a pseudo-tty, + * because the pty code doesn't call pppstart after it has + * drained the t_outq. + */ + if (!idle && (sc->sc_flags & SC_TIMEOUT) == 0) { + sc->sc_ch = timeout(ppp_timeout, (void *) sc, 1); + sc->sc_flags |= SC_TIMEOUT; + } + + splx(s); + +#else + + s = spltty(); + + if( sc->sc_outm != NULL && + cyg_thread_self() != tp->tx_thread ) + { + cyg_semaphore_post( &tp->tx_sem ); + } + + splx(s); + +#endif + +} + +//========================================================================== +/* + * This gets called when a received packet is placed on + * the inq, at splsoftnet. The pppd daemon is to be woken up to do a read(). + */ +static void +pppasyncctlp(sc) + struct ppp_softc *sc; +{ + struct tty *tp; + int s; + + s = spltty(); + tp = (struct tty *) sc->sc_devp; + + tp->pppd_wakeup = 1; + if( tp->pppd_thread != cyg_thread_self() ) + cyg_thread_release( tp->pppd_thread ); + + splx(s); +} + +//========================================================================== +/* + * Allocate enough mbuf to handle current MRU. + */ +static void +pppgetm(sc) + register struct ppp_softc *sc; +{ + struct mbuf *m, **mp; + int len; + + mp = &sc->sc_m; + for (len = sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN; len > 0; ){ + if ((m = *mp) == NULL) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + break; + *mp = m; + MCLGET(m, M_DONTWAIT); + } + len -= M_DATASIZE(m); + mp = &m->m_next; + } +} + +//========================================================================== +/* + * tty interface receiver interrupt. + */ + +static unsigned paritytab[8] = { + 0x96696996, 0x69969669, 0x69969669, 0x96696996, + 0x69969669, 0x96696996, 0x96696996, 0x69969669 +}; + +/* + * Called when character is available from device driver. + * Only guaranteed to be at splsofttty() or spltty() + * This is safe to be called while the upper half's netisr is preempted. + */ +int +cyg_ppp_pppinput(c, tp) + int c; + register struct tty *tp; +{ + register struct ppp_softc *sc; + struct mbuf *m; + int ilen, s; + + sc = (struct ppp_softc *) tp->t_sc; + if (sc == NULL || tp != (struct tty *) sc->sc_devp) + return 0; + +// ++tk_nin; + ++sc->sc_stats.ppp_ibytes; + + s = spltty(); + if (c & 0x80) + sc->sc_flags |= SC_RCV_B7_1; + else + sc->sc_flags |= SC_RCV_B7_0; + if (paritytab[c >> 5] & (1 << (c & 0x1F))) + sc->sc_flags |= SC_RCV_ODDP; + else + sc->sc_flags |= SC_RCV_EVNP; + splx(s); + + if (sc->sc_flags & SC_LOG_RAWIN) + ppplogchar(sc, c); + + if (c == PPP_FLAG) { + ilen = sc->sc_ilen; + sc->sc_ilen = 0; + if (sc->sc_rawin_count > 0) + ppplogchar(sc, -1); + + /* + * If SC_ESCAPED is set, then we've seen the packet + * abort sequence "}~". + */ + if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED) + || (ilen > 0 && sc->sc_fcs != PPP_GOODFCS)) { + s = spltty(); + sc->sc_flags |= SC_PKTLOST; /* note the dropped packet */ + if ((sc->sc_flags & (SC_FLUSH | SC_ESCAPED)) == 0){ +#if 1 + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: bad fcs %x, pkt len %d\n", + sc->sc_if.if_unit, sc->sc_fcs, ilen); +#endif + sc->sc_if.if_ierrors++; + sc->sc_stats.ppp_ierrors++; + } else + sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED); + splx(s); + return 0; + } + + if (ilen < PPP_HDRLEN + PPP_FCSLEN) { + if (ilen) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: too short (%d)\n", sc->sc_if.if_unit, ilen); + s = spltty(); + sc->sc_if.if_ierrors++; + sc->sc_stats.ppp_ierrors++; + sc->sc_flags |= SC_PKTLOST; + splx(s); + } + return 0; + } + + /* + * Remove FCS trailer. Somewhat painful... + */ + ilen -= 2; + if (--sc->sc_mc->m_len == 0) { + for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next) + ; + sc->sc_mc = m; + } + sc->sc_mc->m_len--; + + /* excise this mbuf chain */ + m = sc->sc_m; + sc->sc_m = sc->sc_mc->m_next; + sc->sc_mc->m_next = NULL; + + ppppktin(sc, m, sc->sc_flags & SC_PKTLOST); + if (sc->sc_flags & SC_PKTLOST) { + s = spltty(); + sc->sc_flags &= ~SC_PKTLOST; + splx(s); + } + + pppgetm(sc); + return 0; + } + + if (sc->sc_flags & SC_FLUSH) { + if (sc->sc_flags & SC_LOG_FLUSH) + ppplogchar(sc, c); + return 0; + } + + if (c < 0x20 && (sc->sc_rasyncmap & (1 << c))) + return 0; + + s = spltty(); + if (sc->sc_flags & SC_ESCAPED) { + sc->sc_flags &= ~SC_ESCAPED; + c ^= PPP_TRANS; + } else if (c == PPP_ESCAPE) { + sc->sc_flags |= SC_ESCAPED; + splx(s); + return 0; + } + splx(s); + + /* + * Initialize buffer on first octet received. + * First octet could be address or protocol (when compressing + * address/control). + * Second octet is control. + * Third octet is first or second (when compressing protocol) + * octet of protocol. + * Fourth octet is second octet of protocol. + */ + if (sc->sc_ilen == 0) { + /* reset the first input mbuf */ + if (sc->sc_m == NULL) { + pppgetm(sc); + if (sc->sc_m == NULL) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: no input mbufs!\n", sc->sc_if.if_unit); + goto flush; + } + } + m = sc->sc_m; + m->m_len = 0; + m->m_data = M_DATASTART(sc->sc_m); + sc->sc_mc = m; + sc->sc_mp = mtod(m, char *); + sc->sc_fcs = PPP_INITFCS; + if (c != PPP_ALLSTATIONS) { + if (sc->sc_flags & SC_REJ_COMP_AC) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: garbage received: 0x%x (need 0xFF)\n", + sc->sc_if.if_unit, c); + goto flush; + } + *sc->sc_mp++ = PPP_ALLSTATIONS; + *sc->sc_mp++ = PPP_UI; + sc->sc_ilen += 2; + m->m_len += 2; + } + } + if (sc->sc_ilen == 1 && c != PPP_UI) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: missing UI (0x3), got 0x%x\n", + sc->sc_if.if_unit, c); + goto flush; + } + if (sc->sc_ilen == 2 && (c & 1) == 1) { + /* a compressed protocol */ + *sc->sc_mp++ = 0; + sc->sc_ilen++; + sc->sc_mc->m_len++; + } + if (sc->sc_ilen == 3 && (c & 1) == 0) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: bad protocol %x\n", sc->sc_if.if_unit, + (sc->sc_mp[-1] << 8) + c); + goto flush; + } + + /* packet beyond configured mru? */ + if (++sc->sc_ilen > sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: packet too big\n", sc->sc_if.if_unit); + goto flush; + } + + /* is this mbuf full? */ + m = sc->sc_mc; + if (M_TRAILINGSPACE(m) <= 0) { + if (m->m_next == NULL) { + pppgetm(sc); + if (m->m_next == NULL) { + if (sc->sc_flags & SC_DEBUG) + diag_printf("ppp%d: too few input mbufs!\n", sc->sc_if.if_unit); + goto flush; + } + } + sc->sc_mc = m = m->m_next; + m->m_len = 0; + m->m_data = M_DATASTART(m); + sc->sc_mp = mtod(m, char *); + } + + ++m->m_len; + *sc->sc_mp++ = c; + sc->sc_fcs = PPP_FCS(sc->sc_fcs, c); + + return 0; + + flush: + if (!(sc->sc_flags & SC_FLUSH)) { + s = spltty(); + sc->sc_if.if_ierrors++; + sc->sc_stats.ppp_ierrors++; + sc->sc_flags |= SC_FLUSH; + splx(s); + if (sc->sc_flags & SC_LOG_FLUSH) + ppplogchar(sc, c); + } + return 0; +} + +//========================================================================== + +#define MAX_DUMP_BYTES 128 + +static void +ppplogchar(sc, c) + struct ppp_softc *sc; + int c; +{ + if (c >= 0) + sc->sc_rawin[sc->sc_rawin_count++] = c; + if (sc->sc_rawin_count >= sizeof(sc->sc_rawin) + || (c < 0 && sc->sc_rawin_count > 0)) { + diag_printf("ppp%d input:\n"); + diag_vdump_buf_with_offset( diag_printf, sc->sc_rawin, sc->sc_rawin_count, sc->sc_rawin ); + sc->sc_rawin_count = 0; + } +} + +//========================================================================== +// End of ppp_io.c + diff --git a/packages/net/ppp/current/src/pppd.c b/packages/net/ppp/current/src/pppd.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/pppd.c @@ -0,0 +1,1070 @@ +//========================================================================== +// +// src/pppd.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * main.c - Point-to-Point Protocol main module + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/main.c,v 1.19.6.1 2002/07/30 19:17:27 nectar Exp $"; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXPATHLEN PATH_MAX + +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/magic.h" +#include "cyg/ppp/fsm.h" +#include "cyg/ppp/lcp.h" +#include "cyg/ppp/ipcp.h" +#include "cyg/ppp/upap.h" +#include "cyg/ppp/chap.h" +#include "cyg/ppp/ccp.h" +#include "cyg/ppp/ppp_io.h" + +#ifdef CBCP_SUPPORT +#include "cbcp.h" +#endif + +#if defined(SUNOS4) +extern char *strerror(); +#endif + +#ifdef IPX_CHANGE +#include "ipxcp.h" +#endif /* IPX_CHANGE */ +#ifdef AT_CHANGE +#include "atcp.h" +#endif + +/* options */ + +#define option_error(msg) db_printf("Option error: %s\n", msg ) + + +/* + * Variables set by command-line options. + */ + +int debug = 1; /* Debug flag */ +int kdebugflag = 1; /* Tell kernel to print debug messages */ +int default_device = 0; /* Using /dev/tty or equivalent */ +char devnam[MAXPATHLEN] = "/dev/ser0"; /* Device name */ +int flowctl = CYG_PPP_FLOWCTL_HARDWARE; /* flow control */ +int modem = 0; /* Use modem control lines */ +cyg_serial_baud_rate_t inspeed = 0; /* Input/Output speed requested */ +u_int32_t netmask = 0; /* IP netmask to set on interface */ +int lockflag = 0; /* Create lock file to lock the serial dev */ +char *connector = NULL; /* Script to establish physical link */ +char *disconnector = NULL; /* Script to disestablish physical link */ +char *welcomer = NULL; /* Script to welcome client after connection */ +int max_con_attempts = 10; /* Maximum number of times to try dialing */ +int maxconnect = 0; /* Maximum connect time (seconds) */ +char user[MAXNAMELEN] = "nickg"; /* Our name for authenticating ourselves */ +char passwd[MAXSECRETLEN] = "xsecretx"; /* Password for PAP */ +int auth_required = 0; /* Peer is required to authenticate */ +int proxyarp = 0; /* Set up proxy ARP entry for peer */ +int persist = 0; /* Reopen link after it goes down */ +int uselogin = 0; /* Use /etc/passwd for checking PAP */ +int lcp_echo_interval = 60; /* Interval between LCP echo-requests */ +int lcp_echo_fails = 4; /* Tolerance to unanswered echo-requests */ +char our_name[20]; /* Our name for authentication purposes */ +char remote_name[20]; /* Peer's name for authentication */ +int explicit_remote = 1; /* remote_name specified with remotename opt */ +int usehostname = 0; /* Use hostname for our_name */ +int disable_defaultip = 0; /* Don't use hostname for default IP adrs */ +//int demand = 0; /* Do dial-on-demand */ +char *ipparam = NULL; /* Extra parameter for ip up/down scripts */ +int cryptpap = 0; /* Others' PAP passwords are encrypted */ +int idle_time_limit = 60; /* Shut down link if idle for this long */ +int holdoff = 0; /* Dead time before restarting */ +int refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ +int refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ + +const char **script; /* Chat connection script */ + + +/* interface vars */ +char ifname[32]; /* Interface name */ +static int ifunit; /* Interface unit number */ + +char *progname; /* Name of this program */ +char cyg_ppp_hostname[MAXNAMELEN]; /* Our hostname */ +time_t etime,stime; /* End and Start time */ +int minutes; /* connection duration */ + +cyg_io_handle_t tty_handle; /* IO handle on serial stream */ +mode_t tty_mode = -1; /* Original access permissions to tty */ +int baud_rate; /* Actual bits/second for serial device */ +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 phase; /* where the link is at */ +int kill_link; +int open_ccp_flag; + +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 */ + +char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n"; + +/* Prototypes for procedures local to this file. */ + +static void cleanup __P((void)); +static void close_tty __P((void)); +static void get_input __P((void)); +static void calltimeout __P((void)); +static struct timeval *timeleft __P((struct timeval *)); +//static void holdoff_end __P((void *)); +static void pr_log __P((void *, char *, ...)); + +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, +#ifdef CYGPKG_PPP_PAP + &pap_protent, +#endif +#ifdef CYGPKG_PPP_CHAP + &chap_protent, +#endif +#ifdef CBCP_SUPPORT + &cbcp_protent, +#endif + &ipcp_protent, + &ccp_protent, +#ifdef IPX_CHANGE + &ipxcp_protent, +#endif +#ifdef AT_CHANGE + &atcp_protent, +#endif + NULL +}; + + +externC void +cyg_pppd_main(CYG_ADDRWORD arg) +{ + int i; + struct timeval timo; + struct protent *protp; + int connect_attempts = 0; + + phase = PHASE_INITIALIZE; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + (*protp->init)(0); + + cyg_ppp_options_install( ((struct tty *)arg)->options ); + + if (!ppp_available()) { + option_error(no_ppp_msg); + exit(1); + } + + /* + * Initialize system-dependent stuff and magic number package. + */ + sys_init(); + magic_init(); + if (debug) + setlogmask(LOG_UPTO(LOG_DEBUG)); + + + for (;;) { + + need_holdoff = 1; + + { + Cyg_ErrNo err; + while ((err = cyg_io_lookup(devnam, &tty_handle)) < 0) { + if (err != 0) + syslog(LOG_ERR, "Failed to open %s: %d", devnam,err); + } + +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS + if( modem ) + { + cyg_uint32 len = sizeof(ppp_tty.serial_callbacks); + ppp_tty.serial_callbacks.fn = cyg_ppp_serial_callback; + ppp_tty.serial_callbacks.priv = (CYG_ADDRWORD)&ppp_tty; + + + err = cyg_io_set_config( tty_handle, + CYG_IO_SET_CONFIG_SERIAL_STATUS_CALLBACK, + &ppp_tty.serial_callbacks, + &len); + + if( err != 0 ) { + syslog(LOG_ERR, "cyg_io_set_config(serial callbacks): %d",err); + die(1); + } + + } +#endif + } + + hungup = 0; + kill_link = 0; + + /* set line speed, flow control, etc.; clear CLOCAL if modem option */ + set_up_tty(tty_handle, 0); + + if( script != NULL ) + { + if( !cyg_ppp_chat( devnam, script ) ) + { + connect_attempts++; + goto fail; + } + } + +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS + if( modem ) + { + while( !ppp_tty.carrier_detected ) + cyg_thread_delay(100); + } +#endif + + connect_attempts = 0; /* we made it through ok */ + + /* set up the serial device as a ppp interface */ + establish_ppp(tty_handle); + + syslog(LOG_INFO, "Using interface ppp%d", ifunit); + (void) sprintf(ifname, "ppp%d", ifunit); + + /* + * Start opening the connection and wait for + * incoming events (reply, timeout, etc.). + */ + syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devnam); + stime = time((time_t *) NULL); + + lcp_lowerup(0); + lcp_open(0); /* Start protocol */ + for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) { + wait_input(timeleft(&timo)); + calltimeout(); + get_input(); + if (kill_link) { + lcp_close(0, "User request"); + kill_link = 0; + } + if (open_ccp_flag) { + if (phase == PHASE_NETWORK) { + ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */ + (*ccp_protent.open)(0); + } + open_ccp_flag = 0; + } + } + + clean_check(); + disestablish_ppp(tty_handle); + + fail: + if (tty_handle != 0) + close_tty(); + + /* limit to retries? */ + if (max_con_attempts) + if (connect_attempts >= max_con_attempts) + break; + + if (!persist) + die(1); + +#if 0 + if (holdoff > 0 && need_holdoff) { + phase = PHASE_HOLDOFF; + TIMEOUT(holdoff_end, NULL, holdoff); + do { + wait_time(timeleft(&timo)); + calltimeout(); + if (kill_link) { + if (!persist) + die(0); + kill_link = 0; + phase = PHASE_DORMANT; /* allow signal to end holdoff */ + } + + } while (phase == PHASE_HOLDOFF); + } +#endif + + } + + die(0); +} + +/* + * get_input - called when incoming data is available. + */ +static void +get_input() +{ + int len, i; + u_char *p; + u_short protocol; + struct protent *protp; + + p = inpacket_buf; /* point to beginning of packet buffer */ + + len = read_packet(inpacket_buf); + + if (len < 0) + return; + + if (len == 0) { + etime = time((time_t *) NULL); + minutes = (etime-stime)/60; + syslog(LOG_NOTICE, "Modem hangup, connected for %d minutes", (minutes >1) ? minutes : 1); + hungup = 1; + lcp_lowerdown(0); /* serial link is no longer available */ + link_terminated(0); + return; + } + + if (debug /*&& (debugflags & DBG_INPACKET)*/) + log_packet(p, len, "rcvd ", LOG_DEBUG); + + if (len < PPP_HDRLEN) { + MAINDEBUG((LOG_INFO, "io(): Received short packet.")); + return; + } + + p += 2; /* Skip address and control */ + GETSHORT(protocol, p); + len -= PPP_HDRLEN; + + /* + * Toss all non-LCP packets unless LCP is OPEN. + */ + if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) { + MAINDEBUG((LOG_INFO, + "get_input: Received non-LCP packet when LCP not open.")); + return; + } + + /* + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if (phase <= PHASE_AUTHENTICATE + && !(protocol == PPP_LCP || protocol == PPP_LQR + || protocol == PPP_PAP || protocol == PPP_CHAP)) { + MAINDEBUG((LOG_INFO, "get_input: discarding proto 0x%x in phase %d", + protocol, phase)); + return; + } + + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol && protp->enabled_flag) { + (*protp->input)(0, p, len); + return; + } + if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag + && protp->datainput != NULL) { + (*protp->datainput)(0, p, len); + return; + } + } + + if (debug) + syslog(LOG_WARNING, "Unsupported protocol (0x%x) received", protocol); + lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN); +} + + +/* + * quit - Clean up state and exit (with an error indication). + */ +void +quit() +{ + die(1); +} + +/* + * die - like quit, except we can specify an exit status. + */ +void +die(status) + int status; +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); +#ifndef __ECOS + cleanup(); + syslog(LOG_INFO, "Exit."); + exit(status); +#else + externC void sys_exit(void); + cleanup(); + sys_exit(); +#endif +} + +/* + * cleanup - restore anything which needs to be restored before we exit + */ +/* ARGSUSED */ +static void +cleanup() +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); + sys_cleanup(); + + if (tty_handle != 0) + close_tty(); +} + +/* + * close_tty - restore the terminal device and close it. + */ +static void +close_tty() +{ + disestablish_ppp(tty_handle); + +#ifndef __ECOS + /* drop dtr to hang up */ + if (modem) { + setdtr(ttyfd, FALSE); + /* + * 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); + } +#endif + + db_printf("%s called\n", __PRETTY_FUNCTION__); + + restore_tty(tty_handle); + + if( ppp_tty.t_handle != 0 ) + { + cyg_ppp_pppclose( &ppp_tty, 0 ); + ppp_tty.t_handle = 0; + } + + tty_handle = 0; +} + + +struct callout { + struct timeval c_time; /* time at which to call routine */ + void *c_arg; /* argument to routine */ + void (*c_func) __P((void *)); /* routine */ + struct callout *c_next; +}; + +static struct callout *callout = NULL; /* Callout list */ +static struct timeval timenow; /* Current time */ + +/* + * timeout - Schedule a timeout. + * + * Note that this timeout takes the number of seconds, NOT hz (as in + * the kernel). + */ +void +cyg_ppp_timeout(void (*func) __P((void *)), + void *arg, + int time) +{ + struct callout *newp, *p, **pp; + + MAINDEBUG((LOG_DEBUG, "Timeout %lx:%lx in %d seconds.", + (long) func, (long) arg, time)); + + /* + * Allocate timeout. + */ + if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) { + syslog(LOG_ERR, "Out of memory in timeout()!"); + die(1); + } + newp->c_arg = arg; + newp->c_func = func; + gettimeofday(&timenow, NULL); + newp->c_time.tv_sec = timenow.tv_sec + time; + newp->c_time.tv_usec = timenow.tv_usec; + + /* + * 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_sec)) + break; + newp->c_next = p; + *pp = newp; +} + + +/* + * untimeout - Unschedule a timeout. + */ +void +cyg_ppp_untimeout(void (*func) __P((void *)), + void *arg) +{ + struct callout **copp, *freep; + + MAINDEBUG((LOG_DEBUG, "Untimeout %lx:%lx.", (long) func, (long) arg)); + + /* + * Find first matching timeout and remove it from the list. + */ + for (copp = &callout; (freep = *copp); copp = &freep->c_next) + if (freep->c_func == func && freep->c_arg == arg) { + *copp = freep->c_next; + (void) 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) { + syslog(LOG_ERR, "Failed to get time of day: %m"); + die(1); + } + 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; +} + +/* + * log_packet - format a packet and log it. + */ + +char line[256]; /* line to be logged accumulated here */ +char *linep; + +void +log_packet(p, len, prefix, level) + u_char *p; + int len; + char *prefix; + int level; +{ + strcpy(line, prefix); + linep = line + strlen(line); + format_packet(p, len, pr_log, NULL); + if (linep != line) + syslog(level, "%s", line); +} + +/* + * format_packet - make a readable representation of a packet, + * calling `printer(arg, format, ...)' to output it. + */ +void +format_packet(p, len, printer, arg) + u_char *p; + int len; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int i, n; + u_short proto; + u_char x; + 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 { + printer(arg, "[proto=0x%x]", proto); + } + } + + for (; len > 0; --len) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } +} + +static void +pr_log __V((void *arg, char *fmt, ...)) +{ + int n; + va_list pvar; + char buf[256]; + +#if __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 = vfmtmsg(buf, sizeof(buf), fmt, pvar); + va_end(pvar); + + if (linep + n + 1 > line + sizeof(line)) { + syslog(LOG_DEBUG, "%s", line); + linep = line; + } + strcpy(linep, buf); + linep += n; +} + +/* + * 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, "\""); +} + +/* + * novm - log an error message saying we ran out of memory, and die. + */ +void +novm(msg) + char *msg; +{ + syslog(LOG_ERR, "Virtual memory exhausted allocating %s\n", msg); + die(1); +} + +/* + * fmtmsg - format a message into a buffer. Like sprintf except we + * also specify the length of the output buffer, and we handle + * %r (recursive format), %m (error message) and %I (IP address) formats. + * Doesn't do floating-point formats. + * Returns the number of chars put into buf. + */ +int +fmtmsg __V((char *buf, int buflen, char *fmt, ...)) +{ + va_list args; + int n; + +#if __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 = vfmtmsg(buf, buflen, fmt, args); + va_end(args); + return n; +} + +/* + * vfmtmsg - like fmtmsg, takes a va_list instead of a list of args. + */ +#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) + +int +vfmtmsg(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; + 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 'I': + str = ip_ntoa(va_arg(args, u_int32_t)); + break; + case 'r': + f = va_arg(args, char *); +//#ifndef __powerpc__ +// n = vfmtmsg(buf, buflen + 1, f, va_arg(args, va_list)); +//#else + /* On the powerpc, a va_list is an array of 1 structure */ + n = vfmtmsg(buf, buflen + 1, f, va_arg(args, void *)); +//#endif + buf += n; + buflen -= n; + continue; + case 't': + time(&t); + str = ctime(&t); + str += 4; /* chop off the day name */ + str[15] = 0; /* chop off year and newline */ + break; + case 'v': /* "visible" string */ + case 'q': /* quoted string */ + quoted = c == 'q'; + p = va_arg(args, unsigned char *); + if (fillch == '0' && prec > 0) { + n = prec; + } else { + n = strlen((char *)p); + if (prec > 0 && prec < n) + n = prec; + } + while (n > 0 && buflen > 0) { + c = *p++; + --n; + if (!quoted && c >= 0x80) { + OUTCHAR('M'); + OUTCHAR('-'); + c -= 0x80; + } + if (quoted && (c == '"' || c == '\\')) + OUTCHAR('\\'); + if (c < 0x20 || (0x7f <= c && c < 0xa0)) { + if (quoted) { + OUTCHAR('\\'); + switch (c) { + case '\t': OUTCHAR('t'); break; + case '\n': OUTCHAR('n'); break; + case '\b': OUTCHAR('b'); break; + case '\f': OUTCHAR('f'); break; + default: + OUTCHAR('x'); + OUTCHAR(hexchars[c >> 4]); + OUTCHAR(hexchars[c & 0xf]); + } + } else { + if (c == '\t') + OUTCHAR(c); + else { + OUTCHAR('^'); + OUTCHAR(c ^ 0x40); + } + } + } else + OUTCHAR(c); + } + continue; + default: + *buf++ = '%'; + if (c != '%') + --fmt; /* so %z outputs %z etc. */ + --buflen; + continue; + } + if (base != 0) { + str = num + sizeof(num); + *--str = 0; + while (str > num + neg) { + *--str = hexchars[val % base]; + val = val / base; + if (--prec <= 0 && val == 0) + break; + } + switch (neg) { + case 1: + *--str = '-'; + break; + case 2: + *--str = 'x'; + *--str = '0'; + break; + } + len = num + sizeof(num) - 1 - str; + } else { + len = strlen(str); + if (prec > 0 && len > prec) + len = prec; + } + if (width > 0) { + if (width > buflen) + width = buflen; + if ((n = width - len) > 0) { + buflen -= n; + for (; n > 0; --n) + *buf++ = fillch; + } + } + if (len > buflen) + len = buflen; + memcpy(buf, str, len); + buf += len; + buflen -= len; + } + *buf = 0; + return buf - buf0; +} + diff --git a/packages/net/ppp/current/src/slcompress.c b/packages/net/ppp/current/src/slcompress.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/slcompress.c @@ -0,0 +1,664 @@ +//========================================================================== +// +// src/slcompress.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/*- + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)slcompress.c 8.2 (Berkeley) 4/16/94 + * $FreeBSD: src/sys/net/slcompress.c,v 1.16 1999/12/29 04:38:37 peter Exp $ + */ + +/* + * Routines to compress and uncompess tcp packets (for transmission + * over low speed serial lines. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + * + */ + +#define _KERNEL + + +#include +#include +//#include + +#include +#include +#include +#include + +#include + +typedef CYG_ADDRWORD intptr_t; + +#ifndef SL_NO_STATS +#define INCR(counter) ++comp->counter; +#else +#define INCR(counter) +#endif + +#define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n)) +#define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n)) +#ifndef _KERNEL +#define ovbcopy bcopy +#endif + +void +sl_compress_init(comp, max_state) + struct slcompress *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)); + } else { + /* Don't reset statistics */ + bzero((char *)comp->tstate, sizeof(comp->tstate)); + bzero((char *)comp->rstate, sizeof(comp->rstate)); + } + 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 = SLF_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_int16_t)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ +} +#define ENCODEZ(n) { \ + if ((u_int16_t)(n) >= 256 || (u_int16_t)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \ + cp += 3; \ + } else { \ + (f) = htonl(ntohl(f) + (u_int32_t)*cp++); \ + } \ +} + +#define DECODES(f) { \ + if (*cp == 0) {\ + (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \ + cp += 3; \ + } else { \ + (f) = htons(ntohs(f) + (u_int32_t)*cp++); \ + } \ +} + +#define DECODEU(f) { \ + if (*cp == 0) {\ + (f) = htons((cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = htons((u_int32_t)*cp++); \ + } \ +} + +/* + * Attempt to compress an outgoing TCP packet and return the type of + * the result. The caller must have already verified that the protocol + * is TCP. The first mbuf must contain the complete IP and TCP headers, + * and "ip" must be == mtod(m, struct ip *). "comp" supplies the + * compression state, and "compress_cid" tells us whether it is OK + * to leave out the CID field when feasible. + * + * The caller is responsible for adjusting m->m_pkthdr.len upon return, + * if m is an M_PKTHDR mbuf. + */ +u_int +sl_compress_tcp(m, ip, comp, compress_cid) + struct mbuf *m; + register struct ip *ip; + struct slcompress *comp; + int compress_cid; +{ + register struct cstate *cs = comp->last_cs->cs_next; + register u_int hlen = ip->ip_hl; + 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)) || m->m_len < 40) + return (TYPE_IP); + + th = (struct tcphdr *)&((int32_t *)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(sls_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 || + *(int32_t *)th != ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl]) { + /* + * 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(sls_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 + && *(int32_t *)th == + ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl]) + 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(sls_misses) + comp->last_cs = lcs; + hlen += th->th_off; + hlen <<= 2; + if (hlen > m->m_len) + 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 *)&((int32_t *)&cs->cs_ip)[hlen]; + deltaS = hlen; + hlen += th->th_off; + hlen <<= 2; + if (hlen > m->m_len) + return TYPE_IP; + + if (((u_int16_t *)ip)[0] != ((u_int16_t *)&cs->cs_ip)[0] || + ((u_int16_t *)ip)[3] != ((u_int16_t *)&cs->cs_ip)[3] || + ((u_int16_t *)ip)[4] != ((u_int16_t *)&cs->cs_ip)[4] || + th->th_off != oth->th_off || + (deltaS > 5 && + BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) || + (th->th_off > 5 && + BCMP(th + 1, oth + 1, (th->th_off - 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; + + deltaS = (u_int16_t)(ntohs(th->th_win) - ntohs(oth->th_win)); + if (deltaS) { + ENCODE(deltaS); + changes |= NEW_W; + } + + deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack); + if (deltaA) { + if (deltaA > 0xffff) + goto uncompressed; + ENCODE(deltaA); + changes |= NEW_A; + } + + deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq); + if (deltaS) { + 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; + cp += hlen; + *cp++ = changes | NEW_C; + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + cp += hlen; + *cp++ = changes; + } + m->m_len -= hlen; + m->m_data += hlen; + *cp++ = deltaA >> 8; + *cp++ = deltaA; + BCOPY(new_seq, cp, deltaS); + INCR(sls_compressed) + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet ('uncompressed' + * means 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); +} + + +int +sl_uncompress_tcp(bufp, len, type, comp) + u_char **bufp; + int len; + u_int type; + struct slcompress *comp; +{ + u_char *hdr, *cp; + int hlen, vjlen; + + cp = bufp? *bufp: NULL; + vjlen = sl_uncompress_tcp_core(cp, len, len, type, comp, &hdr, &hlen); + if (vjlen < 0) + return (0); /* error */ + if (vjlen == 0) + return (len); /* was uncompressed already */ + + cp += vjlen; + len -= vjlen; + + /* + * At this point, cp points to the first byte of data in the + * packet. If we're not aligned on a 4-byte boundary, copy the + * data down so the ip & tcp headers will be aligned. Then back up + * cp by the tcp/ip header length to make room for the reconstructed + * header (we assume the packet we were handed has enough space to + * prepend 128 bytes of header). + */ + if ((intptr_t)cp & 3) { + if (len > 0) + (void) ovbcopy(cp, (caddr_t)((intptr_t)cp &~ 3), len); + cp = (u_char *)((intptr_t)cp &~ 3); + } + cp -= hlen; + len += hlen; + BCOPY(hdr, cp, hlen); + + *bufp = cp; + return (len); +} + +/* + * Uncompress a packet of total length total_len. The first buflen + * bytes are at buf; this must include the entire (compressed or + * uncompressed) 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 +sl_uncompress_tcp_core(buf, buflen, total_len, type, comp, hdrp, hlenp) + u_char *buf; + int buflen, total_len; + u_int type; + struct slcompress *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 struct ip *ip; + register u_int16_t *bp; + register u_int vjlen; + + switch (type) { + + case TYPE_UNCOMPRESSED_TCP: + ip = (struct ip *) buf; + if (ip->ip_p >= MAX_STATES) + goto bad; + cs = &comp->rstate[comp->last_recv = ip->ip_p]; + comp->flags &=~ SLF_TOSS; + ip->ip_p = IPPROTO_TCP; + /* + * Calculate the size of the TCP/IP header and make sure that + * we don't overflow the space we have available for it. + */ + hlen = ip->ip_hl << 2; + if (hlen + sizeof(struct tcphdr) > buflen) + goto bad; + hlen += ((struct tcphdr *)&((char *)ip)[hlen])->th_off << 2; + if (hlen > MAX_HDR || hlen > buflen) + goto bad; + BCOPY(ip, &cs->cs_ip, hlen); + cs->cs_hlen = hlen; + INCR(sls_uncompressedin) + *hdrp = (u_char *) &cs->cs_ip; + *hlenp = hlen; + return (0); + + default: + goto bad; + + case TYPE_COMPRESSED_TCP: + break; + } + /* We've got a compressed packet. */ + INCR(sls_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 &=~ SLF_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 & SLF_TOSS) { + INCR(sls_tossed) + return (-1); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = cs->cs_ip.ip_hl << 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_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; + th->th_ack = htonl(ntohl(th->th_ack) + i); + th->th_seq = htonl(ntohl(th->th_seq) + i); + } + break; + + case SPECIAL_D: + th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) + - cs->cs_hlen); + 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 = htons(ntohs(cs->cs_ip.ip_id) + 1); + + /* + * 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_int16_t *) &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 |= SLF_TOSS; + INCR(sls_errorin) + return (-1); +} diff --git a/packages/net/ppp/current/src/sys-ecos.c b/packages/net/ppp/current/src/sys-ecos.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/sys-ecos.c @@ -0,0 +1,1918 @@ +//========================================================================== +// +// src/sys-ecos.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003, 2004 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * sys-bsd.c - System-dependent procedures for setting up + * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.) + * + * Copyright (c) 1989 Carnegie Mellon University. + * Copyright (c) 1995 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University and The Australian National University. + * The names of the Universities may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/sys-bsd.c,v 1.17 1999/08/28 01:19:08 peter Exp $"; +#endif +/* $NetBSD: sys-bsd.c,v 1.1.1.3 1997/09/26 18:53:04 christos Exp $ */ + +/* + * TODO: + */ + +//========================================================================== + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#define _KERNEL 1 +#include +#undef _KERNEL +#include +#include +#include +#include +#include +#ifdef NetBSD1_2 +#include +#endif +#ifdef PPP_FILTER +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef IPX_CHANGE +#include +#endif + +#if RTM_VERSION >= 3 +#include +#if defined(NetBSD) && (NetBSD >= 199703) +#include +#else /* NetBSD 1.2D or later */ +#include +#endif +#endif + +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/fsm.h" +#include "cyg/ppp/ipcp.h" + +#include "cyg/ppp/ppp_io.h" + +#include + +//========================================================================== + +static int rtm_seq; + +static cyg_io_handle_t ppp_handle; /* IO subsystem handle to PPP stream */ +struct tty ppp_tty; /* dummy TTY structure */ + +static cyg_handle_t ppp_rtc; +static cyg_resolution_t ppp_rtc_resolution; + +static int loop_slave = -1; +static int loop_master; + +static unsigned char inbuf[PPP_MTU + PPP_HDRLEN + 100]; /* buffer for chars read from input */ + +static int sockfd = -1; /* socket for doing interface ioctls */ + +static int if_is_up; /* the interface is currently up */ +static u_int32_t ifaddrs[2]; /* local and remote addresses we set */ +static u_int32_t default_route_gateway; /* gateway addr for default route */ +static u_int32_t proxy_arp_addr; /* remote addr for proxy arp */ + +/* Prototypes for procedures local to this file. */ +static int dodefaultroute __P((u_int32_t, int)); +static int get_ether_addr __P((u_int32_t, struct sockaddr_dl *)); + +static void wait_input_alarm(cyg_handle_t alarm, cyg_addrword_t data); +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS +void cyg_ppp_serial_callback( cyg_serial_line_status_t *s, + CYG_ADDRWORD priv ); +#endif + +u_int32_t netmask; /* IP netmask to set on interface */ + +//========================================================================== +/* + * sys_init - System-dependent initialization. + */ +void +sys_init() +{ + if( sockfd == -1 ) + { + /* Get an internet socket for doing socket ioctl's on. */ + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "Couldn't create IP socket: %d",errno); + die(1); + } + } + + ppp_tty.pppd_wakeup = 0; + ppp_tty.pppd_thread_running = true; + + ppp_rtc = cyg_real_time_clock(); + ppp_rtc_resolution = cyg_clock_get_resolution( ppp_rtc ); + + cyg_alarm_create( ppp_rtc, + wait_input_alarm, + (cyg_addrword_t)&ppp_tty, + &ppp_tty.alarm, + &ppp_tty.alarm_obj); +} + +//========================================================================== +/* + * 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() +{ + struct ifreq ifr; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + if (if_is_up) { + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) >= 0 + && ((ifr.ifr_flags & IFF_UP) != 0)) { + ifr.ifr_flags &= ~IFF_UP; + ioctl(sockfd, SIOCSIFFLAGS, &ifr); + } + } + if (ifaddrs[0] != 0) + cifaddr(0, ifaddrs[0], ifaddrs[1]); + if (default_route_gateway) + cifdefaultroute(0, 0, default_route_gateway); + if (proxy_arp_addr) + cifproxyarp(0, proxy_arp_addr); + + if( sockfd != -1 ) + { + close(sockfd); + sockfd = -1; + } +} + +//========================================================================== + +#ifdef __ECOS + +void +sys_exit(void) +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); + phase = PHASE_DEAD; + while( ppp_tty.tx_thread_running ) + { + db_printf("kick tx thread\n"); + cyg_semaphore_post( &ppp_tty.tx_sem ); + cyg_thread_delay(100); + } + ppp_tty.pppd_thread_running = false; + cyg_thread_exit(); +} +#endif + +//========================================================================== +/* + * sys_close - Clean up in a child process before execing. + */ +void +sys_close() +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); + if (loop_slave >= 0) { + close(loop_slave); + close(loop_master); + } +} + +//========================================================================== +/* + * sys_check_options - check the options that the user specified + */ +void +sys_check_options() +{ +//db_printf("%s called\n", __PRETTY_FUNCTION__); +} + +//========================================================================== +/* + * ppp_available - check whether the system has any ppp interfaces + * (in fact we check whether we can do an ioctl on ppp0). + */ +int +ppp_available() +{ + int s, ok; + struct ifreq ifr; + extern char *no_ppp_msg; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return 1; /* can't tell */ + + strncpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name)); + ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0; + close(s); + + no_ppp_msg = "\ +This system lacks kernel support for PPP. To include PPP support\n\ +in the kernel, please follow the steps detailed in the README.bsd\n\ +file in the ppp-2.2 distribution.\n"; + return ok; +} + +//========================================================================== +/* + * establish_ppp - Turn the serial port into a ppp interface. + */ +void +establish_ppp(cyg_io_handle_t handle) +{ + int s; + int x; + int err; + +//db_printf("%s called\n", __PRETTY_FUNCTION__); + + ppp_handle = ppp_tty.t_handle = handle; + ppp_tty.t_sc = NULL; + + s = splsoftnet(); + + err = cyg_ppp_pppopen( &ppp_tty ); + + if( err != 0 ) + syslog( LOG_ERR, "Couldn't establish PPP interface: %d", err ); + + /* + * Enable debug in the driver if requested. + */ + if (kdebugflag) { + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCGFLAGS, (caddr_t) &x, 0)) < 0) { + syslog(LOG_WARNING, "ioctl (PPPIOCGFLAGS): %d",err); + } else { + x |= (kdebugflag & 0xFF) * SC_DEBUG; + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSFLAGS, (caddr_t) &x, 0)) < 0) + syslog(LOG_WARNING, "ioctl(PPPIOCSFLAGS): %d",err); + } + } + + splx(s); +} + +//========================================================================== +/* + * restore_loop - reattach the ppp unit to the loopback. + */ +void +restore_loop() +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); +} + + +//========================================================================== +/* + * disestablish_ppp - Restore the serial port to normal operation. + * This shouldn't call die() because it's called from die(). + */ +void +disestablish_ppp(cyg_io_handle_t handle) +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); +} + +//========================================================================== +/* + * Check whether the link seems not to be 8-bit clean. + */ +void +clean_check() +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); +} + +//========================================================================== +/* + * 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. + * + * For *BSD, we assume that speed_t values numerically equal bits/second. + */ +void +set_up_tty(cyg_io_handle_t handle, int local) +{ + cyg_serial_info_t cfg; + int err; + int len = sizeof(cfg); + + err = cyg_io_get_config( handle, + CYG_IO_GET_CONFIG_SERIAL_INFO, + &cfg, + &len); + + if( err != 0 ) { + syslog(LOG_ERR, "cyg_io_get_config: %d",err); + die(1); + } + + switch ( flowctl ) + { + case CYG_PPP_FLOWCTL_DEFAULT: + break; + + case CYG_PPP_FLOWCTL_NONE: + cfg.flags &= ~(CYGNUM_SERIAL_FLOW_RTSCTS_RX|CYGNUM_SERIAL_FLOW_RTSCTS_TX| + CYGNUM_SERIAL_FLOW_XONXOFF_RX|CYGNUM_SERIAL_FLOW_XONXOFF_TX); + break; + + case CYG_PPP_FLOWCTL_HARDWARE: + cfg.flags &= ~(CYGNUM_SERIAL_FLOW_XONXOFF_RX|CYGNUM_SERIAL_FLOW_XONXOFF_TX); + cfg.flags |= CYGNUM_SERIAL_FLOW_RTSCTS_RX|CYGNUM_SERIAL_FLOW_RTSCTS_TX; + break; + + case CYG_PPP_FLOWCTL_SOFTWARE: + cfg.flags &= ~(CYGNUM_SERIAL_FLOW_RTSCTS_RX|CYGNUM_SERIAL_FLOW_RTSCTS_TX); + cfg.flags |= CYGNUM_SERIAL_FLOW_XONXOFF_RX|CYGNUM_SERIAL_FLOW_XONXOFF_TX; + break; + } + + if( inspeed != 0 ) + cfg.baud = inspeed; + + err = cyg_io_set_config( handle, + CYG_IO_SET_CONFIG_SERIAL_INFO, + &cfg, + &len); + + if( err != 0 ) { + syslog(LOG_ERR, "cyg_io_set_config: %d",err); + die(1); + } +} + +//========================================================================== +/* + * restore_tty - restore the terminal to the saved settings. + */ +void +restore_tty(cyg_io_handle_t handle) +{ +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS + if( modem ) + { + // Restore callback handler if it was set. + + Cyg_ErrNo err; + cyg_uint32 len = sizeof(ppp_tty.serial_callbacks); + + db_printf("%s called\n", __PRETTY_FUNCTION__); + + err = cyg_io_set_config( handle, + CYG_IO_SET_CONFIG_SERIAL_STATUS_CALLBACK, + &ppp_tty.serial_callbacks, + &len); + + if( err != 0 ) { + syslog(LOG_ERR, "cyg_io_set_config(restore serial callbacks): %d",err); + die(1); + } + } +#endif +} + +//========================================================================== +/* + * 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; +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); +} + +//========================================================================== +/* + * output - Output PPP packet. + */ +void +output(unit, p, len) + int unit; + u_char *p; + int len; +{ + Cyg_ErrNo err; + struct uio uio; + struct iovec iov; + int s; + + if (debug) + log_packet(p, len, "sent ", LOG_DEBUG); + + + iov.iov_base = p; + iov.iov_len = len; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = len; + uio.uio_segflg = UIO_USERSPACE; + uio.uio_rw = UIO_WRITE; + + s = splsoftnet(); + + err = cyg_ppp_pppwrite( &ppp_tty, &uio, 0 ); + + splx(s); + + if( err != 0 ) + syslog(LOG_ERR, "write: %d",err); + +} + +//========================================================================== + +#ifdef __ECOS + +static void wait_input_alarm(cyg_handle_t alarm, cyg_addrword_t data) +{ + cyg_thread_release( ppp_tty.pppd_thread ); + ppp_tty.pppd_wakeup = 1; +} + +#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS +void cyg_ppp_serial_callback( cyg_serial_line_status_t *s, + CYG_ADDRWORD priv ) +{ + externC int kill_link; + +// db_printf("serial callback %d %x\n",s->which, s->value ); + + if( s->which == CYGNUM_SERIAL_STATUS_CARRIERDETECT ) + { + if( s->value == 0 ) + { + // CD lost + kill_link = 1; + cyg_thread_release( ppp_tty.pppd_thread ); + ppp_tty.pppd_wakeup = 1; + } + else + { + // CD up + ppp_tty.carrier_detected = 1; + } + } +} +#endif + +static void cyg_ppp_tx_thread(CYG_ADDRWORD arg) +{ + ppp_tty.tx_thread_running = true; + + // Wait for the PPPD thread to get going and start the PPP + // initialization phase. + while(phase == PHASE_DEAD ) + cyg_thread_delay(100); + + // Now loop until the link goes back down. + while( phase != PHASE_DEAD ) + { + cyg_semaphore_wait( &ppp_tty.tx_sem ); + + if( phase == PHASE_DEAD ) + break; + + // Call into PPP driver to get transmissions going. This is + // only called if some other thread has failed to transmit all + // of a packet due to a full buffer, or flow control. + + cyg_ppp_pppstart( &ppp_tty ); + } + +db_printf("%s exiting\n", __PRETTY_FUNCTION__); + ppp_tty.tx_thread_running = false; + cyg_thread_exit(); +} + +#endif + + +//========================================================================== +/* + * wait_input - wait until there is data available on ttyfd, + * for the length of time specified by *timo (indefinite + * if timo is NULL). + */ +void +wait_input(timo) + struct timeval *timo; +{ + if( timo != NULL ) + { + cyg_tick_count_t trigger = timo->tv_sec*ppp_rtc_resolution.divisor; + + // If the timeval has a microseconds value, just add another + // second on to the trigger time. These are after all just + // timeouts, not accurate timings, so a bit of imprecision + // will not hurt. + if( timo->tv_usec != 0 ) + trigger += ppp_rtc_resolution.divisor; + + trigger += cyg_current_time(); + + // We set the alarm to retrigger after a second. This is in + // case it catches cyg_io_read() at an uninterruptible + // point. The alarm is disabled as soon as the read returns, + // so the retrigger will usually not happen. + cyg_alarm_initialize( ppp_tty.alarm, + trigger, + ppp_rtc_resolution.divisor ); + } + + for(;;) + { + int s; + int len = 1; + Cyg_ErrNo err; + cyg_serial_buf_info_t info; + cyg_uint32 ilen = sizeof(info); + +#if 1 + // Work out how many bytes are waiting in the serial device + // buffer and read them all at once. If there are none, we + // just wait for a single character to arrive. + + if( cyg_io_get_config( ppp_tty.t_handle, CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO, + &info, &ilen ) == 0 && info.rx_count > 1 ) + { + len = info.rx_count-1; + if( len > sizeof(inbuf) ) + len = sizeof(inbuf); + } +#endif + + if( timo != NULL ) + cyg_alarm_enable( ppp_tty.alarm ); + + err = cyg_io_read( ppp_handle, &inbuf, &len ); + + if( timo != NULL ) + cyg_alarm_disable( ppp_tty.alarm ); + +// db_printf("read: err %d len %d byte %02x\n",err,len,inbuf[0]); + + if( err == 0 ) + { + int i; + + // Pass all input data to PPP driver for analysis. If this + // turns out to be for the PPPD, it will call + // pppasyncctlp() which in turn will set + // ppp_tty.pppd_wakeup. We detect that on return from + // pppinput() and return to the caller to do pppd + // processing. + + s = splsoftnet(); + + for( i = 0; i < len; i++ ) + { + err = cyg_ppp_pppinput( inbuf[i], &ppp_tty ); + + if( err != 0 ) + syslog( LOG_ERR, "pppinput error: %d", err); + + } + + splx(s); + + } + else if( err != -EINTR ) + syslog( LOG_ERR, "Read error: %d",err); + + if( ppp_tty.pppd_wakeup ) + { + ppp_tty.pppd_wakeup = 0; + break; + } + } +} + +//========================================================================== +/* + * wait_time - wait for a given length of time or until a + * signal is received. + */ +void +wait_time(timo) + struct timeval *timo; +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); +} + + +//========================================================================== +/* + * read_packet - get a PPP packet from the serial device. + */ +int +read_packet(buf) + u_char *buf; +{ + int err; + struct uio uio; + struct iovec iov; + int len = PPP_MTU + PPP_HDRLEN; + int s; + +//db_printf("%s called\n", __PRETTY_FUNCTION__); + + iov.iov_base = buf; + iov.iov_len = len; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = len; + uio.uio_segflg = UIO_USERSPACE; + uio.uio_rw = UIO_READ; + + s = splsoftnet(); + + err = cyg_ppp_pppread( &ppp_tty, &uio, 0 ); + + splx(s); + + if( err == EWOULDBLOCK ) + return -1; + + if( err != 0 ) + { + syslog(LOG_ERR, "pppread: %d",err); + die(1); + } + + len -= uio.uio_resid; + + return len; +} + +//========================================================================== +/* + * ppp_send_config - configure the transmit characteristics of + * the ppp interface. + */ +void +ppp_send_config(unit, mtu, asyncmap, pcomp, accomp) + int unit, mtu; + u_int32_t asyncmap; + int pcomp, accomp; +{ + u_int x; + struct ifreq ifr; + int err; + int s; + +// db_printf("%s: unit %d mtu %d asyncmap %08x pcomp %08x accomp %08x\n", __PRETTY_FUNCTION__, +// unit,mtu,asyncmap,pcomp,accomp); + + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + ifr.ifr_mtu = mtu; + if (ioctl(sockfd, SIOCSIFMTU, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFMTU): %d",errno); + quit(); + } + + + s = splsoftnet(); + + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSASYNCMAP, (caddr_t) &asyncmap, 0)) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSASYNCMAP): %d",err); + } + + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCGFLAGS, (caddr_t) &x, 0)) < 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %d",err); + } + x = pcomp? x | SC_COMP_PROT: x &~ SC_COMP_PROT; + x = accomp? x | SC_COMP_AC: x &~ SC_COMP_AC; + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSFLAGS, (caddr_t) &x, 0)) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %d",err); + } + + splx(s); +} + + +//========================================================================== +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(unit, accm) + int unit; + ext_accm accm; +{ + int error; + int s; + +//db_printf("%s called\n", __PRETTY_FUNCTION__); + + s = splsoftnet(); + + error = cyg_ppp_ppptioctl( &ppp_tty, PPPIOCSXASYNCMAP, (caddr_t)accm, 0 ); + + splx(s); + + if( error != 0 ) + syslog(LOG_WARNING, "ioctl(set extended ACCM): %d",error); +} + + +//========================================================================== +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void +ppp_recv_config(unit, mru, asyncmap, pcomp, accomp) + int unit, mru; + u_int32_t asyncmap; + int pcomp, accomp; +{ + int x; + int err; + int s; + + s = splsoftnet(); + + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSMRU, (caddr_t) &mru, 0)) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSMRU): %d",err); + } + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap, 0)) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSRASYNCMAP): %d",err); + } + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCGFLAGS, (caddr_t) &x, 0)) < 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %d",err); + } + x = !accomp? x | SC_REJ_COMP_AC: x &~ SC_REJ_COMP_AC; + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSFLAGS, (caddr_t) &x, 0)) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %d",err); + } + + splx(s); +} + +//========================================================================== +/* + * ccp_test - ask kernel whether a given compression method + * is acceptable for use. Returns 1 if the method and parameters + * are OK, 0 if the method is known but the parameters are not OK + * (e.g. code size should be reduced), or -1 if the method is unknown. + */ +int +ccp_test(unit, opt_ptr, opt_len, for_transmit) + int unit, opt_len, for_transmit; + u_char *opt_ptr; +{ + struct ppp_option_data data; + int s; + + data.ptr = opt_ptr; + data.length = opt_len; + data.transmit = for_transmit; +//db_printf("%s called\n", __PRETTY_FUNCTION__); + + s = splsoftnet(); + + errno = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSCOMPRESS, (caddr_t) &data, 0 ); + + splx(s); + + if (errno == 0) + return 1; + else return (errno == ENOBUFS)? 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 x; + + int err; +//db_printf("%s called\n", __PRETTY_FUNCTION__); + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCGFLAGS, (caddr_t) &x, 0)) != 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %d",err); + return; + } + x = isopen? x | SC_CCP_OPEN: x &~ SC_CCP_OPEN; + x = isup? x | SC_CCP_UP: x &~ SC_CCP_UP; + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSFLAGS, (caddr_t) &x,0)) != 0) + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %d",err); +} + +//========================================================================== +/* + * 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 x; + int err; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCGFLAGS, (caddr_t) &x, 0)) != 0) { + syslog(LOG_ERR, "ioctl(PPPIOCGFLAGS): %d",err); + return 0; + } + return x & SC_DC_FERROR; +} + +//========================================================================== +/* + * get_idle_time - return how long the link has been idle. + */ +int +get_idle_time(u, ip) + int u; + struct ppp_idle *ip; +{ + return cyg_ppp_ppptioctl(&ppp_tty, PPPIOCGIDLE, (caddr_t)ip, 0) == 0; +} + + +//========================================================================== + +#ifdef PPP_FILTER +/* + * set_filters - transfer the pass and active filters to the kernel. + */ +int +set_filters(pass, active) + struct bpf_program *pass, *active; +{ + int ret = 1; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + if (pass->bf_len > 0) { + if (ioctl(ppp_fd, PPPIOCSPASS, pass) < 0) { + syslog(LOG_ERR, "Couldn't set pass-filter in kernel: %m"); + ret = 0; + } + } + if (active->bf_len > 0) { + if (ioctl(ppp_fd, PPPIOCSACTIVE, active) < 0) { + syslog(LOG_ERR, "Couldn't set active-filter in kernel: %m"); + ret = 0; + } + } + return ret; +} +#endif + +//========================================================================== +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp(u, vjcomp, cidcomp, maxcid) + int u, vjcomp, cidcomp, maxcid; +{ + u_int x; + int err; + + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCGFLAGS, (caddr_t) &x, 0)) != 0) { + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %d",err); + return 0; + } + x = vjcomp ? x | SC_COMP_TCP: x &~ SC_COMP_TCP; + x = cidcomp? x & ~SC_NO_TCP_CCID: x | SC_NO_TCP_CCID; + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSFLAGS, (caddr_t) &x, 0)) != 0) { + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %d",err); + return 0; + } + if (vjcomp && ((err=cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSMAXCID, (caddr_t) &maxcid, 0)) != 0)) { + syslog(LOG_ERR, "ioctl(PPPIOCSMAXCID): %d",err); + return 0; + } + return 1; +} + +//========================================================================== +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int +sifup(u) + int u; +{ + struct ifreq ifr; + +//db_printf("%s called\n", __PRETTY_FUNCTION__); + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m"); + return 0; + } + ifr.ifr_flags |= IFF_UP; + if (ioctl(sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); + return 0; + } + if_is_up = 1; + 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; +{ + struct npioctl npi; + +//db_printf("%s called\n", __PRETTY_FUNCTION__); + npi.protocol = proto; + npi.mode = mode; + { + int err; + + if ((err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSNPMODE, (caddr_t)&npi, 0)) < 0) { + syslog(LOG_ERR, "ioctl(set NP %d mode to %d): %d", proto, mode,err); + return 0; + } + } + + return 1; +} + +//========================================================================== +/* + * sifdown - Config the interface down and disable IP. + */ +int +sifdown(u) + int u; +{ + struct ifreq ifr; + int rv; + struct npioctl npi; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + rv = 1; + npi.protocol = PPP_IP; + npi.mode = NPMODE_ERROR; + { + int err = cyg_ppp_ppptioctl(&ppp_tty, PPPIOCSNPMODE, (caddr_t) &npi, 0); + if( err < 0 ) + syslog(LOG_WARNING, "ioctl(PPPIOCSNPMODE): %d",err); + } + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m"); + rv = 0; + } else { + ifr.ifr_flags &= ~IFF_UP; + if (ioctl(sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); + rv = 0; + } else + if_is_up = 0; + } + return rv; + return 0; +} + +//========================================================================== +/* + * SET_SA_FAMILY - set the sa_family field of a struct sockaddr, + * if it exists. + */ +#define SET_SA_FAMILY(addr, family) \ + BZERO((char *) &(addr), sizeof(addr)); \ + addr.sa_family = (family); \ + addr.sa_len = sizeof(addr); + +//========================================================================== +/* + * sifaddr - Config the interface IP addresses and netmask. + */ +int +sifaddr(u, o, h, m) + int u; + u_int32_t o, h, m; +{ + struct ifaliasreq ifra; + struct ifreq ifr; + +//db_printf("%s called\n", __PRETTY_FUNCTION__); + strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name)); + SET_SA_FAMILY(ifra.ifra_addr, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o; + SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h; + if (m != 0) { + SET_SA_FAMILY(ifra.ifra_mask, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_mask)->sin_addr.s_addr = m; + } else + BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask)); + BZERO(&ifr, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(sockfd, SIOCDIFADDR, (caddr_t) &ifr) < 0) { + if (errno != EADDRNOTAVAIL) + syslog(LOG_WARNING, "Couldn't remove interface address: %d",errno); + } + if (ioctl(sockfd, SIOCAIFADDR, (caddr_t) &ifra) < 0) { + if (errno != EEXIST) { + syslog(LOG_ERR, "Couldn't set interface address: %d",errno); + return 0; + } + syslog(LOG_WARNING, + "Couldn't set interface address: Address %s already exists", + ip_ntoa(o)); + } + ifaddrs[0] = o; + ifaddrs[1] = h; + return 1; +} + +//========================================================================== +/* + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + */ +int +cifaddr(u, o, h) + int u; + u_int32_t o, h; +{ + struct ifaliasreq ifra; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + ifaddrs[0] = 0; + strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name)); + SET_SA_FAMILY(ifra.ifra_addr, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o; + SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET); + ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h; + BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask)); + if (ioctl(sockfd, SIOCDIFADDR, (caddr_t) &ifra) < 0) { + if (errno != EADDRNOTAVAIL) + syslog(LOG_WARNING, "Couldn't delete interface address: %m"); + return 0; + } + return 1; +} + +//========================================================================== +/* + * sifdefaultroute - assign a default route through the address given. + */ +int +sifdefaultroute(u, l, g) + int u; + u_int32_t l, g; +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); + return dodefaultroute(g, 's'); +} + +//========================================================================== +/* + * cifdefaultroute - delete a default route through the address given. + */ +int +cifdefaultroute(u, l, g) + int u; + u_int32_t l, g; +{ +db_printf("%s called\n", __PRETTY_FUNCTION__); + return dodefaultroute(g, 'c'); +} + +//========================================================================== +/* + * dodefaultroute - talk to a routing socket to add/delete a default route. + */ +static int +dodefaultroute(g, cmd) + u_int32_t g; + int cmd; +{ + int routes; + struct { + struct rt_msghdr hdr; + struct sockaddr_in dst; + struct sockaddr_in gway; + struct sockaddr_in mask; + } rtmsg; + +db_printf("%s %08x %c\n", __PRETTY_FUNCTION__,g,cmd); + if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { + syslog(LOG_ERR, "Couldn't %s default route: socket: %d", + cmd=='s'? "add": "delete",errno); + return 0; + } + + memset(&rtmsg, 0, sizeof(rtmsg)); + rtmsg.hdr.rtm_type = cmd == 's'? RTM_ADD: RTM_DELETE; + rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; + rtmsg.hdr.rtm_version = RTM_VERSION; + rtmsg.hdr.rtm_seq = ++rtm_seq; + rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + rtmsg.dst.sin_len = sizeof(rtmsg.dst); + rtmsg.dst.sin_family = AF_INET; + rtmsg.gway.sin_len = sizeof(rtmsg.gway); + rtmsg.gway.sin_family = AF_INET; + rtmsg.gway.sin_addr.s_addr = g; + rtmsg.mask.sin_len = sizeof(rtmsg.dst); + rtmsg.mask.sin_family = AF_INET; + + rtmsg.hdr.rtm_msglen = sizeof(rtmsg); + if (write(routes, &rtmsg, sizeof(rtmsg)) < 0) { + syslog(LOG_ERR, "Couldn't %s default route: %d", + cmd=='s'? "add": "delete",errno); + close(routes); + return 0; + } + + close(routes); + default_route_gateway = (cmd == 's')? g: 0; + return 1; +} + +//========================================================================== + +#if RTM_VERSION >= 3 + +/* + * sifproxyarp - Make a proxy ARP entry for the peer. + */ +static struct { + struct rt_msghdr hdr; + struct sockaddr_inarp dst; + struct sockaddr_dl hwa; + char extra[128]; +} arpmsg; + +static int arpmsg_valid; + +int +sifproxyarp(unit, hisaddr) + int unit; + u_int32_t hisaddr; +{ + int routes; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + /* + * Get the hardware address of an interface on the same subnet + * as our local address. + */ + memset(&arpmsg, 0, sizeof(arpmsg)); + if (!get_ether_addr(hisaddr, &arpmsg.hwa)) { + syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP"); + return 0; + } + + if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { + syslog(LOG_ERR, "Couldn't add proxy arp entry: socket: %m"); + return 0; + } + + arpmsg.hdr.rtm_type = RTM_ADD; + arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC; + arpmsg.hdr.rtm_version = RTM_VERSION; + arpmsg.hdr.rtm_seq = ++rtm_seq; + arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; + arpmsg.hdr.rtm_inits = RTV_EXPIRE; + arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp); + arpmsg.dst.sin_family = AF_INET; + arpmsg.dst.sin_addr.s_addr = hisaddr; + arpmsg.dst.sin_other = SIN_PROXY; + + arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg + + arpmsg.hwa.sdl_len; + if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { + syslog(LOG_ERR, "Couldn't add proxy arp entry: %m"); + close(routes); + return 0; + } + + close(routes); + arpmsg_valid = 1; + 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; +{ + int routes; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + if (!arpmsg_valid) + return 0; + arpmsg_valid = 0; + + arpmsg.hdr.rtm_type = RTM_DELETE; + arpmsg.hdr.rtm_seq = ++rtm_seq; + + if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { + syslog(LOG_ERR, "Couldn't delete proxy arp entry: socket: %m"); + return 0; + } + + if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { + syslog(LOG_ERR, "Couldn't delete proxy arp entry: %m"); + close(routes); + return 0; + } + + close(routes); + proxy_arp_addr = 0; + return 1; +} + +//========================================================================== + +#else /* RTM_VERSION */ + +/* + * sifproxyarp - Make a proxy ARP entry for the peer. + */ +int +sifproxyarp(unit, hisaddr) + int unit; + u_int32_t hisaddr; +{ + struct arpreq arpreq; + struct { + struct sockaddr_dl sdl; + char space[128]; + } dls; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + BZERO(&arpreq, sizeof(arpreq)); + + /* + * Get the hardware address of an interface on the same subnet + * as our local address. + */ + if (!get_ether_addr(hisaddr, &dls.sdl)) { + syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP"); + return 0; + } + + arpreq.arp_ha.sa_len = sizeof(struct sockaddr); + arpreq.arp_ha.sa_family = AF_UNSPEC; + BCOPY(LLADDR(&dls.sdl), arpreq.arp_ha.sa_data, dls.sdl.sdl_alen); + SET_SA_FAMILY(arpreq.arp_pa, AF_INET); + ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr; + arpreq.arp_flags = ATF_PERM | ATF_PUBL; + if (ioctl(sockfd, SIOCSARP, (caddr_t)&arpreq) < 0) { + syslog(LOG_ERR, "Couldn't add 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; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + BZERO(&arpreq, sizeof(arpreq)); + SET_SA_FAMILY(arpreq.arp_pa, AF_INET); + ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr; + if (ioctl(sockfd, SIOCDARP, (caddr_t)&arpreq) < 0) { + syslog(LOG_WARNING, "Couldn't delete proxy arp entry: %m"); + return 0; + } + proxy_arp_addr = 0; + return 1; +} +#endif /* RTM_VERSION */ + + +//========================================================================== +/* + * get_ether_addr - get the hardware address of an interface on the + * the same subnet as ipaddr. + */ +#define MAX_IFS 32 + +static int +get_ether_addr(ipaddr, hwaddr) + u_int32_t ipaddr; + struct sockaddr_dl *hwaddr; +{ + struct ifreq *ifr, *ifend, *ifp; + u_int32_t ina, mask; + struct sockaddr_dl *dla; + struct ifreq ifreq; + struct ifconf ifc; + struct ifreq ifs[MAX_IFS]; + +db_printf("%s called\n", __PRETTY_FUNCTION__); + ifc.ifc_len = sizeof(ifs); + ifc.ifc_req = ifs; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { + syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m"); + return 0; + } + + /* + * Scan through looking for an interface with an Internet + * address on the same subnet as `ipaddr'. + */ + ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < ifend; + ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)))) { + if (ifr->ifr_addr.sa_family == AF_INET) { + ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr; + strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); + /* + * Check that the interface is up, and not point-to-point + * or loopback. + */ + if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0) + continue; + if ((ifreq.ifr_flags & + (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP)) + != (IFF_UP|IFF_BROADCAST)) + continue; + /* + * Get its netmask and check that it's on the right subnet. + */ + if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0) + continue; + mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr; + if ((ipaddr & mask) != (ina & mask)) + continue; + + break; + } + } + + if (ifr >= ifend) + return 0; + syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name); + + /* + * Now scan through again looking for a link-level address + * for this interface. + */ + ifp = ifr; + for (ifr = ifc.ifc_req; ifr < ifend; ) { + if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 + && ifr->ifr_addr.sa_family == AF_LINK) { + /* + * Found the link-level address - copy it out + */ + dla = (struct sockaddr_dl *) &ifr->ifr_addr; + BCOPY(dla, hwaddr, dla->sdl_len); + return 1; + } + ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr))); + } + + return 0; +} + +//========================================================================== +/* + * Return user specified netmask, modified by any mask we might determine + * for address `addr' (in network byte order). + * Here we scan through the system's list of interfaces, looking for + * any non-point-to-point interfaces which might appear to be on the same + * network as `addr'. If we find any, we OR in their netmask to the + * user-specified netmask. + */ +u_int32_t +GetMask(addr) + u_int32_t addr; +{ + u_int32_t mask, nmask, ina; + struct ifreq *ifr, *ifend, ifreq; + struct ifconf ifc; + 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(sockfd, SIOCGIFCONF, &ifc) < 0) { + syslog(LOG_WARNING, "ioctl(SIOCGIFCONF): %m"); + return mask; + } + ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < ifend; + ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)))) { + /* + * Check the interface's internet address. + */ + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr; + if ((ntohl(ina) & nmask) != (addr & nmask)) + continue; + /* + * Check that the interface is up, and not point-to-point or loopback. + */ + strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0) + continue; + if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK)) + != IFF_UP) + continue; + /* + * Get its netmask and OR it into our mask. + */ + if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0) + continue; + mask |= ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr; + } + + return mask; +} + +//========================================================================== +/* + * Use the hostid as part of the random number seed. + */ +int +get_host_seed() +{ +//db_printf("%s called\n", __PRETTY_FUNCTION__); +#ifndef __ECOS + return gethostid(); +#endif + return 0; +} + +//===================================================================== +// PAP stubs +// +// When omitting PAP, these fill in the dangling references from auth.c +// + +#ifndef CYGPKG_PPP_PAP + +void +upap_authwithpeer(unit, user, password) + int unit; + char *user, *password; +{ + unit=unit; + user=user; + password=password; +} + +void +upap_authpeer(unit) + int unit; +{ + unit=unit; +} + +#endif + +//===================================================================== +// CHAP stubs +// +// When omitting CHAP, these fill in the dangling references from auth.c +// + +#ifndef CYGPKG_PPP_CHAP + +void +ChapAuthWithPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + unit=unit; + our_name=our_name; + digest=digest; +} + +void +ChapAuthPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + unit=unit; + our_name=our_name; + digest=digest; +} + +#endif + +//===================================================================== +// eCos API + +externC cyg_int32 cyg_ppp_options_init( cyg_ppp_options_t *options ) +{ + if( options == NULL ) + return -1; + + options->debug = 0; + options->kdebugflag = 0; + options->default_route = 1; + options->modem = 0; + options->flowctl = CYG_PPP_FLOWCTL_HARDWARE; + options->refuse_pap = 0; + options->refuse_chap = 0; + + options->baud = CYGNUM_SERIAL_BAUD_115200; + + options->idle_time_limit = 1*60; + options->maxconnect = 0; + + options->our_address = 0; + options->his_address = 0; + + options->script = NULL; + + strncpy( options->user, CYGPKG_PPP_AUTH_DEFAULT_USER, MAXNAMELEN ); + strncpy( options->passwd, CYGPKG_PPP_AUTH_DEFAULT_PASSWD, MAXSECRETLEN ); + + return 0; +} + +// ------------------------------------------------------------------------- + + +#define CYGNUM_PPP_PPPD_THREAD_STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL+0x1000) + +static char cyg_pppd_stack[CYGNUM_PPP_PPPD_THREAD_STACK_SIZE]; +static cyg_thread cyg_pppd_thread_obj; + +static char cyg_ppp_tx_thread_stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; +static cyg_thread cyg_ppp_tx_thread_obj; + + +externC void cyg_pppd_main(CYG_ADDRWORD arg); + + +externC cyg_ppp_handle_t cyg_ppp_up( const char *devnam_arg, + const cyg_ppp_options_t *options ) +{ + + if( options == NULL || phase != PHASE_DEAD ) + return 0; + + strncpy( devnam, devnam_arg, PATH_MAX ); + + ppp_tty.options = options; + + // Start the PPPD thread + cyg_thread_create(CYGNUM_PPP_PPPD_THREAD_PRIORITY, + cyg_pppd_main, + (CYG_ADDRWORD)&ppp_tty, + "PPPD", + &cyg_pppd_stack[0], + CYGNUM_PPP_PPPD_THREAD_STACK_SIZE, + &ppp_tty.pppd_thread, + &cyg_pppd_thread_obj + ); + cyg_thread_resume(ppp_tty.pppd_thread); + + // Start the TX thread + cyg_semaphore_init( &ppp_tty.tx_sem, 0 ); + + cyg_thread_create(CYGNUM_PPP_PPPD_THREAD_PRIORITY+1, + cyg_ppp_tx_thread, + (CYG_ADDRWORD)&ppp_tty, + "PPP Tx Thread", + &cyg_ppp_tx_thread_stack[0], + sizeof(cyg_ppp_tx_thread_stack), + &ppp_tty.tx_thread, + &cyg_ppp_tx_thread_obj + ); + cyg_thread_resume(ppp_tty.tx_thread); + + // Wait for the PPPD thread to get going and start the PPP + // initialization phase. + while(phase == PHASE_DEAD ) + cyg_thread_delay(100); + + return (cyg_ppp_handle_t)&ppp_tty; +} + +// ------------------------------------------------------------------------- + +externC char **script; + +externC void cyg_ppp_options_install( const cyg_ppp_options_t *options ) +{ + debug = options->debug; + kdebugflag = options->kdebugflag; + + modem = options->modem; + flowctl = options->flowctl; + refuse_pap = options->refuse_pap; + refuse_chap = options->refuse_chap; + + inspeed = options->baud; + + idle_time_limit = options->idle_time_limit; + maxconnect = options->maxconnect; + + { + ipcp_options *wo = &ipcp_wantoptions[0]; + + wo->ouraddr = options->our_address; + wo->hisaddr = options->his_address; + + wo->default_route = options->default_route; + + } + + script = options->script; + + strncpy( user, &options->user[0], MAXNAMELEN ); + strncpy( passwd, &options->passwd[0], MAXSECRETLEN ); + +} + +// ------------------------------------------------------------------------- + +externC cyg_int32 cyg_ppp_down( const cyg_ppp_handle_t handle ) +{ + if( phase != PHASE_DEAD ) + { + externC int kill_link; + kill_link = 1; + cyg_thread_release( ppp_tty.pppd_thread ); + ppp_tty.pppd_wakeup = 1; + return 0; + } + else + return -1; +} + +// ------------------------------------------------------------------------- + +externC cyg_int32 cyg_ppp_wait_up( cyg_ppp_handle_t handle ) +{ + while(!( (phase == PHASE_NETWORK && ifaddrs[0] != 0) || + phase == PHASE_DEAD ) ) + cyg_thread_delay(100); + + return phase == PHASE_NETWORK ? 0 : -1; +} + +// ------------------------------------------------------------------------- + +externC void cyg_ppp_wait_down( cyg_ppp_handle_t handle ) +{ + while( ppp_tty.tx_thread_running || ppp_tty.pppd_thread_running ) + cyg_thread_delay(100); + + cyg_thread_delete( ppp_tty.tx_thread ); + cyg_thread_delete( ppp_tty.pppd_thread ); +} + +//===================================================================== +// eCos extras + +void syslog( int level, char *fmt, ... ) +{ + va_list ap; + int ret; + +#ifdef CYGPKG_PPP_DEBUG_WARN_ONLY + if(!( level == LOG_ERR || + level == LOG_WARNING )) + return; +#endif + + va_start(ap, fmt); + diag_printf("SYSLOG %02x: ",level); + ret = diag_vprintf(fmt, ap); + diag_printf("\n"); + va_end(ap); +} + +//===================================================================== + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + cyg_tick_count_t time = cyg_current_time(); + + tv->tv_sec = time/CYGNUM_HAL_RTC_DENOMINATOR; + tv->tv_usec = (time%CYGNUM_HAL_RTC_DENOMINATOR)*10000; + +// db_printf("%s: %d %d\n", __PRETTY_FUNCTION__, tv->tv_sec, tv->tv_usec); + + return 0; +} + +//===================================================================== + +char *crypt (const char *key, const char *salt) +{ + static char res[13]; + + db_printf("%s called\n", __PRETTY_FUNCTION__); + + return res; +} + + +//===================================================================== +/* + * Substitute procedures for those systems which don't have + * drand48 et al. + */ + +double +drand48(void) +{ + return (double)rand() / (double)0x7fffffffL; /* 2**31-1 */ +} + +long +mrand48(void) +{ + return rand(); +} + +void +srand48(long seedval) +{ + srand(seedval); +} + + +//===================================================================== + +#if 0 + +#undef MD5Init +#undef MD5Update +#undef MD5Final + +#include + +#include + + +void cyg_MD5Init( MD5_CTX *ctx ); +void cyg_MD5Update (MD5_CTX *ctx, const unsigned char *buf, unsigned int size); +void cyg_MD5Final (unsigned char hash[16], MD5_CTX *ctx); + + +void cyg_ppp_MD5Init( MD5_CTX *ctx ) +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); + cyg_MD5Init( ctx ); + return; +} + +void cyg_ppp_MD5Update (MD5_CTX *ctx, const unsigned char *buf, unsigned int size) +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); + cyg_MD5Update( ctx, buf, size ); + return; +} + +void cyg_ppp_MD5Final (unsigned char hash[16], MD5_CTX *ctx) +{ + db_printf("%s called\n", __PRETTY_FUNCTION__); + cyg_MD5Final( hash, ctx ); + return; +} + +#endif + +//===================================================================== +// End of sys-ecos.c + + diff --git a/packages/net/ppp/current/src/upap.c b/packages/net/ppp/current/src/upap.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/upap.c @@ -0,0 +1,666 @@ +//========================================================================== +// +// src/upap.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/upap.c,v 1.8 1999/08/28 01:19:08 peter Exp $"; +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include "cyg/ppp/pppd.h" +#include "cyg/ppp/upap.h" + +/* + * 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, + NULL, + NULL +}; + +upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ + +static void upap_timeout __P((void *)); +static void upap_reqtimeout __P((void *)); +static void upap_rauthreq __P((upap_state *, u_char *, int, int)); +static void upap_rauthack __P((upap_state *, u_char *, int, int)); +static void upap_rauthnak __P((upap_state *, u_char *, int, int)); +static void upap_sauthreq __P((upap_state *)); +static void upap_sresp __P((upap_state *, int, int, char *, int)); + + +/* + * upap_init - Initialize a UPAP unit. + */ +static void +upap_init(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; + u->us_reqtimeout = UPAP_DEFREQTIME; +} + + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void +upap_authwithpeer(unit, user, password) + int unit; + char *user, *password; +{ + upap_state *u = &upap[unit]; + + /* Save the username and password we're given */ + u->us_user = user; + u->us_userlen = strlen(user); + u->us_passwd = password; + u->us_passwdlen = strlen(password); + u->us_transmits = 0; + + /* Lower layer up yet? */ + if (u->us_clientstate == UPAPCS_INITIAL || + u->us_clientstate == UPAPCS_PENDING) { + u->us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(u); /* Start protocol */ +} + + +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void +upap_authpeer(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + /* Lower layer up yet? */ + if (u->us_serverstate == UPAPSS_INITIAL || + u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_PENDING; + return; + } + + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); +} + + +/* + * upap_timeout - Retransmission timer for sending auth-reqs expired. + */ +static void +upap_timeout(arg) + void *arg; +{ + upap_state *u = (upap_state *) arg; + + if (u->us_clientstate != UPAPCS_AUTHREQ) + return; + + if (u->us_transmits >= u->us_maxtransmits) { + /* give up in disgust */ + syslog(LOG_ERR, "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) { + syslog(LOG_ERR, "PAP authentication failed due to protocol-reject"); + auth_withpeer_fail(unit, PPP_PAP); + } + if (u->us_serverstate == UPAPSS_LISTEN) { + syslog(LOG_ERR, "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((LOG_INFO, "pap_input: rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd illegal length.")); + return; + } + if (len > l) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd short packet.")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: + upap_rauthreq(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + break; + } +} + + +/* + * upap_rauth - Receive Authenticate. + */ +static void +upap_rauthreq(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char ruserlen, rpasswdlen; + char *ruser, *rpasswd; + int retcode; + char *msg; + int msglen; + + UPAPDEBUG((LOG_INFO, "pap_rauth: Rcvd id %d.", id)); + + if (u->us_serverstate < UPAPSS_LISTEN) + return; + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (u->us_serverstate == UPAPSS_OPEN) { + upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (u->us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char); + if (len < 0) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.")); + return; + } + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, + rpasswdlen, &msg, &msglen); + BZERO(rpasswd, rpasswdlen); + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + auth_peer_fail(u->us_unit, PPP_PAP); + } + + if (u->us_reqtimeout > 0) + UNTIMEOUT(upap_reqtimeout, u); +} + + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void +upap_rauthack(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char msglen; + char *msg; + + UPAPDEBUG((LOG_INFO, "pap_rauthack: Rcvd id %d.", id)); + if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(u->us_unit, PPP_PAP); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nakk. + */ +static void +upap_rauthnak(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char msglen; + char *msg; + + UPAPDEBUG((LOG_INFO, "pap_rauthnak: Rcvd id %d.", id)); + if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_BADAUTH; + + syslog(LOG_ERR, "PAP authentication failed"); + auth_withpeer_fail(u->us_unit, 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); + + UPAPDEBUG((LOG_INFO, "pap_sauth: Sent id %d.", u->us_id)); + + 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); + + UPAPDEBUG((LOG_INFO, "pap_sresp: Sent code %d, id %d.", code, id)); +} + +/* + * 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="); + print_string(pwd, wlen, printer, arg); + break; + case UPAP_AUTHACK: + case UPAP_AUTHNAK: + if (len < 1) + break; + mlen = p[0]; + if (len < mlen + 1) + break; + msg = (char *) (p + 1); + p += mlen + 1; + len -= mlen + 1; + printer(arg, " "); + 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/packages/net/ppp/current/src/zlib.c b/packages/net/ppp/current/src/zlib.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/src/zlib.c @@ -0,0 +1,5443 @@ +//========================================================================== +// +// src/zlib.c +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== + +/* + * 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 + * + * $FreeBSD: src/sys/net/zlib.c,v 1.10.6.2 2002/03/24 23:23:58 jedgar 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 _KERNEL + +#define NO_DUMMY_DECL +#define NO_ZCFUNCS +#define MY_ZCALLOC + +#if 1 //defined(__FreeBSD__) && 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 + +#ifdef _KERNEL +#include +#else +#include "zlib.h" +#endif + +#ifdef _KERNEL +/* Assume this is a *BSD or SVR4 kernel */ +#include +#include +#include +//#include +#ifndef __ECOS +# define HAVE_MEMCPY +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memset(d, v, n) bzero((d), (n)) +# define memcmp bcmp +#else +# include +//# include +#endif + +#else +#if defined(__KERNEL__) +/* Assume this is a Linux kernel */ +#include +#define HAVE_MEMCPY + +#else /* not kernel */ + +#if defined(MSDOS)||defined(VMS)||defined(CRAY)||defined(WIN32)||defined(RISCOS) +# include +# include +#else + extern int errno; +#endif +#ifdef STDC +# include +# include +#endif +#endif /* __KERNEL__ */ +#endif /* _KERNEL */ + +#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 = (const char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#ifdef MSDOS +# define OS_CODE 0x00 +# ifdef __TURBOC__ +# include +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +#endif + +#ifdef WIN32 /* Window 95 & Windows NT */ +# define OS_CODE 0x0b +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define FOPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef MACOS +# define OS_CODE 0x07 +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0F +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef FOPEN +# define FOPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#ifdef HAVE_STRERROR + extern char *strerror OF((int)); +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(_MSC_VER) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, Bytef* source, uInt len)); + extern int zmemcmp OF((Bytef* s1, Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG_ZLIB +# include +# ifndef verbose +# define verbose 0 +# endif + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) OF((uLong check, const Bytef *buf, uInt len)); + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* _Z_UTIL_H */ +/* --- zutil.h */ + +/* +++ deflate.h */ +/* deflate.h -- internal compression state + * Copyright (C) 1995-1996 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* From: deflate.h,v 1.10 1996/07/02 12:41:00 me Exp $ */ + +#ifndef _DEFLATE_H +#define _DEFLATE_H + +/* #include "zutil.h" */ + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct deflate_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + int pending; /* nb of bytes in the pending buffer */ + int noheader; /* suppress zlib header and adler32 */ + Byte data_type; /* UNKNOWN, BINARY or ASCII */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + ulg compressed_len; /* total bit length of compressed file */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG_ZLIB + ulg bits_sent; /* bit length of the compressed data */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +ulg _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_stored_type_only OF((deflate_state *)); + +#endif +/* --- deflate.h */ + +/* +++ deflate.c */ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-1996 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in ftp://ds.internic.net/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* From: deflate.c,v 1.15 1996/07/24 13:40:58 me Exp $ */ + +/* #include "deflate.h" */ + +char deflate_copyright[] = " deflate 1.0.4 Copyright 1995-1996 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +local block_state deflate_slow OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, charf *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG_ZLIB +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +local config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */ + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((charf *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int noheader = 0; + static char* my_version = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; +#ifndef NO_ZCFUNCS + if (strm->zalloc == Z_NULL) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == Z_NULL) strm->zfree = zcfree; +#endif + + if (level == Z_DEFAULT_COMPRESSION) level = 6; + + if (windowBits < 0) { /* undocumented feature: suppress zlib header */ + noheader = 1; + windowBits = -windowBits; + } + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 9 || 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 = (const char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) + return Z_STREAM_ERROR; + + s = (deflate_state *) strm->state; + if (s->status != INIT_STATE) return Z_STREAM_ERROR; + + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); +#ifndef USE_DICT_HEAD + dictionary += dictLength - length; /* use the tail of the dictionary */ +#endif + } + zmemcpy((charf *)s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR; + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->noheader < 0) { + s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */ + } + s->status = s->noheader ? BUSY_STATE : INIT_STATE; + strm->adler = 1; + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = (deflate_state *) strm->state; + + if (level == Z_DEFAULT_COMPRESSION) { + level = 6; + } + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + deflate_state *s = (deflate_state *) strm->state; + unsigned len = s->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + if (strm->next_out != Z_NULL) { + zmemcpy(strm->next_out, s->pending_out, len); + strm->next_out += len; + } + s->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + s->pending -= len; + if (s->pending == 0) { + s->pending_out = s->pending_buf; + } +} + +/* ========================================================================= */ +int deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = (deflate_state *) strm->state; + + if ((strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the zlib header */ + if (s->status == INIT_STATE) { + + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags = (s->level-1) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = 1L; + } + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUFF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush == Z_PACKET_FLUSH) { + /* Output just the 3-bit `stored' block type value, + but not a zero length. */ + _tr_stored_type_only(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->noheader) return Z_STREAM_END; + + /* Write the zlib trailer (adler32) */ + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + s->noheader = -1; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int deflateEnd (strm) + z_streamp strm; +{ + int status; + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = (deflate_state *) strm->state; + + status = s->status; + if (status != INIT_STATE && status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, s->pending_buf); + TRY_FREE(strm, s->head); + TRY_FREE(strm, s->prev); + TRY_FREE(strm, s->window); + + ZFREE(strm, s); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + */ +int deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) + return Z_STREAM_ERROR; + ss = (deflate_state *) source->state; + + zmemcpy(dest, source, sizeof(*dest)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(*ds)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* ??? following zmemcpy doesn't work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +} + +/* =========================================================================== + * Return the number of bytes of output which are immediately available + * for output from the decompressor. + */ +int deflateOutputPending (strm) + z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return 0; + + return ((deflate_state *)(strm->state))->pending; +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + charf *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (!((deflate_state *)(strm->state))->noheader) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +} + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#ifdef DEBUG_ZLIB +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp((charf *)s->window + match, + (charf *)s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + } else if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy((charf *)s->window, (charf *)s->window+wsize, + (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, (charf *)s->window + s->strstart + s->lookahead, + more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + bflush = _tr_tally(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in hash table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + bflush = _tr_tally (s, 0, s->window[s->strstart]); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED || + (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR))) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + bflush = _tr_tally(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + if (_tr_tally (s, 0, s->window[s->strstart-1])) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally (s, 0, s->window[s->strstart-1]); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +/* --- deflate.c */ + +/* +++ trees.c */ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-1996 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* From: trees.c,v 1.11 1996/07/24 13:41:06 me Exp $ */ + +/* #include "deflate.h" */ + +#ifdef DEBUG_ZLIB +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +local uch dist_code[512]; +/* distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +local uch length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +struct static_tree_desc_s { + ct_data *static_tree; /* static tree or NULL */ + intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifndef DEBUG_ZLIB +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG_ZLIB */ +# define send_code(s, c, tree) \ + { if (verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +#define d_code(dist) \ + ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG_ZLIB +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG_ZLIB */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG_ZLIB */ + + +#define MAX(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. In a multi-threaded environment, + * this function may be called by two threads concurrently, but this is + * harmless since both invocations do exactly the same thing. + */ +local void tr_static_init() +{ + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; +} + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->compressed_len = 0L; + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG_ZLIB + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + ct_data *stree = desc->stat_desc->static_tree; + intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; + + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* Send just the `stored block' type code without any length bytes or data. + */ +void _tr_stored_type_only(s) + deflate_state *s; +{ + send_bits(s, (STORED_BLOCK << 1), 3); + bi_windup(s); + s->compressed_len = (s->compressed_len + 3) & ~7L; +} + + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); + s->compressed_len += 10L; + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length for the file so far. + */ +ulg _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is ascii or binary */ + if (s->data_type == Z_UNKNOWN) set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute first the block length in bytes*/ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + + /* If compression failed and this is the first and last block, + * and if the .zip file can be seeked (to rewrite the local header), + * the whole file is transformed into a stored file: + */ +#ifdef STORED_FILE_OK +# ifdef FORCE_STORED_FILE + if (eof && s->compressed_len == 0L) { /* force stored file */ +# else + if (stored_len <= opt_lenb && eof && s->compressed_len==0L && seekable()) { +# endif + /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ + if (buf == (charf*)0) error ("block vanished"); + + copy_block(s, buf, (unsigned)stored_len, 0); /* without header */ + s->compressed_len = stored_len << 3; + s->method = STORED; + } else +#endif /* STORED_FILE_OK */ + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); + s->compressed_len += 3 + s->static_len; + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); + s->compressed_len += 3 + s->opt_len; + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + init_block(s); + + if (eof) { + bi_windup(s); + s->compressed_len += 7; /* align on byte boundary */ + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); + + return s->compressed_len >> 3; +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + + /* Try to guess if it is profitable to stop the current block here */ + if (s->level > 2 && (s->last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +local void set_data_type(s) + deflate_state *s; +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; + s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG_ZLIB + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG_ZLIB + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG_ZLIB + s->bits_sent += (ulg)len<<3; +#endif + /* bundle up the put_byte(s, *buf++) calls */ + zmemcpy(&s->pending_buf[s->pending], buf, len); + s->pending += len; +} +/* --- trees.c */ + +/* +++ inflate.c */ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ + +/* +++ infblock.h */ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +extern inflate_blocks_statef * inflate_blocks_new OF(( + z_streamp z, + check_func c, /* check function */ + uInt w)); /* window size */ + +extern int inflate_blocks OF(( + inflate_blocks_statef *, + z_streamp , + int)); /* initial return code */ + +extern void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_streamp , + uLongf *)); /* check value on output */ + +extern int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_streamp , + uLongf *)); /* check value on output */ + +extern void inflate_set_dictionary OF(( + inflate_blocks_statef *s, + const Bytef *d, /* dictionary */ + uInt n)); /* dictionary length */ + +extern int inflate_addhistory OF(( + inflate_blocks_statef *, + z_streamp)); + +extern int inflate_packet_flush OF(( + inflate_blocks_statef *)); +/* --- infblock.h */ + +#ifndef NO_DUMMY_DECL +struct inflate_blocks_state {int dummy;}; /* for buggy compilers */ +#endif + +/* inflate private state */ +struct internal_state { + + /* mode */ + enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + DICT4, /* four dictionary check bytes to go */ + DICT3, /* three dictionary check bytes to go */ + DICT2, /* two dictionary check bytes to go */ + DICT1, /* one dictionary check byte to go */ + DICT0, /* waiting for inflateSetDictionary */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ + mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int inflateReset(z) +z_streamp z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, &c); + Trace((stderr, "inflate: reset\n")); + return Z_OK; +} + + +int inflateEnd(z) +z_streamp z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z, &c); + ZFREE(z, z->state); + z->state = Z_NULL; + Trace((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2_(z, w, version, stream_size) +z_streamp z; +int w; +const char *version; +int stream_size; +{ + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != sizeof(z_stream)) + return Z_VERSION_ERROR; + + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; + z->msg = Z_NULL; +#ifndef NO_ZCFUNCS + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; +#endif + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Trace((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int inflateInit_(z, version, stream_size) +z_streamp z; +const char *version; +int stream_size; +{ + return inflateInit2_(z, DEF_WBITS, version, stream_size); +} + + +#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z, f) +z_streamp z; +int f; +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL || f < 0) + return Z_STREAM_ERROR; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + b = NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = BLOCKS; + break; + } + z->state->mode = DICT4; + case DICT4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = DICT3; + case DICT3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = DICT2; + case DICT2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = DICT1; + case DICT1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = DICT0; + return Z_NEED_DICT; + case DICT0: + z->state->mode = BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_STREAM_ERROR; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) + r = inflate_packet_flush(z->state->blocks); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r != Z_STREAM_END) + return r; + r = Z_OK; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + + empty: + if (f != Z_PACKET_FLUSH) + return r; + z->state->mode = BAD; + z->msg = (char *)"need more for packet flush"; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_DATA_ERROR; +} + + +int inflateSetDictionary(z, dictionary, dictLength) +z_streamp z; +const Bytef *dictionary; +uInt dictLength; +{ + uInt length = dictLength; + + if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0) + return Z_STREAM_ERROR; + + if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR; + z->adler = 1L; + + if (length >= ((uInt)1<state->wbits)) + { + length = (1<state->wbits)-1; + dictionary += dictLength - length; + } + inflate_set_dictionary(z->state->blocks, dictionary, length); + z->state->mode = BLOCKS; + return Z_OK; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ + +int inflateIncomp(z) +z_stream *z; +{ + if (z->state->mode != BLOCKS) + return Z_DATA_ERROR; + return inflate_addhistory(z->state->blocks, z); +} + + +int inflateSync(z) +z_streamp z; +{ + uInt n; /* number of bytes to look at */ + Bytef *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != BAD) + { + z->state->mode = BAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + if (*p == (Byte)(m < 2 ? 0 : 0xff)) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = BLOCKS; + return Z_OK; +} + +#undef NEEDBYTE +#undef NEXTBYTE +/* --- inflate.c */ + +/* +++ infblock.c */ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "infblock.h" */ + +/* +++ inftrees.h */ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit machines) */ + union { + uInt Base; /* literal, length base, or distance base */ + inflate_huft *Next; /* pointer to next level of table */ + } more; +}; + +#ifdef DEBUG_ZLIB + extern uInt inflate_hufts; +#endif + +extern int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + z_streamp )); /* for zalloc, zfree functions */ + +extern int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + z_streamp )); /* for zalloc, zfree functions */ + +extern int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *)); /* distance tree result */ + +extern int inflate_trees_free OF(( + inflate_huft *, /* tables to free */ + z_streamp )); /* for zfree function */ + +/* --- inftrees.h */ + +/* +++ infcodes.h */ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +extern inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_streamp )); + +extern int inflate_codes OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +extern void inflate_codes_free OF(( + inflate_codes_statef *, + z_streamp )); + +/* --- infcodes.h */ + +/* +++ infutil.h */ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFUTIL_H +#define _INFUTIL_H + +typedef enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONEB, /* finished last block, done */ + BADB} /* got a data error--stuck here */ +inflate_block_mode; + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + inflate_block_mode mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_huft *tl; + inflate_huft *td; /* trees to free */ + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;} +#define WWRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WWRAP if(m==0){FLUSH WWRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* masks for lower bits (size given to avoid silly warnings with Visual C++) */ +extern uInt inflate_mask[17]; + +/* copy as much as possible from the sliding window to the output area */ +extern int inflate_flush OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#endif +/* --- infutil.h */ + +#ifndef NO_DUMMY_DECL +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ +#endif + +/* Table for deflate from PKZIP's appnote.txt. */ +local const uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +void inflate_blocks_reset(s, z, c) +inflate_blocks_statef *s; +z_streamp z; +uLongf *c; +{ + if (s->checkfn != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == CODES) + { + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + } + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, Z_NULL, 0); + Trace((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z, c, w) +z_streamp z; +check_func c; +uInt w; +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Trace((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, &s->check); + return s; +} + + +#ifdef DEBUG_ZLIB + extern uInt inflate_hufts; +#endif +int inflate_blocks(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Trace((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Trace((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.tl = Z_NULL; /* don't try to free these */ + s->sub.decode.td = Z_NULL; + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Trace((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BADB; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = BADB; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BADB; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (t < 19) + t = 19; + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) { + ZFREE(z, s->sub.trees.blens); + s->mode = BADB; + } + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->word.what.Bits; + c = h->more.Base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + inflate_trees_free(s->sub.trees.tb, z); + ZFREE(z, s->sub.trees.blens); + s->mode = BADB; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + inflate_trees_free(s->sub.trees.tb, z); + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; +#ifdef DEBUG_ZLIB + inflate_hufts = 0; +#endif + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) { + ZFREE(z, s->sub.trees.blens); + s->mode = BADB; + } + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok, %d * %d bytes used\n", + inflate_hufts, sizeof(inflate_huft))); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + inflate_trees_free(td, z); + inflate_trees_free(tl, z); + r = Z_MEM_ERROR; + LEAVE + } + /* + * this ZFREE must occur *BEFORE* we mess with sub.decode, because + * sub.trees is union'd with sub.decode. + */ + ZFREE(z, s->sub.trees.blens); + s->sub.decode.codes = c; + s->sub.decode.tl = tl; + s->sub.decode.td = td; + } + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONEB; + case DONEB: + r = Z_STREAM_END; + LEAVE + case BADB: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(s, z, c) +inflate_blocks_statef *s; +z_streamp z; +uLongf *c; +{ + inflate_blocks_reset(s, z, c); + ZFREE(z, s->window); + ZFREE(z, s); + Trace((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + +void inflate_set_dictionary(s, d, n) +inflate_blocks_statef *s; +const Bytef *d; +uInt n; +{ + zmemcpy((charf *)s->window, d, n); + s->read = s->write = s->window + n; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ +int inflate_addhistory(s, z) +inflate_blocks_statef *s; +z_stream *z; +{ + uLong b; /* bit buffer */ /* NOT USED HERE */ + uInt k; /* bits in bit buffer */ /* NOT USED HERE */ + uInt t; /* temporary storage */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + if (s->read != s->write) + return Z_STREAM_ERROR; + if (s->mode != TYPE) + return Z_DATA_ERROR; + + /* we're ready to rock */ + LOAD + /* while there is input ready, copy to output buffer, moving + * pointers as needed. + */ + while (n) { + t = n; /* how many to do */ + /* is there room until end of buffer? */ + if (t > m) t = m; + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, t); + zmemcpy(q, p, t); + q += t; + p += t; + n -= t; + z->total_out += t; + s->read = q; /* drag read pointer forward */ +/* WWRAP */ /* expand WWRAP macro by hand to handle s->read */ + if (q == s->end) { + s->read = q = s->window; + m = WAVAIL; + } + } + UPDATE + return Z_OK; +} + + +/* + * At the end of a Deflate-compressed PPP packet, we expect to have seen + * a `stored' block type value but not the (zero) length bytes. + */ +int inflate_packet_flush(s) + inflate_blocks_statef *s; +{ + if (s->mode != LENS) + return Z_DATA_ERROR; + s->mode = TYPE; + return Z_OK; +} +/* --- infblock.c */ + +/* +++ inftrees.c */ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "inftrees.h" */ + +char inflate_copyright[] = " inflate 1.0.4 Copyright 1995-1996 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + const uIntf *, /* list of base values for non-simple codes */ + const uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + z_streamp )); /* for zalloc function */ + +local voidpf falloc OF(( + voidpf, /* opaque pointer (not used) */ + uInt, /* number of items */ + uInt)); /* size of item */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* see note #13 above about 258 */ +local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ +local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local const uInt cpdext[30] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ +#define N_MAX 288 /* maximum number of codes in any set */ + +#ifdef DEBUG_ZLIB + uInt inflate_hufts; +#endif + +local int huft_build(b, n, s, d, e, t, m, zs) +uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ +uInt n; /* number of codes (assumed <= N_MAX) */ +uInt s; /* number of simple-valued codes (0..s-1) */ +const uIntf *d; /* list of base values for non-simple codes */ +const uIntf *e; /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t; /* result: starting table */ +uIntf *m; /* maximum lookup bits, returns actual */ +z_streamp zs; /* for zalloc function */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + lengths), or Z_MEM_ERROR if not enough memory. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + uInt v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; /* set n to length of v */ + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = g - w; + z = z > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (inflate_huft *)ZALLOC + (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) + { + if (h) + inflate_trees_free(u[0], zs); + return Z_MEM_ERROR; /* not enough memory */ + } +#ifdef DEBUG_ZLIB + inflate_hufts += z + 1; +#endif + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->next)) = Z_NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + r.next = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits(c, bb, tb, z) +uIntf *c; /* 19 code lengths */ +uIntf *bb; /* bits tree desired/actual depth */ +inflate_huft * FAR *tb; /* bits tree result */ +z_streamp z; /* for zfree function */ +{ + int r; + + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + inflate_trees_free(*tb, z); + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + return r; +} + + +int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) +uInt nl; /* number of literal/length codes */ +uInt nd; /* number of distance codes */ +uIntf *c; /* that many (total) code lengths */ +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +z_streamp z; /* for zfree function */ +{ + int r; + + /* build literal/length tree */ + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + inflate_trees_free(*tl, z); + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + return r; + } + + /* build distance tree */ + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + inflate_trees_free(*td, z); + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + inflate_trees_free(*tl, z); + return r; +#endif + } + + /* done */ + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +local int fixed_built = 0; +#define FIXEDH 530 /* number of hufts used by fixed tables */ +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; + + +local voidpf falloc(q, n, s) +voidpf q; /* opaque pointer */ +uInt n; /* number of items */ +uInt s; /* size of item */ +{ + Assert(s == sizeof(inflate_huft) && n <= *(intf *)q, + "inflate_trees falloc overflow"); + *(intf *)q -= n+s-s; /* s-s to avoid warning */ + return (voidpf)(fixed_mem + *(intf *)q); +} + + +int inflate_trees_fixed(bl, bd, tl, td) +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +{ + /* build fixed tables if not already (multiple overlapped executions ok) */ + if (!fixed_built) + { + int k; /* temporary variable */ + unsigned c[288]; /* length list for huft_build */ + z_stream z; /* for falloc function */ + int f = FIXEDH; /* number of hufts left in fixed_mem */ + + /* set up fake z_stream for memory routines */ + z.zalloc = falloc; + z.zfree = Z_NULL; + z.opaque = (voidpf)&f; + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 7; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); + + /* done */ + Assert(f == 0, "invalid build of fixed tables"); + fixed_built = 1; + } + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +int inflate_trees_free(t, z) +inflate_huft *t; /* table to free */ +z_streamp z; /* for zfree function */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register inflate_huft *p, *q, *r; + + /* Reverse linked list */ + p = Z_NULL; + q = t; + while (q != Z_NULL) + { + r = (q - 1)->next; + (q - 1)->next = p; + p = q; + q = r; + } + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + while (p != Z_NULL) + { + q = (--p)->next; + ZFREE(z,p); + p = q; + } + return Z_OK; +} +/* --- inftrees.c */ + +/* +++ infcodes.c */ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "inftrees.h" */ +/* #include "infblock.h" */ +/* #include "infcodes.h" */ +/* #include "infutil.h" */ + +/* +++ inffast.h */ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +extern int inflate_fast OF(( + uInt, + uInt, + inflate_huft *, + inflate_huft *, + inflate_blocks_statef *, + z_streamp )); +/* --- inffast.h */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ + mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) +uInt bl, bd; +inflate_huft *tl; +inflate_huft *td; /* need separate declaration for Borland C++ */ +z_streamp z; +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ +#ifndef __TURBOC__ /* Turbo C bug for following expression */ + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; +#else + f = q - c->sub.copy.dist; + if ((uInt)(q - s->window) < c->sub.copy.dist) + f = s->end - (c->sub.copy.dist - (uInt)(q - s->window)); +#endif + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(c, z) +inflate_codes_statef *c; +z_streamp z; +{ + ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} +/* --- infcodes.c */ + +/* +++ infutil.c */ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "infblock.h" */ +/* #include "inftrees.h" */ +/* #include "infcodes.h" */ +/* #include "infutil.h" */ + +#ifndef NO_DUMMY_DECL +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ +#endif + +/* And'ing with mask[n] masks the lower n bits */ +uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + + +/* copy as much as possible from the sliding window to the output area */ +int inflate_flush(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt n; + Bytef *p; + Bytef *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + if (p != Z_NULL) { + zmemcpy(p, q, n); + p += n; + } + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + if (p != Z_NULL) { + zmemcpy(p, q, n); + p += n; + } + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} +/* --- infutil.c */ + +/* +++ inffast.c */ +/* inffast.c -- process literals and length/distance pairs fast + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "inftrees.h" */ +/* #include "infblock.h" */ +/* #include "infcodes.h" */ +/* #include "infutil.h" */ +/* #include "inffast.h" */ + +#ifndef NO_DUMMY_DECL +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ +#endif + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<>3);p-=c;k&=7;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +int inflate_fast(bl, bd, tl, td, s, z) +uInt bl, bd; +inflate_huft *tl; +inflate_huft *td; /* need separate declaration for Borland C++ */ +inflate_blocks_statef *s; +z_streamp z; +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Bytef *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + if ((uInt)(q - s->window) >= d) /* offset before dest */ + { /* just copy */ + r = q - d; + *q++ = *r++; c--; /* minimum count is three, */ + *q++ = *r++; c--; /* so unroll loop a little */ + } + else /* else offset after destination */ + { + e = d - (uInt)(q - s->window); /* bytes from offset to end */ + r = s->end - e; /* pointer to offset */ + if (c > e) /* if source crosses, */ + { + c -= e; /* copy to end of window */ + do { + *q++ = *r++; + } while (--e); + r = s->window; /* copy rest from start of window */ + } + } + do { /* copy all or what's left */ + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} +/* --- inffast.c */ + +/* +++ zutil.c */ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-1996 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zutil.c,v 1.17 1996/07/24 13:41:12 me Exp $ */ + +#ifdef DEBUG_ZLIB +#include +#endif + +/* #include "zutil.h" */ + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef STDC +extern void exit OF((int)); +#endif + +static 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/packages/net/ppp/current/tests/chat.c b/packages/net/ppp/current/tests/chat.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/chat.c @@ -0,0 +1,192 @@ +//========================================================================== +// +// tests/ppp_up.c +// +// Simple test of PPP and networking support +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas +// Date: 2000-01-10 +// Purpose: +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// PPP test code + +#if 0 +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#endif + +#include "ppp_test_support.inl" + + + +//========================================================================== + + +#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000) +static char stack[STACK_SIZE]; +static cyg_thread thread_data; +static cyg_handle_t thread_handle; + +//========================================================================== + +static const char *script1[] = +{ + "ABORT" , "BUSY" , + "ABORT" , "NO CARRIER" , + "" , "Chat Test" , + "CONNECT" , "\\c" , + "TIMEOUT" , "10" , + "ogin:--ogin:" , "ppp" , + "TIMEOUT" , "5" , + "assword:" , "hithere" , + 0 +}; + +//========================================================================== + +static struct test_info +{ + char *name; + const char **script; + cyg_int32 result; +} tests[] = +{ + { "CHAT_TEST_1" , script1 , 0 }, // Simple test to completion + { "CHAT_TEST_2" , script1 , 1 }, // Expects an ABORT + { "CHAT_TEST_3" , script1 , 1 }, // Expects a timeout + { NULL , NULL , 0 } +}; + +//========================================================================== + +void +chat_test(cyg_addrword_t p) +{ + cyg_serial_baud_rate_t old; + cyg_int32 failures = 0; + cyg_int32 result; + struct test_info *test; + + CYG_TEST_INIT(); + + CYG_TEST_INFO("Start CHAT test"); + + old = ppp_test_set_baud( CYGNUM_SERIAL_BAUD_115200 ); + + for( test = &tests[0]; test->name != NULL; test++ ) + { + CYG_TEST_INFO( test->name ); + ppp_test_announce( test->name ); + + result = cyg_ppp_chat( CYGPKG_PPP_TEST_DEVICE, test->script ); + + diag_printf("chat result %d expected %d\n",result,test->result ); + + if( result != test->result ) + { + CYG_TEST_FAIL( test->name ); + failures++; + } + else + CYG_TEST_PASS( test->name ); + + cyg_thread_delay( 300 ); + } + + ppp_test_set_baud( old ); + + ppp_test_finish(); + +// ppp_test_announce( "CHAT_TEST_1" ); + +// success = cyg_ppp_chat( CYGPKG_PPP_TEST_DEVICE, script ); + +// if( !success ) +// CYG_TEST_INFO("Chat script failed"); + + CYG_TEST_FINISH("CHAT test done"); +} + +//========================================================================== + +void +cyg_start(void) +{ + // Create a main thread, so we can run the scheduler and have time 'pass' + cyg_thread_create(10, // Priority - just a number + chat_test, // entry + 0, // entry parameter + "CHAT test", // Name + &stack[0], // Stack + STACK_SIZE, // Size + &thread_handle, // Handle + &thread_data // Thread data structure + ); + cyg_thread_resume(thread_handle); // Start it + cyg_scheduler_start(); +} + +//========================================================================== diff --git a/packages/net/ppp/current/tests/isp.c b/packages/net/ppp/current/tests/isp.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/isp.c @@ -0,0 +1,392 @@ +//========================================================================== +// +// tests/isp.c +// +// Test of PPP and CHAT connection to an ISP +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: gthomas (ping code), nickg +// Date: 2003-06-01 +// Purpose: +// Description: This test uses CHAT to set up a modem, dial, talk to an ISP +// and then ping some well known addresses. It then brings the +// link down. +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// PPP test code + +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +//========================================================================== + +#include + +// Fill in the blanks if necessary +#ifndef TNR_OFF +# define TNR_OFF() +#endif +#ifndef TNR_ON +# define TNR_ON() +#endif +#ifndef TNR_INIT +# define TNR_INIT() +#endif +#ifndef TNR_PRINT_ACTIVITY +# define TNR_PRINT_ACTIVITY() +#endif + +#define NUM_PINGS 16 +#define MAX_PACKET 4096 +#define MIN_PACKET 64 +#define MAX_SEND 4000 + +#define PACKET_ADD ((MAX_SEND - MIN_PACKET)/NUM_PINGS) +#define nPACKET_ADD 1 + +static unsigned char pkt1[MAX_PACKET], pkt2[MAX_PACKET]; + +#define UNIQUEID 0x1234 + +void +pexit(char *s) +{ + CYG_TEST_FAIL_FINISH(s); +} + +// Compute INET checksum +int +inet_cksum(u_short *addr, int len) +{ + register int nleft = len; + register u_short *w = addr; + register u_short answer; + register u_int sum = 0; + u_short odd_byte = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while( nleft > 1 ) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if( nleft == 1 ) { + *(u_char *)(&odd_byte) = *(u_char *)w; + sum += odd_byte; + } + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0x0000ffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +static int +show_icmp(unsigned char *pkt, int len, + struct sockaddr_in *from, struct sockaddr_in *to) +{ + cyg_tick_count_t *tp, tv; + struct ip *ip; + struct icmp *icmp; + tv = cyg_current_time(); + ip = (struct ip *)pkt; + if ((len < sizeof(*ip)) || ip->ip_v != IPVERSION) { + diag_printf("%s: Short packet or not IP! - Len: %d, Version: %d\n", + inet_ntoa(from->sin_addr), len, ip->ip_v); + return 0; + } + icmp = (struct icmp *)(pkt + sizeof(*ip)); + len -= (sizeof(*ip) + 8); + tp = (cyg_tick_count_t *)&icmp->icmp_data; + if (icmp->icmp_type != ICMP_ECHOREPLY) { + diag_printf("%s: Invalid ICMP - type: %d\n", + inet_ntoa(from->sin_addr), icmp->icmp_type); + return 0; + } + if (icmp->icmp_id != UNIQUEID) { + diag_printf("%s: ICMP received for wrong id - sent: %x, recvd: %x\n", + inet_ntoa(from->sin_addr), UNIQUEID, icmp->icmp_id); + } + diag_printf("%d bytes from %s: ", len, inet_ntoa(from->sin_addr)); + diag_printf("icmp_seq=%d", icmp->icmp_seq); + diag_printf(", time=%dms\n", (int)(tv - *tp)*10); + return (from->sin_addr.s_addr == to->sin_addr.s_addr); +} + +static void +ping_host(int s, struct sockaddr_in *host) +{ + struct icmp *icmp = (struct icmp *)pkt1; + int icmp_len = MIN_PACKET; + int seq, ok_recv, bogus_recv; + cyg_tick_count_t *tp; + long *dp; + struct sockaddr_in from; + int i, len, fromlen; + + ok_recv = 0; + bogus_recv = 0; + diag_printf("PING server %s\n", inet_ntoa(host->sin_addr)); + for (seq = 0; seq < NUM_PINGS; seq++, icmp_len += PACKET_ADD ) { + TNR_ON(); + // Build ICMP packet + icmp->icmp_type = ICMP_ECHO; + icmp->icmp_code = 0; + icmp->icmp_cksum = 0; + icmp->icmp_seq = seq; + icmp->icmp_id = 0x1234; + // Set up ping data + tp = (cyg_tick_count_t *)&icmp->icmp_data; + *tp++ = cyg_current_time(); + dp = (long *)tp; + for (i = sizeof(*tp); i < icmp_len; i += sizeof(*dp)) { + *dp++ = i; + } + // Add checksum + icmp->icmp_cksum = inet_cksum( (u_short *)icmp, icmp_len+8); + // Send it off + if (sendto(s, icmp, icmp_len+8, 0, (struct sockaddr *)host, sizeof(*host)) < 0) { + TNR_OFF(); + perror("sendto"); + continue; + } + // Wait for a response + fromlen = sizeof(from); + len = recvfrom(s, pkt2, sizeof(pkt2), 0, (struct sockaddr *)&from, &fromlen); + TNR_OFF(); + if (len < 0) { + perror("recvfrom"); + icmp_len = MIN_PACKET - PACKET_ADD; // just in case - long routes + } else { + if (show_icmp(pkt2, len, &from, host)) { + ok_recv++; + } else { + bogus_recv++; + } + } + } + TNR_OFF(); + diag_printf("Sent %d packets, received %d OK, %d bad\n", NUM_PINGS, ok_recv, bogus_recv); +} + +static void do_ping(char *addr) +{ + struct protoent *p; + struct timeval tv; + struct sockaddr_in host; + int s; + + if ((p = getprotobyname("icmp")) == (struct protoent *)0) { + pexit("getprotobyname"); + return; + } + s = socket(AF_INET, SOCK_RAW, p->p_proto); + if (s < 0) { + pexit("socket"); + return; + } + tv.tv_sec = 1; + tv.tv_usec = 0; + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + // Set up host address + host.sin_family = AF_INET; + host.sin_len = sizeof(host); + inet_aton(addr, &host.sin_addr); + host.sin_port = 0; + + ping_host(s, &host); + +} + +//========================================================================== + + +#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000) +static char stack[STACK_SIZE]; +static cyg_thread thread_data; +static cyg_handle_t thread_handle; + +//========================================================================== +/* + +Captured session: + + +AT S7=45 S0=0 L1 V1 X4 &c1 E1 Q0 +OK +ATD08440416662 +CONNECT 42666/ARQ/V90/LAPM/V42BIS +anchor-du-10.access.demon.net + + +login: xxxxxx +Password: yyyyyy +Protocol: ppp +xxxxxx: IP Address: XXX.XXX.XXX.XXX Running PPP on 1223 +No operational problems reported. +Finger status@gate.demon.co.uk for more info - Last change 16:30 Jun 08 +HELLO + + + +OK +ATH +OK + +*/ + +static char *script[] = +{ + "ABORT" , "BUSY" , + "ABORT" , "NO CARRIER" , + "ABORT" , "ERROR" , + "" , "ATZ" , + "OK" , "AT S7=45 S0=0 L1 V1 X4 &C1 E1 Q0" , + "OK" , "ATD" CYGPKG_PPP_DEFAULT_DIALUP_NUMBER , + "ogin:--ogin:" , CYGPKG_PPP_AUTH_DEFAULT_USER , + "assword:" , CYGPKG_PPP_AUTH_DEFAULT_PASSWD , + "otocol:" , "ppp" , + "HELLO" , "\\c" , + 0 +}; + +//========================================================================== + +void +isp_test(cyg_addrword_t p) +{ + CYG_TEST_INIT(); + + diag_printf("Start ISP test\n"); + + init_all_network_interfaces(); + + { + cyg_ppp_options_t options; + cyg_ppp_handle_t ppp_handle; + + cyg_ppp_options_init( &options ); + +// options.debug = 1; +// options.kdebugflag = 1; + + options.modem = 1; + options.script = &script[0]; + + ppp_handle = cyg_ppp_up( CYGPKG_PPP_TEST_DEVICE, &options ); + + CYG_TEST_INFO( "Waiting for PPP to come up"); + + cyg_ppp_wait_up( ppp_handle ); + + CYG_TEST_INFO( "Delaying for a while..."); + + cyg_thread_delay(10*100); + + CYG_TEST_INFO( "Pinging"); + + do_ping("195.173.57.10"); // anchor-du-10.access.demon.net + do_ping("194.154.160.254"); // fluffy.ecoscentric.com + do_ping("66.187.233.205"); // sources.redhat.com + + CYG_TEST_INFO( "Bringing PPP down"); + + cyg_ppp_down( ppp_handle ); + + CYG_TEST_INFO( "Waiting for PPP to go down"); + + cyg_ppp_wait_down( ppp_handle ); + } + + CYG_TEST_PASS_FINISH("ISP test OK"); +} + +//========================================================================== + +void +cyg_start(void) +{ + // Create a main thread, so we can run the scheduler and have time 'pass' + cyg_thread_create(10, // Priority - just a number + isp_test, // entry + 0, // entry parameter + "ISP test", // Name + &stack[0], // Stack + STACK_SIZE, // Size + &thread_handle, // Handle + &thread_data // Thread data structure + ); + cyg_thread_resume(thread_handle); // Start it + cyg_scheduler_start(); +} + +//========================================================================== diff --git a/packages/net/ppp/current/tests/nc_test_framework.h b/packages/net/ppp/current/tests/nc_test_framework.h new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/nc_test_framework.h @@ -0,0 +1,202 @@ +//========================================================================== +// +// tests/nc_test_framework.h +// +// Network characterization tests framework +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas +// Date: 2000-01-10 +// Purpose: +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#ifndef _TESTS_NC_TEST_FRAMEWORK_H_ +#define _TESTS_NC_TEST_FRAMEWORK_H_ + +#ifdef __ECOS +#include +#else +#undef _KERNEL +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#ifndef EAI_NONE +#define EAI_NONE 0 +#endif +#endif + +#ifdef __ECOS +#define test_printf diag_printf +typedef cyg_addrword_t test_param_t; +#else +#define test_printf printf +typedef void *test_param_t; +#endif + +#ifndef true +#define false 0 +#define true 1 +#endif + +#define NC_SLAVE_PORT 7777 +#define NC_MASTER_PORT 7776 + +#define NC_TESTING_SLAVE_PORT 8770 +#define NC_TESTING_MASTER_PORT 8771 + +#define __string(s) #s +#define _string(s) __string(s) + +// +// The basic idea behind this test structure is that one end will run +// in "slave" mode and the other in "master" mode. Typically, the slave +// will run on a target platform with the master running on a host. +// +// The slave starts up by listening for a connection on the "SLAVE_PORT". +// In order for the testing to require the minimum stack support, this +// connection (and the protocol) will use UDP. +// +// The master will connect to the slave and send it a request over this +// connection. Once the slave accepts the request, then master and slave +// will execute the operation, typically a test. The control connection +// will remain active until the master sends a 'disconnect' request. The +// control connection will be broken after the reply to this request has +// been sent. +// + +#define MAX_ERRORS 5 // Give up after this many errors + +#define NC_REPLY_TIMEOUT 10 // The slave may be slow +#define NC_TEST_TIMEOUT 3 // More generous for tests +#define NC_RESULTS_TIMEOUT (MAX_ERRORS+2)*NC_TEST_TIMEOUT + +struct nc_request { + int type; // Description of request + int seq; // Sequence number, used to build response + int nbufs; // Number of "buffers" to send + int buflen; // Length of each buffer + int slave_port; // Network ports to use + int master_port; + int timeout; // Max time to wait for any packet +}; + +#define NC_REQUEST_DISCONNECT 0x0001 +#define NC_REQUEST_UDP_SEND 0x0010 // Slave to send UDP data +#define NC_REQUEST_UDP_RECV 0x0011 // Slave to receive UDP data +#define NC_REQUEST_UDP_ECHO 0x0012 // Master->slave->master +#define NC_REQUEST_TCP_SEND 0x0020 // Slave to send TCP data +#define NC_REQUEST_TCP_RECV 0x0021 // Slave to receive TCP data +#define NC_REQUEST_TCP_ECHO 0x0022 // Master->slave->master +#define NC_REQUEST_START_IDLE 0x0100 // Start some idle processing +#define NC_REQUEST_STOP_IDLE 0x0101 // Stop idle processing +#define NC_REQUEST_SET_LOAD 0x0200 // Set the background load level + +struct nc_reply { + int response; // ACK or NAK + int seq; // Must match request + int reason; // If NAK, why request turned down + union { // Miscellaneous data, depending on request + struct { + long elapsed_time; // In 10ms "ticks" + long count[2]; // Result + } idle_results; + } misc; +}; + +#define NC_REPLY_ACK 0x0001 // Request accepted +#define NC_REPLY_NAK 0x0000 // Request denied + +#define NC_REPLY_NAK_UNKNOWN_REQUEST 0x0001 +#define NC_REPLY_NAK_BAD_REQUEST 0x0002 // Slave can't handle +#define NC_REPLY_NAK_NO_BACKGROUND 0x0003 // Slave can't do background/idle + +// +// Test data 'packets' look like this + +struct nc_test_data { + long key1; + int seq; + int len; + long key2; + char data[0]; // Actual data +}; + +#define NC_TEST_DATA_KEY1 0xC0DEADC0 +#define NC_TEST_DATA_KEY2 0xC0DEADC1 + +struct nc_test_results { + long key1; // Identify uniquely as a response record + int seq; // Matches request + int nsent; + int nrecvd; + long key2; // Additional verification +}; + +#define NC_TEST_RESULT_KEY1 0xDEADC0DE +#define NC_TEST_RESULT_KEY2 0xDEADC1DE + +#endif // _TESTS_NC_TEST_FRAMEWORK_H_ + + + diff --git a/packages/net/ppp/current/tests/nc_test_slave.c b/packages/net/ppp/current/tests/nc_test_slave.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/nc_test_slave.c @@ -0,0 +1,867 @@ +//========================================================================== +// +// tests/nc_test_slave.c +// +// Network characterizations test (slave portion) +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas +// Date: 2000-01-10 +// Purpose: +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// Network characterization test code - slave portion + +#include + +#include + +#include "nc_test_framework.h" +#include + +#ifdef __ECOS + +#include "ppp_test_support.inl" + +#ifndef CYGPKG_LIBC_STDIO +#define perror(s) diag_printf(#s ": %s\n", strerror(errno)) +#endif +#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000) +#define MAX_LOAD_THREAD_LEVEL 20 +#define MIN_LOAD_THREAD_LEVEL 0 +#define NUM_LOAD_THREADS 10 +#define IDLE_THREAD_PRIORITY CYGNUM_PPP_PPPD_THREAD_PRIORITY+3 +#define LOAD_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-1 +#define MAIN_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-2 +#define DESIRED_BACKGROUND_LOAD 20 +static char main_thread_stack[CYGHWR_NET_DRIVERS][STACK_SIZE]; +static cyg_thread main_thread_data[CYGHWR_NET_DRIVERS]; +static cyg_handle_t main_thread_handle[CYGHWR_NET_DRIVERS]; +static char idle_thread_stack[STACK_SIZE]; +static cyg_thread idle_thread_data; +static cyg_handle_t idle_thread_handle; +static cyg_sem_t idle_thread_sem; +volatile static long long idle_thread_count; +static cyg_tick_count_t idle_thread_start_time; +static cyg_tick_count_t idle_thread_stop_time; +static char load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE]; +static cyg_thread load_thread_data[NUM_LOAD_THREADS]; +static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS]; +static cyg_sem_t load_thread_sem[NUM_LOAD_THREADS]; +static long load_thread_level; +static void calibrate_load(int load); +static void start_load(int load); +static void do_some_random_computation(int p); +#define abs(n) ((n) < 0 ? -(n) : (n)) +#endif + +#ifdef __ECOS +#define test_param_t cyg_addrword_t +#ifdef CYGDBG_NET_TIMING_STATS +extern void show_net_times(void); +#endif +#else +#define test_param_t int +#endif + +#define MAX_BUF 8192 +static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF]; + +#ifdef __ECOS +extern void +cyg_test_exit(void); +#else +void +cyg_test_exit(void) +{ + test_printf("... Done\n"); + exit(1); +} + +static void +show_net_times(void) +{ +} +#endif + +#ifdef __ECOS +static void +test_delay(int ticks) +{ + cyg_thread_delay(ticks); +} + +#else + +static void +test_delay(int ticks) +{ + usleep(ticks * 10000); +} +#endif + +void +pexit(char *s) +{ + perror(s); +#ifdef CYGDBG_NET_TIMING_STATS + show_net_times(); +#endif + cyg_test_exit(); +} + +// +// Generic UDP test +// + +static void +do_udp_test(int s1, struct nc_request *req, struct sockaddr_in *master) +{ + int i, s, td_len, seq, seq_errors, lost; + struct sockaddr_in test_chan_slave, test_chan_master; + fd_set fds; + struct timeval timeout; + struct nc_test_results results; + struct nc_test_data *tdp; + int nsent, nrecvd; + int need_recv, need_send; + + need_recv = true; need_send = true; + switch (ntohl(req->type)) { + case NC_REQUEST_UDP_SEND: + need_recv = false; + need_send = true; + break; + case NC_REQUEST_UDP_RECV: + need_recv = true; + need_send = false; + break; + case NC_REQUEST_UDP_ECHO: + break; + } + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + pexit("datagram socket"); + } + + memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave)); + test_chan_slave.sin_family = AF_INET; +#ifdef __ECOS + test_chan_slave.sin_len = sizeof(test_chan_slave); +#endif + test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY); + test_chan_slave.sin_port = htons(ntohl(req->slave_port)); + + if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) { + perror("bind"); + close(s); + } + + memcpy(&test_chan_master, master, sizeof(*master)); + test_chan_master.sin_port = htons(ntohl(req->master_port)); + nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0; + for (i = 0; i < ntohl(req->nbufs); i++) { + if (need_recv) { + FD_ZERO(&fds); + FD_SET(s, &fds); + timeout.tv_sec = NC_TEST_TIMEOUT; + timeout.tv_usec = 0; + if (select(s+1, &fds, 0, 0, &timeout) <= 0) { + test_printf("recvfrom timeout, expecting seq #%d\n", seq); + if (++lost > MAX_ERRORS) { + test_printf("... giving up\n"); + break; + } + } else { + nrecvd++; + tdp = (struct nc_test_data *)in_buf; + td_len = ntohl(req->buflen) + sizeof(struct nc_test_data); + if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) { + perror("recvfrom"); + close(s); + return; + } + if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) && + (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) { + if (ntohl(tdp->seq) != seq) { + test_printf("Packets out of sequence - recvd: %d, expected: %d\n", + ntohl(tdp->seq), seq); + seq = ntohl(tdp->seq); + seq_errors++; + } + } else { + test_printf("Bad data packet - key: %x/%x, seq: %d\n", + ntohl(tdp->key1), ntohl(tdp->key2), + ntohl(tdp->seq)); + } + } + } + if (need_send) { + int retries = 10; + int sent = false; + int res; + + tdp = (struct nc_test_data *)out_buf; + tdp->key1 = htonl(NC_TEST_DATA_KEY1); + tdp->key2 = htonl(NC_TEST_DATA_KEY2); + tdp->seq = htonl(seq); + td_len = ntohl(req->buflen) + sizeof(struct nc_test_data); + tdp->len = htonl(td_len); + while (!sent && (--retries >= 0)) { + res = sendto(s, tdp, td_len, 0, + (struct sockaddr *)&test_chan_master, sizeof(test_chan_master)); + if (res > 0) { + sent = true; + break; + } + if (errno == ENOBUFS) { + // Saturated the system + test_delay(1); // Time for 200 500 byte 10-baseT packets + } else { + // What else to do? + close(s); + return; + } + } + if (sent) { + nsent++; + } else { + perror("sendto"); + } + } + seq++; + } + results.key1 = htonl(NC_TEST_RESULT_KEY1); + results.key2 = htonl(NC_TEST_RESULT_KEY2); + results.seq = req->seq; + results.nsent = htonl(nsent); + results.nrecvd = htonl(nrecvd); + if (sendto(s, &results, sizeof(results), 0, + (struct sockaddr *)&test_chan_master, sizeof(test_chan_master)) < 0) { + perror("sendto results"); + } + close(s); +} + +// +// Read data from a stream, accounting for the fact that packet 'boundaries' +// are not preserved. This can also timeout (which would probably wreck the +// data boundaries). +// + +int +do_read(int fd, void *buf, int buflen) +{ + char *p = (char *)buf; + int len = buflen; + int res; + while (len) { + res = read(fd, p, len); + if (res < 0) { + perror("read"); + } else { + len -= res; + p += res; + if (res == 0) { + break; + } + } + } + return (buflen - len); +} + +// +// Generic TCP test +// + +static void +do_tcp_test(int s1, struct nc_request *req, struct sockaddr_in *master) +{ + int i, s, len, td_len, seq, seq_errors, lost, test_chan, res; + struct sockaddr_in test_chan_slave, test_chan_master; + struct nc_test_results results; + struct nc_test_data *tdp; + int nsent, nrecvd; + int need_recv, need_send; + int one = 1; + static int slave_tcp_port = -1; + + need_recv = true; need_send = true; + switch (ntohl(req->type)) { + case NC_REQUEST_TCP_SEND: + need_recv = false; + need_send = true; + break; + case NC_REQUEST_TCP_RECV: + need_recv = true; + need_send = false; + break; + case NC_REQUEST_TCP_ECHO: + break; + } + + if (slave_tcp_port < 0) { + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + pexit("datagram socket"); + } + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { + perror("setsockopt SO_REUSEADDR"); + return; + } +#ifdef SO_REUSEPORT + if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) { + perror("setsockopt SO_REUSEPORT"); + return; + } +#endif + memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave)); + test_chan_slave.sin_family = AF_INET; +#ifdef __ECOS + test_chan_slave.sin_len = sizeof(test_chan_slave); +#endif + test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY); + test_chan_slave.sin_port = htons(ntohl(req->slave_port)); + + if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) { + perror("bind"); + close(s); + } + listen(s, SOMAXCONN); + slave_tcp_port = s; + } + + s = slave_tcp_port; + len = sizeof(test_chan_master); + if ((test_chan = accept(s, (struct sockaddr *)&test_chan_master, &len)) < 0) { + pexit("accept"); + } + len = sizeof(test_chan_master); + getpeername(test_chan, (struct sockaddr *)&test_chan_master, &len); + test_printf("connection from %s.%d\n", inet_ntoa(test_chan_master.sin_addr), + ntohs(test_chan_master.sin_port)); + + nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0; + for (i = 0; i < ntohl(req->nbufs); i++) { + if (need_recv) { + tdp = (struct nc_test_data *)in_buf; + td_len = ntohl(req->buflen) + sizeof(struct nc_test_data); + res = do_read(test_chan, tdp, td_len); + if (res != td_len) { + test_printf("recvfrom timeout, expecting seq #%d\n", seq); + if (++lost > MAX_ERRORS) { + test_printf("... giving up\n"); + break; + } + } else { + nrecvd++; + if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) && + (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) { + if (ntohl(tdp->seq) != seq) { + test_printf("Packets out of sequence - recvd: %d, expected: %d\n", + ntohl(tdp->seq), seq); + seq = ntohl(tdp->seq); + seq_errors++; + } + } else { + test_printf("Bad data packet - key: %x/%x, seq: %d\n", + ntohl(tdp->key1), ntohl(tdp->key2), + ntohl(tdp->seq)); + } + } + } + if (need_send) { + tdp = (struct nc_test_data *)out_buf; + tdp->key1 = htonl(NC_TEST_DATA_KEY1); + tdp->key2 = htonl(NC_TEST_DATA_KEY2); + tdp->seq = htonl(seq); + td_len = ntohl(req->buflen) + sizeof(struct nc_test_data); + tdp->len = htonl(td_len); + if (write(test_chan, tdp, td_len) != td_len) { + perror("write"); + if (errno == ENOBUFS) { + // Saturated the system + test_delay(25); + } else { + // What else to do? + close(test_chan); + return; + } + } else { + nsent++; + } + } + seq++; + } + results.key1 = htonl(NC_TEST_RESULT_KEY1); + results.key2 = htonl(NC_TEST_RESULT_KEY2); + results.seq = req->seq; + results.nsent = htonl(nsent); + results.nrecvd = htonl(nrecvd); + if (write(test_chan, &results, sizeof(results)) != sizeof(results)) { + perror("write"); + } + close(test_chan); +} + +// +// Protocol driver for testing slave. +// +// This function is the main routine running here, handling requests sent from +// the master and providing various responses. +// +static void +nc_slave(test_param_t param) +{ + int s, masterlen; + struct sockaddr_in my_addr, master; + struct nc_request req; + struct nc_reply reply; + int done = false; + + test_printf("Start test for eth%d\n", param); + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + pexit("datagram socket"); + } + + memset((char *) &my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; +#ifdef __ECOS + my_addr.sin_len = sizeof(my_addr); +#endif + my_addr.sin_addr.s_addr = htonl(INADDR_ANY); + my_addr.sin_port = htons(NC_SLAVE_PORT); + + if (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) { + pexit("bind"); + } + + while (!done) { + masterlen = sizeof(master); + if (recvfrom(s, &req, sizeof(req), 0, (struct sockaddr *)&master, &masterlen) < 0) { + pexit("recvfrom"); + } +#if 0 + test_printf("Request %d from %s:%d\n", ntohl(req.type), + inet_ntoa(master.sin_addr), ntohs(master.sin_port)); +#endif + reply.response = htonl(NC_REPLY_ACK); + reply.seq = req.seq; + switch (ntohl(req.type)) { + case NC_REQUEST_DISCONNECT: + done = true; + break; + case NC_REQUEST_UDP_SEND: + test_printf("UDP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + break; + case NC_REQUEST_UDP_RECV: + test_printf("UDP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + break; + case NC_REQUEST_UDP_ECHO: + test_printf("UDP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + break; + case NC_REQUEST_TCP_SEND: + test_printf("TCP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + break; + case NC_REQUEST_TCP_RECV: + test_printf("TCP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + break; + case NC_REQUEST_TCP_ECHO: + test_printf("TCP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen)); + break; +#ifdef __ECOS + case NC_REQUEST_SET_LOAD: + start_load(ntohl(req.nbufs)); + break; + case NC_REQUEST_START_IDLE: + test_printf("Start IDLE thread\n"); + idle_thread_count = 0; + idle_thread_start_time = cyg_current_time(); + cyg_semaphore_post(&idle_thread_sem); + break; + case NC_REQUEST_STOP_IDLE: + cyg_semaphore_wait(&idle_thread_sem); + idle_thread_stop_time = cyg_current_time(); + test_printf("Stop IDLE thread\n"); + reply.misc.idle_results.elapsed_time = htonl(idle_thread_stop_time - idle_thread_start_time); + reply.misc.idle_results.count[0] = htonl(idle_thread_count >> 32); + reply.misc.idle_results.count[1] = htonl((long)idle_thread_count); + break; +#endif + default: + test_printf("Unrecognized request: %d\n", ntohl(req.type)); + reply.response = htonl(NC_REPLY_NAK); + reply.reason = htonl(NC_REPLY_NAK_UNKNOWN_REQUEST); + break; + } + if (sendto(s, &reply, sizeof(reply), 0, (struct sockaddr *)&master, masterlen) < 0) { + pexit("sendto"); + } + if (reply.response == ntohl(NC_REPLY_NAK)) { + continue; + } + switch (ntohl(req.type)) { + case NC_REQUEST_UDP_SEND: + case NC_REQUEST_UDP_RECV: + case NC_REQUEST_UDP_ECHO: + do_udp_test(s, &req, &master); + break; + case NC_REQUEST_TCP_SEND: + case NC_REQUEST_TCP_RECV: + case NC_REQUEST_TCP_ECHO: + do_tcp_test(s, &req, &master); + break; + case NC_REQUEST_START_IDLE: + case NC_REQUEST_STOP_IDLE: + case NC_REQUEST_SET_LOAD: + default: + break; + } + } + close(s); +} + +void +net_test(test_param_t param) +{ +#ifdef __ECOS + cyg_serial_baud_rate_t old; + cyg_ppp_options_t options; + cyg_ppp_handle_t ppp_handle; + + CYG_TEST_INIT(); +#endif + +// int i; + if (param == 0) { + test_printf("Start Network Characterization - SLAVE\n"); +#ifdef __ECOS + init_all_network_interfaces(); + calibrate_load(DESIRED_BACKGROUND_LOAD); +#if 0 +// I can see what this is trying to do, but I get "bind: Address already in +// use" errors from the 2nd interface - and the parameter is not used +// anyway, so one thread does quite well enough (but only tests one i/f at +// once). + +// Comment in the 'int i' above too. + for (i = 1; i < CYGHWR_NET_DRIVERS; i++) { + cyg_thread_resume(main_thread_handle[i]); // Start other threads + } +#endif +#endif + } + +#ifdef __ECOS + + old = ppp_test_set_baud( CYGNUM_SERIAL_BAUD_115200 ); + + ppp_test_announce( "NC_TEST_SLAVE" ); + + cyg_ppp_options_init( &options ); + +// options.debug = 1; +// options.kdebugflag = 1; + +// options.script = script; +// options.flowctl = CYG_PPP_FLOWCTL_SOFTWARE; + + ppp_handle = cyg_ppp_up( CYGPKG_PPP_TEST_DEVICE, &options ); + + CYG_TEST_INFO( "Waiting for PPP to come up"); + + cyg_ppp_wait_up( ppp_handle ); +#endif + + nc_slave(param); +#ifdef CYGDBG_NET_TIMING_STATS + show_net_times(); +#endif + +#ifdef __ECOS + CYG_TEST_INFO( "Bringing PPP down"); + + cyg_ppp_down( ppp_handle ); + + CYG_TEST_INFO( "Waiting for PPP to go down"); + + cyg_ppp_wait_down( ppp_handle ); + + cyg_thread_delay( 200 ); + + ppp_test_set_baud( old ); + + ppp_test_finish(); + + CYG_TEST_PASS_FINISH( "Network Characterization - SLAVE" ); + +#endif + + cyg_test_exit(); +} + +#ifdef __ECOS + +// +// This function is called to calibrate the "background load" which can be +// applied during testing. It will be called before any commands from the +// host are managed. +// +static void +calibrate_load(int desired_load) +{ + long long no_load_idle, load_idle; + int percent_load; + int high, low; + + // Set limits + high = MAX_LOAD_THREAD_LEVEL; + low = MIN_LOAD_THREAD_LEVEL; + + // Compute the "no load" idle value + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + cyg_thread_delay(1*100); // Pause for one second + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + no_load_idle = idle_thread_count; + diag_printf("No load = %d\n", (int)idle_thread_count); + + // First ensure that the HIGH level is indeed higher + while (true) { + load_thread_level = high; + start_load(desired_load); // Start up a given load + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + cyg_thread_delay(1*100); // Pause for one second + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + load_idle = idle_thread_count; + start_load(0); // Shut down background load + percent_load = 100 - ((load_idle * 100) / no_load_idle); + diag_printf("High Load[%d] = %d => %d%%\n", load_thread_level, + (int)idle_thread_count, percent_load); + if ( percent_load > desired_load ) + break; // HIGH level is indeed higher + low = load_thread_level; // known to be lower + high *= 2; // else double it and try again + } + + // Now chop down to the level required + while (true) { + load_thread_level = (high + low) / 2; + start_load(desired_load); // Start up a given load + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + cyg_thread_delay(1*100); // Pause for one second + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + load_idle = idle_thread_count; + start_load(0); // Shut down background load + percent_load = 100 - ((load_idle * 100) / no_load_idle); + diag_printf("Load[%d] = %d => %d%%\n", load_thread_level, + (int)idle_thread_count, percent_load); + if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break; + if (percent_load < desired_load) { + low = load_thread_level; + } else { + high = load_thread_level; + } + } + + // Now we are within a few percent of the target; scale the load + // factor to get a better fit, and test it, print the answer. + load_thread_level *= desired_load; + load_thread_level /= percent_load; + start_load(desired_load); // Start up a given load + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + cyg_thread_delay(1*100); // Pause for one second + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + load_idle = idle_thread_count; + start_load(0); // Shut down background load + percent_load = 100 - ((load_idle * 100) / no_load_idle); + diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level, + (int)idle_thread_count, percent_load); +// no_load_idle_count_1_second = no_load_idle; +} + +// +// This function is called to set up a load level of 'load' percent (given +// as a whole number, e.g. start_load(20) would mean initiate a background +// load of 20%, leaving the cpu 80% idle). +// +static void +start_load(int load) +{ + static int prev_load = 0; + int i; + test_printf("Set background load = %d%%\n", load); + if (load == 0) { + if (prev_load == 0) return; // Nothing out there to stop + for (i = 0; i < prev_load/10; i++) { + cyg_semaphore_wait(&load_thread_sem[i]); + } + prev_load = 0; + } else { + for (i = 0; i < load/10; i++) { + cyg_semaphore_post(&load_thread_sem[i]); + } + prev_load = load; + } +} + +// +// These thread(s) do some amount of "background" computing. This is used +// to simulate a given load level. They need to be run at a higher priority +// than the network code itself. +// +// Like the "idle" thread, they run as long as their "switch" (aka semaphore) +// is enabled. +// +void +net_load(cyg_addrword_t who) +{ + int i; + while (true) { + cyg_semaphore_wait(&load_thread_sem[who]); + for (i = 0; i < load_thread_level; i++) { + do_some_random_computation(i); + } + cyg_thread_delay(1); // Wait until the next 'tick' + cyg_semaphore_post(&load_thread_sem[who]); + } +} + +// +// Some arbitrary computation, designed to use up the CPU and cause associated +// cache "thrash" behaviour - part of background load modelling. +// +static void +do_some_random_computation(int p) +{ + // Just something that might be "hard" + volatile double x; + x = ((p * 10) * 3.14159) / 180.0; // radians +} + +// +// This thread does nothing but count. It will be allowed to count +// as long as the semaphore is "free". +// +void +net_idle(cyg_addrword_t param) +{ + while (true) { + cyg_semaphore_wait(&idle_thread_sem); + idle_thread_count++; + cyg_semaphore_post(&idle_thread_sem); + } +} + +void +cyg_start(void) +{ + int i; + // Create processing threads + for (i = 0; i < CYGHWR_NET_DRIVERS; i++) { + cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority + net_test, // entry + i, // entry parameter + "Network test", // Name + &main_thread_stack[i][0], // Stack + STACK_SIZE, // Size + &main_thread_handle[i], // Handle + &main_thread_data[i] // Thread data structure + ); + } + cyg_thread_resume(main_thread_handle[0]); // Start first one + // Create the idle thread environment + cyg_semaphore_init(&idle_thread_sem, 0); + cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority + net_idle, // entry + 0, // entry parameter + "Network idle", // Name + &idle_thread_stack[0], // Stack + STACK_SIZE, // Size + &idle_thread_handle, // Handle + &idle_thread_data // Thread data structure + ); + cyg_thread_resume(idle_thread_handle); // Start it + // Create the load threads and their environment(s) + for (i = 0; i < NUM_LOAD_THREADS; i++) { + cyg_semaphore_init(&load_thread_sem[i], 0); + cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority + net_load, // entry + i, // entry parameter + "Background load", // Name + &load_thread_stack[i][0], // Stack + STACK_SIZE, // Size + &load_thread_handle[i], // Handle + &load_thread_data[i] // Thread data structure + ); + cyg_thread_resume(load_thread_handle[i]); // Start it + } + cyg_scheduler_start(); +} + +#else + +int +main(int argc, char *argv[]) +{ + net_test(0); +} +#endif diff --git a/packages/net/ppp/current/tests/ppp_auth.c b/packages/net/ppp/current/tests/ppp_auth.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/ppp_auth.c @@ -0,0 +1,260 @@ +//========================================================================== +// +// tests/ppp_auth.c +// +// Simple test of PPP authentication support +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2003-06-01 +// Purpose: +// Description: This test just brings the PPP link up and waits for the +// other end to bring it down. Meanwhile the other end can +// ping us, or talk to the HTTPD if it is configured. +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// PPP test code + +#include "ppp_test_support.inl" + +//========================================================================== + +typedef void pr_fun(char *fmt, ...); + +externC void show_network_tables(pr_fun *pr); + +//========================================================================== + + +#ifndef CYGPKG_LIBC_STDIO +#define perror(s) diag_printf(#s ": %s\n", strerror(errno)) +#endif + +#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000) +static char stack[STACK_SIZE]; +static cyg_thread thread_data; +static cyg_handle_t thread_handle; + +//========================================================================== +// Tests +// +// The following table contains parameters for various tests permuting +// the authentication requirements of the peer, the authentication +// requirements of this end and the user name and password used. +// +// For this test to work the /etc/ppp/pap-secrets file on the remote +// end should contain the following two lines: +// +// eCos * secret * +// eCosPAP * secretPAP * +// +// The /etc/ppp/chap-secrets file should contain: +// +// eCos * secret * +// eCosCHAP * secretCHAP * +// + +static struct auth_info +{ + char *name; + cyg_serial_baud_rate_t baud; + int refuse_pap; + int refuse_chap; + char user[MAXNAMELEN]; + char passwd[MAXSECRETLEN]; + cyg_int32 result; +} tests[] = +{ + // PPP_AUTH_1 just tries the default auth mechanism for the server + { "PPP_AUTH_1", CYGNUM_SERIAL_BAUD_115200, 0, 0, "eCos", "secret", 0 }, + { "PPP_AUTH_1", CYGNUM_SERIAL_BAUD_115200, 0, 0, "eCos", "secretX", -1 }, + { "PPP_AUTH_1", CYGNUM_SERIAL_BAUD_115200, 0, 0, "eCosX", "secret", -1 }, + + // PPP_AUTH_2 requires PAP authentication only + { "PPP_AUTH_2", CYGNUM_SERIAL_BAUD_115200, 1, 0, "eCosCHAP", "secretCHAP", -1 }, + { "PPP_AUTH_2", CYGNUM_SERIAL_BAUD_115200, 1, 0, "eCosPAP", "secretPAP", -1 }, + { "PPP_AUTH_2", CYGNUM_SERIAL_BAUD_115200, 1, 0, "eCos", "secret", -1 }, + { "PPP_AUTH_2", CYGNUM_SERIAL_BAUD_115200, 0, 1, "eCosCHAP", "secretCHAP", -1 }, + { "PPP_AUTH_2", CYGNUM_SERIAL_BAUD_115200, 0, 1, "eCosPAP", "secretPAP", 0 }, + { "PPP_AUTH_2", CYGNUM_SERIAL_BAUD_115200, 0, 1, "eCos", "secret", 0 }, + + // PPP_AUTH_3 requires CHAP authentication only + { "PPP_AUTH_3", CYGNUM_SERIAL_BAUD_115200, 0, 1, "eCosPAP", "secretPAP", -1 }, + { "PPP_AUTH_3", CYGNUM_SERIAL_BAUD_115200, 0, 1, "eCosCHAP", "secretCHAP", -1 }, + { "PPP_AUTH_3", CYGNUM_SERIAL_BAUD_115200, 0, 1, "eCos", "secret", -1 }, + { "PPP_AUTH_3", CYGNUM_SERIAL_BAUD_115200, 1, 0, "eCosPAP", "secretPAP", -1 }, + { "PPP_AUTH_3", CYGNUM_SERIAL_BAUD_115200, 1, 0, "eCosCHAP", "secretCHAP", 0 }, + { "PPP_AUTH_3", CYGNUM_SERIAL_BAUD_115200, 1, 0, "eCos", "secret", 0 }, + + { NULL } +}; + +//========================================================================== + + +static int do_test( struct auth_info *test ) +{ + cyg_ppp_options_t options; + cyg_ppp_handle_t ppp_handle; + int i = 0; + cyg_int32 result; + + CYG_TEST_INFO( test->name ); + + ppp_test_set_baud( test->baud ); + + ppp_test_announce( test->name ); + + cyg_ppp_options_init( &options ); + +// options.debug = 1; +// options.kdebugflag = 1; + + options.baud = test->baud; + options.refuse_pap = test->refuse_pap; + options.refuse_chap = test->refuse_chap; + strncpy( options.user, test->user, MAXNAMELEN ); + strncpy( options.passwd, test->passwd, MAXNAMELEN ); + +// show_network_tables( diag_printf ); + + ppp_handle = cyg_ppp_up( CYGPKG_PPP_TEST_DEVICE, &options ); + + CYG_TEST_INFO( "Waiting for PPP to come up"); + + result = cyg_ppp_wait_up( ppp_handle ); + + if( result != test->result ) + { + CYG_TEST_INFO( "Failed" ); + + return 0; + } + else + { + CYG_TEST_INFO( "Waiting until remote end goes down"); + + while( cyg_ppp_wait_up( ppp_handle ) == 0 ) + { + i++; + +// if( (i % 60) == 0 ) +// show_network_tables( diag_printf ); + + cyg_thread_delay(100); + } + + cyg_ppp_wait_down( ppp_handle ); + + return 1; + } + +} + +//========================================================================== + +void +ppp_test(cyg_addrword_t p) +{ + cyg_serial_baud_rate_t old; + struct auth_info *test; + int failures = 0; + + CYG_TEST_INIT(); + diag_printf("Start PPP test\n"); + + init_all_network_interfaces(); + + old = ppp_test_set_baud( CYGNUM_SERIAL_BAUD_38400 ); + + + for( test = &tests[0]; test->name != NULL; test++ ) + { + if( !do_test( test ) ) + failures++; + + cyg_thread_delay(300); + } + + ppp_test_set_baud( old ); + + ppp_test_finish(); + + if( failures > 0 ) + CYG_TEST_FAIL("Some tests failed"); + else + CYG_TEST_PASS("OK"); + + CYG_TEST_FINISH("PPP auth test"); +} + +//========================================================================== + +void +cyg_start(void) +{ + // Create a main thread, so we can run the scheduler and have time 'pass' + cyg_thread_create(10, // Priority - just a number + ppp_test, // entry + 0, // entry parameter + "PPP test", // Name + &stack[0], // Stack + STACK_SIZE, // Size + &thread_handle, // Handle + &thread_data // Thread data structure + ); + cyg_thread_resume(thread_handle); // Start it + cyg_scheduler_start(); +} + +//========================================================================== diff --git a/packages/net/ppp/current/tests/ppp_test_support.inl b/packages/net/ppp/current/tests/ppp_test_support.inl new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/ppp_test_support.inl @@ -0,0 +1,225 @@ +//========================================================================== +// +// tests/ppp_test_support.c +// +// PPP testing support routines +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2003-07-01 +// Purpose: +// Description: This file contains routines that are used by the PPP tests. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// PPP test support + +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +//========================================================================== + +static cyg_serial_baud_rate_t ppp_test_set_baud( cyg_serial_baud_rate_t baud ) +{ +#ifdef CYGPKG_PPP_TESTS_AUTOMATE + cyg_io_handle_t handle; + cyg_serial_info_t cfg; + int err; + int len = sizeof(cfg); + char buf[30]; + int blen; + cyg_serial_baud_rate_t old_baud; + + + CYG_TEST_INFO( "Switching baud rate" ); + + err = cyg_io_lookup( CYGPKG_PPP_TEST_DEVICE, &handle ); + + if( err != 0 ) { + CYG_TEST_INFO("cyg_io_lookup: failed"); + } + + sprintf( buf, "BAUD:%03d\r\n", baud ); + blen = strlen(buf); + + err = cyg_io_write( handle, buf, &blen ); + + if( err != 0 ) { + CYG_TEST_INFO("cyg_io_write: failed"); + } + + // Wait for those characters to reach the remote end + cyg_thread_delay(200); + + err = cyg_io_get_config( handle, + CYG_IO_GET_CONFIG_SERIAL_INFO, + &cfg, + &len); + + if( err != 0 ) { + CYG_TEST_INFO("cyg_io_get_config: failed"); + } + + cfg.flags |= CYGNUM_SERIAL_FLOW_RTSCTS_RX|CYGNUM_SERIAL_FLOW_RTSCTS_TX; + + old_baud = cfg.baud; + cfg.baud = baud; + + err = cyg_io_set_config( handle, + CYG_IO_SET_CONFIG_SERIAL_INFO, + &cfg, + &len); + + if( err != 0 ) { + CYG_TEST_INFO("cyg_io_set_config failed"); + } + + // Wait for other end to switch... + cyg_thread_delay(100); + + CYG_TEST_INFO( "Baud rate switched" ); + + return old_baud; + +#else + return 0; +#endif + + +} + +//========================================================================== + +static void ppp_test_announce( char *buf ) +{ +#ifdef CYGPKG_PPP_TESTS_AUTOMATE + cyg_io_handle_t handle; + int err; + int blen; + +// cyg_thread_delay(200); + + CYG_TEST_INFO( "Sending announce string" ); + + err = cyg_io_lookup( CYGPKG_PPP_TEST_DEVICE, &handle ); + + if( err != 0 ) { + CYG_TEST_INFO("cyg_io_lookup: failed"); + } + + blen = strlen(buf); + + err = cyg_io_write( handle, buf, &blen ); + + if( err != 0 ) { + CYG_TEST_INFO("cyg_io_write: failed"); + } + + blen = 2; + err = cyg_io_write( handle, "\r\n", &blen ); + + if( err != 0 ) { + CYG_TEST_INFO("cyg_io_write: failed"); + } + + cyg_thread_delay(200); + + CYG_TEST_INFO( "Announce string sent" ); +#endif +} + +//========================================================================== + +static void ppp_test_finish(void) +{ +#ifdef CYGPKG_PPP_TESTS_AUTOMATE + cyg_io_handle_t handle; + int err; + char *buf = "FINISH\r\n"; + int blen; + +// cyg_thread_delay(200); + + CYG_TEST_INFO( "Sending FINISH" ); + + err = cyg_io_lookup( CYGPKG_PPP_TEST_DEVICE, &handle ); + + if( err != 0 ) { + CYG_TEST_INFO("cyg_io_lookup: failed"); + } + + blen = strlen(buf); + + err = cyg_io_write( handle, buf, &blen ); + + if( err != 0 ) { + CYG_TEST_INFO("cyg_io_write: failed"); + } + + cyg_thread_delay(200); + + CYG_TEST_INFO( "FINISH sent" ); +#endif +} + +//========================================================================== diff --git a/packages/net/ppp/current/tests/ppp_up.c b/packages/net/ppp/current/tests/ppp_up.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/ppp_up.c @@ -0,0 +1,190 @@ +//========================================================================== +// +// tests/ppp_up.c +// +// Simple test of PPP and networking support +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2003-06-01 +// Purpose: +// Description: This test just brings the PPP link up and waits for the +// other end to bring it down. Meanwhile the other end can +// ping us, or talk to the HTTPD if it is configured. +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// PPP test code + +#include "ppp_test_support.inl" + +//========================================================================== + +typedef void pr_fun(char *fmt, ...); + +externC void show_network_tables(pr_fun *pr); + +//========================================================================== + + +#ifndef CYGPKG_LIBC_STDIO +#define perror(s) diag_printf(#s ": %s\n", strerror(errno)) +#endif + +#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000) +static char stack[STACK_SIZE]; +static cyg_thread thread_data; +static cyg_handle_t thread_handle; + +//========================================================================== + +static void do_test( cyg_serial_baud_rate_t baud ) +{ + cyg_ppp_options_t options; + cyg_ppp_handle_t ppp_handle; + int i = 0; + + ppp_test_announce( "PPP_UP" ); + + cyg_ppp_options_init( &options ); + +// options.debug = 1; +// options.kdebugflag = 1; + + options.baud = baud; + + show_network_tables( diag_printf ); + + ppp_handle = cyg_ppp_up( CYGPKG_PPP_TEST_DEVICE, &options ); + + CYG_TEST_INFO( "Waiting for PPP to come up"); + + cyg_ppp_wait_up( ppp_handle ); + + CYG_TEST_INFO( "Waiting until remote end goes down"); + + while( cyg_ppp_wait_up( ppp_handle ) == 0 ) + { + i++; + + if( (i % 60) == 0 ) + show_network_tables( diag_printf ); + + cyg_thread_delay(100); + } + + cyg_ppp_wait_down( ppp_handle ); +} + +//========================================================================== + +void +ppp_test(cyg_addrword_t p) +{ + cyg_serial_baud_rate_t old; + + CYG_TEST_INIT(); + diag_printf("Start PPP test\n"); + + init_all_network_interfaces(); + +// old = ppp_test_set_baud( CYGNUM_SERIAL_BAUD_115200 ); +// do_test( CYGNUM_SERIAL_BAUD_115200 ); + + old = ppp_test_set_baud( CYGNUM_SERIAL_BAUD_19200 ); + do_test( CYGNUM_SERIAL_BAUD_19200 ); + +#ifdef CYGPKG_PPP_TESTS_AUTOMATE + + { + static cyg_serial_baud_rate_t test_rates[] = + { CYGDAT_PPP_TEST_BAUD_RATES, 0 }; + + int i; + + for( i = 0; test_rates[i] != 0; i++ ) + { + ppp_test_set_baud( test_rates[i] ); + do_test( test_rates[i] ); + } + + ppp_test_set_baud( old ); + + ppp_test_finish(); + + } + +#endif + + CYG_TEST_PASS_FINISH("PPP test OK"); +} + +//========================================================================== + +void +cyg_start(void) +{ + // Create a main thread, so we can run the scheduler and have time 'pass' + cyg_thread_create(10, // Priority - just a number + ppp_test, // entry + 0, // entry parameter + "PPP test", // Name + &stack[0], // Stack + STACK_SIZE, // Size + &thread_handle, // Handle + &thread_data // Thread data structure + ); + cyg_thread_resume(thread_handle); // Start it + cyg_scheduler_start(); +} + +//========================================================================== diff --git a/packages/net/ppp/current/tests/ppp_updown.c b/packages/net/ppp/current/tests/ppp_updown.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/ppp_updown.c @@ -0,0 +1,407 @@ +//========================================================================== +// +// tests/ppp_updown.c +// +// Simple test of PPP and networking support +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: gthomas (ping code), nickg +// Date: 2003-06-01 +// Purpose: +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// PPP test code + +#include + +#include +#include + +#include "ppp_test_support.inl" + +//========================================================================== + +typedef void pr_fun(char *fmt, ...); + +externC void show_network_tables(pr_fun *pr); + +//========================================================================== + +#include + +// Fill in the blanks if necessary +#ifndef TNR_OFF +# define TNR_OFF() +#endif +#ifndef TNR_ON +# define TNR_ON() +#endif +#ifndef TNR_INIT +# define TNR_INIT() +#endif +#ifndef TNR_PRINT_ACTIVITY +# define TNR_PRINT_ACTIVITY() +#endif + +#define NUM_PINGS 16 +#define MAX_PACKET 4096 +#define MIN_PACKET 64 +#define MAX_SEND 4000 + +#define PACKET_ADD ((MAX_SEND - MIN_PACKET)/NUM_PINGS) +#define nPACKET_ADD 1 + +static unsigned char pkt1[MAX_PACKET], pkt2[MAX_PACKET]; + +#define UNIQUEID 0x1234 + +void +pexit(char *s) +{ + CYG_TEST_FAIL_FINISH(s); +} + +// Compute INET checksum +int +inet_cksum(u_short *addr, int len) +{ + register int nleft = len; + register u_short *w = addr; + register u_short answer; + register u_int sum = 0; + u_short odd_byte = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while( nleft > 1 ) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if( nleft == 1 ) { + *(u_char *)(&odd_byte) = *(u_char *)w; + sum += odd_byte; + } + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0x0000ffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +static int +show_icmp(unsigned char *pkt, int len, + struct sockaddr_in *from, struct sockaddr_in *to) +{ +#if 1 + cyg_tick_count_t *tp, tv; + struct ip *ip; + struct icmp *icmp; + tv = cyg_current_time(); + ip = (struct ip *)pkt; + if ((len < sizeof(*ip)) || ip->ip_v != IPVERSION) { + diag_printf("%s: Short packet or not IP! - Len: %d, Version: %d\n", + inet_ntoa(from->sin_addr), len, ip->ip_v); + return 0; + } + icmp = (struct icmp *)(pkt + sizeof(*ip)); + len -= (sizeof(*ip) + 8); + tp = (cyg_tick_count_t *)&icmp->icmp_data; + if (icmp->icmp_type != ICMP_ECHOREPLY) { + diag_printf("%s: Invalid ICMP - type: %d\n", + inet_ntoa(from->sin_addr), icmp->icmp_type); + return 0; + } + if (icmp->icmp_id != UNIQUEID) { + diag_printf("%s: ICMP received for wrong id - sent: %x, recvd: %x\n", + inet_ntoa(from->sin_addr), UNIQUEID, icmp->icmp_id); + } + diag_printf("%d bytes from %s: ", len, inet_ntoa(from->sin_addr)); + diag_printf("icmp_seq=%d", icmp->icmp_seq); + diag_printf(", time=%dms\n", (int)(tv - *tp)*10); + return (from->sin_addr.s_addr == to->sin_addr.s_addr); +#else + return 1; +#endif +} + +static void +ping_host(int s, struct sockaddr_in *host) +{ + struct icmp *icmp = (struct icmp *)pkt1; + int icmp_len = MIN_PACKET; + int seq, ok_recv, bogus_recv; + cyg_tick_count_t *tp; + long *dp; + struct sockaddr_in from; + int i, len, fromlen; + + ok_recv = 0; + bogus_recv = 0; + diag_printf("PING server %s\n", inet_ntoa(host->sin_addr)); + for (seq = 0; seq < NUM_PINGS; seq++, icmp_len += PACKET_ADD ) { + TNR_ON(); + // Build ICMP packet + icmp->icmp_type = ICMP_ECHO; + icmp->icmp_code = 0; + icmp->icmp_cksum = 0; + icmp->icmp_seq = seq; + icmp->icmp_id = 0x1234; + // Set up ping data + tp = (cyg_tick_count_t *)&icmp->icmp_data; + *tp++ = cyg_current_time(); + dp = (long *)tp; + for (i = sizeof(*tp); i < icmp_len; i += sizeof(*dp)) { + *dp++ = i; + } + // Add checksum + icmp->icmp_cksum = inet_cksum( (u_short *)icmp, icmp_len+8); + // Send it off + if (sendto(s, icmp, icmp_len+8, 0, (struct sockaddr *)host, sizeof(*host)) < 0) { + TNR_OFF(); + perror("sendto"); + continue; + } + // Wait for a response + fromlen = sizeof(from); + len = recvfrom(s, pkt2, sizeof(pkt2), 0, (struct sockaddr *)&from, &fromlen); + TNR_OFF(); + if (len < 0) { + perror("recvfrom"); + icmp_len = MIN_PACKET - PACKET_ADD; // just in case - long routes + } else { + if (show_icmp(pkt2, len, &from, host)) { + ok_recv++; + } else { + bogus_recv++; + } + } + } + TNR_OFF(); + diag_printf("Sent %d packets, received %d OK, %d bad\n", NUM_PINGS, ok_recv, bogus_recv); +} + +static void do_ping(void) +{ + struct protoent *p; + struct timeval tv; + struct sockaddr_in host; + int s; + + if ((p = getprotobyname("icmp")) == (struct protoent *)0) { + pexit("getprotobyname"); + return; + } + s = socket(AF_INET, SOCK_RAW, p->p_proto); + if (s < 0) { + pexit("socket"); + return; + } + tv.tv_sec = 7; + tv.tv_usec = 0; + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + // Set up host address + host.sin_family = AF_INET; + host.sin_len = sizeof(host); + host.sin_port = 0; + + // Set a default remote end address + inet_aton("10.0.0.100", &host.sin_addr); + + // Now quiz the ppp0 interface for its destination address and use + // that as our ping-buddy. + { + struct ifreq ifr; + int sock = socket(AF_INET, SOCK_DGRAM, 0); + + if( sock != -1 ) + { + strncpy( ifr.ifr_name, "ppp0", sizeof(ifr.ifr_name)); + if( ioctl( sock, SIOCGIFDSTADDR, &ifr ) == 0 ) + host.sin_addr = ((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr; + + close(sock); + } + } + + ping_host(s, &host); + +} + +//========================================================================== + +#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000) +static char stack[STACK_SIZE]; +static cyg_thread thread_data; +static cyg_handle_t thread_handle; + + +//========================================================================== + +static void do_test( cyg_serial_baud_rate_t baud ) +{ + cyg_ppp_options_t options; + cyg_ppp_handle_t ppp_handle; + + ppp_test_announce( "PPP_UPDOWN" ); + + cyg_ppp_options_init( &options ); + +// options.debug = 1; +// options.kdebugflag = 1; + + options.baud = baud; + + ppp_handle = cyg_ppp_up( CYGPKG_PPP_TEST_DEVICE, &options ); + + CYG_TEST_INFO( "Waiting for PPP to come up"); + + cyg_ppp_wait_up( ppp_handle ); + +// show_network_tables( diag_printf ); + + CYG_TEST_INFO( "Delaying..."); + + cyg_thread_delay(10*100); + + CYG_TEST_INFO( "Pinging remote"); + + do_ping(); + +// show_network_tables( diag_printf ); + + CYG_TEST_INFO( "Bringing PPP down"); + + cyg_ppp_down( ppp_handle ); + + CYG_TEST_INFO( "Waiting for PPP to go down"); + + cyg_ppp_wait_down( ppp_handle ); +} + +//========================================================================== + +void +ppp_test(cyg_addrword_t p) +{ + cyg_serial_baud_rate_t old; + + CYG_TEST_INIT(); + diag_printf("Start PPP test\n"); + + init_all_network_interfaces(); + + old = ppp_test_set_baud( CYGNUM_SERIAL_BAUD_115200 ); + do_test( CYGNUM_SERIAL_BAUD_115200 ); + +#ifdef CYGPKG_PPP_TESTS_AUTOMATE + + CYG_TEST_INFO( "Delaying..."); + cyg_thread_delay(10*100); + + { + static cyg_serial_baud_rate_t test_rates[] = + { CYGDAT_PPP_TEST_BAUD_RATES, 0 }; + + int i; + + for( i = 0; test_rates[i] != 0; i++ ) + { + ppp_test_set_baud( test_rates[i] ); + do_test( test_rates[i] ); + + CYG_TEST_INFO( "Delaying..."); + cyg_thread_delay(10*100); + + } + + ppp_test_set_baud( old ); + + ppp_test_finish(); + + } + +#endif + + CYG_TEST_PASS_FINISH("PPP test OK"); +} + +//========================================================================== + +void +cyg_start(void) +{ + // Create a main thread, so we can run the scheduler and have time 'pass' + cyg_thread_create(10, // Priority - just a number + ppp_test, // entry + 0, // entry parameter + "PPP test", // Name + &stack[0], // Stack + STACK_SIZE, // Size + &thread_handle, // Handle + &thread_data // Thread data structure + ); + cyg_thread_resume(thread_handle); // Start it + cyg_scheduler_start(); +} + +//========================================================================== diff --git a/packages/net/ppp/current/tests/tcp_echo.c b/packages/net/ppp/current/tests/tcp_echo.c new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/tcp_echo.c @@ -0,0 +1,642 @@ +//========================================================================== +// +// tests/tcp_echo.c +// +// Simple TCP throughput test - echo component +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Portions created by Nick Garnett are +// Copyright (C) 2003 eCosCentric Ltd. +// +// eCos 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 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD, +// FreeBSD or other sources, and are covered by the appropriate +// copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas, nickg +// Contributors: gthomas, nickg +// Date: 2000-01-10 +// Purpose: +// Description: This is the middle part of a three part test. The idea is +// to test the throughput of box in a configuration like this: +// +// +------+ port +----+ port +----+ +// |SOURCE|=========>|ECHO|============>|SINK| +// +------+ 9990 +----+ 9991 +----+ +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include + +#include +#include + +#include + +#include + +#include "ppp_test_support.inl" + + +// Network throughput test code + +#include + +static __inline__ unsigned int +max(unsigned int m, unsigned int n) +{ + return m > n ? m : n; +} + +#define SOURCE_PORT 9990 +#define SINK_PORT 9991 + +#define MAX_BUF 8192 +static unsigned char data_buf[MAX_BUF]; + +struct test_params { + long nbufs; + long bufsize; + long load; +}; + +struct test_status { + long ok; +}; + +#ifndef CYGPKG_LIBC_STDIO +#define perror(s) diag_printf(#s ": %s\n", strerror(errno)) +#endif + +#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000) +static char stack[STACK_SIZE]; +static cyg_thread thread_data; +static cyg_handle_t thread_handle; + +// Background load stuff +#define NUM_LOAD_THREADS 20 // Get 5% granularity +#define IDLE_THREAD_PRIORITY CYGNUM_PPP_PPPD_THREAD_PRIORITY+3 +#define LOAD_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-3 +#define MAIN_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-4 +#define DESIRED_BACKGROUND_LOAD 50 // should be accurate enough over range + +// starting points for load calculation +#define MAX_LOAD_THREAD_LEVEL 100 +#define MIN_LOAD_THREAD_LEVEL 0 + +static char idle_thread_stack[STACK_SIZE]; +static cyg_thread idle_thread_data; +static cyg_handle_t idle_thread_handle; +static cyg_sem_t idle_thread_sem; +volatile static long long idle_thread_count; +static char load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE]; +static cyg_thread load_thread_data[NUM_LOAD_THREADS]; +static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS]; +static cyg_sem_t load_thread_sem[NUM_LOAD_THREADS]; +static long load_thread_level; +static void calibrate_load(int load); +static void start_load(int load); +static void do_some_random_computation(int p,int id); +#define abs(n) ((n) < 0 ? -(n) : (n)) + +static long long no_load_idle_count_1_second; + +extern void +cyg_test_exit(void); + +void +pexit(char *s) +{ + perror(s); + cyg_test_exit(); +} + +int +do_read(int s, void *_buf, int len) +{ + int total, slen, rlen; + unsigned char *buf = (unsigned char *)_buf; + total = 0; + rlen = len; + while (total < len) { + slen = read(s, buf, rlen); + if (slen != rlen) { + if (slen < 0) { + diag_printf("Error after reading %d bytes\n", total); + return -1; + } + rlen -= slen; + buf += slen; + } + total += slen; + } + return total; +} + +int +do_write(int s, void *_buf, int len) +{ + int total, slen, rlen; + unsigned char *buf = (unsigned char *)_buf; + total = 0; + rlen = len; + while (total < len) { + slen = write(s, buf, rlen); + if (slen != rlen) { + if (slen < 0) { + diag_printf("Error after writing %d bytes\n", total); + return -1; + } + rlen -= slen; + buf += slen; + } + total += slen; + } + return total; +} + +// +// This function is called to calibrate the "background load" which can be +// applied during testing. It will be called before any commands from the +// host are managed. +// +static void +calibrate_load(int desired_load) +{ + long long no_load_idle, load_idle; + int percent_load; + int high, low; + + // Set limits + high = MAX_LOAD_THREAD_LEVEL; + low = MIN_LOAD_THREAD_LEVEL; + + // Compute the "no load" idle value + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + cyg_thread_delay(1*100); // Pause for one second + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + no_load_idle = idle_thread_count; + diag_printf("No load = %d\n", (int)idle_thread_count); + + // First ensure that the HIGH level is indeed higher + while (true) { + load_thread_level = high; + start_load(desired_load); // Start up a given load + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + cyg_thread_delay(1*100); // Pause for one second + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + load_idle = idle_thread_count; + start_load(0); // Shut down background load + percent_load = 100 - ((load_idle * 100) / no_load_idle); + diag_printf("High Load[%d] = %d => %d%%\n", load_thread_level, + (int)idle_thread_count, percent_load); + if ( percent_load > desired_load ) + break; // HIGH level is indeed higher + low = load_thread_level; // known to be lower + high *= 2; // else double it and try again + } + + // Now chop down to the level required + while (true) { + load_thread_level = (high + low) / 2; + start_load(desired_load); // Start up a given load + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + cyg_thread_delay(1*100); // Pause for one second + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + load_idle = idle_thread_count; + start_load(0); // Shut down background load + percent_load = 100 - ((load_idle * 100) / no_load_idle); + diag_printf("Load[%d] = %d => %d%%\n", load_thread_level, + (int)idle_thread_count, percent_load); + if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break; + if (percent_load < desired_load) { + low = load_thread_level; + } else { + high = load_thread_level; + } + } + + // Now we are within a few percent of the target; scale the load + // factor to get a better fit, and test it, print the answer. + load_thread_level *= desired_load; + load_thread_level /= percent_load; + start_load(desired_load); // Start up a given load + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + cyg_thread_delay(1*100); // Pause for one second + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + load_idle = idle_thread_count; + start_load(0); // Shut down background load + percent_load = 100 - ((load_idle * 100) / no_load_idle); + diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level, + (int)idle_thread_count, percent_load); + no_load_idle_count_1_second = no_load_idle; +} + +// +// This function is called to set up a load level of 'load' percent (given +// as a whole number, e.g. start_load(20) would mean initiate a background +// load of 20%, leaving the cpu 80% idle). +// +static void +start_load(int load) +{ + static int prev_load = 0; + int i; + if (load == 0) { + diag_printf("Set no background load\n"); + if (prev_load == 0) return; // Nothing out there to stop + for (i = 0; i < prev_load * NUM_LOAD_THREADS/100; i++) { + cyg_semaphore_wait(&load_thread_sem[i]); + } + prev_load = 0; + } else { + diag_printf("Set background load = %d%% starting %d threads\n", + load, load * NUM_LOAD_THREADS/100 ); + for (i = 0; i < load * NUM_LOAD_THREADS/100; i++) { + cyg_semaphore_post(&load_thread_sem[i]); + } + prev_load = load; + } +} + +// +// These thread(s) do some amount of "background" computing. This is used +// to simulate a given load level. They need to be run at a higher priority +// than the network code itself. +// +// Like the "idle" thread, they run as long as their "switch" (aka semaphore) +// is enabled. +// +void +net_load(cyg_addrword_t who) +{ + int i; + while (true) { + cyg_semaphore_wait(&load_thread_sem[who]); + for (i = 0; i < load_thread_level; i++) { + do_some_random_computation(i,who); + } + cyg_thread_delay(1); // Wait until the next 'tick' + cyg_semaphore_post(&load_thread_sem[who]); + } +} + +// +// Some arbitrary computation, designed to use up the CPU and cause associated +// cache "thrash" behaviour - part of background load modelling. +// +static void +do_some_random_computation(int p,int id) +{ + // Just something that might be "hard" +#if 0 + { + volatile double x; + x = ((p * 10) * 3.14159) / 180.0; // radians + } +#endif +#if 1 + { + static int footle[0x10001]; + static int counter = 0; + register int i; + + i = (p << 8) + id + counter++; + i &= 0xffff; + footle[ i+1 ] += footle[ i ] + 1; + } +#endif +} + +// +// This thread does nothing but count. It will be allowed to count +// as long as the semaphore is "free". +// +void +net_idle(cyg_addrword_t param) +{ + while (true) { + cyg_semaphore_wait(&idle_thread_sem); + idle_thread_count++; + cyg_semaphore_post(&idle_thread_sem); + } +} + +static void +echo_test(cyg_addrword_t p) +{ + int s_source, s_sink, e_source, e_sink; + struct sockaddr_in e_source_addr, e_sink_addr, local; + int one = 1; + fd_set in_fds; + int i, num, len; + struct test_params params,nparams; + struct test_status status,nstatus; + + cyg_tick_count_t starttime, stoptime; + + s_source = socket(AF_INET, SOCK_STREAM, 0); + if (s_source < 0) { + pexit("stream socket"); + } + if (setsockopt(s_source, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { + pexit("setsockopt /source/ SO_REUSEADDR"); + } + if (setsockopt(s_source, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) { + pexit("setsockopt /source/ SO_REUSEPORT"); + } + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_len = sizeof(local); + local.sin_port = ntohs(SOURCE_PORT); + local.sin_addr.s_addr = INADDR_ANY; + if(bind(s_source, (struct sockaddr *) &local, sizeof(local)) < 0) { + pexit("bind /source/ error"); + } + listen(s_source, SOMAXCONN); + + s_sink = socket(AF_INET, SOCK_STREAM, 0); + if (s_sink < 0) { + pexit("stream socket"); + } + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_len = sizeof(local); + local.sin_port = ntohs(SINK_PORT); + local.sin_addr.s_addr = INADDR_ANY; + if(bind(s_sink, (struct sockaddr *) &local, sizeof(local)) < 0) { + pexit("bind /sink/ error"); + } + if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { + pexit("setsockopt /sink/ SO_REUSEADDR"); + } + if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) { + pexit("setsockopt /sink/ SO_REUSEPORT"); + } + listen(s_sink, SOMAXCONN); + + e_source = 0; e_sink = 0; + while (true) { + // Wait for a connection on either of the ports + FD_ZERO(&in_fds); + FD_SET(s_source, &in_fds); + FD_SET(s_sink, &in_fds); + num = select(max(s_sink,s_source)+1, &in_fds, 0, 0, 0); + if (FD_ISSET(s_source, &in_fds)) { + len = sizeof(e_source_addr); + if ((e_source = accept(s_source, (struct sockaddr *)&e_source_addr, &len)) < 0) { + pexit("accept /source/"); + } + diag_printf("SOURCE connection from %s:%d\n", + inet_ntoa(e_source_addr.sin_addr), ntohs(e_source_addr.sin_port)); + } + if (FD_ISSET(s_sink, &in_fds)) { + len = sizeof(e_sink_addr); + if ((e_sink = accept(s_sink, (struct sockaddr *)&e_sink_addr, &len)) < 0) { + pexit("accept /sink/"); + } + diag_printf("SINK connection from %s:%d\n", + inet_ntoa(e_sink_addr.sin_addr), ntohs(e_sink_addr.sin_port)); + } + // Continue with test once a connection is established in both directions + if ((e_source != 0) && (e_sink != 0)) { + break; + } + } + + // Wait for "source" to tell us the testing paramters + if (do_read(e_source, &nparams, sizeof(nparams)) != sizeof(nparams)) { + pexit("Can't read initialization parameters"); + } + + params.nbufs = ntohl(nparams.nbufs); + params.bufsize = ntohl(nparams.bufsize); + params.load = ntohl(nparams.load); + + diag_printf("Using %d buffers of %d bytes each, %d%% background load\n", + params.nbufs, params.bufsize, params.load); + + // Tell the sink what the parameters are + if (do_write(e_sink, &nparams, sizeof(nparams)) != sizeof(nparams)) { + pexit("Can't write initialization parameters"); + } + + status.ok = 1; + nstatus.ok = htonl(status.ok); + + // Tell the "source" to start - we're all connected and ready to go! + if (do_write(e_source, &nstatus, sizeof(nstatus)) != sizeof(nstatus)) { + pexit("Can't send ACK to 'source' host"); + } + + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + starttime = cyg_current_time(); + start_load(params.load); + + // Echo the data from the source to the sink hosts + for (i = 0; i < params.nbufs; i++) { + if ((len = do_read(e_source, data_buf, params.bufsize)) != params.bufsize) { + diag_printf("Can't read buf #%d: ", i+1); + if (len < 0) { + perror("I/O error"); + } else { + diag_printf("short read - only %d bytes\n", len); + } + } +// else diag_printf("Got %d bytes\n",len); + if ((len = do_write(e_sink, data_buf, params.bufsize)) != params.bufsize) { + diag_printf("Can't write buf #%d: ", i+1); + if (len < 0) { + perror("I/O error"); + } else { + diag_printf("short write - only %d bytes\n", len); + } + } +// else diag_printf("Sent %d bytes\n",len); + } + + // Wait for the data to drain and the "sink" to tell us all is OK. + if (do_read(e_sink, &status, sizeof(status)) != sizeof(status)) { + pexit("Can't receive ACK from 'sink' host"); + } + + start_load(0); + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + stoptime = cyg_current_time(); + stoptime -= starttime; // time taken in cS + // expected idle loops in that time period for an idle system: + starttime = no_load_idle_count_1_second * stoptime / 100; + diag_printf( "%d ticks elapsed, %d kloops predicted for an idle system\n", + (int)stoptime, (int)(starttime/1000) ); + diag_printf( "actual kloops %d, CPU was %d%% idle during transfer\n", + (int)(idle_thread_count/1000), + (int)(idle_thread_count * 100 / starttime) ); + + // Now examine how close that loading actually was: + start_load(params.load); // Start up a given load + idle_thread_count = 0; + cyg_semaphore_post(&idle_thread_sem); // Start idle thread + cyg_thread_delay(1*100); // Pause for one second + cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread + start_load(0); // Shut down background load + i = 100 - ((idle_thread_count * 100) / no_load_idle_count_1_second ); + diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level, + (int)idle_thread_count, i); + +//#ifdef CYGDBG_USE_ASSERTS +#ifdef CYGDBG_NET_TIMING_STATS + { + extern void show_net_times(void); + show_net_times(); + } +#endif +//#endif +} + +//========================================================================== + +void +net_test(cyg_addrword_t param) +{ + cyg_serial_baud_rate_t old; + cyg_ppp_options_t options; + cyg_ppp_handle_t ppp_handle; + + CYG_TEST_INIT(); + + diag_printf("Start TCP test - ECHO mode\n"); + init_all_network_interfaces(); + calibrate_load(DESIRED_BACKGROUND_LOAD); +#ifdef CYGPKG_SNMPAGENT + { + extern void cyg_net_snmp_init(void); + cyg_net_snmp_init(); + } +#endif + + old = ppp_test_set_baud( CYGNUM_SERIAL_BAUD_115200 ); + + ppp_test_announce( "TCP_ECHO" ); + + cyg_ppp_options_init( &options ); + +// options.debug = 1; +// options.kdebugflag = 1; +// options.flowctl = CYG_PPP_FLOWCTL_SOFTWARE; + + ppp_handle = cyg_ppp_up( CYGPKG_PPP_TEST_DEVICE, &options ); + + CYG_TEST_INFO( "Waiting for PPP to come up"); + + cyg_ppp_wait_up( ppp_handle ); + + echo_test(param); + + CYG_TEST_INFO( "Bringing PPP down"); + + cyg_ppp_down( ppp_handle ); + + CYG_TEST_INFO( "Waiting for PPP to go down"); + + cyg_ppp_wait_down( ppp_handle ); + + cyg_thread_delay( 200 ); + + ppp_test_set_baud( old ); + + ppp_test_finish(); + + CYG_TEST_PASS_FINISH("TCP ECHO test OK"); +} + +void +cyg_start(void) +{ + int i; + // Create a main thread which actually runs the test + cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority + net_test, // entry + 0, // entry parameter + "Network test", // Name + &stack[0], // Stack + STACK_SIZE, // Size + &thread_handle, // Handle + &thread_data // Thread data structure + ); + cyg_thread_resume(thread_handle); // Start it + // Create the idle thread environment + cyg_semaphore_init(&idle_thread_sem, 0); + cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority + net_idle, // entry + 0, // entry parameter + "Network idle", // Name + &idle_thread_stack[0], // Stack + STACK_SIZE, // Size + &idle_thread_handle, // Handle + &idle_thread_data // Thread data structure + ); + cyg_thread_resume(idle_thread_handle); // Start it + // Create the load threads and their environment(s) + for (i = 0; i < NUM_LOAD_THREADS; i++) { + cyg_semaphore_init(&load_thread_sem[i], 0); + cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority + net_load, // entry + i, // entry parameter + "Background load", // Name + &load_thread_stack[i][0], // Stack + STACK_SIZE, // Size + &load_thread_handle[i], // Handle + &load_thread_data[i] // Thread data structure + ); + cyg_thread_resume(load_thread_handle[i]); // Start it + } + cyg_scheduler_start(); +} + +// EOF tcp_echo.c diff --git a/packages/net/ppp/current/tests/test_server.sh b/packages/net/ppp/current/tests/test_server.sh new file mode 100644 --- /dev/null +++ b/packages/net/ppp/current/tests/test_server.sh @@ -0,0 +1,406 @@ +#! /bin/bash +#========================================================================== +# +# tests/test_server.sh +# +# PPP test server script +# +#========================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +# ------------------------------------------- +# This file is part of eCos, the Embedded Configurable Operating System. +# Copyright (C) 2003, 2004 eCosCentric Ltd. +# +# eCos 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 or (at your option) any later version. +# +# eCos is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with eCos; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +# +# As a special exception, if other files instantiate templates or use macros +# or inline functions from this file, or you compile this file and link it +# with other works to produce a work based on this file, this file does not +# by itself cause the resulting work to be covered by the GNU General Public +# License. However the source code for this file must still be made available +# in accordance with section (3) of the GNU General Public License. +# +# This exception does not invalidate any other reasons why a work based on +# this file might be covered by the GNU General Public License. +# +# ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +#========================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): nickg +# Contributors: +# Date: 2003-06-26 +# Purpose: +# Description: +# +# +#####DESCRIPTIONEND#### +# +#========================================================================== + +# -------------------------------------------------------------------- +# Global variables + +ppp_prev= +ppp_optarg= + +ppp_dev= +ppp_myip= +ppp_hisip= +ppp_debug= +ppp_redboot= +ppp_redboot_baud=38400 +ppp_baud=$ppp_redboot_baud +ppp_flow=crtscts +ppp_ping_interval=1 + +# -------------------------------------------------------------------- +# Parse the options: + +for ppp_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ppp_prev"; then + eval "$ppp_prev=\$ppp_option" + ppp_prev= + continue + fi + + # If this option is of the form --thing=value then store + # the value into $ppp_optarg. + case "$ppp_option" in + -*=*) ppp_optarg=`echo "$ppp_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ppp_optarg= ;; + esac + + # Now parse the option + + case "$ppp_option" in + + --dev) ppp_prev=ppp_dev ;; + --dev=*) ppp_dev=$ppp_optarg ;; + + --myip) ppp_prev=ppp_myip ;; + --myip=*) ppp_myip=$ppp_optarg ;; + + --hisip) ppp_prev=ppp_hisip ;; + --hisip=*) ppp_hisip=$ppp_optarg ;; + + --baud) ppp_prev=ppp_baud ;; + --baud=*) ppp_baud=$ppp_optarg ;; + + --flow) ppp_prev=ppp_flow ;; + --flow=*) ppp_flow=$ppp_optarg ;; + + --redboot) ppp_redboot=y ;; + + --redboot-baud) ppp_prev=ppp_redboot_baud ;; + --redboot-baud=*) ppp_redboot_baud=$ppp_optarg ;; + + --debug) ppp_debug=y ;; + + *) + echo "test_server: Unrecognized option: \"$ppp_option\"." >&2 + exit 1 + ;; + esac +done + +# -------------------------------------------------------------------- +# debug echo function. This only generates output if the --debug +# flag was given. + +dbecho() +{ + if [ $ppp_debug ] ; then + echo $* + fi +} + +# -------------------------------------------------------------------- +# Usage message + +usage() +{ + echo "test_server --dev=" + echo " --myip=" + echo " --hisip=" + echo " [--baud=]" + echo " [--flow=[crtscts|xonxoff|none]]" + echo " [--redboot [--redboot-baud=]]" + echo " [--debug]" + exit 1 +} + +# -------------------------------------------------------------------- +# Check that all the required options are present, and report their +# values. + +if [ -z "$ppp_dev" ] ; then usage ; fi +if [ -z "$ppp_myip" ] ; then usage ; fi +if [ -z "$ppp_hisip" ] ; then usage ; fi + +dbecho "Device : " $ppp_dev +dbecho "My IP : " $ppp_myip +dbecho "His IP : " $ppp_hisip +dbecho "Baud : " $ppp_baud +dbecho "Flow : " $ppp_flow + +if [ "$ppp_flow" == "none" ] ; then ppp_flow="" ; fi + +# -------------------------------------------------------------------- +# Bring the PPP link up by calling pppd. The pid of the PPPD is +# stored in pppd_pid for later use. + +pppup() +{ + dbecho pppd $ppp_dev $ppp_baud $ppp_flow local nodetach $ppp_myip:$ppp_hisip $* & + pppd $ppp_dev $ppp_baud $ppp_flow local nodetach $ppp_myip:$ppp_hisip $* & + pppd_pid=$! +# dbecho "PPPD Pid: " $pppd_pid +} + +# -------------------------------------------------------------------- +# Simple test for bringing PPP up. Once the link is up the remote +# end is pinged for a while and then we bring the link down by +# signalling the PPPD. + +ppp_up_test() +{ + dbecho ppp_up_test + pppup + sleep 6 + ping -i$ppp_ping_interval -w45 -s3000 -c20 $ppp_hisip + kill -SIGINT $pppd_pid + wait $pppd_pid +} + +# -------------------------------------------------------------------- +# Up/down test. In this case the link is brought down by the remote +# end. + +ppp_updown_test() +{ + dbecho ppp_updown_test + pppup + wait $pppd_pid +} + + +# -------------------------------------------------------------------- +# Chat tests. These use chat itself to test the chat scripts on the +# remote end. The tests are: +# +# chat_test_1 - run throught the entire script +# chat_test_2 - simulate a carrier drop +# chat_test_3 - simulate a timeout + +chat_test_1() +{ + chat -V "Chat Test" "CONNECT\rlogin:\c" ppp "Password:\c" hithere "" <$ppp_dev >$ppp_dev +} + +chat_test_2() +{ + chat -V "Chat Test" "CONNECT\rlogin:\c" ppp "NO CARRIER" <$ppp_dev >$ppp_dev +} + +chat_test_3() +{ + chat -V "Chat Test" "CONNECT\rlogin:\c" ppp <$ppp_dev >$ppp_dev +} + +# -------------------------------------------------------------------- +# Authentication tests. These bring up the PPPD with different +# authentication requirements against which the remote end tests +# itself. The tests are: +# +# auth_test_1 - authenticate by default method (usually CHAP) +# auth_test_2 - require PAP authentication +# auth_test_3 - require CHAP authentication + +auth_test_1() +{ + dbecho ppp_up_test + pppup auth + sleep 6 + ps -p $pppd_pid >/dev/null + if [ "$?" == "0" ] ; then + ping -i$ppp_ping_interval -w45 -s3000 -c5 $ppp_hisip + kill -SIGINT $pppd_pid + wait $pppd_pid + fi +} + +auth_test_2() +{ + dbecho ppp_up_test + pppup auth require-pap + sleep 6 + ps -p $pppd_pid >/dev/null + if [ "$?" == "0" ] ; then + ping -i$ppp_ping_interval -w45 -s3000 -c5 $ppp_hisip + kill -SIGINT $pppd_pid + wait $pppd_pid + fi +} + +auth_test_3() +{ + dbecho ppp_up_test + pppup auth require-chap + sleep 6 + ps -p $pppd_pid >/dev/null + if [ "$?" == "0" ] ; then + ping -i$ppp_ping_interval -w45 -s3000 -c5 $ppp_hisip + kill -SIGINT $pppd_pid + wait $pppd_pid + fi +} + + +# -------------------------------------------------------------------- +# TCP echo test. After bringing up the link this test runs the +# tcp_source and tcp_sink test programs to exercise the link. This +# can take a long time so it is not really suitable for automated +# testing. + +tcp_echo_test() +{ + local sink_pid + dbecho tcp_echo_test + pppup + sleep 10 + tcp_sink $ppp_hisip & + sink_pid=$! + sleep 5 + tcp_source $ppp_hisip 60 + wait $sink_pid + sleep 5 + wait $pppd_pid +} + +# -------------------------------------------------------------------- +# Network characterisation test. After bringing up the link this test +# runs the nc_test_master test program to exercise the link. This can +# take a long time so it is not really suitable for automated testing. + +nc_test_slave_test() +{ + dbecho nc_test_slave_test + pppup + sleep 10 + nc_test_master $ppp_hisip + sleep 5 + wait $pppd_pid +} + +# -------------------------------------------------------------------- +# Change the baud rate. Depending on the value sent as part of the +# BAUD message, change the link baudrate. + +new_baud() +{ + ppp_new_baud=`echo $ppp_test | sed 's/.*BAUD:\([0-9]*\).*/\1/'` + dbecho "New Baud " $ppp_new_baud + case $ppp_new_baud in + 016) ppp_baud=14400; ppp_ping_interval=6 ;; + 017) ppp_baud=19200; ppp_ping_interval=4 ;; + 018) ppp_baud=38400; ppp_ping_interval=2 ;; + 019) ppp_baud=57600; ppp_ping_interval=2 ;; + 020) ppp_baud=115200; ppp_ping_interval=1 ;; + 021) ppp_baud=230400 ;; + + *) dbecho "Unknown baud rate: " $ppp_new_baud + esac + dbecho "New Baud Rate : " $ppp_baud +} + + + +# -------------------------------------------------------------------- +# Look for a RedBoot> prompt. + +ppp_redboot_prompt() +{ + local done= + dbecho ppp_redboot_prompt + + stty -F $ppp_dev $ppp_redboot_baud + + while [ ! $done ] ; do + chat -V "RedBoot>" "\c" <$ppp_dev >$ppp_dev + if [ "$?" == "0" ] ; then done=1; fi + done +} + +# -------------------------------------------------------------------- +# Main loop. + +while true ; do + + if [ $ppp_redboot ] ; then ppp_redboot_prompt; fi + + ppp_running=y + + while [ $ppp_running ] ; do + + dbecho "" + + dbecho "Setting baud rate : " $ppp_baud + stty -F $ppp_dev $ppp_baud + + dbecho "Waiting for test..." + + read ppp_test ppp_junk < $ppp_dev + + ppp_test=`echo $ppp_test | sed 's/\([a-zA-Z_:0-9]*\).*/\1/'` + + dbecho "PPP test: >" $ppp_test "<" + + case $ppp_test in + + PPP_UPDOWN) ppp_updown_test ;; + + PPP_UP) ppp_up_test ;; + + CHAT_TEST_1) chat_test_1 ;; + CHAT_TEST_2) chat_test_2 ;; + CHAT_TEST_3) chat_test_3 ;; + + PPP_AUTH_1) auth_test_1 ;; + PPP_AUTH_2) auth_test_2 ;; + PPP_AUTH_3) auth_test_3 ;; + + TCP_ECHO) tcp_echo_test ;; + + NC_TEST_SLAVE) nc_test_slave_test ;; + + BAUD:*) new_baud ;; + + FINISH) unset ppp_running ;; + + *) echo "Unknown test: " $ppp_test ;; + + esac + + done + +done + +# -------------------------------------------------------------------- +# end of test_server.sh +