From bfd30c4c13346fb70f834cbfa37641e4dcde3215 Mon Sep 17 00:00:00 2001 From: kejun.gao Date: Wed, 27 Jul 2011 09:41:20 +0000 Subject: Init PPPoE from rp-pppoe-3.10 --- diff --git a/Android.mk b/Android.mk new file mode 100755 index 0000000..0de6841 --- a/dev/null +++ b/Android.mk @@ -0,0 +1,39 @@ +LOCAL_PATH:= $(call my-dir) + +PPPOE_VERSION="\"3.0\"" + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := src/pppoe.c \ + src/if.c \ + src/debug.c \ + src/common.c \ + src/ppp.c \ + src/discovery.c +LOCAL_C_INCLUDES := $(KERNEL_HEADERS) +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_MODULE = pppoe +LOCAL_CFLAGS := -DVERSION=$(PPPOE_VERSION) +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := src/pppoe-sniff.c \ + src/if.c \ + src/debug.c \ + src/common.c +LOCAL_C_INCLUDES := $(KERNEL_HEADERS) +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_MODULE = pppoe-sniff +LOCAL_CFLAGS := -DVERSION=$(PPPOE_VERSION) +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := src/relay.c \ + src/if.c \ + src/debug.c \ + src/common.c +LOCAL_C_INCLUDES := $(KERNEL_HEADERS) +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_MODULE = pppoe-relay +LOCAL_CFLAGS := -DVERSION=$(PPPOE_VERSION) +include $(BUILD_EXECUTABLE) + diff --git a/README b/README index 7dfd2da..2df6b47 100644 --- a/README +++ b/README @@ -1 +1,89 @@ -This repository contains PPPoE library/utility +# LIC: GPL + +pppoe: a PPP-over-Ethernet redirector for pppd +Copyright (C) 2001-2005 Roaring Penguin Software Inc. + +Some inspiration from an earlier client by Luke Stras. + +The MSS clamping was inspired by mssclampfw by Marc Boucher +with acknowledgements to Rebel.com (http://www.rebel.com). However, the +actual MSS clamping code is original and is licensed under the GPL, unlike +the original mssclampfw. + +Introduction +============ + +pppoe is a user-space redirector which permits the use of PPPoE +(Point-to-Point Over Ethernet) with Linux. PPPoE is used by many +DSL service providers. + +Installation +============ + +Requirements +------------ + +1) Linux 2.2.9 or later on Intel, Sparc or PowerPC. It may work on + Alpha, too -- anyone care to let me know? + + OR + + Linux 2.0.36 or later. + + OR + + FreeBSD, NetBSD or OpenBSD with BPF support. I have access only + to FreeBSD. In general, I can't answer questions about the *BSD's + as well as I can about Linux. + + +2) pppd 2.3.10 or later. Versions 2.3.7 and later work unless you use + demand-dialling. For demand dialling, you *must* use 2.3.10 or later. + +QUICKSTART +---------- + +If you're lucky, the "quickstart" method will work. After unpacking +the archive, become root and type: + + ./go + +This should configure, compile and install the software and set up your +DSL connection. You'll have to answer a few questions along the way. + +If you want the GUI wrapper, type: + + ./go-gui + +If ./go and ./go-gui didn't work, read the rest of this README. + +Compiling +--------- + +Compile and install pppd if you don't already have it. Then: + +1) Unpack: + + $ tar xzvf rp-pppoe-xxx.tar.gz + +2) Change to source directory: + + $ cd src + +3) Configure: + + $ ./configure + +4) Compile: + + $ make + +4) Install (this step must be done as root) + + # make install + +5) Now read doc/HOW-TO-CONNECT + +-- +David F. Skoll | Roaring Penguin Software Inc. +http://www.roaringpenguin.com diff --git a/SERVPOET b/SERVPOET new file mode 100755 index 0000000..b6fd5f7 --- a/dev/null +++ b/SERVPOET @@ -0,0 +1,18 @@ +# LIC: GPL + +ServPoET +-------- + +ServPoET is a commercial version of the PPPoE server. While everything +you received in this package is licensed under the GNU General Public +License, ServPoET is not free software and is licensed under a traditional +commercial license. + +ServPoET features RADIUS support, support for different realms, +real-time server status reporting and a friendly curses-based GUI for +administration. For information on ServPoET: + +Contact Fine Point Technologies, Inc. (http://www.finepoint.com/) + +-- +David F. Skoll diff --git a/configs/firewall-masq b/configs/firewall-masq new file mode 100755 index 0000000..14b9971 --- a/dev/null +++ b/configs/firewall-masq @@ -0,0 +1,71 @@ +#!/bin/sh +# +# firewall-masq This script sets up firewall rules for a machine +# acting as a masquerading gateway +# +# Copyright (C) 2000 Roaring Penguin Software Inc. This software may +# be distributed under the terms of the GNU General Public License, version +# 2 or any later version. +# LIC: GPL + +# Interface to Internet +EXTIF=ppp+ + +# NAT-Tables are different, so we can use ACCEPT everywhere (?) +iptables -t nat -P PREROUTING ACCEPT +iptables -t nat -P OUTPUT ACCEPT +iptables -t nat -P POSTROUTING ACCEPT + +# Flush the NAT-Table +iptables -t nat -F + +iptables -t filter -P INPUT DROP +iptables -t filter -F + +# Allow incoming SSH +#iptables -t filter -A INPUT -i $EXTIF -p tcp --dport 22 -j ACCEPT + +# Log & Deny the rest of the privileged ports +iptables -t filter -A INPUT -i $EXTIF -p tcp --dport 0:1023 -j LOG +iptables -t filter -A INPUT -i $EXTIF -p udp --dport 0:1023 -j LOG +iptables -t filter -A INPUT -i $EXTIF -p tcp --dport 0:1023 -j DROP +iptables -t filter -A INPUT -i $EXTIF -p udp --dport 0:1023 -j DROP + +# Log & Deny NFS +iptables -t filter -A INPUT -i $EXTIF -p udp --dport 2049 -j LOG +iptables -t filter -A INPUT -i $EXTIF -p tcp --dport 2049 -j LOG +iptables -t filter -A INPUT -i $EXTIF -p udp --dport 2049 -j DROP +iptables -t filter -A INPUT -i $EXTIF -p tcp --dport 2049 -j DROP + +# Log & Deny X11 +iptables -t filter -A INPUT -i $EXTIF -p tcp --dport 6000:6063 -j LOG +iptables -t filter -A INPUT -i $EXTIF -p tcp --dport 6000:6063 -j DROP + +# Log & Deny XFS +iptables -t filter -A INPUT -i $EXTIF -p tcp --dport 7100 -j LOG +iptables -t filter -A INPUT -i $EXTIF -p tcp --dport 7100 -j DROP + +# Deny TCP connection attempts +iptables -t filter -A INPUT -i $EXTIF -p tcp --syn -j LOG +iptables -t filter -A INPUT -i $EXTIF -p tcp --syn -j DROP + +# Deny ICMP echo-requests +iptables -t filter -A INPUT -i $EXTIF -p icmp --icmp-type echo-request -j DROP + +# Do masquerading +iptables -t nat -A POSTROUTING -o $EXTIF -j MASQUERADE + +# Enable forwarding +echo 1 > /proc/sys/net/ipv4/ip_forward + +# no IP spoofing +if [ -e /proc/sys/net/ipv4/conf/all/rp_filter ] ; then + for i in /proc/sys/net/ipv4/conf/*/rp_filter; do + echo 1 > $i + done +fi + +# Disable Source Routed Packets +for i in /proc/sys/net/ipv4/conf/*/accept_source_route; do + echo 0 > $i +done diff --git a/configs/firewall-standalone b/configs/firewall-standalone new file mode 100755 index 0000000..15b310e --- a/dev/null +++ b/configs/firewall-standalone @@ -0,0 +1,34 @@ +#!/bin/sh +# +# firewall-standalone This script sets up firewall rules for a standalone +# machine +# +# Copyright (C) 2005 Roaring Penguin Software Inc. This software may +# be distributed under the terms of the GNU General Public License, version +# 2 or any later version. +# LIC: GPL + +# Interface to Internet +EXTIF=ppp+ + +iptables -P INPUT ACCEPT +iptables -P OUTPUT ACCEPT +iptables -P FORWARD DROP + +iptables -F FORWARD +iptables -F INPUT +iptables -F OUTPUT + +# Deny TCP and UDP packets to privileged ports +iptables -A INPUT -p udp -i $EXTIF --dport 0:1023 -j LOG +iptables -A INPUT -p tcp -i $EXTIF --dport 0:1023 -j LOG +iptables -A INPUT -p udp -i $EXTIF --dport 0:1023 -j DROP +iptables -A INPUT -p tcp -i $EXTIF --dport 0:1023 -j DROP + +# Deny TCP connection attempts +iptables -A INPUT -i $EXTIF -p tcp --syn -j LOG +iptables -A INPUT -i $EXTIF -p tcp --syn -j DROP + +# Deny ICMP echo-requests +iptables -A INPUT -i $EXTIF -p icmp --icmp-type echo-request -j DROP + diff --git a/configs/pap-secrets b/configs/pap-secrets new file mode 100755 index 0000000..e1bcaff --- a/dev/null +++ b/configs/pap-secrets @@ -0,0 +1,10 @@ +# LIC: GPL +# Edit this file and place it in /etc/ppp/pap-secrets + +#User #Server #Password #IP +bxxxxx@sympatico.ca * my_password * + +# Replace bxxxxx@sympatico.ca with your Sympatico user-ID +# Replace my_password with your Sympatico password + +# For Magma, use xxyyzz@magma.ca diff --git a/configs/pppoe-server-options b/configs/pppoe-server-options new file mode 100755 index 0000000..b442db4 --- a/dev/null +++ b/configs/pppoe-server-options @@ -0,0 +1,6 @@ +# PPP options for the PPPoE server +# LIC: GPL +require-pap +login +lcp-echo-interval 10 +lcp-echo-failure 2 diff --git a/configs/pppoe.conf b/configs/pppoe.conf new file mode 100755 index 0000000..c222b2f --- a/dev/null +++ b/configs/pppoe.conf @@ -0,0 +1,140 @@ +#*********************************************************************** +# +# pppoe.conf +# +# Configuration file for rp-pppoe. Edit as appropriate and install in +# /etc/ppp/pppoe.conf +# +# NOTE: This file is used by the pppoe-start, pppoe-stop, pppoe-connect and +# pppoe-status shell scripts. It is *not* used in any way by the +# "pppoe" executable. +# +# Copyright (C) 2000 Roaring Penguin Software Inc. +# +# This file may be distributed under the terms of the GNU General +# Public License. +# +# LIC: GPL +# $Id$ +#*********************************************************************** + +# When you configure a variable, DO NOT leave spaces around the "=" sign. + +# Ethernet card connected to DSL modem +ETH=eth1 + +# PPPoE user name. You may have to supply "@provider.com" Sympatico +# users in Canada do need to include "@sympatico.ca" +# Sympatico uses PAP authentication. Make sure /etc/ppp/pap-secrets +# contains the right username/password combination. +# For Magma, use xxyyzz@magma.ca +USER=bxxxnxnx@sympatico.ca + +# Bring link up on demand? Default is to leave link up all the time. +# If you want the link to come up on demand, set DEMAND to a number indicating +# the idle time after which the link is brought down. +DEMAND=no +#DEMAND=300 + +# DNS type: SERVER=obtain from server; SPECIFY=use DNS1 and DNS2; +# NOCHANGE=do not adjust. +DNSTYPE=SERVER + +# Obtain DNS server addresses from the peer (recent versions of pppd only) +# In old config files, this used to be called USEPEERDNS. Changed to +# PEERDNS for better Red Hat compatibility +PEERDNS=yes + +DNS1= +DNS2= + +# Make the PPPoE connection your default route. Set to +# DEFAULTROUTE=no if you don't want this. +DEFAULTROUTE=yes + +### ONLY TOUCH THE FOLLOWING SETTINGS IF YOU'RE AN EXPERT + +# How long pppoe-start waits for a new PPP interface to appear before +# concluding something went wrong. If you use 0, then pppoe-start +# exits immediately with a successful status and does not wait for the +# link to come up. Time is in seconds. +# +# WARNING WARNING WARNING: +# +# If you are using rp-pppoe on a physically-inaccessible host, set +# CONNECT_TIMEOUT to 0. This makes SURE that the machine keeps trying +# to connect forever after pppoe-start is called. Otherwise, it will +# give out after CONNECT_TIMEOUT seconds and will not attempt to +# connect again, making it impossible to reach. +CONNECT_TIMEOUT=30 + +# How often in seconds pppoe-start polls to check if link is up +CONNECT_POLL=2 + +# Specific desired AC Name +ACNAME= + +# Specific desired service name +SERVICENAME= + +# Character to echo at each poll. Use PING="" if you don't want +# anything echoed +PING="." + +# File where the pppoe-connect script writes its process-ID. +# Three files are actually used: +# $PIDFILE contains PID of pppoe-connect script +# $PIDFILE.pppoe contains PID of pppoe process +# $PIDFILE.pppd contains PID of pppd process +CF_BASE=`basename $CONFIG` +PIDFILE="/var/run/$CF_BASE-pppoe.pid" + +# Do you want to use synchronous PPP? "yes" or "no". "yes" is much +# easier on CPU usage, but may not work for you. It is safer to use +# "no", but you may want to experiment with "yes". "yes" is generally +# safe on Linux machines with the n_hdlc line discipline; unsafe on others. +SYNCHRONOUS=no + +# Do you want to clamp the MSS? Here's how to decide: +# - If you have only a SINGLE computer connected to the DSL modem, choose +# "no". +# - If you have a computer acting as a gateway for a LAN, choose "1412". +# The setting of 1412 is safe for either setup, but uses slightly more +# CPU power. +CLAMPMSS=1412 +#CLAMPMSS=no + +# LCP echo interval and failure count. +LCP_INTERVAL=20 +LCP_FAILURE=3 + +# PPPOE_TIMEOUT should be about 4*LCP_INTERVAL +PPPOE_TIMEOUT=80 + +# Firewalling: One of NONE, STANDALONE or MASQUERADE +FIREWALL=NONE + +# Linux kernel-mode plugin for pppd. If you want to try the kernel-mode +# plugin, use LINUX_PLUGIN=/etc/ppp/plugins/rp-pppoe.so +LINUX_PLUGIN= + +# Any extra arguments to pass to pppoe. Normally, use a blank string +# like this: +PPPOE_EXTRA="" + +# Rumour has it that "Citizen's Communications" with a 3Com +# HomeConnect DSL Modem DualLink requires these extra options: +# PPPOE_EXTRA="-f 3c12:3c13 -S ISP" + +# Any extra arguments to pass to pppd. Normally, use a blank string +# like this: +PPPD_EXTRA="" + + +########## DON'T CHANGE BELOW UNLESS YOU KNOW WHAT YOU ARE DOING +# If you wish to COMPLETELY overrride the pppd invocation: +# Example: +# OVERRIDE_PPPD_COMMAND="pppd call dsl" + +# If you want pppoe-connect to exit when connection drops: +# RETRY_ON_FAILURE=no diff --git a/doc/CHANGES b/doc/CHANGES new file mode 100755 index 0000000..fe315f4 --- a/dev/null +++ b/doc/CHANGES @@ -0,0 +1,339 @@ +# LIC: GPL + +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$ diff --git a/doc/HOW-TO-CONNECT b/doc/HOW-TO-CONNECT new file mode 100755 index 0000000..f0d7185 --- a/dev/null +++ b/doc/HOW-TO-CONNECT @@ -0,0 +1,268 @@ +# LIC: GPL + +$Id$ + +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: + + ifconfig eth0 + +should display something like this: + + eth0 Link encap:Ethernet HWaddr 00:60:67:62:31:D4 + +plust some more lines. Your HWaddr will be different. As long as you see +the HWaddr line, your card should be working. + +DO NOT assign an IP address to the Ethernet card. DO NOT configure the +card to come up at boot time. + +2. Configure various files +-------------------------- + +Several files need editing. The easiest way to do this is to run +the following command as root: + + pppoe-setup + +Answer the questions and you should be all set. If you want to know what +goes on behind the scenes, continue reading this document. If you don't +care and your connection works, stop reading. :-) + +3. Edit pap-secrets +------------------- + +Edit the "pap-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. + +4. Edit /etc/ppp/pppoe.conf +----------------------------- + +The file /etc/ppp/pppoe.conf contains configuration information for the +DSL connection. You need to edit the following items: + +- Change ETH=eth1 to the correct Ethernet device for your modem. +- Change USER=bxxxnxnx@sympatico.ca to your proper DSL user-ID. + +Don't edit any of the other settings unless you're an expert. + +5. Set up DNS +------------- + +If you are using DNS servers supplied by your ISP, edit the file +/etc/resolv.conf to contain these lines: + + nameserver ip_addr_of_first_dns_server + nameserver ip_addr_of_second_dns_server + +For example: + + nameserver 204.101.251.1 + nameserver 204.101.251.2 + + +6. Firewall your machine +------------------------ + +MAKE SURE YOU FIREWALL YOUR MACHINE. A sample firewall script is given +in the shell script "firewall" To install the script: + +a) Copy it to /etc/rc.d/init.d/firewall +b) Type: chkconfig firewall on +c) Start the firewall: sh /etc/rc.d/init.d/firewall start + +(The above procedure works ONLY on Red Hat-like systems.) + +You may want to tweak the script somewhat. + +7. Bring up the connection at boot time +--------------------------------------- + +On a Red Hat system, the installation procedure should have installed +a script called /etc/rc.d/init.d/pppoe. To bring up the connection +at boot time, just type this command as root: + + chkconfig --add pppoe + +On non-Red-Hat systems, add this line to the end +of /etc/rc.d/rc.local: + + /usr/sbin/pppoe-start + +8. Configure LAN Hosts +---------------------- + +If you have a LAN behind the firewall, you have to lower the TCP +maximum segment size from the normal 1460 to 1452 (or better, 1412.) +You have two options: Either set the MTU of all the interfaces on +other hosts on the LAN to 1452, or use the "-m 1412" option to pppoe. +The "-m" option for pppoe is far simpler and makes it easier to add +hosts to the LAN, but consumes some extra CPU time. + +If you want to manually configure the LAN hosts, here's how: + +In Linux, use: "ifconfig eth0 mtu 1452". For best results, put this +in an /etc/rc.d/rc.local script. + +For Windows, machines, see http://lan.cns.ksu.edu/OS/WIN95/slip95.htm. +Set the MaxMTU to 1452. + +9. Commands to control the PPPoE link +------------------------------------- + +As root, bring up the link by typing: pppoe-start +As root, bring down the link by typing: pppoe-stop + +That's it! + +-- +David F. Skoll | Roaring Penguin Software Inc. +http://www.roaringpenguin.com | + +PROBLEMS! DAVE, IT DOESN'T WORK! +--------------------------------- + +Here are some problems PPPoE users have encountered. + +----------------------------------------------------------------------------- +A) Can't see the Ethernet interface + +Well, I can't really help you here. To use these instructions, you must +have Linux working to the point where it recognizes your Ethernet card. +If you type "ifconfig ethx" and you get back a HWAddr value, your Ethernet +card is probably OK. But I really can't help with hardware configuration +issues. + +----------------------------------------------------------------------------- +B) Connection seems to come up, but I can't browse the web or ping anything + +You probably don't have DNS set up. See step 6. + +----------------------------------------------------------------------------- +C) Can't compile PPPoE + +Make sure you have "make", the C compiler and all development header +files installed. I only test rp-pppoe on Linux. It might not work on +*BSD and probably won't work on any other version of UNIX. + +----------------------------------------------------------------------------- +D) pppd complains about (i) "unknown option pty" or (ii) "pty option precludes + specifying device name" + +(i) Your pppd is too old. You need at least 2.3.7. +(ii) Your /etc/ppp/options file is not empty. Empty it! + +----------------------------------------------------------------------------- +E) pppoe dies with the log message "Message too long" + +You set the MTU of the Ethernet interface connected to the DSL modem +to less than 1500. Don't do that. + +----------------------------------------------------------------------------- +F) Internal hosts can't see the Internet + +Do you have masquerading set up? I can't help you in great detail, but +see the IPCHAINS-HOWTO and the IP-Masquerade mini-HOWTO. + +----------------------------------------------------------------------------- +G) Authentication fails + +Make sure you have the right secret in /etc/ppp/pap-secrets. Your ISP +may be using CHAP; it won't hurt to copy the line to /etc/ppp/chap-secrets. + +Also, MAKE SURE that /etc/ppp/options is EMPTY. The "pppoe-connect" script +supplies all required options on the command line; additional options +in /etc/ppp/options may mess things up. + +----------------------------------------------------------------------------- +H) VPN software does not work + +If you are using VPN software on a Windows or Linux machine with another +Linux machine running PPPoE as the gateway, you MUST NOT use the "-m" option +to pppoe. This alters IP packets, which will break any VPN which uses IPSec. +In /etc/ppp/pppoe.conf, set CLAMPMSS to "no". You'll also have to reduce +the MTU on the hosts behind the gateway to 1452. + +----------------------------------------------------------------------------- +I) I can browse some web sites just fine, but others stall forever. + +There is probably a buggy router or firewall between you and the Web server. +One possible workaround: In /etc/ppp/pppoe.conf, find the line which reads: + + CLAMPMSS=1412 + +Try lowering the 1412 until it works (go down in steps of 100 or so.) Each +time you lower the value, you have to restart your connection like this: + + pppoe-stop; pppoe-start + +This should work around buggy routers which do not support Path MTU discovery. + +----------------------------------------------------------------------------- +J) Whenever I connect using DSL, my internal LAN no longer sees the gateway + +You are more than likely running a 2.0.X Linux kernel. To solve this +problem, give the Ethernet card connected to the DSL modem a fake IP +address. For example, if eth0 is your internal LAN card and eth1 goes to +the DSL modem, do something like this: + + ifconfig eth1 10.0.0.1 netmask 255.255.255.0 + +(You may have to choose a different IP address; experiment.) +----------------------------------------------------------------------------- +K) How can I run a script every time I connect and get a new IP address? + +Put the script in /etc/ppp/ip-up. See the pppd(8) man page. +----------------------------------------------------------------------------- +L) Nothing works! + +You may need to put your Ethernet card in half-duplex, 10Mb/s mode to +work with the DSL modem. You may have to run a DOS program to do this, +or pass special parameters to the Linux driver. + +Some providers object to attempts to set the MRU or MTU. Try removing +"mtu 1492 mru 1492" from PPP_STD_OPTIONS in the pppoe-connect script. +This problem has been seen with an ISP in Hong Kong. + +Your DSL provider may be using non-standard PPPoE frames or require +something special in the Service-Name field. If you have two computers, +you can try sniffing out these values with the "pppoe-sniff" program. +Type "man pppoe-sniff" for details. If you don't have two computers, +you'll have to ask your DSL provider if it uses non-standard PPPoE frames +or special Service-Name fields. Good luck getting an answer... + +If pppoe-sniff indicates that nothing is amiss, make sure the Ethernet +card associated with the DSL modem does NOT have a valid IP address. +(NOTE: For 2.0 kernels, you may have to give it a fake IP address +which is not on your internal subnet. Something like 192.168.42.42 +might work if you are not using 192.168.42.*) + +If you are using synchronous PPP on a slow machine, try switching to +asynchronous PPP. + +Make sure no entries in the routing table go through the Ethernet card +connected to the DSL modem. You might want to add these lines in +pppoe-connect: + + ifconfig ethx down + ifconfig ethx up mtu 1500 + +which should reset things to sane values. diff --git a/doc/KERNEL-MODE-PPPOE b/doc/KERNEL-MODE-PPPOE new file mode 100755 index 0000000..03e6d2a --- a/dev/null +++ b/doc/KERNEL-MODE-PPPOE @@ -0,0 +1,98 @@ +# LIC: GPL + +RP-PPPoE now supports kernel-mode PPPoE on Linux kernels 2.4 or newer. +However, the default "./go" build procedure does not make kernel-mode +support. + +Here's what you need to do: + +0) Make sure you are running kernel 2.4 or newer on the machine you +will build rp-pppoe on. You must have the following kernel +configuration settings: + + CONFIG_PPP=m or CONFIG_PPP=y + CONFIG_PPP_ASYNC=m or CONFIG_PPP_ASYNC=y + CONFIG_PPP_SYNC_TTY=m or CONFIG_PPP_SYNC_TTY=y + CONFIG_PPP_DEFLATE=m or CONFIG_PPP_DEFLATE=y + CONFIG_PPP_BSDCOMP=m or CONFIG_PPP_BSDCOMP=y + CONFIG_PPPOE=m or CONFIG_PPPOE=y + CONFIG_N_HDLC=m or CONFIG_N_HDLC=y + CONFIG_UNIX98_PTYS=y + +You also need a /dev/ppp file: + + mknod --mode=664 /dev/ppp c 108 0 + +You might want to add these lines to /etc/modules.conf: + + alias char-major-108 ppp_generic + alias tty-ldisc-3 ppp_async + alias tty-ldisc-13 n_hdlc + alias tty-ldisc-14 ppp_synctty + alias net-pf-24 pppoe + +1) If you are running pppd older than 2.4.0, check out the latest +version of the PPP software from the CVS repository at cvs.samba.org. +Here's how to do this: + + cvs -d :pserver:cvs@pserver.samba.org:/cvsroot login + # When prompted for a password, type "cvs" + + # Change to the directory in which you want to store the PPP source + # code. + cd /path/to/checked/out/sources + + # Check out the source + cvs -z5 -d :pserver:cvs@pserver.samba.org:/cvsroot co ppp + +If you are running pppd 2.4.0 or newer, and have the pppd development +headers installed, you can skip steps 1 and 2. + +2) The source gets checked out into a subdirectory called ppp. If +the source ends up in /path/to/checked/out/sources/ppp, then call +that path $PPPDIR. + +Build and install the checked-out ppp code according to its instructions. + +3) Unpack rp-pppoe. + +4) In the rp-pppoe directory, change to src/ and type: + + ./configure --enable-plugin=$PPPDIR + +where $PPPDIR, of course, refers to the checked-out "ppp" directory +from cvs.samba.org. + +If you didn't check out the PPP software from cvs.samba.org, use: + + ./configure --enable-plugin + +This will work if the header pppd/pppd.h is located in /usr/include +or /usr/local/include. + +4) Type make; make install + +5) Edit /etc/ppp/pppoe.conf to include this line: + + LINUX_PLUGIN=/etc/ppp/plugins/rp-pppoe.so + +After that, pppoe-start should use kernel-mode PPPoE. + +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. + + +-- +David F. Skoll + diff --git a/doc/LICENSE b/doc/LICENSE new file mode 100755 index 0000000..c9fc902 --- a/dev/null +++ b/doc/LICENSE @@ -0,0 +1,341 @@ +# LIC: GPL + + 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. diff --git a/doc/PROBLEMS b/doc/PROBLEMS new file mode 100755 index 0000000..0f77d61 --- a/dev/null +++ b/doc/PROBLEMS @@ -0,0 +1,5 @@ +# LIC: GPL + +Problems? + +See the last section of HOW-TO-CONNECT. diff --git a/man/pppoe-connect.8 b/man/pppoe-connect.8 new file mode 100755 index 0000000..cbb7a6b --- a/dev/null +++ b/man/pppoe-connect.8 @@ -0,0 +1,66 @@ +.\" LIC: GPL +.TH PPPOE-CONNECT 8 "21 February 2000" +.UC 4 +.SH NAME +pppoe-connect \- Shell script to manage a PPPoE link + +.SH SYNOPSIS +.B pppoe-connect \fR[\fIconfig_file\fR] +.P +.B pppoe-connect \fR\fIinterface user\fR [\fIconfig_file\fR] + + +.SH DESCRIPTION +\fBpppoe-connect\fR is a shell script which manages a PPPoE connection +using the Roaring Penguin user-space PPPoE client. If you omit +\fIconfig_file\fR, the default file \fB/etc/ppp/pppoe.conf\fR is used. +If you supply \fIinterface\fR and \fIuser\fR, then they override the +Ethernet interface and user-name settings in the configuration file. +.P +Note that normally, you should \fInot\fR invoke \fBpppoe-connect\fR +directly. Instead, use \fBpppoe-start\fR to bring up the PPPoE connection. +.P +\fBpppoe-connect\fR first reads a configuration file. It then brings +up a PPPoE connection. If the connection ever drops, a message is logged +to syslog, and \fBpppoe-connect\fR re-establishes the connection. In addition, +each time the connection is dropped or cannot be established, +\fBpppoe-connect\fR executes the script \fB/etc/ppp/pppoe-lost\fR if it +exists and is executable. + +.P +The shell script \fBpppoe-stop\fR causes \fBpppoe-connect\fR to break out +of its loop, bring the connection down, and exit. + +.SH TECHNICAL DETAILS +\fBpppoe-connect\fR uses the following shell variables from the +configuration file: + +.TP +.B ETH +The Ethernet interface connected to the DSL modem (for example, eth0). + +.TP +.B USER +The PPPoE user-id (for example, b1xxnxnx@sympatico.ca). + +.TP +.B PIDFILE +A file in which to write the process-ID of the pppoe-connect process +(for example, \fB/var/run/pppoe.pid\fR). Two additional files +($PIDFILE.pppd and $PIDFILE.pppoe) hold the process-ID's of the +\fBpppd\fR and \fBpppoe\fR processes, respectively. + +.P +By using different configuration files with different PIDFILE +settings, you can manage multiple PPPoE connections. Just specify the +configuration file as an argument to \fBpppoe-start\fR and +\fBpppoe-stop\fR. + +.SH AUTHOR +\fBpppoe-connect\fR was written by David F. Skoll . + +The \fBpppoe\fR home page is \fIhttp://www.roaringpenguin.com/pppoe/\fR. + +.SH SEE ALSO +pppoe(8), pppoe-start(8), pppoe-stop(8), pppd(8), pppoe.conf(5), pppoe-setup(8), pppoe-status(8), pppoe-sniff(8), pppoe-server(8), pppoe-relay(8) + diff --git a/man/pppoe-relay.8 b/man/pppoe-relay.8 new file mode 100755 index 0000000..9e52a30 --- a/dev/null +++ b/man/pppoe-relay.8 @@ -0,0 +1,124 @@ +.\" LIC: GPL +.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 David F. Skoll . + +The \fBpppoe\fR home page is \fIhttp://www.roaringpenguin.com/pppoe/\fR. + +.SH SEE ALSO +pppoe-start(8), pppoe-stop(8), pppoe-connect(8), pppd(8), pppoe.conf(5), +pppoe(8), pppoe-setup(8), pppoe-status(8), pppoe-sniff(8), pppoe-server(8) + diff --git a/man/pppoe-server.8 b/man/pppoe-server.8 new file mode 100755 index 0000000..b24c10b --- a/dev/null +++ b/man/pppoe-server.8 @@ -0,0 +1,184 @@ +.\" LIC: GPL +.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 should \fInot\fR be configured to 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 \-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. + +.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. + +.TP +.B \-N \fInum\fR +Allows at most \fInum\fR concurrent PPPoE sessions. If not specified, +the default is 64. + +.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 \-h +The \fB\-h\fR option prints a brief usage message and exits. + +.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 AUTHORS +\fBpppoe-server\fR was written by David F. Skoll . + +The \fBpppoe\fR home page is \fIhttp://www.roaringpenguin.com/pppoe/\fR. + +.SH SEE ALSO +pppoe-start(8), pppoe-stop(8), pppoe-connect(8), pppd(8), pppoe.conf(5), +pppoe(8), pppoe-setup(8), pppoe-status(8), pppoe-sniff(8), pppoe-relay(8) + diff --git a/man/pppoe-setup.8 b/man/pppoe-setup.8 new file mode 100755 index 0000000..636cd56 --- a/dev/null +++ b/man/pppoe-setup.8 @@ -0,0 +1,23 @@ +.\" LIC: GPL +.TH PPPOE-SETUP 8 "21 February 2000" +.UC 4 +.SH NAME +pppoe-setup \- Shell script to configure Roaring Penguin PPPoE client +.SH SYNOPSIS +.B pppoe-setup + +.SH DESCRIPTION +\fBpppoe-setup\fR is a shell script which prompts you for various pieces +of information and sets up an /etc/ppp/pppoe.conf configuration script +for the \fBpppoe-start\fR, \fBpppoe-stop\fR and \fBpppoe-connect\fR scripts. + +.SH AUTHOR +\fBpppoe-setup\fR was written by David F. Skoll . + +The \fBpppoe\fR home page is \fIhttp://www.roaringpenguin.com/pppoe/\fR. + +.SH SEE ALSO +pppoe(8), pppoe-start(8), pppoe-stop(8), pppoe-connect(8), pppd(8), +pppoe.conf(5), pppoe-status(8), pppoe-sniff(8), pppoe-relay(8), +pppoe-server(8) + diff --git a/man/pppoe-sniff.8 b/man/pppoe-sniff.8 new file mode 100755 index 0000000..95c9e24 --- a/dev/null +++ b/man/pppoe-sniff.8 @@ -0,0 +1,77 @@ +.\" LIC: GPL +.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 + ifconfig eth0 promisc + 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 \fBPPPOE_EXTRA\fR. Set this value in \fB/etc/ppp/pppoe.conf\fR. +If \fBpppoe-sniff\fR indicates that something special is required in +\fBPPPOE_EXTRA\fR, please e-mail this to \fBpppoe@roaringpenguin.com\fR +along with the name of your ISP and the manufacturer and model number of +your DSL modem. This information will be collated and provided on the +PPPoE web page for users who do not have two computers. + +After \fBpppoe-sniff\fR finishes (or you stop it if it seems hung), +remember to turn off promiscuous mode: + +.nf + ifconfig eth0 -promisc +.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 David F. Skoll . + +The \fBpppoe\fR home page is \fIhttp://www.roaringpenguin.com/pppoe/\fR. + +.SH SEE ALSO +pppoe-start(8), pppoe-stop(8), pppoe-connect(8), pppd(8), pppoe.conf(5), +pppoe(8), pppoe-setup(8), pppoe-status(8), pppoe-server(8), pppoe-relay(8) + diff --git a/man/pppoe-start.8 b/man/pppoe-start.8 new file mode 100755 index 0000000..aa2ce95 --- a/dev/null +++ b/man/pppoe-start.8 @@ -0,0 +1,27 @@ +.\" LIC: GPL +.TH PPPOE-START 8 "21 February 2000" +.UC 4 +.SH NAME +pppoe-start \- Shell script to bring up a PPPoE link +.SH SYNOPSIS +.B pppoe-start \fR[\fIconfig_file\fR] +.P +.B pppoe-start \fR\fIinterface user\fR [\fIconfig_file\fR] + +.SH DESCRIPTION +\fBpppoe-start\fR is a shell script which starts the Roaring Penguin +user-space PPPoE client. If you omit \fIconfig_file\fR, the default +file \fB/etc/ppp/pppoe.conf\fR is used. If you supply +\fIinterface\fR and \fIuser\fR, then they override the Ethernet interface +and user-name settings in the configuration file. + +.SH AUTHOR +\fBpppoe-start\fR was written by David F. Skoll . + +The \fBpppoe\fR home page is \fIhttp://www.roaringpenguin.com/pppoe/\fR. + +.SH SEE ALSO +pppoe(8), pppoe-stop(8), pppoe-connect(8), pppd(8), pppoe.conf(5), +pppoe-setup(8), pppoe-status(8), pppoe-sniff(8), pppoe-relay(8), +pppoe-server(8) + diff --git a/man/pppoe-status.8 b/man/pppoe-status.8 new file mode 100755 index 0000000..65ba874 --- a/dev/null +++ b/man/pppoe-status.8 @@ -0,0 +1,25 @@ +.\" LIC: GPL +.TH PPPOE-STATUS 8 "16 March 2000" +.UC 4 +.SH NAME +pppoe-status \- Shell script to report on status of PPPoE link +.SH SYNOPSIS +.B pppoe-status \fR[\fIconfig_file\fR] + +.SH DESCRIPTION +\fBpppoe-status\fR is a shell script which checks the status of the +PPPoE link established by the Roaring Penguin user-space PPPoE client. +If you omit \fIconfig_file\fR, the default file +\fB/etc/ppp/pppoe.conf\fR is used. + +.SH AUTHOR +\fBpppoe-status\fR was written by David F. Skoll . + +The \fBpppoe\fR home page is \fIhttp://www.roaringpenguin.com/pppoe/\fR. + +.SH SEE ALSO +pppoe(8), pppoe-start(8), pppoe-connect(8), pppd(8), pppoe.conf(5), +pppoe-setup(8), pppoe-stop(8), pppoe-sniff(8), pppoe-relay(8), +pppoe-server(8) + + diff --git a/man/pppoe-stop.8 b/man/pppoe-stop.8 new file mode 100755 index 0000000..123ca1b --- a/dev/null +++ b/man/pppoe-stop.8 @@ -0,0 +1,21 @@ +.\" LIC: GPL +.TH PPPOE-STOP 8 "21 February 2000" +.UC 4 +.SH NAME +pppoe-stop \- Shell script to shut down a PPPoE link +.SH SYNOPSIS +.B pppoe-stop \fR[\fIconfig_file\fR] + +.SH DESCRIPTION +\fBpppoe-stop\fR is a shell script which stops the Roaring Penguin +user-space PPPoE client. If you omit \fIconfig_file\fR, the default +file \fB/etc/ppp/pppoe.conf\fR is used. + +.SH AUTHOR +\fBpppoe-stop\fR was written by David F. Skoll . + +The \fBpppoe\fR home page is \fIhttp://www.roaringpenguin.com/pppoe/\fR. + +.SH SEE ALSO +pppoe(8), pppoe-start(8), pppoe-connect(8), pppd(8), pppoe.conf(5), pppoe-setup(8), pppoe-status(8), pppoe-sniff(8), pppoe-relay(8), pppoe-server(8) + diff --git a/man/pppoe.8 b/man/pppoe.8 new file mode 100755 index 0000000..6ef60c6 --- a/dev/null +++ b/man/pppoe.8 @@ -0,0 +1,236 @@ +.\" LIC: GPL +.TH PPPOE 8 "3 July 2000" +.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. + +.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, +http://www.roaringpenguin.com, 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 David F. Skoll , +with much inspiration from an earlier version by Luke Stras. + +The \fBpppoe\fR home page is \fIhttp://www.roaringpenguin.com/pppoe/\fR. + +.SH SEE ALSO +pppoe-start(8), pppoe-stop(8), pppoe-connect(8), pppd(8), pppoe.conf(5), pppoe-setup(8), pppoe-status(8), pppoe-sniff(8), pppoe-server(8), pppoe-relay(8) + diff --git a/man/pppoe.conf.5 b/man/pppoe.conf.5 new file mode 100755 index 0000000..58f77ca --- a/dev/null +++ b/man/pppoe.conf.5 @@ -0,0 +1,167 @@ +.\" LIC: GPL +.TH PPPOE.CONF 5 "21 February 2000" +.UC 4 +.SH NAME +pppoe.conf \- Configuration file used by \fBpppoe-start\fR(8), +\fBpppoe-stop\fR(8), \fBpppoe-status(8)\fR and \fBpppoe-connect\fR(8). + +.SH DESCRIPTION +\fB/etc/ppp/pppoe.conf\fR is a shell script which contains configuration +information for Roaring Penguin's PPPoE scripts. Note that \fBpppoe.conf\fR +is used only by the various pppoe-* shell scripts, not by \fBpppoe\fR +itself. + +\fBpppoe.conf\fR consists of a sequence of shell variable assignments. +The variables and their meanings are: + +.TP +.B ETH +The Ethernet interface connected to the DSL modem (for example, eth0). + +.TP +.B USER +The PPPoE user-id (for example, b1xxnxnx@sympatico.ca). + +.TP +.B SERVICENAME +If this is not blank, then it is passed with the \fB\-S\fR option to +\fBpppoe\fR. It specifies a service name to ask for. Usually, you +should leave it blank. + +.TP +.B ACNAME +If this is not blank, then it is passed with the \fB\-C\fR option to +\fBpppoe\fR. It specifies the name of the access concentrator to connect +to. Usually, you should leave it blank. + +.TP +.B DEMAND +If set to a number, the link is activated on demand and brought down +after after \fBDEMAND\fR seconds. If set to \fBno\fR, the link is kept +up all the time rather than being activated on demand. + +.TP +.B DNSTYPE +One of \fBNOCHANGE\fR, \fBSPECIFY\fR or \fBSERVER\fR. If +set to NOCHANGE, \fBpppoe-connect\fR will not adjust the DNS setup in +any way. If set to SPECIFY, it will re-write /etc/resolv.conf with +the values of DNS1 and DNS2. If set to \fBSERVER\fR, it will +supply the \fIusepeerdns\fR option to \fBpppd\fR, and make a symlink +from /etc/resolv.conf to /etc/ppp/resolv.conf. + +.TP +.B DNS1, DNS2 +IP addresses of DNS servers if you use DNSTYPE=SPECIFY. + +.TP +.B NONROOT +If the line \fBNONROOT=OK\fR (exactly like that; no whitespace or comments) +appears in the configuration file, then \fBpppoe-wrapper\fR will allow +non-root users to bring the conneciton up or down. The wrapper is installed +only if you installed the rp-pppoe-gui package. + +.TP +.B USEPEERDNS +If set to "yes", then \fBpppoe-connect\fR will supply the \fIusepeerdns\fR +option to \fBpppd\fR, which causes it to obtain DNS server addresses +from the peer and create a new \fB/etc/resolv.conf\fR file. Otherwise, +\fBpppoe-connect\fR will not supply this option, and \fBpppd\fR will not +modify \fB/etc/resolv.conf\fR. + +.TP +.B CONNECT_POLL +How often (in seconds) \fBpppoe-start\fR should check to see if a new PPP +interface has come up. If this is set to 0, the \fBpppoe-start\fR simply +initiates the PPP session, but does not wait to see if it comes up +successfully. + +.TP +.B CONNECT_TIMEOUT +How long (in seconds) \fBpppoe-start\fR should wait for a new PPP interface +to come up before concluding that \fBpppoe-connect\fR has failed and killing +the session. + +.TP +.B PING +A character which is echoed every \fBCONNECT_POLL\fR seconds while +\fBpppoe-start\fR is waiting for the PPP interface to come up. + +.TP +.B FORCEPING +A character which is echoed every \fBCONNECT_POLL\fR seconds while +\fBpppoe-start\fR is waiting for the PPP interface to come up. Similar +to \fBPING\fR, but the character is echoed even if \fBpppoe-start\fR's +standard output is not a tty. + +.TP +.B PIDFILE +A file in which to write the process-ID of the pppoe-connect process +(for example, \fB/var/run/pppoe.pid\fR). Two additional files +($PIDFILE.pppd and $PIDFILE.pppoe) hold the process-ID's of the +\fBpppd\fR and \fBpppoe\fR processes, respectively. + +.TP +.B SYNCHRONOUS +An indication of whether or not to use synchronous PPP (\fByes\fR or +\fBno\fR). Synchronous PPP is safe on Linux machines with the n_hdlc +line discipline. (If you have a file called "n_hdlc.o" in your +modules directory, you have the line discipline.) It is \fInot +recommended\fR on other machines or on Linux machines without the +n_hdlc line discipline due to some known and unsolveable race +conditions in a user-mode client. + +.TP +.B CLAMPMSS +The value at which to "clamp" the advertised MSS for TCP sessions. The +default of 1412 should be fine. + +.TP +.B LCP_INTERVAL +How often (in seconds) \fBpppd\fR sends out LCP echo-request packets. + +.TP +.B LCP_FAILURE +How many unanswered LCP echo-requests must occur before \fBpppd\fR +concludes the link is dead. + +.TP +.B PPPOE_TIMEOUT +If this many seconds elapse without any activity seen by \fBpppoe\fR, +then \fBpppoe\fR exits. + +.TP +.B FIREWALL +One of NONE, STANDALONE or MASQUERADE. If NONE, then \fBpppoe-connect\fR does +not add any firewall rules. If STANDALONE, then it clears existing firewall +rules and sets up basic rules for a standalone machine. If MASQUERADE, then +it clears existing firewall rules and sets up basic rules for an Internet +gateway. If you run services on your machine, these simple firewall scripts +are inadequate; you'll have to make your own firewall rules and set FIREWALL +to NONE. + +.TP +.B PPPOE_EXTRA +Any extra arguments to pass to \fBpppoe\fR + +.TP +.B PPPD_EXTRA +Any extra arguments to pass to \fBpppd\fR + +.TP +.B LINUX_PLUGIN +If non-blank, the full path of the Linux kernel-mode PPPoE plugin +(typically \fB/etc/ppp/plugins/rp-pppoe.so\fR.) This forces +\fBpppoe-connect\fR to use kernel-mode PPPoE on Linux 2.4.x systems. +This code is experimental and unsupported. Use of the plugin causes +\fBpppoe-connect\fR to ignore CLAMPMSS, PPPOE_EXTRA, SYNCHRONOUS and +PPPOE_TIMEOUT. + +.P +By using different configuration files with different PIDFILE +settings, you can manage multiple PPPoE connections. Just specify the +configuration file as an argument to \fBpppoe-start\fR and \fBpppoe-stop\fR. + +.SH SEE ALSO +pppoe(8), pppoe-connect(8), pppoe-start(8), pppoe-stop(8), pppd(8), pppoe-setup(8), +pppoe-wrapper(8) + diff --git a/pstart b/pstart new file mode 100644 index 0000000..029e405 --- a/dev/null +++ b/pstart @@ -0,0 +1,3 @@ +#! /system/bin/sh +pppd pty 'pppoe -p /var/run/pppoe.pid -I eth0 -T 80 -U -m 1412' debug logfd 1 noipdefault noauth default-asyncmap defaultroute nodetach mtu 1492 mru 1492 noaccomp nodeflate nopcomp novj novjccomp user gaokj password fatpig lcp-echo-interval 20 lcp-echo-failure 3 & + diff --git a/pstop b/pstop new file mode 100644 index 0000000..2ff6a00 --- a/dev/null +++ b/pstop @@ -0,0 +1,21 @@ +#!/system/bin/sh + +PPPOE_PIDFILE="/var/run/pppoe.pid" + +# Check for pidfile + PPPOE_PID=`cat $PPPOE_PIDFILE` + echo "$PPPOE_PIDFILE" + echo "$PPPOE_PID" + # Check if still running + kill -0 $PPPOE_PID > /dev/null 2>&1 + if [ $? != 0 ] ; then + echo "The pppoe(PID $PPPOE_PID) appears to have died" >& 2 + fi + + # Kill pppoe, which should in turn kill pppd + echo "Killing pppoe (PID $PPPOE_PID)" + kill -9 $PPPOE_PID > /dev/null 2>&1 || exit 1 + + rm "$PPPOE_PIDFILE" + +exit 0 diff --git a/scripts/pppoe-connect b/scripts/pppoe-connect new file mode 100755 index 0000000..88dd15c --- a/dev/null +++ b/scripts/pppoe-connect @@ -0,0 +1,319 @@ +#!/bin/sh +# ../scripts/pppoe-connect. Generated from pppoe-connect.in by configure. +#*********************************************************************** +# +# pppoe-connect +# +# Shell script to connect to a PPPoE provider +# +# Copyright (C) 2000 Roaring Penguin Software Inc. +# +# $Id$ +# +# This file may be distributed under the terms of the GNU General +# Public License. +# +# LIC: GPL +# +# Usage: pppoe-connect [config_file] +# pppoe-connect interface user [config_file] +# Second form overrides USER and ETH from config file. +# If config_file is omitted, defaults to /etc//ppp/pppoe.conf +# +#*********************************************************************** + +# From AUTOCONF +prefix=/usr +exec_prefix=${prefix} +localstatedir=/var + +# Paths to programs +IFCONFIG=/sbin/ifconfig +PPPD=pppd +SETSID=/usr/bin/setsid +PPPOE=${exec_prefix}/sbin/pppoe +LOGGER="/usr/bin/logger -t `basename $0`" + +# Set to "C" locale so we can parse messages from commands +LANG=C +export LANG + +# Must be root +if test "`/usr/bin/id -u`" != 0 ; then + echo "$0: You must be root to run this script" >& 2 + exit 1 +fi + +if test "$SETSID" != "" -a ! -x "$SETSID"; then + SETSID="" +fi + +CONFIG=/etc//ppp/pppoe.conf +USER="" +ETH="" + +# Sort out command-line arguments +case "$#" in + 1) + CONFIG="$1" + ;; + 3) + CONFIG="$3" + ;; +esac + +if test ! -f "$CONFIG" -o ! -r "$CONFIG" ; then + echo "$0: Cannot read configuration file '$CONFIG'" >& 2 + exit 1 +fi +export CONFIG +. $CONFIG + +PPPOE_PIDFILE="$PIDFILE.pppoe" +PPPD_PIDFILE="$PIDFILE.pppd" + +# Check for command-line overriding of ETH and USER +case "$#" in + 2|3) + ETH="$1" + USER="$2" + ;; +esac + +# Check that config file is sane +if test "$USER" = "" ; then + echo "$0: Check '$CONFIG' -- no setting for USER" >& 2 + exit 1 +fi +if test "$ETH" = "" ; then + echo "$0: Check '$CONFIG' -- no setting for ETH" >& 2 + exit 1 +fi + +PPPD_PID=0 + +# Catch common error +if test "$DEBUG" = "1" ; then + echo "*** If you want to use DEBUG, invoke pppoe-start, not pppoe-connect." + exit 1 +fi + +if test "$DEBUG" != "" ; then + if test "$LINUX_PLUGIN" != "" ; then + echo "Cannot use DEBUG mode and LINUX_PLUGIN at the same time." + echo "Kernel-mode PPPoE is experimental and unsupported." + exit 1 + fi + echo "* The following section identifies your Ethernet interface" >> $DEBUG + echo "* and user name. Some ISP's need 'username'; others" >> $DEBUG + echo "* need 'username@isp.com'. Try both" >> $DEBUG + echo "ETH=$ETH; USER=$USER" >> $DEBUG + echo "---------------------------------------------" >> $DEBUG +fi + +# MTU of Ethernet card attached to modem MUST be 1500. This apparently +# fails on some *BSD's, so we'll only do it under Linux + +if test `uname -s` = Linux ; then + $IFCONFIG $ETH up mtu 1500 + # For 2.4 kernels. Will fail on 2.2.x, but who cares? + modprobe ppp_generic > /dev/null 2>&1 + modprobe ppp_async > /dev/null 2>&1 + modprobe ppp_synctty > /dev/null 2>&1 + if test -n "$LINUX_PLUGIN" ; then + modprobe pppox > /dev/null 2>&1 + modprobe pppoe > /dev/null 2>&1 + fi +fi + +if test "$SYNCHRONOUS" = "yes" ; then + PPPOE_SYNC=-s + PPPD_SYNC=sync + # Increase the chances of it working on Linux... + if test `uname -s` = Linux ; then + modprobe n_hdlc > /dev/null 2>&1 + fi +else + PPPOE_SYNC="" + PPPD_SYNC="" +fi + +if test -n "$ACNAME" ; then + ACNAME="-C $ACNAME" +fi + +if test -n "$SERVICENAME" ; then + SERVICENAMEOPT="-S $SERVICENAME" +else + SERVICENAMEOPT="" +fi + +if test "$CLAMPMSS" = "no" ; then + CLAMPMSS="" +else + CLAMPMSS="-m $CLAMPMSS" +fi + +# If DNSTYPE is SERVER, we must use "usepeerdns" option to pppd. +if test "$DNSTYPE" = "SERVER" ; then + PEERDNS=yes + USEPEERDNS=yes +fi + +if test "$PEERDNS" = "yes" ; then + PEERDNS="usepeerdns" +else + PEERDNS="" +fi + +# Backward config file compatibility -- PEERDNS used to be USEPEERDNS +if test "$USEPEERDNS" = "yes" ; then + PEERDNS="usepeerdns" +fi +if test "$USEPEERDNS" = "no" ; then + PEERDNS="" +fi + + +# Backward config file compatibility +if test "$DEMAND" = "" ; then + DEMAND=no +fi + +if test "$DEMAND" = "no" ; then + DEMAND="" +else + DEMAND="demand persist idle $DEMAND 10.112.112.112:10.112.112.113 ipcp-accept-remote ipcp-accept-local connect true noipdefault ktune" +fi + +case "$FIREWALL" in + STANDALONE) + . /etc/ppp/firewall-standalone + ;; + MASQUERADE) + . /etc/ppp/firewall-masq + ;; +esac + +# If we're using kernel-mode PPPoE on Linux... +if test "$LINUX_PLUGIN" != "" ; then + PLUGIN_OPTS="plugin $LINUX_PLUGIN nic-$ETH" + if test -n "$SERVICENAME" ; then + PLUGIN_OPTS="$PLUGIN_OPTS rp_pppoe_service $SERVICENAME" + fi + modprobe pppoe > /dev/null 2>&1 +fi + +if test "$DEFAULTROUTE" != "no" ; then + DEFAULTROUTE="defaultroute" +else + DEFAULTROUTE="" +fi + +# Standard PPP options we always use +PPP_STD_OPTIONS="$PLUGIN_OPTS noipdefault noauth default-asyncmap $DEFAULTROUTE hide-password nodetach $PEERDNS mtu 1492 mru 1492 noaccomp nodeflate nopcomp novj novjccomp user $USER lcp-echo-interval $LCP_INTERVAL lcp-echo-failure $LCP_FAILURE $PPPD_EXTRA" + +# Jigger DNS if required... +if test "$DNSTYPE" = "SERVER" ; then + # Sorry, dude... + rm -f /etc/resolv.conf + ln -s /etc/ppp/resolv.conf /etc/resolv.conf +elif test "$DNSTYPE" = "SPECIFY" ; then + # Sorry, dude... + rm -f /etc/resolv.conf + echo "nameserver $DNS1" > /etc/resolv.conf + if test -n "$DNS2" ; then + echo "nameserver $DNS2" >> /etc/resolv.conf + fi +fi + +# PPPoE invocation +PPPOE_CMD="$PPPOE -p $PPPOE_PIDFILE -I $ETH -T $PPPOE_TIMEOUT -U $PPPOE_SYNC $CLAMPMSS $ACNAME $SERVICENAMEOPT $PPPOE_EXTRA" +if test "$DEBUG" != "" ; then + if test "$DEMAND" != "" ; then + echo "(Turning off DEMAND for debugging purposes)" + DEMAND="" + fi + echo "* The following section shows the pppd command we will invoke" >> $DEBUG + echo "pppd invocation" >> $DEBUG + echo "$SETSID $PPPD pty '$PPPOE_CMD' $PPP_STD_OPTIONS $PPPD_SYNC debug" >> $DEBUG + echo "---------------------------------------------" >> $DEBUG + $SETSID $PPPD pty "$PPPOE_CMD -D $DEBUG-0" \ + $PPP_STD_OPTIONS \ + $PPPD_SYNC \ + debug >> $DEBUG 2>&1 + echo "---------------------------------------------" >> $DEBUG + echo "* The following section is an extract from your log." >> $DEBUG + echo "* Look for error messages from pppd, such as" >> $DEBUG + echo "* a lack of kernel support for PPP, authentication failure" >> $DEBUG + echo "* etc." >> $DEBUG + if test -f "/var/log/messages" ; then + echo "Extract from /var/log/messages" >> $DEBUG + grep 'ppp' /var/log/messages | tail -150 >> $DEBUG + elif test -f "/var/adm/messages"; then + echo "Extract from /var/adm/messages" >> $DEBUG + grep 'ppp' /var/adm/messages | tail -150 >> $DEBUG + else + echo "Can't find messages file (looked for /var/{log,adm}/messages" >> $DEBUG + fi + date >> $DEBUG + echo "---------------------------------------------" >> $DEBUG + echo "* The following section is a dump of the packets" >> $DEBUG + echo "* sent and received by rp-pppoe. If you don't see" >> $DEBUG + echo "* any output, it's an Ethernet driver problem. If you only" >> $DEBUG + echo "* see three PADI packets and nothing else, check your cables" >> $DEBUG + echo "* and modem. Make sure the modem lights flash when you try" >> $DEBUG + echo "* to connect. Check that your Ethernet card is in" >> $DEBUG + echo "* half-duplex, 10Mb/s mode. If all else fails," >> $DEBUG + echo "* try using pppoe-sniff." >> $DEBUG + echo "rp-pppoe debugging dump" >> $DEBUG + cat $DEBUG-0 >> $DEBUG + rm -f $DEBUG-0 + for i in 1 2 3 4 5 6 7 8 9 10 ; do + echo "" + echo "" + echo "" + done + echo "*** Finished debugging run. Please review the file" + echo "*** '$DEBUG' and try to" + echo "*** figure out what is going on." + echo "***" + echo "*** Unfortunately, we can NO LONGER accept debugging" + echo "*** output for analysis. Please do not send this to" + echo "*** Roaring Penguin; it is too time-consuming for" + echo "*** us to deal with all the analyses we have been sent." + exit 0 +fi + +echo $$ > $PIDFILE + +while [ true ] ; do + if test "$OVERRIDE_PPPD_COMMAND" != "" ; then + $SETSID $OVERRIDE_PPPD_COMMAND & + echo "$!" > $PPPD_PIDFILE + elif test "$LINUX_PLUGIN" != "" ; then + $SETSID $PPPD $PPP_STD_OPTIONS $DEMAND & + echo "$!" > $PPPD_PIDFILE + else + $SETSID $PPPD pty "$PPPOE_CMD" \ + $PPP_STD_OPTIONS \ + $DEMAND \ + $PPPD_SYNC & + echo "$!" > $PPPD_PIDFILE + fi + wait + + if test "$RETRY_ON_FAILURE" = "no" ; then + exit + fi + + # Run /etc/ppp/pppoe-lost if it exists + test -x /etc/ppp/pppoe-lost && /etc/ppp/pppoe-lost + + # Re-establish the connection + $LOGGER -p daemon.notice \ + "PPPoE connection lost; attempting re-connection." + + # Wait a bit in case a problem causes tons of log messages :-) + sleep 5 +done diff --git a/scripts/pppoe-connect.in b/scripts/pppoe-connect.in new file mode 100755 index 0000000..635cffe --- a/dev/null +++ b/scripts/pppoe-connect.in @@ -0,0 +1,319 @@ +#!/bin/sh +# @configure_input@ +#*********************************************************************** +# +# pppoe-connect +# +# Shell script to connect to a PPPoE provider +# +# Copyright (C) 2000 Roaring Penguin Software Inc. +# +# $Id$ +# +# This file may be distributed under the terms of the GNU General +# Public License. +# +# LIC: GPL +# +# Usage: pppoe-connect [config_file] +# pppoe-connect interface user [config_file] +# Second form overrides USER and ETH from config file. +# If config_file is omitted, defaults to /etc//ppp/pppoe.conf +# +#*********************************************************************** + +# From AUTOCONF +prefix=@prefix@ +exec_prefix=@exec_prefix@ +localstatedir=/var + +# Paths to programs +IFCONFIG=/sbin/ifconfig +PPPD=@PPPD@ +SETSID=@SETSID@ +PPPOE=@sbindir@/pppoe +LOGGER="/usr/bin/logger -t `basename $0`" + +# Set to "C" locale so we can parse messages from commands +LANG=C +export LANG + +# Must be root +if test "`@ID@ -u`" != 0 ; then + echo "$0: You must be root to run this script" >& 2 + exit 1 +fi + +if test "$SETSID" != "" -a ! -x "$SETSID"; then + SETSID="" +fi + +CONFIG=/etc//ppp/pppoe.conf +USER="" +ETH="" + +# Sort out command-line arguments +case "$#" in + 1) + CONFIG="$1" + ;; + 3) + CONFIG="$3" + ;; +esac + +if test ! -f "$CONFIG" -o ! -r "$CONFIG" ; then + echo "$0: Cannot read configuration file '$CONFIG'" >& 2 + exit 1 +fi +export CONFIG +. $CONFIG + +PPPOE_PIDFILE="$PIDFILE.pppoe" +PPPD_PIDFILE="$PIDFILE.pppd" + +# Check for command-line overriding of ETH and USER +case "$#" in + 2|3) + ETH="$1" + USER="$2" + ;; +esac + +# Check that config file is sane +if test "$USER" = "" ; then + echo "$0: Check '$CONFIG' -- no setting for USER" >& 2 + exit 1 +fi +if test "$ETH" = "" ; then + echo "$0: Check '$CONFIG' -- no setting for ETH" >& 2 + exit 1 +fi + +PPPD_PID=0 + +# Catch common error +if test "$DEBUG" = "1" ; then + echo "*** If you want to use DEBUG, invoke pppoe-start, not pppoe-connect." + exit 1 +fi + +if test "$DEBUG" != "" ; then + if test "$LINUX_PLUGIN" != "" ; then + echo "Cannot use DEBUG mode and LINUX_PLUGIN at the same time." + echo "Kernel-mode PPPoE is experimental and unsupported." + exit 1 + fi + echo "* The following section identifies your Ethernet interface" >> $DEBUG + echo "* and user name. Some ISP's need 'username'; others" >> $DEBUG + echo "* need 'username@isp.com'. Try both" >> $DEBUG + echo "ETH=$ETH; USER=$USER" >> $DEBUG + echo "---------------------------------------------" >> $DEBUG +fi + +# MTU of Ethernet card attached to modem MUST be 1500. This apparently +# fails on some *BSD's, so we'll only do it under Linux + +if test `uname -s` = Linux ; then + $IFCONFIG $ETH up mtu 1500 + # For 2.4 kernels. Will fail on 2.2.x, but who cares? + modprobe ppp_generic > /dev/null 2>&1 + modprobe ppp_async > /dev/null 2>&1 + modprobe ppp_synctty > /dev/null 2>&1 + if test -n "$LINUX_PLUGIN" ; then + modprobe pppox > /dev/null 2>&1 + modprobe pppoe > /dev/null 2>&1 + fi +fi + +if test "$SYNCHRONOUS" = "yes" ; then + PPPOE_SYNC=-s + PPPD_SYNC=sync + # Increase the chances of it working on Linux... + if test `uname -s` = Linux ; then + modprobe n_hdlc > /dev/null 2>&1 + fi +else + PPPOE_SYNC="" + PPPD_SYNC="" +fi + +if test -n "$ACNAME" ; then + ACNAME="-C $ACNAME" +fi + +if test -n "$SERVICENAME" ; then + SERVICENAMEOPT="-S $SERVICENAME" +else + SERVICENAMEOPT="" +fi + +if test "$CLAMPMSS" = "no" ; then + CLAMPMSS="" +else + CLAMPMSS="-m $CLAMPMSS" +fi + +# If DNSTYPE is SERVER, we must use "usepeerdns" option to pppd. +if test "$DNSTYPE" = "SERVER" ; then + PEERDNS=yes + USEPEERDNS=yes +fi + +if test "$PEERDNS" = "yes" ; then + PEERDNS="usepeerdns" +else + PEERDNS="" +fi + +# Backward config file compatibility -- PEERDNS used to be USEPEERDNS +if test "$USEPEERDNS" = "yes" ; then + PEERDNS="usepeerdns" +fi +if test "$USEPEERDNS" = "no" ; then + PEERDNS="" +fi + + +# Backward config file compatibility +if test "$DEMAND" = "" ; then + DEMAND=no +fi + +if test "$DEMAND" = "no" ; then + DEMAND="" +else + DEMAND="demand persist idle $DEMAND 10.112.112.112:10.112.112.113 ipcp-accept-remote ipcp-accept-local connect true noipdefault ktune" +fi + +case "$FIREWALL" in + STANDALONE) + . /etc/ppp/firewall-standalone + ;; + MASQUERADE) + . /etc/ppp/firewall-masq + ;; +esac + +# If we're using kernel-mode PPPoE on Linux... +if test "$LINUX_PLUGIN" != "" ; then + PLUGIN_OPTS="plugin $LINUX_PLUGIN nic-$ETH" + if test -n "$SERVICENAME" ; then + PLUGIN_OPTS="$PLUGIN_OPTS rp_pppoe_service $SERVICENAME" + fi + modprobe pppoe > /dev/null 2>&1 +fi + +if test "$DEFAULTROUTE" != "no" ; then + DEFAULTROUTE="defaultroute" +else + DEFAULTROUTE="" +fi + +# Standard PPP options we always use +PPP_STD_OPTIONS="$PLUGIN_OPTS noipdefault noauth default-asyncmap $DEFAULTROUTE hide-password nodetach $PEERDNS mtu 1492 mru 1492 noaccomp nodeflate nopcomp novj novjccomp user $USER lcp-echo-interval $LCP_INTERVAL lcp-echo-failure $LCP_FAILURE $PPPD_EXTRA" + +# Jigger DNS if required... +if test "$DNSTYPE" = "SERVER" ; then + # Sorry, dude... + rm -f /etc/resolv.conf + ln -s /etc/ppp/resolv.conf /etc/resolv.conf +elif test "$DNSTYPE" = "SPECIFY" ; then + # Sorry, dude... + rm -f /etc/resolv.conf + echo "nameserver $DNS1" > /etc/resolv.conf + if test -n "$DNS2" ; then + echo "nameserver $DNS2" >> /etc/resolv.conf + fi +fi + +# PPPoE invocation +PPPOE_CMD="$PPPOE -p $PPPOE_PIDFILE -I $ETH -T $PPPOE_TIMEOUT -U $PPPOE_SYNC $CLAMPMSS $ACNAME $SERVICENAMEOPT $PPPOE_EXTRA" +if test "$DEBUG" != "" ; then + if test "$DEMAND" != "" ; then + echo "(Turning off DEMAND for debugging purposes)" + DEMAND="" + fi + echo "* The following section shows the pppd command we will invoke" >> $DEBUG + echo "pppd invocation" >> $DEBUG + echo "$SETSID $PPPD pty '$PPPOE_CMD' $PPP_STD_OPTIONS $PPPD_SYNC debug" >> $DEBUG + echo "---------------------------------------------" >> $DEBUG + $SETSID $PPPD pty "$PPPOE_CMD -D $DEBUG-0" \ + $PPP_STD_OPTIONS \ + $PPPD_SYNC \ + debug >> $DEBUG 2>&1 + echo "---------------------------------------------" >> $DEBUG + echo "* The following section is an extract from your log." >> $DEBUG + echo "* Look for error messages from pppd, such as" >> $DEBUG + echo "* a lack of kernel support for PPP, authentication failure" >> $DEBUG + echo "* etc." >> $DEBUG + if test -f "/var/log/messages" ; then + echo "Extract from /var/log/messages" >> $DEBUG + grep 'ppp' /var/log/messages | tail -150 >> $DEBUG + elif test -f "/var/adm/messages"; then + echo "Extract from /var/adm/messages" >> $DEBUG + grep 'ppp' /var/adm/messages | tail -150 >> $DEBUG + else + echo "Can't find messages file (looked for /var/{log,adm}/messages" >> $DEBUG + fi + date >> $DEBUG + echo "---------------------------------------------" >> $DEBUG + echo "* The following section is a dump of the packets" >> $DEBUG + echo "* sent and received by rp-pppoe. If you don't see" >> $DEBUG + echo "* any output, it's an Ethernet driver problem. If you only" >> $DEBUG + echo "* see three PADI packets and nothing else, check your cables" >> $DEBUG + echo "* and modem. Make sure the modem lights flash when you try" >> $DEBUG + echo "* to connect. Check that your Ethernet card is in" >> $DEBUG + echo "* half-duplex, 10Mb/s mode. If all else fails," >> $DEBUG + echo "* try using pppoe-sniff." >> $DEBUG + echo "rp-pppoe debugging dump" >> $DEBUG + cat $DEBUG-0 >> $DEBUG + rm -f $DEBUG-0 + for i in 1 2 3 4 5 6 7 8 9 10 ; do + echo "" + echo "" + echo "" + done + echo "*** Finished debugging run. Please review the file" + echo "*** '$DEBUG' and try to" + echo "*** figure out what is going on." + echo "***" + echo "*** Unfortunately, we can NO LONGER accept debugging" + echo "*** output for analysis. Please do not send this to" + echo "*** Roaring Penguin; it is too time-consuming for" + echo "*** us to deal with all the analyses we have been sent." + exit 0 +fi + +echo $$ > $PIDFILE + +while [ true ] ; do + if test "$OVERRIDE_PPPD_COMMAND" != "" ; then + $SETSID $OVERRIDE_PPPD_COMMAND & + echo "$!" > $PPPD_PIDFILE + elif test "$LINUX_PLUGIN" != "" ; then + $SETSID $PPPD $PPP_STD_OPTIONS $DEMAND & + echo "$!" > $PPPD_PIDFILE + else + $SETSID $PPPD pty "$PPPOE_CMD" \ + $PPP_STD_OPTIONS \ + $DEMAND \ + $PPPD_SYNC & + echo "$!" > $PPPD_PIDFILE + fi + wait + + if test "$RETRY_ON_FAILURE" = "no" ; then + exit + fi + + # Run /etc/ppp/pppoe-lost if it exists + test -x /etc/ppp/pppoe-lost && /etc/ppp/pppoe-lost + + # Re-establish the connection + $LOGGER -p daemon.notice \ + "PPPoE connection lost; attempting re-connection." + + # Wait a bit in case a problem causes tons of log messages :-) + sleep 5 +done diff --git a/scripts/pppoe-init b/scripts/pppoe-init new file mode 100755 index 0000000..0d61565 --- a/dev/null +++ b/scripts/pppoe-init @@ -0,0 +1,66 @@ +#!/bin/sh +# +# pppoe This script starts or stops a PPPoE connection +# +# chkconfig: 2345 99 01 +# description: Connects to PPPoE provider +# +# LIC: GPL +# +# Copyright (C) 2000 Roaring Penguin Software Inc. This software may +# be distributed under the terms of the GNU General Public License, version +# 2 or any later version. + +# Source function library if it exists +test -r /etc/rc.d/init.d/functions && . /etc/rc.d/init.d/functions + +# From AUTOCONF +prefix=/usr +exec_prefix=${prefix} + +# Paths to programs +START=${exec_prefix}/sbin/pppoe-start +STOP=${exec_prefix}/sbin/pppoe-stop +STATUS=${exec_prefix}/sbin/pppoe-status +case "$1" in + start) + echo -n "Bringing up PPPoE link" + + $START + if [ $? = 0 ] ; then + touch /var/lock/subsys/pppoe + echo_success + else + echo_failure + fi + echo "" + ;; + + stop) + echo -n "Shutting down PPPoE link" + + $STOP > /dev/null 2>&1 + if [ $? = 0 ] ; then + rm -f /var/lock/subsys/pppoe + echo_success + else + echo_failure + fi + echo "" + ;; + + restart) + $0 stop + $0 start + ;; + + status) + $STATUS + ;; + + *) + echo "Usage: pppoe {start|stop|restart|status}" + exit 1 +esac + +exit 0 diff --git a/scripts/pppoe-init-suse b/scripts/pppoe-init-suse new file mode 100755 index 0000000..64f6998 --- a/dev/null +++ b/scripts/pppoe-init-suse @@ -0,0 +1,64 @@ +#!/bin/sh +# +# pppoe This script starts or stops a PPPoE connection +# +# chkconfig: 2345 99 01 +# description: Connects to PPPoE provider +# +# LIC: GPL +# +# Copyright (C) 2000 Roaring Penguin Software Inc. This software may +# be distributed under the terms of the GNU General Public License, version +# 2 or any later version. +# Modifed to work with SuSE 6.4 linux by Gary Cameron. +# +# Source function library. +#. /etc/rc.d/init.d/functions # For red hat? +. /etc/rc.config # For SuSE, enables setting from /etc/rc.config + +#Tweak this +restart_time=120 + +# From AUTOCONF +prefix=/usr +exec_prefix=${prefix} + +# Paths to programs +START=${exec_prefix}/sbin/pppoe-start +STOP=${exec_prefix}/sbin/pppoe-stop +STATUS=${exec_prefix}/sbin/pppoe-status + +test "$PPPoE_START" = "yes" || exit 0 + +# The echo return value for success (defined in /etc/rc.config). +return=$rc_done +case "$1" in + start) + echo -n "Bringing up PPPoE link" + $START > /dev/null 2>&1 || return=$rc_failed + echo -e "$return" + ;; + + stop) + echo -n "Shutting down PPPoE link" + $STOP > /dev/null 2>&1 || return=$rc_failed + echo -e "$return" + ;; + + restart) + $0 stop + echo "Waiting" $restart_time "seconds for the host to reset itself" + sleep $restart_time #Note: Need time for host to reset itself + $0 start + ;; + + status) + $STATUS + ;; + + *) + echo "Usage: pppoe {start|stop|restart|status}" + exit 1 +esac + +exit 0 diff --git a/scripts/pppoe-init-suse.in b/scripts/pppoe-init-suse.in new file mode 100755 index 0000000..28376f4 --- a/dev/null +++ b/scripts/pppoe-init-suse.in @@ -0,0 +1,64 @@ +#!/bin/sh +# +# pppoe This script starts or stops a PPPoE connection +# +# chkconfig: 2345 99 01 +# description: Connects to PPPoE provider +# +# LIC: GPL +# +# Copyright (C) 2000 Roaring Penguin Software Inc. This software may +# be distributed under the terms of the GNU General Public License, version +# 2 or any later version. +# Modifed to work with SuSE 6.4 linux by Gary Cameron. +# +# Source function library. +#. /etc/rc.d/init.d/functions # For red hat? +. /etc/rc.config # For SuSE, enables setting from /etc/rc.config + +#Tweak this +restart_time=120 + +# From AUTOCONF +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +# Paths to programs +START=@sbindir@/pppoe-start +STOP=@sbindir@/pppoe-stop +STATUS=@sbindir@/pppoe-status + +test "$PPPoE_START" = "yes" || exit 0 + +# The echo return value for success (defined in /etc/rc.config). +return=$rc_done +case "$1" in + start) + echo -n "Bringing up PPPoE link" + $START > /dev/null 2>&1 || return=$rc_failed + echo -e "$return" + ;; + + stop) + echo -n "Shutting down PPPoE link" + $STOP > /dev/null 2>&1 || return=$rc_failed + echo -e "$return" + ;; + + restart) + $0 stop + echo "Waiting" $restart_time "seconds for the host to reset itself" + sleep $restart_time #Note: Need time for host to reset itself + $0 start + ;; + + status) + $STATUS + ;; + + *) + echo "Usage: pppoe {start|stop|restart|status}" + exit 1 +esac + +exit 0 diff --git a/scripts/pppoe-init-turbolinux b/scripts/pppoe-init-turbolinux new file mode 100755 index 0000000..aad06d6 --- a/dev/null +++ b/scripts/pppoe-init-turbolinux @@ -0,0 +1,64 @@ +#!/bin/sh +# +# pppoe This script starts or stops a PPPoE connection +# +# chkconfig: 2345 99 01 +# description: Connects to PPPoE provider +# +# LIC: GPL +# +# Copyright (C) 2000 Roaring Penguin Software Inc. This software may +# be distributed under the terms of the GNU General Public License, version +# 2 or any later version. + +# Source function library if it exists +test -r /etc/rc.d/init.d/functions && . /etc/rc.d/init.d/functions + +# From AUTOCONF +prefix=/usr +exec_prefix=${prefix} + +# Paths to programs +START=${exec_prefix}/sbin/pppoe-start +STOP=${exec_prefix}/sbin/pppoe-stop +STATUS=${exec_prefix}/sbin/pppoe-status +case "$1" in + start) + echo -n "Bringing up PPPoE link: " + + $START + if [ $? = 0 ] ; then + echo success + touch /var/lock/subsys/pppoe + else + echo failure + fi + ;; + + stop) + echo -n "Shutting down PPPoE link: " + + $STOP > /dev/null 2>&1 + if [ $? = 0 ] ; then + echo success + rm -f /var/lock/subsys/pppoe + else + echo failure + fi + ;; + + restart) + $0 stop + $0 start + ;; + + status) + $STATUS + ;; + + *) + echo "Usage: pppoe {start|stop|restart|status}" + exit 1 +esac + +exit 0 diff --git a/scripts/pppoe-init-turbolinux.in b/scripts/pppoe-init-turbolinux.in new file mode 100755 index 0000000..d35c801 --- a/dev/null +++ b/scripts/pppoe-init-turbolinux.in @@ -0,0 +1,64 @@ +#!/bin/sh +# +# pppoe This script starts or stops a PPPoE connection +# +# chkconfig: 2345 99 01 +# description: Connects to PPPoE provider +# +# LIC: GPL +# +# Copyright (C) 2000 Roaring Penguin Software Inc. This software may +# be distributed under the terms of the GNU General Public License, version +# 2 or any later version. + +# Source function library if it exists +test -r /etc/rc.d/init.d/functions && . /etc/rc.d/init.d/functions + +# From AUTOCONF +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +# Paths to programs +START=@sbindir@/pppoe-start +STOP=@sbindir@/pppoe-stop +STATUS=@sbindir@/pppoe-status +case "$1" in + start) + echo -n "Bringing up PPPoE link: " + + $START + if [ $? = 0 ] ; then + echo success + touch /var/lock/subsys/pppoe + else + echo failure + fi + ;; + + stop) + echo -n "Shutting down PPPoE link: " + + $STOP > /dev/null 2>&1 + if [ $? = 0 ] ; then + echo success + rm -f /var/lock/subsys/pppoe + else + echo failure + fi + ;; + + restart) + $0 stop + $0 start + ;; + + status) + $STATUS + ;; + + *) + echo "Usage: pppoe {start|stop|restart|status}" + exit 1 +esac + +exit 0 diff --git a/scripts/pppoe-init.in b/scripts/pppoe-init.in new file mode 100755 index 0000000..26dbe0b --- a/dev/null +++ b/scripts/pppoe-init.in @@ -0,0 +1,66 @@ +#!/bin/sh +# +# pppoe This script starts or stops a PPPoE connection +# +# chkconfig: 2345 99 01 +# description: Connects to PPPoE provider +# +# LIC: GPL +# +# Copyright (C) 2000 Roaring Penguin Software Inc. This software may +# be distributed under the terms of the GNU General Public License, version +# 2 or any later version. + +# Source function library if it exists +test -r /etc/rc.d/init.d/functions && . /etc/rc.d/init.d/functions + +# From AUTOCONF +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +# Paths to programs +START=@sbindir@/pppoe-start +STOP=@sbindir@/pppoe-stop +STATUS=@sbindir@/pppoe-status +case "$1" in + start) + echo -n "Bringing up PPPoE link" + + $START + if [ $? = 0 ] ; then + touch /var/lock/subsys/pppoe + echo_success + else + echo_failure + fi + echo "" + ;; + + stop) + echo -n "Shutting down PPPoE link" + + $STOP > /dev/null 2>&1 + if [ $? = 0 ] ; then + rm -f /var/lock/subsys/pppoe + echo_success + else + echo_failure + fi + echo "" + ;; + + restart) + $0 stop + $0 start + ;; + + status) + $STATUS + ;; + + *) + echo "Usage: pppoe {start|stop|restart|status}" + exit 1 +esac + +exit 0 diff --git a/scripts/pppoe-setup b/scripts/pppoe-setup new file mode 100755 index 0000000..5665118 --- a/dev/null +++ b/scripts/pppoe-setup @@ -0,0 +1,352 @@ +#!/bin/sh +#*********************************************************************** +# +# pppoe-setup +# +# All-purpose slicing/dicing shell script to configure rp-pppoe. +# +# LIC: GPL +# +# Copyright (C) 2000 Roaring Penguin Software Inc. +# +# $Id$ +#*********************************************************************** + +# From AUTOCONF +prefix=/usr +exec_prefix=${prefix} + +# Paths to programs +IFCONFIG=/sbin/ifconfig +PPPD=pppd +PPPOE=${exec_prefix}/sbin/pppoe +ECHO=/bin/echo +LOGGER="/usr/bin/logger -t `basename $0`" + +# Set to "C" locale so we can parse messages from commands +LANG=C +export LANG + +CONFIG=/etc/ppp/pppoe.conf + +# Protect created files +umask 077 + +copy() { + cp $1 $2 + if [ "$?" != 0 ] ; then + $ECHO "*** Error copying $1 to $2" + $ECHO "*** Quitting." + exit 1 + fi +} + +$ECHO "Welcome to the Roaring Penguin PPPoE client setup. First, I will run" +$ECHO "some checks on your system to make sure the PPPoE client is installed" +$ECHO "properly..." +$ECHO "" + +# Must be root +if [ "`/usr/bin/id -u`" != 0 ] ; then + $ECHO "$0: Sorry, you must be root to run this script" + exit 1 +fi + +# Prototype config file must exist +if [ ! -r "$CONFIG" ] ; then + $ECHO "Oh, dear, I don't see the file '$CONFIG' anywhere. Please" + $ECHO "re-install the PPPoE client." + exit 1 +fi + +# Must have pppd +if [ ! -x $PPPD ] ; then + $ECHO "Oops, I can't execute the program '$PPPD'. You" + $ECHO "must install the PPP software suite, version 2.3.10 or later." + exit 1 +fi +export CONFIG +. $CONFIG + +if [ "$DEMAND" = "" ] ; then + DEMAND=no +fi + +# pppoe must exist +if [ ! -x "$PPPOE" ] ; then + $ECHO "Oh, dear, I can't execute the program '$PPPOE'. Please" + $ECHO "re-install the rp-pppoe client." + exit 1 +fi + +$ECHO "Looks good! Now, please enter some information:" + +while [ true ] ; do + $ECHO "" + $ECHO "USER NAME" + $ECHO "" + printf "%s" ">>> Enter your PPPoE user name (default $USER): " + read U + + if [ "$U" = "" ] ; then + U="$USER" + fi + + # Under Linux, "fix" the default interface if eth1 is not available + if test `uname -s` = "Linux" ; then + $IFCONFIG $ETH > /dev/null 2>&1 || ETH=eth0 + fi + $ECHO "" + $ECHO "INTERFACE" + $ECHO "" + $ECHO ">>> Enter the Ethernet interface connected to the DSL modem" + $ECHO "For Solaris, this is likely to be something like /dev/hme0." + $ECHO "For Linux, it will be ethn, where 'n' is a number." + printf "%s" "(default $ETH): " + read E + + if [ "$E" = "" ] ; then + E="$ETH" + fi + + $ECHO "" + $ECHO "Do you want the link to come up on demand, or stay up continuously?" + $ECHO "If you want it to come up on demand, enter the idle time in seconds" + $ECHO "after which the link should be dropped. If you want the link to" + $ECHO "stay up permanently, enter 'no' (two letters, lower-case.)" + $ECHO "NOTE: Demand-activated links do not interact well with dynamic IP" + $ECHO "addresses. You may have some problems with demand-activated links." + printf "%s" ">>> Enter the demand value (default $DEMAND): " + read D + if [ "$D" = "" ] ; then + D=$DEMAND + fi + + $ECHO "" + $ECHO "DNS" + $ECHO "" + $ECHO "Please enter the IP address of your ISP's primary DNS server." + $ECHO "If your ISP claims that 'the server will provide DNS addresses'," + $ECHO "enter 'server' (all lower-case) here." + $ECHO "If you just press enter, I will assume you know what you are" + $ECHO "doing and not modify your DNS setup." + printf "%s" ">>> Enter the DNS information here: " + + read DNS1 + + + if [ "$DNS1" != "" ] ; then + if [ "$DNS1" != "server" ] ; then + $ECHO "Please enter the IP address of your ISP's secondary DNS server." + $ECHO "If you just press enter, I will assume there is only one DNS server." + printf "%s" ">>> Enter the secondary DNS server address here: " + read DNS2 + fi + fi + + while [ true ] ; do + $ECHO "" + $ECHO "PASSWORD" + $ECHO "" + stty -echo + printf "%s" ">>> Please enter your PPPoE password: " + read PWD1 + $ECHO "" + printf "%s" ">>> Please re-enter your PPPoE password: " + read PWD2 + $ECHO "" + stty echo + if [ "$PWD1" = "$PWD2" ] ; then + break + fi + + printf "%s" ">>> Sorry, the passwords do not match. Try again? (y/n)" + read ANS + case "$ANS" in + N|No|NO|Non|n|no|non) + $ECHO "OK, quitting. Bye." + exit 1 + esac + done + + # Firewalling + $ECHO "" + $ECHO "FIREWALLING" + $ECHO "" + if test `uname -s` != "Linux" ; then + $ECHO "Sorry, firewalling is only supported under Linux. Consult" + $ECHO "your operating system manuals for details on setting up" + $ECHO "packet filters for your system." + FIREWALL=NONE + else + $ECHO "Please choose the firewall rules to use. Note that these rules are" + $ECHO "very basic. You are strongly encouraged to use a more sophisticated" + $ECHO "firewall setup; however, these will provide basic security. If you" + $ECHO "are running any servers on your machine, you must choose 'NONE' and" + $ECHO "set up firewalling yourself. Otherwise, the firewall rules will deny" + $ECHO "access to all standard servers like Web, e-mail, ftp, etc. If you" + $ECHO "are using SSH, the rules will block outgoing SSH connections which" + $ECHO "allocate a privileged source port." + $ECHO "" + while [ true ] ; do + $ECHO "The firewall choices are:" + $ECHO "0 - NONE: This script will not set any firewall rules. You are responsible" + $ECHO " for ensuring the security of your machine. You are STRONGLY" + $ECHO " recommended to use some kind of firewall rules." + $ECHO "1 - STANDALONE: Appropriate for a basic stand-alone web-surfing workstation" + $ECHO "2 - MASQUERADE: Appropriate for a machine acting as an Internet gateway" + $ECHO " for a LAN" + printf "%s" ">>> Choose a type of firewall (0-2): " + read a + if [ "$a" = 0 -o "$a" = 1 -o "$a" = 2 ] ; then + break + fi + $ECHO "Please enter a number from 0 to 2" + done + + case "$a" in + 0) + FIREWALL=NONE + ;; + 1) + FIREWALL=STANDALONE + ;; + 2) + FIREWALL=MASQUERADE + ;; + esac + fi + + $ECHO "" + $ECHO "** Summary of what you entered **" + $ECHO "" + $ECHO "Ethernet Interface: $E" + $ECHO "User name: $U" + if [ "$D" = "no" ] ; then + $ECHO "Activate-on-demand: No" + else + $ECHO "Activate-on-demand: Yes; idle timeout = $D seconds" + fi + + if [ "$DNS1" != "" ] ; then + if [ "$DNS1" = "server" ] ; then + $ECHO "DNS addresses: Supplied by ISP's server" + else + $ECHO "Primary DNS: $DNS1" + if [ "$DNS2" != "" ] ; then + $ECHO "Secondary DNS: $DNS2" + fi + fi + else + $ECHO "DNS: Do not adjust" + fi + $ECHO "Firewalling: $FIREWALL" + $ECHO "" + while [ true ] ; do + printf "%s" '>>> Accept these settings and adjust configuration files (y/n)? ' + read ANS + case "ANS" in + Y|y|yes|Yes|oui|Oui) + ANS=y + ;; + N|n|no|No|non|Non) + ANS=n + ;; + esac + if [ "$ANS" = "y" -o "$ANS" = "n" ] ; then + break + fi + done + if [ "$ANS" = "y" ] ; then + break + fi +done + +# Adjust configuration files. First to $CONFIG + +$ECHO "Adjusting $CONFIG" + +copy $CONFIG $CONFIG-bak +if [ "$DNS1" = "server" ] ; then + DNSTYPE=SERVER + DNS1="" + PEERDNS=yes +else + PEERDNS=no + if [ "$DNS1" = "" ] ; then + DNSTYPE=NOCHANGE + else + DNSTYPE=SPECIFY + fi +fi + +# Where is pppd likely to put its pid? +if [ -d /var/run ] ; then + VARRUN=/var/run +else + VARRUN=/etc/ppp +fi + +# Some #$(*& ISP's use a slash in the user name... +sed -e "s&^USER=.*&USER='$U'&" \ + -e "s&^ETH=.*Ð='$E'&" \ + -e "s&^PIDFILE=.*&PIDFILE=\"$VARRUN/\$CF_BASE-pppoe.pid\"&" \ + -e "s/^FIREWALL=.*/FIREWALL=$FIREWALL/" \ + -e "s/^DEMAND=.*/DEMAND=$D/" \ + -e "s/^DNSTYPE=.*/DNSTYPE=$DNSTYPE/" \ + -e "s/^DNS1=.*/DNS1=$DNS1/" \ + -e "s/^DNS2=.*/DNS2=$DNS2/" \ + -e "s/^PEERDNS=.*/PEERDNS=$PEERDNS/" \ + < $CONFIG-bak > $CONFIG + +if [ $? != 0 ] ; then + $ECHO "** Error modifying $CONFIG" + $ECHO "** Quitting" + exit 1 +fi + +if [ "$DNS1" != "" ] ; then + if [ "$DNS1" != "server" ] ; then + $ECHO "Adjusting /etc/resolv.conf" + if [ -r /etc/resolv.conf ] ; then + grep -s "MADE-BY-RP-PPPOE" /etc/resolv.conf > /dev/null 2>&1 + if [ "$?" != 0 ] ; then + $ECHO " (But first backing it up to /etc/resolv.conf-bak)" + copy /etc/resolv.conf /etc/resolv.conf-bak + fi + fi + $ECHO "# MADE-BY-RP-PPPOE" > /etc/resolv.conf + $ECHO "nameserver $DNS1" >> /etc/resolv.conf + if [ "$DNS2" != "" ] ; then + $ECHO "nameserver $DNS2" >> /etc/resolv.conf + fi + fi +fi + +$ECHO "Adjusting /etc/ppp/pap-secrets and /etc/ppp/chap-secrets" +if [ -r /etc/ppp/pap-secrets ] ; then + $ECHO " (But first backing it up to /etc/ppp/pap-secrets-bak)" + copy /etc/ppp/pap-secrets /etc/ppp/pap-secrets-bak +else + cp /dev/null /etc/ppp/pap-secrets-bak +fi +if [ -r /etc/ppp/chap-secrets ] ; then + $ECHO " (But first backing it up to /etc/ppp/chap-secrets-bak)" + copy /etc/ppp/chap-secrets /etc/ppp/chap-secrets-bak +else + cp /dev/null /etc/ppp/chap-secrets-bak +fi + +egrep -v "^$U|^\"$U\"" /etc/ppp/pap-secrets-bak > /etc/ppp/pap-secrets +$ECHO "\"$U\" * \"$PWD1\"" >> /etc/ppp/pap-secrets +egrep -v "^$U|^\"$U\"" /etc/ppp/chap-secrets-bak > /etc/ppp/chap-secrets +$ECHO "\"$U\" * \"$PWD1\"" >> /etc/ppp/chap-secrets + +$ECHO "" +$ECHO "" +$ECHO "" +$ECHO "Congratulations, it should be all set up!" +$ECHO "" +$ECHO "Type 'pppoe-start' to bring up your PPPoE link and 'pppoe-stop' to bring" +$ECHO "it down. Type 'pppoe-status' to see the link status." +exit 0 diff --git a/scripts/pppoe-setup.in b/scripts/pppoe-setup.in new file mode 100755 index 0000000..b378479 --- a/dev/null +++ b/scripts/pppoe-setup.in @@ -0,0 +1,352 @@ +#!/bin/sh +#*********************************************************************** +# +# pppoe-setup +# +# All-purpose slicing/dicing shell script to configure rp-pppoe. +# +# LIC: GPL +# +# Copyright (C) 2000 Roaring Penguin Software Inc. +# +# $Id$ +#*********************************************************************** + +# From AUTOCONF +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +# Paths to programs +IFCONFIG=/sbin/ifconfig +PPPD=@PPPD@ +PPPOE=@sbindir@/pppoe +ECHO=@ECHO@ +LOGGER="/usr/bin/logger -t `basename $0`" + +# Set to "C" locale so we can parse messages from commands +LANG=C +export LANG + +CONFIG=/etc/ppp/pppoe.conf + +# Protect created files +umask 077 + +copy() { + cp $1 $2 + if [ "$?" != 0 ] ; then + $ECHO "*** Error copying $1 to $2" + $ECHO "*** Quitting." + exit 1 + fi +} + +$ECHO "Welcome to the Roaring Penguin PPPoE client setup. First, I will run" +$ECHO "some checks on your system to make sure the PPPoE client is installed" +$ECHO "properly..." +$ECHO "" + +# Must be root +if [ "`@ID@ -u`" != 0 ] ; then + $ECHO "$0: Sorry, you must be root to run this script" + exit 1 +fi + +# Prototype config file must exist +if [ ! -r "$CONFIG" ] ; then + $ECHO "Oh, dear, I don't see the file '$CONFIG' anywhere. Please" + $ECHO "re-install the PPPoE client." + exit 1 +fi + +# Must have pppd +if [ ! -x $PPPD ] ; then + $ECHO "Oops, I can't execute the program '$PPPD'. You" + $ECHO "must install the PPP software suite, version 2.3.10 or later." + exit 1 +fi +export CONFIG +. $CONFIG + +if [ "$DEMAND" = "" ] ; then + DEMAND=no +fi + +# pppoe must exist +if [ ! -x "$PPPOE" ] ; then + $ECHO "Oh, dear, I can't execute the program '$PPPOE'. Please" + $ECHO "re-install the rp-pppoe client." + exit 1 +fi + +$ECHO "Looks good! Now, please enter some information:" + +while [ true ] ; do + $ECHO "" + $ECHO "USER NAME" + $ECHO "" + printf "%s" ">>> Enter your PPPoE user name (default $USER): " + read U + + if [ "$U" = "" ] ; then + U="$USER" + fi + + # Under Linux, "fix" the default interface if eth1 is not available + if test `uname -s` = "Linux" ; then + $IFCONFIG $ETH > /dev/null 2>&1 || ETH=eth0 + fi + $ECHO "" + $ECHO "INTERFACE" + $ECHO "" + $ECHO ">>> Enter the Ethernet interface connected to the DSL modem" + $ECHO "For Solaris, this is likely to be something like /dev/hme0." + $ECHO "For Linux, it will be ethn, where 'n' is a number." + printf "%s" "(default $ETH): " + read E + + if [ "$E" = "" ] ; then + E="$ETH" + fi + + $ECHO "" + $ECHO "Do you want the link to come up on demand, or stay up continuously?" + $ECHO "If you want it to come up on demand, enter the idle time in seconds" + $ECHO "after which the link should be dropped. If you want the link to" + $ECHO "stay up permanently, enter 'no' (two letters, lower-case.)" + $ECHO "NOTE: Demand-activated links do not interact well with dynamic IP" + $ECHO "addresses. You may have some problems with demand-activated links." + printf "%s" ">>> Enter the demand value (default $DEMAND): " + read D + if [ "$D" = "" ] ; then + D=$DEMAND + fi + + $ECHO "" + $ECHO "DNS" + $ECHO "" + $ECHO "Please enter the IP address of your ISP's primary DNS server." + $ECHO "If your ISP claims that 'the server will provide DNS addresses'," + $ECHO "enter 'server' (all lower-case) here." + $ECHO "If you just press enter, I will assume you know what you are" + $ECHO "doing and not modify your DNS setup." + printf "%s" ">>> Enter the DNS information here: " + + read DNS1 + + + if [ "$DNS1" != "" ] ; then + if [ "$DNS1" != "server" ] ; then + $ECHO "Please enter the IP address of your ISP's secondary DNS server." + $ECHO "If you just press enter, I will assume there is only one DNS server." + printf "%s" ">>> Enter the secondary DNS server address here: " + read DNS2 + fi + fi + + while [ true ] ; do + $ECHO "" + $ECHO "PASSWORD" + $ECHO "" + stty -echo + printf "%s" ">>> Please enter your PPPoE password: " + read PWD1 + $ECHO "" + printf "%s" ">>> Please re-enter your PPPoE password: " + read PWD2 + $ECHO "" + stty echo + if [ "$PWD1" = "$PWD2" ] ; then + break + fi + + printf "%s" ">>> Sorry, the passwords do not match. Try again? (y/n)" + read ANS + case "$ANS" in + N|No|NO|Non|n|no|non) + $ECHO "OK, quitting. Bye." + exit 1 + esac + done + + # Firewalling + $ECHO "" + $ECHO "FIREWALLING" + $ECHO "" + if test `uname -s` != "Linux" ; then + $ECHO "Sorry, firewalling is only supported under Linux. Consult" + $ECHO "your operating system manuals for details on setting up" + $ECHO "packet filters for your system." + FIREWALL=NONE + else + $ECHO "Please choose the firewall rules to use. Note that these rules are" + $ECHO "very basic. You are strongly encouraged to use a more sophisticated" + $ECHO "firewall setup; however, these will provide basic security. If you" + $ECHO "are running any servers on your machine, you must choose 'NONE' and" + $ECHO "set up firewalling yourself. Otherwise, the firewall rules will deny" + $ECHO "access to all standard servers like Web, e-mail, ftp, etc. If you" + $ECHO "are using SSH, the rules will block outgoing SSH connections which" + $ECHO "allocate a privileged source port." + $ECHO "" + while [ true ] ; do + $ECHO "The firewall choices are:" + $ECHO "0 - NONE: This script will not set any firewall rules. You are responsible" + $ECHO " for ensuring the security of your machine. You are STRONGLY" + $ECHO " recommended to use some kind of firewall rules." + $ECHO "1 - STANDALONE: Appropriate for a basic stand-alone web-surfing workstation" + $ECHO "2 - MASQUERADE: Appropriate for a machine acting as an Internet gateway" + $ECHO " for a LAN" + printf "%s" ">>> Choose a type of firewall (0-2): " + read a + if [ "$a" = 0 -o "$a" = 1 -o "$a" = 2 ] ; then + break + fi + $ECHO "Please enter a number from 0 to 2" + done + + case "$a" in + 0) + FIREWALL=NONE + ;; + 1) + FIREWALL=STANDALONE + ;; + 2) + FIREWALL=MASQUERADE + ;; + esac + fi + + $ECHO "" + $ECHO "** Summary of what you entered **" + $ECHO "" + $ECHO "Ethernet Interface: $E" + $ECHO "User name: $U" + if [ "$D" = "no" ] ; then + $ECHO "Activate-on-demand: No" + else + $ECHO "Activate-on-demand: Yes; idle timeout = $D seconds" + fi + + if [ "$DNS1" != "" ] ; then + if [ "$DNS1" = "server" ] ; then + $ECHO "DNS addresses: Supplied by ISP's server" + else + $ECHO "Primary DNS: $DNS1" + if [ "$DNS2" != "" ] ; then + $ECHO "Secondary DNS: $DNS2" + fi + fi + else + $ECHO "DNS: Do not adjust" + fi + $ECHO "Firewalling: $FIREWALL" + $ECHO "" + while [ true ] ; do + printf "%s" '>>> Accept these settings and adjust configuration files (y/n)? ' + read ANS + case "ANS" in + Y|y|yes|Yes|oui|Oui) + ANS=y + ;; + N|n|no|No|non|Non) + ANS=n + ;; + esac + if [ "$ANS" = "y" -o "$ANS" = "n" ] ; then + break + fi + done + if [ "$ANS" = "y" ] ; then + break + fi +done + +# Adjust configuration files. First to $CONFIG + +$ECHO "Adjusting $CONFIG" + +copy $CONFIG $CONFIG-bak +if [ "$DNS1" = "server" ] ; then + DNSTYPE=SERVER + DNS1="" + PEERDNS=yes +else + PEERDNS=no + if [ "$DNS1" = "" ] ; then + DNSTYPE=NOCHANGE + else + DNSTYPE=SPECIFY + fi +fi + +# Where is pppd likely to put its pid? +if [ -d /var/run ] ; then + VARRUN=/var/run +else + VARRUN=/etc/ppp +fi + +# Some #$(*& ISP's use a slash in the user name... +sed -e "s&^USER=.*&USER='$U'&" \ + -e "s&^ETH=.*Ð='$E'&" \ + -e "s&^PIDFILE=.*&PIDFILE=\"$VARRUN/\$CF_BASE-pppoe.pid\"&" \ + -e "s/^FIREWALL=.*/FIREWALL=$FIREWALL/" \ + -e "s/^DEMAND=.*/DEMAND=$D/" \ + -e "s/^DNSTYPE=.*/DNSTYPE=$DNSTYPE/" \ + -e "s/^DNS1=.*/DNS1=$DNS1/" \ + -e "s/^DNS2=.*/DNS2=$DNS2/" \ + -e "s/^PEERDNS=.*/PEERDNS=$PEERDNS/" \ + < $CONFIG-bak > $CONFIG + +if [ $? != 0 ] ; then + $ECHO "** Error modifying $CONFIG" + $ECHO "** Quitting" + exit 1 +fi + +if [ "$DNS1" != "" ] ; then + if [ "$DNS1" != "server" ] ; then + $ECHO "Adjusting /etc/resolv.conf" + if [ -r /etc/resolv.conf ] ; then + grep -s "MADE-BY-RP-PPPOE" /etc/resolv.conf > /dev/null 2>&1 + if [ "$?" != 0 ] ; then + $ECHO " (But first backing it up to /etc/resolv.conf-bak)" + copy /etc/resolv.conf /etc/resolv.conf-bak + fi + fi + $ECHO "# MADE-BY-RP-PPPOE" > /etc/resolv.conf + $ECHO "nameserver $DNS1" >> /etc/resolv.conf + if [ "$DNS2" != "" ] ; then + $ECHO "nameserver $DNS2" >> /etc/resolv.conf + fi + fi +fi + +$ECHO "Adjusting /etc/ppp/pap-secrets and /etc/ppp/chap-secrets" +if [ -r /etc/ppp/pap-secrets ] ; then + $ECHO " (But first backing it up to /etc/ppp/pap-secrets-bak)" + copy /etc/ppp/pap-secrets /etc/ppp/pap-secrets-bak +else + cp /dev/null /etc/ppp/pap-secrets-bak +fi +if [ -r /etc/ppp/chap-secrets ] ; then + $ECHO " (But first backing it up to /etc/ppp/chap-secrets-bak)" + copy /etc/ppp/chap-secrets /etc/ppp/chap-secrets-bak +else + cp /dev/null /etc/ppp/chap-secrets-bak +fi + +egrep -v "^$U|^\"$U\"" /etc/ppp/pap-secrets-bak > /etc/ppp/pap-secrets +$ECHO "\"$U\" * \"$PWD1\"" >> /etc/ppp/pap-secrets +egrep -v "^$U|^\"$U\"" /etc/ppp/chap-secrets-bak > /etc/ppp/chap-secrets +$ECHO "\"$U\" * \"$PWD1\"" >> /etc/ppp/chap-secrets + +$ECHO "" +$ECHO "" +$ECHO "" +$ECHO "Congratulations, it should be all set up!" +$ECHO "" +$ECHO "Type 'pppoe-start' to bring up your PPPoE link and 'pppoe-stop' to bring" +$ECHO "it down. Type 'pppoe-status' to see the link status." +exit 0 diff --git a/scripts/pppoe-start b/scripts/pppoe-start new file mode 100755 index 0000000..63501f6 --- a/dev/null +++ b/scripts/pppoe-start @@ -0,0 +1,196 @@ +#!/bin/sh +# ../scripts/pppoe-start. Generated from pppoe-start.in by configure. +#*********************************************************************** +# +# pppoe-start +# +# Shell script to bring up a PPPoE connection +# +# Copyright (C) 2000 Roaring Penguin Software Inc. +# +# $Id$ +# +# This file may be distributed under the terms of the GNU General +# Public License. +# +# LIC: GPL +# +# Usage: pppoe-start [config_file] +# pppoe-start interface user [config_file] +# Second form overrides USER and ETH from config file. +# If config_file is omitted, defaults to /etc/ppp/pppoe.conf +# +#*********************************************************************** + +# From AUTOCONF +prefix=/usr +exec_prefix=${prefix} + +# Paths to programs +CONNECT=${exec_prefix}/sbin/pppoe-connect +ECHO=/bin/echo +IFCONFIG=/sbin/ifconfig + +# Set to "C" locale so we can parse messages from commands +LANG=C +export LANG + +# Defaults +CONFIG=/etc/ppp/pppoe.conf +USER="" +ETH="" +ME=`basename $0` +# Must be root +if [ "`/usr/bin/id -u`" != 0 ] ; then + $ECHO "$ME: You must be root to run this script" >& 2 + exit 1 +fi + +# Debugging +if [ "$DEBUG" = "1" ] ; then + $ECHO "*** Running in debug mode... please be patient..." + DEBUG=/tmp/pppoe-debug-$$ + export DEBUG + mkdir $DEBUG + if [ "$?" != 0 ] ; then + $ECHO "Could not create directory $DEBUG... exiting" + exit 1 + fi + DEBUG=$DEBUG/pppoe-debug.txt + + # Initial debug output + $ECHO "---------------------------------------------" > $DEBUG + $ECHO "* The following section contains information about your system" >> $DEBUG + date >> $DEBUG + $ECHO "Output of uname -a" >> $DEBUG + uname -a >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + $ECHO "* The following section contains information about your network" >> $DEBUG + $ECHO "* interfaces. The one you chose for PPPoE should contain the words:" >> $DEBUG + $ECHO "* 'UP' and 'RUNNING'. If it does not, you probably have an Ethernet" >> $DEBUG + $ECHO "* driver problem." >> $DEBUG + $ECHO "Output of ifconfig -a" >> $DEBUG + $IFCONFIG -a >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + if [ "`uname -s`" = "Linux" ] ; then + $ECHO "* The following section contains information about kernel modules" >> $DEBUG + $ECHO "* If the module for your Ethernet card is 'tulip', you might" >> $DEBUG + $ECHO "* want to look for an updated version at http://www.scyld.com" >> $DEBUG + $ECHO "Output of lsmod" >> $DEBUG + lsmod >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + fi + $ECHO "* The following section lists your routing table." >> $DEBUG + $ECHO "* If you have an entry which starts with '0.0.0.0', you probably" >> $DEBUG + $ECHO "* have defined a default route and gateway, and pppd will" >> $DEBUG + $ECHO "* not create a default route using your ISP. Try getting" >> $DEBUG + $ECHO "* rid of this route." >> $DEBUG + $ECHO "Output of netstat -n -r" >> $DEBUG + netstat -n -r >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + $ECHO "Contents of /etc/resolv.conf" >> $DEBUG + $ECHO "* The following section lists DNS setup." >> $DEBUG + $ECHO "* If you can browse by IP address, but not name, suspect" >> $DEBUG + $ECHO "* a DNS problem." >> $DEBUG + cat /etc/resolv.conf >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + $ECHO "* The following section lists /etc/ppp/options." >> $DEBUG + $ECHO "* You should have NOTHING in that file." >> $DEBUG + $ECHO "Contents of /etc/ppp/options" >> $DEBUG + cat /etc/ppp/options >> $DEBUG 2>/dev/null + $ECHO "---------------------------------------------" >> $DEBUG +else + DEBUG="" +fi + +# Sort out command-line arguments +case "$#" in + 1) + CONFIG="$1" + ;; + 3) + CONFIG="$3" + ;; +esac + +if [ ! -f "$CONFIG" -o ! -r "$CONFIG" ] ; then + $ECHO "$ME: Cannot read configuration file '$CONFIG'" >& 2 + exit 1 +fi +export CONFIG +. $CONFIG + +# Check for command-line overriding of ETH and USER +case "$#" in + 2|3) + ETH="$1" + USER="$2" + ;; +esac + +# Check for pidfile +if [ -r "$PIDFILE" ] ; then + PID=`cat "$PIDFILE"` + # Check if still running + kill -0 $PID > /dev/null 2>&1 + if [ $? = 0 ] ; then + $ECHO "$ME: There already seems to be a PPPoE connection up (PID $PID)" >& 2 + exit 1 + fi + # Delete bogus PIDFILE + rm -f "$PIDFILE" "$PIDFILE.pppd" "$PIDFILE.pppoe" "$PIDFILE.start" +fi + +echo $$ > $PIDFILE.start + +# Start the connection in the background unless we're debugging +if [ "$DEBUG" != "" ] ; then + $CONNECT "$@" + exit 0 +fi + +$CONNECT "$@" > /dev/null 2>&1 & +CONNECT_PID=$! + +if [ "$CONNECT_TIMEOUT" = "" -o "$CONNECT_TIMEOUT" = 0 ] ; then + exit 0 +fi + +# Don't monitor connection if dial-on-demand +if [ "$DEMAND" != "" -a "$DEMAND" != "no" ] ; then + exit 0 +fi + +# Monitor connection +TIME=0 +while [ true ] ; do + ${exec_prefix}/sbin/pppoe-status $CONFIG > /dev/null 2>&1 + + # Looks like the interface came up + if [ $? = 0 ] ; then + # Print newline if standard input is a TTY + tty -s && $ECHO " Connected!" + exit 0 + fi + + if test -n "$FORCEPING" ; then + printf "%s" "$FORCEPING" + else + tty -s && printf "%s" "$PING" + fi + sleep $CONNECT_POLL + TIME=`expr $TIME + $CONNECT_POLL` + if [ $TIME -gt $CONNECT_TIMEOUT ] ; then + break + fi +done + +$ECHO "TIMED OUT" >& 2 +# Timed out! Kill the pppoe-connect process and quit +kill $CONNECT_PID > /dev/null 2>&1 + +# Clean up PIDFILE(s) +rm -f "$PIDFILE" "$PIDFILE.pppd" "$PIDFILE.pppoe" "$PIDFILE.start" + +exit 1 + diff --git a/scripts/pppoe-start.in b/scripts/pppoe-start.in new file mode 100755 index 0000000..7e15804 --- a/dev/null +++ b/scripts/pppoe-start.in @@ -0,0 +1,196 @@ +#!/bin/sh +# @configure_input@ +#*********************************************************************** +# +# pppoe-start +# +# Shell script to bring up a PPPoE connection +# +# Copyright (C) 2000 Roaring Penguin Software Inc. +# +# $Id$ +# +# This file may be distributed under the terms of the GNU General +# Public License. +# +# LIC: GPL +# +# Usage: pppoe-start [config_file] +# pppoe-start interface user [config_file] +# Second form overrides USER and ETH from config file. +# If config_file is omitted, defaults to /etc/ppp/pppoe.conf +# +#*********************************************************************** + +# From AUTOCONF +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +# Paths to programs +CONNECT=@sbindir@/pppoe-connect +ECHO=@ECHO@ +IFCONFIG=/sbin/ifconfig + +# Set to "C" locale so we can parse messages from commands +LANG=C +export LANG + +# Defaults +CONFIG=/etc/ppp/pppoe.conf +USER="" +ETH="" +ME=`basename $0` +# Must be root +if [ "`@ID@ -u`" != 0 ] ; then + $ECHO "$ME: You must be root to run this script" >& 2 + exit 1 +fi + +# Debugging +if [ "$DEBUG" = "1" ] ; then + $ECHO "*** Running in debug mode... please be patient..." + DEBUG=/tmp/pppoe-debug-$$ + export DEBUG + mkdir $DEBUG + if [ "$?" != 0 ] ; then + $ECHO "Could not create directory $DEBUG... exiting" + exit 1 + fi + DEBUG=$DEBUG/pppoe-debug.txt + + # Initial debug output + $ECHO "---------------------------------------------" > $DEBUG + $ECHO "* The following section contains information about your system" >> $DEBUG + date >> $DEBUG + $ECHO "Output of uname -a" >> $DEBUG + uname -a >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + $ECHO "* The following section contains information about your network" >> $DEBUG + $ECHO "* interfaces. The one you chose for PPPoE should contain the words:" >> $DEBUG + $ECHO "* 'UP' and 'RUNNING'. If it does not, you probably have an Ethernet" >> $DEBUG + $ECHO "* driver problem." >> $DEBUG + $ECHO "Output of ifconfig -a" >> $DEBUG + $IFCONFIG -a >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + if [ "`uname -s`" = "Linux" ] ; then + $ECHO "* The following section contains information about kernel modules" >> $DEBUG + $ECHO "* If the module for your Ethernet card is 'tulip', you might" >> $DEBUG + $ECHO "* want to look for an updated version at http://www.scyld.com" >> $DEBUG + $ECHO "Output of lsmod" >> $DEBUG + lsmod >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + fi + $ECHO "* The following section lists your routing table." >> $DEBUG + $ECHO "* If you have an entry which starts with '0.0.0.0', you probably" >> $DEBUG + $ECHO "* have defined a default route and gateway, and pppd will" >> $DEBUG + $ECHO "* not create a default route using your ISP. Try getting" >> $DEBUG + $ECHO "* rid of this route." >> $DEBUG + $ECHO "Output of netstat -n -r" >> $DEBUG + netstat -n -r >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + $ECHO "Contents of /etc/resolv.conf" >> $DEBUG + $ECHO "* The following section lists DNS setup." >> $DEBUG + $ECHO "* If you can browse by IP address, but not name, suspect" >> $DEBUG + $ECHO "* a DNS problem." >> $DEBUG + cat /etc/resolv.conf >> $DEBUG + $ECHO "---------------------------------------------" >> $DEBUG + $ECHO "* The following section lists /etc/ppp/options." >> $DEBUG + $ECHO "* You should have NOTHING in that file." >> $DEBUG + $ECHO "Contents of /etc/ppp/options" >> $DEBUG + cat /etc/ppp/options >> $DEBUG 2>/dev/null + $ECHO "---------------------------------------------" >> $DEBUG +else + DEBUG="" +fi + +# Sort out command-line arguments +case "$#" in + 1) + CONFIG="$1" + ;; + 3) + CONFIG="$3" + ;; +esac + +if [ ! -f "$CONFIG" -o ! -r "$CONFIG" ] ; then + $ECHO "$ME: Cannot read configuration file '$CONFIG'" >& 2 + exit 1 +fi +export CONFIG +. $CONFIG + +# Check for command-line overriding of ETH and USER +case "$#" in + 2|3) + ETH="$1" + USER="$2" + ;; +esac + +# Check for pidfile +if [ -r "$PIDFILE" ] ; then + PID=`cat "$PIDFILE"` + # Check if still running + kill -0 $PID > /dev/null 2>&1 + if [ $? = 0 ] ; then + $ECHO "$ME: There already seems to be a PPPoE connection up (PID $PID)" >& 2 + exit 1 + fi + # Delete bogus PIDFILE + rm -f "$PIDFILE" "$PIDFILE.pppd" "$PIDFILE.pppoe" "$PIDFILE.start" +fi + +echo $$ > $PIDFILE.start + +# Start the connection in the background unless we're debugging +if [ "$DEBUG" != "" ] ; then + $CONNECT "$@" + exit 0 +fi + +$CONNECT "$@" > /dev/null 2>&1 & +CONNECT_PID=$! + +if [ "$CONNECT_TIMEOUT" = "" -o "$CONNECT_TIMEOUT" = 0 ] ; then + exit 0 +fi + +# Don't monitor connection if dial-on-demand +if [ "$DEMAND" != "" -a "$DEMAND" != "no" ] ; then + exit 0 +fi + +# Monitor connection +TIME=0 +while [ true ] ; do + @sbindir@/pppoe-status $CONFIG > /dev/null 2>&1 + + # Looks like the interface came up + if [ $? = 0 ] ; then + # Print newline if standard input is a TTY + tty -s && $ECHO " Connected!" + exit 0 + fi + + if test -n "$FORCEPING" ; then + printf "%s" "$FORCEPING" + else + tty -s && printf "%s" "$PING" + fi + sleep $CONNECT_POLL + TIME=`expr $TIME + $CONNECT_POLL` + if [ $TIME -gt $CONNECT_TIMEOUT ] ; then + break + fi +done + +$ECHO "TIMED OUT" >& 2 +# Timed out! Kill the pppoe-connect process and quit +kill $CONNECT_PID > /dev/null 2>&1 + +# Clean up PIDFILE(s) +rm -f "$PIDFILE" "$PIDFILE.pppd" "$PIDFILE.pppoe" "$PIDFILE.start" + +exit 1 + diff --git a/scripts/pppoe-status b/scripts/pppoe-status new file mode 100755 index 0000000..9fe89cd --- a/dev/null +++ b/scripts/pppoe-status @@ -0,0 +1,84 @@ +#!/bin/sh +#*********************************************************************** +# +# pppoe-status +# +# Shell script to report on status of PPPoE connection +# +# Copyright (C) 2000-2001 Roaring Penguin Software Inc. +# +# $Id$ +# +# This file may be distributed under the terms of the GNU General +# Public License. +# +# LIC: GPL +# +# Usage: pppoe-status [config_file] +# If config_file is omitted, defaults to /etc/ppp/pppoe.conf +# +#*********************************************************************** + +# Defaults +CONFIG=/etc/ppp/pppoe.conf + +case "$#" in + 1) + CONFIG="$1" + ;; +esac + +if [ ! -f "$CONFIG" -o ! -r "$CONFIG" ] ; then + echo "$0: Cannot read configuration file '$CONFIG'" >& 2 + exit 1 +fi + +. $CONFIG + +PPPOE_PIDFILE="$PIDFILE.pppoe" +PPPD_PIDFILE="$PIDFILE.pppd" + +if [ "$DEMAND" != "no" ] ; then + echo "Note: You have enabled demand-connection; pppoe-status may be inaccurate." +fi + +# If no PPPOE_PIDFILE, connection is down, unless we're using the Linux plugin +if [ "$LINUX_PLUGIN" = "" ] ; then + if [ ! -r "$PPPOE_PIDFILE" ] ; then + echo "pppoe-status: Link is down (can't read pppoe PID file $PPPOE_PIDFILE)" + exit 1 + fi +fi + +# If no PPPD_PIDFILE, something fishy! +if [ ! -r "$PPPD_PIDFILE" ] ; then + echo "pppoe-status: Link is down (can't read pppd PID file $PPPD_PIDFILE)" + exit 1 +fi + +PPPD_PID=`cat "$PPPD_PIDFILE"` + +# Sigh. Some versions of pppd put PID files in /var/run; others put them +# in /etc/ppp. Since it's too messy to figure out what pppd does, we +# try both locations. +for i in /etc/ppp/ppp*.pid /var/run/ppp*.pid ; do + if [ -r $i ] ; then + PID=`cat $i` + if [ "$PID" = "$PPPD_PID" ] ; then + IF=`basename $i .pid` + netstat -rn | grep " ${IF}\$" > /dev/null + # /sbin/ifconfig $IF | grep "UP.*POINTOPOINT" > /dev/null + if [ "$?" != "0" ] ; then + echo "pppoe-status: Link is attached to $IF, but $IF is down" + exit 1 + fi + echo "pppoe-status: Link is up and running on interface $IF" + /sbin/ifconfig $IF + exit 0 + fi + fi +done + +echo "pppoe-status: Link is down -- could not find interface corresponding to" +echo "pppd pid $PPPD_PID" +exit 1 \ No newline at end of file diff --git a/scripts/pppoe-stop b/scripts/pppoe-stop new file mode 100755 index 0000000..5880377 --- a/dev/null +++ b/scripts/pppoe-stop @@ -0,0 +1,96 @@ +#!/bin/sh +# ../scripts/pppoe-stop. Generated from pppoe-stop.in by configure. +#*********************************************************************** +# +# pppoe-stop +# +# Shell script to bring down a PPPoE connection +# +# Copyright (C) 2000 Roaring Penguin Software Inc. +# +# $Id$ +# +# This file may be distributed under the terms of the GNU General +# Public License. +# +# LIC: GPL +# +# Usage: pppoe-stop [config_file] +# If config_file is omitted, defaults to /etc/ppp/pppoe.conf +# +#*********************************************************************** + +# Set to "C" locale so we can parse messages from commands +LANG=C +export LANG + +ME="`basename $0`" +LOGGER="/usr/bin/logger -t $ME" +CONFIG="$1" +if [ "$CONFIG" = "" ] ; then + CONFIG=/etc/ppp/pppoe.conf +fi + +if [ ! -f "$CONFIG" -o ! -r "$CONFIG" ] ; then + echo "$ME: Cannot read configuration file '$CONFIG'" >& 2 + exit 1 +fi +export CONFIG +. $CONFIG + +PPPOE_PIDFILE="$PIDFILE.pppoe" +PPPD_PIDFILE="$PIDFILE.pppd" +STARTPID="$PIDFILE.start" + +# Backward config file compatibility +if test "$DEMAND" = "" ; then + DEMAND=no +fi + +# Ignore SIGTERM +trap "" 15 + +# Check for pidfile +if [ -r "$PIDFILE" ] ; then + PID=`cat $PIDFILE` + + # Check if still running + kill -0 $PID > /dev/null 2>&1 + if [ $? != 0 ] ; then + echo "$ME: The pppoe-connect script (PID $PID) appears to have died" >& 2 + fi + + # Kill pppd, which should in turn kill pppoe + if [ -r "$PPPD_PIDFILE" ] ; then + PPPD_PID=`cat "$PPPD_PIDFILE"` + $LOGGER -p daemon.notice "Killing pppd" + echo "Killing pppd ($PPPD_PID)" + kill $PPPD_PID > /dev/null 2>&1 || exit 1 + fi + + # Kill pppoe-start + PIDS=`cat $STARTPID` + kill -0 $PIDS > /dev/null 2>&1 + if [ $? = 0 ] ; then + $LOGGER -p daemon.notice "Killing pppoe-connect" + kill $PIDS > /dev/null 2>&1 + fi + + # Kill pppoe-connect + $LOGGER -p daemon.notice "Killing pppoe-connect" + echo "Killing pppoe-connect ($PID)" + kill -9 $PID > /dev/null 2>&1 + + # Kill pppd again, in case it's still hanging around + if [ -r "$PPPD_PIDFILE" ] ; then + PPPD_PID=`cat "$PPPD_PIDFILE"` + kill -9 $PPPD_PID > /dev/null 2>&1 || exit 1 + fi + + rm -f "$PIDFILE" "$PPPD_PIDFILE" "$PPPOE_PIDFILE" "$STARTPID" +else + echo "$ME: No PPPoE connection appears to be running" >&2 + exit 1 +fi + +exit 0 diff --git a/scripts/pppoe-stop.in b/scripts/pppoe-stop.in new file mode 100755 index 0000000..1ba8756 --- a/dev/null +++ b/scripts/pppoe-stop.in @@ -0,0 +1,96 @@ +#!/bin/sh +# @configure_input@ +#*********************************************************************** +# +# pppoe-stop +# +# Shell script to bring down a PPPoE connection +# +# Copyright (C) 2000 Roaring Penguin Software Inc. +# +# $Id$ +# +# This file may be distributed under the terms of the GNU General +# Public License. +# +# LIC: GPL +# +# Usage: pppoe-stop [config_file] +# If config_file is omitted, defaults to /etc/ppp/pppoe.conf +# +#*********************************************************************** + +# Set to "C" locale so we can parse messages from commands +LANG=C +export LANG + +ME="`basename $0`" +LOGGER="/usr/bin/logger -t $ME" +CONFIG="$1" +if [ "$CONFIG" = "" ] ; then + CONFIG=/etc/ppp/pppoe.conf +fi + +if [ ! -f "$CONFIG" -o ! -r "$CONFIG" ] ; then + echo "$ME: Cannot read configuration file '$CONFIG'" >& 2 + exit 1 +fi +export CONFIG +. $CONFIG + +PPPOE_PIDFILE="$PIDFILE.pppoe" +PPPD_PIDFILE="$PIDFILE.pppd" +STARTPID="$PIDFILE.start" + +# Backward config file compatibility +if test "$DEMAND" = "" ; then + DEMAND=no +fi + +# Ignore SIGTERM +trap "" 15 + +# Check for pidfile +if [ -r "$PIDFILE" ] ; then + PID=`cat $PIDFILE` + + # Check if still running + kill -0 $PID > /dev/null 2>&1 + if [ $? != 0 ] ; then + echo "$ME: The pppoe-connect script (PID $PID) appears to have died" >& 2 + fi + + # Kill pppd, which should in turn kill pppoe + if [ -r "$PPPD_PIDFILE" ] ; then + PPPD_PID=`cat "$PPPD_PIDFILE"` + $LOGGER -p daemon.notice "Killing pppd" + echo "Killing pppd ($PPPD_PID)" + kill $PPPD_PID > /dev/null 2>&1 || exit 1 + fi + + # Kill pppoe-start + PIDS=`cat $STARTPID` + kill -0 $PIDS > /dev/null 2>&1 + if [ $? = 0 ] ; then + $LOGGER -p daemon.notice "Killing pppoe-connect" + kill $PIDS > /dev/null 2>&1 + fi + + # Kill pppoe-connect + $LOGGER -p daemon.notice "Killing pppoe-connect" + echo "Killing pppoe-connect ($PID)" + kill -9 $PID > /dev/null 2>&1 + + # Kill pppd again, in case it's still hanging around + if [ -r "$PPPD_PIDFILE" ] ; then + PPPD_PID=`cat "$PPPD_PIDFILE"` + kill -9 $PPPD_PID > /dev/null 2>&1 || exit 1 + fi + + rm -f "$PIDFILE" "$PPPD_PIDFILE" "$PPPOE_PIDFILE" "$STARTPID" +else + echo "$ME: No PPPoE connection appears to be running" >&2 + exit 1 +fi + +exit 0 diff --git a/src/common.c b/src/common.c new file mode 100755 index 0000000..b8c53fd --- a/dev/null +++ b/src/common.c @@ -0,0 +1,651 @@ +/*********************************************************************** +* +* common.c +* +* Implementation of user-space PPPoE redirector for Linux. +* +* Common functions used by PPPoE client and server +* +* Copyright (C) 2000 by Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; +/* For vsnprintf prototype */ +#define _ISOC99_SOURCE 1 + +/* For seteuid prototype */ +#define _BSD_SOURCE 1 + +#include "pppoe.h" + + +#ifdef HAVE_SYSLOG_H +#include +#include +#define syslog(prio, fmt...) \ + __android_log_print(prio, "PPPOE", fmt) +#endif + +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +/* Are we running SUID or SGID? */ +int IsSetID = 0; + +static uid_t saved_uid = -2; +static uid_t saved_gid = -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 (packet->ver != 1) { + syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver); + return -1; + } + if (packet->type != 1) { + syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type); + return -1; + } + + /* Do some sanity checks on packet */ + if (len > ETH_DATA_LEN - 6) { /* 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 < 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 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 (packet->ver != 1) { + syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver); + return NULL; + } + if (packet->type != 1) { + syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type); + return NULL; + } + + /* Do some sanity checks on packet */ + if (len > ETH_DATA_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 < 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 ((int)saved_uid < 0) saved_uid = geteuid(); + if ((int)saved_gid < 0) 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) { + setegid(getgid()); + seteuid(getuid()); + } +} + +/********************************************************************** +*%FUNCTION: printErr +*%ARGUMENTS: +* str -- error message +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Prints a message to stderr and syslog. +***********************************************************************/ +void +printErr(char const *str) +{ + fprintf(stderr, "pppoe: %s\n", str); + syslog(LOG_ERR, "%s", str); +} + + +/********************************************************************** +*%FUNCTION: strDup +*%ARGUMENTS: +* str -- string to copy +*%RETURNS: +* A malloc'd copy of str. Exits if malloc fails. +***********************************************************************/ +char * +strDup(char const *str) +{ + char *copy = malloc(strlen(str)+1); + if (!copy) { + rp_fatal("strdup failed"); + } + strcpy(copy, str); + return copy; +} + +/********************************************************************** +*%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 <= (unsigned int)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.ver = 1; + packet.type = 1; + packet.code = CODE_PADT; + packet.session = conn->session; + + /* Reset Session to zero so there is no possibility of + recursive calls to this function by any signal handler */ + conn->session = 0; + + /* If we're using Host-Uniq, copy it over */ + if (conn->useHostUniq) { + PPPoETag hostUniq; + pid_t pid = getpid(); + hostUniq.type = htons(TAG_HOST_UNIQ); + hostUniq.length = htons(sizeof(pid)); + memcpy(hostUniq.payload, &pid, sizeof(pid)); + memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); + cursor += sizeof(pid) + TAG_HDR_SIZE; + plen += sizeof(pid) + TAG_HDR_SIZE; + } + + /* Copy error message */ + if (msg) { + PPPoETag err; + size_t elen = strlen(msg); + err.type = htons(TAG_GENERIC_ERROR); + err.length = htons(elen); + strcpy((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; + default: + str = "Generic-Error"; + } + + 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); +} diff --git a/src/config.h b/src/config.h new file mode 100755 index 0000000..b75a298 --- a/dev/null +++ b/src/config.h @@ -0,0 +1,146 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated automatically from configure.in by autoheader. */ +/* LIC: GPL */ + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if the setvbuf function takes the buffering type as its second + argument and the buffer pointer as the third, as on System V + before release 3. */ +/* #undef SETVBUF_REVERSED */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +#define HAVE_STRUCT_SOCKADDR_LL 1 + +/* The number of bytes in a unsigned int. */ +#define SIZEOF_UNSIGNED_INT 4 + +/* The number of bytes in a unsigned long. */ +#define SIZEOF_UNSIGNED_LONG 4 + +/* The number of bytes in a unsigned short. */ +#define SIZEOF_UNSIGNED_SHORT 2 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strtol function. */ +#define HAVE_STRTOL 1 + +/* Define if you have the header file. */ +#define HAVE_ASM_TYPES_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_IF_ETHER_H 1 + +/* Define if you have kernel-mode PPPoE in Linux file. */ +/* #undef HAVE_LINUX_KERNEL_PPPOE */ + +/* Define if you have the header file. */ +#define HAVE_LINUX_IF_PACKET_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_IF_PPPOX_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_NET_BPF_H */ + +/* Define if you have the header file. */ +#define HAVE_NET_IF_ARP_H 1 + +/* Define if you have the header file. */ +#define HAVE_NET_ETHERNET_H 1 + +/* Define if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_IF_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_NET_IF_DL_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NET_IF_ETHER_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NET_IF_TYPES_H */ + +/* Define if you have the header file. */ +#define HAVE_NETINET_IF_ETHER_H 1 + +/* Define if you have the header file. */ +#define HAVE_NETPACKET_PACKET_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_CDEFS_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_DLPI_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#ifndef HAVE_SYS_UIO_H +#define HAVE_SYS_UIO_H 1 +#endif + +/* Define if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the N_HDLC line discipline in linux/termios.h */ +/* #undef HAVE_N_HDLC */ + +/* Define if bitfields are packed in reverse order */ +#define PACK_BITFIELDS_REVERSED 1 + +/* Define to include debugging code */ +#define DEBUGGING_ENABLED 1 + +/* Solaris moans if we don't do this... */ +#ifdef __sun +#define __EXTENSIONS__ 1 +#endif diff --git a/src/debug.c b/src/debug.c new file mode 100755 index 0000000..b606272 --- a/dev/null +++ b/src/debug.c @@ -0,0 +1,152 @@ +/*********************************************************************** +* +* debug.c +* +* Implementation of user-space PPPoE redirector for Linux. +* +* Functions for printing debugging information +* +* Copyright (C) 2000 by Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef DEBUGGING_ENABLED + +#include +#include +#include +#include + +/********************************************************************** +*%FUNCTION: dumpHex +*%ARGUMENTS: +* fp -- file to dump to +* buf -- buffer to dump +* len -- length of data +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Dumps buffer to fp in an easy-to-read format +***********************************************************************/ +void +dumpHex(FILE *fp, unsigned char const *buf, int len) +{ + int i; + int base; + + if (!fp) return; + + /* do NOT dump PAP packets */ + if (len >= 2 && buf[0] == 0xC0 && buf[1] == 0x23) { + fprintf(fp, "(PAP Authentication Frame -- Contents not dumped)\n"); + return; + } + + for (base=0; baselength); + + /* Sheesh... printing times is a pain... */ + struct timeval tv; + time_t now; + int millisec; + struct tm *lt; + char timebuf[256]; + + UINT16_t type = etherType(packet); + if (!fp) return; + gettimeofday(&tv, NULL); + now = (time_t) tv.tv_sec; + millisec = tv.tv_usec / 1000; + lt = localtime(&now); + strftime(timebuf, 256, "%H:%M:%S", lt); + fprintf(fp, "%s.%03d %s PPPoE ", timebuf, millisec, dir); + if (type == Eth_PPPOE_Discovery) { + fprintf(fp, "Discovery (%x) ", (unsigned) type); + } else if (type == Eth_PPPOE_Session) { + fprintf(fp, "Session (%x) ", (unsigned) type); + } else { + fprintf(fp, "Unknown (%x) ", (unsigned) type); + } + + switch(packet->code) { + case CODE_PADI: fprintf(fp, "PADI "); break; + case CODE_PADO: fprintf(fp, "PADO "); break; + case CODE_PADR: fprintf(fp, "PADR "); break; + case CODE_PADS: fprintf(fp, "PADS "); break; + case CODE_PADT: fprintf(fp, "PADT "); break; + case CODE_PADM: fprintf(fp, "PADM "); break; + case CODE_PADN: fprintf(fp, "PADN "); break; + case CODE_SESS: fprintf(fp, "SESS "); break; + } + + fprintf(fp, "sess-id %d length %d\n", + (int) ntohs(packet->session), + len); + + /* Ugly... I apologize... */ + fprintf(fp, + "SourceAddr %02x:%02x:%02x:%02x:%02x:%02x " + "DestAddr %02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned) packet->ethHdr.h_source[0], + (unsigned) packet->ethHdr.h_source[1], + (unsigned) packet->ethHdr.h_source[2], + (unsigned) packet->ethHdr.h_source[3], + (unsigned) packet->ethHdr.h_source[4], + (unsigned) packet->ethHdr.h_source[5], + (unsigned) packet->ethHdr.h_dest[0], + (unsigned) packet->ethHdr.h_dest[1], + (unsigned) packet->ethHdr.h_dest[2], + (unsigned) packet->ethHdr.h_dest[3], + (unsigned) packet->ethHdr.h_dest[4], + (unsigned) packet->ethHdr.h_dest[5]); + dumpHex(fp, packet->payload, ntohs(packet->length)); +} + +#endif /* DEBUGGING_ENABLED */ diff --git a/src/discovery.c b/src/discovery.c new file mode 100755 index 0000000..c92ceef --- a/dev/null +++ b/src/discovery.c @@ -0,0 +1,736 @@ +/*********************************************************************** +* +* discovery.c +* +* Perform PPPoE discovery +* +* Copyright (C) 1999 by Roaring Penguin Software Inc. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_SYSLOG_H +#include +#include +#define syslog(prio, fmt...) \ + __android_log_print(prio, "PPPOE", fmt) +#endif + +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include + +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef USE_LINUX_PACKET +#include +#include +#endif + +#include + +/* Supplied by pppd if we're a plugin */ +extern int persist; + +/********************************************************************** +*%FUNCTION: parseForHostUniq +*%ARGUMENTS: +* type -- tag type +* len -- tag length +* data -- tag data. +* extra -- user-supplied pointer. This is assumed to be a pointer to int. +*%RETURNS: +* Nothing +*%DESCRIPTION: +* If a HostUnique tag is found which matches our PID, sets *extra to 1. +***********************************************************************/ +static void +parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + int *val = (int *) extra; + if (type == TAG_HOST_UNIQ && len == sizeof(pid_t)) { + pid_t tmp; + memcpy(&tmp, data, len); + if (tmp == getpid()) { + *val = 1; + } + } +} + +/********************************************************************** +*%FUNCTION: packetIsForMe +*%ARGUMENTS: +* conn -- PPPoE connection info +* packet -- a received PPPoE packet +*%RETURNS: +* 1 if packet is for this PPPoE daemon; 0 otherwise. +*%DESCRIPTION: +* If we are using the Host-Unique tag, verifies that packet contains +* our unique identifier. +***********************************************************************/ +static int +packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) +{ + int forMe = 0; + + /* If packet is not directed to our MAC address, forget it */ + if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; + + /* If we're not using the Host-Unique tag, then accept the packet */ + if (!conn->useHostUniq) return 1; + + parsePacket(packet, parseForHostUniq, &forMe); + return forMe; +} + +/********************************************************************** +*%FUNCTION: parsePADOTags +*%ARGUMENTS: +* type -- tag type +* len -- tag length +* data -- tag data +* extra -- extra user data. Should point to a PacketCriteria structure +* which gets filled in according to selected AC name and service +* name. +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Picks interesting tags out of a PADO packet +***********************************************************************/ +static void +parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + struct PacketCriteria *pc = (struct PacketCriteria *) extra; + PPPoEConnection *conn = pc->conn; + int i; + + switch(type) { + case TAG_AC_NAME: + pc->seenACName = 1; + if (conn->printACNames) { + 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); + exit(1); + } + 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); + exit(1); + } + 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); + exit(1); + } + break; + } +} + +/********************************************************************** +*%FUNCTION: parsePADSTags +*%ARGUMENTS: +* type -- tag type +* len -- tag length +* data -- tag data +* extra -- extra user data (pointer to PPPoEConnection structure) +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Picks interesting tags out of a PADS packet +***********************************************************************/ +static void +parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + PPPoEConnection *conn = (PPPoEConnection *) extra; + switch(type) { + case TAG_SERVICE_NAME: + 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; + } +} + +/*********************************************************************** +*%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.ver = 1; + packet.type = 1; + packet.code = CODE_PADI; + packet.session = 0; + + if (!omit_service_name) { + plen = TAG_HDR_SIZE + namelen; + CHECK_ROOM(cursor, packet.payload, plen); + + svc->type = TAG_SERVICE_NAME; + svc->length = htons(namelen); + + if (conn->serviceName) { + memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); + } + cursor += namelen + TAG_HDR_SIZE; + } else { + plen = 0; + } + + /* If we're using Host-Uniq, copy it over */ + if (conn->useHostUniq) { + PPPoETag hostUniq; + pid_t pid = getpid(); + hostUniq.type = htons(TAG_HOST_UNIQ); + hostUniq.length = htons(sizeof(pid)); + memcpy(hostUniq.payload, &pid, sizeof(pid)); + CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE); + memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); + cursor += sizeof(pid) + TAG_HDR_SIZE; + plen += sizeof(pid) + TAG_HDR_SIZE; + } + + packet.length = htons(plen); + + sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); +#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; + struct timeval now; + + PPPoEPacket packet; + int len; + + struct PacketCriteria pc; + pc.conn = conn; + pc.acNameOK = (conn->acName) ? 0 : 1; + pc.serviceNameOK = (conn->serviceName) ? 0 : 1; + pc.seenACName = 0; + pc.seenServiceName = 0; + + if (gettimeofday(&expire_at, NULL) < 0) { + fatalSys("gettimeofday (waitForPADO)"); + } + expire_at.tv_sec += timeout; + + do { + if (BPF_BUFFER_IS_EMPTY) { + if (gettimeofday(&now, NULL) < 0) { + fatalSys("gettimeofday (waitForPADO)"); + } + 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; + } + } + if (tv.tv_sec <= 0 && tv.tv_usec <= 0) { + /* 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) { + syslog( LOG_INFO, "waitForPADO: (r >= 0 || errno != EINTR)\n"); + 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 > (unsigned int)len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + continue; + } + +#ifdef USE_BPF + /* If it's not a Discovery packet, loop again */ + if (etherType(&packet) != Eth_PPPOE_Discovery) continue; +#endif + +#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 (NOT_UNICAST(packet.ethHdr.h_source)) { + printErr("Ignoring PADO packet from non-unicast MAC address"); + continue; + } + parsePacket(&packet, parsePADOTags, &pc); + 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]); + printf("--------------------------------------------------\n"); + 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.ver = 1; + packet.type = 1; + packet.code = CODE_PADR; + packet.session = 0; + + svc->type = TAG_SERVICE_NAME; + svc->length = htons(namelen); + if (conn->serviceName) { + memcpy(svc->payload, conn->serviceName, namelen); + } + cursor += namelen + TAG_HDR_SIZE; + + /* If we're using Host-Uniq, copy it over */ + if (conn->useHostUniq) { + PPPoETag hostUniq; + pid_t pid = getpid(); + hostUniq.type = htons(TAG_HOST_UNIQ); + hostUniq.length = htons(sizeof(pid)); + memcpy(hostUniq.payload, &pid, sizeof(pid)); + CHECK_ROOM(cursor, packet.payload, sizeof(pid)+TAG_HDR_SIZE); + memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); + cursor += sizeof(pid) + TAG_HDR_SIZE; + plen += sizeof(pid) + TAG_HDR_SIZE; + } + + /* Copy cookie and relay-ID if needed */ + if (conn->cookie.type) { + CHECK_ROOM(cursor, packet.payload, + ntohs(conn->cookie.length) + TAG_HDR_SIZE); + memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); + cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; + plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; + } + + if (conn->relayId.type) { + CHECK_ROOM(cursor, packet.payload, + ntohs(conn->relayId.length) + TAG_HDR_SIZE); + memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); + cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; + plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; + } + + packet.length = htons(plen); + sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); +#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; + struct timeval now; + + PPPoEPacket packet; + int len; + + if (gettimeofday(&expire_at, NULL) < 0) { + fatalSys("gettimeofday (waitForPADS)"); + } + expire_at.tv_sec += timeout; + + do { + if (BPF_BUFFER_IS_EMPTY) { + if (gettimeofday(&now, NULL) < 0) { + fatalSys("gettimeofday (waitForPADS)"); + } + 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; + } + } + if (tv.tv_sec <= 0 && tv.tv_usec <= 0) { + /* 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 > (unsigned int)len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + continue; + } + +#ifdef USE_BPF + /* If it's not a Discovery packet, loop again */ + if (etherType(&packet) != Eth_PPPOE_Discovery) continue; +#endif +#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 +***********************************************************************/ +static struct timeval current_time; + +static int g_padi_sent = 0; +void +discovery(PPPoEConnection *conn) +{ + int padiAttempts = 0; + int padrAttempts = 0; + int timeout = conn->discoveryTimeout; + + fprintf(stderr, "discovery: timeout = %d\n", timeout); + + /* Skip discovery? */ + if (conn->skipDiscovery) { + conn->discoveryState = STATE_SESSION; + if (conn->killSession) { + sendPADT(conn, "RP-PPPoE: Session killed manually"); + exit(0); + } + return; + } + + do { + padiAttempts++; + if (padiAttempts > MAX_PADI_ATTEMPTS) { + if (persist) { + padiAttempts = 0; + timeout = conn->discoveryTimeout; + printErr("Timeout waiting for PADO packets"); + } else { + rp_fatal("Timeout waiting for PADO packets"); + } + } + fprintf(stderr, "sendPADI\n"); + syslog( LOG_INFO, "sendPADI\n"); + sendPADI(conn); + + conn->discoveryState = STATE_SENT_PADI; + gettimeofday(¤t_time, NULL); + fprintf(stderr, "before: sec = %d, usec = %d\n", (int)current_time.tv_sec, (int)current_time.tv_usec); + waitForPADO(conn, timeout); + gettimeofday(¤t_time, NULL); + fprintf(stderr, "after: sec = %d, usec = %d\n", (int)current_time.tv_sec, (int)current_time.tv_usec); + /* 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) { + exit(0); + } + + timeout = conn->discoveryTimeout; + do { + padrAttempts++; + if (padrAttempts > MAX_PADI_ATTEMPTS) { + if (persist) { + padrAttempts = 0; + timeout = conn->discoveryTimeout; + printErr("Timeout waiting for PADS packets"); + } else { + rp_fatal("Timeout waiting for PADS packets"); + } + } + fprintf(stderr, "sendPADR\n"); + syslog( LOG_INFO, "sendPADR\n"); + sendPADR(conn); + conn->discoveryState = STATE_SENT_PADR; + waitForPADS(conn, timeout); + timeout *= 2; + } while (conn->discoveryState == STATE_SENT_PADR); + + /* We're done. */ + conn->discoveryState = STATE_SESSION; + return; +} diff --git a/src/if.c b/src/if.c new file mode 100755 index 0000000..d076b69 --- a/dev/null +++ b/src/if.c @@ -0,0 +1,352 @@ +/*********************************************************************** +* +* if.c +* +* Implementation of user-space PPPoE redirector for Linux. +* +* Functions for opening a raw socket and reading/writing raw Ethernet frames. +* +* Copyright (C) 2000 by Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +#include +#elif defined(HAVE_LINUX_IF_PACKET_H) +#include +#endif + +#ifdef HAVE_NET_ETHERNET_H +//#include +#endif + +#ifdef HAVE_ASM_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYSLOG_H +#include +#include +#define syslog(prio, fmt...) \ + __android_log_print(prio, "PPPOE", fmt) +#endif + +#include +#include +#include + +#ifdef HAVE_NET_IF_ARP_H +#include +#endif + +#ifdef USE_DLPI + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* function declarations */ + +static void dlpromisconreq( int fd, u_long level); +void dlinforeq(int fd); +void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen); +void dlinfoack(int fd, char *bufp); +void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest); +void dlattachreq(int fd, u_long ppa); +void dlokack(int fd, char *bufp); +void dlbindack(int fd, char *bufp); +int strioctl(int fd, int cmd, int timout, int len, char *dp); +void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller); +void sigalrm(int sig); +void expecting(int prim, union DL_primitives *dlp); +static char *dlprim(u_long prim); + +/* #define DL_DEBUG */ + +static int dl_abssaplen; +static int dl_saplen; +static int dl_addrlen; + +#endif + +#ifdef USE_BPF +#include +#include + +static unsigned char *bpfBuffer; /* Packet filter buffer */ +static int bpfLength = 0; /* Packet filter buffer length */ + int bpfSize = 0; /* Number of unread bytes in buffer */ +static int bpfOffset = 0; /* Current offset in bpfBuffer */ +#endif + +/* Initialize frame types to RFC 2516 values. Some broken peers apparently + use different frame types... sigh... */ + +UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; +UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; + +/********************************************************************** +*%FUNCTION: etherType +*%ARGUMENTS: +* packet -- a received PPPoE packet +*%RETURNS: +* ethernet packet type (see /usr/include/net/ethertypes.h) +*%DESCRIPTION: +* Checks the ethernet packet header to determine its type. +* We should only be receveing DISCOVERY and SESSION types if the BPF +* is set up correctly. Logs an error if an unexpected type is received. +* Note that the ethernet type names come from "pppoe.h" and the packet +* packet structure names use the LINUX dialect to maintain consistency +* with the rest of this file. See the BSD section of "pppoe.h" for +* translations of the data structure names. +***********************************************************************/ +UINT16_t +etherType(PPPoEPacket *packet) +{ + UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); + if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { + syslog(LOG_ERR, "Invalid ether type 0x%x", type); + } + return type; +} + + +#ifdef USE_LINUX_PACKET +/********************************************************************** +*%FUNCTION: openInterface +*%ARGUMENTS: +* ifname -- name of interface +* type -- Ethernet frame type +* hwaddr -- if non-NULL, set to the hardware address +*%RETURNS: +* A raw socket for talking to the Ethernet card. Exits on error. +*%DESCRIPTION: +* Opens a raw Ethernet socket +***********************************************************************/ +int +openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) +{ + int optval=1; + int fd; + struct ifreq ifr; + int domain, stype; + + struct sockaddr_ll sa; + + memset(&sa, 0, sizeof(sa)); + + domain = PF_PACKET; + stype = SOCK_RAW; + + if ((fd = socket(domain, stype, htons(type))) < 0) { + /* Give a more helpful message for the common error case */ + if (errno == EPERM) { + rp_fatal("Cannot create raw socket -- pppoe must be run as root."); + } + fatalSys("socket"); + } + + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { + fatalSys("setsockopt"); + } + + /* Fill in hardware address */ + if (hwaddr) { + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + fatalSys("ioctl(SIOCGIFHWADDR)"); + } + memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + if (NOT_UNICAST(hwaddr)) { + char buffer[256]; + sprintf(buffer, + "Interface %.16s has broadcast/multicast MAC address??", + ifname); + rp_fatal(buffer); + } + } + + /* Sanity check on MTU */ + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { + fatalSys("ioctl(SIOCGIFMTU)"); + } + if (ifr.ifr_mtu < ETH_DATA_LEN) { + char buffer[256]; + sprintf(buffer, "Interface %.16s has MTU of %d -- should be %d. You may have serious connection problems.", + ifname, ifr.ifr_mtu, ETH_DATA_LEN); + printErr(buffer); + } + + /* Get interface index */ + sa.sll_family = AF_PACKET; + sa.sll_protocol = htons(type); + + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { + fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index"); + } + sa.sll_ifindex = ifr.ifr_ifindex; + + /* We're only interested in packets on specified interface */ + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + fatalSys("bind"); + } + + return fd; +} + +#endif /* USE_LINUX */ + +/*********************************************************************** +*%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 (send(sock, pkt, size, 0) < 0 && (errno != ENOBUFS)) { + sysErr("send (sendPacket)"); + return -1; + } + return 0; +} + +#ifdef USE_BPF +/*********************************************************************** +*%FUNCTION: clearPacketHeader +*%ARGUMENTS: +* pkt -- packet that needs its head clearing +*%RETURNS: +* nothing +*%DESCRIPTION: +* Clears a PPPoE packet header after a truncated packet has been +* received. Insures that the packet will fail any integrity tests +* and will be discarded by upper level routines. Also resets the +* bpfSize and bpfOffset variables to force a new read on the next +* call to receivePacket(). +***********************************************************************/ +void +clearPacketHeader(PPPoEPacket *pkt) +{ + bpfSize = bpfOffset = 0; + memset(pkt, 0, HDR_SIZE); +} +#endif + +/*********************************************************************** +*%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) +{ +#ifdef USE_BPF + struct bpf_hdr hdr; + int seglen, copylen; + + if (bpfSize <= 0) { + bpfOffset = 0; + if ((bpfSize = read(sock, bpfBuffer, bpfLength)) < 0) { + sysErr("read (receivePacket)"); + return -1; + } + } + if (bpfSize < sizeof(hdr)) { + syslog(LOG_ERR, "Truncated bpf packet header: len=%d", bpfSize); + clearPacketHeader(pkt); /* resets bpfSize and bpfOffset */ + return 0; + } + memcpy(&hdr, bpfBuffer + bpfOffset, sizeof(hdr)); + if (hdr.bh_caplen != hdr.bh_datalen) { + syslog(LOG_ERR, "Truncated bpf packet: caplen=%d, datalen=%d", + hdr.bh_caplen, hdr.bh_datalen); + clearPacketHeader(pkt); /* resets bpfSize and bpfOffset */ + return 0; + } + seglen = hdr.bh_hdrlen + hdr.bh_caplen; + if (seglen > bpfSize) { + syslog(LOG_ERR, "Truncated bpf packet: seglen=%d, bpfSize=%d", + seglen, bpfSize); + clearPacketHeader(pkt); /* resets bpfSize and bpfOffset */ + return 0; + } + seglen = BPF_WORDALIGN(seglen); + *size = copylen = ((hdr.bh_caplen < sizeof(PPPoEPacket)) ? + hdr.bh_caplen : sizeof(PPPoEPacket)); + memcpy(pkt, bpfBuffer + bpfOffset + hdr.bh_hdrlen, copylen); + if (seglen >= bpfSize) { + bpfSize = bpfOffset = 0; + } else { + bpfSize -= seglen; + bpfOffset += seglen; + } +#else +#ifdef USE_DLPI + struct strbuf data; + int flags = 0; + int retval; + + data.buf = (char *) pkt; + data.maxlen = MAXDLBUF; + data.len = 0; + + if ((retval = getmsg(sock, NULL, &data, &flags)) < 0) { + sysErr("read (receivePacket)"); + return -1; + } + + *size = data.len; + +#else + if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { + sysErr("recv (receivePacket)"); + return -1; + } +#endif +#endif + return 0; +} + diff --git a/src/libevent/Makefile b/src/libevent/Makefile new file mode 100755 index 0000000..6f0a5ed --- a/dev/null +++ b/src/libevent/Makefile @@ -0,0 +1,42 @@ +# Generated automatically from Makefile.in by configure. +# $Id$ +# +# Makefile for event-handling library +# +# Copyright 2002 Roaring Penguin Software Inc. +# +# This software may be distributed according to the terms of the GNU +# General Public License, version 2 or (at your option) any later version. +# LIC: GPL + +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=-g -O2 -Wall -Wstrict-prototypes -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) + gcc $(CFLAGS) -c -o event.o event.c + +hash.o: hash.c $(HDRS) + gcc $(CFLAGS) -c -o hash.o hash.c + +event_sig.o: event_sig.c $(HDRS) + gcc $(CFLAGS) -c -o event_sig.o event_sig.c + +event_tcp.o: event_tcp.c $(HDRS) + gcc $(CFLAGS) -c -o event_tcp.o event_tcp.c + +clean: FORCE + rm -f *.a *.o *~ + +FORCE: + +.phony: FORCE \ No newline at end of file diff --git a/src/libevent/Makefile.in b/src/libevent/Makefile.in new file mode 100755 index 0000000..7f7988c --- a/dev/null +++ b/src/libevent/Makefile.in @@ -0,0 +1,42 @@ +# Generated automatically from Makefile.in by configure. +# $Id$ +# +# Makefile for event-handling library +# +# Copyright 2002 Roaring Penguin Software Inc. +# +# This software may be distributed according to the terms of the GNU +# General Public License, version 2 or (at your option) any later version. +# LIC: GPL + +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 event.o event.c + +hash.o: hash.c $(HDRS) + @CC@ $(CFLAGS) -c -o hash.o hash.c + +event_sig.o: event_sig.c $(HDRS) + @CC@ $(CFLAGS) -c -o event_sig.o event_sig.c + +event_tcp.o: event_tcp.c $(HDRS) + @CC@ $(CFLAGS) -c -o event_tcp.o event_tcp.c + +clean: FORCE + rm -f *.a *.o *~ + +FORCE: + +.phony: FORCE \ No newline at end of file diff --git a/src/libevent/event.c b/src/libevent/event.c new file mode 100755 index 0000000..d6514cb --- a/dev/null +++ b/src/libevent/event.c @@ -0,0 +1,645 @@ +/*********************************************************************** +* +* event.c +* +* Abstraction of select call into "event-handling" to make programming +* easier. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#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\n", eh, flags)); + eh->fn(es, eh->fd, flags, eh->data); + EVENT_DEBUG(("Leave callback: eh=%p flags=%u\n", eh, flags)); + } + } + } + + 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; +} diff --git a/src/libevent/event.h b/src/libevent/event.h new file mode 100755 index 0000000..fad5b95 --- a/dev/null +++ b/src/libevent/event.h @@ -0,0 +1,114 @@ +/*********************************************************************** +* +* event.h +* +* Abstraction of select call into "event-handling" to make programming +* easier. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* $Id$ +* +* LIC: GPL +* +***********************************************************************/ + +#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 diff --git a/src/libevent/event_sig.c b/src/libevent/event_sig.c new file mode 100755 index 0000000..e3b4e4b --- a/dev/null +++ b/src/libevent/event_sig.c @@ -0,0 +1,265 @@ +/*********************************************************************** +* +* event_sig.c +* +* Code for handling signals nicely (synchronously) and for dealing +* with reaping child processes. +* +* Copyright (C) 2002 by Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#define _POSIX_SOURCE 1 /* For sigaction defines */ +#define _BSD_SOURCE 1 /* For SA_RESTART */ + +#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 EventHandler *PipeHandler = NULL; +static sig_atomic_t PipeFull = 0; +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; + + /* Clear buffer */ + read(fd, buf, 64); + PipeFull = 0; + + /* Fire handlers */ + for (i=0; i MAX_SIGNALS) { + /* Ooops... */ + return; + } + SignalHandlers[sig].fired = 1; + if (!PipeFull) { + write(Pipe[1], &sig, 1); + PipeFull = 1; + } +} + +/********************************************************************** +* %FUNCTION: child_handler +* %ARGUMENTS: +* sig -- signal number (whoop-dee-doo) +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Called *SYNCHRONOUSLY* to reap dead children. +***********************************************************************/ +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) { + if (ce->handler) { + ce->handler(pid, status, ce->data); + } + hash_remove(&child_process_table, ce); + 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) +{ + /* If already done, do nothing */ + if (PipeHandler) return 0; + + /* 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; + } + + 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; + + 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; + hash_insert(&child_process_table, ce); + return 0; +} diff --git a/src/libevent/event_tcp.c b/src/libevent/event_tcp.c new file mode 100755 index 0000000..679e06b --- a/dev/null +++ b/src/libevent/event_tcp.c @@ -0,0 +1,577 @@ +/*********************************************************************** +* +* event_tcp.c -- implementation of event-driven socket I/O. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#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; + +/********************************************************************** +* %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; + EventTcpAcceptFunc f; + + 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; + f = (EventTcpAcceptFunc) data; + + f(es, conn); +} + +/********************************************************************** +* %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) +{ + 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; + } + + return Event_AddHandler(es, socket, EVENT_FLAG_READABLE, + handle_accept, (void *) f); + +} + +/********************************************************************** +* %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 (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 (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); +} diff --git a/src/libevent/event_tcp.h b/src/libevent/event_tcp.h new file mode 100755 index 0000000..7e281d0 --- a/dev/null +++ b/src/libevent/event_tcp.h @@ -0,0 +1,87 @@ +/*********************************************************************** +* +* event-tcp.h +* +* Event-driven TCP functions to allow for single-threaded "concurrent" +* server. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* $Id$ +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +#ifndef INCLUDE_EVENT_TCP_H +#define INCLUDE_EVENT_TCP_H 1 + +#include "event.h" +#include + +typedef void (*EventTcpAcceptFunc)(EventSelector *es, + int fd); + +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); + +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 diff --git a/src/libevent/eventpriv.h b/src/libevent/eventpriv.h new file mode 100755 index 0000000..7be7654 --- a/dev/null +++ b/src/libevent/eventpriv.h @@ -0,0 +1,46 @@ +/*********************************************************************** +* +* 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. +* +* 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$ +* +* LIC: GPL +* +***********************************************************************/ + +#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 diff --git a/src/libevent/hash.c b/src/libevent/hash.c new file mode 100755 index 0000000..cc58ef0 --- a/dev/null +++ b/src/libevent/hash.c @@ -0,0 +1,266 @@ +/*********************************************************************** +* +* hash.c +* +* Implementation of hash tables. Each item inserted must include +* a hash_bucket member. +* +* Copyright (C) 2002 Roaring Penguin Software Inc. +* +* This software may be distributed under the terms of the GNU General +* Public License, Version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#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; +} diff --git a/src/libevent/hash.h b/src/libevent/hash.h new file mode 100755 index 0000000..6169c6e --- a/dev/null +++ b/src/libevent/hash.h @@ -0,0 +1,54 @@ +/*********************************************************************** +* +* hash.h +* +* Hash table utilities +* +* Copyright (C) 2002 Roaring Penguin Software Inc. +* +* LIC: GPL +* +***********************************************************************/ + +#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 diff --git a/src/md5.c b/src/md5.c new file mode 100755 index 0000000..a379cf5 --- a/dev/null +++ b/src/md5.c @@ -0,0 +1,249 @@ +/* + * 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. + * + * LIC: GPL + * + * 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; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) 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; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) 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 *) 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 *) 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 *) 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 *) ctx->in)[14] = ctx->bits[0]; + ((uint32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* 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 buf[4], uint32 const in[16]) +{ + register uint32 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; +} + +#endif diff --git a/src/md5.h b/src/md5.h new file mode 100755 index 0000000..525a45e --- a/dev/null +++ b/src/md5.h @@ -0,0 +1,34 @@ +#ifndef MD5_H +#define MD5_H +/* + * LIC: GPL + */ + +#include "config.h" + +#if SIZEOF_UNSIGNED_INT == 4 +typedef unsigned int uint32; +#elif SIZEOF_UNSIGNED_LONG == 4 +typedef unsigned long uint32; +#else +# error Could not find a 32-bit integer type +#endif + +struct MD5Context { + uint32 buf[4]; + uint32 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 buf[4], uint32 const in[16]); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ +typedef struct MD5Context MD5_CTX; + +#endif /* !MD5_H */ diff --git a/src/plugin.c b/src/plugin.c new file mode 100755 index 0000000..83978f0 --- a/dev/null +++ b/src/plugin.c @@ -0,0 +1,469 @@ +/*********************************************************************** +* +* plugin.c +* +* pppd plugin for kernel-mode PPPoE on Linux +* +* Copyright (C) 2001 by Roaring Penguin Software Inc., Michal Ostrowski +* and Jamal Hadi Salim. +* +* Much code and many ideas derived from pppoe plugin by Michal +* Ostrowski and Jamal Hadi Salim, which carries this copyright: +* +* Copyright 2000 Michal Ostrowski , +* Jamal Hadi Salim +* Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr., +* which is based in part on work from Jens Axboe and Paul Mackerras. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version +* 2 of the License, or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#define _GNU_SOURCE 1 +#include "pppoe.h" + +#include "pppd/pppd.h" +#include "pppd/fsm.h" +#include "pppd/lcp.h" +#include "pppd/ipcp.h" +#include "pppd/ccp.h" +/* #include "pppd/pathnames.h" */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _ROOT_PATH +#define _ROOT_PATH "" +#endif + +#define _PATH_ETHOPT _ROOT_PATH "/etc/ppp/options." + +char pppd_version[] = VERSION; + +/* From sys-linux.c in pppd -- MUST FIX THIS! */ +extern int new_style_driver; + +char *pppd_pppoe_service = NULL; +static char *acName = NULL; +static char *existingSession = NULL; +static int printACNames = 0; + +static 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"}, + { NULL } +}; +int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL; +static PPPoEConnection *conn = NULL; + +/********************************************************************** + * %FUNCTION: PPPOEInitDevice + * %ARGUMENTS: + * None + * %RETURNS: + * + * %DESCRIPTION: + * Initializes PPPoE device. + ***********************************************************************/ +static int +PPPOEInitDevice(void) +{ + conn = malloc(sizeof(PPPoEConnection)); + if (!conn) { + 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); + } + SET_STRING(conn->ifName, devnam); + conn->discoverySocket = -1; + conn->sessionSocket = -1; + conn->useHostUniq = 1; + conn->printACNames = printACNames; + conn->discoveryTimeout = PADI_TIMEOUT; + return 1; +} + +/********************************************************************** + * %FUNCTION: PPPOEConnectDevice + * %ARGUMENTS: + * None + * %RETURNS: + * Non-negative if all goes well; -1 otherwise + * %DESCRIPTION: + * Connects PPPoE device. + ***********************************************************************/ +static int +PPPOEConnectDevice(void) +{ + struct sockaddr_pppox sp; + + /* 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; + } + + strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); + if (existingSession) { + unsigned int mac[ETH_ALEN]; + int i, ses; + if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x", + &ses, &mac[0], &mac[1], &mac[2], + &mac[3], &mac[4], &mac[5]) != 7) { + fatal("Illegal value for rp_pppoe_sess option"); + } + conn->session = htons(ses); + for (i=0; ipeerEth[i] = (unsigned char) mac[i]; + } + } else { + conn->discoverySocket = + openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); + discovery(conn); + if (conn->discoveryState != STATE_SESSION) { + error("Unable to complete PPPoE Discovery"); + return -1; + } + } + + /* Set PPPoE session-number for further consumption */ + ppp_session_number = ntohs(conn->session); + + sp.sa_family = AF_PPPOX; + sp.sa_protocol = PX_PROTO_OE; + sp.sa_addr.pppoe.sid = conn->session; + memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); + memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); + + /* Set remote_number for ServPoET */ + sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X", + (unsigned) conn->peerEth[0], + (unsigned) conn->peerEth[1], + (unsigned) conn->peerEth[2], + (unsigned) conn->peerEth[3], + (unsigned) conn->peerEth[4], + (unsigned) conn->peerEth[5]); + + warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s", + (unsigned) conn->peerEth[0], + (unsigned) conn->peerEth[1], + (unsigned) conn->peerEth[2], + (unsigned) conn->peerEth[3], + (unsigned) conn->peerEth[4], + (unsigned) conn->peerEth[5], + conn->ifName); + + script_setenv("MACREMOTE", remote_number, 0); + + if (connect(conn->sessionSocket, (struct sockaddr *) &sp, + sizeof(struct sockaddr_pppox)) < 0) { + error("Failed to connect PPPoE socket: %d %m", errno); + return -1; + } + + return conn->sessionSocket; +} + +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; + } + 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); +} + + +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; + memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); + memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); + if (connect(conn->sessionSocket, (struct sockaddr *) &sp, + sizeof(struct sockaddr_pppox)) < 0) { + fatal("Failed to disconnect PPPoE socket: %d %m", errno); + return; + } + close(conn->sessionSocket); + close(conn->discoverySocket); + +} + +static void +PPPOEDeviceOptions(void) +{ + char buf[256]; + snprintf(buf, 256, _PATH_ETHOPT "%s",devnam); + if(!options_from_file(buf, 0, 0, 1)) + exit(EXIT_OPTION_ERROR); + +} + +struct channel pppoe_channel; + +/********************************************************************** + * %FUNCTION: PPPoEDevnameHook + * %ARGUMENTS: + * cmd -- the command (actually, the device name + * argv -- argument vector + * doit -- if non-zero, set device name. Otherwise, just check if possible + * %RETURNS: + * 1 if we will handle this device; 0 otherwise. + * %DESCRIPTION: + * Checks if name is a valid interface name; if so, returns 1. Also + * sets up devnam (string representation of device). + ***********************************************************************/ +static int +PPPoEDevnameHook(char *cmd, char **argv, int doit) +{ + int r = 1; + int fd; + struct ifreq ifr; + + /* 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)) { + if (OldDevnameHook) return OldDevnameHook(cmd, argv, doit); + return 0; + } + } + + /* Open a socket */ + if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { + r = 0; + } + + /* Try getting interface index */ + if (r) { + strncpy(ifr.ifr_name, cmd, 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) { + error("Interface %s not Ethernet", cmd); + r=0; + } + } + } + } + + /* Close socket */ + close(fd); + if (r) { + strncpy(devnam, cmd, sizeof(devnam)); + if (the_channel != &pppoe_channel) { + + the_channel = &pppoe_channel; + modem = 0; + + 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(); + } + return 1; + } + + if (OldDevnameHook) r = OldDevnameHook(cmd, argv, doit); + return r; +} + +/********************************************************************** + * %FUNCTION: plugin_init + * %ARGUMENTS: + * None + * %RETURNS: + * Nothing + * %DESCRIPTION: + * Initializes hooks for pppd plugin + ***********************************************************************/ +void +plugin_init(void) +{ + if (!ppp_available() && !new_style_driver) { + fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?"); + } + + add_options(Options); + + info("RP-PPPoE plugin version %s compiled against pppd %s", + RP_VERSION, VERSION); +} + +/********************************************************************** +*%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; + sprintf(buf, "%.256s: %.256s", str, strerror(i)); + printErr(buf); + sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i)); + sendPADT(conn, buf); + exit(1); +} + +/********************************************************************** +*%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(str); + sendPADTf(conn, "RP-PPPoE: %.256s", str); + exit(1); +} + +/********************************************************************** +*%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); +} + + +struct channel pppoe_channel = { + .options = Options, + .process_extra_options = &PPPOEDeviceOptions, + .check_options = NULL, + .connect = &PPPOEConnectDevice, + .disconnect = &PPPOEDisconnectDevice, + .establish_ppp = &generic_establish_ppp, + .disestablish_ppp = &generic_disestablish_ppp, + .send_config = &PPPOESendConfig, + .recv_config = &PPPOERecvConfig, + .close = NULL, + .cleanup = NULL +}; diff --git a/src/ppp.c b/src/ppp.c new file mode 100755 index 0000000..543ba71 --- a/dev/null +++ b/src/ppp.c @@ -0,0 +1,262 @@ +/*********************************************************************** +* +* ppp.c +* +* Implementation of user-space PPPoE redirector for Linux. +* +* Functions for talking to PPP daemon +* +* Copyright (C) 2000 by Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_SYSLOG_H +#include +#include +#define syslog(prio, fmt...) \ + __android_log_print(prio, "PPPOE", fmt) +#endif + +#include +#include +#include + +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_N_HDLC +#ifndef N_HDLC +#include +#endif +#endif + +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_DATA_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_DATA_LEN - PPPOE_OVERHEAD + 2]; + r = read(0, buf, ETH_DATA_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(0); + } + + 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(0); + } + + 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_DATA_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); +} diff --git a/src/pppoe-server.c b/src/pppoe-server.c new file mode 100755 index 0000000..b59cd3c --- a/dev/null +++ b/src/pppoe-server.c @@ -0,0 +1,2137 @@ +/*********************************************************************** +* +* pppoe-server.c +* +* Implementation of a user-space PPPoE server +* +* Copyright (C) 2000 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* $Id$ +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "config.h" + +#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) +#define _POSIX_SOURCE 1 /* For sigaction defines */ +#endif + +#define _BSD_SOURCE 1 /* for gethostname */ + +#include "pppoe-server.h" +#include "md5.h" + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_GETOPT_H +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include + +#include + +#ifdef HAVE_LICENSE +#include "license.h" +#include "licensed-only/servfuncs.h" +static struct License const *ServerLicense; +static struct License const *ClusterLicense; +#else +#define control_session_started(x) (void) 0 +#define control_session_terminated(x) (void) 0 +#define control_exit() (void) 0 +#define realpeerip peerip +#endif + +#ifdef HAVE_L2TP +extern PppoeSessionFunctionTable L2TPSessionFunctionTable; +extern void pppoe_to_l2tp_add_interface(EventSelector *es, + Interface *interface); +#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) + +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[MAX_INTERFACES]; +int NumInterfaces = 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; + +/* File with PPPD options */ +static char *pppoptfile = NULL; + +static int Debug = 0; +static int CheckPoolSyntax = 0; + +/* Synchronous mode */ +static int Synchronous = 0; + +/* 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; +} + +/********************************************************************** +*%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; + +#ifdef HAVE_L2TP + /* We're acting as LAC, so when child exits, become a PPPoE <-> L2TP + relay */ + if (session->flags & FLAG_ACT_AS_LAC) { + syslog(LOG_INFO, "Session %u for client " + "%02x:%02x:%02x:%02x:%02x:%02x handed off to LNS %s", + (unsigned int) ntohs(session->sess), + session->eth[0], session->eth[1], session->eth[2], + session->eth[3], session->eth[4], session->eth[5], + inet_ntoa(session->tunnel_endpoint.sin_addr)); + session->pid = 0; + session->funcs = &L2TPSessionFunctionTable; + return; + } +#endif + + memset(&conn, 0, sizeof(conn)); + conn.useHostUniq = 0; + + 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->realpeerip[0], (int) session->realpeerip[1], + (int) session->realpeerip[2], (int) session->realpeerip[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 = ""; + control_session_terminated(session); + 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: 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; + } +#ifdef HAVE_L2TP + pppoe_close_l2tp_tunnels(); +#endif +} + +/********************************************************************** +*%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(1); + } + + 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; +#ifdef HAVE_LICENSE + memcpy(Sessions[numAddrs].realpeerip, + Sessions[numAddrs].peerip, IPV4ALEN); +#endif + } + 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; +#ifdef HAVE_LICENSE + memcpy(Sessions[numAddrs].realpeerip, + Sessions[numAddrs].peerip, IPV4ALEN); +#endif + 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; +#ifdef HAVE_LICENSE + memcpy(Sessions[numAddrs].realpeerip, + Sessions[numAddrs].peerip, IPV4ALEN); +#endif + } + 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_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_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) +{ + char buf[SMALLBUF]; + snprintf(buf, SMALLBUF, "%s: %s", str, strerror(errno)); + printErr(buf); + control_exit(); + 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) +{ + char buf[1024]; + sprintf(buf, "%.256s: %.256s", str, strerror(errno)); + printErr(buf); +} + +/********************************************************************** +*%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(str); + control_exit(); + 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 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 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; + 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.ver = 1; + pado.type = 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 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)) { + 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; + +#ifdef HAVE_LICENSE + int freemem; +#endif + + /* 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; + } + } + 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 = ""; + } + + +#ifdef HAVE_LICENSE + /* Are we licensed for this many sessions? */ + if (License_NumLicenses("PPPOE-SESSIONS") <= NumActiveSessions) { + syslog(LOG_ERR, "Insufficient session licenses (%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 session licenses available"); + return; + } +#endif + /* Enough free memory? */ +#ifdef HAVE_LICENSE + freemem = getFreeMem(); + if (freemem < MIN_FREE_MEMORY) { + syslog(LOG_WARNING, + "Insufficient free memory to create session: Want %d, have %d", + MIN_FREE_MEMORY, freemem); + sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, + TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Insufficient free RAM"); + return; + } +#endif + /* 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); + control_session_started(cliSession); + return; + } + + /* In the child process. */ + + /* Close all file descriptors except for socket */ + closelog(); + 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.ver = 1; + pads.type = 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 (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)); + + /* Close sock; don't need it any more */ + close(sock); + + startPPPD(cliSession); +} + +/********************************************************************** +*%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); + killAllSessions(); + control_exit(); + exit(0); +} + +/********************************************************************** +*%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"); +#ifdef USE_BPF + fprintf(stderr, " -I if_name -- Specify interface (REQUIRED)\n"); +#else + fprintf(stderr, " -I if_name -- Specify interface (default %s.)\n", + DEFAULT_IF); +#endif + 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 -- Optain 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"); +#ifdef HAVE_LINUX_KERNEL_PPPOE + fprintf(stderr, " -k -- Use kernel-mode PPPoE.\n"); +#endif + 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"); +#ifdef HAVE_LICENSE + fprintf(stderr, " -c secret:if:port -- Enable clustering on interface 'if'.\n"); + fprintf(stderr, " -1 -- Allow only one session per user.\n"); +#endif + + fprintf(stderr, " -h -- Print usage information.\n\n"); + fprintf(stderr, "PPPoE-Server Version %s, Copyright (C) 2001-2006 Roaring Penguin Software Inc.\n", VERSION); + +#ifndef HAVE_LICENSE + 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"); +#endif + fprintf(stderr, "http://www.roaringpenguin.com\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; +#ifdef HAVE_LICENSE + int use_clustering = 0; +#endif + +#ifndef HAVE_LINUX_KERNEL_PPPOE + char *options = "x:hI:C:L:R:T:m:FN:f:O:o:sp:lrudPc:S:1"; +#else + char *options = "x:hI:C:L:R:T:m:FN:f:O:o:skp:lrudPc:S:1"; +#endif + + if (getuid() != geteuid() || + getgid() != getegid()) { + fprintf(stderr, "SECURITY WARNING: pppoe-server will NOT run suid or sgid. Fix your installation.\n"); + exit(1); + } + + memset(interfaces, 0, sizeof(interfaces)); + + /* Initialize syslog */ + openlog("pppoe-server", LOG_PID, LOG_DAEMON); + + /* 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 'x': + if (sscanf(optarg, "%d", &MaxSessionsPerMac) != 1) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + if (MaxSessionsPerMac < 0) { + MaxSessionsPerMac = 0; + } + break; + +#ifdef HAVE_LINUX_KERNEL_PPPOE + case 'k': + UseLinuxKernelModePPPoE = 1; + break; +#endif + case 'S': + if (NumServiceNames == MAX_SERVICE_NAMES) { + fprintf(stderr, "Too many '-S' options (%d max)", + MAX_SERVICE_NAMES); + exit(1); + } + ServiceNames[NumServiceNames] = strdup(optarg); + if (!ServiceNames[NumServiceNames]) { + fprintf(stderr, "Out of memory"); + exit(1); + } + NumServiceNames++; + break; + case 'c': +#ifndef HAVE_LICENSE + fprintf(stderr, "Clustering capability not available.\n"); + exit(1); +#else + cluster_handle_option(optarg); + use_clustering = 1; + break; +#endif + + 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 '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 >= MAX_INTERFACES) { + fprintf(stderr, "Too many -I options (max %d)\n", + MAX_INTERFACES); + 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 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + case '1': +#ifdef HAVE_LICENSE + MaxSessionsPerUser = 1; +#else + fprintf(stderr, "-1 option not valid.\n"); + exit(1); +#endif + break; + } + } + + if (!pppoptfile) { + pppoptfile = PPPOE_SERVER_OPTIONS; + } + +#ifdef HAVE_LICENSE + License_SetVersion(SERVPOET_VERSION); + License_ReadBundleFile("/etc/rp/bundle.txt"); + License_ReadFile("/etc/rp/license.txt"); + ServerLicense = License_GetFeature("PPPOE-SERVER"); + if (!ServerLicense) { + fprintf(stderr, "License: GetFeature failed: %s\n", + License_ErrorMessage()); + exit(1); + } +#endif + +#ifdef USE_LINUX_PACKET +#ifndef HAVE_STRUCT_SOCKADDR_LL + fprintf(stderr, "The PPPoE server does not work on Linux 2.0 kernels.\n"); + exit(EXIT_FAILURE); +#endif +#endif + + 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(0); + } + } + + /* 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(0); + } + + /* Open all the interfaces */ + for (i=0; i= 0) { + dup2(i, 0); + dup2(i, 1); + dup2(i, 2); + if (i > 2) close(i); + } + } + + for(;;) { + i = Event_HandleEvent(event_selector); + if (i < 0) { + fatalSys("Event_HandleEvent"); + } + +#ifdef HAVE_LICENSE + if (License_Expired(ServerLicense)) { + syslog(LOG_INFO, "Server license has expired -- killing all PPPoE sessions"); + killAllSessions(); + control_exit(); + exit(0); + } +#endif + } + return 0; +} + +void +serverProcessPacket(Interface *i) +{ + int len; + PPPoEPacket packet; + int sock = i->sock; + + 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; + } + + /* Sanity check on packet */ + if (packet.ver != 1 || packet.type != 1) { + /* Syslog an error */ + 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.ver = 1; + pads.type = 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: startPPPDUserMode +*%ARGUMENTS: +* session -- client session record +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Starts PPPD for user-mode PPPoE +***********************************************************************/ +void +startPPPDUserMode(ClientSession *session) +{ + /* Leave some room */ + char *argv[32]; + + char buffer[SMALLBUF]; + + int c = 0; + + argv[c++] = "pppd"; + argv[c++] = "pty"; + + /* Let's hope service-name does not have ' in it... */ + snprintf(buffer, SMALLBUF, "%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); + } + + argv[c++] = "file"; + argv[c++] = pppoptfile; + + snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d", + (int) session->myip[0], (int) session->myip[1], + (int) session->myip[2], (int) session->myip[3], + (int) session->peerip[0], (int) session->peerip[1], + (int) session->peerip[2], (int) session->peerip[3]); + 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++] = strdup(buffer); + if (!argv[c-1]) { + /* TODO: Send a PADT */ + exit(EXIT_FAILURE); + } + argv[c++] = "nodetach"; + argv[c++] = "noaccomp"; + argv[c++] = "nobsdcomp"; + argv[c++] = "nodeflate"; + argv[c++] = "nopcomp"; + argv[c++] = "novj"; + argv[c++] = "novjccomp"; + argv[c++] = "default-asyncmap"; + if (Synchronous) { + argv[c++] = "sync"; + } + if (PassUnitOptionToPPPD) { + argv[c++] = "unit"; + sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1 - SessOffset)); + argv[c++] = buffer; + } + argv[c++] = NULL; + + execv(PPPD_PATH, argv); + exit(EXIT_FAILURE); +} + +/********************************************************************** +*%FUNCTION: startPPPDLinuxKernelMode +*%ARGUMENTS: +* session -- client session record +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Starts PPPD for kernel-mode PPPoE on Linux +***********************************************************************/ +void +startPPPDLinuxKernelMode(ClientSession *session) +{ + /* Leave some room */ + char *argv[32]; + + int c = 0; + + char buffer[SMALLBUF]; + + argv[c++] = "pppd"; + 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; + argv[c++] = "file"; + argv[c++] = pppoptfile; + + snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d", + (int) session->myip[0], (int) session->myip[1], + (int) session->myip[2], (int) session->myip[3], + (int) session->peerip[0], (int) session->peerip[1], + (int) session->peerip[2], (int) session->peerip[3]); + 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++] = strdup(buffer); + if (!argv[c-1]) { + /* TODO: Send a PADT */ + exit(EXIT_FAILURE); + } + argv[c++] = "nodetach"; + argv[c++] = "noaccomp"; + argv[c++] = "nobsdcomp"; + argv[c++] = "nodeflate"; + argv[c++] = "nopcomp"; + argv[c++] = "novj"; + argv[c++] = "novjccomp"; + argv[c++] = "default-asyncmap"; + if (PassUnitOptionToPPPD) { + argv[c++] = "unit"; + sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1 - SessOffset)); + argv[c++] = buffer; + } + argv[c++] = NULL; + execv(PPPD_PATH, argv); + exit(EXIT_FAILURE); +} + +/********************************************************************** +*%FUNCTION: startPPPD +*%ARGUMENTS: +* session -- client session record +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Starts PPPD +***********************************************************************/ +void +startPPPD(ClientSession *session) +{ + if (UseLinuxKernelModePPPoE) startPPPDLinuxKernelMode(session); + else startPPPDUserMode(session); +} + +/********************************************************************** +* %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.useHostUniq = 0; + + 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); +} + +#ifdef HAVE_LICENSE +/********************************************************************** +* %FUNCTION: getFreeMem +* %ARGUMENTS: +* None +* %RETURNS: +* The amount of free RAM in kilobytes, or -1 if it could not be +* determined +* %DESCRIPTION: +* Reads Linux-specific /proc/meminfo file and extracts free RAM +***********************************************************************/ +int +getFreeMem(void) +{ + char buf[512]; + int memfree=0, buffers=0, cached=0; + FILE *fp = fopen("/proc/meminfo", "r"); + if (!fp) return -1; + + while (fgets(buf, sizeof(buf), fp)) { + if (!strncmp(buf, "MemFree:", 8)) { + if (sscanf(buf, "MemFree: %d", &memfree) != 1) { + fclose(fp); + return -1; + } + } else if (!strncmp(buf, "Buffers:", 8)) { + if (sscanf(buf, "Buffers: %d", &buffers) != 1) { + fclose(fp); + return -1; + } + } else if (!strncmp(buf, "Cached:", 7)) { + if (sscanf(buf, "Cached: %d", &cached) != 1) { + fclose(fp); + return -1; + } + } + } + fclose(fp); + /* return memfree + buffers + cached; */ + return memfree; +} +#endif + +/********************************************************************** +* %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 = ""; +#ifdef HAVE_LICENSE + memset(ses->user, 0, MAX_USERNAME_LEN+1); + memset(ses->realm, 0, MAX_USERNAME_LEN+1); + memset(ses->realpeerip, 0, IPV4ALEN); +#endif +#ifdef HAVE_L2TP + ses->l2tp_ses = NULL; +#endif + 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; + ses->flags = 0; +#ifdef HAVE_L2TP + ses->l2tp_ses = NULL; +#endif + 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)) { + syslog(LOG_WARNING, "sendHURL(%s): URL must begin with http://", 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.ver = 1; + packet.type = 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 +} diff --git a/src/pppoe-server.h b/src/pppoe-server.h new file mode 100755 index 0000000..ba4635f --- a/dev/null +++ b/src/pppoe-server.h @@ -0,0 +1,156 @@ +/********************************************************************** +* +* pppoe-server.h +* +* Definitions for PPPoE server +* +* Copyright (C) 2001-2006 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +* $Id$ +* +***********************************************************************/ + +#include "pppoe.h" +#include "event.h" + +#ifdef HAVE_L2TP +#include "l2tp/l2tp.h" +#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 */ + + /* Next fields are used only if we're an L2TP LAC */ +#ifdef HAVE_L2TP + int session_sock; /* Session socket */ + EventHandler *lac_eh; /* LAC's event-handler */ +#endif +} 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 */ +#ifdef HAVE_LICENSE + char user[MAX_USERNAME_LEN+1]; /* Authenticated user-name */ + char realm[MAX_USERNAME_LEN+1]; /* Realm */ + unsigned char realpeerip[IPV4ALEN]; /* Actual IP address -- may be assigned + by RADIUS server */ + int maxSessionsPerUser; /* Max sessions for this user */ +#endif +#ifdef HAVE_L2TP + l2tp_session *l2tp_ses; /* L2TP session */ + struct sockaddr_in tunnel_endpoint; /* L2TP endpoint */ +#endif +} ClientSession; + +/* Hack for daemonizing */ +#define CLOSEFD 64 + +/* Max. number of interfaces to listen on */ +#define MAX_INTERFACES 64 + +/* 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[MAX_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); + +#ifdef HAVE_LICENSE +extern int getFreeMem(void); +#endif diff --git a/src/pppoe-sniff.c b/src/pppoe-sniff.c new file mode 100755 index 0000000..1569857 --- a/dev/null +++ b/src/pppoe-sniff.c @@ -0,0 +1,266 @@ +/*********************************************************************** +* +* pppoe-sniff.c +* +* Sniff a network for likely-looking PPPoE frames and deduce the value +* to supply to PPPOE_EXTRA in /etc/ppp/pppoe.conf. USE AT YOUR OWN RISK. +* +* Copyright (C) 2000 by Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include +#include + +#ifdef USE_DLPI +#include +/* function declarations */ +void dlpromisconreq( int fd, u_long level); +void dlokack(int fd, char *bufp); +#endif + +/* 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) +{ + char buf[1024]; + sprintf(buf, "%.256s: %.256s", str, strerror(errno)); + printErr(buf); + exit(1); +} + +/********************************************************************** +*%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(str); + exit(1); +} + +/********************************************************************** +*%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", 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, "http://www.roaringpenguin.com\n"); + exit(0); +} + +#if !defined(USE_LINUX_PACKET) && !defined(USE_DLPI) + +int +main() +{ + fprintf(stderr, "Sorry, pppoe-sniff works only on Linux.\n"); + return 1; +} + +#else + +/********************************************************************** +*%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; +#ifdef USE_DLPI + long buf[MAXDLBUF]; +#endif + + if (getuid() != geteuid() || + getgid() != getegid()) { + fprintf(stderr, "SECURITY WARNING: pppoe-sniff will NOT run suid or sgid. Fix your installation.\n"); + exit(1); + } + + while((opt = getopt(argc, argv, "I:V")) != -1) { + switch(opt) { + case 'I': + SET_STRING(IfName, optarg); + break; + case 'V': + printf("pppoe-sniff: Roaring Penguin PPPoE Version %s\n", VERSION); + exit(0); + default: + usage(argv[0]); + } + } + + /* Pick a default interface name */ + if (!IfName) { + IfName = DEFAULT_IF; + } + + /* Open the interface */ +#ifdef USE_DLPI + sock = openInterface(IfName, Eth_PPPOE_Discovery, NULL); + dlpromisconreq(sock, DL_PROMISC_PHYS); + dlokack(sock, (char *)buf); + dlpromisconreq(sock, DL_PROMISC_SAP); + dlokack(sock, (char *)buf); +#else + + sock = openInterface(IfName, ETH_P_ALL, NULL); + +#endif + + /* We assume interface is in promiscuous mode -- use ifconfig 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 > (unsigned int)size) continue; + if (pkt.ver != 1 || pkt.type != 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 > (unsigned int)size) continue; + if (pkt.ver != 1 || pkt.type != 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 anything special in the configuration file.\n"); + return 0; + } + + fprintf(stderr, "\nOK, looks like you need something special in the configuration file.\nTry this:\n\n"); + if (ServiceName != NULL && *ServiceName != 0) { + fprintf(stderr, "SERVICENAME='%s'\n", ServiceName); + } + if (DiscType != ETH_PPPOE_DISCOVERY || SessType != ETH_PPPOE_SESSION) { + fprintf(stderr, " PPPOE_EXTRA='-f %x:%x'\n", DiscType, SessType); + } + return 0; +} + +#endif +/********************************************************************** +*%FUNCTION: sysErr +*%ARGUMENTS: +* str -- error message +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Prints a message plus the errno value to syslog. +***********************************************************************/ +void +sysErr(char const *str) +{ + char buf[1024]; + sprintf(buf, "%.256s: %.256s", str, strerror(errno)); + printErr(buf); +} diff --git a/src/pppoe.c b/src/pppoe.c new file mode 100755 index 0000000..f64df82 --- a/dev/null +++ b/src/pppoe.c @@ -0,0 +1,959 @@ +/*********************************************************************** +* +* pppoe.c +* +* Implementation of user-space PPPoE redirector for Linux. +* +* Copyright (C) 2000-2006 by Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_SYSLOG_H +#include +#include +#define syslog(prio, fmt...) \ + __android_log_print(prio, "PPPOE", fmt) +#endif + +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef USE_LINUX_PACKET +#include +#include +#endif + +#include + +#ifdef HAVE_N_HDLC +#ifndef N_HDLC +#include +#endif +#endif + +/* 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 */ + +int persist = 0; /* We are not a pppd plugin */ +/*********************************************************************** +*%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 + +} + +#ifdef USE_BPF +/********************************************************************** +*%FUNCTION: sessionDiscoveryPacket +*%ARGUMENTS: +* packet -- the discovery packet that was received +*%RETURNS: +* Nothing +*%DESCRIPTION: +* We got a discovery packet during the session stage. This most likely +* means a PADT. +* +* The BSD version uses a single socket for both discovery and session +* packets. When a packet comes in over the wire once we are in +* session mode, either syncReadFromEth() or asyncReadFromEth() will +* have already read the packet and determined it to be a discovery +* packet before passing it here. +***********************************************************************/ +static void +sessionDiscoveryPacket(PPPoEPacket *packet) +{ + /* Sanity check */ + if (packet->code != CODE_PADT) { + return; + } + + /* It's a PADT, all right. Is it for us? */ + if (packet->session != Connection->session) { + /* Nope, ignore it */ + return; + } + if (memcmp(packet->ethHdr.h_dest, Connection->myEth, ETH_ALEN)) { + return; + } + + if (memcmp(packet->ethHdr.h_source, Connection->peerEth, ETH_ALEN)) { + return; + } + + syslog(LOG_INFO, + "Session %d terminated -- received PADT from peer", + (int) ntohs(packet->session)); + parsePacket(packet, parseLogErrs, NULL); + sendPADT(Connection, "Received PADT from peer"); + exit(EXIT_SUCCESS); +} +#else +/********************************************************************** +*%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 > (unsigned int)len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + return; + } + + if (packet.code != CODE_PADT) { + /* Not PADT; ignore it */ + return; + } + + /* It's a PADT, all right. Is it for us? */ + if (packet.session != conn->session) { + /* Nope, ignore it */ + return; + } + + if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { + return; + } + + if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { + 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); +} +#endif /* USE_BPF */ + +/********************************************************************** +*%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.ver = 1; + packet.type = 1; + packet.code = CODE_SESS; + packet.session = conn->session; + + initPPP(); + +#ifdef USE_BPF + /* check for buffered session data */ + while (BPF_BUFFER_HAS_DATA) { + if (conn->synchronous) { + syncReadFromEth(conn, conn->sessionSocket, optClampMSS); + } else { + asyncReadFromEth(conn, conn->sessionSocket, optClampMSS); + } + } +#endif + + 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)) { + do { + if (conn->synchronous) { + syncReadFromEth(conn, conn->sessionSocket, optClampMSS); + } else { + asyncReadFromEth(conn, conn->sessionSocket, optClampMSS); + } + } while (BPF_BUFFER_HAS_DATA); + } + +#ifndef USE_BPF + /* BSD uses a single socket, see *syncReadFromEth() */ + /* for calls to sessionDiscoveryPacket() */ + if (conn->discoverySocket >= 0) { + if (FD_ISSET(conn->discoverySocket, &readable)) { + sessionDiscoveryPacket(conn); + } + } +#endif + + } +} + + +/*********************************************************************** +*%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: Received 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"); +#ifdef USE_BPF + fprintf(stderr, " -I if_name -- Specify interface (REQUIRED)\n"); +#else + fprintf(stderr, " -I if_name -- Specify interface (default %s.)\n", + DEFAULT_IF); +#endif +#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" + " -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" + "PPPoE Version %s, Copyright (C) 2001-2006 Roaring Penguin Software Inc.\n" + "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" + "http://www.roaringpenguin.com\n", 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:Usm:np:e:kdf:F:t:"; +#else + options = "I:VAT:hS:C:Usm: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': + conn.useHostUniq = 1; + 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", 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("Roaring Penguin PPPoE Version %s\n", 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) { +#ifdef USE_BPF + fprintf(stderr, "No interface specified (-I option)\n"); + exit(EXIT_FAILURE); +#else + SET_STRING(conn.ifName, DEFAULT_IF); +#endif + } + + 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); + 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); + + /* 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); + syslog( LOG_INFO, "discovery\n"); + 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; ignore TERM and INT */ + signal(SIGTERM, SIG_IGN); + signal(SIGINT, SIG_IGN); + 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) +{ + char buf[1024]; + sprintf(buf, "%.256s: Session %d: %.256s", + str, (int) ntohs(Connection->session), strerror(errno)); + printErr(buf); + 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) +{ + char buf[1024]; + sprintf(buf, "%.256s: %.256s", str, strerror(errno)); + printErr(buf); +} + +/********************************************************************** +*%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(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]; +#ifdef USE_BPF + int type; +#endif + + if (receivePacket(sock, &packet, &len) < 0) { + return; + } + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > (unsigned int)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 + +#ifdef USE_BPF + /* Make sure this is a session packet before processing further */ + type = etherType(&packet); + if (type == Eth_PPPOE_Discovery) { + sessionDiscoveryPacket(&packet); + } else if (type != Eth_PPPOE_Session) { + return; + } +#endif + + /* Sanity check */ + if (packet.code != CODE_SESS) { + syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code); + return; + } + if (packet.ver != 1) { + syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver); + return; + } + if (packet.type != 1) { + syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type); + 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 > (unsigned int)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 (unsigned int)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 + +#ifdef USE_BPF + /* Make sure this is a session packet before processing further */ + type = etherType(&packet); + if (type == Eth_PPPOE_Discovery) { + sessionDiscoveryPacket(&packet); + } else if (type != Eth_PPPOE_Session) { + return; + } +#endif + + /* Sanity check */ + if (packet.code != CODE_SESS) { + syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code); + return; + } + if (packet.ver != 1) { + syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver); + return; + } + if (packet.type != 1) { + syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type); + 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 > (unsigned int)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"); + } +} diff --git a/src/pppoe.h b/src/pppoe.h new file mode 100755 index 0000000..77f2a2d --- a/dev/null +++ b/src/pppoe.h @@ -0,0 +1,347 @@ +/*********************************************************************** +* +* pppoe.h +* +* Declaration of various PPPoE constants +* +* Copyright (C) 2000 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +* $Id$ +* +***********************************************************************/ + +#include "config.h" + +extern int IsSetID; + +#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) +#define _POSIX_SOURCE 1 /* For sigaction defines */ +#endif + +#include /* For FILE */ +#include /* For pid_t */ + +/* How do we access raw Ethernet devices? */ +#undef USE_LINUX_PACKET +#undef USE_BPF + +#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) +#define USE_LINUX_PACKET 1 +#elif defined(HAVE_SYS_DLPI_H) +#define USE_DLPI +#elif defined(HAVE_NET_BPF_H) +#define USE_BPF 1 +#endif + +/* Sanity check */ +#if !defined(USE_BPF) && !defined(USE_LINUX_PACKET) && !defined(USE_DLPI) +#error Unknown method for accessing raw Ethernet frames +#endif + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +/* Ugly header files on some Linux boxes... */ +#if defined(HAVE_LINUX_IF_H) +#include +#elif defined(HAVE_NET_IF_H) +#include +#endif + +#ifdef HAVE_NET_IF_TYPES_H +#include +#endif + +#ifdef HAVE_NET_IF_DL_H +#include +#endif + +/* I'm not sure why this is needed... I do not have OpenBSD */ +#if defined(__OpenBSD__) +#include +#include +#endif + +#ifdef USE_BPF +extern int bpfSize; +struct PPPoEPacketStruct; +void sessionDiscoveryPacket(struct PPPoEPacketStruct *packet); +#define BPF_BUFFER_IS_EMPTY (bpfSize <= 0) +#define BPF_BUFFER_HAS_DATA (bpfSize > 0) +#define ethhdr ether_header +#define h_dest ether_dhost +#define h_source ether_shost +#define h_proto ether_type +#define ETH_DATA_LEN ETHERMTU +#define ETH_ALEN ETHER_ADDR_LEN +#else +#undef USE_BPF +#define BPF_BUFFER_IS_EMPTY 1 +#define BPF_BUFFER_HAS_DATA 0 +#endif + +#ifdef USE_DLPI +#include +#define ethhdr ether_header +#define ETH_DATA_LEN ETHERMTU +#define ETH_ALEN ETHERADDRL +#define h_dest ether_dhost.ether_addr_octet +#define h_source ether_shost.ether_addr_octet +#define h_proto ether_type + +/* cloned from dltest.h */ +#define MAXDLBUF 8192 +#define MAXDLADDR 1024 +#define MAXWAIT 15 +#define OFFADDR(s, n) (u_char*)((char*)(s) + (int)(n)) +#define CASERET(s) case s: return ("s") + +#endif + +/* Define various integer types -- assumes a char is 8 bits */ +#if SIZEOF_UNSIGNED_SHORT == 2 +typedef unsigned short UINT16_t; +#elif SIZEOF_UNSIGNED_INT == 2 +typedef unsigned int UINT16_t; +#else +#error Could not find a 16-bit integer type +#endif + +#if SIZEOF_UNSIGNED_SHORT == 4 +typedef unsigned short UINT32_t; +#elif SIZEOF_UNSIGNED_INT == 4 +typedef unsigned int UINT32_t; +#elif SIZEOF_UNSIGNED_LONG == 4 +typedef unsigned long UINT32_t; +#else +#error Could not find a 32-bit integer type +#endif + +#ifdef HAVE_LINUX_IF_ETHER_H +#include +#endif + +#include + +#ifdef HAVE_NETINET_IF_ETHER_H +#include + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifndef HAVE_SYS_DLPI_H +#include +#endif +#endif + + + +/* Ethernet frame types according to RFC 2516 */ +#define ETH_PPPOE_DISCOVERY 0x8863 +#define ETH_PPPOE_SESSION 0x8864 + +/* But some brain-dead peers disobey the RFC, so frame types are variables */ +extern UINT16_t Eth_PPPOE_Discovery; +extern UINT16_t Eth_PPPOE_Session; + +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_SERVICE_NAME_ERROR 0x0201 +#define TAG_AC_SYSTEM_ERROR 0x0202 +#define TAG_GENERIC_ERROR 0x0203 + +/* Extensions from draft-carrel-info-pppoe-ext-00 */ +/* I do NOT like these tags one little bit */ +#define TAG_HURL 0x111 +#define TAG_MOTM 0x112 +#define TAG_IP_ROUTE_ADD 0x121 + +/* Discovery phase states */ +#define STATE_SENT_PADI 0 +#define STATE_RECEIVED_PADO 1 +#define STATE_SENT_PADR 2 +#define STATE_SESSION 3 +#define STATE_TERMINATED 4 + +/* How many PADI/PADS attempts? */ +#define MAX_PADI_ATTEMPTS 3 + +/* Initial timeout for PADO/PADS */ +#define PADI_TIMEOUT 5 + +/* States for scanning PPP frames */ +#define STATE_WAITFOR_FRAME_ADDR 0 +#define STATE_DROP_PROTO 1 +#define STATE_BUILDING_PACKET 2 + +/* Special PPP frame characters */ +#define FRAME_ESC 0x7D +#define FRAME_FLAG 0x7E +#define FRAME_ADDR 0xFF +#define FRAME_CTRL 0x03 +#define FRAME_ENC 0x20 + +#define IPV4ALEN 4 +#define SMALLBUF 256 + +/* A PPPoE Packet, including Ethernet headers */ +typedef struct PPPoEPacketStruct { + struct ethhdr ethHdr; /* Ethernet header */ +#ifdef PACK_BITFIELDS_REVERSED + unsigned int type:4; /* PPPoE Type (must be 1) */ + unsigned int ver:4; /* PPPoE Version (must be 1) */ +#else + unsigned int ver:4; /* PPPoE Version (must be 1) */ + unsigned int type:4; /* PPPoE Type (must be 1) */ +#endif + unsigned int code:8; /* PPPoE code */ + unsigned int session:16; /* PPPoE session */ + unsigned int length:16; /* Payload length */ + unsigned char payload[ETH_DATA_LEN]; /* A bit of room to spare */ +} PPPoEPacket; + +/* Header size of a PPPoE packet */ +#define PPPOE_OVERHEAD 6 /* type, code, session, length */ +#define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD) +#define MAX_PPPOE_PAYLOAD (ETH_DATA_LEN - PPPOE_OVERHEAD) +#define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - 2) + +/* PPPoE Tag */ + +typedef struct PPPoETagStruct { + unsigned int type:16; /* tag type */ + unsigned int length:16; /* Length of payload */ + unsigned char payload[ETH_DATA_LEN]; /* A LOT of room to spare */ +} PPPoETag; +/* Header size of a PPPoE tag */ +#define TAG_HDR_SIZE 4 + +/* Chunk to read from stdin */ +#define READ_CHUNK 4096 + +/* Function passed to parsePacket */ +typedef void ParseFunc(UINT16_t type, + UINT16_t len, + unsigned char *data, + void *extra); + +#define PPPINITFCS16 0xffff /* Initial FCS value */ + +/* Keep track of the state of a connection -- collect everything in + one spot */ + +typedef struct PPPoEConnectionStruct { + int discoveryState; /* Where we are in discovery */ + int discoverySocket; /* Raw socket for discovery frames */ + int sessionSocket; /* Raw socket for session frames */ + unsigned char myEth[ETH_ALEN]; /* My MAC address */ + unsigned char peerEth[ETH_ALEN]; /* Peer's MAC address */ + UINT16_t session; /* Session ID */ + char *ifName; /* Interface name */ + char *serviceName; /* Desired service name, if any */ + char *acName; /* Desired AC name, if any */ + int synchronous; /* Use synchronous PPP */ + int useHostUniq; /* Use Host-Uniq tag */ + int printACNames; /* Just print AC names */ + 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 */ +} PPPoEConnection; + +/* Structure used to determine acceptable PADO or PADS packet */ +struct PacketCriteria { + PPPoEConnection *conn; + int acNameOK; + int serviceNameOK; + int seenACName; + int seenServiceName; +}; + +/* Function Prototypes */ +UINT16_t etherType(PPPoEPacket *packet); +int openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr); +int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size); +int receivePacket(int sock, PPPoEPacket *pkt, int *size); +void fatalSys(char const *str); +void rp_fatal(char const *str); +void printErr(char const *str); +void sysErr(char const *str); +#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); +char *strDup(char const *str); +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); + +#define SET_STRING(var, val) do { if (var) free(var); var = strDup(val); } while(0); + +#define CHECK_ROOM(cursor, start, len) \ +do {\ + if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \ + 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) diff --git a/src/relay.c b/src/relay.c new file mode 100755 index 0000000..2dad044 --- a/dev/null +++ b/src/relay.c @@ -0,0 +1,1559 @@ +/*********************************************************************** +* +* relay.c +* +* Implementation of PPPoE relay +* +* Copyright (C) 2001-2006 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +* $Id$ +* +***********************************************************************/ +static char const RCSID[] = +"$Id$"; + +#define _GNU_SOURCE 1 /* For SA_RESTART */ + +#include "relay.h" + +#include + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_UNISTD_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", 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, "http://www.roaringpenguin.com\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(1); + } + + + 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]); + } + } + +#ifdef USE_LINUX_PACKET +#ifndef HAVE_STRUCT_SOCKADDR_LL + fprintf(stderr, "The PPPoE relay does not work on Linux 2.0 kernels.\n"); + exit(EXIT_FAILURE); +#endif +#endif + + /* 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(0); + } + setsid(); + signal(SIGHUP, SIG_IGN); + i = fork(); + if (i < 0) { + fatalSys("fork"); + } else if (i != 0) { + exit(0); + } + + 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); + i->sessionSock = openInterface(ifname, Eth_PPPOE_Session, 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) +{ + char buf[1024]; + sprintf(buf, "%.256s: %.256s", str, strerror(errno)); + printErr(buf); + 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) +{ + char buf[1024]; + sprintf(buf, "%.256s: %.256s", str, strerror(errno)); + printErr(buf); +} + +/********************************************************************** +*%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(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 (packet.ver != 1 || packet.type != 1) { + return; + } + + /* Validate length */ + if (ntohs(packet.length) + HDR_SIZE > (unsigned int)size) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + return; + } + + /* Drop Ethernet frame padding */ + if ((unsigned int)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 (packet.ver != 1 || packet.type != 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 > (unsigned int)size) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + return; + } + + /* Drop Ethernet frame padding */ + if ((unsigned int)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; + + 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 must be unicast */ + if (NOT_UNICAST(packet->ethHdr.h_source)) { + syslog(LOG_ERR, + "PADO 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, + "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; + int acIndex; + 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; + } + + acIndex = iface - Interfaces; + + /* 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.type = 1; + packet.ver = 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) { + write(CleanPipe[1], "", 1); + } +} + +/********************************************************************** +*%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; + } +} diff --git a/src/relay.h b/src/relay.h new file mode 100755 index 0000000..36d89a7 --- a/dev/null +++ b/src/relay.h @@ -0,0 +1,99 @@ +/********************************************************************** +* +* relay.h +* +* Definitions for PPPoE relay +* +* Copyright (C) 2001-2006 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +* $Id$ +* +***********************************************************************/ + +#include "pppoe.h" + +/* 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 -- cgit