rp-pppoe-4.0/0000755000175000017500000000000014422272526011237 5ustar dfsdfsrp-pppoe-4.0/scripts/0000755000175000017500000000000014422272526012726 5ustar dfsdfsrp-pppoe-4.0/configs/0000755000175000017500000000000014422272526012667 5ustar dfsdfsrp-pppoe-4.0/configs/pppoe-server-options0000644000175000017500000000021114422272526016724 0ustar dfsdfs# PPP options for the PPPoE server # SPDX-License-Identifier: GPL-2.0-or-later require-pap login lcp-echo-interval 10 lcp-echo-failure 2 rp-pppoe-4.0/doc/0000755000175000017500000000000014422272526012004 5ustar dfsdfsrp-pppoe-4.0/doc/HOW-TO-CONNECT0000644000175000017500000000500214422272526013770 0ustar dfsdfs# SPDX-License-Identifier: GPL-2.0-or-later This package lets you connect a Linux machine to an ISP that uses PPPoE. PPPoE is used by many DSL providers and some wireless providers. Follow these steps and you should have your PPPoE service up and running. 0. Install the rp-pppoe-software -------------------------------- You should have already done this by the time you're reading this. If not, go back and read README. 1. Set up your Ethernet hardware -------------------------------- First, make sure the Ethernet card you intend to use with the modem is visible to the Linux kernel. Just how to do this is beyond the scope of this document. However, if the card is the only Ethernet card in the system, executing: ip link show eth0 should display something like this: 2: eth0: mtu 1500 qdisc pfifo_fast qlen 1000 link/ether fc:aa:15:39:a7:b3 brd ff:ff:ff:ff:ff:ff Your second line will most definately be different. As long as you see the link/ether line, your card should be working. 2. Edit pap-secrets or chap-secrets ----------------------------------- Edit the "pap-secrets" or "chap-secrets" file, inserting your proper user-ID and password. Install the file (or copy the relevant lines) to /etc/ppp/pap-secrets. Your ISP may use CHAP authentication. In this case, add the line to /etc/ppp/chap-secrets. 3. Create a PPP configuration file ---------------------------------- Create a file called /etc/ppp/peers/NAME where NAME is a convenient name for the connection. If you are using user-mode PPPoE, the peer file should look something like this: pty '/usr/sbin/pppoe -s -I eth0' sync noauth remotename YOUR_ISP name YOUR_USERNAME noipdefault nodetach noaccomp nodeflate nopcomp novj novjccomp lcp-echo-interval 10 (Use the appropriate Ethernet interface name in place of eth0, if necessary.) If you are using kernel-mode PPPoE, the peer file should look something like this: plugin /etc/ppp/plugins/rp-pppoe.so nic-eth0 noauth remotename YOUR_ISP name YOUR_USERNAME noipdefault nodetach noaccomp nodeflate nopcomp novj novjccomp lcp-echo-interval 10 Again, adjust nic-eth0 as appropriate for your Ethernet interface. Always prefix the interface name with "nic-". Then to connect, simply invoke: pppd call NAME where NAME is the name of the file you created under /etc/ppp/peers. -- Dianne Skoll https://dianne.skoll.ca/projects/rp-pppoe/ | rp-pppoe-4.0/doc/KERNEL-MODE-PPPOE0000644000175000017500000000272114422272526014314 0ustar dfsdfs# SPDX-License-Identifier: GPL-2.0-or-later RP-PPPoE now supports kernel-mode PPPoE on Linux kernels 2.4 or newer. Here's what you need to do: 1) If you are running pppd older than 2.4.0, upgrade to the newest version. Make sure you have the pppd development files installed. 2) Unpack rp-pppoe. 3) In the rp-pppoe directory, change to src/ and type: ./configure --enable-plugin 4) Type make; make install 5) Use the "plugin /etc/ppp/plugins/rp-pppoe.so" pppd option. The rp-pppoe.so plugin adds the following command-line options to pppd: ethXXX -- Use interface ethXXX as Ethernet interface brXXX -- Use interface brXXX as Ethernet interface nic-XXXX -- Use interface XXXX as the Ethernet interface rp_pppoe_service SERVICE_NAME -- Specify desired service name rp_pppoe_ac NAME -- Specify desired access concentrator name rp_pppoe_verbose 0|1 -- Print names of access concentrators rp_pppoe_sess nnnn:aa:bb:cc:dd:ee:ff -- Attach to existing session 'nnnn' on AC with Ethernet address aa:bb:cc:dd:ee:ff This skips the discovery phase. rp_pppoe_mac aa:bb:cc:dd:ee:ff -- only accept PADOs from specified MAC address The kernel-mode PPPoE plugin permits an MTU of up to 1500 on the PPP interface providing that the MTU on the underlying Ethernet interface is at least 1508. -- Dianne Skoll rp-pppoe-4.0/doc/CHANGES0000644000175000017500000003641314422272526013006 0ustar dfsdfs# SPDX-License-Identifier: GPL-2.0-or-later Changes from version 3.15 to 4.0: - Release 4.0 (2023-04-26) - SPDX-License-Identifier: tags added to most source files - rp-pppoe.so plugin: Modified to compile against both pppd >= 2.5.0 and pppd < 2.5.0. - pppoe-server: If -L or -R option is specified as 0.0.0.0, then IP allocation is delegated to pppd. - pppoe-server: New -g option specifies the path of the rp-pppoe.so plugin to use with pppd in kernel-mode PPPoE. - pppoe-server: New -U option specifies a UNIX-domain control socket. This lets you send control commands to the server while it is running; see the pppoe-server man page for details. - All source code: Many cleanups and use of standard types like uint16_t rather than ones we define. - All source code: Support for OSes other than Linux has been dropped. - Documentation: Updated fro modern Linux systems. - Cleanup: The following old and obsolete files have been removed: - Old connection scripts scripts/pppoe-connect.in, scripts/pppoe-start.in, scripts/pppoe-init-suse.in, scripts/pppoe-setup.in, scripts/pppoe-status, scripts/pppoe-init.in, and scripts/pppoe-stop.in - Obsolete firewall scripts configs/firewall-standalone and configs/firewall-masq - Obsolete configuration files configs/pppoe.conf and configs/pap-secrets - The Tcl/Tk gui script gui/tkpppoe.in and supporting files - The RPM spec file rp-pppoe.spec Changes from version 3.14 to 3.15: - Release 3.15 (2021-05-07) - src/pppoe.c: Don't ignore SIGTERM and SIGINT. Send PADT and exit if one of those signals is received. - General: Switch from net-tools (ifconfig and friends) to iproute2 (ip ...) on Linux. Changes from version 3.13 to 3.14: - Release 3.14 (2020-05-26) - Delete scripts/pppoe-init-turbolinux.in - TurboLinux is defunct. - pppoe-server: Add -H and -M options for sending HURL and MOTM packets respectively. - Change VERSION macro to RP_VERSION to avoid conflict with pppd macro. - Apply all of the patches at https://github.com/nabijaczleweli/rp-pppoe/tree/patches/0006-touchups-3.14-BETA-2-v1 which fix up various compile-time warnings. - Apply all of the patches at https://github.com/nabijaczleweli/rp-pppoe/tree/patches/0004-mostly-cosmetic-3.14-BETA-1-v1 which implement the following 6 changes: - 1. Clarify a couple of comments - 2. Remove the '@' before the command that installs the plugin, in the Makefile - 3. Fix a typo in doc/HOW-TO-CONNECT - 4. Use echo instead of /bin/echo in scripts - 5. Fix potential snprintf(...) truncation - 6. Cosmetic fix to copyright printout - Apply all the patches at https://github.com/nabijaczleweli/rp-pppoe/tree/patches/0002-pppd-bugfixes-3.13-v1 which implement the following 13 changes: - 1. Don't exit if we time out waiting for PADO/PADS packets if persist option set - 2. Remove unused variable - 3. Don't error out if device is not Ethernet if doit is 0 in PPPoEDevnameHook - 4. Use one 8-bit bitfield and macros for both ver and type in PPPoEPacket - 5. Remove unnecessary casts from common.c - 6. Replace strDup() with POSIX strdup(3) - 7. Extract expiration check into time_left() function - 8. Suppress false error message on PPPoE disconnect - 9. Send PADT on PPPoE disconnect - 10. Various fixes for errors found by coverity static analysis - 11. Remove all references to cdefs.h - 12. Eliminate snprintf-related compiler warning in plugin.c - 13. Make tag parsing loop condition more accurate Changes from version 3.12 to 3.13: - Release 3.13 (2018-11-25) - Fix potential use-after-free bug - Properly detecte kernel-mode PPPoE; fix compilation problems on Debian Jessie and Debian Stretch. Changes from version 3.11 to 3.12: - Update author's name to "Dianne Skoll" - Add "-W" option to pppoe executable to explicitly set a Host-Unique value - Fix a few compiler warnings and configure script bugs. - Removed dead code Changes from Version 3.10 to 3.11: - Make the rp-pppoe.so plugin avoid calling exit() if the "persist" pppd option was given. - Permit both PPPoE server and client to specify an MTU/MRU of 1500 assuming the underlying Ethernet interface has an MTU of at least 1508. The larger MTU is negotiated per RFC 4638. NOTE: Only available with kernel-mode plugin, not user-mode pty redirector. - Get rid of hard-coded nobsdcomp, nodeflate, novj and novjccomp options in pppoe-server. - Apply patch from Philip Prindeville to allow cross-compiling. - Handle UNIT=xxx directive in pppoe-connect. (Keith Waters) - Add "-i" flag to pppoe-server to ignore PADIs if there are no free sessions. Change suggested by Miroslav Lednicky. - Ignore PADTs not destined for our ethernet address. - Add "-X " option to pppoe-server. - Cleanup Makefile.in. (Philip Prindeville) - In plugin, don't set devnam if it's already set. (Mike Ireton) - Eliminate race condition in signal handling. - pppoe-server: Add -q and -Q options to allow specification of path to pppd and pppoe programs respectively. - Add a few more sanity checks to pppoe-server packet handling. - Use -fno-strict-aliasing when compiling with gcc to avoid possible problems from -O2 optimization level. Changes from Version 3.9 to 3.10: (30 June 2008) - Fixed compilation problems on various platforms. - The Makefiles now use (standard) DESTDIR instead of (non-standard) RPM_INSTALL_ROOT to relocate installed files. - Spec file has been updated (it had languished since 3.6.) Changes from Version 3.8 to 3.9: (21 June 2008) - pppoe-server has new "-x" option to limit the number of sessions per MAC address. - Added proper timeout handling while waiting for PADO/PADS. - Fix race condition with some access concentrators that move very quickly into session mode (problem noted by Luigi Sgro) - Fixed compilation problem on BSD. - Fixed compilation problems with old versions of gcc - Remove superfluous options in scripts/pppoe-connect.in Changes from Version 3.7 to 3.8: (2 April 2006) - Adjusted code and made it possible to disable debugging code to shrink size of pppoe executable. - Fixed bug in MD5 code that caused pppoe-server to segfault on 64-bit machines. - Made various functions and variables static that didn't need to be visible outside their source files. Changes from Version 3.6 to 3.7: - Fixed typo in the firewall-standalone sample firewall script. Fix courtesy of Robert Vogelgesang - Added -O option to pppoe-server to let you specify a different default options file for pppd instead of /etc/ppp/pppoe-server-options Feature courtesy of Robert Vogelgesang - Fixed some silliness and incorrectness in configure.in. Feature courtesy of Robert Vogelgesang - Fixed a typo in pppoe-connect.in that made it fail if used with the kernel-mode plugin. - Make pppoe-server prepend "nic-" to interface name if used with kernel-mode plugin. This lets you use interfaces that don't start with "eth" more easily. Changes from Version 3.5 to 3.6: - Changed the names of commands from adsl-* to pppoe-* to more logically name the scripts. NOTE INCOMPATIBILITY: OLD NAME NEW NAME adsl-start pppoe-start adsl-stop pppoe-stop adsl-status pppoe-status adsl-connect pppoe-connect adsl-setup pppoe-setup - Changed sample firewall scripts to use iptables instead of the old ipchains command. - Updated KERNEL-MODE-PPPOE instructions to reflect more modern pppd that is commonly distributed. - Make the userland pppoe daemon run as "nobody" if possible, once session has started. - Make userland pppoe program somewhat safe if it is installed SUID or SGID. Note that I still do *NOT* recommend a SUID/SGID pppoe. - Fix long-standing bug in pppoe-server that passed arguments to pppd in the wrong order. - Fix kernel-mode plugin. It was broken by changes to pppd. The pppd maintainers fixed their version of the plugin, but neglected to inform me. Thanks a lot, guys! - Make plugin accept argument of the form "nix-XXXX" to force it to use device "XXXX" as the Ethernet interface. This allows the use of devices whose names do not start with "eth" Changes from Version 3.4 to 3.5: - Fixes for compilation on Solaris. Changes from Version 3.3 to 3.4: - INCOMPATIBILITY WITH EARLIER VERSIONS: Kernel-mode plugin now is built against latest CVS ppp source rather than Michal Ostrowski's patched version. If you use kernel-mode PPPoE, you MUST use the CVS version of the ppp source code with rp-pppoe 3.4. - Print PPPoE session number when connection terminates. Thanks to Alexander Dalloz for suggesting this. - Fixed a bug in MSS clamping -- it now works with protocol-field compression. Thanks to Gerd v. Egidy for the patch. - Ignore SIGINT and SIGTERM so LCP termination packets make it out. Changes from Version 3.2 to 3.3: - Client works on Solaris again. It was broken in 3.2. - Added DEFAULTROUTE=yes|no option to configuration file. - Server parses address pool file better. - Server address pool allows ranges of addresses on a line: a.b.c.d-e - Added "-d" (=debug) and "-P" (=check pool file syntax) options to pppoe-server. Changes from Version 3.1 to 3.2: - Client now ignores PADT's if they are from the wrong source MAC address or to the wrong destination MAC address. - Minor fixes to Makefile.in for Turbolinux. Changes from Version 3.0 to 3.1: - Improved KERNEL-MODE-PPPOE instructions - Works with patched pppd 2.4.1 - Many improvements to server: Added "-u" and "-r" options; server can now respond to request on multiple Ethernet interfaces. - SECURITY BUG FIX: Server now ignores PADT's if they are from the wrong source MAC address. You are STRONGLY RECOMMENDED to upgrade to 3.1 if you use pppoe-server in production. Changes from Version 2.8 to 3.0: - Many small improvements to server. Server now only makes one discovery socket, systemwide, with addition of "-n" option to pppoe. - Fixes for compilation problems on BSD, Solaris and some Linux platforms. - Added "-p" option to pppoe-server to allow you to specify a pool of IP addresses to assign to clients. - Added GUI system (tkpppoe). This work was funded by Iospan Wireless, Inc. The GUI includes a Set-UID wrapper (pppoe-wrapper) which allows ordinary users to control a link (if so authorized.) I believe the wrapper script is secure, but please audit the source code (gui/wrapper.c) if you have any concerns. - Changes to scripts and pppoe.conf. DNS setup is now dynamic (happens each time adsl-connect runs.) - Made relay.c check packet lengths rigorously; made it throw out Ethernet frame padding on session packets as well as discovery packets. Changes from Version 2.7 to 2.8: - Added init scripts for TurboLinux, courtesy of Yasuhiro Sumi. - Made relay.c check packet lengths rigorously; made it throw out Ethernet frame padding on discovery packets. *** NOTE: 2.7 was not released publicly Changes from Version 2.6 to 2.7: - Completely restructured source file tree. - Much internal restructuring to eliminate a bunch of global variables. - adsl-connect now executes /etc/ppp/adsl-lost whenever connection is dropped or cannot be established. - Split pppoe.c into pppoe.c and discovery.c. - Added relay agent (pppoe-relay). - Made adsl-connect script use the "-U" (host-unique) option to better support multiple PPPoE links. - Added support for kernel-mode PPPoE (EXPERIMENTAL, UNSUPPORTED!) - Added "-o" option to PPPoE server; encoded server PID in pppoe-server cookie. Changes from Version 2.5 to 2.6: - Code should now compile cleanly on Caldera and Slackware Linux - Fixed rp-pppoe.spec file to work on Mandrake and Red Hat. - Deleted some obsolete files - Fixed bug in Solaris/x86 port (thanks to Philippe Levan) - Made shell scripts nicer under Solaris (again, Philippe Levan) - Made adsl-status look under /var/run and /etc/ppp for PID files. Should fix problems with NetBSD. - Added PPPD_EXTRA to pppoe.conf; made the PID file depend on the config file name. This makes it easier to run multiple PPPoE sessions. Changes from Version 2.4 to 2.5: - Tested for zero-length TCP option-length field, and for reverse-packing of type/code bitfields. Thanks to Robert Schlabbach for pointing out these problems. - Set umask to 077 in adsl-setup.in to protect created files like /etc/ppp/pap-secrets. Changes from Version 2.3 to 2.4: - Fixed spec file to automatically add .gz extension to man files as required - Tightened firewall rules. - Better check for /var/run in adsl-status; minor shell script fixes and cleanups for NetBSD and Solaris. - Added FAQ to HOW-TO-CONNECT regarding running a script each time a connection is made. Changes from Version 2.2 to 2.3: - Fixed the init script to create/remove /var/lock/subsys/adsl (patch courtesy of Charley Carter.) - Added support (under Linux) for N_HDLC line discipline which should greatly reduce CPU usage. My tests show it cuts CPU usage in half. My 486 DX2/66 gets 800 kb/s at 22% CPU usage. - adsl-connect uses "setsid" (if available) so that adsl-stop doesn't kill its caller. There is (IMO) a bug in pppd which kills all processes in its process group if the "pty" option is used. The setsid program gets around this bug, on Linux at least. - Port to Solaris, courtesy of David Holland. - Renamed spec file from "spec" to "rp-pppoe.spec" and made some cleanups. NOTE: Red Hat, in their infinite wisdom, decided to make the new RPM compress man pages automatically. You may have problems building RPM's from source unless you get the latest rpm package and make sure it compresses man pages. Changes from Version 2.1 to 2.2: - Added "-f" option to pppoe to allow use of any Ethernet frame type for PPPoE. USE WITH CAUTION -- this is a workaround for broken DSL providers, not something you should monkey with freely! - Added pppoe-sniff program to help expose non-standard PPPoE implementations. Changes from Version 2.0 to 2.1: - Fixed minor bugs in bounds-checking - Modified adsl-status to use output of "netstat -r -n" to determine whether or not link is up. This should make it independent of locale, I hope! - Added "-k" and "-d" options to pppoe. Changes from Version 1.9 to 2.0: - Addition of pppoe-server - Massive internal code restructuring - Zealous bounds-checking everywhere. - adsl-setup now quotes user name and password in /etc/ppp/pap-secrets. - Ported to OpenBSD, FreeBSD and NetBSD, courtesy of Geoff Mottram and Yannis Sismanis. - Rearranged adsl-* shell scripts, courtesy of Heiko Schlittermann. - Fixed bug in which Host-Uniq did not work if access concentrator sent a cookie. - Addition of SuSE-specific "init" script, courtesy of Gary Cameron. Changes from Version 1.8 to 1.9: - Added some more documentation to HOW-TO-CONNECT - Demand-dialling option now works correctly - SIGHUP terminates pppoe after sending a PADT to the access concentrator - Minor cleanups to connection shell scripts Changes from Version 1.7 to 1.8: - Added demand-dialling option - Clarified HOW-TO-CONNECT - Added adsl-status script - Added "restart" and "status" options to Red Hat /etc/rc.d/init.d/adsl script - Made adsl-setup check for existence of pppd - Wildcarded external interface in firewall rules - Made pppoe send a PADT frame if connection is terminated $Id$ rp-pppoe-4.0/doc/LICENSE0000644000175000017500000004331714422272526013021 0ustar dfsdfs# SPDX-License-Identifier: GPL-2.0-or-later GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. rp-pppoe-4.0/src/0000755000175000017500000000000014422272526012026 5ustar dfsdfsrp-pppoe-4.0/src/common.c0000644000175000017500000004237014422272526013470 0ustar dfsdfs/*********************************************************************** * * common.c * * Implementation of user-space PPPoE redirector for Linux. * * Common functions used by PPPoE client and server * * Copyright (C) 2000-2012 by Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ /* For vsnprintf prototype */ #define _ISOC99_SOURCE 1 #define _GNU_SOURCE 1 #include "config.h" #include #include #include #include #include #include #include #include #include "pppoe.h" /* Are we running SUID or SGID? */ int IsSetID = 0; static uid_t saved_uid = (uid_t) -2; static uid_t saved_gid = (uid_t) -2; /********************************************************************** *%FUNCTION: parsePacket *%ARGUMENTS: * packet -- the PPPoE discovery packet to parse * func -- function called for each tag in the packet * extra -- an opaque data pointer supplied to parsing function *%RETURNS: * 0 if everything went well; -1 if there was an error *%DESCRIPTION: * Parses a PPPoE discovery packet, calling "func" for each tag in the packet. * "func" is passed the additional argument "extra". ***********************************************************************/ int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra) { uint16_t len = ntohs(packet->length); unsigned char *curTag; uint16_t tagType, tagLen; if (PPPOE_VER(packet->vertype) != 1) { syslog(LOG_ERR, "Invalid PPPoE version (%d)", PPPOE_VER(packet->vertype)); return -1; } if (PPPOE_TYPE(packet->vertype) != 1) { syslog(LOG_ERR, "Invalid PPPoE type (%d)", PPPOE_TYPE(packet->vertype)); return -1; } /* Do some sanity checks on packet */ if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */ syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len); return -1; } /* Step through the tags */ curTag = packet->payload; while (curTag - packet->payload + TAG_HDR_SIZE <= len) { /* Alignment is not guaranteed, so do this by hand... */ tagType = (curTag[0] << 8) + curTag[1]; tagLen = (curTag[2] << 8) + curTag[3]; if (tagType == TAG_END_OF_LIST) { return 0; } if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen); return -1; } func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); curTag = curTag + TAG_HDR_SIZE + tagLen; } return 0; } /********************************************************************** *%FUNCTION: findTag *%ARGUMENTS: * packet -- the PPPoE discovery packet to parse * type -- the type of the tag to look for * tag -- will be filled in with tag contents *%RETURNS: * A pointer to the tag if one of the specified type is found; NULL * otherwise. *%DESCRIPTION: * Looks for a specific tag type. ***********************************************************************/ unsigned char * findTag(PPPoEPacket *packet, uint16_t type, PPPoETag *tag) { uint16_t len = ntohs(packet->length); unsigned char *curTag; uint16_t tagType, tagLen; if (PPPOE_VER(packet->vertype) != 1) { syslog(LOG_ERR, "Invalid PPPoE version (%d)", PPPOE_VER(packet->vertype)); return NULL; } if (PPPOE_TYPE(packet->vertype) != 1) { syslog(LOG_ERR, "Invalid PPPoE type (%d)", PPPOE_TYPE(packet->vertype)); return NULL; } /* Do some sanity checks on packet */ if (len > ETH_JUMBO_LEN - 6) { /* 6-byte overhead for PPPoE header */ syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len); return NULL; } /* Step through the tags */ curTag = packet->payload; while(curTag - packet->payload + TAG_HDR_SIZE <= len) { /* Alignment is not guaranteed, so do this by hand... */ tagType = (((uint16_t) curTag[0]) << 8) + (uint16_t) curTag[1]; tagLen = (((uint16_t) curTag[2]) << 8) + (uint16_t) curTag[3]; if (tagType == TAG_END_OF_LIST) { return NULL; } if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen); return NULL; } if (tagType == type) { memcpy(tag, curTag, tagLen + TAG_HDR_SIZE); return curTag; } curTag = curTag + TAG_HDR_SIZE + tagLen; } return NULL; } /********************************************************************** *%FUNCTION: switchToRealID *%ARGUMENTS: * None *%RETURNS: * Nothing *%DESCRIPTION: * Sets effective user-ID and group-ID to real ones. Aborts on failure ***********************************************************************/ void switchToRealID (void) { if (IsSetID) { if (saved_uid == (uid_t) -2) saved_uid = geteuid(); if (saved_gid == (uid_t) -2) saved_gid = getegid(); if (setegid(getgid()) < 0) { printErr("setgid failed"); exit(EXIT_FAILURE); } if (seteuid(getuid()) < 0) { printErr("seteuid failed"); exit(EXIT_FAILURE); } } } /********************************************************************** *%FUNCTION: switchToEffectiveID *%ARGUMENTS: * None *%RETURNS: * Nothing *%DESCRIPTION: * Sets effective user-ID and group-ID back to saved gid/uid ***********************************************************************/ void switchToEffectiveID (void) { if (IsSetID) { if (setegid(saved_gid) < 0) { printErr("setgid failed"); exit(EXIT_FAILURE); } if (seteuid(saved_uid) < 0) { printErr("seteuid failed"); exit(EXIT_FAILURE); } } } /********************************************************************** *%FUNCTION: dropPrivs *%ARGUMENTS: * None *%RETURNS: * Nothing *%DESCRIPTION: * If effective ID is root, try to become "nobody". If that fails and * we're SUID, switch to real user-ID ***********************************************************************/ void dropPrivs(void) { struct passwd *pw = NULL; int ok = 0; if (geteuid() == 0) { pw = getpwnam("nobody"); if (pw) { if (setgid(pw->pw_gid) >= 0) ok++; if (setuid(pw->pw_uid) >= 0) ok++; } } if (ok < 2 && IsSetID) { ok = 0; if (setegid(getgid()) >= 0) ok++; if (seteuid(getuid()) >= 0) ok++; } if (ok < 2) { printErr("unable to drop privileges"); exit(EXIT_FAILURE); } } /********************************************************************** *%FUNCTION: printErr *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message to stderr and syslog. ***********************************************************************/ void printErr(char const *fmt, ...) { char *str; va_list ap; int r; va_start(ap, fmt); r = vasprintf(&str, fmt, ap); va_end(ap); if (r < 0) return; fprintf(stderr, "pppoe: %s\n", str); syslog(LOG_ERR, "%s", str); free(str); } /********************************************************************** *%FUNCTION: computeTCPChecksum *%ARGUMENTS: * ipHdr -- pointer to IP header * tcpHdr -- pointer to TCP header *%RETURNS: * The computed TCP checksum ***********************************************************************/ uint16_t computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr) { uint32_t sum = 0; uint16_t count = ipHdr[2] * 256 + ipHdr[3]; uint16_t tmp; unsigned char *addr = tcpHdr; unsigned char pseudoHeader[12]; /* Count number of bytes in TCP header and data */ count -= (ipHdr[0] & 0x0F) * 4; memcpy(pseudoHeader, ipHdr+12, 8); pseudoHeader[8] = 0; pseudoHeader[9] = ipHdr[9]; pseudoHeader[10] = (count >> 8) & 0xFF; pseudoHeader[11] = (count & 0xFF); /* Checksum the pseudo-header */ sum += * (uint16_t *) pseudoHeader; sum += * ((uint16_t *) (pseudoHeader+2)); sum += * ((uint16_t *) (pseudoHeader+4)); sum += * ((uint16_t *) (pseudoHeader+6)); sum += * ((uint16_t *) (pseudoHeader+8)); sum += * ((uint16_t *) (pseudoHeader+10)); /* Checksum the TCP header and data */ while (count > 1) { memcpy(&tmp, addr, sizeof(tmp)); sum += (uint32_t) tmp; addr += sizeof(tmp); count -= sizeof(tmp); } if (count > 0) { sum += (unsigned char) *addr; } while(sum >> 16) { sum = (sum & 0xffff) + (sum >> 16); } return (uint16_t) ((~sum) & 0xFFFF); } /********************************************************************** *%FUNCTION: clampMSS *%ARGUMENTS: * packet -- PPPoE session packet * dir -- either "incoming" or "outgoing" * clampMss -- clamp value *%RETURNS: * Nothing *%DESCRIPTION: * Clamps MSS option if TCP SYN flag is set. ***********************************************************************/ void clampMSS(PPPoEPacket *packet, char const *dir, int clampMss) { unsigned char *tcpHdr; unsigned char *ipHdr; unsigned char *opt; unsigned char *endHdr; unsigned char *mssopt = NULL; uint16_t csum; int len, minlen; /* check PPP protocol type */ if (packet->payload[0] & 0x01) { /* 8 bit protocol type */ /* Is it IPv4? */ if (packet->payload[0] != 0x21) { /* Nope, ignore it */ return; } ipHdr = packet->payload + 1; minlen = 41; } else { /* 16 bit protocol type */ /* Is it IPv4? */ if (packet->payload[0] != 0x00 || packet->payload[1] != 0x21) { /* Nope, ignore it */ return; } ipHdr = packet->payload + 2; minlen = 42; } /* Is it too short? */ len = (int) ntohs(packet->length); if (len < minlen) { /* 20 byte IP header; 20 byte TCP header; at least 1 or 2 byte PPP protocol */ return; } /* Verify once more that it's IPv4 */ if ((ipHdr[0] & 0xF0) != 0x40) { return; } /* Is it a fragment that's not at the beginning of the packet? */ if ((ipHdr[6] & 0x1F) || ipHdr[7]) { /* Yup, don't touch! */ return; } /* Is it TCP? */ if (ipHdr[9] != 0x06) { return; } /* Get start of TCP header */ tcpHdr = ipHdr + (ipHdr[0] & 0x0F) * 4; /* Is SYN set? */ if (!(tcpHdr[13] & 0x02)) { return; } /* Compute and verify TCP checksum -- do not touch a packet with a bad checksum */ csum = computeTCPChecksum(ipHdr, tcpHdr); if (csum) { syslog(LOG_ERR, "Bad TCP checksum %x", (unsigned int) csum); /* Upper layers will drop it */ return; } /* Look for existing MSS option */ endHdr = tcpHdr + ((tcpHdr[12] & 0xF0) >> 2); opt = tcpHdr + 20; while (opt < endHdr) { if (!*opt) break; /* End of options */ switch(*opt) { case 1: opt++; break; case 2: if (opt[1] != 4) { /* Something fishy about MSS option length. */ syslog(LOG_ERR, "Bogus length for MSS option (%u) from %u.%u.%u.%u", (unsigned int) opt[1], (unsigned int) ipHdr[12], (unsigned int) ipHdr[13], (unsigned int) ipHdr[14], (unsigned int) ipHdr[15]); return; } mssopt = opt; break; default: if (opt[1] < 2) { /* Someone's trying to attack us? */ syslog(LOG_ERR, "Bogus TCP option length (%u) from %u.%u.%u.%u", (unsigned int) opt[1], (unsigned int) ipHdr[12], (unsigned int) ipHdr[13], (unsigned int) ipHdr[14], (unsigned int) ipHdr[15]); return; } opt += (opt[1]); break; } /* Found existing MSS option? */ if (mssopt) break; } /* If MSS exists and it's low enough, do nothing */ if (mssopt) { unsigned mss = mssopt[2] * 256 + mssopt[3]; if (mss <= clampMss) { return; } mssopt[2] = (((unsigned) clampMss) >> 8) & 0xFF; mssopt[3] = ((unsigned) clampMss) & 0xFF; } else { /* No MSS option. Don't add one; we'll have to use 536. */ return; } /* Recompute TCP checksum */ tcpHdr[16] = 0; tcpHdr[17] = 0; csum = computeTCPChecksum(ipHdr, tcpHdr); (* (uint16_t *) (tcpHdr+16)) = csum; } /*********************************************************************** *%FUNCTION: sendPADT *%ARGUMENTS: * conn -- PPPoE connection * msg -- if non-NULL, extra error message to include in PADT packet. *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADT packet ***********************************************************************/ void sendPADT(PPPoEConnection *conn, char const *msg) { PPPoEPacket packet; unsigned char *cursor = packet.payload; uint16_t plen = 0; /* Do nothing if no session established yet */ if (!conn->session) return; /* Do nothing if no discovery socket */ if (conn->discoverySocket < 0) return; memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_PADT; packet.session = conn->session; /* Reset Session to zero so there is no possibility of recursive calls to this function by any signal handler */ conn->session = 0; /* If we're using Host-Uniq, copy it over */ if (conn->hostUniq) { PPPoETag hostUniq; int len = (int) strlen(conn->hostUniq); hostUniq.type = htons(TAG_HOST_UNIQ); hostUniq.length = htons(len); memcpy(hostUniq.payload, conn->hostUniq, len); CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); memcpy(cursor, &hostUniq, len + TAG_HDR_SIZE); cursor += len + TAG_HDR_SIZE; plen += len + TAG_HDR_SIZE; } /* Copy error message */ if (msg) { PPPoETag err; size_t elen = strlen(msg); err.type = htons(TAG_GENERIC_ERROR); err.length = htons(elen); strcpy((char *) err.payload, msg); memcpy(cursor, &err, elen + TAG_HDR_SIZE); cursor += elen + TAG_HDR_SIZE; plen += elen + TAG_HDR_SIZE; } /* Copy cookie and relay-ID if needed */ if (conn->cookie.type) { CHECK_ROOM(cursor, packet.payload, ntohs(conn->cookie.length) + TAG_HDR_SIZE); memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; } if (conn->relayId.type) { CHECK_ROOM(cursor, packet.payload, ntohs(conn->relayId.length) + TAG_HDR_SIZE); memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; } packet.length = htons(plen); sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "SENT"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif syslog(LOG_INFO,"Sent PADT"); } /*********************************************************************** *%FUNCTION: sendPADTf *%ARGUMENTS: * conn -- PPPoE connection * msg -- printf-style format string * args -- arguments for msg *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADT packet with a formatted message ***********************************************************************/ void sendPADTf(PPPoEConnection *conn, char const *fmt, ...) { char msg[512]; va_list ap; va_start(ap, fmt); vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); msg[511] = 0; sendPADT(conn, msg); } /********************************************************************** *%FUNCTION: pktLogErrs *%ARGUMENTS: * pkt -- packet type (a string) * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data *%RETURNS: * Nothing *%DESCRIPTION: * Logs error tags ***********************************************************************/ void pktLogErrs(char const *pkt, uint16_t type, uint16_t len, unsigned char *data, void *extra) { char const *str; char const *fmt = "%s: %s: %.*s"; switch(type) { case TAG_SERVICE_NAME_ERROR: str = "Service-Name-Error"; break; case TAG_AC_SYSTEM_ERROR: str = "System-Error"; break; case TAG_GENERIC_ERROR: str = "Generic-Error"; break; default: return; } syslog(LOG_ERR, fmt, pkt, str, (int) len, data); fprintf(stderr, fmt, pkt, str, (int) len, data); fprintf(stderr, "\n"); } /********************************************************************** *%FUNCTION: parseLogErrs *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data *%RETURNS: * Nothing *%DESCRIPTION: * Picks error tags out of a packet and logs them. ***********************************************************************/ void parseLogErrs(uint16_t type, uint16_t len, unsigned char *data, void *extra) { pktLogErrs("PADT", type, len, data, extra); } /********************************************************************** *%FUNCTION: rp_strlcpy *%ARGUMENTS: * dst -- destination buffer * src -- source string * size -- size of destination buffer *%RETURNS: * Number of characters copied, excluding NUL terminator *%DESCRIPTION: * Copy at most size-1 characters from src to dst, * always NUL-terminating dst if size!=0. ***********************************************************************/ size_t rp_strlcpy(char *dst, const char *src, size_t size) { const char *orig_src = src; if (size == 0) { return 0; } while (--size != 0) { if ((*dst++ = *src++) == '\0') { break; } } if (size == 0) { *dst = '\0'; } return src - orig_src - 1; } rp-pppoe-4.0/src/relay.c0000644000175000017500000013022014422272526013304 0ustar dfsdfs/*********************************************************************** * * relay.c * * Implementation of PPPoE relay * * Copyright (C) 2001-2018 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * * $Id$ * ***********************************************************************/ #define _GNU_SOURCE 1 /* For SA_RESTART */ #include "config.h" #include #include #include "relay.h" #include #include #include #include #include #include #ifdef HAVE_SYS_UIO_H #include #endif #include #if defined(HAVE_LINUX_IF_H) #include #endif /* Interfaces (max MAX_INTERFACES) */ PPPoEInterface Interfaces[MAX_INTERFACES]; int NumInterfaces; /* Relay info */ int NumSessions; int MaxSessions; PPPoESession *AllSessions; PPPoESession *FreeSessions; PPPoESession *ActiveSessions; SessionHash *AllHashes; SessionHash *FreeHashes; SessionHash *Buckets[HASHTAB_SIZE]; volatile unsigned int Epoch = 0; volatile unsigned int CleanCounter = 0; /* How often to clean up stale sessions? */ #define MIN_CLEAN_PERIOD 30 /* Minimum period to run cleaner */ #define TIMEOUT_DIVISOR 20 /* How often to run cleaner per timeout period */ unsigned int CleanPeriod = MIN_CLEAN_PERIOD; /* How long a session can be idle before it is cleaned up? */ unsigned int IdleTimeout = MIN_CLEAN_PERIOD * TIMEOUT_DIVISOR; /* Pipe for breaking select() to initiate periodic cleaning */ int CleanPipe[2]; /* Our relay: if_index followed by peer_mac */ #define MY_RELAY_TAG_LEN (sizeof(int) + ETH_ALEN) /* Hack for daemonizing */ #define CLOSEFD 64 /********************************************************************** *%FUNCTION: keepDescriptor *%ARGUMENTS: * fd -- a file descriptor *%RETURNS: * 1 if descriptor should NOT be closed during daemonizing; 0 otherwise. ***********************************************************************/ static int keepDescriptor(int fd) { int i; if (fd == CleanPipe[0] || fd == CleanPipe[1]) return 1; for (i=0; ipayload, tag, ntohs(tag->length) + TAG_HDR_SIZE); } /********************************************************************** *%FUNCTION: insertBytes *%ARGUMENTS: * packet -- a PPPoE packet * loc -- location at which to insert bytes of data * bytes -- the data to insert * len -- length of data to insert *%RETURNS: * -1 if no room in packet; len otherwise. *%DESCRIPTION: * Inserts "len" bytes of data at location "loc" in "packet", moving all * other data up to make room. ***********************************************************************/ int insertBytes(PPPoEPacket *packet, unsigned char *loc, void const *bytes, int len) { int toMove; int plen = ntohs(packet->length); /* Sanity checks */ if (loc < packet->payload || loc > packet->payload + plen || len + plen > MAX_PPPOE_PAYLOAD) { return -1; } toMove = (packet->payload + plen) - loc; memmove(loc+len, loc, toMove); memcpy(loc, bytes, len); packet->length = htons(plen + len); return len; } /********************************************************************** *%FUNCTION: removeBytes *%ARGUMENTS: * packet -- a PPPoE packet * loc -- location at which to remove bytes of data * len -- length of data to remove *%RETURNS: * -1 if there was a problem, len otherwise *%DESCRIPTION: * Removes "len" bytes of data from location "loc" in "packet", moving all * other data down to close the gap ***********************************************************************/ int removeBytes(PPPoEPacket *packet, unsigned char *loc, int len) { int toMove; int plen = ntohs(packet->length); /* Sanity checks */ if (len < 0 || len > plen || loc < packet->payload || loc + len > packet->payload + plen) { return -1; } toMove = ((packet->payload + plen) - loc) - len; memmove(loc, loc+len, toMove); packet->length = htons(plen - len); return len; } /********************************************************************** *%FUNCTION: usage *%ARGUMENTS: * argv0 -- program name *%RETURNS: * Nothing *%DESCRIPTION: * Prints usage information and exits. ***********************************************************************/ void usage(char const *argv0) { fprintf(stderr, "Usage: %s [options]\n", argv0); fprintf(stderr, "Options:\n"); fprintf(stderr, " -S if_name -- Specify interface for PPPoE Server\n"); fprintf(stderr, " -C if_name -- Specify interface for PPPoE Client\n"); fprintf(stderr, " -B if_name -- Specify interface for both clients and server\n"); fprintf(stderr, " -n nsess -- Maxmimum number of sessions to relay\n"); fprintf(stderr, " -i timeout -- Idle timeout in seconds (0 = no timeout)\n"); fprintf(stderr, " -F -- Do not fork into background\n"); fprintf(stderr, " -h -- Print this help message\n"); fprintf(stderr, "\nPPPoE Version %s, Copyright (C) 2001-2006 Roaring Penguin Software Inc.\n", RP_VERSION); fprintf(stderr, " %*s Copyright (C) 2018-2023 Dianne Skoll\n", (int) strlen(RP_VERSION), ""); fprintf(stderr, "PPPoE comes with ABSOLUTELY NO WARRANTY.\n"); fprintf(stderr, "This is free software, and you are welcome to redistribute it under the terms\n"); fprintf(stderr, "of the GNU General Public License, version 2 or any later version.\n"); fprintf(stderr, "https://dianne.skoll.ca/projects/rp-pppoe/\n"); exit(EXIT_SUCCESS); } /********************************************************************** *%FUNCTION: main *%ARGUMENTS: * argc, argv -- usual suspects *%RETURNS: * EXIT_SUCCESS or EXIT_FAILURE *%DESCRIPTION: * Main program. Options: * -C ifname -- Use interface for PPPoE clients * -S ifname -- Use interface for PPPoE servers * -B ifname -- Use interface for both clients and servers * -n sessions -- Maximum of "n" sessions ***********************************************************************/ int main(int argc, char *argv[]) { int opt; int nsess = DEFAULT_SESSIONS; struct sigaction sa; int beDaemon = 1; if (getuid() != geteuid() || getgid() != getegid()) { fprintf(stderr, "SECURITY WARNING: pppoe-relay will NOT run suid or sgid. Fix your installation.\n"); exit(EXIT_FAILURE); } openlog("pppoe-relay", LOG_PID, LOG_DAEMON); while((opt = getopt(argc, argv, "hC:S:B:n:i:F")) != -1) { switch(opt) { case 'h': usage(argv[0]); break; case 'F': beDaemon = 0; break; case 'C': addInterface(optarg, 1, 0); break; case 'S': addInterface(optarg, 0, 1); break; case 'B': addInterface(optarg, 1, 1); break; case 'i': if (sscanf(optarg, "%u", &IdleTimeout) != 1) { fprintf(stderr, "Illegal argument to -i: should be -i timeout\n"); exit(EXIT_FAILURE); } CleanPeriod = IdleTimeout / TIMEOUT_DIVISOR; if (CleanPeriod < MIN_CLEAN_PERIOD) CleanPeriod = MIN_CLEAN_PERIOD; break; case 'n': if (sscanf(optarg, "%d", &nsess) != 1) { fprintf(stderr, "Illegal argument to -n: should be -n #sessions\n"); exit(EXIT_FAILURE); } if (nsess < 1 || nsess > 65534) { fprintf(stderr, "Illegal argument to -n: must range from 1 to 65534\n"); exit(EXIT_FAILURE); } break; default: usage(argv[0]); } } /* Check that at least two interfaces were defined */ if (NumInterfaces < 2) { fprintf(stderr, "%s: Must define at least two interfaces\n", argv[0]); exit(EXIT_FAILURE); } /* Make a pipe for the cleaner */ if (pipe(CleanPipe) < 0) { fatalSys("pipe"); } /* Set up alarm handler */ sa.sa_handler = alarmHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGALRM, &sa, NULL) < 0) { fatalSys("sigaction"); } /* Allocate memory for sessions, etc. */ initRelay(nsess); /* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */ if (beDaemon) { int i; i = fork(); if (i < 0) { fatalSys("fork"); } else if (i != 0) { /* parent */ exit(EXIT_SUCCESS); } setsid(); signal(SIGHUP, SIG_IGN); i = fork(); if (i < 0) { fatalSys("fork"); } else if (i != 0) { exit(EXIT_SUCCESS); } if (chdir("/") < 0) { fatalSys("chdir"); } closelog(); for (i=0; i= MAX_INTERFACES) { fprintf(stderr, "Too many interfaces (%d max)\n", MAX_INTERFACES); exit(EXIT_FAILURE); } i = &Interfaces[NumInterfaces++]; strncpy(i->name, ifname, IFNAMSIZ); i->name[IFNAMSIZ] = 0; i->discoverySock = openInterface(ifname, Eth_PPPOE_Discovery, i->mac, NULL); i->sessionSock = openInterface(ifname, Eth_PPPOE_Session, NULL, NULL); i->clientOK = clientOK; i->acOK = acOK; } /********************************************************************** *%FUNCTION: initRelay *%ARGUMENTS: * nsess -- maximum allowable number of sessions *%RETURNS: * Nothing *%DESCRIPTION: * Initializes relay hash table and session tables. ***********************************************************************/ void initRelay(int nsess) { int i; NumSessions = 0; MaxSessions = nsess; AllSessions = calloc(MaxSessions, sizeof(PPPoESession)); if (!AllSessions) { rp_fatal("Unable to allocate memory for PPPoE session table"); } AllHashes = calloc(MaxSessions*2, sizeof(SessionHash)); if (!AllHashes) { rp_fatal("Unable to allocate memory for PPPoE hash table"); } /* Initialize sessions in a linked list */ AllSessions[0].prev = NULL; if (MaxSessions > 1) { AllSessions[0].next = &AllSessions[1]; } else { AllSessions[0].next = NULL; } for (i=1; i 1) { AllSessions[MaxSessions-1].prev = &AllSessions[MaxSessions-2]; AllSessions[MaxSessions-1].next = NULL; } FreeSessions = AllSessions; ActiveSessions = NULL; /* Initialize session numbers which we hand out */ for (i=0; i= MaxSessions) { printErr("Maximum number of sessions reached -- cannot create new session"); return NULL; } /* Grab a free session */ sess = FreeSessions; FreeSessions = sess->next; NumSessions++; /* Link it to the active list */ sess->next = ActiveSessions; if (sess->next) { sess->next->prev = sess; } ActiveSessions = sess; sess->prev = NULL; sess->epoch = Epoch; /* Get two hash entries */ acHash = FreeHashes; cliHash = acHash->next; FreeHashes = cliHash->next; acHash->peer = cliHash; cliHash->peer = acHash; sess->acHash = acHash; sess->clientHash = cliHash; acHash->interface = ac; cliHash->interface = cli; memcpy(acHash->peerMac, acMac, ETH_ALEN); acHash->sesNum = acSes; acHash->ses = sess; memcpy(cliHash->peerMac, cliMac, ETH_ALEN); cliHash->sesNum = sess->sesNum; cliHash->ses = sess; addHash(acHash); addHash(cliHash); /* Log */ syslog(LOG_INFO, "Opened session: server=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d), client=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d)", acHash->peerMac[0], acHash->peerMac[1], acHash->peerMac[2], acHash->peerMac[3], acHash->peerMac[4], acHash->peerMac[5], acHash->interface->name, ntohs(acHash->sesNum), cliHash->peerMac[0], cliHash->peerMac[1], cliHash->peerMac[2], cliHash->peerMac[3], cliHash->peerMac[4], cliHash->peerMac[5], cliHash->interface->name, ntohs(cliHash->sesNum)); return sess; } /********************************************************************** *%FUNCTION: freeSession *%ARGUMENTS: * ses -- session to free * msg -- extra message to log on syslog. *%RETURNS: * Nothing *%DESCRIPTION: * Frees data used by a PPPoE session -- adds hashes and session back * to the free list ***********************************************************************/ void freeSession(PPPoESession *ses, char const *msg) { syslog(LOG_INFO, "Closed session: server=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d), client=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d): %s", ses->acHash->peerMac[0], ses->acHash->peerMac[1], ses->acHash->peerMac[2], ses->acHash->peerMac[3], ses->acHash->peerMac[4], ses->acHash->peerMac[5], ses->acHash->interface->name, ntohs(ses->acHash->sesNum), ses->clientHash->peerMac[0], ses->clientHash->peerMac[1], ses->clientHash->peerMac[2], ses->clientHash->peerMac[3], ses->clientHash->peerMac[4], ses->clientHash->peerMac[5], ses->clientHash->interface->name, ntohs(ses->clientHash->sesNum), msg); /* Unlink from active sessions */ if (ses->prev) { ses->prev->next = ses->next; } else { ActiveSessions = ses->next; } if (ses->next) { ses->next->prev = ses->prev; } /* Link onto free list -- this is a singly-linked list, so we do not care about prev */ ses->next = FreeSessions; FreeSessions = ses; unhash(ses->acHash); unhash(ses->clientHash); NumSessions--; } /********************************************************************** *%FUNCTION: unhash *%ARGUMENTS: * sh -- session hash to free *%RETURNS: * Nothing *%DESCRIPTION: * Frees a session hash -- takes it out of hash table and puts it on * free list. ***********************************************************************/ void unhash(SessionHash *sh) { unsigned int b = hash(sh->peerMac, sh->sesNum) % HASHTAB_SIZE; if (sh->prev) { sh->prev->next = sh->next; } else { Buckets[b] = sh->next; } if (sh->next) { sh->next->prev = sh->prev; } /* Add to free list (singly-linked) */ sh->next = FreeHashes; FreeHashes = sh; } /********************************************************************** *%FUNCTION: addHash *%ARGUMENTS: * sh -- a session hash *%RETURNS: * Nothing *%DESCRIPTION: * Adds a SessionHash to the hash table ***********************************************************************/ void addHash(SessionHash *sh) { unsigned int b = hash(sh->peerMac, sh->sesNum) % HASHTAB_SIZE; sh->next = Buckets[b]; sh->prev = NULL; if (sh->next) { sh->next->prev = sh; } Buckets[b] = sh; } /********************************************************************** *%FUNCTION: hash *%ARGUMENTS: * mac -- an Ethernet address * sesNum -- a session number *%RETURNS: * A hash value combining Ethernet address with session number. * Currently very simplistic; we may need to experiment with different * hash values. ***********************************************************************/ unsigned int hash(unsigned char const *mac, uint16_t sesNum) { unsigned int ans1 = ((unsigned int) mac[0]) | (((unsigned int) mac[1]) << 8) | (((unsigned int) mac[2]) << 16) | (((unsigned int) mac[3]) << 24); unsigned int ans2 = ((unsigned int) sesNum) | (((unsigned int) mac[4]) << 16) | (((unsigned int) mac[5]) << 24); return ans1 ^ ans2; } /********************************************************************** *%FUNCTION: findSession *%ARGUMENTS: * mac -- an Ethernet address * sesNum -- a session number *%RETURNS: * The session hash for peer address "mac", session number sesNum ***********************************************************************/ SessionHash * findSession(unsigned char const *mac, uint16_t sesNum) { unsigned int b = hash(mac, sesNum) % HASHTAB_SIZE; SessionHash *sh = Buckets[b]; while(sh) { if (!memcmp(mac, sh->peerMac, ETH_ALEN) && sesNum == sh->sesNum) { return sh; } sh = sh->next; } return NULL; } /********************************************************************** *%FUNCTION: fatalSys *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to stderr and syslog and exits. ***********************************************************************/ void fatalSys(char const *str) { printErr("%.256s: %.256s", str, strerror(errno)); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: sysErr *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to syslog. ***********************************************************************/ void sysErr(char const *str) { printErr("%.256s: %.256s", str, strerror(errno)); } /********************************************************************** *%FUNCTION: rp_fatal *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message to stderr and syslog and exits. ***********************************************************************/ void rp_fatal(char const *str) { printErr("%s", str); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: relayLoop *%ARGUMENTS: * None *%RETURNS: * Nothing *%DESCRIPTION: * Runs the relay loop. This function never returns ***********************************************************************/ void relayLoop() { fd_set readable, readableCopy; int maxFD; int i, r; int sock; /* Build the select set */ FD_ZERO(&readable); maxFD = 0; for (i=0; i maxFD) maxFD = sock; FD_SET(sock, &readable); sock = Interfaces[i].sessionSock; if (sock > maxFD) maxFD = sock; FD_SET(sock, &readable); if (CleanPipe[0] > maxFD) maxFD = CleanPipe[0]; FD_SET(CleanPipe[0], &readable); } maxFD++; for(;;) { readableCopy = readable; for(;;) { r = select(maxFD, &readableCopy, NULL, NULL, NULL); if (r >= 0 || errno != EINTR) break; } if (r < 0) { sysErr("select (relayLoop)"); continue; } /* Handle session packets first */ for (i=0; idiscoverySock, &packet, &size) < 0) { return; } /* Ignore unknown code/version */ if (PPPOE_VER(packet.vertype) != 1 || PPPOE_TYPE(packet.vertype) != 1) { return; } /* Validate length */ if (ntohs(packet.length) + HDR_SIZE > size) { syslog(LOG_ERR, "Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); return; } /* Drop Ethernet frame padding */ if (size > ntohs(packet.length) + HDR_SIZE) { size = ntohs(packet.length) + HDR_SIZE; } switch(packet.code) { case CODE_PADT: relayHandlePADT(iface, &packet, size); break; case CODE_PADI: relayHandlePADI(iface, &packet, size); break; case CODE_PADO: relayHandlePADO(iface, &packet, size); break; case CODE_PADR: relayHandlePADR(iface, &packet, size); break; case CODE_PADS: relayHandlePADS(iface, &packet, size); break; default: syslog(LOG_ERR, "Discovery packet on %s with unknown code %d", iface->name, (int) packet.code); } } /********************************************************************** *%FUNCTION: relayGotSessionPacket *%ARGUMENTS: * iface -- interface on which packet is waiting *%RETURNS: * Nothing *%DESCRIPTION: * Receives and processes a session packet. ***********************************************************************/ void relayGotSessionPacket(PPPoEInterface const *iface) { PPPoEPacket packet; int size; SessionHash *sh; PPPoESession *ses; if (receivePacket(iface->sessionSock, &packet, &size) < 0) { return; } /* Ignore unknown code/version */ if (PPPOE_VER(packet.vertype) != 1 || PPPOE_TYPE(packet.vertype) != 1) { return; } /* Must be a session packet */ if (packet.code != CODE_SESS) { syslog(LOG_ERR, "Session packet with code %d", (int) packet.code); return; } /* Ignore session packets whose destination address isn't ours */ if (memcmp(packet.ethHdr.h_dest, iface->mac, ETH_ALEN)) { return; } /* Validate length */ if (ntohs(packet.length) + HDR_SIZE > size) { syslog(LOG_ERR, "Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); return; } /* Drop Ethernet frame padding */ if (size > ntohs(packet.length) + HDR_SIZE) { size = ntohs(packet.length) + HDR_SIZE; } /* We're in business! Find the hash */ sh = findSession(packet.ethHdr.h_source, packet.session); if (!sh) { /* Don't log this. Someone could be running the client and the relay on the same box. */ return; } /* Relay it */ ses = sh->ses; ses->epoch = Epoch; sh = sh->peer; packet.session = sh->sesNum; memcpy(packet.ethHdr.h_source, sh->interface->mac, ETH_ALEN); memcpy(packet.ethHdr.h_dest, sh->peerMac, ETH_ALEN); #if 0 fprintf(stderr, "Relaying %02x:%02x:%02x:%02x:%02x:%02x(%s:%d) to %02x:%02x:%02x:%02x:%02x:%02x(%s:%d)\n", sh->peer->peerMac[0], sh->peer->peerMac[1], sh->peer->peerMac[2], sh->peer->peerMac[3], sh->peer->peerMac[4], sh->peer->peerMac[5], sh->peer->interface->name, ntohs(sh->peer->sesNum), sh->peerMac[0], sh->peerMac[1], sh->peerMac[2], sh->peerMac[3], sh->peerMac[4], sh->peerMac[5], sh->interface->name, ntohs(sh->sesNum)); #endif sendPacket(NULL, sh->interface->sessionSock, &packet, size); } /********************************************************************** *%FUNCTION: relayHandlePADT *%ARGUMENTS: * iface -- interface on which packet was received * packet -- the PADT packet *%RETURNS: * Nothing *%DESCRIPTION: * Receives and processes a PADT packet. ***********************************************************************/ void relayHandlePADT(PPPoEInterface const *iface, PPPoEPacket *packet, int size) { SessionHash *sh; PPPoESession *ses; /* Destination address must be interface's MAC address */ if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) { return; } sh = findSession(packet->ethHdr.h_source, packet->session); if (!sh) { return; } /* Relay the PADT to the peer */ sh = sh->peer; ses = sh->ses; packet->session = sh->sesNum; memcpy(packet->ethHdr.h_source, sh->interface->mac, ETH_ALEN); memcpy(packet->ethHdr.h_dest, sh->peerMac, ETH_ALEN); sendPacket(NULL, sh->interface->sessionSock, packet, size); /* Destroy the session */ freeSession(ses, "Received PADT"); } /********************************************************************** *%FUNCTION: relayHandlePADI *%ARGUMENTS: * iface -- interface on which packet was received * packet -- the PADI packet *%RETURNS: * Nothing *%DESCRIPTION: * Receives and processes a PADI packet. ***********************************************************************/ void relayHandlePADI(PPPoEInterface const *iface, PPPoEPacket *packet, int size) { PPPoETag tag; unsigned char *loc; int i, r; int ifIndex; /* Can a client legally be behind this interface? */ if (!iface->clientOK) { syslog(LOG_ERR, "PADI packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Source address must be unicast */ if (NOT_UNICAST(packet->ethHdr.h_source)) { syslog(LOG_ERR, "PADI packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Destination address must be broadcast */ if (NOT_BROADCAST(packet->ethHdr.h_dest)) { syslog(LOG_ERR, "PADI packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not to a broadcast address", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Get array index of interface */ ifIndex = iface - Interfaces; loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag); if (!loc) { tag.type = htons(TAG_RELAY_SESSION_ID); tag.length = htons(MY_RELAY_TAG_LEN); memcpy(tag.payload, &ifIndex, sizeof(ifIndex)); memcpy(tag.payload+sizeof(ifIndex), packet->ethHdr.h_source, ETH_ALEN); /* Add a relay tag if there's room */ r = addTag(packet, &tag); if (r < 0) return; size += r; } else { /* We do not re-use relay-id tags. Drop the frame. The RFC says the relay agent SHOULD return a Generic-Error tag, but this does not make sense for PADI packets. */ return; } /* Broadcast the PADI on all AC-capable interfaces except the interface on which it came */ for (i=0; i < NumInterfaces; i++) { if (iface == &Interfaces[i]) continue; if (!Interfaces[i].acOK) continue; memcpy(packet->ethHdr.h_source, Interfaces[i].mac, ETH_ALEN); sendPacket(NULL, Interfaces[i].discoverySock, packet, size); } } /********************************************************************** *%FUNCTION: relayHandlePADO *%ARGUMENTS: * iface -- interface on which packet was received * packet -- the PADO packet *%RETURNS: * Nothing *%DESCRIPTION: * Receives and processes a PADO packet. ***********************************************************************/ void relayHandlePADO(PPPoEInterface const *iface, PPPoEPacket *packet, int size) { PPPoETag tag; unsigned char *loc; int ifIndex; int acIndex; /* Can a server legally be behind this interface? */ if (!iface->acOK) { syslog(LOG_ERR, "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } acIndex = iface - Interfaces; /* Source address can't be broadcast */ if (BROADCAST(packet->ethHdr.h_source)) { syslog(LOG_ERR, "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s from a broadcast address", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Destination address must be interface's MAC address */ if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) { return; } /* Find relay tag */ loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag); if (!loc) { syslog(LOG_ERR, "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have Relay-Session-Id tag", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* If it's the wrong length, ignore it */ if (ntohs(tag.length) != MY_RELAY_TAG_LEN) { syslog(LOG_ERR, "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have correct length Relay-Session-Id tag", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Extract interface index */ memcpy(&ifIndex, tag.payload, sizeof(ifIndex)); if (ifIndex < 0 || ifIndex >= NumInterfaces || !Interfaces[ifIndex].clientOK || iface == &Interfaces[ifIndex]) { syslog(LOG_ERR, "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s has invalid interface in Relay-Session-Id tag", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Replace Relay-ID tag with opposite-direction tag */ memcpy(loc+TAG_HDR_SIZE, &acIndex, sizeof(acIndex)); memcpy(loc+TAG_HDR_SIZE+sizeof(ifIndex), packet->ethHdr.h_source, ETH_ALEN); /* Set destination address to MAC address in relay ID */ memcpy(packet->ethHdr.h_dest, tag.payload + sizeof(ifIndex), ETH_ALEN); /* Set source address to MAC address of interface */ memcpy(packet->ethHdr.h_source, Interfaces[ifIndex].mac, ETH_ALEN); /* Send the PADO to the proper client */ sendPacket(NULL, Interfaces[ifIndex].discoverySock, packet, size); } /********************************************************************** *%FUNCTION: relayHandlePADR *%ARGUMENTS: * iface -- interface on which packet was received * packet -- the PADR packet *%RETURNS: * Nothing *%DESCRIPTION: * Receives and processes a PADR packet. ***********************************************************************/ void relayHandlePADR(PPPoEInterface const *iface, PPPoEPacket *packet, int size) { PPPoETag tag; unsigned char *loc; int ifIndex; int cliIndex; /* Can a client legally be behind this interface? */ if (!iface->clientOK) { syslog(LOG_ERR, "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } cliIndex = iface - Interfaces; /* Source address must be unicast */ if (NOT_UNICAST(packet->ethHdr.h_source)) { syslog(LOG_ERR, "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Destination address must be interface's MAC address */ if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) { return; } /* Find relay tag */ loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag); if (!loc) { syslog(LOG_ERR, "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have Relay-Session-Id tag", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* If it's the wrong length, ignore it */ if (ntohs(tag.length) != MY_RELAY_TAG_LEN) { syslog(LOG_ERR, "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have correct length Relay-Session-Id tag", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Extract interface index */ memcpy(&ifIndex, tag.payload, sizeof(ifIndex)); if (ifIndex < 0 || ifIndex >= NumInterfaces || !Interfaces[ifIndex].acOK || iface == &Interfaces[ifIndex]) { syslog(LOG_ERR, "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s has invalid interface in Relay-Session-Id tag", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Replace Relay-ID tag with opposite-direction tag */ memcpy(loc+TAG_HDR_SIZE, &cliIndex, sizeof(cliIndex)); memcpy(loc+TAG_HDR_SIZE+sizeof(ifIndex), packet->ethHdr.h_source, ETH_ALEN); /* Set destination address to MAC address in relay ID */ memcpy(packet->ethHdr.h_dest, tag.payload + sizeof(ifIndex), ETH_ALEN); /* Set source address to MAC address of interface */ memcpy(packet->ethHdr.h_source, Interfaces[ifIndex].mac, ETH_ALEN); /* Send the PADR to the proper access concentrator */ sendPacket(NULL, Interfaces[ifIndex].discoverySock, packet, size); } /********************************************************************** *%FUNCTION: relayHandlePADS *%ARGUMENTS: * iface -- interface on which packet was received * packet -- the PADS packet *%RETURNS: * Nothing *%DESCRIPTION: * Receives and processes a PADS packet. ***********************************************************************/ void relayHandlePADS(PPPoEInterface const *iface, PPPoEPacket *packet, int size) { PPPoETag tag; unsigned char *loc; int ifIndex; PPPoESession *ses = NULL; SessionHash *sh; /* Can a server legally be behind this interface? */ if (!iface->acOK) { syslog(LOG_ERR, "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Source address must be unicast */ if (NOT_UNICAST(packet->ethHdr.h_source)) { syslog(LOG_ERR, "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Destination address must be interface's MAC address */ if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) { return; } /* Find relay tag */ loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag); if (!loc) { syslog(LOG_ERR, "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have Relay-Session-Id tag", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* If it's the wrong length, ignore it */ if (ntohs(tag.length) != MY_RELAY_TAG_LEN) { syslog(LOG_ERR, "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have correct length Relay-Session-Id tag", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* Extract interface index */ memcpy(&ifIndex, tag.payload, sizeof(ifIndex)); if (ifIndex < 0 || ifIndex >= NumInterfaces || !Interfaces[ifIndex].clientOK || iface == &Interfaces[ifIndex]) { syslog(LOG_ERR, "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s has invalid interface in Relay-Session-Id tag", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], iface->name); return; } /* If session ID is zero, it's the AC respoding with an error. Just relay it; do not create a session */ if (packet->session != htons(0)) { /* Check for existing session */ sh = findSession(packet->ethHdr.h_source, packet->session); if (sh) ses = sh->ses; /* If already an existing session, assume it's a duplicate PADS. Send the frame, but do not create a new session. Is this the right thing to do? Arguably, should send an error to the client and a PADT to the server, because this could happen due to a server crash and reboot. */ if (!ses) { /* Create a new session */ ses = createSession(iface, &Interfaces[ifIndex], packet->ethHdr.h_source, loc + TAG_HDR_SIZE + sizeof(ifIndex), packet->session); if (!ses) { /* Can't allocate session -- send error PADS to client and PADT to server */ PPPoETag hostUniq, *hu; if (findTag(packet, TAG_HOST_UNIQ, &hostUniq)) { hu = &hostUniq; } else { hu = NULL; } relaySendError(CODE_PADS, htons(0), &Interfaces[ifIndex], loc + TAG_HDR_SIZE + sizeof(ifIndex), hu, "RP-PPPoE: Relay: Unable to allocate session"); relaySendError(CODE_PADT, packet->session, iface, packet->ethHdr.h_source, NULL, "RP-PPPoE: Relay: Unable to allocate session"); return; } } /* Replace session number */ packet->session = ses->sesNum; } /* Remove relay-ID tag */ removeBytes(packet, loc, MY_RELAY_TAG_LEN + TAG_HDR_SIZE); size -= (MY_RELAY_TAG_LEN + TAG_HDR_SIZE); /* Set destination address to MAC address in relay ID */ memcpy(packet->ethHdr.h_dest, tag.payload + sizeof(ifIndex), ETH_ALEN); /* Set source address to MAC address of interface */ memcpy(packet->ethHdr.h_source, Interfaces[ifIndex].mac, ETH_ALEN); /* Send the PADS to the proper client */ sendPacket(NULL, Interfaces[ifIndex].discoverySock, packet, size); } /********************************************************************** *%FUNCTION: relaySendError *%ARGUMENTS: * code -- PPPoE packet code (PADS or PADT, typically) * session -- PPPoE session number * iface -- interface on which to send frame * mac -- Ethernet address to which frame should be sent * hostUniq -- if non-NULL, a hostUniq tag to add to error frame * errMsg -- error message to insert into Generic-Error tag. *%RETURNS: * Nothing *%DESCRIPTION: * Sends either a PADS or PADT packet with a Generic-Error tag and an * error message. ***********************************************************************/ void relaySendError(unsigned char code, uint16_t session, PPPoEInterface const *iface, unsigned char const *mac, PPPoETag const *hostUniq, char const *errMsg) { PPPoEPacket packet; PPPoETag errTag; int size; memcpy(packet.ethHdr.h_source, iface->mac, ETH_ALEN); memcpy(packet.ethHdr.h_dest, mac, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = code; packet.session = session; packet.length = htons(0); if (hostUniq) { if (addTag(&packet, hostUniq) < 0) return; } errTag.type = htons(TAG_GENERIC_ERROR); errTag.length = htons(strlen(errMsg)); strcpy((char *) errTag.payload, errMsg); if (addTag(&packet, &errTag) < 0) return; size = ntohs(packet.length) + HDR_SIZE; if (code == CODE_PADT) { sendPacket(NULL, iface->discoverySock, &packet, size); } else { sendPacket(NULL, iface->sessionSock, &packet, size); } } /********************************************************************** *%FUNCTION: alarmHandler *%ARGUMENTS: * sig -- signal number *%RETURNS: * Nothing *%DESCRIPTION: * SIGALRM handler. Increments Epoch; if necessary, writes a byte of * data to the alarm pipe to trigger the stale-session cleaner. ***********************************************************************/ void alarmHandler(int sig) { alarm(1); Epoch++; CleanCounter++; if (CleanCounter == CleanPeriod) { #pragma GCC diagnostic ignored "-Wunused-result" write(CleanPipe[1], "", 1); #pragma GCC diagnostic warning "-Wunused-result" } } /********************************************************************** *%FUNCTION: cleanSessions *%ARGUMENTS: * None *%RETURNS: * Nothing *%DESCRIPTION: * Goes through active sessions and cleans sessions idle for longer * than IdleTimeout seconds. ***********************************************************************/ void cleanSessions(void) { PPPoESession *cur, *next; cur = ActiveSessions; while(cur) { next = cur->next; if (Epoch - cur->epoch > IdleTimeout) { /* Send PADT to each peer */ relaySendError(CODE_PADT, cur->acHash->sesNum, cur->acHash->interface, cur->acHash->peerMac, NULL, "RP-PPPoE: Relay: Session exceeded idle timeout"); relaySendError(CODE_PADT, cur->clientHash->sesNum, cur->clientHash->interface, cur->clientHash->peerMac, NULL, "RP-PPPoE: Relay: Session exceeded idle timeout"); freeSession(cur, "Idle Timeout"); } cur = next; } } rp-pppoe-4.0/src/install-sh0000755000175000017500000001124514422272526014035 0ustar dfsdfs#! /bin/sh # # install - install a program, script, or datafile # This comes from X11R5. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. # # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 rp-pppoe-4.0/src/pppoe.h0000644000175000017500000002052314422272526013324 0ustar dfsdfs/*********************************************************************** * * pppoe.h * * Declaration of various PPPoE constants * * Copyright (C) 2000-2012 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * * $Id$ * ***********************************************************************/ #include "config.h" extern int IsSetID; #define _POSIX_SOURCE 1 /* For sigaction defines */ #include #include #include #include #include #include /* Ethernet frame types according to RFC 2516 */ #define ETH_PPPOE_DISCOVERY 0x8863 #define ETH_PPPOE_SESSION 0x8864 /* But some brain-dead peers disobey the RFC, so frame types are variables */ extern uint16_t Eth_PPPOE_Discovery; extern uint16_t Eth_PPPOE_Session; extern void switchToRealID(void); extern void switchToEffectiveID(void); extern void dropPrivs(void); /* PPPoE codes */ #define CODE_PADI 0x09 #define CODE_PADO 0x07 #define CODE_PADR 0x19 #define CODE_PADS 0x65 #define CODE_PADT 0xA7 /* Extensions from draft-carrel-info-pppoe-ext-00 */ /* I do NOT like PADM or PADN, but they are here for completeness */ #define CODE_PADM 0xD3 #define CODE_PADN 0xD4 #define CODE_SESS 0x00 /* PPPoE Tags */ #define TAG_END_OF_LIST 0x0000 #define TAG_SERVICE_NAME 0x0101 #define TAG_AC_NAME 0x0102 #define TAG_HOST_UNIQ 0x0103 #define TAG_AC_COOKIE 0x0104 #define TAG_VENDOR_SPECIFIC 0x0105 #define TAG_RELAY_SESSION_ID 0x0110 #define TAG_PPP_MAX_PAYLOAD 0x0120 #define TAG_SERVICE_NAME_ERROR 0x0201 #define TAG_AC_SYSTEM_ERROR 0x0202 #define TAG_GENERIC_ERROR 0x0203 /* Extensions from draft-carrel-info-pppoe-ext-00 */ /* I do NOT like these tags one little bit */ #define TAG_HURL 0x111 #define TAG_MOTM 0x112 #define TAG_IP_ROUTE_ADD 0x121 /* Discovery phase states */ #define STATE_SENT_PADI 0 #define STATE_RECEIVED_PADO 1 #define STATE_SENT_PADR 2 #define STATE_SESSION 3 #define STATE_TERMINATED 4 /* How many PADI/PADS attempts? */ #define MAX_PADI_ATTEMPTS 3 /* Initial timeout for PADO/PADS */ #define PADI_TIMEOUT 5 /* States for scanning PPP frames */ #define STATE_WAITFOR_FRAME_ADDR 0 #define STATE_DROP_PROTO 1 #define STATE_BUILDING_PACKET 2 /* Special PPP frame characters */ #define FRAME_ESC 0x7D #define FRAME_FLAG 0x7E #define FRAME_ADDR 0xFF #define FRAME_CTRL 0x03 #define FRAME_ENC 0x20 #define IPV4ALEN 4 #define SMALLBUF 256 /* Allow for 1500-byte PPPoE data which makes the Ethernet packet size bigger by 8 bytes */ #define ETH_JUMBO_LEN (ETH_DATA_LEN+8) /* A PPPoE Packet, including Ethernet headers */ typedef struct PPPoEPacketStruct { struct ethhdr ethHdr; /* Ethernet header */ unsigned int vertype:8; /* PPPoE Version (high nibble) and Type (low nibble) (must both be 1) */ unsigned int code:8; /* PPPoE code */ unsigned int session:16; /* PPPoE session */ unsigned int length:16; /* Payload length */ unsigned char payload[ETH_JUMBO_LEN]; /* A bit of room to spare */ } PPPoEPacket; #define PPPOE_VER(vt) ((vt) >> 4) #define PPPOE_TYPE(vt) ((vt) & 0xf) #define PPPOE_VER_TYPE(v, t) (((v) << 4) | (t)) /* Header size of a PPPoE packet */ #define PPPOE_OVERHEAD 6 /* type, code, session, length */ #define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD) #define MAX_PPPOE_PAYLOAD (ETH_JUMBO_LEN - PPPOE_OVERHEAD) #define PPP_OVERHEAD 2 #define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - PPP_OVERHEAD) #define TOTAL_OVERHEAD (PPPOE_OVERHEAD + PPP_OVERHEAD) /* Normal PPPoE MTU without jumbo frames */ #define ETH_PPPOE_MTU (ETH_DATA_LEN - TOTAL_OVERHEAD) /* PPPoE Tag */ typedef struct PPPoETagStruct { unsigned int type:16; /* tag type */ unsigned int length:16; /* Length of payload */ unsigned char payload[ETH_DATA_LEN]; } PPPoETag; /* Header size of a PPPoE tag */ #define TAG_HDR_SIZE 4 /* Chunk to read from stdin */ #define READ_CHUNK 4096 /* Function passed to parsePacket */ typedef void ParseFunc(uint16_t type, uint16_t len, unsigned char *data, void *extra); #define PPPINITFCS16 0xffff /* Initial FCS value */ /* Keep track of the state of a connection -- collect everything in one spot */ typedef struct PPPoEConnectionStruct { int discoveryState; /* Where we are in discovery */ int discoverySocket; /* Raw socket for discovery frames */ int sessionSocket; /* Raw socket for session frames */ unsigned char myEth[ETH_ALEN]; /* My MAC address */ unsigned char peerEth[ETH_ALEN]; /* Peer's MAC address */ unsigned char req_peer_mac[ETH_ALEN]; /* required peer MAC address */ unsigned char req_peer; /* require mac addr to match req_peer_mac */ uint16_t session; /* Session ID */ char *ifName; /* Interface name */ char *serviceName; /* Desired service name, if any */ char *acName; /* Desired AC name, if any */ int synchronous; /* Use synchronous PPP */ char *hostUniq; /* Host-Uniq tag, if any */ int printACNames; /* Just print AC names */ int skipDiscovery; /* Skip discovery */ int noDiscoverySocket; /* Don't even open discovery socket */ int killSession; /* Kill session and exit */ FILE *debugFile; /* Debug file for dumping packets */ int numPADOs; /* Number of PADO packets received */ PPPoETag cookie; /* We have to send this if we get it */ PPPoETag relayId; /* Ditto */ int PADSHadError; /* If PADS had an error tag */ int discoveryTimeout; /* Timeout for discovery packets */ int seenMaxPayload; int mtu; int mru; } PPPoEConnection; /* Structure used to determine acceptable PADO or PADS packet */ struct PacketCriteria { PPPoEConnection *conn; int acNameOK; int serviceNameOK; int seenACName; int seenServiceName; int gotError; }; /* Function Prototypes */ uint16_t etherType(PPPoEPacket *packet); int openInterface(char const *ifname, uint16_t type, unsigned char *hwaddr, uint16_t *mtu); int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size); int receivePacket(int sock, PPPoEPacket *pkt, int *size); void fatalSys(char const *str); void rp_fatal(char const *str); __attribute__((format (printf, 1, 2))) void printErr(char const *fmt, ...); void sysErr(char const *str); #ifdef DEBUGGING_ENABLED void dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir); void dumpHex(FILE *fp, unsigned char const *buf, int len); #endif int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra); void parseLogErrs(uint16_t typ, uint16_t len, unsigned char *data, void *xtra); void pktLogErrs(char const *pkt, uint16_t typ, uint16_t len, unsigned char *data, void *xtra); void syncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet); void asyncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet); void asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss); void syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss); void sendPADT(PPPoEConnection *conn, char const *msg); void sendPADTf(PPPoEConnection *conn, char const *fmt, ...); void sendSessionPacket(PPPoEConnection *conn, PPPoEPacket *packet, int len); void initPPP(void); void clampMSS(PPPoEPacket *packet, char const *dir, int clampMss); uint16_t computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr); uint16_t pppFCS16(uint16_t fcs, unsigned char *cp, int len); void discovery(PPPoEConnection *conn); unsigned char *findTag(PPPoEPacket *packet, uint16_t tagType, PPPoETag *tag); size_t rp_strlcpy(char *dst, const char *src, size_t size); #define SET_STRING(var, val) do { if (var) free(var); var = strdup(val); if (!var) rp_fatal("strdup failed"); } while(0); #define CHECK_ROOM(cursor, start, len) \ do {\ if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \ syslog(LOG_ERR, "Would create too-long packet"); \ return; \ } \ } while(0) /* True if Ethernet address is broadcast or multicast */ #define NOT_UNICAST(e) ((e[0] & 0x01) != 0) #define BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) == 0xFF) #define NOT_BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) != 0xFF) rp-pppoe-4.0/src/if.c0000644000175000017500000001422214422272526012571 0ustar dfsdfs/*********************************************************************** * * if.c * * Implementation of user-space PPPoE redirector for Linux. * * Functions for opening a raw socket and reading/writing raw Ethernet frames. * * Copyright (C) 2000-2012 by Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #include #include #include #include #include #include #include #include #include "pppoe.h" #include #include /* Initialize frame types to RFC 2516 values. Some broken peers apparently use different frame types... sigh... */ uint16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; uint16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; /********************************************************************** *%FUNCTION: etherType *%ARGUMENTS: * packet -- a received PPPoE packet *%RETURNS: * ethernet packet type (see /usr/include/net/ethertypes.h) *%DESCRIPTION: * Checks the ethernet packet header to determine its type. * We should only be receiving DISCOVERY and SESSION types if the BPF * is set up correctly. Logs an error if an unexpected type is received. * Note that the ethernet type names come from "pppoe.h" and the packet * packet structure names use the LINUX dialect to maintain consistency * with the rest of this file. See the BSD section of "pppoe.h" for * translations of the data structure names. ***********************************************************************/ uint16_t etherType(PPPoEPacket *packet) { uint16_t type = (uint16_t) ntohs(packet->ethHdr.h_proto); if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { syslog(LOG_ERR, "Invalid ether type 0x%x", type); } return type; } /********************************************************************** *%FUNCTION: openInterface *%ARGUMENTS: * ifname -- name of interface * type -- Ethernet frame type * hwaddr -- if non-NULL, set to the hardware address * mtu -- if non-NULL, set to the MTU *%RETURNS: * A raw socket for talking to the Ethernet card. Exits on error. *%DESCRIPTION: * Opens a raw Ethernet socket ***********************************************************************/ int openInterface(char const *ifname, uint16_t type, unsigned char *hwaddr, uint16_t *mtu) { int optval=1; int fd; struct ifreq ifr; int domain, stype; #ifdef HAVE_STRUCT_SOCKADDR_LL struct sockaddr_ll sa; #else struct sockaddr sa; #endif memset(&sa, 0, sizeof(sa)); #ifdef HAVE_STRUCT_SOCKADDR_LL domain = PF_PACKET; stype = SOCK_RAW; #else domain = PF_INET; stype = SOCK_PACKET; #endif if ((fd = socket(domain, stype, htons(type))) < 0) { /* Give a more helpful message for the common error case */ if (errno == EPERM) { rp_fatal("Cannot create raw socket -- pppoe must be run as root."); } fatalSys("socket"); } if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { fatalSys("setsockopt"); } /* Fill in hardware address */ if (hwaddr) { rp_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { fatalSys("ioctl(SIOCGIFHWADDR)"); } memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); #ifdef ARPHRD_ETHER if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { char buffer[256]; sprintf(buffer, "Interface %.16s is not Ethernet", ifname); rp_fatal(buffer); } #endif if (NOT_UNICAST(hwaddr)) { char buffer[256]; sprintf(buffer, "Interface %.16s has broadcast/multicast MAC address??", ifname); rp_fatal(buffer); } } /* Sanity check on MTU */ rp_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { fatalSys("ioctl(SIOCGIFMTU)"); } if (ifr.ifr_mtu < ETH_DATA_LEN) { printErr("Interface %.16s has MTU of %d -- should be %d. You may have serious connection problems.", ifname, ifr.ifr_mtu, ETH_DATA_LEN); } if (mtu) *mtu = ifr.ifr_mtu; #ifdef HAVE_STRUCT_SOCKADDR_LL /* Get interface index */ sa.sll_family = AF_PACKET; sa.sll_protocol = htons(type); rp_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index"); } sa.sll_ifindex = ifr.ifr_ifindex; #else strcpy(sa.sa_data, ifname); #endif /* We're only interested in packets on specified interface */ if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { fatalSys("bind"); } return fd; } /*********************************************************************** *%FUNCTION: sendPacket *%ARGUMENTS: * sock -- socket to send to * pkt -- the packet to transmit * size -- size of packet (in bytes) *%RETURNS: * 0 on success; -1 on failure *%DESCRIPTION: * Transmits a packet ***********************************************************************/ int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) { #if defined(HAVE_STRUCT_SOCKADDR_LL) if (send(sock, pkt, size, 0) < 0 && (errno != ENOBUFS)) { sysErr("send (sendPacket)"); return -1; } #else struct sockaddr sa; if (!conn) { rp_fatal("relay and server not supported on Linux 2.0 kernels"); } strcpy(sa.sa_data, conn->ifName); if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) { sysErr("sendto (sendPacket)"); return -1; } #endif return 0; } /*********************************************************************** *%FUNCTION: receivePacket *%ARGUMENTS: * sock -- socket to read from * pkt -- place to store the received packet * size -- set to size of packet in bytes *%RETURNS: * >= 0 if all OK; < 0 if error *%DESCRIPTION: * Receives a packet ***********************************************************************/ int receivePacket(int sock, PPPoEPacket *pkt, int *size) { if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { sysErr("recv (receivePacket)"); return -1; } return 0; } rp-pppoe-4.0/src/configure.ac0000644000175000017500000001432514422272526014321 0ustar dfsdfsdnl Process this file with autoconf to produce a configure script. dnl SPDX-License-Identifier: GPL-2.0-or-later AC_INIT AC_CONFIG_SRCDIR([pppoe.c]) dnl pppd directory for kernel-mode PPPoE PPPD_DIR=ppp-2.4.1.pppoe2 AC_CONFIG_HEADERS([config.h]) AC_PREFIX_DEFAULT(/usr) dnl Checks for programs. AC_PROG_CC AC_PROG_RANLIB AC_CHECK_TOOL([AR],[ar],[/bin/false]) dnl Checks for libraries. dnl Checks for header files. AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(net/if_arp.h sys/uio.h net/ethernet.h linux/if_packet.h) AC_CHECK_HEADERS(linux/if.h, [], [], [#include]) AC_CHECK_HEADERS(linux/if_pppox.h, [], [], [ #include #include #include #include ]) dnl Check for sockaddr_ll AC_MSG_CHECKING(for struct sockaddr_ll) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include ]], [[struct sockaddr_ll sa;]])],[ac_cv_struct_sockaddr_ll=yes],[ac_cv_struct_sockaddr_ll=no]) AC_MSG_RESULT($ac_cv_struct_sockaddr_ll) if test "$ac_cv_struct_sockaddr_ll" = yes ; then AC_DEFINE(HAVE_STRUCT_SOCKADDR_LL) fi dnl Check for N_HDLC line discipline AC_MSG_CHECKING(for N_HDLC line discipline) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[int x = N_HDLC;]])],[ac_cv_n_hdlc=yes],[ac_cv_n_hdlc=no]) AC_MSG_RESULT($ac_cv_n_hdlc) if test "$ac_cv_n_hdlc" = yes ; then AC_DEFINE(HAVE_N_HDLC) fi AC_ARG_ENABLE(plugin, [ --enable-plugin=pppd_src_path build pppd plugin], ac_cv_pluginpath=$enableval, ac_cv_pluginpath=no) AC_ARG_ENABLE(debugging, [ --disable-debugging disable debugging code], ac_cv_debugging=$enableval, ac_cv_debugging=yes) AC_ARG_ENABLE(static, [ --enable-static Build static executables], ac_cv_static=$enableval, ac_cv_static=no) dnl If we were given "--enable-plugin" without a path, take a stab at where dnl the pppd source code might be. if test "$ac_cv_pluginpath" = "yes" ; then for i in /usr/include /usr/local/include `echo $CPATH | sed -e 's/:/ /g'`; do if test -r $i/pppd/pppd.h; then ac_cv_pluginpath=$i fi done fi if test "$ac_cv_pluginpath" = "yes" ; then echo "*** Could not find pppd/pppd.h anywhere... not building plugin" ac_cv_pluginpath=no fi AC_ARG_ENABLE(licenses, [ --enable-licenses=lic_path build commercial version], ac_cv_licpath=$enableval, ac_cv_licpath=no) PPPOE_SERVER_DEPS="" dnl Determine whether or not to build Linux pppd plugin LINUX_KERNELMODE_PLUGIN="" PPPD_INCDIR="" if test "$ac_cv_header_linux_if_pppox_h" = yes ; then if test "$ac_cv_pluginpath" != no ; then LINUX_KERNELMODE_PLUGIN=rp-pppoe.so AC_DEFINE(HAVE_LINUX_KERNEL_PPPOE) PPPD_INCDIR=$ac_cv_pluginpath fi fi if test "$PPPD_INCDIR" = "" ; then PPPD_INCDIR=/usr/include fi if test "$ac_cv_debugging" = "yes" ; then AC_DEFINE(DEBUGGING_ENABLED) fi AC_SUBST(LINUX_KERNELMODE_PLUGIN) AC_SUBST(PPPD_INCDIR) AC_SUBST(PPPD_H) dnl Determine whether or not to build PPPoE relay PPPOE_RELAY="" if test "`uname -s`" = "Linux" ; then PPPOE_RELAY=pppoe-relay fi AC_SUBST(PPPOE_RELAY) dnl Checks for library functions. AC_FUNC_MEMCMP AC_CHECK_FUNCS(select socket strerror strtol strlcpy) AC_PROG_INSTALL dnl Check for location of ip AC_PATH_PROG(IP, ip, NOTFOUND, $PATH:/bin:/sbin:/usr/bin:/usr/sbin) dnl Check for location of pppd AC_PATH_PROG(PPPD, pppd, NOTFOUND, $PATH:/sbin:/usr/sbin:/usr/local/sbin) dnl Check for setsid (probably Linux-specific) AC_PATH_PROG(SETSID, setsid, "", $PATH:/sbin:/usr/sbin:/usr/local/sbin) dnl Check for an "id" which accepts "-u" option -- hack for Solaris. AC_PATH_PROG(ID, id, "", /usr/xpg4/bin:$PATH) dnl Check for Linux-specific kernel support for PPPoE AC_MSG_CHECKING(for Linux 2.4.X kernel-mode PPPoE support) AC_CACHE_VAL(ac_cv_linux_kernel_pppoe,[ if test "`uname -s`" = "Linux" ; then if test $cross_compiling = "no"; then dnl Do a bunch of modprobes. Can't hurt; might help. modprobe ppp_generic > /dev/null 2>&1 modprobe ppp_async > /dev/null 2>&1 modprobe n_hdlc > /dev/null 2>&1 modprobe ppp_synctty > /dev/null 2>&1 modprobe pppoe > /dev/null 2>&1 fi AC_RUN_IFELSE([AC_LANG_SOURCE([[#include #include #include #include int main() { if (socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE) >= 0) return 0; else return 1; } ]])],[ac_cv_linux_kernel_pppoe=yes],[ac_cv_linux_kernel_pppoe=no],[ac_cv_linux_kernel_pppoe=no; echo "cross-compiling, default: "]) else ac_cv_linux_kernel_pppoe=no fi ]) AC_MSG_RESULT($ac_cv_linux_kernel_pppoe) if test "$ac_cv_linux_kernel_pppoe" != yes ; then if test "$LINUX_KERNELMODE_PLUGIN" = rp-pppoe.so; then echo "*** Your kernel does not appear to have built-in PPPoE support," echo "*** but I will build the kernel-mode plugin anyway." fi fi dnl GCC warning level if test "$GCC" = yes; then CFLAGS="$CFLAGS -fno-strict-aliasing -Wall -Wstrict-prototypes" fi dnl If we couldn't find pppd, die if test "$PPPD" = "NOTFOUND"; then AC_MSG_WARN([*** Oops! I couldn't find pppd, the PPP daemon anywhere.]) AC_MSG_WARN([*** You must install pppd, version 2.3.10 or later.]) AC_MSG_WARN([*** I will keep going, but it may not work.]) PPPD=pppd fi # Sigh... got to fix this up for tcl test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Fully resolve WRAPPER for Tcl script. WRAPPER=${sbindir}/pppoe-wrapper eval "WRAPPER=${WRAPPER}" eval "WRAPPER=${WRAPPER}" AC_SUBST(WRAPPER) # Determine what targets to build TARGETS="pppoe pppoe-server pppoe-sniff pppoe-relay" # plugin is built only if we have kernel support if test -n "$LINUX_KERNELMODE_PLUGIN" ; then TARGETS="$TARGETS $LINUX_KERNELMODE_PLUGIN" mkdir plugin > /dev/null 2>&1 fi EXTRACONFIGS="" RDYNAMIC="" if test "$ac_cv_static" = "yes" ; then STATIC=-static else STATIC="" fi LIBEVENT=../libevent AC_SUBST(TARGETS) AC_SUBST(PPPOE_SERVER_DEPS) AC_SUBST(RDYNAMIC) AC_SUBST(LIBEVENT) AC_SUBST(LDFLAGS) AC_SUBST(STATIC) datadir_evaluated=`eval echo $datadir` AC_SUBST(datadir_evaluated) AC_CONFIG_FILES([Makefile libevent/Makefile $EXTRACONFIGS]) AC_OUTPUT AC_MSG_RESULT([On this platform, the following targets will be built:]) AC_MSG_RESULT([$TARGETS]) AC_MSG_RESULT([Type 'make' to compile the software.]) rp-pppoe-4.0/src/pppoe-server.c0000644000175000017500000020340014422272526014620 0ustar dfsdfs/*********************************************************************** * * pppoe-server.c * * Implementation of a user-space PPPoE server * * Copyright (C) 2000-2012 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * $Id$ * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #include "config.h" #include #if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) #define _POSIX_SOURCE 1 /* For sigaction defines */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pppoe-server.h" #include "md5.h" #include "control_socket.h" #if defined(HAVE_LINUX_IF_H) #include #endif static void InterfaceHandler(EventSelector *es, int fd, unsigned int flags, void *data); static void startPPPD(ClientSession *sess); static void sendErrorPADS(int sock, unsigned char *source, unsigned char *dest, int errorTag, char *errorMsg); #define CHECK_ROOM(cursor, start, len) \ do {\ if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \ syslog(LOG_ERR, "Would create too-long packet"); \ return; \ } \ } while(0) #define DRAIN_OFF 0 #define DRAIN_ON 1 #define DRAIN_QUIT 2 static const char* drain_string[] = { "off (actively accepting connection)", "on (not accepting connections)", "quit (not accepting, will terminate when drained)", }; static char *plugin_path = PLUGIN_PATH; static void PppoeStopSession(ClientSession *ses, char const *reason); static int PppoeSessionIsActive(ClientSession *ses); /* Service-Names we advertise */ #define MAX_SERVICE_NAMES 64 static int NumServiceNames = 0; static char const *ServiceNames[MAX_SERVICE_NAMES]; PppoeSessionFunctionTable DefaultSessionFunctionTable = { PppoeStopSession, PppoeSessionIsActive, NULL }; /* An array of client sessions */ ClientSession *Sessions = NULL; ClientSession *FreeSessions = NULL; ClientSession *LastFreeSession = NULL; ClientSession *BusySessions = NULL; /* Interfaces we're listening on */ Interface *interfaces = NULL; int NumInterfaces = 0; int MaxInterfaces = 0; int draining = 0; /* The number of session slots */ size_t NumSessionSlots; /* Maximum number of sessions per MAC address */ int MaxSessionsPerMac; /* Number of active sessions */ size_t NumActiveSessions = 0; /* Offset of first session */ size_t SessOffset = 0; /* Event Selector */ EventSelector *event_selector; /* Use Linux kernel-mode PPPoE? */ static int UseLinuxKernelModePPPoE = 0; /* Requested max_ppp_payload */ static uint16_t max_ppp_payload = 0; /* File with PPPD options */ static char *pppoptfile = NULL; static char *pppd_path = PPPD_PATH; static char *pppoe_path = PPPOE_PATH; static char *motd_string = NULL; static char *hurl_string = NULL; static int Debug = 0; static int CheckPoolSyntax = 0; /* Synchronous mode */ static int Synchronous = 0; /* Ignore PADI if no free sessions */ static int IgnorePADIIfNoFreeSessions = 0; static int KidPipe[2] = {-1, -1}; static int LockFD = -1; /* Random seed for cookie generation */ #define SEED_LEN 16 #define MD5_LEN 16 #define COOKIE_LEN (MD5_LEN + sizeof(pid_t)) /* Cookie is 16-byte MD5 + PID of server */ static unsigned char CookieSeed[SEED_LEN]; #define MAXLINE 512 /* Default interface if no -I option given */ #define DEFAULT_IF "eth0" /* Access concentrator name */ char *ACName = NULL; /* Options to pass to pppoe process */ char PppoeOptions[SMALLBUF] = ""; /* Our local IP address */ unsigned char LocalIP[IPV4ALEN] = {10, 0, 0, 1}; /* Counter optionally STARTS here */ unsigned char RemoteIP[IPV4ALEN] = {10, 67, 15, 1}; /* Counter STARTS here */ /* Do we increment local IP for each connection? */ int IncrLocalIP = 0; /* Do we randomize session numbers? */ int RandomizeSessionNumbers = 0; /* Do we pass the "unit" option to pppd? (2.4 or greater) */ int PassUnitOptionToPPPD = 0; static PPPoETag hostUniq; static PPPoETag relayId; static PPPoETag receivedCookie; static PPPoETag requestedService; #define HOSTNAMELEN 256 static int count_sessions_from_mac(unsigned char *eth) { int n=0; ClientSession *s = BusySessions; while(s) { if (!memcmp(eth, s->eth, ETH_ALEN)) n++; s = s->next; } return n; } /********************************************************************** *Structures describing the CLI interface, and forward declarations. ***********************************************************************/ static int handle_set_drain(ClientConnection *client, const char* const* argv, int argi, void* pvt, void* clientpvt); ControlCommand cmd_set[] = { { .command = "drain", .handler = handle_set_drain, }, { .command = NULL, } }; static int handle_status(ClientConnection *client, const char* const* argv, int argi, void* pvt, void* clientpvt); ControlCommand cmd_status[] = { { .command = "status", .handler = handle_status, }, { .command = NULL, } }; ControlCommand cmd_root[] = { { .command = "set", .handler = control_socket_handle_command, .pvt = &cmd_set, }, { .command = "show", .handler = control_socket_handle_command, .pvt = &cmd_status, }, { .command = NULL, } }; /********************************************************************** *%FUNCTION: childHandler *%ARGUMENTS: * pid -- pid of child * status -- exit status * ses -- which session terminated *%RETURNS: * Nothing *%DESCRIPTION: * Called synchronously when a child dies. Remove from busy list. ***********************************************************************/ static void childHandler(pid_t pid, int status, void *s) { ClientSession *session = s; /* Temporary structure for sending PADT's. */ PPPoEConnection conn; memset(&conn, 0, sizeof(conn)); conn.hostUniq = NULL; syslog(LOG_INFO, "Session %u closed for client " "%02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s", (unsigned int) ntohs(session->sess), session->eth[0], session->eth[1], session->eth[2], session->eth[3], session->eth[4], session->eth[5], (int) session->peerip[0], (int) session->peerip[1], (int) session->peerip[2], (int) session->peerip[3], session->ethif->name); memcpy(conn.myEth, session->ethif->mac, ETH_ALEN); conn.discoverySocket = session->ethif->sock; conn.session = session->sess; memcpy(conn.peerEth, session->eth, ETH_ALEN); if (!(session->flags & FLAG_SENT_PADT)) { if (session->flags & FLAG_RECVD_PADT) { sendPADT(&conn, "RP-PPPoE: Received PADT from peer"); } else { sendPADT(&conn, "RP-PPPoE: Child pppd process terminated"); } session->flags |= FLAG_SENT_PADT; } session->serviceName = ""; if (pppoe_free_session(session) < 0) { return; } } /********************************************************************** *%FUNCTION: incrementIPAddress (static) *%ARGUMENTS: * addr -- a 4-byte array representing IP address *%RETURNS: * Nothing *%DESCRIPTION: * Increments addr in-place ***********************************************************************/ static void incrementIPAddress(unsigned char ip[IPV4ALEN]) { ip[3]++; if (!ip[3]) { ip[2]++; if (!ip[2]) { ip[1]++; if (!ip[1]) { ip[0]++; } } } } /********************************************************************** *%FUNCTION: ipIsNull (static) *%ARGUMENTS: * addr -- a 4-byte array representing IP address *%RETURNS: * 1 if ip represents 0.0.0.0, used to indicate delegation, 0 otherwise. *%DESCRIPTION: * Checks if ip is the null address used to indicate that IP allocation is * to be delegated to pppd. ***********************************************************************/ static int ipIsNull(const unsigned char ip[IPV4ALEN]) { int i; for (i = 0; i < IPV4ALEN; ++i) if (ip[i]) return 0; return 1; } /********************************************************************** *%FUNCTION: killAllSessions *%ARGUMENTS: * None *%RETURNS: * Nothing *%DESCRIPTION: * Kills all pppd processes (and hence all PPPoE sessions) ***********************************************************************/ void killAllSessions(void) { ClientSession *sess = BusySessions; while(sess) { sess->funcs->stop(sess, "Shutting Down"); sess = sess->next; } } /********************************************************************** *%FUNCTION: parseAddressPool *%ARGUMENTS: * fname -- name of file containing IP address pool. * install -- if true, install IP addresses in sessions. *%RETURNS: * Number of valid IP addresses found. *%DESCRIPTION: * Reads a list of IP addresses from a file. ***********************************************************************/ static int parseAddressPool(char const *fname, int install) { FILE *fp = fopen(fname, "r"); int numAddrs = 0; unsigned int a, b, c, d; unsigned int e, f, g, h; char line[MAXLINE]; if (!fp) { sysErr("Cannot open address pool file"); exit(EXIT_FAILURE); } while (!feof(fp)) { if (!fgets(line, MAXLINE, fp)) { break; } if ((sscanf(line, "%u.%u.%u.%u:%u.%u.%u.%u", &a, &b, &c, &d, &e, &f, &g, &h) == 8) && a < 256 && b < 256 && c < 256 && d < 256 && e < 256 && f < 256 && g < 256 && h < 256) { /* Both specified (local:remote) */ if (install) { Sessions[numAddrs].myip[0] = (unsigned char) a; Sessions[numAddrs].myip[1] = (unsigned char) b; Sessions[numAddrs].myip[2] = (unsigned char) c; Sessions[numAddrs].myip[3] = (unsigned char) d; Sessions[numAddrs].peerip[0] = (unsigned char) e; Sessions[numAddrs].peerip[1] = (unsigned char) f; Sessions[numAddrs].peerip[2] = (unsigned char) g; Sessions[numAddrs].peerip[3] = (unsigned char) h; } numAddrs++; } else if ((sscanf(line, "%u.%u.%u.%u-%u", &a, &b, &c, &d, &e) == 5) && a < 256 && b < 256 && c < 256 && d < 256 && e < 256) { /* Remote specied as a.b.c.d-e. Example: 1.2.3.4-8 yields: 1.2.3.4, 1.2.3.5, 1.2.3.6, 1.2.3.7, 1.2.3.8 */ /* Swap d and e so that e >= d */ if (e < d) { f = d; d = e; e = f; } if (install) { while (d <= e) { Sessions[numAddrs].peerip[0] = (unsigned char) a; Sessions[numAddrs].peerip[1] = (unsigned char) b; Sessions[numAddrs].peerip[2] = (unsigned char) c; Sessions[numAddrs].peerip[3] = (unsigned char) d; d++; numAddrs++; } } else { numAddrs += (e-d) + 1; } } else if ((sscanf(line, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) && a < 256 && b < 256 && c < 256 && d < 256) { /* Only remote specified */ if (install) { Sessions[numAddrs].peerip[0] = (unsigned char) a; Sessions[numAddrs].peerip[1] = (unsigned char) b; Sessions[numAddrs].peerip[2] = (unsigned char) c; Sessions[numAddrs].peerip[3] = (unsigned char) d; } numAddrs++; } } fclose(fp); if (!numAddrs) { rp_fatal("No valid ip addresses found in pool file"); } return numAddrs; } /********************************************************************** *%FUNCTION: parsePADITags *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data. *%RETURNS: * Nothing *%DESCRIPTION: * Picks interesting tags out of a PADI packet ***********************************************************************/ void parsePADITags(uint16_t type, uint16_t len, unsigned char *data, void *extra) { switch(type) { case TAG_PPP_MAX_PAYLOAD: if (len == sizeof(max_ppp_payload)) { memcpy(&max_ppp_payload, data, sizeof(max_ppp_payload)); max_ppp_payload = ntohs(max_ppp_payload); if (max_ppp_payload <= ETH_PPPOE_MTU) { max_ppp_payload = 0; } } break; case TAG_SERVICE_NAME: /* Copy requested service name */ requestedService.type = htons(type); requestedService.length = htons(len); memcpy(requestedService.payload, data, len); break; case TAG_RELAY_SESSION_ID: relayId.type = htons(type); relayId.length = htons(len); memcpy(relayId.payload, data, len); break; case TAG_HOST_UNIQ: hostUniq.type = htons(type); hostUniq.length = htons(len); memcpy(hostUniq.payload, data, len); break; } } /********************************************************************** *%FUNCTION: parsePADRTags *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data. *%RETURNS: * Nothing *%DESCRIPTION: * Picks interesting tags out of a PADR packet ***********************************************************************/ void parsePADRTags(uint16_t type, uint16_t len, unsigned char *data, void *extra) { switch(type) { case TAG_PPP_MAX_PAYLOAD: if (len == sizeof(max_ppp_payload)) { memcpy(&max_ppp_payload, data, sizeof(max_ppp_payload)); max_ppp_payload = ntohs(max_ppp_payload); if (max_ppp_payload <= ETH_PPPOE_MTU) { max_ppp_payload = 0; } } break; case TAG_RELAY_SESSION_ID: relayId.type = htons(type); relayId.length = htons(len); memcpy(relayId.payload, data, len); break; case TAG_HOST_UNIQ: hostUniq.type = htons(type); hostUniq.length = htons(len); memcpy(hostUniq.payload, data, len); break; case TAG_AC_COOKIE: receivedCookie.type = htons(type); receivedCookie.length = htons(len); memcpy(receivedCookie.payload, data, len); break; case TAG_SERVICE_NAME: requestedService.type = htons(type); requestedService.length = htons(len); memcpy(requestedService.payload, data, len); break; } } /********************************************************************** *%FUNCTION: fatalSys *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to stderr and syslog and exits. ***********************************************************************/ void fatalSys(char const *str) { printErr("%s: %s", str, strerror(errno)); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: sysErr *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to syslog. ***********************************************************************/ void sysErr(char const *str) { printErr("%.256s: %.256s", str, strerror(errno)); } /********************************************************************** *%FUNCTION: rp_fatal *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message to stderr and syslog and exits. ***********************************************************************/ void rp_fatal(char const *str) { printErr("%s", str); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: genCookie *%ARGUMENTS: * peerEthAddr -- peer Ethernet address (6 bytes) * myEthAddr -- my Ethernet address (6 bytes) * seed -- random cookie seed to make things tasty (16 bytes) * cookie -- buffer which is filled with server PID and * md5 sum of previous items *%RETURNS: * Nothing *%DESCRIPTION: * Forms the md5 sum of peer MAC address, our MAC address and seed, useful * in a PPPoE Cookie tag. ***********************************************************************/ void genCookie(unsigned char const *peerEthAddr, unsigned char const *myEthAddr, unsigned char const *seed, unsigned char *cookie) { struct MD5Context ctx; pid_t pid = getpid(); MD5Init(&ctx); MD5Update(&ctx, peerEthAddr, ETH_ALEN); MD5Update(&ctx, myEthAddr, ETH_ALEN); MD5Update(&ctx, seed, SEED_LEN); MD5Final(cookie, &ctx); memcpy(cookie+MD5_LEN, &pid, sizeof(pid)); } /********************************************************************** *%FUNCTION: processPADI *%ARGUMENTS: * ethif -- Interface * packet -- PPPoE PADI packet * len -- length of received packet *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADO packet back to client ***********************************************************************/ void processPADI(Interface *ethif, PPPoEPacket *packet, int len) { PPPoEPacket pado; PPPoETag acname; PPPoETag servname; PPPoETag cookie; size_t acname_len; unsigned char *cursor = pado.payload; uint16_t plen; int sock = ethif->sock; int i; int ok = 0; unsigned char *myAddr = ethif->mac; /* Ignore PADI's if we're draining the server */ if (draining != DRAIN_OFF) { syslog(LOG_ERR, "PADI ignored due to server draining."); return; } /* Ignore PADI's which don't come from a unicast address */ if (NOT_UNICAST(packet->ethHdr.h_source)) { syslog(LOG_ERR, "PADI packet from non-unicast source address"); return; } /* If no free sessions and "-i" flag given, ignore */ if (IgnorePADIIfNoFreeSessions && !FreeSessions) { syslog(LOG_INFO, "PADI ignored - No free session slots available"); return; } /* If number of sessions per MAC is limited, check here and don't send PADO if already max number of sessions. */ if (MaxSessionsPerMac) { if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) { syslog(LOG_INFO, "PADI: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], MaxSessionsPerMac); return; } } acname.type = htons(TAG_AC_NAME); acname_len = strlen(ACName); acname.length = htons(acname_len); memcpy(acname.payload, ACName, acname_len); relayId.type = 0; hostUniq.type = 0; requestedService.type = 0; max_ppp_payload = 0; parsePacket(packet, parsePADITags, NULL); /* If PADI specified non-default service name, and we do not offer that service, DO NOT send PADO */ if (requestedService.type) { int slen = ntohs(requestedService.length); if (slen) { for (i=0; iethHdr.h_source, myAddr, CookieSeed, cookie.payload); /* Construct a PADO packet */ memcpy(pado.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN); memcpy(pado.ethHdr.h_source, myAddr, ETH_ALEN); pado.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); pado.vertype = PPPOE_VER_TYPE(1, 1); pado.code = CODE_PADO; pado.session = 0; plen = TAG_HDR_SIZE + acname_len; CHECK_ROOM(cursor, pado.payload, acname_len+TAG_HDR_SIZE); memcpy(cursor, &acname, acname_len + TAG_HDR_SIZE); cursor += acname_len + TAG_HDR_SIZE; /* If we asked for an MTU, handle it */ if (max_ppp_payload > ETH_PPPOE_MTU && ethif->mtu > 0) { /* Shrink payload to fit */ if (max_ppp_payload > ethif->mtu - TOTAL_OVERHEAD) { max_ppp_payload = ethif->mtu - TOTAL_OVERHEAD; } if (max_ppp_payload > ETH_JUMBO_LEN - TOTAL_OVERHEAD) { max_ppp_payload = ETH_JUMBO_LEN - TOTAL_OVERHEAD; } if (max_ppp_payload > ETH_PPPOE_MTU) { PPPoETag maxPayload; uint16_t mru = htons(max_ppp_payload); maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); maxPayload.length = htons(sizeof(mru)); memcpy(maxPayload.payload, &mru, sizeof(mru)); CHECK_ROOM(cursor, pado.payload, sizeof(mru) + TAG_HDR_SIZE); memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); cursor += sizeof(mru) + TAG_HDR_SIZE; plen += sizeof(mru) + TAG_HDR_SIZE; } } /* If no service-names specified on command-line, just send default zero-length name. Otherwise, add all service-name tags */ servname.type = htons(TAG_SERVICE_NAME); if (!NumServiceNames) { servname.length = 0; CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE); memcpy(cursor, &servname, TAG_HDR_SIZE); cursor += TAG_HDR_SIZE; plen += TAG_HDR_SIZE; } else { for (i=0; imac; /* Ignore PADT's not directed at us */ if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return; /* Get session's index */ i = ntohs(packet->session) - 1 - SessOffset; if (i >= NumSessionSlots) return; if (Sessions[i].sess != packet->session) { syslog(LOG_ERR, "Session index %u doesn't match session number %u", (unsigned int) i, (unsigned int) ntohs(packet->session)); return; } /* If source MAC does not match, do not kill session */ if (memcmp(packet->ethHdr.h_source, Sessions[i].eth, ETH_ALEN)) { if (!Sessions[i].eth[0] && !Sessions[i].eth[1] && !Sessions[i].eth[2] && !Sessions[i].eth[3] && !Sessions[i].eth[4] && !Sessions[i].eth[5]) { syslog(LOG_INFO, "PADT for closed session %u received from " "%02X:%02X:%02X:%02X:%02X:%02X", (unsigned int) ntohs(packet->session), packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5]); } else { syslog(LOG_WARNING, "PADT for session %u received from " "%02X:%02X:%02X:%02X:%02X:%02X; should be from " "%02X:%02X:%02X:%02X:%02X:%02X", (unsigned int) ntohs(packet->session), packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], Sessions[i].eth[0], Sessions[i].eth[1], Sessions[i].eth[2], Sessions[i].eth[3], Sessions[i].eth[4], Sessions[i].eth[5]); } return; } Sessions[i].flags |= FLAG_RECVD_PADT; parsePacket(packet, parseLogErrs, NULL); Sessions[i].funcs->stop(&Sessions[i], "Received PADT"); } /********************************************************************** *%FUNCTION: processPADR *%ARGUMENTS: * ethif -- Ethernet interface * packet -- PPPoE PADR packet * len -- length of received packet *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADS packet back to client and starts a PPP session if PADR * packet is OK. ***********************************************************************/ void processPADR(Interface *ethif, PPPoEPacket *packet, int len) { unsigned char cookieBuffer[COOKIE_LEN]; ClientSession *cliSession; pid_t child; PPPoEPacket pads; unsigned char *cursor = pads.payload; uint16_t plen; int i; int sock = ethif->sock; unsigned char *myAddr = ethif->mac; int slen = 0; char const *serviceName = NULL; /* Temporary structure for sending PADM's. */ PPPoEConnection conn; /* Initialize some globals */ relayId.type = 0; hostUniq.type = 0; receivedCookie.type = 0; requestedService.type = 0; /* Ignore PADR's not directed at us */ if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return; /* Ignore PADR's from non-unicast addresses */ if (NOT_UNICAST(packet->ethHdr.h_source)) { syslog(LOG_ERR, "PADR packet from non-unicast source address"); return; } /* If number of sessions per MAC is limited, check here and don't send PADS if already max number of sessions. */ if (MaxSessionsPerMac) { if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) { syslog(LOG_INFO, "PADR: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)", packet->ethHdr.h_source[0], packet->ethHdr.h_source[1], packet->ethHdr.h_source[2], packet->ethHdr.h_source[3], packet->ethHdr.h_source[4], packet->ethHdr.h_source[5], MaxSessionsPerMac); return; } } max_ppp_payload = 0; parsePacket(packet, parsePADRTags, NULL); /* Check that everything's cool */ if (!receivedCookie.type) { /* Drop it -- do not send error PADS */ return; } /* Is cookie kosher? */ if (receivedCookie.length != htons(COOKIE_LEN)) { /* Drop it -- do not send error PADS */ return; } genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookieBuffer); if (memcmp(receivedCookie.payload, cookieBuffer, COOKIE_LEN)) { /* Drop it -- do not send error PADS */ return; } /* Check service name */ if (!requestedService.type) { syslog(LOG_ERR, "Received PADR packet with no SERVICE_NAME tag"); sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: No service name tag"); return; } slen = ntohs(requestedService.length); if (slen) { /* Check supported services */ for(i=0; iethHdr.h_source, TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: Invalid service name tag"); return; } } else { serviceName = ""; } /* Looks cool... find a slot for the session */ cliSession = pppoe_alloc_session(); if (!cliSession) { syslog(LOG_ERR, "No client slots available (%02x:%02x:%02x:%02x:%02x:%02x)", (unsigned int) packet->ethHdr.h_source[0], (unsigned int) packet->ethHdr.h_source[1], (unsigned int) packet->ethHdr.h_source[2], (unsigned int) packet->ethHdr.h_source[3], (unsigned int) packet->ethHdr.h_source[4], (unsigned int) packet->ethHdr.h_source[5]); sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: No client slots available"); return; } /* Set up client session peer Ethernet address */ memcpy(cliSession->eth, packet->ethHdr.h_source, ETH_ALEN); cliSession->ethif = ethif; cliSession->flags = 0; cliSession->funcs = &DefaultSessionFunctionTable; cliSession->startTime = time(NULL); cliSession->serviceName = serviceName; /* Create child process, send PADS packet back */ child = fork(); if (child < 0) { sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: Unable to start session process"); pppoe_free_session(cliSession); return; } if (child != 0) { /* In the parent process. Mark pid in session slot */ cliSession->pid = child; Event_HandleChildExit(event_selector, child, childHandler, cliSession); return; } /* In the child process */ /* Reset signal handlers to default */ signal(SIGTERM, SIG_DFL); signal(SIGINT, SIG_DFL); /* Close all file descriptors except for socket */ closelog(); if (LockFD >= 0) close(LockFD); for (i=0; iethHdr.h_source, ETH_ALEN); memcpy(pads.ethHdr.h_source, myAddr, ETH_ALEN); pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); pads.vertype = PPPOE_VER_TYPE(1, 1); pads.code = CODE_PADS; pads.session = cliSession->sess; plen = 0; /* Copy requested service name tag back in. If requested-service name length is zero, and we have non-zero services, use first service-name as default */ if (!slen && NumServiceNames) { slen = strlen(ServiceNames[0]); memcpy(&requestedService.payload, ServiceNames[0], slen); requestedService.length = htons(slen); } memcpy(cursor, &requestedService, TAG_HDR_SIZE+slen); cursor += TAG_HDR_SIZE+slen; plen += TAG_HDR_SIZE+slen; /* If we asked for an MTU, handle it */ if (max_ppp_payload > ETH_PPPOE_MTU && ethif->mtu > 0) { /* Shrink payload to fit */ if (max_ppp_payload > ethif->mtu - TOTAL_OVERHEAD) { max_ppp_payload = ethif->mtu - TOTAL_OVERHEAD; } if (max_ppp_payload > ETH_JUMBO_LEN - TOTAL_OVERHEAD) { max_ppp_payload = ETH_JUMBO_LEN - TOTAL_OVERHEAD; } if (max_ppp_payload > ETH_PPPOE_MTU) { PPPoETag maxPayload; uint16_t mru = htons(max_ppp_payload); maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); maxPayload.length = htons(sizeof(mru)); memcpy(maxPayload.payload, &mru, sizeof(mru)); CHECK_ROOM(cursor, pads.payload, sizeof(mru) + TAG_HDR_SIZE); memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); cursor += sizeof(mru) + TAG_HDR_SIZE; plen += sizeof(mru) + TAG_HDR_SIZE; cliSession->requested_mtu = max_ppp_payload; } } if (relayId.type) { memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE); cursor += ntohs(relayId.length) + TAG_HDR_SIZE; plen += ntohs(relayId.length) + TAG_HDR_SIZE; } if (hostUniq.type) { memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; } pads.length = htons(plen); sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE)); if (hurl_string || motd_string) { memset(&conn, 0, sizeof(conn)); conn.hostUniq = NULL; memcpy(conn.myEth, cliSession->ethif->mac, ETH_ALEN); conn.discoverySocket = sock; conn.session = cliSession->sess; memcpy(conn.peerEth, cliSession->eth, ETH_ALEN); if (hurl_string != NULL) sendHURLorMOTM(&conn, hurl_string, TAG_HURL); if (motd_string != NULL) sendHURLorMOTM(&conn, motd_string, TAG_MOTM); } /* Close sock; don't need it any more */ close(sock); startPPPD(cliSession); } /********************************************************************** *%FUNCTION: pppoe_terminate *%ARGUMENTS: * sig -- signal number *%RETURNS: * Nothing *%DESCRIPTION: * Call this in order to terminate the server. ***********************************************************************/ static __attribute__((noreturn)) void pppoe_terminate(void) { killAllSessions(); exit(EXIT_SUCCESS); } /********************************************************************** *%FUNCTION: termHandler *%ARGUMENTS: * sig -- signal number *%RETURNS: * Nothing *%DESCRIPTION: * Called by SIGTERM or SIGINT. Causes all sessions to be killed! ***********************************************************************/ static void termHandler(int sig) { syslog(LOG_INFO, "Terminating on signal %d -- killing all PPPoE sessions", sig); pppoe_terminate(); } /********************************************************************** *%FUNCTION: usage *%ARGUMENTS: * argv0 -- argv[0] from main *%RETURNS: * Nothing *%DESCRIPTION: * Prints usage instructions ***********************************************************************/ void usage(char const *argv0) { fprintf(stderr, "Usage: %s [options]\n", argv0); fprintf(stderr, "Options:\n"); fprintf(stderr, " -I if_name -- Specify interface (default %s.)\n", DEFAULT_IF); fprintf(stderr, " -T timeout -- Specify inactivity timeout in seconds.\n"); fprintf(stderr, " -C name -- Set access concentrator name.\n"); fprintf(stderr, " -m MSS -- Clamp incoming and outgoing MSS options.\n"); fprintf(stderr, " -L ip -- Set local IP address.\n"); fprintf(stderr, " -l -- Increment local IP address for each session.\n"); fprintf(stderr, " -R ip -- Set start address of remote IP pool.\n"); fprintf(stderr, " -S name -- Advertise specified service-name.\n"); fprintf(stderr, " -O fname -- Use PPPD options from specified file\n"); fprintf(stderr, " (default %s).\n", PPPOE_SERVER_OPTIONS); fprintf(stderr, " -p fname -- Obtain IP address pool from specified file.\n"); fprintf(stderr, " -N num -- Allow 'num' concurrent sessions.\n"); fprintf(stderr, " -o offset -- Assign session numbers starting at offset+1.\n"); fprintf(stderr, " -f disc:sess -- Set Ethernet frame types (hex).\n"); fprintf(stderr, " -s -- Use synchronous PPP mode.\n"); fprintf(stderr, " -X pidfile -- Write PID and lock pidfile.\n"); fprintf(stderr, " -q /path/pppd -- Specify full path to pppd.\n"); fprintf(stderr, " -Q /path/pppoe -- Specify full path to pppoe.\n"); fprintf(stderr, " -k -- Use kernel-mode PPPoE.\n"); fprintf(stderr, " -g path -- Specify full path to plugin (default %s)\n", PLUGIN_PATH); fprintf(stderr, " -u -- Pass 'unit' option to pppd.\n"); fprintf(stderr, " -r -- Randomize session numbers.\n"); fprintf(stderr, " -d -- Debug session creation.\n"); fprintf(stderr, " -x n -- Limit to 'n' sessions/MAC address.\n"); fprintf(stderr, " -P -- Check pool file for correctness and exit.\n"); fprintf(stderr, " -i -- Ignore PADI if no free sessions.\n"); fprintf(stderr, " -M msg -- Send MSG in a MOTM tag in PADM packet after PADS.\n"); fprintf(stderr, " -H url -- Send URL in a HURL tag in PADM packet after PADS.\n"); fprintf(stderr, " -h -- Print usage information.\n\n"); fprintf(stderr, "PPPoE-Server Version %s, Copyright (C) 2001-2009 Roaring Penguin Software Inc.\n", RP_VERSION); fprintf(stderr, " %*s Copyright (C) 2018-2023 Dianne Skoll\n", (int) strlen(RP_VERSION), ""); fprintf(stderr, "PPPoE-Server comes with ABSOLUTELY NO WARRANTY.\n"); fprintf(stderr, "This is free software, and you are welcome to redistribute it\n"); fprintf(stderr, "under the terms of the GNU General Public License, version 2\n"); fprintf(stderr, "or (at your option) any later version.\n"); fprintf(stderr, "https://dianne.skoll.ca/projects/rp-pppoe/\n"); } /********************************************************************** *%FUNCTION: main *%ARGUMENTS: * argc, argv -- usual suspects *%RETURNS: * Exit status *%DESCRIPTION: * Main program of PPPoE server ***********************************************************************/ int main(int argc, char **argv) { FILE *fp; int i, j; int opt; int d[IPV4ALEN]; int beDaemon = 1; int found; unsigned int discoveryType, sessionType; char *addressPoolFname = NULL; char *pidfile = NULL; char *unix_control = NULL; char c; char const *s; int cookie_ok = 0; char const *options = "X:ix:hI:C:L:R:T:m:FN:f:O:o:skp:lrudPS:q:Q:H:M:U:g:"; if (getuid() != geteuid() || getgid() != getegid()) { fprintf(stderr, "SECURITY WARNING: pppoe-server will NOT run suid or sgid. Fix your installation.\n"); exit(EXIT_FAILURE); } /* Initialize syslog */ openlog("pppoe-server", LOG_PID, LOG_DAEMON); MaxInterfaces = INIT_INTERFACES; interfaces = malloc(sizeof(*interfaces) * INIT_INTERFACES); if (!interfaces) { fprintf(stderr, "Out of memory allocating initial interfaces.\n"); exit(EXIT_FAILURE); } /* Default number of session slots */ NumSessionSlots = DEFAULT_MAX_SESSIONS; MaxSessionsPerMac = 0; /* No limit */ NumActiveSessions = 0; /* Parse command-line options */ while((opt = getopt(argc, argv, options)) != -1) { switch(opt) { case 'i': IgnorePADIIfNoFreeSessions = 1; break; case 'x': if (sscanf(optarg, "%d", &MaxSessionsPerMac) != 1) { usage(argv[0]); exit(EXIT_FAILURE); } if (MaxSessionsPerMac < 0) { MaxSessionsPerMac = 0; } break; case 'k': UseLinuxKernelModePPPoE = 1; break; case 'g': plugin_path = strdup(optarg); if (!plugin_path) { fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE); } break; case 'S': if (NumServiceNames == MAX_SERVICE_NAMES) { fprintf(stderr, "Too many '-S' options (%d max)", MAX_SERVICE_NAMES); exit(EXIT_FAILURE); } /* Service names can only be [-_.A-Za-z0-9/] for shell-escaping safety reasons */ for (s=optarg; *s; s++) { if (!strchr("-_.ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/", *s)) { fprintf(stderr, "Illegal character `%c' in service-name: Must be A-Z, a-z, 0-9 or one of ./-_\n", *s); exit(EXIT_FAILURE); } } ServiceNames[NumServiceNames] = strdup(optarg); if (!ServiceNames[NumServiceNames]) { fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE); } NumServiceNames++; break; case 'q': pppd_path = strdup(optarg); if (!pppd_path) { fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE); } break; case 'Q': pppoe_path = strdup(optarg); if (!pppoe_path) { fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE); } break; case 'M': if (motd_string) { free(motd_string); motd_string = NULL; } motd_string = strdup(optarg); if (!motd_string) { fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE); } break; case 'H': if (hurl_string) { free(hurl_string); hurl_string = NULL; } hurl_string = strdup(optarg); if (!hurl_string) { fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE); } break; case 'd': Debug = 1; break; case 'P': CheckPoolSyntax = 1; break; case 'u': PassUnitOptionToPPPD = 1; break; case 'r': RandomizeSessionNumbers = 1; break; case 'l': IncrLocalIP = 1; break; case 'p': SET_STRING(addressPoolFname, optarg); break; case 'X': SET_STRING(pidfile, optarg); break; case 's': Synchronous = 1; /* Pass the Synchronous option on to pppoe */ snprintf(PppoeOptions + strlen(PppoeOptions), SMALLBUF-strlen(PppoeOptions), " -s"); break; case 'f': if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) { fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n"); exit(EXIT_FAILURE); } Eth_PPPOE_Discovery = (uint16_t) discoveryType; Eth_PPPOE_Session = (uint16_t) sessionType; /* This option gets passed to pppoe */ snprintf(PppoeOptions + strlen(PppoeOptions), SMALLBUF-strlen(PppoeOptions), " -%c %s", opt, optarg); break; case 'F': beDaemon = 0; break; case 'N': if (sscanf(optarg, "%d", &opt) != 1) { usage(argv[0]); exit(EXIT_FAILURE); } if (opt <= 0) { fprintf(stderr, "-N: Value must be positive\n"); exit(EXIT_FAILURE); } NumSessionSlots = opt; break; case 'O': SET_STRING(pppoptfile, optarg); break; case 'o': if (sscanf(optarg, "%d", &opt) != 1) { usage(argv[0]); exit(EXIT_FAILURE); } if (opt < 0) { fprintf(stderr, "-o: Value must be non-negative\n"); exit(EXIT_FAILURE); } SessOffset = (size_t) opt; break; case 'I': if (NumInterfaces >= MaxInterfaces) { MaxInterfaces *= 2; interfaces = realloc(interfaces, sizeof(*interfaces) * MaxInterfaces); if (!interfaces) { fprintf(stderr, "Memory allocation failure trying to increase MaxInterfaces to %d\n", MaxInterfaces); exit(EXIT_FAILURE); } } found = 0; for (i=0; i 255) { usage(argv[0]); exit(EXIT_FAILURE); } if (opt == 'L') { LocalIP[i] = (unsigned char) d[i]; } else { RemoteIP[i] = (unsigned char) d[i]; } } break; case 'T': case 'm': /* These just get passed to pppoe */ snprintf(PppoeOptions + strlen(PppoeOptions), SMALLBUF-strlen(PppoeOptions), " -%c %s", opt, optarg); break; case 'U': SET_STRING(unix_control, optarg); break; case 'h': usage(argv[0]); exit(EXIT_SUCCESS); } } if (!pppoptfile) { pppoptfile = PPPOE_SERVER_OPTIONS; } if (!NumInterfaces) { strcpy(interfaces[0].name, DEFAULT_IF); NumInterfaces = 1; } if (!ACName) { ACName = malloc(HOSTNAMELEN); if (gethostname(ACName, HOSTNAMELEN) < 0) { fatalSys("gethostname"); } } /* If address pool filename given, count number of addresses */ if (addressPoolFname) { NumSessionSlots = parseAddressPool(addressPoolFname, 0); if (CheckPoolSyntax) { printf("%lu\n", (unsigned long) NumSessionSlots); exit(EXIT_SUCCESS); } } /* Max 65534 - SessOffset sessions */ if (NumSessionSlots + SessOffset > 65534) { fprintf(stderr, "-N and -o options must add up to at most 65534\n"); exit(EXIT_FAILURE); } /* Allocate memory for sessions */ Sessions = calloc(NumSessionSlots, sizeof(ClientSession)); if (!Sessions) { rp_fatal("Cannot allocate memory for session slots"); } /* Fill in local addresses first (let pool file override later */ for (i=0; i> 8) & 0xFF; for (i=2; i> (i % 9)) & 0xFF; } } if (RandomizeSessionNumbers) { int *permutation; int tmp; permutation = malloc(sizeof(int) * NumSessionSlots); if (!permutation) { fprintf(stderr, "Could not allocate memory to randomize session numbers\n"); exit(EXIT_FAILURE); } for (i=0; isess)), ses->myip[0], ses->myip[1], ses->myip[2], ses->myip[3], ses->peerip[0], ses->peerip[1], ses->peerip[2], ses->peerip[3]); ses = ses->next; } exit(EXIT_SUCCESS); } /* Open all the interfaces */ for (i=0; i= 0) { close(KidPipe[0]); KidPipe[0] = -1; } /* Point stdin/stdout/stderr to /dev/null */ for (i=0; i<3; i++) { close(i); } i = open("/dev/null", O_RDWR); if (i >= 0) { dup2(i, 0); dup2(i, 1); dup2(i, 2); if (i > 2) close(i); } } if (pidfile) { FILE *foo = NULL; if (KidPipe[1] >= 0) foo = fdopen(KidPipe[1], "w"); struct flock fl; char buf[64]; fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; LockFD = open(pidfile, O_RDWR|O_CREAT, 0666); if (LockFD < 0) { syslog(LOG_INFO, "Could not open PID file %s: %s", pidfile, strerror(errno)); if (foo) fprintf(foo, "ECould not open PID file %s: %s\n", pidfile, strerror(errno)); exit(EXIT_FAILURE); } if (fcntl(LockFD, F_SETLK, &fl) < 0) { syslog(LOG_INFO, "Could not lock PID file %s: Is another process running?", pidfile); if (foo) fprintf(foo, "ECould not lock PID file %s: Is another process running?\n", pidfile); exit(EXIT_FAILURE); } if (ftruncate(LockFD, 0) < 0) { syslog(LOG_INFO, "Could not truncate PID file %s: %s", pidfile, strerror(errno)); if (foo) fprintf(foo, "ECould not truncate PID file %s: %s", pidfile, strerror(errno)); exit(EXIT_FAILURE); } snprintf(buf, sizeof(buf), "%lu\n", (unsigned long) getpid()); if (write(LockFD, buf, strlen(buf)) < strlen(buf)) { syslog(LOG_INFO, "Could not write PID file %s: %s", pidfile, strerror(errno)); if (foo) fprintf(foo, "ECould not write PID file %s: %s", pidfile, strerror(errno)); exit(EXIT_FAILURE); } /* Do not close fd... use it to retain lock */ } /* Set signal handlers for SIGTERM and SIGINT */ if (Event_HandleSignal(event_selector, SIGTERM, termHandler) < 0 || Event_HandleSignal(event_selector, SIGINT, termHandler) < 0) { fatalSys("Event_HandleSignal"); } /* Tell parent all is cool */ if (KidPipe[1] >= 0) { #pragma GCC diagnostic ignored "-Wunused-result" write(KidPipe[1], "X", 1); close(KidPipe[1]); #pragma GCC diagnostic warning "-Wunused-result" KidPipe[1] = -1; } for(;;) { i = Event_HandleEvent(event_selector); if (i < 0) { fatalSys("Event_HandleEvent"); } if (draining == DRAIN_QUIT && NumActiveSessions == 0) { syslog(LOG_INFO, "All active sessions are terminated and draining is set to quit."); pppoe_terminate(); } } return 0; } void serverProcessPacket(Interface *i) { int len; PPPoEPacket packet; int sock = i->sock; if (receivePacket(sock, &packet, &len) < 0) { return; } if (len < HDR_SIZE) { /* Impossible - ignore */ return; } /* Sanity check on packet */ if (PPPOE_VER(packet.vertype) != 1 || PPPOE_TYPE(packet.vertype) != 1) { /* Syslog an error */ return; } /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { syslog(LOG_ERR, "Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); return; } switch(packet.code) { case CODE_PADI: processPADI(i, &packet, len); break; case CODE_PADR: processPADR(i, &packet, len); break; case CODE_PADT: /* Kill the child */ processPADT(i, &packet, len); break; case CODE_SESS: /* Ignore SESS -- children will handle them */ break; case CODE_PADO: case CODE_PADS: /* Ignore PADO and PADS totally */ break; default: /* Syslog an error */ break; } } /********************************************************************** *%FUNCTION: sendErrorPADS *%ARGUMENTS: * sock -- socket to write to * source -- source Ethernet address * dest -- destination Ethernet address * errorTag -- error tag * errorMsg -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADS packet with an error message ***********************************************************************/ void sendErrorPADS(int sock, unsigned char *source, unsigned char *dest, int errorTag, char *errorMsg) { PPPoEPacket pads; unsigned char *cursor = pads.payload; uint16_t plen; PPPoETag err; int elen = strlen(errorMsg); memcpy(pads.ethHdr.h_dest, dest, ETH_ALEN); memcpy(pads.ethHdr.h_source, source, ETH_ALEN); pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); pads.vertype = PPPOE_VER_TYPE(1, 1); pads.code = CODE_PADS; pads.session = htons(0); plen = 0; err.type = htons(errorTag); err.length = htons(elen); memcpy(err.payload, errorMsg, elen); memcpy(cursor, &err, TAG_HDR_SIZE+elen); cursor += TAG_HDR_SIZE + elen; plen += TAG_HDR_SIZE + elen; if (relayId.type) { memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE); cursor += ntohs(relayId.length) + TAG_HDR_SIZE; plen += ntohs(relayId.length) + TAG_HDR_SIZE; } if (hostUniq.type) { memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; } pads.length = htons(plen); sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE)); } /********************************************************************** *%FUNCTION: makePPPDIpArg *%ARGUMENTS: * buffer -- target buffer output, should be large enough (32 bytes worst case). * localip -- the local IP address (myip), 0.0.0.0 for delegation. * remoteip -- the local IP address (myip), 0.0.0.0 for delegation. *%DESCRIPTION: * prints the localip:remoteip argument for pppd, taking delegation * into account, returning 0 if, and only if, both IP addresses are delegated, * and 1 otherwise. ***********************************************************************/ static void makePPPDIpArg(char* buffer, const unsigned char localip[IPV4ALEN], const unsigned char remoteip[IPV4ALEN]) { if (!ipIsNull(localip)) buffer += sprintf(buffer, "%u.%u.%u.%u", localip[0], localip[1], localip[2], localip[3]); strcpy(buffer++, ":"); if (!ipIsNull(remoteip)) buffer += sprintf(buffer, "%u.%u.%u.%u", remoteip[0], remoteip[1], remoteip[2], remoteip[3]); } /********************************************************************** *%FUNCTION: startPPPD *%ARGUMENTS: * session -- client session record *%RETURNS: * Nothing *%DESCRIPTION: * Starts PPPD for user- or kernel-mode PPPoE ***********************************************************************/ static void startPPPD(ClientSession *session) { /* Leave some room */ char *argv[64]; char buffer[2 * SMALLBUF]; char *mrumtu; int c = 0; syslog(LOG_INFO, "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s using Service-Name '%s'", (unsigned int) ntohs(session->sess), session->eth[0], session->eth[1], session->eth[2], session->eth[3], session->eth[4], session->eth[5], (int) session->peerip[0], (int) session->peerip[1], (int) session->peerip[2], (int) session->peerip[3], session->ethif->name, session->serviceName); argv[c++] = "pppd"; if (UseLinuxKernelModePPPoE) { /* kernel mode */ argv[c++] = "plugin"; argv[c++] = plugin_path; /* Add "nic-" to interface name */ snprintf(buffer, SMALLBUF, "nic-%s", session->ethif->name); argv[c++] = strdup(buffer); if (!argv[c-1]) { exit(EXIT_FAILURE); } snprintf(buffer, SMALLBUF, "%u:%02x:%02x:%02x:%02x:%02x:%02x", (unsigned int) ntohs(session->sess), session->eth[0], session->eth[1], session->eth[2], session->eth[3], session->eth[4], session->eth[5]); argv[c++] = "rp_pppoe_sess"; argv[c++] = strdup(buffer); if (!argv[c-1]) { /* TODO: Send a PADT */ exit(EXIT_FAILURE); } argv[c++] = "rp_pppoe_service"; argv[c++] = (char *) session->serviceName; } else { /* user mode */ argv[c++] = "pty"; /* Let's hope service-name does not have ' in it... */ snprintf(buffer, sizeof(buffer), "%s -n -I %s -e %u:%02x:%02x:%02x:%02x:%02x:%02x%s -S '%s'", pppoe_path, session->ethif->name, (unsigned int) ntohs(session->sess), session->eth[0], session->eth[1], session->eth[2], session->eth[3], session->eth[4], session->eth[5], PppoeOptions, session->serviceName); argv[c++] = strdup(buffer); if (!argv[c-1]) { /* TODO: Send a PADT */ exit(EXIT_FAILURE); } if (Synchronous) { argv[c++] = "sync"; } } argv[c++] = "file"; argv[c++] = pppoptfile; makePPPDIpArg(buffer, session->myip, session->peerip); argv[c++] = strdup(buffer); if (!argv[c-1]) { /* TODO: Send a PADT */ exit(EXIT_FAILURE); } argv[c++] = "nodetach"; argv[c++] = "noaccomp"; argv[c++] = "nopcomp"; argv[c++] = "default-asyncmap"; argv[c++] = "remotenumber"; snprintf(buffer, SMALLBUF, "%02x:%02x:%02x:%02x:%02x:%02x", session->eth[0], session->eth[1], session->eth[2], session->eth[3], session->eth[4], session->eth[5]); if (!(argv[c++] = strdup(buffer))) { exit(EXIT_FAILURE); } if (PassUnitOptionToPPPD) { argv[c++] = "unit"; sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1 - SessOffset)); if (!(argv[c++] = strdup(buffer))) { exit(EXIT_FAILURE); } } if (session->requested_mtu > 1492) { sprintf(buffer, "%u", (unsigned int) session->requested_mtu); if (!(mrumtu = strdup(buffer))) { exit(EXIT_FAILURE); } argv[c++] = "mru"; argv[c++] = mrumtu; argv[c++] = "mtu"; argv[c++] = mrumtu; } else { argv[c++] = "mru"; argv[c++] = "1492"; argv[c++] = "mtu"; argv[c++] = "1492"; } argv[c++] = NULL; execv(pppd_path, argv); exit(EXIT_FAILURE); } /********************************************************************** * %FUNCTION: InterfaceHandler * %ARGUMENTS: * es -- event selector (ignored) * fd -- file descriptor which is readable * flags -- ignored * data -- Pointer to the Interface structure * %RETURNS: * Nothing * %DESCRIPTION: * Handles a packet ready at an interface ***********************************************************************/ void InterfaceHandler(EventSelector *es, int fd, unsigned int flags, void *data) { serverProcessPacket((Interface *) data); } /********************************************************************** * %FUNCTION: PppoeStopSession * %ARGUMENTS: * ses -- the session * reason -- reason session is being stopped. * %RETURNS: * Nothing * %DESCRIPTION: * Kills pppd. ***********************************************************************/ static void PppoeStopSession(ClientSession *ses, char const *reason) { /* Temporary structure for sending PADT's. */ PPPoEConnection conn; memset(&conn, 0, sizeof(conn)); conn.hostUniq = NULL; memcpy(conn.myEth, ses->ethif->mac, ETH_ALEN); conn.discoverySocket = ses->ethif->sock; conn.session = ses->sess; memcpy(conn.peerEth, ses->eth, ETH_ALEN); sendPADT(&conn, reason); ses->flags |= FLAG_SENT_PADT; if (ses->pid) { kill(ses->pid, SIGTERM); } ses->funcs = &DefaultSessionFunctionTable; } /********************************************************************** * %FUNCTION: PppoeSessionIsActive * %ARGUMENTS: * ses -- the session * %RETURNS: * True if session is active, false if not. ***********************************************************************/ static int PppoeSessionIsActive(ClientSession *ses) { return (ses->pid != 0); } /********************************************************************** * %FUNCTION: pppoe_alloc_session * %ARGUMENTS: * None * %RETURNS: * NULL if no session is available, otherwise a ClientSession structure. * %DESCRIPTION: * Allocates a ClientSession structure and removes from free list, puts * on busy list ***********************************************************************/ ClientSession * pppoe_alloc_session(void) { ClientSession *ses = FreeSessions; if (!ses) return NULL; /* Remove from free sessions list */ if (ses == LastFreeSession) { LastFreeSession = NULL; } FreeSessions = ses->next; /* Put on busy sessions list */ ses->next = BusySessions; BusySessions = ses; /* Initialize fields to sane values */ ses->funcs = &DefaultSessionFunctionTable; ses->pid = 0; ses->ethif = NULL; memset(ses->eth, 0, ETH_ALEN); ses->flags = 0; ses->startTime = time(NULL); ses->serviceName = ""; ses->requested_mtu = 0; NumActiveSessions++; return ses; } /********************************************************************** * %FUNCTION: pppoe_free_session * %ARGUMENTS: * ses -- session to free * %RETURNS: * 0 if OK, -1 if error * %DESCRIPTION: * Places a ClientSession on the free list. ***********************************************************************/ int pppoe_free_session(ClientSession *ses) { ClientSession *cur, *prev; cur = BusySessions; prev = NULL; while (cur) { if (ses == cur) break; prev = cur; cur = cur->next; } if (!cur) { syslog(LOG_ERR, "pppoe_free_session: Could not find session %p on busy list", (void *) ses); return -1; } /* Remove from busy sessions list */ if (prev) { prev->next = ses->next; } else { BusySessions = ses->next; } /* Add to end of free sessions */ ses->next = NULL; if (LastFreeSession) { LastFreeSession->next = ses; LastFreeSession = ses; } else { FreeSessions = ses; LastFreeSession = ses; } /* Initialize fields to sane values */ ses->funcs = &DefaultSessionFunctionTable; ses->pid = 0; memset(ses->eth, 0, ETH_ALEN); ses->flags = 0; NumActiveSessions--; return 0; } /********************************************************************** * %FUNCTION: sendHURLorMOTM * %ARGUMENTS: * conn -- PPPoE connection * url -- a URL, which *MUST* begin with "http://" or it won't be sent, or * a message. * tag -- one of TAG_HURL or TAG_MOTM * %RETURNS: * Nothing * %DESCRIPTION: * Sends a PADM packet contaning a HURL or MOTM tag to the victim...er, peer. ***********************************************************************/ void sendHURLorMOTM(PPPoEConnection *conn, char const *url, uint16_t tag) { PPPoEPacket packet; PPPoETag hurl; size_t elen; unsigned char *cursor = packet.payload; uint16_t plen = 0; if (!conn->session) return; if (conn->discoverySocket < 0) return; if (tag == TAG_HURL) { if (strncmp(url, "http://", 7) && strncmp(url, "https://", 8)) { syslog(LOG_WARNING, "sendHURL(%s): URL must begin with http:// or https://", url); return; } } else { tag = TAG_MOTM; } memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_PADM; packet.session = conn->session; elen = strlen(url); if (elen > 256) { syslog(LOG_WARNING, "MOTM or HURL too long: %d", (int) elen); return; } hurl.type = htons(tag); hurl.length = htons(elen); strcpy((char *) hurl.payload, url); memcpy(cursor, &hurl, elen + TAG_HDR_SIZE); cursor += elen + TAG_HDR_SIZE; plen += elen + TAG_HDR_SIZE; packet.length = htons(plen); sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "SENT"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif } /********************************************************************** * %FUNCTION: handle_status ***********************************************************************/ #define opt_matches(o) (wlen == 0 || strncmp(opt, o, wlen) == 0) #define opt_outp(o, fmt, ...) cs_ret_printf(client, "%20s: " fmt "\n", o, ## __VA_ARGS__) #define opt_status(o, fmt, ...) do { if (opt_matches(o)) { opt_outp(o, fmt, ## __VA_ARGS__); }} while(0) static int handle_status(ClientConnection *client, const char* const* argv, int argi, void* pvt, void* clientpvt) { char opt[64]; /* WARNING: may not be null terminated!!!! */ size_t wlen = 0; while (wlen < sizeof(opt) && argv[argi]) { int r = snprintf(&opt[wlen], sizeof(opt) - wlen, "%s ", argv[argi++]); if (r < 0) { syslog(LOG_WARNING, "snprintf error: %s", strerror(errno)); return -1; } wlen += r; } if (wlen > sizeof(opt)) wlen = sizeof(opt); if (opt[wlen-1] == ' ') --wlen; opt_status("active sessions", "%zu", NumActiveSessions); opt_status("maximum sessions", "%zu", NumSessionSlots); opt_status("sessions per mac", "%d", MaxSessionsPerMac); opt_status("interface count", "%d", NumInterfaces); opt_status("global drain", "%s", drain_string[draining]); if (opt_matches("interface list")) { int i; for (i = 0; i < NumInterfaces; ++i) { if (!opt_matches(interfaces[i].name)) continue; cs_ret_printf(client, "Interface details: %s\n", interfaces[i].name); opt_outp("local mac", "%02x:%02x:%02x:%02x:%02x:%02x", interfaces[i].mac[0], interfaces[i].mac[1], interfaces[i].mac[2], interfaces[i].mac[3], interfaces[i].mac[4], interfaces[i].mac[5]); opt_outp("mtu", "%u", interfaces[i].mtu); } } cs_ret_printf(client, "-- end --\n"); return 0; } #undef opt_status #undef opt_outp #undef opt_matches /********************************************************************** * %FUNCTION: handle_set_drain ***********************************************************************/ static int handle_set_drain(ClientConnection *client, const char* const* argv, int argi, void* pvt, void* clientpvt) { if (!argv[argi]) { cs_ret_printf(client, "USAGE: set drain {off|on|quit}\n"); return 0; } if (strcmp(argv[argi], "off") == 0) { draining = DRAIN_OFF; cs_ret_printf(client, "Server is not draining\n"); } else if (strcmp(argv[argi], "on") == 0) { draining = DRAIN_ON; cs_ret_printf(client, "Server is now draining\n"); } else if (strcmp(argv[argi], "quit") == 0) { draining = DRAIN_QUIT; cs_ret_printf(client, "Server is now draining, and will quit when all clients are disconnected\n"); } else { cs_ret_printf(client, "Invalid value %s for set drain, value must be one of off, on or quit.\n", argv[argi]); } return 0; } rp-pppoe-4.0/src/md5.c0000644000175000017500000001753714422272526012674 0ustar dfsdfs/* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * SPDX-License-Identifier: GPL-2.0-or-later * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ #include /* for memcpy() */ #include "md5.h" static void byteReverse(unsigned char *buf, unsigned longs); /* * Note: this code is harmless on little-endian machines. */ static void byteReverse(unsigned char *buf, unsigned longs) { uint32_t t; do { t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsigned) buf[1] << 8 | buf[0]); *(uint32_t *) buf = t; buf += 4; } while (--longs); } /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) { uint32_t t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if (t) { unsigned char *p = (unsigned char *) ctx->in + t; t = 64 - t; if (len < t) { memcpy(p, buf, len); return; } memcpy(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32_t *) ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32_t *) ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned count; unsigned char *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ memset(p, 0, count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32_t *) ctx->in); /* Now fill the next block with 56 bytes */ memset(ctx->in, 0, 56); } else { /* Pad block to 56 bytes */ memset(p, 0, count - 8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ ((uint32_t *) ctx->in)[14] = ctx->bits[0]; ((uint32_t *) ctx->in)[15] = ctx->bits[1]; MD5Transform(ctx->buf, (uint32_t *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { register uint32_t a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } rp-pppoe-4.0/src/control_socket.h0000644000175000017500000000364314422272526015235 0ustar dfsdfs/********************************************************************** * * control_socket.h * * Definitions for the PPPoE server's control socket. * * Copyright (C) 2001-2012 Roaring Penguin Software Inc. * Copyright (C) 2018-2022 Dianne Skoll * Copyright (C) 2022 Jaco Kroon * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later ***********************************************************************/ #include "event.h" typedef struct ClientConnection ClientConnection; /* command control structures */ typedef struct ControlCommand { const char* command; /* single word such as 'set' */ int (*handler)(struct ClientConnection* cc, const char * const * argv, int argi, void* cmdpvt, void* clientpvt); /* argv contains word-split command, argi the index that needs to be handled next, zero return indicates success, negative return states to close the socket, positive values indicates that there was a proccessing failure and further processing should stop, but the connection can be retained. fd is a file descriptor to the control socket. */ void* pvt; } ControlCommand; typedef void (*control_socket_exit_handler)(struct ClientConnection *cc, void* clientpvt); int control_socket_init(EventSelector *event_selector, const char* unix_socket, ControlCommand* root); int control_socket_push_context(struct ClientConnection *cc, control_socket_exit_handler exitfunc, ControlCommand* newroot, void* clientpvt); int control_socket_handle_command(struct ClientConnection *client, const char* const* argv, int argi, void* _subs, void*); __attribute__ ((format (printf, 2, 3))) int control_socket_printf(struct ClientConnection *cc, const char* fmt, ...); #define cs_printf(...) control_socket_printf(__VA_ARGS__) #define cs_ret_printf(...) do { if (cs_printf(__VA_ARGS__) < 0) return -1; } while(0) rp-pppoe-4.0/src/control_socket.c0000644000175000017500000002321114422272526015221 0ustar dfsdfs/*********************************************************************** * * control_socket.c * * Re-usable code for implementing a control socket. * * Copyright (C) 2000-2012 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * Copyright (C) 2022 Jaco Kroon * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * $Id$ * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #define _GNU_SOURCE #include "control_socket.h" #include "event_tcp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "pppoe.h" #define MAX_CMD_LEN 2048 typedef struct ClientContext { ControlCommand *commands; void* clientpvt; control_socket_exit_handler exithandler; } ClientContext; typedef struct ClientConnection { int fd; ClientContext *context; int ctxi; char *writebuf; int writebuflen; bool close_on_write_complete; } ClientConnection; int control_socket_handle_command(ClientConnection *client, const char* const* argv, int argi, void *_subs, void *unused __attribute__((unused))) { ControlCommand* subs = _subs; ControlCommand* selected = NULL; ControlCommand *cc; size_t wlen = 0; bool listall = true; if (argv[argi]) { wlen = strlen(argv[argi]); for (cc = subs; cc->command; ++cc) { if (strncmp(cc->command, argv[argi], wlen) == 0) { /* the word starts with the provided input */ /* if we previously found a match, it means the command is * ambiguous, so drop-through and output list of available next * options */ if (selected) { selected = NULL; listall = false; break; } selected = cc; } } if (selected) { return selected->handler(client, argv, argi+1, selected->pvt, client->context[client->ctxi].clientpvt); } cs_ret_printf(client, "%s '%s' not found or ambiguous, possible options:\n", argi ? "Sub-command" : "Command", argv[argi]); } else if (argi) { int i; cs_ret_printf(client, "Incomplete command after '"); for (i = 0; i < argi; ++i) { cs_ret_printf(client, "%s%s", i ? " " : "", argv[i]); } cs_ret_printf(client, "', possible completions:\n"); } else { cs_ret_printf(client, "No command specified, base commands:\n"); } for (cc = subs; cc->command; ++cc) { if (!listall && argv[argi] && strncmp(argv[argi], cc->command, wlen) != 0) continue; cs_ret_printf(client, " %s\n", cc->command); } cs_ret_printf(client, "-- end --\n"); return 0; } void control_socket_cleanup_client(ClientConnection *client, int fd) { int i; printErr("Closing UNIX control connection."); close(fd); for (i = client->ctxi; i >= 0; --i) { if (client->context[i].exithandler) client->context[i].exithandler(client, client->context[i].clientpvt); } free(client->writebuf); free(client->context); free(client); } int control_socket_push_context(ClientConnection *client, control_socket_exit_handler exitfunc, ControlCommand* newroot, void* clientpvt) { ClientContext *t = realloc(client->context, (client->ctxi + 2) * sizeof(*client->context)); if (!t) { printErr("Memory allocation error trying to push UNIX control context."); return -1; } client->context = t; client->ctxi++; client->context[client->ctxi].commands = newroot; client->context[client->ctxi].exithandler = exitfunc; client->context[client->ctxi].clientpvt = clientpvt; return 0; } static void control_socket_read(EventSelector *es, int fd, char* command, int len, int flag, void *_client); static void control_socket_write_complete(EventSelector *es, int fd, char* buf, int len, int flag, void *_client) { ClientConnection *client = _client; /* free_state takes care of freeing buf */ if (flag == EVENT_TCP_FLAG_COMPLETE && !client->close_on_write_complete && EventTcp_ReadBuf(es, fd, MAX_CMD_LEN, '\n', control_socket_read, -1, _client)) return; if (flag != EVENT_TCP_FLAG_COMPLETE) printErr("Error writing to control socket"); control_socket_cleanup_client(client, fd); } static void control_socket_read(EventSelector *es, int fd, char* command, int len, int flag, void *_client) { char *argv[128]; int argi = 0; ClientConnection *client = _client; if (flag != EVENT_TCP_FLAG_COMPLETE) goto closeout; if (client->writebuf) { printErr("BUG, we're not supposed to have a pre-existing write-buffer. Contents:\n%.*s---", client->writebuflen, client->writebuf); goto closeout; } command[len-1] = 0; /* \n => 0 */ if (strcmp(command, "quit") == 0 || (strcmp(command, "exit") == 0 && client->ctxi == 0)) { cs_printf(client, "Good bye.\n"); client->close_on_write_complete = true; goto checkwrite; } if (strcmp(command, "exit") == 0) { if (client->context[client->ctxi].exithandler) client->context[client->ctxi].exithandler(client, client->context[client->ctxi].clientpvt); client->ctxi--; } printErr("Received Control Command: %.*s.", len, command); while (*command) { int quoted; /* strip leading whitespace. */ while (*command && isspace(*command)) ++command; /* break on EOL */ if (!*command) break; argv[argi] = command; /* find the end of the word. */ quoted = *command++ == '"'; while (*command && ( quoted ? (*command != '"' || command[-1] == '\\') : !isspace(*command))) command++; if (quoted) { if (*command != '"') { cs_printf(client, "Error locating closing quote for %s\n", argv[argi]); goto checkwrite; } if (command[1] && !isspace(command[1])) { cs_printf(client, "Unescaped mid-word quote in %s\n", argv[argi]); goto checkwrite; } argv[argi]++; } if (*command) *command++ = 0; if (quoted) { /* compress \" to ". */ char *s, *d; s = d = strstr(argv[argi], "\\\""); while (s && *s) { if (*s == '\\' && s[1] == '"') ++s; *d++ = *s++; } *d = 0; } argi++; } argv[argi] = NULL; if (control_socket_handle_command(client, (const char*const*)argv, 0, client->context[0].commands, NULL) < 0) goto closeout; checkwrite: if (client->writebuf) { if (!EventTcp_WriteBuf(es, fd, client->writebuf, client->writebuflen, control_socket_write_complete, -1, client)) { printErr("Failed to set up write buffer. Closing control connection."); goto closeout; } free(client->writebuf); client->writebuf = NULL; client->writebuflen = 0; } else { // no output, go directly to read mode again if (!EventTcp_ReadBuf(es, fd, MAX_CMD_LEN, '\n', control_socket_read, -1, client)) { printErr("Failed to set up reader, closing control connection."); goto closeout; } } return; closeout: control_socket_cleanup_client(client, fd); } static void control_socket_acceptor(EventSelector *es, int fd, void* _root) { printErr("Accepted UNIX control connection."); ControlCommand *root = _root; ClientConnection *client = malloc(sizeof(*client)); if (!client) { printErr("Error allocating memory for new client."); goto errout; } memset(client, 0, sizeof(*client)); client->context = malloc(sizeof(*client->context)); if (!client->context) { printErr("Error allocating memory for new client."); goto errout; } memset(&client->context[0], 0, sizeof(client->context[0])); client->context[0].commands = root; if (!EventTcp_ReadBuf(es, fd, MAX_CMD_LEN, '\n', control_socket_read, -1, client)) { printErr("Failed to set up reader, closing control connection."); goto errout; } return; errout: close(fd); if (client) { if (client->context) free(client->context); free(client); } } int control_socket_init(EventSelector *event_selector, const char* unix_socket, ControlCommand* root) { struct sockaddr_un su; int unix_fd = -1; mode_t oldmask; su.sun_family = AF_UNIX; if (sizeof(su.sun_path) - 1 < strlen(unix_socket)) { printErr("Error creating UNIX socket: %s.", "Specified path too long to fit in socket address structure"); goto failout; } strcpy(su.sun_path, unix_socket); unix_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (unix_fd < 0) { printErr("Error creating UNIX socket: %s.", strerror(errno)); goto failout; } fcntl(unix_fd, F_SETFL, fcntl(unix_fd, F_GETFL, 0) | FD_CLOEXEC); /* I don't like this but in case of left-over sockets we have no choice. */ unlink(unix_socket); oldmask = umask(0177); if (bind(unix_fd, (struct sockaddr *)&su, sizeof(su)) < 0) { umask(oldmask); printErr("Error binding UNIX socket: %s.", strerror(errno)); goto failout; } umask(oldmask); if (listen(unix_fd, 8) < 0) { printErr("Error listening on UNIX socket: %s.", strerror(errno)); goto failout; } if (!EventTcp_CreateAcceptor(event_selector, unix_fd, control_socket_acceptor, root)) { printErr("Error creating UNIX socket acceptor event handler."); goto failout; } return 0; failout: if (unix_fd >= 0) close(unix_fd); return -1; } int control_socket_printf(ClientConnection *client, const char* fmt, ...) { char *bfr; va_list vargs; va_start(vargs, fmt); int l = vasprintf(&bfr, fmt, vargs); va_end(vargs); if (l < 0) return -1; void* tmp = realloc(client->writebuf, client->writebuflen + l); if (!tmp) return -1; memcpy(tmp + client->writebuflen, bfr, l); free(bfr); client->writebuf = tmp; client->writebuflen += l; return 0; } rp-pppoe-4.0/src/pppoe.c0000644000175000017500000005741514422272526013331 0ustar dfsdfs/*********************************************************************** * * pppoe.c * * Implementation of user-space PPPoE redirector for Linux. * * Copyright (C) 2000-2015 by Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_N_HDLC #ifndef N_HDLC #include #endif #endif #include "pppoe.h" /* Default interface if no -I option given */ #define DEFAULT_IF "eth0" /* Global variables -- options */ int optInactivityTimeout = 0; /* Inactivity timeout */ int optClampMSS = 0; /* Clamp MSS to this value */ int optSkipSession = 0; /* Perform discovery, print session info and exit */ int optFloodDiscovery = 0; /* Flood server with discovery requests. USED FOR STRESS-TESTING ONLY. DO NOT USE THE -F OPTION AGAINST A REAL ISP */ PPPoEConnection *Connection = NULL; /* Must be global -- used in signal handler */ /*********************************************************************** *%FUNCTION: sendSessionPacket *%ARGUMENTS: * conn -- PPPoE connection * packet -- the packet to send * len -- length of data to send *%RETURNS: * Nothing *%DESCRIPTION: * Transmits a session packet to the peer. ***********************************************************************/ void sendSessionPacket(PPPoEConnection *conn, PPPoEPacket *packet, int len) { packet->length = htons(len); if (optClampMSS) { clampMSS(packet, "outgoing", optClampMSS); } if (sendPacket(conn, conn->sessionSocket, packet, len + HDR_SIZE) < 0) { if (errno == ENOBUFS) { /* No buffer space is a transient error */ return; } exit(EXIT_FAILURE); } #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, packet, "SENT"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif } /********************************************************************** *%FUNCTION: sessionDiscoveryPacket *%ARGUMENTS: * conn -- PPPoE connection *%RETURNS: * Nothing *%DESCRIPTION: * We got a discovery packet during the session stage. This most likely * means a PADT. ***********************************************************************/ static void sessionDiscoveryPacket(PPPoEConnection *conn) { PPPoEPacket packet; int len; if (receivePacket(conn->discoverySocket, &packet, &len) < 0) { return; } /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { syslog(LOG_ERR, "Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); return; } /* Is it for our session? */ if (packet.session != conn->session) { /* Nope, ignore it */ return; } /* Is it for our Ethernet interface? */ if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { /* Nope, ignore it */ return; } /* Is it from our peer's Ethernet interface? */ if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { /* Nope, ignore it */ return; } if (packet.code != CODE_PADT) { /* Not PADT; ignore it */ return; } #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif syslog(LOG_INFO, "Session %d terminated -- received PADT from peer", (int) ntohs(packet.session)); parsePacket(&packet, parseLogErrs, NULL); sendPADT(conn, "Received PADT from peer"); exit(EXIT_SUCCESS); } /********************************************************************** *%FUNCTION: session *%ARGUMENTS: * conn -- PPPoE connection info *%RETURNS: * Nothing *%DESCRIPTION: * Handles the "session" phase of PPPoE ***********************************************************************/ void session(PPPoEConnection *conn) { fd_set readable; PPPoEPacket packet; struct timeval tv; struct timeval *tvp = NULL; int maxFD = 0; int r; /* Drop privileges */ dropPrivs(); /* Prepare for select() */ if (conn->sessionSocket > maxFD) maxFD = conn->sessionSocket; if (conn->discoverySocket > maxFD) maxFD = conn->discoverySocket; maxFD++; /* Fill in the constant fields of the packet to save time */ memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Session); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_SESS; packet.session = conn->session; initPPP(); for (;;) { if (optInactivityTimeout > 0) { tv.tv_sec = optInactivityTimeout; tv.tv_usec = 0; tvp = &tv; } FD_ZERO(&readable); FD_SET(0, &readable); /* ppp packets come from stdin */ if (conn->discoverySocket >= 0) { FD_SET(conn->discoverySocket, &readable); } FD_SET(conn->sessionSocket, &readable); while(1) { r = select(maxFD, &readable, NULL, NULL, tvp); if (r >= 0 || errno != EINTR) break; } if (r < 0) { fatalSys("select (session)"); } if (r == 0) { /* Inactivity timeout */ syslog(LOG_ERR, "Inactivity timeout... something wicked happened on session %d", (int) ntohs(conn->session)); sendPADT(conn, "RP-PPPoE: Inactivity timeout"); exit(EXIT_FAILURE); } /* Handle ready sockets */ if (FD_ISSET(0, &readable)) { if (conn->synchronous) { syncReadFromPPP(conn, &packet); } else { asyncReadFromPPP(conn, &packet); } } if (FD_ISSET(conn->sessionSocket, &readable)) { if (conn->synchronous) { syncReadFromEth(conn, conn->sessionSocket, optClampMSS); } else { asyncReadFromEth(conn, conn->sessionSocket, optClampMSS); } } if (conn->discoverySocket >= 0) { if (FD_ISSET(conn->discoverySocket, &readable)) { sessionDiscoveryPacket(conn); } } } } /*********************************************************************** *%FUNCTION: sigPADT *%ARGUMENTS: * src -- signal received *%RETURNS: * Nothing *%DESCRIPTION: * If an established session exists send PADT to terminate from session * from our end ***********************************************************************/ static void sigPADT(int src) { syslog(LOG_DEBUG,"Received signal %d on session %d.", (int)src, (int) ntohs(Connection->session)); sendPADTf(Connection, "RP-PPPoE: Client terminated by signal %d", src); exit(EXIT_SUCCESS); } /********************************************************************** *%FUNCTION: usage *%ARGUMENTS: * argv0 -- program name *%RETURNS: * Nothing *%DESCRIPTION: * Prints usage information and exits. ***********************************************************************/ void usage(char const *argv0) { fprintf(stderr, "Usage: %s [options]\n", argv0); fprintf(stderr, "Options:\n"); fprintf(stderr, " -I if_name -- Specify interface (default %s.)\n", DEFAULT_IF); #ifdef DEBUGGING_ENABLED fprintf(stderr, " -D filename -- Log debugging information in filename.\n"); #endif fprintf(stderr, " -T timeout -- Specify inactivity timeout in seconds.\n" " -t timeout -- Initial timeout for discovery packets in seconds\n" " -V -- Print version and exit.\n" " -A -- Print access concentrator names and exit.\n" " -S name -- Set desired service name.\n" " -C name -- Set desired access concentrator name.\n" " -U -- Use Host-Unique to allow multiple PPPoE sessions.\n" " -W value -- Use Host-Unique set to 'value' specifically.\n" " -s -- Use synchronous PPP encapsulation.\n" " -m MSS -- Clamp incoming and outgoing MSS options.\n" " -p pidfile -- Write process-ID to pidfile.\n" " -e sess:mac -- Skip discovery phase; use existing session.\n" " -n -- Do not open discovery socket.\n" " -k -- Kill a session with PADT (requires -e)\n" " -d -- Perform discovery, print session info and exit.\n" " -f disc:sess -- Set Ethernet frame types (hex).\n" " -h -- Print usage information.\n\n" "RP-PPPoE Version %s, Copyright (C) 2001-2018 Roaring Penguin Software Inc.\n" " %*s Copyright (C) 2018-2023 Dianne Skoll\n" "RP-PPPoE comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it under the terms\n" "of the GNU General Public License, version 2 or any later version.\n" "https://dianne.skoll.ca/projects/rp-pppoe/\n", RP_VERSION, (int) strlen(RP_VERSION), ""); exit(EXIT_SUCCESS); } /********************************************************************** *%FUNCTION: main *%ARGUMENTS: * argc, argv -- count and values of command-line arguments *%RETURNS: * Nothing *%DESCRIPTION: * Main program ***********************************************************************/ int main(int argc, char *argv[]) { int opt; int n; unsigned int m[6]; /* MAC address in -e option */ unsigned int s; /* Temporary to hold session */ FILE *pidfile; unsigned int discoveryType, sessionType; char const *options; PPPoEConnection conn; #ifdef HAVE_N_HDLC int disc = N_HDLC; long flags; #endif if (getuid() != geteuid() || getgid() != getegid()) { IsSetID = 1; } /* Initialize connection info */ memset(&conn, 0, sizeof(conn)); conn.discoverySocket = -1; conn.sessionSocket = -1; conn.discoveryTimeout = PADI_TIMEOUT; /* For signal handler */ Connection = &conn; /* Initialize syslog */ openlog("pppoe", LOG_PID, LOG_DAEMON); #ifdef DEBUGGING_ENABLED options = "I:VAT:D:hS:C:UW:sm:np:e:kdf:F:t:"; #else options = "I:VAT:hS:C:UW:sm:np:e:kdf:F:t:"; #endif while((opt = getopt(argc, argv, options)) != -1) { switch(opt) { case 't': if (sscanf(optarg, "%d", &conn.discoveryTimeout) != 1) { fprintf(stderr, "Illegal argument to -t: Should be -t timeout\n"); exit(EXIT_FAILURE); } if (conn.discoveryTimeout < 1) { conn.discoveryTimeout = 1; } break; case 'F': if (sscanf(optarg, "%d", &optFloodDiscovery) != 1) { fprintf(stderr, "Illegal argument to -F: Should be -F numFloods\n"); exit(EXIT_FAILURE); } if (optFloodDiscovery < 1) optFloodDiscovery = 1; fprintf(stderr, "WARNING: DISCOVERY FLOOD IS MEANT FOR STRESS-TESTING\n" "A PPPOE SERVER WHICH YOU OWN. DO NOT USE IT AGAINST\n" "A REAL ISP. YOU HAVE 5 SECONDS TO ABORT.\n"); sleep(5); break; case 'f': if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) { fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n"); exit(EXIT_FAILURE); } Eth_PPPOE_Discovery = (uint16_t) discoveryType; Eth_PPPOE_Session = (uint16_t) sessionType; break; case 'd': optSkipSession = 1; break; case 'k': conn.killSession = 1; break; case 'n': /* Do not even open a discovery socket -- used when invoked by pppoe-server */ conn.noDiscoverySocket = 1; break; case 'e': /* Existing session: "sess:xx:yy:zz:aa:bb:cc" where "sess" is session-ID, and xx:yy:zz:aa:bb:cc is MAC-address of peer */ n = sscanf(optarg, "%u:%2x:%2x:%2x:%2x:%2x:%2x", &s, &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]); if (n != 7) { fprintf(stderr, "Illegal argument to -e: Should be sess:xx:yy:zz:aa:bb:cc\n"); exit(EXIT_FAILURE); } /* Copy MAC address of peer */ for (n=0; n<6; n++) { conn.peerEth[n] = (unsigned char) m[n]; } /* Convert session */ conn.session = htons(s); /* Skip discovery phase! */ conn.skipDiscovery = 1; break; case 'p': switchToRealID(); pidfile = fopen(optarg, "w"); if (pidfile) { fprintf(pidfile, "%lu\n", (unsigned long) getpid()); fclose(pidfile); } switchToEffectiveID(); break; case 'S': SET_STRING(conn.serviceName, optarg); break; case 'C': SET_STRING(conn.acName, optarg); break; case 's': conn.synchronous = 1; break; case 'U': if (conn.hostUniq) { fprintf(stderr, "-U and -W are mutually-exclusive and may only be used once.\n"); exit(EXIT_FAILURE); } /* Allows for a 64-bit PID */ conn.hostUniq = malloc(17); if (!conn.hostUniq) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } sprintf(conn.hostUniq, "%lx", (unsigned long) getpid()); break; case 'W': if (conn.hostUniq) { fprintf(stderr, "-U and -W are mutually-exclusive and may only be used once.\n"); exit(EXIT_FAILURE); } if (strlen(optarg) > 64) { fprintf(stderr, "Maximum length of -W argument is 64 bytes.\n"); exit(EXIT_FAILURE); } SET_STRING(conn.hostUniq, optarg); break; #ifdef DEBUGGING_ENABLED case 'D': switchToRealID(); conn.debugFile = fopen(optarg, "w"); switchToEffectiveID(); if (!conn.debugFile) { fprintf(stderr, "Could not open %s: %s\n", optarg, strerror(errno)); exit(EXIT_FAILURE); } fprintf(conn.debugFile, "rp-pppoe-%s\n", RP_VERSION); fflush(conn.debugFile); break; #endif case 'T': optInactivityTimeout = (int) strtol(optarg, NULL, 10); if (optInactivityTimeout < 0) { optInactivityTimeout = 0; } break; case 'm': optClampMSS = (int) strtol(optarg, NULL, 10); if (optClampMSS < 536) { fprintf(stderr, "-m: %d is too low (min 536)\n", optClampMSS); exit(EXIT_FAILURE); } if (optClampMSS > 1452) { fprintf(stderr, "-m: %d is too high (max 1452)\n", optClampMSS); exit(EXIT_FAILURE); } break; case 'I': SET_STRING(conn.ifName, optarg); break; case 'V': printf("RP-PPPoE Version %s\n", RP_VERSION); exit(EXIT_SUCCESS); case 'A': conn.printACNames = 1; break; case 'h': usage(argv[0]); break; default: usage(argv[0]); } } /* Pick a default interface name */ if (!conn.ifName) { SET_STRING(conn.ifName, DEFAULT_IF); } if (!conn.printACNames) { #ifdef HAVE_N_HDLC if (conn.synchronous) { if (ioctl(0, TIOCSETD, &disc) < 0) { printErr("Unable to set line discipline to N_HDLC. Make sure your kernel supports the N_HDLC line discipline, or do not use the SYNCHRONOUS option. Quitting."); exit(EXIT_FAILURE); } else { syslog(LOG_INFO, "Changed pty line discipline to N_HDLC for synchronous mode"); } /* There is a bug in Linux's select which returns a descriptor * as readable if N_HDLC line discipline is on, even if * it isn't really readable. This return happens only when * select() times out. To avoid blocking forever in read(), * make descriptor 0 non-blocking */ flags = fcntl(0, F_GETFL); if (flags < 0) fatalSys("fcntl(F_GETFL)"); if (fcntl(0, F_SETFL, (long) flags | O_NONBLOCK) < 0) { fatalSys("fcntl(F_SETFL)"); } } #endif } if (optFloodDiscovery) { for (n=0; n < optFloodDiscovery; n++) { if (conn.printACNames) { fprintf(stderr, "Sending discovery flood %d\n", n+1); } conn.discoverySocket = openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth, NULL); discovery(&conn); conn.discoveryState = STATE_SENT_PADI; close(conn.discoverySocket); } exit(EXIT_SUCCESS); } /* Open session socket before discovery phase, to avoid losing session */ /* packets sent by peer just after PADS packet (noted on some Cisco */ /* server equipment). */ /* Opening this socket just before waitForPADS in the discovery() */ /* function would be more appropriate, but it would mess-up the code */ if (!optSkipSession) { conn.sessionSocket = openInterface(conn.ifName, Eth_PPPOE_Session, conn.myEth, NULL); } /* Skip discovery and don't open discovery socket? */ if (conn.skipDiscovery && conn.noDiscoverySocket) { conn.discoveryState = STATE_SESSION; } else { conn.discoverySocket = openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth, NULL); discovery(&conn); } if (optSkipSession) { printf("%u:%02x:%02x:%02x:%02x:%02x:%02x\n", ntohs(conn.session), conn.peerEth[0], conn.peerEth[1], conn.peerEth[2], conn.peerEth[3], conn.peerEth[4], conn.peerEth[5]); exit(EXIT_SUCCESS); } /* Set signal handlers: send PADT on HUP, TERM and INT */ signal(SIGTERM, sigPADT); signal(SIGINT, sigPADT); signal(SIGHUP, sigPADT); session(&conn); return 0; } /********************************************************************** *%FUNCTION: fatalSys *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to stderr and syslog and exits. ***********************************************************************/ void fatalSys(char const *str) { printErr("%.256s: Session %d: %.256s", str, (int) ntohs(Connection->session), strerror(errno)); sendPADTf(Connection, "RP-PPPoE: System call error: %s", strerror(errno)); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: sysErr *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to syslog. ***********************************************************************/ void sysErr(char const *str) { printErr("%.256s: %.256s", str, strerror(errno)); } /********************************************************************** *%FUNCTION: rp_fatal *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message to stderr and syslog and exits. ***********************************************************************/ void rp_fatal(char const *str) { printErr("%s", str); sendPADTf(Connection, "RP-PPPoE: Session %d: %.256s", (int) ntohs(Connection->session), str); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: asyncReadFromEth *%ARGUMENTS: * conn -- PPPoE connection info * sock -- Ethernet socket * clampMss -- if non-zero, do MSS-clamping *%RETURNS: * Nothing *%DESCRIPTION: * Reads a packet from the Ethernet interface and sends it to async PPP * device. ***********************************************************************/ void asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss) { PPPoEPacket packet; int len; int plen; int i; unsigned char pppBuf[4096]; unsigned char *ptr = pppBuf; unsigned char c; uint16_t fcs; unsigned char header[2] = {FRAME_ADDR, FRAME_CTRL}; unsigned char tail[2]; if (receivePacket(sock, &packet, &len) < 0) { return; } /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { syslog(LOG_ERR, "Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); return; } #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif /* Sanity check */ if (packet.code != CODE_SESS) { syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code); return; } if (PPPOE_VER(packet.vertype) != 1) { syslog(LOG_ERR, "Unexpected packet version %d", PPPOE_VER(packet.vertype)); return; } if (PPPOE_TYPE(packet.vertype) != 1) { syslog(LOG_ERR, "Unexpected packet type %d", PPPOE_TYPE(packet.vertype)); return; } if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { return; } if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { /* Not for us -- must be another session. This is not an error, so don't log anything. */ return; } if (packet.session != conn->session) { /* Not for us -- must be another session. This is not an error, so don't log anything. */ return; } plen = ntohs(packet.length); if (plen + HDR_SIZE > len) { syslog(LOG_ERR, "Bogus length field in session packet %d (%d)", (int) plen, (int) len); return; } /* Clamp MSS */ if (clampMss) { clampMSS(&packet, "incoming", clampMss); } /* Compute FCS */ fcs = pppFCS16(PPPINITFCS16, header, 2); fcs = pppFCS16(fcs, packet.payload, plen) ^ 0xffff; tail[0] = fcs & 0x00ff; tail[1] = (fcs >> 8) & 0x00ff; /* Build a buffer to send to PPP */ *ptr++ = FRAME_FLAG; *ptr++ = FRAME_ADDR; *ptr++ = FRAME_ESC; *ptr++ = FRAME_CTRL ^ FRAME_ENC; for (i=0; i len) { syslog(LOG_ERR, "Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); return; } #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif /* Sanity check */ if (packet.code != CODE_SESS) { syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code); return; } if (PPPOE_VER(packet.vertype) != 1) { syslog(LOG_ERR, "Unexpected packet version %d", PPPOE_VER(packet.vertype)); return; } if (PPPOE_TYPE(packet.vertype) != 1) { syslog(LOG_ERR, "Unexpected packet type %d", PPPOE_TYPE(packet.vertype)); return; } if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { /* Not for us -- must be another session. This is not an error, so don't log anything. */ return; } if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { /* Not for us -- must be another session. This is not an error, so don't log anything. */ return; } if (packet.session != conn->session) { /* Not for us -- must be another session. This is not an error, so don't log anything. */ return; } plen = ntohs(packet.length); if (plen + HDR_SIZE > len) { syslog(LOG_ERR, "Bogus length field in session packet %d (%d)", (int) plen, (int) len); return; } /* Clamp MSS */ if (clampMss) { clampMSS(&packet, "incoming", clampMss); } /* Ship it out */ vec[0].iov_base = (void *) dummy; dummy[0] = FRAME_ADDR; dummy[1] = FRAME_CTRL; vec[0].iov_len = 2; vec[1].iov_base = (void *) packet.payload; vec[1].iov_len = plen; if (writev(1, vec, 2) < 0) { fatalSys("syncReadFromEth: write"); } } rp-pppoe-4.0/src/debug.c0000644000175000017500000001005614422272526013262 0ustar dfsdfs/*********************************************************************** * * debug.c * * Implementation of user-space PPPoE redirector for Linux. * * Functions for printing debugging information * * Copyright (C) 2000 by Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #include "config.h" #ifdef DEBUGGING_ENABLED #include #include #include #include #include "pppoe.h" /********************************************************************** *%FUNCTION: dumpHex *%ARGUMENTS: * fp -- file to dump to * buf -- buffer to dump * len -- length of data *%RETURNS: * Nothing *%DESCRIPTION: * Dumps buffer to fp in an easy-to-read format ***********************************************************************/ void dumpHex(FILE *fp, unsigned char const *buf, int len) { int i; int base; if (!fp) return; /* do NOT dump PAP packets */ if (len >= 2 && buf[0] == 0xC0 && buf[1] == 0x23) { fprintf(fp, "(PAP Authentication Frame -- Contents not dumped)\n"); return; } for (base=0; baselength); /* Sheesh... printing times is a pain... */ struct timeval tv; time_t now; int millisec; struct tm *lt; char timebuf[256]; uint16_t type = etherType(packet); if (!fp) return; gettimeofday(&tv, NULL); now = (time_t) tv.tv_sec; millisec = tv.tv_usec / 1000; lt = localtime(&now); strftime(timebuf, 256, "%H:%M:%S", lt); fprintf(fp, "%s.%03d %s PPPoE ", timebuf, millisec, dir); if (type == Eth_PPPOE_Discovery) { fprintf(fp, "Discovery (%x) ", (unsigned) type); } else if (type == Eth_PPPOE_Session) { fprintf(fp, "Session (%x) ", (unsigned) type); } else { fprintf(fp, "Unknown (%x) ", (unsigned) type); } switch(packet->code) { case CODE_PADI: fprintf(fp, "PADI "); break; case CODE_PADO: fprintf(fp, "PADO "); break; case CODE_PADR: fprintf(fp, "PADR "); break; case CODE_PADS: fprintf(fp, "PADS "); break; case CODE_PADT: fprintf(fp, "PADT "); break; case CODE_PADM: fprintf(fp, "PADM "); break; case CODE_PADN: fprintf(fp, "PADN "); break; case CODE_SESS: fprintf(fp, "SESS "); break; } fprintf(fp, "sess-id %d length %d\n", (int) ntohs(packet->session), len); /* Ugly... I apologize... */ fprintf(fp, "SourceAddr %02x:%02x:%02x:%02x:%02x:%02x " "DestAddr %02x:%02x:%02x:%02x:%02x:%02x\n", (unsigned) packet->ethHdr.h_source[0], (unsigned) packet->ethHdr.h_source[1], (unsigned) packet->ethHdr.h_source[2], (unsigned) packet->ethHdr.h_source[3], (unsigned) packet->ethHdr.h_source[4], (unsigned) packet->ethHdr.h_source[5], (unsigned) packet->ethHdr.h_dest[0], (unsigned) packet->ethHdr.h_dest[1], (unsigned) packet->ethHdr.h_dest[2], (unsigned) packet->ethHdr.h_dest[3], (unsigned) packet->ethHdr.h_dest[4], (unsigned) packet->ethHdr.h_dest[5]); dumpHex(fp, packet->payload, ntohs(packet->length)); } #endif /* DEBUGGING_ENABLED */ rp-pppoe-4.0/src/pppoe-sniff.c0000644000175000017500000001464714422272526014434 0ustar dfsdfs/*********************************************************************** * * pppoe-sniff.c * * Sniff a network for likely-looking PPPoE frames and deduce the * command-line options to add to pppoe. USE AT YOUR OWN RISK. * * Copyright (C) 2000-2018 by Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #define _GNU_SOURCE 1 #include #include #include #include #include #include "pppoe.h" /* Default interface if no -I option given */ #define DEFAULT_IF "eth0" /* Global vars */ int SeenPADR = 0; int SeenSess = 0; uint16_t SessType, DiscType; char *IfName = NULL; /* Interface name */ char *ServiceName = NULL; /* Service name */ /********************************************************************** *%FUNCTION: parsePADRTags *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data. *%RETURNS: * Nothing *%DESCRIPTION: * Picks interesting tags out of a PADR packet ***********************************************************************/ void parsePADRTags(uint16_t type, uint16_t len, unsigned char *data, void *extra) { switch(type) { case TAG_SERVICE_NAME: ServiceName = malloc(len+1); if (ServiceName) { memcpy(ServiceName, data, len); ServiceName[len] = 0; } break; } } /********************************************************************** *%FUNCTION: fatalSys *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to stderr and exits. ***********************************************************************/ void fatalSys(char const *str) { printErr("%.256s: %.256s", str, strerror(errno)); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: rp_fatal *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message to stderr and syslog and exits. ***********************************************************************/ void rp_fatal(char const *str) { printErr("%s", str); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: usage *%ARGUMENTS: * argv0 -- program name *%RETURNS: * Nothing *%DESCRIPTION: * Prints usage information and exits. ***********************************************************************/ void usage(char const *argv0) { fprintf(stderr, "Usage: %s [options]\n", argv0); fprintf(stderr, "Options:\n"); fprintf(stderr, " -I if_name -- Specify interface (default %s.)\n", DEFAULT_IF); fprintf(stderr, " -V -- Print version and exit.\n"); fprintf(stderr, "\nPPPoE Version %s, Copyright (C) 2000 Roaring Penguin Software Inc.\n", RP_VERSION); fprintf(stderr, " %*s Copyright (C) 2018-2023 Dianne Skoll\n", (int) strlen(RP_VERSION), ""); fprintf(stderr, "PPPoE comes with ABSOLUTELY NO WARRANTY.\n"); fprintf(stderr, "This is free software, and you are welcome to redistribute it under the terms\n"); fprintf(stderr, "of the GNU General Public License, version 2 or any later version.\n"); fprintf(stderr, "https://dianne.skoll.ca/projects/rp-pppoe/\n"); exit(EXIT_SUCCESS); } /********************************************************************** *%FUNCTION: main *%ARGUMENTS: * argc, argv -- count and values of command-line arguments *%RETURNS: * Nothing *%DESCRIPTION: * Main program ***********************************************************************/ int main(int argc, char *argv[]) { int opt; int sock; PPPoEPacket pkt; int size; if (getuid() != geteuid() || getgid() != getegid()) { fprintf(stderr, "SECURITY WARNING: pppoe-sniff will NOT run suid or sgid. Fix your installation.\n"); exit(EXIT_FAILURE); } while((opt = getopt(argc, argv, "I:V")) != -1) { switch(opt) { case 'I': SET_STRING(IfName, optarg); break; case 'V': printf("pppoe-sniff: RP-PPPoE Version %s\n", RP_VERSION); exit(EXIT_SUCCESS); default: usage(argv[0]); } } /* Pick a default interface name */ if (!IfName) { IfName = DEFAULT_IF; } sock = openInterface(IfName, ETH_P_ALL, NULL, NULL); /* We assume interface is in promiscuous mode -- use "ip link show" to ensure this */ fprintf(stderr, "Sniffing for PADR. Start your connection on another machine...\n"); while (!SeenPADR) { if (receivePacket(sock, &pkt, &size) < 0) continue; if (ntohs(pkt.length) + HDR_SIZE > size) continue; if (PPPOE_VER(pkt.vertype) != 1 || PPPOE_TYPE(pkt.vertype) != 1) continue; if (pkt.code != CODE_PADR) continue; /* Looks promising... parse it */ if (parsePacket(&pkt, parsePADRTags, NULL) < 0) { continue; } DiscType = ntohs(pkt.ethHdr.h_proto); fprintf(stderr, "\nExcellent! Sniffed a likely-looking PADR.\n"); break; } while (!SeenSess) { if (receivePacket(sock, &pkt, &size) < 0) continue; if (ntohs(pkt.length) + HDR_SIZE > size) continue; if (PPPOE_VER(pkt.vertype) != 1 || PPPOE_TYPE(pkt.vertype) != 1) continue; if (pkt.code != CODE_SESS) continue; /* Cool! */ SessType = ntohs(pkt.ethHdr.h_proto); break; } fprintf(stderr, "Wonderful! Sniffed a likely-looking session packet.\n"); if ((ServiceName == NULL || *ServiceName == 0) && DiscType == ETH_PPPOE_DISCOVERY && SessType == ETH_PPPOE_SESSION) { fprintf(stderr, "\nGreat! It looks like a standard PPPoE service.\nYou should not need any special command-line options.\n"); return 0; } fprintf(stderr, "\nOK, looks like you need extra arguments for 'pppoe'.\n"); if (ServiceName != NULL && *ServiceName != 0) { fprintf(stderr, "-S '%s'\n", ServiceName); } if (DiscType != ETH_PPPOE_DISCOVERY || SessType != ETH_PPPOE_SESSION) { fprintf(stderr, "-f %x:%x\n", DiscType, SessType); } return 0; } /********************************************************************** *%FUNCTION: sysErr *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to syslog. ***********************************************************************/ void sysErr(char const *str) { printErr("%.256s: %.256s", str, strerror(errno)); } rp-pppoe-4.0/src/relay.h0000644000175000017500000000736614422272526013327 0ustar dfsdfs/********************************************************************** * * relay.h * * Definitions for PPPoE relay * * Copyright (C) 2001-2006 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * * $Id$ * ***********************************************************************/ #include "config.h" #include "pppoe.h" #if defined(HAVE_LINUX_IF_H) #include #endif /* Description for each active Ethernet interface */ typedef struct InterfaceStruct { char name[IFNAMSIZ+1]; /* Interface name */ int discoverySock; /* Socket for discovery frames */ int sessionSock; /* Socket for session frames */ int clientOK; /* Client requests allowed (PADI, PADR) */ int acOK; /* AC replies allowed (PADO, PADS) */ unsigned char mac[ETH_ALEN]; /* MAC address */ } PPPoEInterface; /* Session state for relay */ struct SessionHashStruct; typedef struct SessionStruct { struct SessionStruct *next; /* Free list link */ struct SessionStruct *prev; /* Free list link */ struct SessionHashStruct *acHash; /* Hash bucket for AC MAC/Session */ struct SessionHashStruct *clientHash; /* Hash bucket for client MAC/Session */ unsigned int epoch; /* Epoch when last activity was seen */ uint16_t sesNum; /* Session number assigned by relay */ } PPPoESession; /* Hash table entry to find sessions */ typedef struct SessionHashStruct { struct SessionHashStruct *next; /* Link in hash chain */ struct SessionHashStruct *prev; /* Link in hash chain */ struct SessionHashStruct *peer; /* Peer for this session */ PPPoEInterface const *interface; /* Interface */ unsigned char peerMac[ETH_ALEN]; /* Peer's MAC address */ uint16_t sesNum; /* Session number */ PPPoESession *ses; /* Session data */ } SessionHash; /* Function prototypes */ void relayGotSessionPacket(PPPoEInterface const *i); void relayGotDiscoveryPacket(PPPoEInterface const *i); PPPoEInterface *findInterface(int sock); unsigned int hash(unsigned char const *mac, uint16_t sesNum); SessionHash *findSession(unsigned char const *mac, uint16_t sesNum); void deleteHash(SessionHash *hash); PPPoESession *createSession(PPPoEInterface const *ac, PPPoEInterface const *cli, unsigned char const *acMac, unsigned char const *cliMac, uint16_t acSes); void freeSession(PPPoESession *ses, char const *msg); void addInterface(char const *ifname, int clientOK, int acOK); void usage(char const *progname); void initRelay(int nsess); void relayLoop(void); void addHash(SessionHash *sh); void unhash(SessionHash *sh); void relayHandlePADT(PPPoEInterface const *iface, PPPoEPacket *packet, int size); void relayHandlePADI(PPPoEInterface const *iface, PPPoEPacket *packet, int size); void relayHandlePADO(PPPoEInterface const *iface, PPPoEPacket *packet, int size); void relayHandlePADR(PPPoEInterface const *iface, PPPoEPacket *packet, int size); void relayHandlePADS(PPPoEInterface const *iface, PPPoEPacket *packet, int size); int addTag(PPPoEPacket *packet, PPPoETag const *tag); int insertBytes(PPPoEPacket *packet, unsigned char *loc, void const *bytes, int length); int removeBytes(PPPoEPacket *packet, unsigned char *loc, int length); void relaySendError(unsigned char code, uint16_t session, PPPoEInterface const *iface, unsigned char const *mac, PPPoETag const *hostUniq, char const *errMsg); void alarmHandler(int sig); void cleanSessions(void); #define MAX_INTERFACES 8 #define DEFAULT_SESSIONS 5000 /* Hash table size -- a prime number; gives load factor of around 6 for 65534 sessions */ #define HASHTAB_SIZE 18917 rp-pppoe-4.0/src/config.h.in0000644000175000017500000000161714422272526014056 0ustar dfsdfs/* config.h.in. Generated automatically from configure.in by autoheader. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ #undef HAVE_STRUCT_SOCKADDR_LL /* Define if you have kernel-mode PPPoE in Linux file. */ #undef HAVE_LINUX_KERNEL_PPPOE /* Define if you have the header file. */ #undef HAVE_LINUX_IF_PACKET_H /* Define if you have the header file. */ #undef HAVE_LINUX_IF_PPPOX_H /* Define if you have the header file. */ #undef HAVE_NET_IF_ARP_H /* Define if you have the header file. */ #undef HAVE_NET_ETHERNET_H /* Define if you have the header file. */ #undef HAVE_LINUX_IF_H /* Define if you have the header file. */ #undef HAVE_SYS_UIO_H /* Define if you have the N_HDLC line discipline in pty.h */ #undef HAVE_N_HDLC /* Define to include debugging code */ #undef DEBUGGING_ENABLED rp-pppoe-4.0/src/configure0000755000175000017500000046705414422272526013755 0ustar dfsdfs#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= PACKAGE_URL= ac_unique_file="pppoe.c" ac_default_prefix=/usr # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS datadir_evaluated STATIC LIBEVENT RDYNAMIC PPPOE_SERVER_DEPS TARGETS WRAPPER ID SETSID PPPD IP INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM LIBOBJS PPPOE_RELAY PPPD_H PPPD_INCDIR LINUX_KERNELMODE_PLUGIN EGREP GREP CPP AR RANLIB OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_plugin enable_debugging enable_static enable_licenses ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-plugin=pppd_src_path build pppd plugin --disable-debugging disable debugging code --enable-static Build static executables --enable-licenses=lic_path build commercial version Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF configure generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu PPPD_DIR=ppp-2.4.1.pppoe2 ac_config_headers="$ac_config_headers config.h" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="${ac_tool_prefix}ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_AR"; then ac_ct_AR=$AR # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_AR" = x; then AR="/bin/false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi else AR="$ac_cv_prog_AR" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 $as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } if ${ac_cv_header_sys_wait_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif int main () { int s; wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_sys_wait_h=yes else ac_cv_header_sys_wait_h=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5 $as_echo "$ac_cv_header_sys_wait_h" >&6; } if test $ac_cv_header_sys_wait_h = yes; then $as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in net/if_arp.h sys/uio.h net/ethernet.h linux/if_packet.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in linux/if.h do : ac_fn_c_check_header_compile "$LINENO" "linux/if.h" "ac_cv_header_linux_if_h" "#include " if test "x$ac_cv_header_linux_if_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LINUX_IF_H 1 _ACEOF fi done for ac_header in linux/if_pppox.h do : ac_fn_c_check_header_compile "$LINENO" "linux/if_pppox.h" "ac_cv_header_linux_if_pppox_h" " #include #include #include #include " if test "x$ac_cv_header_linux_if_pppox_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LINUX_IF_PPPOX_H 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct sockaddr_ll" >&5 $as_echo_n "checking for struct sockaddr_ll... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { struct sockaddr_ll sa; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_struct_sockaddr_ll=yes else ac_cv_struct_sockaddr_ll=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_sockaddr_ll" >&5 $as_echo "$ac_cv_struct_sockaddr_ll" >&6; } if test "$ac_cv_struct_sockaddr_ll" = yes ; then $as_echo "#define HAVE_STRUCT_SOCKADDR_LL 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for N_HDLC line discipline" >&5 $as_echo_n "checking for N_HDLC line discipline... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { int x = N_HDLC; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_n_hdlc=yes else ac_cv_n_hdlc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_n_hdlc" >&5 $as_echo "$ac_cv_n_hdlc" >&6; } if test "$ac_cv_n_hdlc" = yes ; then $as_echo "#define HAVE_N_HDLC 1" >>confdefs.h fi # Check whether --enable-plugin was given. if test "${enable_plugin+set}" = set; then : enableval=$enable_plugin; ac_cv_pluginpath=$enableval else ac_cv_pluginpath=no fi # Check whether --enable-debugging was given. if test "${enable_debugging+set}" = set; then : enableval=$enable_debugging; ac_cv_debugging=$enableval else ac_cv_debugging=yes fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; ac_cv_static=$enableval else ac_cv_static=no fi if test "$ac_cv_pluginpath" = "yes" ; then for i in /usr/include /usr/local/include `echo $CPATH | sed -e 's/:/ /g'`; do if test -r $i/pppd/pppd.h; then ac_cv_pluginpath=$i fi done fi if test "$ac_cv_pluginpath" = "yes" ; then echo "*** Could not find pppd/pppd.h anywhere... not building plugin" ac_cv_pluginpath=no fi # Check whether --enable-licenses was given. if test "${enable_licenses+set}" = set; then : enableval=$enable_licenses; ac_cv_licpath=$enableval else ac_cv_licpath=no fi PPPOE_SERVER_DEPS="" LINUX_KERNELMODE_PLUGIN="" PPPD_INCDIR="" if test "$ac_cv_header_linux_if_pppox_h" = yes ; then if test "$ac_cv_pluginpath" != no ; then LINUX_KERNELMODE_PLUGIN=rp-pppoe.so $as_echo "#define HAVE_LINUX_KERNEL_PPPOE 1" >>confdefs.h PPPD_INCDIR=$ac_cv_pluginpath fi fi if test "$PPPD_INCDIR" = "" ; then PPPD_INCDIR=/usr/include fi if test "$ac_cv_debugging" = "yes" ; then $as_echo "#define DEBUGGING_ENABLED 1" >>confdefs.h fi PPPOE_RELAY="" if test "`uname -s`" = "Linux" ; then PPPOE_RELAY=pppoe-relay fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working memcmp" >&5 $as_echo_n "checking for working memcmp... " >&6; } if ${ac_cv_func_memcmp_working+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : ac_cv_func_memcmp_working=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Some versions of memcmp are not 8-bit clean. */ char c0 = '\100', c1 = '\200', c2 = '\201'; if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) return 1; /* The Next x86 OpenStep bug shows up only when comparing 16 bytes or more and with at least one buffer not starting on a 4-byte boundary. William Lewis provided this test program. */ { char foo[21]; char bar[21]; int i; for (i = 0; i < 4; i++) { char *a = foo + i; char *b = bar + i; strcpy (a, "--------01111111"); strcpy (b, "--------10000000"); if (memcmp (a, b, 16) >= 0) return 1; } return 0; } ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_memcmp_working=yes else ac_cv_func_memcmp_working=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_memcmp_working" >&5 $as_echo "$ac_cv_func_memcmp_working" >&6; } test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in *" memcmp.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" ;; esac for ac_func in select socket strerror strtol strlcpy do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # Extract the first word of "ip", so it can be a program name with args. set dummy ip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_IP+:} false; then : $as_echo_n "(cached) " >&6 else case $IP in [\\/]* | ?:[\\/]*) ac_cv_path_IP="$IP" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="$PATH:/bin:/sbin:/usr/bin:/usr/sbin" for as_dir in $as_dummy do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_IP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_IP" && ac_cv_path_IP="NOTFOUND" ;; esac fi IP=$ac_cv_path_IP if test -n "$IP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $IP" >&5 $as_echo "$IP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "pppd", so it can be a program name with args. set dummy pppd; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PPPD+:} false; then : $as_echo_n "(cached) " >&6 else case $PPPD in [\\/]* | ?:[\\/]*) ac_cv_path_PPPD="$PPPD" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="$PATH:/sbin:/usr/sbin:/usr/local/sbin" for as_dir in $as_dummy do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PPPD="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_PPPD" && ac_cv_path_PPPD="NOTFOUND" ;; esac fi PPPD=$ac_cv_path_PPPD if test -n "$PPPD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PPPD" >&5 $as_echo "$PPPD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "setsid", so it can be a program name with args. set dummy setsid; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_SETSID+:} false; then : $as_echo_n "(cached) " >&6 else case $SETSID in [\\/]* | ?:[\\/]*) ac_cv_path_SETSID="$SETSID" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="$PATH:/sbin:/usr/sbin:/usr/local/sbin" for as_dir in $as_dummy do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_SETSID="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_SETSID" && ac_cv_path_SETSID="""" ;; esac fi SETSID=$ac_cv_path_SETSID if test -n "$SETSID"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SETSID" >&5 $as_echo "$SETSID" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "id", so it can be a program name with args. set dummy id; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ID+:} false; then : $as_echo_n "(cached) " >&6 else case $ID in [\\/]* | ?:[\\/]*) ac_cv_path_ID="$ID" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="/usr/xpg4/bin:$PATH" for as_dir in $as_dummy do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ID="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_ID" && ac_cv_path_ID="""" ;; esac fi ID=$ac_cv_path_ID if test -n "$ID"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ID" >&5 $as_echo "$ID" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux 2.4.X kernel-mode PPPoE support" >&5 $as_echo_n "checking for Linux 2.4.X kernel-mode PPPoE support... " >&6; } if ${ac_cv_linux_kernel_pppoe+:} false; then : $as_echo_n "(cached) " >&6 else if test "`uname -s`" = "Linux" ; then if test $cross_compiling = "no"; then modprobe ppp_generic > /dev/null 2>&1 modprobe ppp_async > /dev/null 2>&1 modprobe n_hdlc > /dev/null 2>&1 modprobe ppp_synctty > /dev/null 2>&1 modprobe pppoe > /dev/null 2>&1 fi if test "$cross_compiling" = yes; then : ac_cv_linux_kernel_pppoe=no; echo "cross-compiling, default: " else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main() { if (socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE) >= 0) return 0; else return 1; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_linux_kernel_pppoe=yes else ac_cv_linux_kernel_pppoe=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi else ac_cv_linux_kernel_pppoe=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_linux_kernel_pppoe" >&5 $as_echo "$ac_cv_linux_kernel_pppoe" >&6; } if test "$ac_cv_linux_kernel_pppoe" != yes ; then if test "$LINUX_KERNELMODE_PLUGIN" = rp-pppoe.so; then echo "*** Your kernel does not appear to have built-in PPPoE support," echo "*** but I will build the kernel-mode plugin anyway." fi fi if test "$GCC" = yes; then CFLAGS="$CFLAGS -fno-strict-aliasing -Wall -Wstrict-prototypes" fi if test "$PPPD" = "NOTFOUND"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Oops! I couldn't find pppd, the PPP daemon anywhere." >&5 $as_echo "$as_me: WARNING: *** Oops! I couldn't find pppd, the PPP daemon anywhere." >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** You must install pppd, version 2.3.10 or later." >&5 $as_echo "$as_me: WARNING: *** You must install pppd, version 2.3.10 or later." >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** I will keep going, but it may not work." >&5 $as_echo "$as_me: WARNING: *** I will keep going, but it may not work." >&2;} PPPD=pppd fi # Sigh... got to fix this up for tcl test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Fully resolve WRAPPER for Tcl script. WRAPPER=${sbindir}/pppoe-wrapper eval "WRAPPER=${WRAPPER}" eval "WRAPPER=${WRAPPER}" # Determine what targets to build TARGETS="pppoe pppoe-server pppoe-sniff pppoe-relay" # plugin is built only if we have kernel support if test -n "$LINUX_KERNELMODE_PLUGIN" ; then TARGETS="$TARGETS $LINUX_KERNELMODE_PLUGIN" mkdir plugin > /dev/null 2>&1 fi EXTRACONFIGS="" RDYNAMIC="" if test "$ac_cv_static" = "yes" ; then STATIC=-static else STATIC="" fi LIBEVENT=../libevent datadir_evaluated=`eval echo $datadir` ac_config_files="$ac_config_files Makefile libevent/Makefile $EXTRACONFIGS" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by $as_me, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "libevent/Makefile") CONFIG_FILES="$CONFIG_FILES libevent/Makefile" ;; "$EXTRACONFIGS") CONFIG_FILES="$CONFIG_FILES $EXTRACONFIGS" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: On this platform, the following targets will be built:" >&5 $as_echo "On this platform, the following targets will be built:" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TARGETS" >&5 $as_echo "$TARGETS" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: Type 'make' to compile the software." >&5 $as_echo "Type 'make' to compile the software." >&6; } rp-pppoe-4.0/src/plugin/0000755000175000017500000000000014422272526013324 5ustar dfsdfsrp-pppoe-4.0/src/md5.h0000644000175000017500000000112414422272526012662 0ustar dfsdfs#ifndef MD5_H #define MD5_H /* * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include struct MD5Context { uint32_t buf[4]; uint32_t bits[2]; unsigned char in[64]; }; void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len); void MD5Final(unsigned char digest[16], struct MD5Context *context); void MD5Transform(uint32_t buf[4], uint32_t const in[16]); /* * This is needed to make RSAREF happy on some MS-DOS compilers. */ typedef struct MD5Context MD5_CTX; #endif /* !MD5_H */ rp-pppoe-4.0/src/pppoe-server.h0000644000175000017500000001045414422272526014632 0ustar dfsdfs/********************************************************************** * * pppoe-server.h * * Definitions for PPPoE server * * Copyright (C) 2001-2012 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * * $Id$ * ***********************************************************************/ #include "config.h" #include "event.h" #include "pppoe.h" #if defined(HAVE_LINUX_IF_H) #include #elif defined(HAVE_NET_IF_H) #include #endif #define MAX_USERNAME_LEN 31 /* An Ethernet interface */ typedef struct { char name[IFNAMSIZ+1]; /* Interface name */ int sock; /* Socket for discovery frames */ unsigned char mac[ETH_ALEN]; /* MAC address */ EventHandler *eh; /* Event handler for this interface */ uint16_t mtu; /* MTU of interface */ } Interface; #define FLAG_RECVD_PADT 1 #define FLAG_USER_SET 2 #define FLAG_IP_SET 4 #define FLAG_SENT_PADT 8 /* Only used if we are an L2TP LAC or LNS */ #define FLAG_ACT_AS_LAC 256 #define FLAG_ACT_AS_LNS 512 /* Forward declaration */ struct ClientSessionStruct; /* Dispatch table for session-related functions. We call different functions for L2TP-terminated sessions than for locally-terminated sessions. */ typedef struct PppoeSessionFunctionTable_t { /* Stop the session */ void (*stop)(struct ClientSessionStruct *ses, char const *reason); /* Return 1 if session is active, 0 otherwise */ int (*isActive)(struct ClientSessionStruct *ses); /* Describe a session in human-readable form */ char const * (*describe)(struct ClientSessionStruct *ses); } PppoeSessionFunctionTable; extern PppoeSessionFunctionTable DefaultSessionFunctionTable; /* A client session */ typedef struct ClientSessionStruct { struct ClientSessionStruct *next; /* In list of free or active sessions */ PppoeSessionFunctionTable *funcs; /* Function table */ pid_t pid; /* PID of child handling session */ Interface *ethif; /* Ethernet interface */ unsigned char myip[IPV4ALEN]; /* Local IP address */ unsigned char peerip[IPV4ALEN]; /* Desired IP address of peer */ uint16_t sess; /* Session number */ unsigned char eth[ETH_ALEN]; /* Peer's Ethernet address */ unsigned int flags; /* Various flags */ time_t startTime; /* When session started */ char const *serviceName; /* Service name */ uint16_t requested_mtu; /* Requested PPP_MAX_PAYLOAD per RFC 4638 */ } ClientSession; /* Hack for daemonizing */ #define CLOSEFD 64 /* Initial Max. number of interfaces to listen on */ #define INIT_INTERFACES 8 /* Max. 64 sessions by default */ #define DEFAULT_MAX_SESSIONS 64 /* An array of client sessions */ extern ClientSession *Sessions; /* Interfaces we're listening on */ extern Interface *interfaces; extern int NumInterfaces; /* The number of session slots */ extern size_t NumSessionSlots; /* The number of active sessions */ extern size_t NumActiveSessions; /* Offset of first session */ extern size_t SessOffset; /* Access concentrator name */ extern char *ACName; extern unsigned char LocalIP[IPV4ALEN]; extern unsigned char RemoteIP[IPV4ALEN]; /* Do not create new sessions if free RAM < 10MB (on Linux only!) */ #define MIN_FREE_MEMORY 10000 /* Do we increment local IP for each connection? */ extern int IncrLocalIP; /* Free sessions */ extern ClientSession *FreeSessions; /* When a session is freed, it is added to the end of the free list */ extern ClientSession *LastFreeSession; /* Busy sessions */ extern ClientSession *BusySessions; extern EventSelector *event_selector; extern int GotAlarm; extern void setAlarm(unsigned int secs); extern void killAllSessions(void); extern void serverProcessPacket(Interface *i); extern void processPADT(Interface *ethif, PPPoEPacket *packet, int len); extern void processPADR(Interface *ethif, PPPoEPacket *packet, int len); extern void processPADI(Interface *ethif, PPPoEPacket *packet, int len); extern void usage(char const *msg); extern ClientSession *pppoe_alloc_session(void); extern int pppoe_free_session(ClientSession *ses); extern void sendHURLorMOTM(PPPoEConnection *conn, char const *url, uint16_t tag); rp-pppoe-4.0/src/libevent/0000755000175000017500000000000014422272526013636 5ustar dfsdfsrp-pppoe-4.0/src/libevent/eventpriv.h0000644000175000017500000000321214422272526016027 0ustar dfsdfs/*********************************************************************** * * eventpriv.h * * Abstraction of select call into "event-handling" to make programming * easier. This header includes "private" definitions which users * of the event-handling code should not care about. * * Copyright (C) 2001 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * $Id$ * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #ifndef INCLUDE_EVENTPRIV_H #define INCLUDE_EVENTPRIV_H 1 #include #include #include /* Handler structure */ typedef struct EventHandler_t { struct EventHandler_t *next; /* Link in list */ int fd; /* File descriptor for select */ unsigned int flags; /* Select on read or write; enable timeout */ struct timeval tmout; /* Absolute time for timeout */ EventCallbackFunc fn; /* Callback function */ void *data; /* Extra data to pass to callback */ } EventHandler; /* Selector structure */ typedef struct EventSelector_t { EventHandler *handlers; /* Linked list of EventHandlers */ int nestLevel; /* Event-handling nesting level */ int opsPending; /* True if operations are pending */ int destroyPending; /* If true, a destroy is pending */ } EventSelector; /* Private flags */ #define EVENT_FLAG_DELETED 256 #endif rp-pppoe-4.0/src/libevent/hash.c0000644000175000017500000001647014422272526014735 0ustar dfsdfs/*********************************************************************** * * hash.c * * Implementation of hash tables. Each item inserted must include * a hash_bucket member. * * Copyright (C) 2002 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This software may be distributed under the terms of the GNU General * Public License, Version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #include "hash.h" #include #define BITS_IN_int ( sizeof(int) * CHAR_BIT ) #define THREE_QUARTERS ((int) ((BITS_IN_int * 3) / 4)) #define ONE_EIGHTH ((int) (BITS_IN_int / 8)) #define HIGH_BITS ( ~((unsigned int)(~0) >> ONE_EIGHTH )) #define GET_BUCKET(tab, data) ((hash_bucket *) ((char *) (data) + (tab)->hash_offset)) #define GET_ITEM(tab, bucket) ((void *) (((char *) (void *) bucket) - (tab)->hash_offset)) static void *hash_next_cursor(hash_table *tab, hash_bucket *b); /********************************************************************** * %FUNCTION: hash_init * %ARGUMENTS: * tab -- hash table * hash_offset -- offset of hash_bucket data member in inserted items * compute -- pointer to function to compute hash value * compare -- pointer to comparison function. Returns 0 if items are equal, * non-zero otherwise. * %RETURNS: * Nothing * %DESCRIPTION: * Initializes a hash table. ***********************************************************************/ void hash_init(hash_table *tab, size_t hash_offset, unsigned int (*compute)(void *data), int (*compare)(void *item1, void *item2)) { size_t i; tab->hash_offset = hash_offset; tab->compute_hash = compute; tab->compare = compare; for (i=0; ibuckets[i] = NULL; } tab->num_entries = 0; } /********************************************************************** * %FUNCTION: hash_insert * %ARGUMENTS: * tab -- hash table to insert into * item -- the item we're inserting * %RETURNS: * Nothing * %DESCRIPTION: * Inserts an item into the hash table. It must not currently be in any * hash table. ***********************************************************************/ void hash_insert(hash_table *tab, void *item) { hash_bucket *b = GET_BUCKET(tab, item); unsigned int val = tab->compute_hash(item); b->hashval = val; val %= HASHTAB_SIZE; b->prev = NULL; b->next = tab->buckets[val]; if (b->next) { b->next->prev = b; } tab->buckets[val] = b; tab->num_entries++; } /********************************************************************** * %FUNCTION: hash_remove * %ARGUMENTS: * tab -- hash table * item -- item in hash table * %RETURNS: * Nothing * %DESCRIPTION: * Removes item from hash table ***********************************************************************/ void hash_remove(hash_table *tab, void *item) { hash_bucket *b = GET_BUCKET(tab, item); unsigned int val = b->hashval % HASHTAB_SIZE; if (b->prev) { b->prev->next = b->next; } else { tab->buckets[val] = b->next; } if (b->next) { b->next->prev = b->prev; } tab->num_entries--; } /********************************************************************** * %FUNCTION: hash_find * %ARGUMENTS: * tab -- hash table * item -- item equal to one we're seeking (in the compare-function sense) * %RETURNS: * A pointer to the item in the hash table, or NULL if no such item * %DESCRIPTION: * Searches hash table for item. ***********************************************************************/ void * hash_find(hash_table *tab, void *item) { unsigned int val = tab->compute_hash(item) % HASHTAB_SIZE; hash_bucket *b; for (b = tab->buckets[val]; b; b = b->next) { void *item2 = GET_ITEM(tab, b); if (!tab->compare(item, item2)) return item2; } return NULL; } /********************************************************************** * %FUNCTION: hash_find_next * %ARGUMENTS: * tab -- hash table * item -- an item returned by hash_find or hash_find_next * %RETURNS: * A pointer to the next equal item in the hash table, or NULL if no such item * %DESCRIPTION: * Searches hash table for anoter item equivalent to this one. Search * starts from item. ***********************************************************************/ void * hash_find_next(hash_table *tab, void *item) { hash_bucket *b = GET_BUCKET(tab, item); for (b = b->next; b; b = b->next) { void *item2 = GET_ITEM(tab, b); if (!tab->compare(item, item2)) return item2; } return NULL; } /********************************************************************** * %FUNCTION: hash_start * %ARGUMENTS: * tab -- hash table * cursor -- a void pointer to keep track of location * %RETURNS: * "first" entry in hash table, or NULL if table is empty * %DESCRIPTION: * Starts an iterator -- sets cursor so hash_next will return next entry. ***********************************************************************/ void * hash_start(hash_table *tab, void **cursor) { int i; for (i=0; ibuckets[i]) { /* Point cursor to NEXT item so it is valid even if current item is free'd */ *cursor = hash_next_cursor(tab, tab->buckets[i]); return GET_ITEM(tab, tab->buckets[i]); } } *cursor = NULL; return NULL; } /********************************************************************** * %FUNCTION: hash_next * %ARGUMENTS: * tab -- hash table * cursor -- cursor into hash table * %RETURNS: * Next item in table, or NULL. * %DESCRIPTION: * Steps cursor to next item in table. ***********************************************************************/ void * hash_next(hash_table *tab, void **cursor) { hash_bucket *b; if (!*cursor) return NULL; b = (hash_bucket *) *cursor; *cursor = hash_next_cursor(tab, b); return GET_ITEM(tab, b); } /********************************************************************** * %FUNCTION: hash_next_cursor * %ARGUMENTS: * tab -- a hash table * b -- a hash bucket * %RETURNS: * Cursor value for bucket following b in hash table. ***********************************************************************/ static void * hash_next_cursor(hash_table *tab, hash_bucket *b) { unsigned int i; if (!b) return NULL; if (b->next) return b->next; i = b->hashval % HASHTAB_SIZE; for (++i; ibuckets[i]) return tab->buckets[i]; } return NULL; } size_t hash_num_entries(hash_table *tab) { return tab->num_entries; } /********************************************************************** * %FUNCTION: hash_pjw * %ARGUMENTS: * str -- a zero-terminated string * %RETURNS: * A hash value using the hashpjw algorithm * %DESCRIPTION: * An adaptation of Peter Weinberger's (PJW) generic hashing * algorithm based on Allen Holub's version. Accepts a pointer * to a datum to be hashed and returns an unsigned integer. ***********************************************************************/ unsigned int hash_pjw(char const * str) { unsigned int hash_value, i; for (hash_value = 0; *str; ++str) { hash_value = ( hash_value << ONE_EIGHTH ) + *str; if (( i = hash_value & HIGH_BITS ) != 0 ) { hash_value = ( hash_value ^ ( i >> THREE_QUARTERS )) & ~HIGH_BITS; } } return hash_value; } rp-pppoe-4.0/src/libevent/event.c0000644000175000017500000003763114422272526015135 0ustar dfsdfs/*********************************************************************** * * event.c * * Abstraction of select call into "event-handling" to make programming * easier. * * Copyright (C) 2001 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #include "event.h" #include #include static void DestroySelector(EventSelector *es); static void DestroyHandler(EventHandler *eh); static void DoPendingChanges(EventSelector *es); /********************************************************************** * %FUNCTION: Event_CreateSelector * %ARGUMENTS: * None * %RETURNS: * A newly-allocated EventSelector, or NULL if out of memory. * %DESCRIPTION: * Creates a new EventSelector. ***********************************************************************/ EventSelector * Event_CreateSelector(void) { EventSelector *es = malloc(sizeof(EventSelector)); if (!es) return NULL; es->handlers = NULL; es->nestLevel = 0; es->destroyPending = 0; es->opsPending = 0; EVENT_DEBUG(("CreateSelector() -> %p\n", (void *) es)); return es; } /********************************************************************** * %FUNCTION: Event_DestroySelector * %ARGUMENTS: * es -- EventSelector to destroy * %RETURNS: * Nothing * %DESCRIPTION: * Destroys an EventSelector. Destruction may be delayed if we * are in the HandleEvent function. ***********************************************************************/ void Event_DestroySelector(EventSelector *es) { if (es->nestLevel) { es->destroyPending = 1; es->opsPending = 1; return; } DestroySelector(es); } /********************************************************************** * %FUNCTION: Event_HandleEvent * %ARGUMENTS: * es -- EventSelector * %RETURNS: * 0 if OK, non-zero on error. errno is set appropriately. * %DESCRIPTION: * Handles a single event (uses select() to wait for an event.) ***********************************************************************/ int Event_HandleEvent(EventSelector *es) { fd_set readfds, writefds; fd_set *rd, *wr; unsigned int flags; struct timeval abs_timeout, now; struct timeval timeout; struct timeval *tm; EventHandler *eh; int r = 0; int errno_save = 0; int foundTimeoutEvent = 0; int foundReadEvent = 0; int foundWriteEvent = 0; int maxfd = -1; int pastDue; /* Avoid compiler warning */ abs_timeout.tv_sec = 0; abs_timeout.tv_usec = 0; EVENT_DEBUG(("Enter Event_HandleEvent(es=%p)\n", (void *) es)); /* Build the select sets */ FD_ZERO(&readfds); FD_ZERO(&writefds); eh = es->handlers; for (eh=es->handlers; eh; eh=eh->next) { if (eh->flags & EVENT_FLAG_DELETED) continue; if (eh->flags & EVENT_FLAG_READABLE) { foundReadEvent = 1; FD_SET(eh->fd, &readfds); if (eh->fd > maxfd) maxfd = eh->fd; } if (eh->flags & EVENT_FLAG_WRITEABLE) { foundWriteEvent = 1; FD_SET(eh->fd, &writefds); if (eh->fd > maxfd) maxfd = eh->fd; } if (eh->flags & EVENT_TIMER_BITS) { if (!foundTimeoutEvent) { abs_timeout = eh->tmout; foundTimeoutEvent = 1; } else { if (eh->tmout.tv_sec < abs_timeout.tv_sec || (eh->tmout.tv_sec == abs_timeout.tv_sec && eh->tmout.tv_usec < abs_timeout.tv_usec)) { abs_timeout = eh->tmout; } } } } if (foundReadEvent) { rd = &readfds; } else { rd = NULL; } if (foundWriteEvent) { wr = &writefds; } else { wr = NULL; } if (foundTimeoutEvent) { gettimeofday(&now, NULL); /* Convert absolute timeout to relative timeout for select */ timeout.tv_usec = abs_timeout.tv_usec - now.tv_usec; timeout.tv_sec = abs_timeout.tv_sec - now.tv_sec; if (timeout.tv_usec < 0) { timeout.tv_usec += 1000000; timeout.tv_sec--; } if (timeout.tv_sec < 0 || (timeout.tv_sec == 0 && timeout.tv_usec < 0)) { timeout.tv_sec = 0; timeout.tv_usec = 0; } tm = &timeout; } else { tm = NULL; } if (foundReadEvent || foundWriteEvent || foundTimeoutEvent) { for(;;) { r = select(maxfd+1, rd, wr, NULL, tm); if (r < 0) { if (errno == EINTR) continue; } break; } } if (foundTimeoutEvent) gettimeofday(&now, NULL); errno_save = errno; es->nestLevel++; if (r >= 0) { /* Call handlers */ for (eh=es->handlers; eh; eh=eh->next) { /* Pending delete for this handler? Ignore it */ if (eh->flags & EVENT_FLAG_DELETED) continue; flags = 0; if ((eh->flags & EVENT_FLAG_READABLE) && FD_ISSET(eh->fd, &readfds)) { flags |= EVENT_FLAG_READABLE; } if ((eh->flags & EVENT_FLAG_WRITEABLE) && FD_ISSET(eh->fd, &writefds)) { flags |= EVENT_FLAG_WRITEABLE; } if (eh->flags & EVENT_TIMER_BITS) { pastDue = (eh->tmout.tv_sec < now.tv_sec || (eh->tmout.tv_sec == now.tv_sec && eh->tmout.tv_usec <= now.tv_usec)); if (pastDue) { flags |= EVENT_TIMER_BITS; if (eh->flags & EVENT_FLAG_TIMER) { /* Timer events are only called once */ es->opsPending = 1; eh->flags |= EVENT_FLAG_DELETED; } } } /* Do callback */ if (flags) { EVENT_DEBUG(("Enter callback: eh=%p flags=%u fd=%d\n", eh, flags, eh->fd)); eh->fn(es, eh->fd, flags, eh->data); EVENT_DEBUG(("Leave callback: eh=%p flags=%u fd=%d\n", eh, flags, eh->fd)); } } } es->nestLevel--; if (!es->nestLevel && es->opsPending) { DoPendingChanges(es); } errno = errno_save; return r; } /********************************************************************** * %FUNCTION: Event_AddHandler * %ARGUMENTS: * es -- event selector * fd -- file descriptor to watch * flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE * fn -- callback function to call when event is triggered * data -- extra data to pass to callback function * %RETURNS: * A newly-allocated EventHandler, or NULL. ***********************************************************************/ EventHandler * Event_AddHandler(EventSelector *es, int fd, unsigned int flags, EventCallbackFunc fn, void *data) { EventHandler *eh; /* Specifically disable timer and deleted flags */ flags &= (~(EVENT_TIMER_BITS | EVENT_FLAG_DELETED)); /* Bad file descriptor */ if (fd < 0) { errno = EBADF; return NULL; } eh = malloc(sizeof(EventHandler)); if (!eh) return NULL; eh->fd = fd; eh->flags = flags; eh->tmout.tv_usec = 0; eh->tmout.tv_sec = 0; eh->fn = fn; eh->data = data; /* Add immediately. This is safe even if we are in a handler. */ eh->next = es->handlers; es->handlers = eh; EVENT_DEBUG(("Event_AddHandler(es=%p, fd=%d, flags=%u) -> %p\n", es, fd, flags, eh)); return eh; } /********************************************************************** * %FUNCTION: Event_AddHandlerWithTimeout * %ARGUMENTS: * es -- event selector * fd -- file descriptor to watch * flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE * t -- Timeout after which to call handler, even if not readable/writable. * If t.tv_sec < 0, calls normal Event_AddHandler with no timeout. * fn -- callback function to call when event is triggered * data -- extra data to pass to callback function * %RETURNS: * A newly-allocated EventHandler, or NULL. ***********************************************************************/ EventHandler * Event_AddHandlerWithTimeout(EventSelector *es, int fd, unsigned int flags, struct timeval t, EventCallbackFunc fn, void *data) { EventHandler *eh; struct timeval now; /* If timeout is negative, just do normal non-timing-out event */ if (t.tv_sec < 0 || t.tv_usec < 0) { return Event_AddHandler(es, fd, flags, fn, data); } /* Specifically disable timer and deleted flags */ flags &= (~(EVENT_FLAG_TIMER | EVENT_FLAG_DELETED)); flags |= EVENT_FLAG_TIMEOUT; /* Bad file descriptor? */ if (fd < 0) { errno = EBADF; return NULL; } /* Bad timeout? */ if (t.tv_usec >= 1000000) { errno = EINVAL; return NULL; } eh = malloc(sizeof(EventHandler)); if (!eh) return NULL; /* Convert time interval to absolute time */ gettimeofday(&now, NULL); t.tv_sec += now.tv_sec; t.tv_usec += now.tv_usec; if (t.tv_usec >= 1000000) { t.tv_usec -= 1000000; t.tv_sec++; } eh->fd = fd; eh->flags = flags; eh->tmout = t; eh->fn = fn; eh->data = data; /* Add immediately. This is safe even if we are in a handler. */ eh->next = es->handlers; es->handlers = eh; EVENT_DEBUG(("Event_AddHandlerWithTimeout(es=%p, fd=%d, flags=%u, t=%d/%d) -> %p\n", es, fd, flags, t.tv_sec, t.tv_usec, eh)); return eh; } /********************************************************************** * %FUNCTION: Event_AddTimerHandler * %ARGUMENTS: * es -- event selector * t -- time interval after which to trigger event * fn -- callback function to call when event is triggered * data -- extra data to pass to callback function * %RETURNS: * A newly-allocated EventHandler, or NULL. ***********************************************************************/ EventHandler * Event_AddTimerHandler(EventSelector *es, struct timeval t, EventCallbackFunc fn, void *data) { EventHandler *eh; struct timeval now; /* Check time interval for validity */ if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) { errno = EINVAL; return NULL; } eh = malloc(sizeof(EventHandler)); if (!eh) return NULL; /* Convert time interval to absolute time */ gettimeofday(&now, NULL); t.tv_sec += now.tv_sec; t.tv_usec += now.tv_usec; if (t.tv_usec >= 1000000) { t.tv_usec -= 1000000; t.tv_sec++; } eh->fd = -1; eh->flags = EVENT_FLAG_TIMER; eh->tmout = t; eh->fn = fn; eh->data = data; /* Add immediately. This is safe even if we are in a handler. */ eh->next = es->handlers; es->handlers = eh; EVENT_DEBUG(("Event_AddTimerHandler(es=%p, t=%d/%d) -> %p\n", es, t.tv_sec,t.tv_usec, eh)); return eh; } /********************************************************************** * %FUNCTION: Event_DelHandler * %ARGUMENTS: * es -- event selector * eh -- event handler * %RETURNS: * 0 if OK, non-zero if there is an error * %DESCRIPTION: * Deletes the event handler eh ***********************************************************************/ int Event_DelHandler(EventSelector *es, EventHandler *eh) { /* Scan the handlers list */ EventHandler *cur, *prev; EVENT_DEBUG(("Event_DelHandler(es=%p, eh=%p)\n", es, eh)); for (cur=es->handlers, prev=NULL; cur; prev=cur, cur=cur->next) { if (cur == eh) { if (es->nestLevel) { eh->flags |= EVENT_FLAG_DELETED; es->opsPending = 1; return 0; } else { if (prev) prev->next = cur->next; else es->handlers = cur->next; DestroyHandler(cur); return 0; } } } /* Handler not found */ return 1; } /********************************************************************** * %FUNCTION: DestroySelector * %ARGUMENTS: * es -- an event selector * %RETURNS: * Nothing * %DESCRIPTION: * Destroys selector and all associated handles. ***********************************************************************/ void DestroySelector(EventSelector *es) { EventHandler *cur, *next; for (cur=es->handlers; cur; cur=next) { next = cur->next; DestroyHandler(cur); } free(es); } /********************************************************************** * %FUNCTION: DestroyHandler * %ARGUMENTS: * eh -- an event handler * %RETURNS: * Nothing * %DESCRIPTION: * Destroys handler ***********************************************************************/ void DestroyHandler(EventHandler *eh) { EVENT_DEBUG(("DestroyHandler(eh=%p)\n", eh)); free(eh); } /********************************************************************** * %FUNCTION: DoPendingChanges * %ARGUMENTS: * es -- an event selector * %RETURNS: * Nothing * %DESCRIPTION: * Makes all pending insertions and deletions happen. ***********************************************************************/ void DoPendingChanges(EventSelector *es) { EventHandler *cur, *prev, *next; es->opsPending = 0; /* If selector is to be deleted, do it and skip everything else */ if (es->destroyPending) { DestroySelector(es); return; } /* Do deletions */ cur = es->handlers; prev = NULL; while(cur) { if (!(cur->flags & EVENT_FLAG_DELETED)) { prev = cur; cur = cur->next; continue; } /* Unlink from list */ if (prev) { prev->next = cur->next; } else { es->handlers = cur->next; } next = cur->next; DestroyHandler(cur); cur = next; } } /********************************************************************** * %FUNCTION: Event_GetCallback * %ARGUMENTS: * eh -- the event handler * %RETURNS: * The callback function ***********************************************************************/ EventCallbackFunc Event_GetCallback(EventHandler *eh) { return eh->fn; } /********************************************************************** * %FUNCTION: Event_GetData * %ARGUMENTS: * eh -- the event handler * %RETURNS: * The "data" field. ***********************************************************************/ void * Event_GetData(EventHandler *eh) { return eh->data; } /********************************************************************** * %FUNCTION: Event_SetCallbackAndData * %ARGUMENTS: * eh -- the event handler * fn -- new callback function * data -- new data value * %RETURNS: * Nothing * %DESCRIPTION: * Sets the callback function and data fields. ***********************************************************************/ void Event_SetCallbackAndData(EventHandler *eh, EventCallbackFunc fn, void *data) { eh->fn = fn; eh->data = data; } #ifdef DEBUG_EVENT #include #include FILE *Event_DebugFP = NULL; /********************************************************************** * %FUNCTION: Event_DebugMsg * %ARGUMENTS: * fmt, ... -- format string * %RETURNS: * Nothing * %DESCRIPTION: * Writes a debug message to the debug file. ***********************************************************************/ void Event_DebugMsg(char const *fmt, ...) { va_list ap; struct timeval now; if (!Event_DebugFP) return; gettimeofday(&now, NULL); fprintf(Event_DebugFP, "%03d.%03d ", (int) now.tv_sec % 1000, (int) now.tv_usec / 1000); va_start(ap, fmt); vfprintf(Event_DebugFP, fmt, ap); va_end(ap); fflush(Event_DebugFP); } #endif /********************************************************************** * %FUNCTION: Event_EnableDebugging * %ARGUMENTS: * fname -- name of file to log debug messages to * %RETURNS: * 1 if debugging was enabled; 0 otherwise. ***********************************************************************/ int Event_EnableDebugging(char const *fname) { #ifndef DEBUG_EVENT return 0; #else Event_DebugFP = fopen(fname, "w"); return (Event_DebugFP != NULL); #endif } /********************************************************************** * %FUNCTION: Event_ChangeTimeout * %ARGUMENTS: * h -- event handler * t -- new timeout * %RETURNS: * Nothing * %DESCRIPTION: * Changes timeout of event handler to be "t" seconds in the future. ***********************************************************************/ void Event_ChangeTimeout(EventHandler *h, struct timeval t) { struct timeval now; /* Check time interval for validity */ if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) { return; } /* Convert time interval to absolute time */ gettimeofday(&now, NULL); t.tv_sec += now.tv_sec; t.tv_usec += now.tv_usec; if (t.tv_usec >= 1000000) { t.tv_usec -= 1000000; t.tv_sec++; } h->tmout = t; } rp-pppoe-4.0/src/libevent/event_tcp.c0000644000175000017500000003524214422272526015777 0ustar dfsdfs/*********************************************************************** * * event_tcp.c -- implementation of event-driven socket I/O. * * Copyright (C) 2001 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #include "event_tcp.h" #include #include #include #include #include #include static void free_state(EventTcpState *state); typedef struct EventTcpConnectState_t { int fd; EventHandler *conn; EventTcpConnectFunc f; void *data; } EventTcpConnectState; typedef struct EventTcpAcceptData_t { EventTcpAcceptFunc fn; void *data; } EventTcpAcceptData_t; /********************************************************************** * %FUNCTION: handle_accept * %ARGUMENTS: * es -- event selector * fd -- socket * flags -- ignored * data -- the accept callback function * %RETURNS: * Nothing * %DESCRIPTION: * Calls accept; if a connection arrives, calls the accept callback * function with connected descriptor ***********************************************************************/ static void handle_accept(EventSelector *es, int fd, unsigned int flags, void *data) { int conn; EventTcpAcceptData_t *d = (EventTcpAcceptData_t*) data; EVENT_DEBUG(("tcp_handle_accept(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); conn = accept(fd, NULL, NULL); if (conn < 0) return; d->fn(es, conn, d->data); } /********************************************************************** * %FUNCTION: handle_connect * %ARGUMENTS: * es -- event selector * fd -- socket * flags -- ignored * data -- the accept callback function * %RETURNS: * Nothing * %DESCRIPTION: * Calls accept; if a connection arrives, calls the accept callback * function with connected descriptor ***********************************************************************/ static void handle_connect(EventSelector *es, int fd, unsigned int flags, void *data) { int error = 0; socklen_t len = sizeof(error); EventTcpConnectState *state = (EventTcpConnectState *) data; EVENT_DEBUG(("tcp_handle_connect(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); /* Cancel writable event */ Event_DelHandler(es, state->conn); state->conn = NULL; /* Timeout? */ if (flags & EVENT_FLAG_TIMEOUT) { errno = ETIMEDOUT; state->f(es, fd, EVENT_TCP_FLAG_TIMEOUT, state->data); free(state); return; } /* Check for pending error */ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data); free(state); return; } if (error) { errno = error; state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data); free(state); return; } /* It looks cool! */ state->f(es, fd, EVENT_TCP_FLAG_COMPLETE, state->data); free(state); } /********************************************************************** * %FUNCTION: EventTcp_CreateAcceptor * %ARGUMENTS: * es -- event selector * socket -- listening socket * f -- function to call when a connection is accepted * data -- extra data to pass to f. * %RETURNS: * An event handler on success, NULL on failure. * %DESCRIPTION: * Sets up an accepting socket and calls "f" whenever a new * connection arrives. ***********************************************************************/ EventHandler * EventTcp_CreateAcceptor(EventSelector *es, int socket, EventTcpAcceptFunc f, void *data) { int flags; EVENT_DEBUG(("EventTcp_CreateAcceptor(es=%p, socket=%d)\n", es, socket)); /* Make sure socket is non-blocking */ flags = fcntl(socket, F_GETFL, 0); if (flags == -1) { return NULL; } if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { return NULL; } EventTcpAcceptData_t *d = malloc(sizeof(EventTcpAcceptData_t)); if (!d) return NULL; d->fn = f; d->data = data; EventHandler *eh = Event_AddHandler(es, socket, EVENT_FLAG_READABLE, handle_accept, (void *) d); if (!eh) { free(d); } return eh; } /********************************************************************** * %FUNCTION: free_state * %ARGUMENTS: * state -- EventTcpState to free * %RETURNS: * Nothing * %DESCRIPTION: * Frees all state associated with the TcpEvent. ***********************************************************************/ static void free_state(EventTcpState *state) { if (!state) return; EVENT_DEBUG(("tcp_free_state(state=%p)\n", state)); if (state->buf) free(state->buf); if (state->eh) Event_DelHandler(state->es, state->eh); free(state); } /********************************************************************** * %FUNCTION: handle_readable * %ARGUMENTS: * es -- event selector * fd -- the readable socket * flags -- ignored * data -- the EventTcpState object * %RETURNS: * Nothing * %DESCRIPTION: * Continues to fill buffer. Calls callback when done. ***********************************************************************/ static void handle_readable(EventSelector *es, int fd, unsigned int flags, void *data) { EventTcpState *state = (EventTcpState *) data; int done = state->cur - state->buf; int togo = state->len - done; int nread = 0; int flag; EVENT_DEBUG(("tcp_handle_readable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); /* Timed out? */ if (flags & EVENT_FLAG_TIMEOUT) { errno = ETIMEDOUT; (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT, state->data); free_state(state); return; } if (state->delim < 0) { /* Not looking for a delimiter */ /* togo had better not be zero here! */ nread = read(fd, state->cur, togo); if (nread <= 0) { /* Change connection reset to EOF if we have read at least one char */ if (nread < 0 && errno == ECONNRESET && done > 0) { nread = 0; } flag = (nread) ? EVENT_TCP_FLAG_IOERROR : EVENT_TCP_FLAG_EOF; /* error or EOF */ (state->f)(es, state->socket, state->buf, done, flag, state->data); free_state(state); return; } state->cur += nread; done += nread; if (done >= state->len) { /* Read enough! */ (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_COMPLETE, state->data); free_state(state); return; } } else { /* Looking for a delimiter */ while ( (togo > 0) && (nread = read(fd, state->cur, 1)) == 1) { togo--; done++; state->cur++; if (*(state->cur - 1) == state->delim) break; } if (nread <= 0) { /* Error or EOF -- check for EAGAIN */ if (nread < 0 && errno == EAGAIN) return; } /* Some other error, or EOF, or delimiter, or read enough */ if (nread < 0) { flag = EVENT_TCP_FLAG_IOERROR; } else if (nread == 0) { flag = EVENT_TCP_FLAG_EOF; } else { flag = EVENT_TCP_FLAG_COMPLETE; } (state->f)(es, state->socket, state->buf, done, flag, state->data); free_state(state); return; } } /********************************************************************** * %FUNCTION: handle_writeable * %ARGUMENTS: * es -- event selector * fd -- the writeable socket * flags -- ignored * data -- the EventTcpState object * %RETURNS: * Nothing * %DESCRIPTION: * Continues to fill buffer. Calls callback when done. ***********************************************************************/ static void handle_writeable(EventSelector *es, int fd, unsigned int flags, void *data) { EventTcpState *state = (EventTcpState *) data; int done = state->cur - state->buf; int togo = state->len - done; int n; /* Timed out? */ if (flags & EVENT_FLAG_TIMEOUT) { errno = ETIMEDOUT; (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT, state->data); free_state(state); return; } /* togo had better not be zero here! */ n = write(fd, state->cur, togo); EVENT_DEBUG(("tcp_handle_writeable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); if (n <= 0) { /* error */ if (state->f) { (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_IOERROR, state->data); } else { close(fd); } free_state(state); return; } state->cur += n; done += n; if (done >= state->len) { /* Written enough! */ if (state->f) { (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_COMPLETE, state->data); } else { close(fd); } free_state(state); return; } } /********************************************************************** * %FUNCTION: EventTcp_ReadBuf * %ARGUMENTS: * es -- event selector * socket -- socket to read from * len -- maximum number of bytes to read * delim -- delimiter at which to stop reading, or -1 if we should * read exactly len bytes * f -- function to call on EOF or when all bytes have been read * timeout -- if non-zero, timeout in seconds after which we cancel * operation. * data -- extra data to pass to function f. * %RETURNS: * A new EventTcpState token or NULL on error * %DESCRIPTION: * Sets up a handler to fill a buffer from a socket. ***********************************************************************/ EventTcpState * EventTcp_ReadBuf(EventSelector *es, int socket, int len, int delim, EventTcpIOFinishedFunc f, int timeout, void *data) { EventTcpState *state; int flags; struct timeval t; EVENT_DEBUG(("EventTcp_ReadBuf(es=%p, socket=%d, len=%d, delim=%d, timeout=%d)\n", es, socket, len, delim, timeout)); if (len <= 0) return NULL; if (socket < 0) return NULL; /* Make sure socket is non-blocking */ flags = fcntl(socket, F_GETFL, 0); if (flags == -1) { return NULL; } if (!(flags & O_NONBLOCK) && fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { return NULL; } state = malloc(sizeof(EventTcpState)); if (!state) return NULL; memset(state, 0, sizeof(EventTcpState)); state->socket = socket; state->buf = malloc(len); if (!state->buf) { free_state(state); return NULL; } state->cur = state->buf; state->len = len; state->f = f; state->es = es; if (timeout <= 0) { t.tv_sec = -1; t.tv_usec = -1; } else { t.tv_sec = timeout; t.tv_usec = 0; } state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_READABLE, t, handle_readable, (void *) state); if (!state->eh) { free_state(state); return NULL; } state->data = data; state->delim = delim; EVENT_DEBUG(("EventTcp_ReadBuf() -> %p\n", state)); return state; } /********************************************************************** * %FUNCTION: EventTcp_WriteBuf * %ARGUMENTS: * es -- event selector * socket -- socket to read from * buf -- buffer to write * len -- number of bytes to write * f -- function to call on EOF or when all bytes have been read * timeout -- timeout after which to cancel operation * data -- extra data to pass to function f. * %RETURNS: * A new EventTcpState token or NULL on error * %DESCRIPTION: * Sets up a handler to fill a buffer from a socket. ***********************************************************************/ EventTcpState * EventTcp_WriteBuf(EventSelector *es, int socket, char *buf, int len, EventTcpIOFinishedFunc f, int timeout, void *data) { EventTcpState *state; int flags; struct timeval t; EVENT_DEBUG(("EventTcp_WriteBuf(es=%p, socket=%d, len=%d, timeout=%d)\n", es, socket, len, timeout)); if (len <= 0) return NULL; if (socket < 0) return NULL; /* Make sure socket is non-blocking */ flags = fcntl(socket, F_GETFL, 0); if (flags == -1) { return NULL; } if (!(flags & O_NONBLOCK) && fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { return NULL; } state = malloc(sizeof(EventTcpState)); if (!state) return NULL; memset(state, 0, sizeof(EventTcpState)); state->socket = socket; state->buf = malloc(len); if (!state->buf) { free_state(state); return NULL; } memcpy(state->buf, buf, len); state->cur = state->buf; state->len = len; state->f = f; state->es = es; if (timeout <= 0) { t.tv_sec = -1; t.tv_usec = -1; } else { t.tv_sec = timeout; t.tv_usec = 0; } state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_WRITEABLE, t, handle_writeable, (void *) state); if (!state->eh) { free_state(state); return NULL; } state->data = data; state->delim = -1; EVENT_DEBUG(("EventTcp_WriteBuf() -> %p\n", state)); return state; } /********************************************************************** * %FUNCTION: EventTcp_Connect * %ARGUMENTS: * es -- event selector * fd -- descriptor to connect * addr -- address to connect to * addrlen -- length of address * f -- function to call with connected socket * data -- extra data to pass to f * %RETURNS: * Nothing * %DESCRIPTION: * Does a non-blocking connect on fd ***********************************************************************/ void EventTcp_Connect(EventSelector *es, int fd, struct sockaddr const *addr, socklen_t addrlen, EventTcpConnectFunc f, int timeout, void *data) { int flags; int n; EventTcpConnectState *state; struct timeval t; /* Make sure socket is non-blocking */ flags = fcntl(fd, F_GETFL, 0); if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { f(es, fd, EVENT_TCP_FLAG_IOERROR, data); return; } n = connect(fd, addr, addrlen); if (n < 0) { if (errno != EINPROGRESS) { f(es, fd, EVENT_TCP_FLAG_IOERROR, data); return; } } if (n == 0) { /* Connect succeeded immediately */ f(es, fd, EVENT_TCP_FLAG_COMPLETE, data); return; } state = malloc(sizeof(*state)); if (!state) { f(es, fd, EVENT_TCP_FLAG_IOERROR, data); return; } state->f = f; state->fd = fd; state->data = data; if (timeout <= 0) { t.tv_sec = -1; t.tv_usec = -1; } else { t.tv_sec = timeout; t.tv_usec = 0; } state->conn = Event_AddHandlerWithTimeout(es, fd, EVENT_FLAG_WRITEABLE, t, handle_connect, (void *) state); if (!state->conn) { free(state); f(es, fd, EVENT_TCP_FLAG_IOERROR, data); return; } } /********************************************************************** * %FUNCTION: EventTcp_CancelPending * %ARGUMENTS: * s -- an EventTcpState * %RETURNS: * Nothing * %DESCRIPTION: * Cancels the pending event handler ***********************************************************************/ void EventTcp_CancelPending(EventTcpState *s) { free_state(s); } rp-pppoe-4.0/src/libevent/event.h0000644000175000017500000000613114422272526015131 0ustar dfsdfs/*********************************************************************** * * event.h * * Abstraction of select call into "event-handling" to make programming * easier. * * Copyright (C) 2001 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * $Id$ * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #define DEBUG_EVENT #ifndef INCLUDE_EVENT_H #define INCLUDE_EVENT_H 1 /* Solaris moans if we don't do this... */ #ifdef __sun #define __EXTENSIONS__ 1 #endif struct EventSelector_t; /* Callback function */ typedef void (*EventCallbackFunc)(struct EventSelector_t *es, int fd, unsigned int flags, void *data); #include "eventpriv.h" /* Create an event selector */ extern EventSelector *Event_CreateSelector(void); /* Destroy the event selector */ extern void Event_DestroySelector(EventSelector *es); /* Handle one event */ extern int Event_HandleEvent(EventSelector *es); /* Add a handler for a ready file descriptor */ extern EventHandler *Event_AddHandler(EventSelector *es, int fd, unsigned int flags, EventCallbackFunc fn, void *data); /* Add a handler for a ready file descriptor with associated timeout*/ extern EventHandler *Event_AddHandlerWithTimeout(EventSelector *es, int fd, unsigned int flags, struct timeval t, EventCallbackFunc fn, void *data); /* Add a timer handler */ extern EventHandler *Event_AddTimerHandler(EventSelector *es, struct timeval t, EventCallbackFunc fn, void *data); /* Change the timeout of a timer handler */ void Event_ChangeTimeout(EventHandler *handler, struct timeval t); /* Delete a handler */ extern int Event_DelHandler(EventSelector *es, EventHandler *eh); /* Retrieve callback function from a handler */ extern EventCallbackFunc Event_GetCallback(EventHandler *eh); /* Retrieve data field from a handler */ extern void *Event_GetData(EventHandler *eh); /* Set callback and data to new values */ extern void Event_SetCallbackAndData(EventHandler *eh, EventCallbackFunc fn, void *data); /* Handle a signal synchronously in event loop */ int Event_HandleSignal(EventSelector *es, int sig, void (*handler)(int sig)); /* Reap children synchronously in event loop */ int Event_HandleChildExit(EventSelector *es, pid_t pid, void (*handler)(pid_t, int, void *), void *data); extern int Event_EnableDebugging(char const *fname); #ifdef DEBUG_EVENT extern void Event_DebugMsg(char const *fmt, ...); #define EVENT_DEBUG(x) Event_DebugMsg x #else #define EVENT_DEBUG(x) ((void) 0) #endif /* Flags */ #define EVENT_FLAG_READABLE 1 #define EVENT_FLAG_WRITEABLE 2 #define EVENT_FLAG_WRITABLE EVENT_FLAG_WRITEABLE /* This is strictly a timer event */ #define EVENT_FLAG_TIMER 4 /* This is a read or write event with an associated timeout */ #define EVENT_FLAG_TIMEOUT 8 #define EVENT_TIMER_BITS (EVENT_FLAG_TIMER | EVENT_FLAG_TIMEOUT) #endif rp-pppoe-4.0/src/libevent/hash.h0000644000175000017500000000266514422272526014743 0ustar dfsdfs/*********************************************************************** * * hash.h * * Hash table utilities * * Copyright (C) 2002 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #ifndef HASH_H #define HASH_H #include /* Fixed-size hash tables for now */ #define HASHTAB_SIZE 67 /* A hash bucket */ typedef struct hash_bucket_t { struct hash_bucket_t *next; struct hash_bucket_t *prev; unsigned int hashval; } hash_bucket; /* A hash table */ typedef struct hash_table_t { hash_bucket *buckets[HASHTAB_SIZE]; size_t hash_offset; unsigned int (*compute_hash)(void *data); int (*compare)(void *item1, void *item2); size_t num_entries; } hash_table; /* Functions */ void hash_init(hash_table *tab, size_t hash_offset, unsigned int (*compute)(void *data), int (*compare)(void *item1, void *item2)); void hash_insert(hash_table *tab, void *item); void hash_remove(hash_table *tab, void *item); void *hash_find(hash_table *tab, void *item); void *hash_find_next(hash_table *tab, void *item); size_t hash_num_entries(hash_table *tab); /* Iteration functions */ void *hash_start(hash_table *tab, void **cursor); void *hash_next(hash_table *tab, void **cursor); /* Utility function: hashpjw for strings */ unsigned int hash_pjw(char const *str); #endif rp-pppoe-4.0/src/libevent/event_sig.c0000644000175000017500000001743314422272526015775 0ustar dfsdfs/*********************************************************************** * * event_sig.c * * Code for handling signals nicely (synchronously) and for dealing * with reaping child processes. * * Copyright (C) 2002 by Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This software may be distributed under the terms of the GNU General * Public License, Version 2, or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #define _POSIX_SOURCE 1 /* For sigaction defines */ #include #include #include #include #include #include #include "event.h" #include "hash.h" /* Kludge for figuring out NSIG */ #ifdef NSIG #define MAX_SIGNALS NSIG #elif defined(_NSIG) #define MAX_SIGNALS _NSIG #else #define MAX_SIGNALS 256 /* Should be safe... */ #endif /* A structure for a "synchronous" signal handler */ struct SynchronousSignalHandler { int fired; /* Have we received this signal? */ void (*handler)(int sig); /* Handler function */ }; /* A structure for calling back when a child dies */ struct ChildEntry { hash_bucket hash; void (*handler)(pid_t pid, int status, void *data); pid_t pid; void *data; }; static struct SynchronousSignalHandler SignalHandlers[MAX_SIGNALS]; static int Pipe[2] = {-1, -1}; static pid_t MyPid = (pid_t) -1; static EventHandler *PipeHandler = NULL; static hash_table child_process_table; static unsigned int child_hash(void *data) { return (unsigned int) ((struct ChildEntry *) data)->pid; } static int child_compare(void *d1, void *d2) { return ((struct ChildEntry *)d1)->pid != ((struct ChildEntry *)d2)->pid; } /********************************************************************** * %FUNCTION: DoPipe * %ARGUMENTS: * es -- event selector * fd -- readable file descriptor * flags -- flags from event system * data -- ignored * %RETURNS: * Nothing * %DESCRIPTION: * Called when an async signal handler wants attention. This function * fires all "synchronous" signal handlers. ***********************************************************************/ static void DoPipe(EventSelector *es, int fd, unsigned int flags, void *data) { char buf[64]; int i; sigset_t set; /* Clear pipe */ while (read(fd, buf, 64) == 64) { ; } /* Fire handlers */ for (i=0; i MAX_SIGNALS) { /* Ooops... */ return; } if (getpid() != MyPid) { /* Spuriously-caught signal caught in child! */ return; } /* If there's no handler, ignore it */ if (!SignalHandlers[sig].handler) { return; } SignalHandlers[sig].fired = 1; int errno_save = errno; #pragma GCC diagnostic ignored "-Wunused-result" write(Pipe[1], &sig, 1); #pragma GCC diagnostic warning "-Wunused-result" errno = errno_save; } /********************************************************************** * %FUNCTION: child_handler * %ARGUMENTS: * sig -- signal number (whoop-dee-doo) * %RETURNS: * Nothing * %DESCRIPTION: * Called *SYNCHRONOUSLY* to reap dead children. SIGCHLD is blocked * during the execution of this function. ***********************************************************************/ static void child_handler(int sig) { int status; int pid; struct ChildEntry *ce; struct ChildEntry candidate; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { candidate.pid = (pid_t) pid; ce = hash_find(&child_process_table, &candidate); if (ce) { hash_remove(&child_process_table, ce); if (ce->handler) { ce->handler(pid, status, ce->data); } free(ce); } } } /********************************************************************** * %FUNCTION: SetupPipes (static) * %ARGUMENTS: * es -- event selector * %RETURNS: * 0 on success; -1 on failure * %DESCRIPTION: * Sets up pipes with an event handler to handle IPC from a signal handler ***********************************************************************/ static int SetupPipes(EventSelector *es) { int flags; int i; /* If already done, do nothing */ if (PipeHandler) return 0; MyPid = getpid(); /* Initialize the child-process hash table */ hash_init(&child_process_table, offsetof(struct ChildEntry, hash), child_hash, child_compare); /* Open pipe to self */ if (pipe(Pipe) < 0) { return -1; } /* Make pipes non-blocking */ for (i=0; i<=1; i++) { flags = fcntl(Pipe[i], F_GETFL, 0); if (flags != -1) { flags = fcntl(Pipe[i], F_SETFL, flags | O_NONBLOCK); } if (flags == -1) { close(Pipe[0]); close(Pipe[1]); return -1; } } PipeHandler = Event_AddHandler(es, Pipe[0], EVENT_FLAG_READABLE, DoPipe, NULL); if (!PipeHandler) { int old_errno = errno; close(Pipe[0]); close(Pipe[1]); errno = old_errno; return -1; } return 0; } /********************************************************************** * %FUNCTION: Event_HandleSignal * %ARGUMENTS: * es -- event selector * sig -- signal number * handler -- handler to call when signal is raised. Handler is called * "synchronously" as events are processed by event loop. * %RETURNS: * 0 on success, -1 on error. * %DESCRIPTION: * Sets up a "synchronous" signal handler. ***********************************************************************/ int Event_HandleSignal(EventSelector *es, int sig, void (*handler)(int sig)) { struct sigaction act; if (SetupPipes(es) < 0) return -1; act.sa_handler = sig_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; #ifdef SA_RESTART act.sa_flags |= SA_RESTART; #endif if (sig == SIGCHLD) { act.sa_flags |= SA_NOCLDSTOP; } if (sigaction(sig, &act, NULL) < 0) return -1; SignalHandlers[sig].handler = handler; return 0; } /********************************************************************** * %FUNCTION: Event_HandleChildExit * %ARGUMENTS: * es -- event selector * pid -- process-ID of child to wait for * handler -- function to call when child exits * data -- data to pass to handler when child exits * %RETURNS: * 0 on success, -1 on failure. * %DESCRIPTION: * Sets things up so that when a child exits, handler() will be called * with the pid of the child and "data" as arguments. The call will * be synchronous (part of the normal event loop on es). ***********************************************************************/ int Event_HandleChildExit(EventSelector *es, pid_t pid, void (*handler)(pid_t, int, void *), void *data) { struct ChildEntry *ce; sigset_t set; if (Event_HandleSignal(es, SIGCHLD, child_handler) < 0) return -1; ce = malloc(sizeof(struct ChildEntry)); if (!ce) return -1; ce->pid = pid; ce->data = data; ce->handler = handler; /* Critical section: Don't let SIGCHLD mess hash_insert */ sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_BLOCK, &set, NULL); hash_insert(&child_process_table, ce); sigprocmask(SIG_UNBLOCK, &set, NULL); return 0; } rp-pppoe-4.0/src/libevent/Makefile.in0000644000175000017500000000163014422272526015703 0ustar dfsdfs# Generated automatically from Makefile.in by configure. # $Id$ # # Makefile for event-handling library # # Copyright 2002 Roaring Penguin Software Inc. # Copyright (C) 2018-2023 Dianne Skoll # # This software may be distributed according to the terms of the GNU # General Public License, version 2 or (at your option) any later version. # SPDX-License-Identifier: GPL-2.0-or-later OBJS=event.o event_tcp.o hash.o event_sig.o SRCS=$(OBJS:.o=.c) HDRS=event.h event_tcp.h eventpriv.h hash.h CFLAGS=@CFLAGS@ -I.. $(DEFINES) AR=ar all: libevent.a libevent.a: $(OBJS) rm -f libevent.a $(AR) -cq libevent.a $(OBJS) @RANLIB@ libevent.a event.o: event.c $(HDRS) @CC@ $(CFLAGS) -c -o $@ $< hash.o: hash.c $(HDRS) @CC@ $(CFLAGS) -c -o $@ $< event_sig.o: event_sig.c $(HDRS) @CC@ $(CFLAGS) -c -o $@ $< event_tcp.o: event_tcp.c $(HDRS) @CC@ $(CFLAGS) -c -o $@ $< clean: FORCE rm -f *.a *.o *~ FORCE: .phony: FORCE rp-pppoe-4.0/src/libevent/event_tcp.h0000644000175000017500000000413614422272526016002 0ustar dfsdfs/*********************************************************************** * * event-tcp.h * * Event-driven TCP functions to allow for single-threaded "concurrent" * server. * * Copyright (C) 2001 Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #ifndef INCLUDE_EVENT_TCP_H #define INCLUDE_EVENT_TCP_H 1 #include "event.h" #include typedef void (*EventTcpAcceptFunc)(EventSelector *es, int fd, void *data); typedef void (*EventTcpConnectFunc)(EventSelector *es, int fd, int flag, void *data); typedef void (*EventTcpIOFinishedFunc)(EventSelector *es, int fd, char *buf, int len, int flag, void *data); #define EVENT_TCP_FLAG_COMPLETE 0 #define EVENT_TCP_FLAG_IOERROR 1 #define EVENT_TCP_FLAG_EOF 2 #define EVENT_TCP_FLAG_TIMEOUT 3 typedef struct EventTcpState_t { int socket; char *buf; char *cur; int len; int delim; EventTcpIOFinishedFunc f; EventSelector *es; EventHandler *eh; void *data; } EventTcpState; extern EventHandler *EventTcp_CreateAcceptor(EventSelector *es, int socket, EventTcpAcceptFunc f, void *data); extern void EventTcp_Connect(EventSelector *es, int fd, struct sockaddr const *addr, socklen_t addrlen, EventTcpConnectFunc f, int timeout, void *data); extern EventTcpState *EventTcp_ReadBuf(EventSelector *es, int socket, int len, int delim, EventTcpIOFinishedFunc f, int timeout, void *data); extern EventTcpState *EventTcp_WriteBuf(EventSelector *es, int socket, char *buf, int len, EventTcpIOFinishedFunc f, int timeout, void *data); extern void EventTcp_CancelPending(EventTcpState *s); #endif rp-pppoe-4.0/src/Makefile.in0000644000175000017500000001742714422272526014106 0ustar dfsdfs# @configure_input@ #*********************************************************************** # # Makefile # # Makefile for Roaring Penguin's Linux user-space PPPoE client. # # Copyright (C) 2000 Roaring Penguin Software Inc. # Copyright (C) 2018-2023 Dianne Skoll # # This program may be distributed according to the terms of the GNU # General Public License, version 2 or (at your option) any later version. # # SPDX-License-Identifier: GPL-2.0-or-later # # $Id$ #*********************************************************************** # Version is set ONLY IN THE MAKEFILE! Don't delete this! RP_VERSION=4.0 DEFINES= prefix=@prefix@ exec_prefix=@exec_prefix@ mandir=@mandir@ docdir=@prefix@/share/doc/rp-pppoe-$(RP_VERSION) install=@INSTALL@ install_dir=@INSTALL@ -d sbindir=@sbindir@ datarootdir=@datarootdir@ # Plugin for pppd on Linux LINUX_KERNELMODE_PLUGIN=@LINUX_KERNELMODE_PLUGIN@ PPPD_INCDIR=@PPPD_INCDIR@ # PPPoE relay -- currently only supported on Linux PPPOE_RELAY=@PPPOE_RELAY@ # Program paths PPPOE_PATH=$(sbindir)/pppoe PPPD_PATH=@PPPD@ # Kernel-mode plugin gets installed here. PLUGIN_DIR=/etc/ppp/plugins PLUGIN_PATH=$(PLUGIN_DIR)/rp-pppoe.so # Configuration file paths PPPOESERVER_PPPD_OPTIONS=/etc/ppp/pppoe-server-options PATHS='-DPPPOE_PATH="$(PPPOE_PATH)"' '-DPPPD_PATH="$(PPPD_PATH)"' \ '-DPLUGIN_PATH="$(PLUGIN_PATH)"' \ '-DPPPOE_SERVER_OPTIONS="$(PPPOESERVER_PPPD_OPTIONS)"' CFLAGS= @CFLAGS@ $(DEFINES) $(PATHS) -Ilibevent TARGETS=@TARGETS@ LDFLAGS=@LDFLAGS@ STATIC=@STATIC@ all: $(TARGETS) @echo "" @echo "Type 'make install' as root to install the software." pppoe-sniff: pppoe-sniff.o if.o common.o debug.o @CC@ -o $@ $^ $(LDFLAGS) $(STATIC) pppoe-server: pppoe-server.o if.o debug.o common.o md5.o control_socket.o libevent/libevent.a @PPPOE_SERVER_DEPS@ @CC@ -o $@ @RDYNAMIC@ $^ $(LDFLAGS) -Llibevent -levent $(STATIC) pppoe: pppoe.o if.o debug.o common.o ppp.o discovery.o @CC@ -o $@ $^ $(LDFLAGS) $(STATIC) pppoe-relay: relay.o if.o debug.o common.o @CC@ -o $@ $^ $(LDFLAGS) $(STATIC) pppoe.o: pppoe.c pppoe.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< discovery.o: discovery.c pppoe.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< ppp.o: ppp.c pppoe.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< control_socket.o: control_socket.c control_socket.h libevent/event_tcp.h pppoe.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< md5.o: md5.c md5.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< pppoe-server.o: pppoe-server.c pppoe.h @PPPOE_SERVER_DEPS@ @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< pppoe-sniff.o: pppoe-sniff.c pppoe.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< if.o: if.c pppoe.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< libevent/libevent.a: cd libevent && $(MAKE) DEFINES="$(DEFINES)" common.o: common.c pppoe.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< debug.o: debug.c pppoe.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< relay.o: relay.c relay.h pppoe.h @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< # Experimental code from Savoir Faire Linux. I do not consider it # production-ready, so not part of the official distribution. #modem.o: modem.c modem.h # @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< #rs232.o: rs232.c rs232.h # @CC@ $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -c -o $@ $< # Linux-specific plugin rp-pppoe.so: plugin/plugin.o plugin/libplugin.a @CC@ -o $@ -shared $^ $(LDFLAGS) plugin/plugin.o: plugin.c @CC@ -DPLUGIN=1 '-DRP_VERSION="$(RP_VERSION)"' $(CFLAGS) -I$(PPPD_INCDIR) -c -o $@ -fPIC $< plugin/libplugin.a: plugin/discovery.o plugin/if.o plugin/common.o plugin/debug.o @AR@ -rc $@ $^ plugin/discovery.o: discovery.c @CC@ -DPLUGIN=1 $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -I$(PPPD_INCDIR) -c -o $@ -fPIC $< plugin/if.o: if.c @CC@ -DPLUGIN=1 $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -I$(PPPD_INCDIR) -c -o $@ -fPIC $< plugin/debug.o: debug.c @CC@ -DPLUGIN=1 $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -I$(PPPD_INCDIR) -c -o $@ -fPIC $< plugin/common.o: common.c @CC@ -DPLUGIN=1 $(CFLAGS) '-DRP_VERSION="$(RP_VERSION)"' -I$(PPPD_INCDIR) -c -o $@ -fPIC $< install: all -mkdir -p $(DESTDIR)$(sbindir) $(install) -m 755 pppoe $(DESTDIR)$(sbindir) $(install) -m 755 pppoe-server $(DESTDIR)$(sbindir) if test -x pppoe-relay ; then $(install) -m 755 pppoe-relay $(DESTDIR)$(sbindir); fi if test -x pppoe-sniff; then $(install) -m 755 pppoe-sniff $(DESTDIR)$(sbindir); fi -mkdir -p $(DESTDIR)$(docdir) $(install) -m 644 ../doc/CHANGES $(DESTDIR)$(docdir) $(install) -m 644 ../doc/KERNEL-MODE-PPPOE $(DESTDIR)$(docdir) $(install) -m 644 ../doc/HOW-TO-CONNECT $(DESTDIR)$(docdir) $(install) -m 644 ../doc/LICENSE $(DESTDIR)$(docdir) $(install) -m 644 ../README.md $(DESTDIR)$(docdir) -mkdir -p $(DESTDIR)$(mandir)/man8 for i in $(TARGETS) ; do \ if test -f ../man/$$i.8 ; then \ $(install) -m 644 ../man/$$i.8 $(DESTDIR)$(mandir)/man8 || exit 1; \ fi; \ done -mkdir -p $(DESTDIR)/etc/ppp -mkdir -p $(DESTDIR)$(PLUGIN_DIR) -echo "# Directory created by rp-pppoe for kernel-mode plugin" > $(DESTDIR)$(PLUGIN_DIR)/README if test -r rp-pppoe.so; then $(install) -m 755 rp-pppoe.so $(DESTDIR)$(PLUGIN_DIR); fi @if [ ! -f $(DESTDIR)$(PPPOESERVER_PPPD_OPTIONS) ] ; then \ $(install) -m 644 ../configs/pppoe-server-options $(DESTDIR)$(PPPOESERVER_PPPD_OPTIONS) ; \ else \ echo "NOT overwriting existing $(DESTDIR)$(PPPOESERVER_PPPD_OPTIONS)"; \ $(install) -m 644 ../configs/pppoe-server-options $(DESTDIR)$(PPPOESERVER_PPPD_OPTIONS)-example ; \ fi tgz: distro cd .. && tar cvf rp-pppoe-$(RP_VERSION)$(BETA).tar rp-pppoe-$(RP_VERSION)$(BETA) gzip -f -v -9 ../rp-pppoe-$(RP_VERSION)$(BETA).tar rm -rf ../rp-pppoe-$(RP_VERSION)$(BETA) distro: rm -rf ../rp-pppoe-$(RP_VERSION)$(BETA) mkdir ../rp-pppoe-$(RP_VERSION)$(BETA) mkdir ../rp-pppoe-$(RP_VERSION)$(BETA)/configs cp ../README.md ../rp-pppoe-$(RP_VERSION)$(BETA) for i in pppoe-server-options ; do \ cp ../configs/$$i ../rp-pppoe-$(RP_VERSION)$(BETA)/configs || exit 1; \ done mkdir ../rp-pppoe-$(RP_VERSION)$(BETA)/doc for i in CHANGES KERNEL-MODE-PPPOE HOW-TO-CONNECT LICENSE ; do \ cp ../doc/$$i ../rp-pppoe-$(RP_VERSION)$(BETA)/doc || exit 1; \ done mkdir ../rp-pppoe-$(RP_VERSION)$(BETA)/man for i in pppoe-server.8 pppoe-sniff.8 pppoe.8 pppoe-relay.8 ; do \ cp ../man/$$i ../rp-pppoe-$(RP_VERSION)$(BETA)/man || exit 1; \ done mkdir ../rp-pppoe-$(RP_VERSION)$(BETA)/scripts mkdir ../rp-pppoe-$(RP_VERSION)$(BETA)/src for i in Makefile.in install-sh common.c config.h.in configure configure.ac debug.c discovery.c if.c md5.c md5.h ppp.c pppoe-server.c pppoe-sniff.c pppoe.c pppoe.h pppoe-server.h plugin.c relay.c relay.h control_socket.c control_socket.h ; do \ cp ../src/$$i ../rp-pppoe-$(RP_VERSION)$(BETA)/src || exit 1; \ done mkdir ../rp-pppoe-$(RP_VERSION)$(BETA)/src/libevent for i in Makefile.in event.c event.h event_tcp.c event_tcp.h eventpriv.h hash.c hash.h event_sig.c ; do \ cp ../src/libevent/$$i ../rp-pppoe-$(RP_VERSION)$(BETA)/src/libevent || exit 1; \ done mkdir ../rp-pppoe-$(RP_VERSION)$(BETA)/src/plugin distro-beta: beta-check $(MAKE) distro BETA=-BETA-$(BETA) tgz-beta: beta-check $(MAKE) tgz BETA=-BETA-$(BETA) beta-check: @if test "$(BETA)" = "" ; then \ echo "Usage: make distro-beta BETA="; \ exit 1; \ fi clean: rm -f *.o pppoe-relay pppoe pppoe-sniff pppoe-server core rp-pppoe.so plugin/*.o plugin/libplugin.a *~ test -f libevent/Makefile && $(MAKE) -C libevent clean || true distclean: clean rm -f Makefile config.h config.cache config.log config.status rm -f libevent/Makefile rm -f libevent/Doc/libevent.aux libevent/Doc/libevent.log libevent/Doc/libevent.out libevent/Doc/libevent.pdf tests/testevent tests/testevent.o rm -rf autom4te.cache .PHONY: clean .PHONY: distclean .PHONY: distro rp-pppoe-4.0/src/ppp.c0000644000175000017500000002212314422272526012771 0ustar dfsdfs/*********************************************************************** * * ppp.c * * Implementation of user-space PPPoE redirector for Linux. * * Functions for talking to PPP daemon * * Copyright (C) 2000-2012 by Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * This program may be distributed according to the terms of the GNU * General Public License, version 2 or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #include "config.h" #include #include #include #include #ifdef HAVE_SYS_UIO_H #include #endif #include #ifdef HAVE_N_HDLC #ifndef N_HDLC #include #endif #endif #include "pppoe.h" static int PPPState; static int PPPPacketSize; static unsigned char PPPXorValue; static uint16_t const 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 }; /********************************************************************** *%FUNCTION: syncReadFromPPP *%ARGUMENTS: * conn -- PPPoEConnection structure * packet -- buffer in which to place PPPoE packet *%RETURNS: * Nothing *%DESCRIPTION: * Reads from a synchronous PPP device and builds and transmits a PPPoE * packet ***********************************************************************/ void syncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet) { int r; #ifndef HAVE_N_HDLC struct iovec vec[2]; unsigned char dummy[2]; vec[0].iov_base = (void *) dummy; vec[0].iov_len = 2; vec[1].iov_base = (void *) packet->payload; vec[1].iov_len = ETH_JUMBO_LEN - PPPOE_OVERHEAD; /* Use scatter-read to throw away the PPP frame address bytes */ r = readv(0, vec, 2); #else /* Bloody hell... readv doesn't work with N_HDLC line discipline... GRR! */ unsigned char buf[ETH_JUMBO_LEN - PPPOE_OVERHEAD + 2]; r = read(0, buf, ETH_JUMBO_LEN - PPPOE_OVERHEAD + 2); if (r >= 2) { memcpy(packet->payload, buf+2, r-2); } #endif if (r < 0) { /* Catch the Linux "select" bug */ if (errno == EAGAIN) { rp_fatal("Linux select bug hit! This message is harmless, but please ask the Linux kernel developers to fix it."); } fatalSys("read (syncReadFromPPP)"); } if (r == 0) { syslog(LOG_INFO, "end-of-file in syncReadFromPPP"); sendPADT(conn, "RP-PPPoE: EOF in syncReadFromPPP"); exit(EXIT_SUCCESS); } if (r < 2) { rp_fatal("too few characters read from PPP (syncReadFromPPP)"); } sendSessionPacket(conn, packet, r-2); } /********************************************************************** *%FUNCTION: initPPP *%ARGUMENTS: * None *%RETURNS: * Nothing *%DESCRIPTION: * Initializes the PPP state machine ***********************************************************************/ void initPPP(void) { PPPState = STATE_WAITFOR_FRAME_ADDR; PPPPacketSize = 0; PPPXorValue = 0; } /********************************************************************** *%FUNCTION: asyncReadFromPPP *%ARGUMENTS: * conn -- PPPoEConnection structure * packet -- buffer in which to place PPPoE packet *%RETURNS: * Nothing *%DESCRIPTION: * Reads from an async PPP device and builds a PPPoE packet to transmit ***********************************************************************/ void asyncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet) { unsigned char buf[READ_CHUNK]; unsigned char *ptr = buf; unsigned char c; int r; r = read(0, buf, READ_CHUNK); if (r < 0) { fatalSys("read (asyncReadFromPPP)"); } if (r == 0) { syslog(LOG_INFO, "end-of-file in asyncReadFromPPP"); sendPADT(conn, "RP-PPPoE: EOF in asyncReadFromPPP"); exit(EXIT_SUCCESS); } while(r) { if (PPPState == STATE_WAITFOR_FRAME_ADDR) { while(r) { --r; if (*ptr++ == FRAME_ADDR) { PPPState = STATE_DROP_PROTO; break; } } } /* Still waiting... */ if (PPPState == STATE_WAITFOR_FRAME_ADDR) return; while(r && PPPState == STATE_DROP_PROTO) { --r; if (*ptr++ == (FRAME_CTRL ^ FRAME_ENC)) { PPPState = STATE_BUILDING_PACKET; } } if (PPPState == STATE_DROP_PROTO) return; /* Start building frame */ while(r && PPPState == STATE_BUILDING_PACKET) { --r; c = *ptr++; switch(c) { case FRAME_ESC: PPPXorValue = FRAME_ENC; break; case FRAME_FLAG: if (PPPPacketSize < 2) { rp_fatal("Packet too short from PPP (asyncReadFromPPP)"); } sendSessionPacket(conn, packet, PPPPacketSize-2); PPPPacketSize = 0; PPPXorValue = 0; PPPState = STATE_WAITFOR_FRAME_ADDR; break; default: if (PPPPacketSize >= ETH_JUMBO_LEN - 4) { syslog(LOG_ERR, "Packet too big! Check MTU on PPP interface"); PPPPacketSize = 0; PPPXorValue = 0; PPPState = STATE_WAITFOR_FRAME_ADDR; } else { packet->payload[PPPPacketSize++] = c ^ PPPXorValue; PPPXorValue = 0; } } } } } /********************************************************************** *%FUNCTION: decodeFromPPP *%ARGUMENTS: * conn -- PPPoEConnection structure * packet -- buffer in which to place PPPoE packet *%RETURNS: * Nothing *%DESCRIPTION: * Reads from an async PPP device and builds a PPPoE packet to transmit ***********************************************************************/ void decodeFromPPP(PPPoEConnection *conn, PPPoEPacket *packet, unsigned char *buf, int r) { unsigned char *ptr = buf; unsigned char c; while(r) { if (PPPState == STATE_WAITFOR_FRAME_ADDR) { while(r) { --r; if (*ptr++ == FRAME_ADDR) { PPPState = STATE_DROP_PROTO; break; } } } /* Still waiting... */ if (PPPState == STATE_WAITFOR_FRAME_ADDR) return; while(r && PPPState == STATE_DROP_PROTO) { --r; if (*ptr++ == (FRAME_CTRL ^ FRAME_ENC)) { PPPState = STATE_BUILDING_PACKET; } } if (PPPState == STATE_DROP_PROTO) return; /* Start building frame */ while(r && PPPState == STATE_BUILDING_PACKET) { --r; c = *ptr++; switch(c) { case FRAME_ESC: PPPXorValue = FRAME_ENC; break; case FRAME_FLAG: if (PPPPacketSize < 2) { rp_fatal("Packet too short from PPP (asyncReadFromPPP)"); } sendSessionPacket(conn, packet, PPPPacketSize-2); PPPPacketSize = 0; PPPXorValue = 0; PPPState = STATE_WAITFOR_FRAME_ADDR; break; default: if (PPPPacketSize >= ETH_JUMBO_LEN - 4) { syslog(LOG_ERR, "Packet too big! Check MTU on PPP interface"); PPPPacketSize = 0; PPPXorValue = 0; PPPState = STATE_WAITFOR_FRAME_ADDR; } else { packet->payload[PPPPacketSize++] = c ^ PPPXorValue; PPPXorValue = 0; } } } } } /********************************************************************** *%FUNCTION: pppFCS16 *%ARGUMENTS: * fcs -- current fcs * cp -- a buffer's worth of data * len -- length of buffer "cp" *%RETURNS: * A new FCS *%DESCRIPTION: * Updates the PPP FCS. ***********************************************************************/ uint16_t pppFCS16(uint16_t fcs, unsigned char * cp, int len) { while (len--) fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff]; return (fcs); } rp-pppoe-4.0/src/discovery.c0000644000175000017500000005320114422272526014202 0ustar dfsdfs/*********************************************************************** * * discovery.c * * Perform PPPoE discovery * * Copyright (C) 1999-2015 by Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #define _GNU_SOURCE 1 #include "config.h" #include #include #include #include #include #include #ifdef HAVE_SYS_UIO_H #include #endif #include #include #include #include #include "pppoe.h" #ifdef PLUGIN #define HAVE_STDARG_H 1 #define HAVE_STDBOOL_H 1 #define HAVE_STDDEF_H 1 #include "pppd/pppd.h" #include "pppd/fsm.h" #include "pppd/lcp.h" #ifdef PPPD_VERSION /* New-style pppd API */ int persist = 1; #endif #else int persist = 0; #endif /* Structure used by parseForHostUniq */ struct HostUniqInfo { char *hostUniq; int forMe; }; /* Calculate time remaining until *expire_at into *tv, returns 0 if now >= *expire_at */ static int time_left(struct timeval *tv, struct timeval *expire_at) { struct timeval now; if (gettimeofday(&now, NULL) < 0) { fatalSys("gettimeofday (time_left)"); } tv->tv_sec = expire_at->tv_sec - now.tv_sec; tv->tv_usec = expire_at->tv_usec - now.tv_usec; if (tv->tv_usec < 0) { tv->tv_usec += 1000000; if (tv->tv_sec) { tv->tv_sec--; } else { /* Timed out */ return 0; } } if (tv->tv_sec <= 0 && tv->tv_usec <= 0) { /* Timed out */ return 0; } return 1; } /********************************************************************** *%FUNCTION: parseForHostUniq *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data. * extra -- user-supplied pointer. This is assumed to be a pointer to a * HostUniqInfo structure *%RETURNS: * Nothing *%DESCRIPTION: * If a HostUnique tag is found which matches our PID, sets *extra to 1. ***********************************************************************/ static void parseForHostUniq(uint16_t type, uint16_t len, unsigned char *data, void *extra) { struct HostUniqInfo *hi = (struct HostUniqInfo *) extra; if (!hi->hostUniq) return; if (type == TAG_HOST_UNIQ && len == strlen(hi->hostUniq) && !memcmp(data, hi->hostUniq, len)) { hi->forMe = 1; } } /********************************************************************** *%FUNCTION: packetIsForMe *%ARGUMENTS: * conn -- PPPoE connection info * packet -- a received PPPoE packet *%RETURNS: * 1 if packet is for this PPPoE daemon; 0 otherwise. *%DESCRIPTION: * If we are using the Host-Unique tag, verifies that packet contains * our unique identifier. ***********************************************************************/ static int packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) { struct HostUniqInfo hi; /* If packet is not directed to our MAC address, forget it */ if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; /* If we're not using the Host-Unique tag, then accept the packet */ if (!conn->hostUniq) return 1; hi.hostUniq = conn->hostUniq; hi.forMe = 0; parsePacket(packet, parseForHostUniq, &hi); return hi.forMe; } /********************************************************************** *%FUNCTION: parsePADOTags *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data. Should point to a PacketCriteria structure * which gets filled in according to selected AC name and service * name. *%RETURNS: * Nothing *%DESCRIPTION: * Picks interesting tags out of a PADO packet ***********************************************************************/ static void parsePADOTags(uint16_t type, uint16_t len, unsigned char *data, void *extra) { struct PacketCriteria *pc = (struct PacketCriteria *) extra; PPPoEConnection *conn = pc->conn; int i; #ifdef PLUGIN uint16_t mru; #endif switch(type) { case TAG_AC_NAME: pc->seenACName = 1; if (conn->printACNames) { printf("Access-Concentrator: %.*s\n", (int) len, data); } if (conn->acName && len == strlen(conn->acName) && !strncmp((char *) data, conn->acName, len)) { pc->acNameOK = 1; } break; case TAG_SERVICE_NAME: pc->seenServiceName = 1; if (conn->printACNames && len > 0) { printf(" Service-Name: %.*s\n", (int) len, data); } if (conn->serviceName && len == strlen(conn->serviceName) && !strncmp((char *) data, conn->serviceName, len)) { pc->serviceNameOK = 1; } break; case TAG_AC_COOKIE: if (conn->printACNames) { printf("Got a cookie:"); /* Print first 20 bytes of cookie */ for (i=0; icookie.type = htons(type); conn->cookie.length = htons(len); memcpy(conn->cookie.payload, data, len); break; case TAG_RELAY_SESSION_ID: if (conn->printACNames) { printf("Got a Relay-ID:"); /* Print first 20 bytes of relay ID */ for (i=0; irelayId.type = htons(type); conn->relayId.length = htons(len); memcpy(conn->relayId.payload, data, len); break; case TAG_SERVICE_NAME_ERROR: if (conn->printACNames) { printf("Got a Service-Name-Error tag: %.*s\n", (int) len, data); } else { pktLogErrs("PADO", type, len, data, extra); pc->gotError = 1; if (!persist) { exit(EXIT_FAILURE); } } break; case TAG_AC_SYSTEM_ERROR: if (conn->printACNames) { printf("Got a System-Error tag: %.*s\n", (int) len, data); } else { pktLogErrs("PADO", type, len, data, extra); pc->gotError = 1; if (!persist) { exit(EXIT_FAILURE); } } break; case TAG_GENERIC_ERROR: if (conn->printACNames) { printf("Got a Generic-Error tag: %.*s\n", (int) len, data); } else { pktLogErrs("PADO", type, len, data, extra); pc->gotError = 1; if (!persist) { exit(EXIT_FAILURE); } } break; #ifdef PLUGIN case TAG_PPP_MAX_PAYLOAD: if (len == sizeof(mru)) { memcpy(&mru, data, sizeof(mru)); mru = ntohs(mru); if (mru >= ETH_PPPOE_MTU) { if (lcp_allowoptions[0].mru > mru) lcp_allowoptions[0].mru = mru; if (lcp_wantoptions[0].mru > mru) lcp_wantoptions[0].mru = mru; conn->seenMaxPayload = 1; } } break; #endif } } /********************************************************************** *%FUNCTION: parsePADSTags *%ARGUMENTS: * type -- tag type * len -- tag length * data -- tag data * extra -- extra user data (pointer to PPPoEConnection structure) *%RETURNS: * Nothing *%DESCRIPTION: * Picks interesting tags out of a PADS packet ***********************************************************************/ static void parsePADSTags(uint16_t type, uint16_t len, unsigned char *data, void *extra) { #ifdef PLUGIN uint16_t mru; #endif PPPoEConnection *conn = (PPPoEConnection *) extra; switch(type) { case TAG_SERVICE_NAME: syslog(LOG_DEBUG, "PADS: Service-Name: '%.*s'", (int) len, data); break; case TAG_GENERIC_ERROR: case TAG_AC_SYSTEM_ERROR: case TAG_SERVICE_NAME_ERROR: pktLogErrs("PADS", type, len, data, extra); conn->PADSHadError = 1; break; case TAG_RELAY_SESSION_ID: conn->relayId.type = htons(type); conn->relayId.length = htons(len); memcpy(conn->relayId.payload, data, len); break; #ifdef PLUGIN case TAG_PPP_MAX_PAYLOAD: if (len == sizeof(mru)) { memcpy(&mru, data, sizeof(mru)); mru = ntohs(mru); if (mru >= ETH_PPPOE_MTU) { if (lcp_allowoptions[0].mru > mru) lcp_allowoptions[0].mru = mru; if (lcp_wantoptions[0].mru > mru) lcp_wantoptions[0].mru = mru; conn->seenMaxPayload = 1; } } break; #endif } } /*********************************************************************** *%FUNCTION: sendPADI *%ARGUMENTS: * conn -- PPPoEConnection structure *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADI packet ***********************************************************************/ static void sendPADI(PPPoEConnection *conn) { PPPoEPacket packet; unsigned char *cursor = packet.payload; PPPoETag *svc = (PPPoETag *) (&packet.payload); uint16_t namelen = 0; uint16_t plen; int omit_service_name = 0; if (conn->serviceName) { namelen = (uint16_t) strlen(conn->serviceName); if (!strcmp(conn->serviceName, "NO-SERVICE-NAME-NON-RFC-COMPLIANT")) { omit_service_name = 1; } } /* Set destination to Ethernet broadcast address */ memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_PADI; packet.session = 0; if (!omit_service_name) { plen = TAG_HDR_SIZE + namelen; CHECK_ROOM(cursor, packet.payload, plen); svc->type = TAG_SERVICE_NAME; svc->length = htons(namelen); if (conn->serviceName) { memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); } cursor += namelen + TAG_HDR_SIZE; } else { plen = 0; } /* If we're using Host-Uniq, copy it over */ if (conn->hostUniq) { PPPoETag hostUniq; int len = (int) strlen(conn->hostUniq); hostUniq.type = htons(TAG_HOST_UNIQ); hostUniq.length = htons(len); CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); memcpy(hostUniq.payload, conn->hostUniq, len); memcpy(cursor, &hostUniq, len + TAG_HDR_SIZE); cursor += len + TAG_HDR_SIZE; plen += len + TAG_HDR_SIZE; } #ifdef PLUGIN #ifndef MIN #define MIN(x,y) ( (x) < (y) ? (x) : (y) ) #endif /* Add our maximum MTU/MRU */ if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) { PPPoETag maxPayload; uint16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru)); maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); maxPayload.length = htons(sizeof(mru)); CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE); memcpy(maxPayload.payload, &mru, sizeof(mru)); memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); cursor += sizeof(mru) + TAG_HDR_SIZE; plen += sizeof(mru) + TAG_HDR_SIZE; } #endif packet.length = htons(plen); sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "SENT"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif } /********************************************************************** *%FUNCTION: waitForPADO *%ARGUMENTS: * conn -- PPPoEConnection structure * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADO packet and copies useful information ***********************************************************************/ static void waitForPADO(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; struct timeval expire_at; PPPoEPacket packet; int len; struct PacketCriteria pc; pc.conn = conn; #ifdef PLUGIN conn->seenMaxPayload = 0; #endif if (gettimeofday(&expire_at, NULL) < 0) { fatalSys("gettimeofday (waitForPADO)"); } expire_at.tv_sec += timeout; do { if(!time_left(&tv, &expire_at)) { /* Timed out */ return; } FD_ZERO(&readable); FD_SET(conn->discoverySocket, &readable); while(1) { r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); if (r >= 0 || errno != EINTR) break; } if (r < 0) { fatalSys("select (waitForPADO)"); } if (r == 0) { /* Timed out */ return; } /* Get the packet */ receivePacket(conn->discoverySocket, &packet, &len); /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { syslog(LOG_ERR, "Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); continue; } #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif /* If it's not for us, loop again */ if (!packetIsForMe(conn, &packet)) continue; if (packet.code == CODE_PADO) { if (BROADCAST(packet.ethHdr.h_source)) { printErr("Ignoring PADO packet from broadcast MAC address"); continue; } #ifdef PLUGIN if (conn->req_peer && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) { warn("Ignoring PADO packet from wrong MAC address"); continue; } #endif pc.gotError = 0; pc.seenACName = 0; pc.seenServiceName = 0; pc.acNameOK = (conn->acName) ? 0 : 1; pc.serviceNameOK = (conn->serviceName) ? 0 : 1; if (conn->printACNames && (conn->numPADOs > 0)) { printf("\n"); } parsePacket(&packet, parsePADOTags, &pc); if (pc.gotError) { printErr("Error in PADO packet"); continue; } if (!pc.seenACName) { printErr("Ignoring PADO packet with no AC-Name tag"); continue; } if (!pc.seenServiceName) { printErr("Ignoring PADO packet with no Service-Name tag"); continue; } conn->numPADOs++; if (pc.acNameOK && pc.serviceNameOK) { memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); if (conn->printACNames) { printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n", (unsigned) conn->peerEth[0], (unsigned) conn->peerEth[1], (unsigned) conn->peerEth[2], (unsigned) conn->peerEth[3], (unsigned) conn->peerEth[4], (unsigned) conn->peerEth[5]); continue; } conn->discoveryState = STATE_RECEIVED_PADO; break; } } } while (conn->discoveryState != STATE_RECEIVED_PADO); } /*********************************************************************** *%FUNCTION: sendPADR *%ARGUMENTS: * conn -- PPPoE connection structur *%RETURNS: * Nothing *%DESCRIPTION: * Sends a PADR packet ***********************************************************************/ static void sendPADR(PPPoEConnection *conn) { PPPoEPacket packet; PPPoETag *svc = (PPPoETag *) packet.payload; unsigned char *cursor = packet.payload; uint16_t namelen = 0; uint16_t plen; if (conn->serviceName) { namelen = (uint16_t) strlen(conn->serviceName); } plen = TAG_HDR_SIZE + namelen; CHECK_ROOM(cursor, packet.payload, plen); memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_PADR; packet.session = 0; svc->type = TAG_SERVICE_NAME; svc->length = htons(namelen); if (conn->serviceName) { memcpy(svc->payload, conn->serviceName, namelen); } cursor += namelen + TAG_HDR_SIZE; /* If we're using Host-Uniq, copy it over */ if (conn->hostUniq) { PPPoETag hostUniq; int len = (int) strlen(conn->hostUniq); hostUniq.type = htons(TAG_HOST_UNIQ); hostUniq.length = htons(len); CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); memcpy(hostUniq.payload, conn->hostUniq, len); memcpy(cursor, &hostUniq, len + TAG_HDR_SIZE); cursor += len + TAG_HDR_SIZE; plen += len + TAG_HDR_SIZE; } /* Copy cookie and relay-ID if needed */ if (conn->cookie.type) { CHECK_ROOM(cursor, packet.payload, ntohs(conn->cookie.length) + TAG_HDR_SIZE); memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; } if (conn->relayId.type) { CHECK_ROOM(cursor, packet.payload, ntohs(conn->relayId.length) + TAG_HDR_SIZE); memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; } #ifdef PLUGIN /* Add our maximum MTU/MRU */ if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) { PPPoETag maxPayload; uint16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru)); maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); maxPayload.length = htons(sizeof(mru)); CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE); memcpy(maxPayload.payload, &mru, sizeof(mru)); memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); cursor += sizeof(mru) + TAG_HDR_SIZE; plen += sizeof(mru) + TAG_HDR_SIZE; } #endif packet.length = htons(plen); sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "SENT"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif } /********************************************************************** *%FUNCTION: waitForPADS *%ARGUMENTS: * conn -- PPPoE connection info * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADS packet and copies useful information ***********************************************************************/ static void waitForPADS(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; struct timeval expire_at; PPPoEPacket packet; int len; if (gettimeofday(&expire_at, NULL) < 0) { fatalSys("gettimeofday (waitForPADS)"); } expire_at.tv_sec += timeout; do { if(!time_left(&tv, &expire_at)) { /* Timed out */ return; } FD_ZERO(&readable); FD_SET(conn->discoverySocket, &readable); while(1) { r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); if (r >= 0 || errno != EINTR) break; } if (r < 0) { fatalSys("select (waitForPADS)"); } if (r == 0) { /* Timed out */ return; } /* Get the packet */ receivePacket(conn->discoverySocket, &packet, &len); /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { syslog(LOG_ERR, "Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); continue; } #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif /* If it's not from the AC, it's not for me */ if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue; /* If it's not for us, loop again */ if (!packetIsForMe(conn, &packet)) continue; /* Is it PADS? */ if (packet.code == CODE_PADS) { /* Parse for goodies */ conn->PADSHadError = 0; parsePacket(&packet, parsePADSTags, conn); if (!conn->PADSHadError) { conn->discoveryState = STATE_SESSION; break; } } } while (conn->discoveryState != STATE_SESSION); /* Don't bother with ntohs; we'll just end up converting it back... */ conn->session = packet.session; syslog(LOG_INFO, "PPP session is %d (0x%x)", (int) ntohs(conn->session), (unsigned int) ntohs(conn->session)); /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */ if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) { syslog(LOG_ERR, "Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session)); } } /********************************************************************** *%FUNCTION: discovery *%ARGUMENTS: * conn -- PPPoE connection info structure *%RETURNS: * Nothing *%DESCRIPTION: * Performs the PPPoE discovery phase ***********************************************************************/ void discovery(PPPoEConnection *conn) { int padiAttempts; int padrAttempts; int timeout = conn->discoveryTimeout; /* Skip discovery? */ if (conn->skipDiscovery) { conn->discoveryState = STATE_SESSION; if (conn->killSession) { sendPADT(conn, "RP-PPPoE: Session killed manually"); exit(EXIT_SUCCESS); } return; } SEND_PADI: padiAttempts = 0; do { padiAttempts++; if (padiAttempts > MAX_PADI_ATTEMPTS) { printErr("Timeout waiting for PADO packets"); if (persist) { padiAttempts = 0; timeout = conn->discoveryTimeout; } else { break; } } sendPADI(conn); conn->discoveryState = STATE_SENT_PADI; waitForPADO(conn, timeout); /* If we're just probing for access concentrators, don't do exponential backoff. This reduces the time for an unsuccessful probe to 15 seconds. */ if (!conn->printACNames) { timeout *= 2; } if (conn->printACNames && conn->numPADOs) { break; } } while (conn->discoveryState == STATE_SENT_PADI); /* If we're only printing access concentrator names, we're done */ if (conn->printACNames) { if (conn->numPADOs) { exit(EXIT_SUCCESS); } else { exit(EXIT_FAILURE); } } timeout = conn->discoveryTimeout; padrAttempts = 0; do { padrAttempts++; if (padrAttempts > MAX_PADI_ATTEMPTS) { printErr("Timeout waiting for PADS packets"); if (persist) { padrAttempts = 0; timeout = conn->discoveryTimeout; /* Go back to sending PADI again */ goto SEND_PADI; } else { return; } } sendPADR(conn); conn->discoveryState = STATE_SENT_PADR; waitForPADS(conn, timeout); timeout *= 2; } while (conn->discoveryState == STATE_SENT_PADR); #ifdef PLUGIN if (!conn->seenMaxPayload) { /* RFC 4638: MUST limit MTU/MRU to 1492 */ if (lcp_allowoptions[0].mru > ETH_PPPOE_MTU) lcp_allowoptions[0].mru = ETH_PPPOE_MTU; if (lcp_wantoptions[0].mru > ETH_PPPOE_MTU) lcp_wantoptions[0].mru = ETH_PPPOE_MTU; } #endif /* We're done. */ conn->discoveryState = STATE_SESSION; return; } rp-pppoe-4.0/src/plugin.c0000644000175000017500000004216014422272526013473 0ustar dfsdfs/*********************************************************************** * * plugin.c * * pppd plugin for kernel-mode PPPoE on Linux * * Copyright (C) 2001-2012 by Roaring Penguin Software Inc. * Copyright (C) 2018-2023 Dianne Skoll * Portions copyright 2000 Michal Ostrowski and Jamal Hadi Salim. * * Much code and many ideas derived from pppoe plugin by Michal * Ostrowski and Jamal Hadi Salim, which carries this copyright: * * Copyright 2000 Michal Ostrowski , * Jamal Hadi Salim * Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr., * which is based in part on work from Jens Axboe and Paul Mackerras. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * ***********************************************************************/ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HAVE_STDARG_H 1 #define HAVE_STDBOOL_H 1 #define HAVE_STDDEF_H 1 #include "pppd/pppd.h" #include "pppd/fsm.h" #include "pppd/lcp.h" #include "pppd/ipcp.h" #include "pppd/ccp.h" /* #include "pppd/pathnames.h" */ #include "pppoe.h" #include /* Needed on ancient Linux systems... */ #ifndef IFNAMSIZ #include #endif #include #include #ifndef _ROOT_PATH #define _ROOT_PATH "" #endif #define _PATH_ETHOPT _ROOT_PATH "/etc/ppp/options." #ifdef PPPD_VERSION /* Handle new-style (as of pppd 2.5) API */ #define VERSION PPPD_VERSION #define PPPD_2_5 1 #define script_setenv ppp_script_setenv #define options_from_file ppp_options_from_file #define option_error ppp_option_error #define add_options ppp_add_options #include "pppd/options.h" static char devnam[MAXNAMELEN]; #endif char pppd_version[] = VERSION; static int seen_devnam[2] = {0, 0}; static char *pppoe_reqd_mac = NULL; /* From sys-linux.c in pppd -- MUST FIX THIS! */ extern int new_style_driver; char *pppd_pppoe_service = NULL; static char *acName = NULL; static char *existingSession = NULL; static int printACNames = 0; static int PPPoEDevnameHook(char *cmd, char **argv, int doit); static option_t Options[] = { { "device name", o_wild, (void *) &PPPoEDevnameHook, "PPPoE device name", OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, devnam}, { "rp_pppoe_service", o_string, &pppd_pppoe_service, "Desired PPPoE service name" }, { "rp_pppoe_ac", o_string, &acName, "Desired PPPoE access concentrator name" }, { "rp_pppoe_sess", o_string, &existingSession, "Attach to existing session (sessid:macaddr)" }, { "rp_pppoe_verbose", o_int, &printACNames, "Be verbose about discovered access concentrators"}, { "rp_pppoe_mac", o_string, &pppoe_reqd_mac, "Only connect to specified MAC address" }, { NULL } }; static PPPoEConnection *conn = NULL; /********************************************************************** * %FUNCTION: PPPOEInitDevice * %ARGUMENTS: * None * %RETURNS: * * %DESCRIPTION: * Initializes PPPoE device. ***********************************************************************/ static int PPPOEInitDevice(void) { conn = malloc(sizeof(PPPoEConnection)); if (!conn) { fatal("Could not allocate memory for PPPoE session"); } memset(conn, 0, sizeof(PPPoEConnection)); if (acName) { SET_STRING(conn->acName, acName); } if (pppd_pppoe_service) { SET_STRING(conn->serviceName, pppd_pppoe_service); } if (strlen(devnam) > IFNAMSIZ) { fatal("Device name %s is too long - max length %d", devnam, (int) IFNAMSIZ); } SET_STRING(conn->ifName, devnam); conn->discoverySocket = -1; conn->sessionSocket = -1; conn->hostUniq = malloc(17); if (!conn->hostUniq) { fatal("Out of Memory"); } snprintf(conn->hostUniq, 17, "%lx", (unsigned long) getpid()); conn->printACNames = printACNames; conn->discoveryTimeout = PADI_TIMEOUT; return 1; } /********************************************************************** * %FUNCTION: PPPOEConnectDevice * %ARGUMENTS: * None * %RETURNS: * Non-negative if all goes well; -1 otherwise * %DESCRIPTION: * Connects PPPoE device. ***********************************************************************/ static int PPPOEConnectDevice(void) { struct sockaddr_pppox sp; struct ifreq ifr; int s; #ifdef PPPD_2_5 char remote_number[MAXNAMELEN]; #endif /* Restore configuration */ lcp_allowoptions[0].mru = conn->mtu; lcp_wantoptions[0].mru = conn->mru; /* Update maximum MRU */ s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { error("Can't get MTU for %s: %m", conn->ifName); return -1; } rp_strlcpy(ifr.ifr_name, conn->ifName, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFMTU, &ifr) < 0) { error("Can't get MTU for %s: %m", conn->ifName); close(s); return -1; } close(s); if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) { lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; } if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) { lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; } /* Open session socket before discovery phase, to avoid losing session */ /* packets sent by peer just after PADS packet (noted on some Cisco */ /* server equipment). */ /* Opening this socket just before waitForPADS in the discovery() */ /* function would be more appropriate, but it would mess-up the code */ conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE); if (conn->sessionSocket < 0) { error("Failed to create PPPoE socket: %m"); return -1; } if (acName) { SET_STRING(conn->acName, acName); } if (pppd_pppoe_service) { SET_STRING(conn->serviceName, pppd_pppoe_service); } #ifdef PPPD_2_5 ppp_set_pppdevnam(devnam); #else rp_strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); #endif if (existingSession) { unsigned int mac[ETH_ALEN]; int i, ses; if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x", &ses, &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 7) { fatal("Illegal value for rp_pppoe_sess option"); } conn->session = htons(ses); for (i=0; ipeerEth[i] = (unsigned char) mac[i]; } } else { conn->discoverySocket = openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth, NULL); discovery(conn); if (conn->discoveryState != STATE_SESSION) { error("Unable to complete PPPoE Discovery"); close(conn->discoverySocket); conn->discoverySocket = -1; return -1; } } /* Set PPPoE session-number for further consumption */ #ifdef PPPD_2_5 ppp_set_session_number(ntohs(conn->session)); #else ppp_session_number = ntohs(conn->session); #endif sp.sa_family = AF_PPPOX; sp.sa_protocol = PX_PROTO_OE; sp.sa_addr.pppoe.sid = conn->session; memset(sp.sa_addr.pppoe.dev, 0, IFNAMSIZ); memcpy(sp.sa_addr.pppoe.dev, conn->ifName, strlen(conn->ifName)); memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); /* Set remote_number for ServPoET */ sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X", (unsigned) conn->peerEth[0], (unsigned) conn->peerEth[1], (unsigned) conn->peerEth[2], (unsigned) conn->peerEth[3], (unsigned) conn->peerEth[4], (unsigned) conn->peerEth[5]); warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s", (unsigned) conn->peerEth[0], (unsigned) conn->peerEth[1], (unsigned) conn->peerEth[2], (unsigned) conn->peerEth[3], (unsigned) conn->peerEth[4], (unsigned) conn->peerEth[5], conn->ifName); #ifdef PPPD_2_5 ppp_set_remote_number(remote_number); #endif script_setenv("MACREMOTE", remote_number, 0); if (connect(conn->sessionSocket, (struct sockaddr *) &sp, sizeof(struct sockaddr_pppox)) < 0) { error("Failed to connect PPPoE socket: %d %m", errno); return -1; } return conn->sessionSocket; } #ifndef PPPD_2_5 static void PPPOESendConfig(int mtu, u_int32_t asyncmap, int pcomp, int accomp) { int sock; struct ifreq ifr; if (mtu > MAX_PPPOE_MTU) { warn("Couldn't increase MTU to %d", mtu); mtu = MAX_PPPOE_MTU; } sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { warn("Couldn't create IP socket: %m"); return; } rp_strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); ifr.ifr_mtu = mtu; if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) { warn("ioctl(SIOCSIFMTU): %m"); return; } (void) close (sock); } #endif static void PPPOERecvConfig(int mru, u_int32_t asyncmap, int pcomp, int accomp) { if (mru > MAX_PPPOE_MTU) { warn("Couldn't increase MRU to %d", mru); } } /********************************************************************** * %FUNCTION: PPPOEDisconnectDevice * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Disconnects PPPoE device ***********************************************************************/ static void PPPOEDisconnectDevice(void) { struct sockaddr_pppox sp; sp.sa_family = AF_PPPOX; sp.sa_protocol = PX_PROTO_OE; sp.sa_addr.pppoe.sid = 0; memset(sp.sa_addr.pppoe.dev, 0, IFNAMSIZ); memcpy(sp.sa_addr.pppoe.dev, conn->ifName, strlen(conn->ifName)); memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); if (connect(conn->sessionSocket, (struct sockaddr *) &sp, sizeof(struct sockaddr_pppox)) < 0 && errno != EALREADY) { fatal("Failed to disconnect PPPoE socket: %d %m", errno); return; } close(conn->sessionSocket); if (conn->discoverySocket >= 0) { sendPADT(conn, "RP-PPPoE: pppd invoked disconnect"); close(conn->discoverySocket); } /* Do NOT free conn; if pppd persist is on, we'll need it again */ } #ifndef PATH_MAX #define PATH_MAX 4096 #endif static void PPPOEDeviceOptions(void) { char buf[PATH_MAX]; rp_strlcpy(buf, _PATH_ETHOPT, PATH_MAX); strlcat(buf, devnam, PATH_MAX); if(!options_from_file(buf, 0, 0, 1)) exit(EXIT_OPTION_ERROR); } struct channel pppoe_channel; /********************************************************************** * %FUNCTION: PPPoEDevnameHook * %ARGUMENTS: * cmd -- the command (actually, the device name) * argv -- argument vector * doit -- if non-zero, set device name. Otherwise, just check if possible * %RETURNS: * 1 if we will handle this device; 0 otherwise. * %DESCRIPTION: * Checks if name is a valid interface name; if so, returns 1. Also * sets up devnam (string representation of device). ***********************************************************************/ static int PPPoEDevnameHook(char *cmd, char **argv, int doit) { int r = 1; int fd; struct ifreq ifr; int seen_idx = doit ? 1 : 0; /* If "devnam" has already been set, ignore. This prevents kernel from doing modprobes against random pppd arguments that happen to begin with "nic-", "eth" or "br" Ideally, "nix-ethXXX" should be supplied immediately after "plugin rp-pppoe.so" Patch based on suggestion from Mike Ireton. */ if (seen_devnam[seen_idx]) { return 0; } /* Only do it if name is "ethXXX" or "brXXX" or what was specified by rp_pppoe_dev option (ugh). */ /* Can also specify nic-XXXX in which case the nic- is stripped off. */ if (!strncmp(cmd, "nic-", 4)) { cmd += 4; } else { if (strncmp(cmd, "eth", 3) && strncmp(cmd, "br", 2)) { return 0; } } /* Open a socket */ if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { r = 0; } /* Try getting interface index */ if (r) { rp_strlcpy(ifr.ifr_name, cmd, IFNAMSIZ); if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { r = 0; } else { if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { r = 0; } else { if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { if (doit) error("Interface %s not Ethernet", cmd); r = 0; } } } } /* Close socket */ close(fd); if (r) { seen_devnam[seen_idx] = 1; if (doit) { rp_strlcpy(devnam, cmd, sizeof(devnam)); if (the_channel != &pppoe_channel) { the_channel = &pppoe_channel; #ifdef PPPD_2_5 ppp_set_modem(0); #else modem = 0; #endif lcp_allowoptions[0].neg_accompression = 0; lcp_wantoptions[0].neg_accompression = 0; lcp_allowoptions[0].neg_asyncmap = 0; lcp_wantoptions[0].neg_asyncmap = 0; lcp_allowoptions[0].neg_pcompression = 0; lcp_wantoptions[0].neg_pcompression = 0; ipcp_allowoptions[0].neg_vj=0; ipcp_wantoptions[0].neg_vj=0; ccp_allowoptions[0].deflate = 0 ; ccp_wantoptions[0].deflate = 0 ; ccp_allowoptions[0].bsd_compress = 0; ccp_wantoptions[0].bsd_compress = 0; PPPOEInitDevice(); } } #ifdef PPPD_2_5 ppp_set_devnam(devnam); #endif return 1; } return r; } /********************************************************************** * %FUNCTION: plugin_init * %ARGUMENTS: * None * %RETURNS: * Nothing * %DESCRIPTION: * Initializes hooks for pppd plugin ***********************************************************************/ void plugin_init(void) { #ifndef PPPD_2_5 if (!ppp_available() && !new_style_driver) { fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?"); } #endif add_options(Options); info("RP-PPPoE plugin version %s compiled against pppd %s", RP_VERSION, VERSION); } /********************************************************************** *%FUNCTION: fatalSys *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to stderr and syslog and exits. * ***********************************************************************/ void fatalSys(char const *str) { char buf[1024]; int i = errno; printErr("%.256s: %.256s", str, strerror(i)); sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i)); sendPADT(conn, buf); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: rp_fatal *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message to stderr and syslog and exits. ***********************************************************************/ void rp_fatal(char const *str) { printErr("%s", str); sendPADTf(conn, "RP-PPPoE: %.256s", str); exit(EXIT_FAILURE); } /********************************************************************** *%FUNCTION: sysErr *%ARGUMENTS: * str -- error message *%RETURNS: * Nothing *%DESCRIPTION: * Prints a message plus the errno value to syslog. ***********************************************************************/ void sysErr(char const *str) { rp_fatal(str); } void pppoe_check_options(void) { unsigned int mac[ETH_ALEN]; int i; if (pppoe_reqd_mac != NULL) { if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != ETH_ALEN) { option_error("cannot parse pppoe-mac option value"); exit(EXIT_OPTION_ERROR); } for (i = 0; i < 6; ++i) conn->req_peer_mac[i] = mac[i]; conn->req_peer = 1; } lcp_allowoptions[0].neg_accompression = 0; lcp_wantoptions[0].neg_accompression = 0; lcp_allowoptions[0].neg_asyncmap = 0; lcp_wantoptions[0].neg_asyncmap = 0; lcp_allowoptions[0].neg_pcompression = 0; lcp_wantoptions[0].neg_pcompression = 0; if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU) { lcp_allowoptions[0].mru = MAX_PPPOE_MTU; } if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU) { lcp_wantoptions[0].mru = MAX_PPPOE_MTU; } /* Save configuration */ conn->mtu = lcp_allowoptions[0].mru; conn->mru = lcp_wantoptions[0].mru; ccp_allowoptions[0].deflate = 0; ccp_wantoptions[0].deflate = 0; ipcp_allowoptions[0].neg_vj = 0; ipcp_wantoptions[0].neg_vj = 0; ccp_allowoptions[0].bsd_compress = 0; ccp_wantoptions[0].bsd_compress = 0; } #ifdef PPPD_2_5 struct channel pppoe_channel = { .options = Options, .process_extra_options = &PPPOEDeviceOptions, .check_options = &pppoe_check_options, .connect = &PPPOEConnectDevice, .disconnect = &PPPOEDisconnectDevice, .establish_ppp = &ppp_generic_establish, .disestablish_ppp = &ppp_generic_disestablish, .send_config = NULL, .recv_config = &PPPOERecvConfig, .close = NULL, .cleanup = NULL }; #else struct channel pppoe_channel = { .options = Options, .process_extra_options = &PPPOEDeviceOptions, .check_options = &pppoe_check_options, .connect = &PPPOEConnectDevice, .disconnect = &PPPOEDisconnectDevice, .establish_ppp = &generic_establish_ppp, .disestablish_ppp = &generic_disestablish_ppp, .send_config = &PPPOESendConfig, .recv_config = &PPPOERecvConfig, .close = NULL, .cleanup = NULL }; #endif rp-pppoe-4.0/man/0000755000175000017500000000000014422272526012012 5ustar dfsdfsrp-pppoe-4.0/man/pppoe-relay.80000644000175000017500000001025214422272526014340 0ustar dfsdfs.\" SPDX-License-Identifier: GPL-2.0-or-later .TH PPPOE-RELAY 8 "26 January 2001" .\"" .UC 4 .SH NAME pppoe-relay \- user-space PPPoE relay agent. .SH SYNOPSIS .B pppoe-relay \fR[\fIoptions\fR] .SH DESCRIPTION \fBpppoe-relay\fR is a user-space relay agent for PPPoE (Point-to-Point Protocol over Ethernet) for Linux. \fBpppoe-relay\fR works in concert with the \fBpppoe\fR client and \fBpppoe-server\fR server. See the OPERATION section later in this manual for details on how \fBpppoe-relay\fR works. .SH OPTIONS .TP .B \-S \fIinterface\fR Adds the Ethernet interface \fIinterface\fR to the list of interfaces managed by \fBpppoe-relay\fR. Only PPPoE servers may be connected to this interface. .TP .B \-C \fIinterface\fR Adds the Ethernet interface \fIinterface\fR to the list of interfaces managed by \fBpppoe-relay\fR. Only PPPoE clients may be connected to this interface. .TP .B \-B \fIinterface\fR Adds the Ethernet interface \fIinterface\fR to the list of interfaces managed by \fBpppoe-relay\fR. Both PPPoE clients and servers may be connected to this interface. .TP .B \-n \fInum\fR Allows at most \fInum\fR concurrent PPPoE sessions. If not specified, the default is 5000. \fInum\fR can range from 1 to 65534. .TP .B \-i \fItimeout\fR Specifies the session idle timeout. If both peers in a session are idle for more than \fItimeout\fR seconds, the session is terminated. If \fItimeout\fR is specified as zero, sessions will never be terminated because of idleness. Note that the idle-session expiry routine is never run more frequently than every 30 seconds, so the timeout is approximate. The default value for \fItimeout\fR is 600 seconds (10 minutes.) .TP .B \-F The \fB\-F\fR option causes \fBpppoe-relay\fR \fInot\fR to fork into the background; instead, it remains in the foreground. .TP .B \-h The \fB\-h\fR option prints a brief usage message and exits. .SH OPERATION \fBpppoe-relay\fR listens for incoming PPPoE PADI frames on all interfaces specified with \fB-B\fR or \fB-C\fR options. When a PADI frame appears, \fBpppoe-relay\fR adds a Relay-Session-ID tag and broadcasts the PADI on all interfaces specified with \fB-B\fR or \fB-S\fR options (except the interface on which the frame arrived.) Any PADO frames received are relayed back to the client which sent the PADI (assuming they contain valid Relay-Session-ID tags.) Likewise, PADR frames from clients are relayed back to the matching access concentrator. When a PADS frame is received, \fBpppoe-relay\fR enters the two peers' MAC addresses and session-ID's into a hash table. (The session-ID seen by the access concentrator may be different from that seen by the client; \fBpppoe-relay\fR must renumber sessions to avoid the possibility of duplicate session-ID's.) Whenever either peer sends a session frame, \fBpppoe-relay\fR looks up the session entry in the hash table and relays the frame to the correct peer. When a PADT frame is received, \fBpppoe-relay\fR relays it to the peer and deletes the session entry from its hash table. If a client and server crash (or frames are lost), PADT frames may never be sent, and \fBpppoe-relay\fR's hash table can fill up with stale sessions. Therefore, a session-cleaning routine runs periodically, and removes old sessions from the hash table. A session is considered "old" if no traffic has been seen within \fItimeout\fR seconds. When a session is deleted because of a timeout, a PADT frame is sent to each peer to make certain that they are aware the session has been killed. .SH EXAMPLE INVOCATIONS .nf pppoe-relay -C eth0 -S eth1 .fi The example above relays frames between PPPoE clients on the eth0 network and PPPoE servers on the eth1 network. .nf pppoe-relay -B eth0 -B eth1 .fi This example is a transparent relay -- frames are relayed between any mix of clients and servers on the eth0 and eth1 networks. .nf pppoe-relay -S eth0 -C eth1 -C eth2 -C eth3 .fi This example relays frames between servers on the eth0 network and clients on the eth1, eth2 and eth3 networks. .SH AUTHORS \fBpppoe-relay\fR was written by Dianne Skoll . The \fBpppoe\fR home page is \fIhttps://dianne.skoll.ca/projects/rp-pppoe/\fR. .SH SEE ALSO pppd(8), pppoe(8), pppoe-sniff(8), pppoe-server(8) rp-pppoe-4.0/man/pppoe-server.80000644000175000017500000002110614422272526014532 0ustar dfsdfs.\" SPDX-License-Identifier: GPL-2.0-or-later .TH PPPOE-SERVER 8 "21 June 2008" .\"" .UC 4 .SH NAME pppoe-server \- user-space PPPoE server .SH SYNOPSIS .B pppoe-server \fR[\fIoptions\fR] .SH DESCRIPTION \fBpppoe-server\fR is a user-space server for PPPoE (Point-to-Point Protocol over Ethernet) for Linux and other UNIX systems. \fBpppoe-server\fR works in concert with the \fBpppoe\fR client to respond to PPPoE discovery packets and set up PPPoE sessions. .SH OPTIONS .TP .B \-F The \fB\-F\fR option causes \fBpppoe-server\fR not to fork and become a daemon. The default is to fork and become a daemon. .TP .B \-I \fIinterface\fR The \fB\-I\fR option specifies the Ethernet interface to use. Under Linux, it is typically \fIeth0\fR or \fIeth1\fR. The interface should be "up" before you start \fBpppoe-server\fR, but need not have an IP address. You can supply multiple \fB\-I\fR options if you want the server to respond on more than one interface. .TP .B \-X \fIpidfile\fR This option causes \fBpppoe-server\fR to write its process ID to \fIpidfile\fR. Additionally, it keeps the file locked so that only a single process may be started for a given \fIpidfile\fR. .TP .B \-q \fI/path/to/pppd\fR Specifies the full path to the \fBpppd\fR program. The default is determined at compile time. One use of this option is to supply a wrapper program that modifies the arguments passed to pppd. This lets you do things not directly supported by the server (for example, specify IPv6 addresses.) .TP .B \-Q \fI/path/to/pppoe\fR Specifies the full path to the \fBpppoe\fR program. The default is determined at compile time. This option is only relevant if you are \fInot\fR using kernel-mode PPPoE. .TP .B \-T \fItimeout\fR This option is passed directly to \fBpppoe\fR; see \fBpppoe\fR(8) for details. If you are using kernel-mode PPPoE, this option has \fIno effect\fR. .TP .B \-C \fIac_name\fR Specifies which name to report as the access concentrator name. If not supplied, the host name is used. .TP .B \-S \fIname\fR Offer a service named \fIname\fR. Multiple \fB\-S\fR options may be specified; each one causes the named service to be advertised in a Service-Name tag in the PADO frame. The first \fB\-S\fR option specifies the default service, and is used if the PPPoE client requests a Service-Name of length zero. .TP .B \-m \fIMSS\fR This option is passed directly to \fBpppoe\fR; see \fBpppoe\fR(8) for details. If you are using kernel-mode PPPoE, this option has \fIno effect\fR. .TP .B \-x \fIn\fR Limit the number of sessions per peer MAC address to \fIn\fR. If a given MAC address attempts to create more than \fIn\fR sessions, then its PADI and PADR packets are ignored. If you set \fIn\fR to 0 (the default), then no limit is imposed on the number of sessions per peer MAC address. .TP .B \-s This option is passed directly to \fBpppoe\fR; see \fBpppoe\fR(8) for details. In addition, it causes \fBpppd\fR to be invoked with the \fIsync\fR option. .TP .B \-L \fIip\fR Sets the local IP address. This is passed to spawned \fBpppd\fR processes. If not specified, the default is 10.0.0.1. If specified as 0.0.0.0 the selection of local IP address is delegated to \fBpppd\fR. .TP .B \-R \fIip\fR Sets the starting remote IP address. As sessions are established, IP addresses are assigned starting from \fIip\fR. \fBpppoe-server\fR automatically keeps track of the pool of addresses and passes a valid remote IP address to \fBpppd\fR. If not specified, a starting address of 10.67.15.1 is used. If specified as 0.0.0.0 remote IP allocation will be delegated to \fBpppd\fR. .TP .B \-N \fInum\fR Allows at most \fInum\fR concurrent PPPoE sessions. If not specified, the default is 64. .TP .B \-M \fIstring\fR Sends \fIstring\fR in a MOTM tag in a PADM packet right after sending the PADS to a client. .TP .B \-H \fIurl\fR Sends \fIurl\fR in a HURL tag in a PADM packet right after sending the PADS to a client. Note that \fIurl\fR must start with either \fBhttp://\fR or \fBhttps://\fR. .TP .B \-O \fIfname\fR This option causes \fBpppoe-server\fR to tell \fBpppd\fR to use the option file \fIfname\fR instead of the default \fI/etc/ppp/pppoe-server-options\fR. .TP .B \-p \fIfname\fR Reads the specified file \fIfname\fR which is a text file consisting of one IP address per line. These IP addresses will be assigned to clients. The number of sessions allowed will equal the number of addresses found in the file. The \fB\-p\fR option overrides both \fB\-R\fR and \fB\-N\fR. In addition to containing IP addresses, the pool file can contain lines of the form: .nf a.b.c.d-e .fi which includes all IP addresses from a.b.c.d to a.b.c.e. For example, the line: .nf 1.2.3.4-7 .fi is equivalent to: .nf 1.2.3.4 1.2.3.5 1.2.3.6 1.2.3.7 .fi .TP .B \-r Tells the PPPoE server to randomly permute session numbers. Instead of handing out sessions in order, the session numbers are assigned in an unpredictable order. .TP .B \-u Tells the server to invoke \fBpppd\fR with the \fIunit\fR option. Note that this option only works for \fBpppd\fR version 2.4.0 or newer. .TP .B \-o \fIoffset\fR Instead of numbering PPPoE sessions starting at 1, they will be numbered starting at \fIoffset\fR+1. This allows you to run multiple servers on a given machine; just make sure that their session numbers do not overlap. .TP .B \-f disc:sess The \fB\-f\fR option sets the Ethernet frame types for PPPoE discovery and session frames. The types are specified as hexadecimal numbers separated by a colon. Standard PPPoE uses frame types 8863:8864. \fIYou should not use this option\fR unless you are absolutely sure the peer you are dealing with uses non-standard frame types. .TP .B \-k The \fB\-k\fR option tells the server to use kernel-mode PPPoE on Linux. This option is available only on Linux kernels 2.4.0 and later, and only if the server was built with kernel-mode support. .TP .B \-g path The \fB\-g\fR option tells the server the full path to the pppoe.so or rp-pppoe.so plugin to use with kernel-mode PPPoE. If omitted, a compiled-in default is used; this default can be displayed using the \fB\-h\fR option. .TP .B \-i The \fB\-i\fR option tells the server to completely ignore PADI frames if there are no free session slots. .TP .B \-h The \fB\-h\fR option prints a brief usage message and exits. .TP .B \-U path The \fB-U\fR option creates a UNIX socket which can be connected to in order to manage pppoe-server at run-time. Please refer to the \fBCONTROL-SOCKET\fR section below for more detailed instructions. .SH OPERATION \fBpppoe-server\fR listens for incoming PPPoE discovery packets. When a session is established, it spawns a \fBpppd\fR process. The following options are passed to \fBpppd\fR: .nf nodetach noaccomp nobsdcom nodeflate nopcomp novj novjccomp default-asyncmap .fi In addition, the local and remote IP address are set based on the \fB\-L\fR and \fB\-R\fR options. The \fBpty\fR option is supplied along with a \fBpppoe\fR command to initiate the PPPoE session. Finally, additional \fBpppd\fR options can be placed in the file \fB/etc/ppp/pppoe-server-options\fR (which must exist, even if it is just empty!) Note that \fBpppoe-server\fR is meant mainly for testing PPPoE clients. It is \fInot\fR a high-performance server meant for production use. .SH CONTROL-SOCKET The control-socket was implemented as a secondary mechanism to improve run-time control of the pppoe-server. To use it you need to start pppoe-server with the \fB-U\fR option described above. You can then (in the absense of a control-client currently) use netcat to connect to the control socket, for example: .nf nc -U /run/pppoe-server.control .fi Assuming \fB-U /run/pppoe-server.control\fR was passed to pppoe-server. The following commands are implemented: .TP .B set drain {off|on|quit} This will set whether or not pppoe-server responds to PADI packets or not. When set to off pppoe-server will respond, else PADI packets will be ignored. This allows the pppoe-server to be drained from clients. In addition when set to quit will terminate pppoe-server when all pppoe-clients have terminated. This allows for (mostly) seamless upgrades in that the currently running instance can be issued with "set drain quit" prior to starting a new interface on the same interfaces, thus allowing new connections to be made whilst maintaining proper state on existing clients. .TP .B show status This will show basic status information for the connected-to pppoe-server. .SH AUTHORS \fBpppoe-server\fR was written by Dianne Skoll . The \fBpppoe\fR home page is \fIhttps://dianne.skoll.ca/projects/rp-pppoe/\fR. .SH SEE ALSO pppd(8), pppoe(8), pppoe-sniff(8), pppoe-relay(8) rp-pppoe-4.0/man/pppoe-sniff.80000644000175000017500000000435214422272526014335 0ustar dfsdfs.\" SPDX-License-Identifier: GPL-2.0-or-later .TH PPPOE-SNIFF 8 "3 July 2000" .\"" .UC 4 .SH NAME pppoe-sniff \- examine network for non-standard PPPoE frames .SH SYNOPSIS .B pppoe-sniff \fR[\fIoptions\fR] .SH DESCRIPTION \fBpppoe-sniff\fR listens for likely-looking PPPoE PADR and session frames and deduces extra options required for \fBpppoe(8)\fR to work. Some DSL providers seem to use non-standard frame types for PPPoE frames, and/or require a certain value in the Service-Name field. It is often easier to sniff those values from a machine which can successfully connect rather than try to pry them out of the DSL provider. To use \fBpppoe-sniff\fR, you need two computers, a DSL modem and an Ethernet hub (\fInot\fR an Ethernet switch.) If the DSL modem normally connects directly to your computer's Ethernet card, connect it to the "uplink" port on the Ethernet hub. Plug two computers into normal ports on the hub. On one computer, run whatever software the DSL provider gave you on whatever operating system the DSL provider supports. On the other computer, run Linux and log in as root. On the Linux machine, put the Ethernet interface into promiscuous mode and start \fBpppoe-sniff\fR. If the ethernet interface is \fIeth0\fR, for example, type these commands: .nf ip link set eth0 promisc on pppoe-sniff -I eth0 .fi On the other machine, start your DSL connection as usual. After a short time, \fBpppoe-sniff\fR should print recommendations for the value of an '-f' option for \fBpppoe\fR. After \fBpppoe-sniff\fR finishes (or you stop it if it seems hung), remember to turn off promiscuous mode: .nf ip link set eth0 promisc on .fi .SH OPTIONS .TP .B \-I \fIinterface\fR The \fB\-I\fR option specifies the Ethernet interface to use. Under Linux, it is typically \fIeth0\fR or \fIeth1\fR. The interface should be "up" and in promiscuous mode before you start \fBpppoe-sniff\fR. .TP .B \-V The \fB\-V\fR option causes \fBpppoe-sniff\fR to print its version number and exit. .SH BUGS \fBpppoe-sniff\fR only works on Linux. .SH AUTHORS \fBpppoe-sniff\fR was written by Dianne Skoll . The \fBpppoe\fR home page is \fIhttps://dianne.skoll.ca/projects/rp-pppoe/\fR. .SH SEE ALSO pppd(8), pppoe(8), pppoe-server(8), pppoe-relay(8) rp-pppoe-4.0/man/pppoe.80000644000175000017500000002257214422272526013236 0ustar dfsdfs.\" SPDX-License-Identifier: GPL-2.0-or-later .TH PPPOE 8 "5 October 2015" .UC 4 .SH NAME pppoe \- user-space PPPoE client. .SH SYNOPSIS .B pppd pty 'pppoe \fR[\fIpppoe_options\fR]\fB' \fR[\fIpppd_options\fR] .P .B pppoe -A \fR[\fIpppoe_options\fR] .SH DESCRIPTION \fBpppoe\fR is a user-space client for PPPoE (Point-to-Point Protocol over Ethernet) for Linux and other UNIX systems. \fBpppoe\fR works in concert with the \fBpppd\fR PPP daemon to provide a PPP connection over Ethernet, as is used by many DSL service providers. .SH OPTIONS .TP .B \-I \fIinterface\fR The \fB\-I\fR option specifies the Ethernet interface to use. Under Linux, it is typically \fIeth0\fR or \fIeth1\fR. The interface should be "up" before you start \fBpppoe\fR, but should \fInot\fR be configured to have an IP address. .TP .B \-T \fItimeout\fR The \fB\-T\fR option causes \fBpppoe\fR to exit if no session traffic is detected for \fItimeout\fR seconds. I recommend that you use this option as an extra safety measure, but if you do, you should make sure that PPP generates enough traffic so the timeout will normally not be triggered. The best way to do this is to use the \fIlcp-echo-interval\fR option to \fBpppd\fR. You should set the PPPoE timeout to be about four times the LCP echo interval. .TP .B \-D \fIfile_name\fR The \fB\-D\fR option causes every packet to be dumped to the specified \fIfile_name\fR. This is intended for debugging only; it produces huge amounts of output and greatly reduces performance. .TP .B \-V The \fB\-V\fR option causes \fBpppoe\fR to print its version number and exit. .TP .B \-A The \fB\-A\fR option causes \fBpppoe\fR to send a PADI packet and then print the names of access concentrators in each PADO packet it receives. Do not use this option in conjunction with \fBpppd\fR; the \fB\-A\fR option is meant to be used interactively to give interesting information about the access concentrator. .TP .B \-S \fIservice_name\fR Specifies the desired service name. \fBpppoe\fR will only initiate sessions with access concentrators which can provide the specified service. In most cases, you should \fInot\fR specify this option. Use it only if you know that there are multiple access concentrators or know that you need a specific service name. .TP .B \-C \fIac_name\fR Specifies the desired access concentrator name. \fBpppoe\fR will only initiate sessions with the specified access concentrator. In most cases, you should \fInot\fR specify this option. Use it only if you know that there are multiple access concentrators. If both the \fB\-S\fR and \fB\-C\fR options are specified, they must \fIboth\fR match for \fBpppoe\fR to initiate a session. .TP .B \-U Causes \fBpppoe\fR to use the Host-Uniq tag in its discovery packets. This lets you run multiple \fBpppoe\fR daemons without having their discovery packets interfere with one another. You must supply this option to \fIall\fR \fBpppoe\fR daemons if you intend to run multiple daemons simultaneously. The specific Host-Uniq value used is the hexadecimal representation of the \fBpppoe\fR process's PID. .TP .B \-W value Causes \fBpppoe\fR to use the Host-Uniq tag in its discovery packets, and furthermore to set the value of Host-Uniq to \fIvalue\fR. Use with caution. Note that \fB\-W\fR and \fB\-U\fR are mutually-incompatible. .TP .B \-s Causes \fBpppoe\fR to use \fIsynchronous\fR PPP encapsulation. If you use this option, then you \fImust\fR use the \fBsync\fR option with \fBpppd\fR. You are encouraged to use this option if it works, because it greatly reduces the CPU overhead of \fBpppoe\fR. However, it MAY be unreliable on slow machines -- there is a race condition between pppd writing data and pppoe reading it. For this reason, the default setting is asynchronous. If you encounter bugs or crashes with Synchronous PPP, turn it off -- don't e-mail me for support! .TP .B \-m \fIMSS\fR Causes \fBpppoe\fR to \fIclamp\fR the TCP maximum segment size at the specified value. Because of PPPoE overhead, the maximum segment size for PPPoE is smaller than for normal Ethernet encapsulation. This could cause problems for machines on a LAN behind a gateway using PPPoE. If you have a LAN behind a gateway, and the gateway connects to the Internet using PPPoE, you are strongly recommended to use a \fB\-m 1412\fR option. This avoids having to set the MTU on all the hosts on the LAN. .TP .B \-p \fIfile\fR Causes \fBpppoe\fR to write its process-ID to the specified file. This can be used to locate and kill \fBpppoe\fR processes. .TP .B \-e \fIsess:mac\fR Causes \fBpppoe\fR to skip the discovery phase and move directly to the session phase. The session is given by \fIsess\fR and the MAC address of the peer by \fImac\fR. This mode is \fInot\fR meant for normal use; it is designed only for \fBpppoe-server\fR(8). .TP .B \-n Causes \fBpppoe\fR not to open a discovery socket. This mode is \fInot\fR meant for normal use; it is designed only for \fBpppoe-server\fR(8). .TP .B \-k Causes \fBpppoe\fR to terminate an existing session by sending a PADT frame, and then exit. You must use the \fB\-e\fR option in conjunction with this option to specify the session to kill. This may be useful for killing sessions when a buggy peer does not realize the session has ended. .TP .B \-d Causes \fBpppoe\fR to perform discovery and then exit, after printing session information to standard output. The session information is printed in exactly the format expected by the \fB\-e\fR option. This option lets you initiate a PPPoE discovery, perform some other work, and then start the actual PPP session. \fIBe careful\fR; if you use this option in a loop, you can create many sessions, which may annoy your peer. .TP .B \-f disc:sess The \fB\-f\fR option sets the Ethernet frame types for PPPoE discovery and session frames. The types are specified as hexadecimal numbers separated by a colon. Standard PPPoE uses frame types 8863:8864. \fIYou should not use this option\fR unless you are absolutely sure the peer you are dealing with uses non-standard frame types. If your ISP uses non-standard frame types, complain! .TP .B \-h The \fB\-h\fR option causes \fBpppoe\fR to print usage information and exit. .SH PPPOE BACKGROUND PPPoE (Point-to-Point Protocol over Ethernet) is described in RFC 2516 and is a protocol which allows the session abstraction to be maintained over bridged Ethernet networks. PPPoE works by encapsulating PPP frames in Ethernet frames. The protocol has two distinct stages: The \fIdiscovery\fR and the \fIsession\fR stage. In the discovery stage, the host broadcasts a special PADI (PPPoE Active Discovery Initiation) frame to discover any \fIaccess concentrators\fR. The access concentrators (typically, only one access concentrator) reply with PADO (PPPoE Active Discovery Offer) packets, announcing their presence and the services they offer. The host picks one of the access concentrators and transmits a PADR (PPPoE Active Discovery Request) packet, asking for a session. The access concentrator replies with a PADS (PPPoE Active Discovery Session-Confirmation) packet. The protocol then moves to the session stage. In the session stage, the host and access concentrator exchange PPP frames embedded in Ethernet frames. The normal Ethernet MTU is 1500 bytes, but the PPPoE overhead plus two bytes of overhead for the encapsulated PPP frame mean that the MTU of the PPP interface is at most 1492 bytes. This causes \fIall kinds of problems\fR if you are using a Linux machine as a firewall and interfaces behind the firewall have an MTU greater than 1492. In fact, to be safe, I recommend setting the MTU of machines behind the firewall to 1412, to allow for worst-case TCP and IP options in their respective headers. Normally, PPP uses the Link Control Protocol (LCP) to shut down a PPP link. However, the PPPoE specification allows the link to be shut down with a special PADT (PPPoE Active Discovery Terminate) packet. This client recognizes this packet and will correctly terminate if a terminate request is received for the PPP session. .SH DESIGN GOALS My design goals for this PPPoE client were as follows, in descending order of importance: .TP .B o It must work. .TP .B o It must be a user-space program and not a kernel patch. .TP .B o The code must be easy to read and maintain. .TP .B o It must be fully compliant with RFC 2516, the proposed PPPoE standard. .TP .B o It must never hang up forever -- if the connection is broken, it must detect this and exit, allowing a wrapper script to restart the connection. .TP .B o It must be fairly efficient. .P I believe I have achieved all of these goals, but (of course) am open to suggestions, patches and ideas. See my home page, https://dianne.skoll.ca/projects/rp-pppoe/, for contact information. .SH NOTES For best results, you must give \fBpppd\fR an mtu option of 1492. I have observed problems with excessively-large frames unless I set this option. Also, if \fBpppoe\fR is running on a firewall machine, all machines behind the firewall should have MTU's of 1412. If you have problems, check your system logs. \fBpppoe\fR logs interesting things to syslog. You may have to turn on logging of \fIdebug\fR-level messages for complete diagnosis. .SH AUTHORS \fBpppoe\fR was written by Dianne Skoll , with much inspiration from an earlier version by Luke Stras. The \fBpppoe\fR home page is \fIhttps://dianne.skoll.ca/projects/rp-pppoe/\fR. .SH SEE ALSO pppd(8), pppoe-sniff(8), pppoe-server(8), pppoe-relay(8) rp-pppoe-4.0/README.md0000644000175000017500000000137714422272526012526 0ustar dfsdfs# RP-PPPoE - a PPPoE client, relay and server for Linux RP-PPPoE is a PPPoE client, relay and server for Linux. It can run completely in user-mode or used the Linux kernel's built-in PPPoE support. Kernel-mode PPPoE is recommended for much better performance. # Installation 1. Compile and install pppd if you don't already have it. If you are installing from OS packages, make sure to install the PPP development package as well. 2. Unpack `rp-pppoe` or clone this git repo. 3. Change to source directory: `cd src` 4. Configure: `./configure --enable-plugin` 5. Compile: `make` 6. Install -- this step must be done as root: `make install` 7. Read `doc/HOW-TO-CONNECT` # Project Home Page [Project Home Page](https://dianne.skoll.ca/projects/rp-pppoe/)