diff options
Diffstat (limited to 'mdk-stage1/rp-pppoe/src')
-rw-r--r-- | mdk-stage1/rp-pppoe/src/Makefile.in | 257 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/common.c | 485 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/config.h.in | 134 | ||||
-rwxr-xr-x | mdk-stage1/rp-pppoe/src/configure | 2356 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/configure.in | 231 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/debug.c | 143 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/discovery.c | 629 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/if.c | 1092 | ||||
-rwxr-xr-x | mdk-stage1/rp-pppoe/src/install-sh | 238 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/md5.c | 246 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/md5.h | 27 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/plugin.c | 397 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/ppp.c | 258 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/pppoe-server.c | 1247 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/pppoe-sniff.c | 258 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/pppoe.c | 834 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/pppoe.h | 331 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/relay.c | 1541 | ||||
-rw-r--r-- | mdk-stage1/rp-pppoe/src/relay.h | 97 |
19 files changed, 10801 insertions, 0 deletions
diff --git a/mdk-stage1/rp-pppoe/src/Makefile.in b/mdk-stage1/rp-pppoe/src/Makefile.in new file mode 100644 index 000000000..8eee012cb --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/Makefile.in @@ -0,0 +1,257 @@ +# @configure_input@ +#*********************************************************************** +# +# Makefile +# +# Makefile for Roaring Penguin's Linux user-space PPPoE client. +# +# 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$ +#*********************************************************************** + +# Version is set ONLY IN THE MAKEFILE! Don't delete this! +VERSION=3.0 + +DEFINES= +prefix=@prefix@ +exec_prefix=@exec_prefix@ +mandir=@mandir@ +docdir=@prefix@/doc/rp-pppoe-$(VERSION) +install=@INSTALL@ +install_dir=@INSTALL@ -d +sbindir=@sbindir@ + +# Plugin for pppd on Linux +LINUX_KERNELMODE_PLUGIN=@LINUX_KERNELMODE_PLUGIN@ +PPPD_INCDIR=@PPPD_INCDIR@ + +# PPPoE relay -- currently only supported on Linux +PPPOE_RELAY=@PPPOE_RELAY@ + +# Program paths +PPPOE_PATH=$(sbindir)/pppoe +PPPD_PATH=@PPPD@ + +# Kernel-mode plugin gets installed here. +PLUGIN_DIR=/etc/ppp/plugins +PLUGIN_PATH=$(PLUGIN_DIR)/rp-pppoe.so + +# Configuration file paths +PPPOESERVER_PPPD_OPTIONS=/etc/ppp/pppoe-server-options + +PATHS='-DPPPOE_PATH="$(PPPOE_PATH)"' '-DPPPD_PATH="$(PPPD_PATH)"' \ + '-DPLUGIN_PATH="$(PLUGIN_PATH)"' \ + '-DPPPOE_SERVER_OPTIONS="$(PPPOESERVER_PPPD_OPTIONS)"' + +CFLAGS= @CFLAGS@ $(DEFINES) $(PATHS) +TARGETS=@TARGETS@ + +all: $(TARGETS) + @echo "" + @echo "Type 'make install' as root to install the software." + +pppoe-sniff: pppoe-sniff.o if.o common.o debug.o + @CC@ -o pppoe-sniff pppoe-sniff.o if.o common.o debug.o $(LIBS) + +pppoe-server: pppoe-server.o if.o debug.o common.o md5.o + @CC@ -o pppoe-server pppoe-server.o if.o debug.o common.o md5.o $(LIBS) + +pppoe: pppoe.o if.o debug.o common.o ppp.o discovery.o + @CC@ -o pppoe pppoe.o if.o debug.o common.o ppp.o discovery.o $(LIBS) + +pppoe-relay: relay.o if.o debug.o common.o + @CC@ -o pppoe-relay relay.o if.o debug.o common.o $(LIBS) + +pppoe.o: pppoe.c pppoe.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o pppoe.o pppoe.c + +discovery.o: discovery.c pppoe.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o discovery.o discovery.c + +ppp.o: ppp.c pppoe.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o ppp.o ppp.c + +md5.o: md5.c md5.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o md5.o md5.c + +pppoe-server.o: pppoe-server.c pppoe.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o pppoe-server.o pppoe-server.c + +pppoe-sniff.o: pppoe-sniff.c pppoe.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o pppoe-sniff.o pppoe-sniff.c + +if.o: if.c pppoe.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o if.o if.c + +common.o: common.c pppoe.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o common.o common.c + +debug.o: debug.c pppoe.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o debug.o debug.c + +relay.o: relay.c relay.h pppoe.h + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o relay.o relay.c + +# Linux-specific plugin +rp-pppoe.so: plugin/libplugin.a plugin/plugin.o + @CC@ -o rp-pppoe.so -shared plugin/plugin.o plugin/libplugin.a + +plugin/plugin.o: plugin.c + @CC@ '-DVERSION="$(VERSION)"' -I$(PPPD_INCDIR) -c -o plugin/plugin.o -fPIC plugin.c + +plugin/libplugin.a: plugin/discovery.o plugin/if.o plugin/common.o plugin/debug.o + ar -rc $@ $^ + +plugin/discovery.o: discovery.c + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o plugin/discovery.o -fPIC discovery.c + +plugin/if.o: if.c + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o plugin/if.o -fPIC if.c + +plugin/debug.o: debug.c + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o plugin/debug.o -fPIC debug.c + +plugin/common.o: common.c + @CC@ $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o plugin/common.o -fPIC common.c + +install: all + -mkdir -p $(RPM_INSTALL_ROOT)$(sbindir) + $(install) -m 755 -s pppoe $(RPM_INSTALL_ROOT)$(sbindir) + $(install) -m 755 -s pppoe-server $(RPM_INSTALL_ROOT)$(sbindir) + if test -x pppoe-relay ; then $(install) -m 755 -s pppoe-relay $(RPM_INSTALL_ROOT)$(sbindir); fi + $(install) -m 755 -s pppoe-sniff $(RPM_INSTALL_ROOT)$(sbindir) + $(install) -m 755 ../scripts/adsl-connect $(RPM_INSTALL_ROOT)$(sbindir) + $(install) -m 755 ../scripts/adsl-start $(RPM_INSTALL_ROOT)$(sbindir) + $(install) -m 755 ../scripts/adsl-status $(RPM_INSTALL_ROOT)$(sbindir) + $(install) -m 755 ../scripts/adsl-stop $(RPM_INSTALL_ROOT)$(sbindir) + $(install) -m 755 ../scripts/adsl-setup $(RPM_INSTALL_ROOT)$(sbindir) + -mkdir -p $(RPM_INSTALL_ROOT)$(docdir) + $(install) -m 644 ../doc/CHANGES $(RPM_INSTALL_ROOT)$(docdir) + $(install) -m 644 ../doc/KERNEL-MODE-PPPOE $(RPM_INSTALL_ROOT)$(docdir) + $(install) -m 644 ../doc/HOW-TO-CONNECT $(RPM_INSTALL_ROOT)$(docdir) + $(install) -m 644 ../doc/LICENSE $(RPM_INSTALL_ROOT)$(docdir) + $(install) -m 644 ../README $(RPM_INSTALL_ROOT)$(docdir) + $(install) -m 644 ../configs/pap-secrets $(RPM_INSTALL_ROOT)$(docdir) + -mkdir -p $(RPM_INSTALL_ROOT)$(mandir)/man8 + for i in $(TARGETS) ; do \ + if test -f ../man/$$i.8 ; then \ + $(install) -m 644 ../man/$$i.8 $(RPM_INSTALL_ROOT)$(mandir)/man8 || exit 1; \ + fi; \ + done + $(install) -m 644 ../man/adsl-start.8 $(RPM_INSTALL_ROOT)$(mandir)/man8 + $(install) -m 644 ../man/adsl-stop.8 $(RPM_INSTALL_ROOT)$(mandir)/man8 + $(install) -m 644 ../man/adsl-status.8 $(RPM_INSTALL_ROOT)$(mandir)/man8 + $(install) -m 644 ../man/adsl-connect.8 $(RPM_INSTALL_ROOT)$(mandir)/man8 + $(install) -m 644 ../man/adsl-setup.8 $(RPM_INSTALL_ROOT)$(mandir)/man8 + -mkdir -p $(RPM_INSTALL_ROOT)$(mandir)/man5 + $(install) -m 644 ../man/pppoe.conf.5 $(RPM_INSTALL_ROOT)$(mandir)/man5 + -mkdir -p $(RPM_INSTALL_ROOT)/etc/ppp + -mkdir -p $(RPM_INSTALL_ROOT)$(PLUGIN_DIR) + -echo "# Directory created by rp-pppoe for kernel-mode plugin" > $(RPM_INSTALL_ROOT)$(PLUGIN_DIR)/README + @if test -r rp-pppoe.so; then $(install) -m 755 rp-pppoe.so $(RPM_INSTALL_ROOT)$(PLUGIN_DIR); fi + @for i in pppoe.conf firewall-standalone firewall-masq ; do \ + if [ ! -f $(RPM_INSTALL_ROOT)/etc/ppp/$$i ] ; then \ + $(install) -m 644 ../configs/$$i $(RPM_INSTALL_ROOT)/etc/ppp ; \ + else \ + echo "NOT overwriting existing $(RPM_INSTALL_ROOT)/etc/ppp/$$i" ;\ + $(install) -m 644 ../configs/$$i $(RPM_INSTALL_ROOT)/etc/ppp/$$i-$(VERSION) ;\ + fi ;\ + done + @if [ ! -f $(RPM_INSTALL_ROOT)$(PPPOESERVER_PPPD_OPTIONS) ] ; then \ + $(install) -m 644 ../configs/pppoe-server-options $(RPM_INSTALL_ROOT)$(PPPOESERVER_PPPD_OPTIONS) ; \ + else \ + echo "NOT overwriting existing $(RPM_INSTALL_ROOT)$(PPPOESERVER_PPPD_OPTIONS)"; \ + $(install) -m 644 ../configs/pppoe-server-options $(RPM_INSTALL_ROOT)$(PPPOESERVER_PPPD_OPTIONS)-example ; \ + fi + @if [ -f /etc/redhat-release ] ; then \ + echo "Looks like a Red Hat system; installing $(RPM_INSTALL_ROOT)/etc/rc.d/init.d/adsl" ; \ + mkdir -p $(RPM_INSTALL_ROOT)/etc/rc.d/init.d ;\ + $(install) -m 755 ../scripts/adsl-init $(RPM_INSTALL_ROOT)/etc/rc.d/init.d/adsl ; \ + fi + @if [ -f /etc/turbolinux-release ] ; then \ + echo "Looks like a TurboLinux system; installing $(RPM_INSTALL_ROOT)/etc/rc.d/init.d/adsl" ; \ + mkdir -p $(RPM_INSTALL_ROOT)/etc/rc.d/init.d ;\ + $(install) -m 755 adsl-init-turbolinux $(RPM_INSTALL_ROOT)/etc/rc.d/init.d/adsl ; \ + fi + @if [ -f /etc/SuSE-release ] ; then \ + echo "Looks like a SuSE Linux system; installing $(RPM_INSTALL_ROOT)/etc/rc.d/init.d/adsl" ; \ + mkdir -p $(RPM_INSTALL_ROOT)/etc/rc.d/init.d ;\ + $(install) -m 755 ../scripts/adsl-init-suse $(RPM_INSTALL_ROOT)/etc/rc.d/init.d/adsl ; \ + fi + @echo "" + @echo "Type 'adsl-setup' to configure the software." + +distro: + cd ..; \ + rm -rf rp-pppoe-$(VERSION) ; \ + mkdir rp-pppoe-$(VERSION) || exit 1; \ + for i in README go go-gui rp-pppoe.spec rp-pppoe-gui.spec; do \ + cp $$i rp-pppoe-$(VERSION) || exit 1; \ + done ; \ + mkdir rp-pppoe-$(VERSION)/gui || exit 1; \ + for i in Makefile.in tkpppoe.in wrapper.c tkpppoe.1 pppoe-wrapper.1 ; do \ + cp gui/$$i rp-pppoe-$(VERSION)/gui || exit 1; \ + done; \ + mkdir rp-pppoe-$(VERSION)/gui/html || exit 1; \ + for i in mainwin-busy.png mainwin-nonroot.png mainwin.png props-advanced.png props-basic.png props-nic.png props-options.png tkpppoe.html ; do \ + cp gui/html/$$i rp-pppoe-$(VERSION)/gui/html || exit 1; \ + done; \ + mkdir rp-pppoe-$(VERSION)/configs || exit 1; \ + for i in firewall-masq firewall-standalone pap-secrets pppoe-server-options pppoe.conf ; do \ + cp configs/$$i rp-pppoe-$(VERSION)/configs || exit 1; \ + done ; \ + mkdir rp-pppoe-$(VERSION)/doc || exit 1; \ + for i in CHANGES KERNEL-MODE-PPPOE HOW-TO-CONNECT LICENSE PROBLEMS ; do \ + cp doc/$$i rp-pppoe-$(VERSION)/doc || exit 1; \ + done; \ + mkdir rp-pppoe-$(VERSION)/man || exit 1; \ + for i in adsl-connect.8 adsl-setup.8 adsl-start.8 adsl-status.8 adsl-stop.8 pppoe-server.8 pppoe-sniff.8 pppoe.8 pppoe-relay.8 pppoe.conf.5 ; do \ + cp man/$$i rp-pppoe-$(VERSION)/man || exit 1; \ + done; \ + mkdir rp-pppoe-$(VERSION)/scripts || exit 1; \ + for i in adsl-connect.in adsl-init-suse.in adsl-init-turbolinux.in adsl-init.in adsl-setup.in adsl-start.in adsl-stop.in adsl-status ; do \ + cp scripts/$$i rp-pppoe-$(VERSION)/scripts || exit 1; \ + done; \ + mkdir rp-pppoe-$(VERSION)/src || exit 1; \ + for i in Makefile.in install-sh common.c config.h.in configure configure.in debug.c discovery.c if.c md5.c md5.h ppp.c pppoe-server.c pppoe-sniff.c pppoe.c pppoe.h plugin.c relay.c relay.h ; do \ + cp src/$$i rp-pppoe-$(VERSION)/src || exit 1; \ + done; \ + mkdir rp-pppoe-$(VERSION)/src/plugin || exit 1; \ + tar cvf rp-pppoe-$(VERSION).tar rp-pppoe-$(VERSION)/* ; \ + gzip -f -v -9 rp-pppoe-$(VERSION).tar ; \ + +rpms: distro + cp ../rp-pppoe-$(VERSION).tar.gz /usr/src/redhat/SOURCES + cd ..; \ + rpm -ba rp-pppoe.spec; \ + rpm -ba rp-pppoe-gui.spec + +clean: + rm -f *.o pppoe pppoe-sniff pppoe-server core rp-pppoe.so plugin/*.o plugin/libplugin.a *~ + +distclean: clean + rm -f Makefile config.h config.cache config.log config.status + rm -f ../scripts/adsl-connect ../scripts/adsl-start ../scripts/adsl-stop ../scripts/adsl-init ../scripts/adsl-setup ../scripts/adsl-init-suse ../scripts/adsl-init-turbolinux + +update-version: + sed -e 's/^Version: .*$$/Version: $(VERSION)/' ../rp-pppoe.spec > ../rp-pppoe.spec.new && mv ../rp-pppoe.spec.new ../rp-pppoe.spec + sed -e 's+^Source: .*$$+Source: http://www.roaringpenguin.com/pppoe/rp-pppoe-$(VERSION).tar.gz+' ../rp-pppoe.spec > ../rp-pppoe.spec.new && mv ../rp-pppoe.spec.new ../rp-pppoe.spec + sed -e 's/^Version: .*$$/Version: $(VERSION)/' ../rp-pppoe-gui.spec > ../rp-pppoe-gui.spec.new && mv ../rp-pppoe-gui.spec.new ../rp-pppoe-gui.spec + sed -e 's+^Source: .*$$+Source: http://www.roaringpenguin.com/pppoe/rp-pppoe-$(VERSION).tar.gz+' ../rp-pppoe-gui.spec > ../rp-pppoe-gui.spec.new && mv ../rp-pppoe-gui.spec.new ../rp-pppoe-gui.spec + sed -e 's+^Requires: rp-pppoe >=.*$$+Requires: rp-pppoe >= $(VERSION)+' ../rp-pppoe-gui.spec > ../rp-pppoe-gui.spec.new && mv ../rp-pppoe-gui.spec.new ../rp-pppoe-gui.spec + +# Convenience target for David! Don't try to use this one. +km: + ./configure --enable-plugin=/home/dfs/Archive/PPP/ppp-2.4.0.pppoe4-patched-dfs + +.PHONY: update-version + +.PHONY: clean + +.PHONY: distclean + +.PHONY: rpms diff --git a/mdk-stage1/rp-pppoe/src/common.c b/mdk-stage1/rp-pppoe/src/common.c new file mode 100644 index 000000000..b27302104 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/common.c @@ -0,0 +1,485 @@ +/*********************************************************************** +* +* 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. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/********************************************************************** +*%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: 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]; + 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) { + sum += * (UINT16_t *) addr; + addr += 2; + count -= 2; + } + if (count > 0) { + sum += *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; + + /* Is it IPv4? */ + if (packet->payload[0] != 0x00 || + packet->payload[1] != 0x21) { + /* Nope, ignore it */ + return; + } + + ipHdr = packet->payload + 2; + + /* Is it too short? */ + len = (int) ntohs(packet->length); + if (len < 42) { + /* 20 byte IP header; 20 byte TCP header; 2 byte PPP protocol */ + return; + } + + /* Verify once more that it's IPv4 */ + if ((ipHdr[0] & 0xF0) != 0x40) { + return; + } + + /* Is it a fragment that's not at the beginning of the packet? */ + if ((ipHdr[6] & 0x1F) || ipHdr[7]) { + /* Yup, don't touch! */ + return; + } + /* Is it TCP? */ + if (ipHdr[9] != 0x06) { + return; + } + + /* Get start of TCP header */ + tcpHdr = ipHdr + (ipHdr[0] & 0x0F) * 4; + + /* Is SYN set? */ + if (!(tcpHdr[13] & 0x02)) { + return; + } + + /* Compute and verify TCP checksum -- do not touch a packet with a bad + checksum */ + csum = computeTCPChecksum(ipHdr, tcpHdr); + if (csum) { + syslog(LOG_ERR, "Bad TCP checksum %x", (unsigned int) csum); + + /* Upper layers will drop it */ + return; + } + + /* Look for existing MSS option */ + endHdr = tcpHdr + ((tcpHdr[12] & 0xF0) >> 2); + opt = tcpHdr + 20; + while (opt < endHdr) { + if (!*opt) break; /* End of options */ + switch(*opt) { + case 1: + opt++; + break; + + case 2: + if (opt[1] != 4) { + /* Something fishy about MSS option length. */ + syslog(LOG_ERR, + "Bogus length for MSS option (%u) from %u.%u.%u.%u", + (unsigned int) opt[1], + (unsigned int) ipHdr[12], + (unsigned int) ipHdr[13], + (unsigned int) ipHdr[14], + (unsigned int) ipHdr[15]); + return; + } + mssopt = opt; + break; + default: + if (opt[1] < 2) { + /* Someone's trying to attack us? */ + syslog(LOG_ERR, + "Bogus TCP option length (%u) from %u.%u.%u.%u", + (unsigned int) opt[1], + (unsigned int) ipHdr[12], + (unsigned int) ipHdr[13], + (unsigned int) ipHdr[14], + (unsigned int) ipHdr[15]); + return; + } + opt += (opt[1]); + break; + } + /* Found existing MSS option? */ + if (mssopt) break; + } + + /* If MSS exists and it's low enough, do nothing */ + if (mssopt) { + unsigned mss = mssopt[2] * 256 + mssopt[3]; + if (mss <= clampMss) { + return; + } + + mssopt[2] = (((unsigned) clampMss) >> 8) & 0xFF; + mssopt[3] = ((unsigned) clampMss) & 0xFF; + } else { + /* No MSS option. Don't add one; we'll have to use 536. */ + return; + } + + /* Recompute TCP checksum */ + tcpHdr[16] = 0; + tcpHdr[17] = 0; + csum = computeTCPChecksum(ipHdr, tcpHdr); + (* (UINT16_t *) (tcpHdr+16)) = csum; +} + +/*********************************************************************** +*%FUNCTION: sendPADT +*%ARGUMENTS: +* conn -- PPPoE connection +* msg -- if non-NULL, extra error message to include in PADT packet. +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Sends a PADT packet +***********************************************************************/ +void +sendPADT(PPPoEConnection *conn, char const *msg) +{ + PPPoEPacket packet; + unsigned char *cursor = packet.payload; + + UINT16_t plen = 0; + + /* Do nothing if no session established yet */ + if (!conn->session) return; + + /* Do nothing if no discovery socket */ + if (conn->discoverySocket < 0) return; + + memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); + memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); + + packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); + packet.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(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)); + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "SENT"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } + syslog(LOG_INFO,"Sent PADT"); +} + +/********************************************************************** +*%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) +{ + switch(type) { + case TAG_SERVICE_NAME_ERROR: + syslog(LOG_ERR, "PADT: Service-Name-Error: %.*s", (int) len, data); + fprintf(stderr, "PADT: Service-Name-Error: %.*s\n", (int) len, data); + break; + case TAG_AC_SYSTEM_ERROR: + syslog(LOG_ERR, "PADT: System-Error: %.*s", (int) len, data); + fprintf(stderr, "PADT: System-Error: %.*s\n", (int) len, data); + break; + case TAG_GENERIC_ERROR: + syslog(LOG_ERR, "PADT: Generic-Error: %.*s", (int) len, data); + fprintf(stderr, "PADT: Generic-Error: %.*s\n", (int) len, data); + break; + } +} + diff --git a/mdk-stage1/rp-pppoe/src/config.h.in b/mdk-stage1/rp-pppoe/src/config.h.in new file mode 100644 index 000000000..e3340389d --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/config.h.in @@ -0,0 +1,134 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef pid_t + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* 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. */ +#undef STDC_HEADERS + +/* Define if you can safely include both <sys/time.h> and <time.h>. */ +#undef TIME_WITH_SYS_TIME + +/* Define if your <sys/time.h> declares struct tm. */ +#undef TM_IN_SYS_TIME + +#undef HAVE_STRUCT_SOCKADDR_LL + +/* The number of bytes in a unsigned int. */ +#undef SIZEOF_UNSIGNED_INT + +/* The number of bytes in a unsigned long. */ +#undef SIZEOF_UNSIGNED_LONG + +/* The number of bytes in a unsigned short. */ +#undef SIZEOF_UNSIGNED_SHORT + +/* Define if you have the select function. */ +#undef HAVE_SELECT + +/* Define if you have the socket function. */ +#undef HAVE_SOCKET + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the strtol function. */ +#undef HAVE_STRTOL + +/* Define if you have the <asm/types.h> header file. */ +#undef HAVE_ASM_TYPES_H + +/* Define if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the <getopt.h> header file. */ +#undef HAVE_GETOPT_H + +/* Define if you have the <linux/if_ether.h> header file. */ +#undef HAVE_LINUX_IF_ETHER_H + +/* Define if you have kernel-mode PPPoE in Linux file. */ +#undef HAVE_LINUX_KERNEL_PPPOE + +/* Define if you have the <linux/if_packet.h> header file. */ +#undef HAVE_LINUX_IF_PACKET_H + +/* Define if you have the <linux/if_pppox.h> header file. */ +#undef HAVE_LINUX_IF_PPPOX_H + +/* Define if you have the <net/bpf.h> header file. */ +#undef HAVE_NET_BPF_H + +/* Define if you have the <net/if_arp.h> header file. */ +#undef HAVE_NET_IF_ARP_H + +/* Define if you have the <net/ethernet.h> header file. */ +#undef HAVE_NET_ETHERNET_H + +/* Define if you have the <net/if.h> header file. */ +#undef HAVE_NET_IF_H + +/* Define if you have the <linux/if.h> header file. */ +#undef HAVE_LINUX_IF_H + +/* Define if you have the <net/if_dl.h> header file. */ +#undef HAVE_NET_IF_DL_H + +/* Define if you have the <net/if_ether.h> header file. */ +#undef HAVE_NET_IF_ETHER_H + +/* Define if you have the <net/if_types.h> header file. */ +#undef HAVE_NET_IF_TYPES_H + +/* Define if you have the <netinet/if_ether.h> header file. */ +#undef HAVE_NETINET_IF_ETHER_H + +/* Define if you have the <netpacket/packet.h> header file. */ +#undef HAVE_NETPACKET_PACKET_H + +/* Define if you have the <sys/cdefs.h> header file. */ +#undef HAVE_SYS_CDEFS_H + +/* Define if you have the <sys/dlpi.h> header file. */ +#undef HAVE_SYS_DLPI_H + +/* Define if you have the <sys/ioctl.h> header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the <sys/param.h> header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define if you have the <sys/socket.h> header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the <sys/uio.h> header file. */ +#undef HAVE_SYS_UIO_H + +/* Define if you have the <syslog.h> header file. */ +#undef HAVE_SYSLOG_H + +/* Define if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* 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 */ +#undef PACK_BITFIELDS_REVERSED diff --git a/mdk-stage1/rp-pppoe/src/configure b/mdk-stage1/rp-pppoe/src/configure new file mode 100755 index 000000000..eede451a1 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/configure @@ -0,0 +1,2356 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_default_prefix=/usr +ac_help="$ac_help + --enable-plugin=pppd_src_path build pppd plugin" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=pppoe.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + + + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:536: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:566: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:617: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:649: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 660 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:665: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:691: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:696: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <<EOF +#ifdef __GNUC__ + yes; +#endif +EOF +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:705: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:724: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + + + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:758: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext <<EOF +#line 773 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:779: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext <<EOF +#line 790 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:796: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext <<EOF +#line 807 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:813: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:838: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 843 "configure" +#include "confdefs.h" +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:851: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 868 "configure" +#include "confdefs.h" +#include <string.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 886 "configure" +#include "confdefs.h" +#include <stdlib.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext <<EOF +#line 907 "configure" +#include "confdefs.h" +#include <ctype.h> +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:918: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6 +echo "configure:942: checking for sys/wait.h that is POSIX.1 compatible" >&5 +if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 947 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/wait.h> +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif +int main() { +int s; +wait (&s); +s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; +; return 0; } +EOF +if { (eval echo configure:963: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_sys_wait_h=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_sys_wait_h=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&6 +if test $ac_cv_header_sys_wait_h = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_SYS_WAIT_H 1 +EOF + +fi + +for ac_hdr in fcntl.h sys/ioctl.h sys/time.h syslog.h unistd.h net/if_arp.h netinet/if_ether.h getopt.h sys/uio.h sys/param.h fcntl.h net/bpf.h netpacket/packet.h net/ethernet.h asm/types.h linux/if_packet.h linux/if_ether.h linux/if_pppox.h sys/socket.h sys/cdefs.h linux/if.h net/if.h net/if_dl.h net/if_ether.h net/if_types.h netinet/if_ether.h net/if_types.h net/if_dl.h sys/dlpi.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:987: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 992 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:997: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1025: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1030 "configure" +#include "confdefs.h" + +int main() { + +/* Ultrix mips cc rejects this. */ +typedef int charset[2]; const charset x; +/* SunOS 4.1.1 cc rejects this. */ +char const *const *ccp; +char **p; +/* NEC SVR4.0.2 mips cc rejects this. */ +struct point {int x, y;}; +static struct point const zero = {0,0}; +/* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in an arm + of an if-expression whose if-part is not a constant expression */ +const char *g = "string"; +ccp = &g + (g ? g-g : 0); +/* HPUX 7.0 cc rejects these. */ +++ccp; +p = (char**) ccp; +ccp = (char const *const *) p; +{ /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; +} +{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; +} +{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; +} +{ /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1079: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for pid_t""... $ac_c" 1>&6 +echo "configure:1100: checking for pid_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1105 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_pid_t=yes +else + rm -rf conftest* + ac_cv_type_pid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_pid_t" 1>&6 +if test $ac_cv_type_pid_t = no; then + cat >> confdefs.h <<\EOF +#define pid_t int +EOF + +fi + +echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 +echo "configure:1133: checking whether time.h and sys/time.h may both be included" >&5 +if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1138 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/time.h> +#include <time.h> +int main() { +struct tm *tp; +; return 0; } +EOF +if { (eval echo configure:1147: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_time=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_time=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_time" 1>&6 +if test $ac_cv_header_time = yes; then + cat >> confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + +echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 +echo "configure:1168: checking whether struct tm is in sys/time.h or time.h" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1173 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <time.h> +int main() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if { (eval echo configure:1181: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm" 1>&6 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + + +# Extract the first word of "echo", so it can be a program name with args. +set dummy echo; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1205: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_ECHO'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$ECHO" in + /*) + ac_cv_path_ECHO="$ECHO" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_ECHO="$ECHO" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="/usr/ucb/bin:$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_ECHO="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_ECHO" && ac_cv_path_ECHO="""" + ;; +esac +fi +ECHO="$ac_cv_path_ECHO" +if test -n "$ECHO"; then + echo "$ac_t""$ECHO" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +$ECHO -n "checking for struct sockaddr_ll... " +cat > conftest.$ac_ext <<EOF +#line 1241 "configure" +#include "confdefs.h" +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> + +int main() { +struct sockaddr_ll sa; +; return 0; } +EOF +if { (eval echo configure:1251: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_sockaddr_ll=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_sockaddr_ll=no +fi +rm -f conftest* +$ECHO $ac_cv_struct_sockaddr_ll +if test "$ac_cv_struct_sockaddr_ll" = yes ; then +cat >> confdefs.h <<\EOF +#define HAVE_STRUCT_SOCKADDR_LL 1 +EOF + +fi + +$ECHO -n "checking for N_HDLC line discipline... " +cat > conftest.$ac_ext <<EOF +#line 1271 "configure" +#include "confdefs.h" +#include <linux/termios.h> +int main() { +int x = N_HDLC; +; return 0; } +EOF +if { (eval echo configure:1278: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_n_hdlc=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_n_hdlc=no +fi +rm -f conftest* +$ECHO $ac_cv_n_hdlc +if test "$ac_cv_n_hdlc" = yes ; then +cat >> confdefs.h <<\EOF +#define HAVE_N_HDLC 1 +EOF + +fi + +# Check whether --enable-plugin or --disable-plugin was given. +if test "${enable_plugin+set}" = set; then + enableval="$enable_plugin" + ac_cv_pluginpath=$enableval +else + ac_cv_pluginpath=no +fi + + +LINUX_KERNELMODE_PLUGIN="" +PPPD_INCDIR="" +if test "$ac_cv_header_linux_if_pppox_h" = yes ; then + if test "$ac_cv_pluginpath" != no ; then + LINUX_KERNELMODE_PLUGIN=rp-pppoe.so + PPPD_INCDIR=$ac_cv_pluginpath + fi +fi + + + + +PPPOE_RELAY="" +if test "`uname -s`" = "Linux" ; then + PPPOE_RELAY=pppoe-relay +fi + + +echo $ac_n "checking for 8-bit clean memcmp""... $ac_c" 1>&6 +echo "configure:1324: checking for 8-bit clean memcmp" >&5 +if eval "test \"`echo '$''{'ac_cv_func_memcmp_clean'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_clean=no +else + cat > conftest.$ac_ext <<EOF +#line 1332 "configure" +#include "confdefs.h" + +main() +{ + char c0 = 0x40, c1 = 0x80, c2 = 0x81; + exit(memcmp(&c0, &c2, 1) < 0 && memcmp(&c1, &c2, 1) < 0 ? 0 : 1); +} + +EOF +if { (eval echo configure:1342: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_func_memcmp_clean=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_memcmp_clean=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_memcmp_clean" 1>&6 +test $ac_cv_func_memcmp_clean = no && LIBOBJS="$LIBOBJS memcmp.${ac_objext}" + +echo $ac_n "checking whether setvbuf arguments are reversed""... $ac_c" 1>&6 +echo "configure:1360: checking whether setvbuf arguments are reversed" >&5 +if eval "test \"`echo '$''{'ac_cv_func_setvbuf_reversed'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1368 "configure" +#include "confdefs.h" +#include <stdio.h> +/* If setvbuf has the reversed format, exit 0. */ +main () { + /* This call has the arguments reversed. + A reversed system may check and see that the address of main + is not _IOLBF, _IONBF, or _IOFBF, and return nonzero. */ + if (setvbuf(stdout, _IOLBF, (char *) main, BUFSIZ) != 0) + exit(1); + putc('\r', stdout); + exit(0); /* Non-reversed systems segv here. */ +} +EOF +if { (eval echo configure:1382: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_func_setvbuf_reversed=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_setvbuf_reversed=no +fi +rm -fr conftest* +fi + +rm -f core core.* *.core +fi + +echo "$ac_t""$ac_cv_func_setvbuf_reversed" 1>&6 +if test $ac_cv_func_setvbuf_reversed = yes; then + cat >> confdefs.h <<\EOF +#define SETVBUF_REVERSED 1 +EOF + +fi + +echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 +echo "configure:1406: checking return type of signal handlers" >&5 +if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1411 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <signal.h> +#ifdef signal +#undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int main() { +int i; +; return 0; } +EOF +if { (eval echo configure:1428: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_type_signal=void +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_type_signal=int +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_type_signal" 1>&6 +cat >> confdefs.h <<EOF +#define RETSIGTYPE $ac_cv_type_signal +EOF + + +for ac_func in select socket strerror strtol +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1449: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1454 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1477: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1532: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +echo $ac_n "checking size of unsigned short""... $ac_c" 1>&6 +echo "configure:1586: checking size of unsigned short" >&5 +if eval "test \"`echo '$''{'ac_cv_sizeof_unsigned_short'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1594 "configure" +#include "confdefs.h" +#include <stdio.h> +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(unsigned short)); + exit(0); +} +EOF +if { (eval echo configure:1605: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_sizeof_unsigned_short=`cat conftestval` +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_sizeof_unsigned_short=0 +fi +rm -fr conftest* +fi + +fi +echo "$ac_t""$ac_cv_sizeof_unsigned_short" 1>&6 +cat >> confdefs.h <<EOF +#define SIZEOF_UNSIGNED_SHORT $ac_cv_sizeof_unsigned_short +EOF + + +echo $ac_n "checking size of unsigned int""... $ac_c" 1>&6 +echo "configure:1625: checking size of unsigned int" >&5 +if eval "test \"`echo '$''{'ac_cv_sizeof_unsigned_int'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1633 "configure" +#include "confdefs.h" +#include <stdio.h> +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(unsigned int)); + exit(0); +} +EOF +if { (eval echo configure:1644: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_sizeof_unsigned_int=`cat conftestval` +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_sizeof_unsigned_int=0 +fi +rm -fr conftest* +fi + +fi +echo "$ac_t""$ac_cv_sizeof_unsigned_int" 1>&6 +cat >> confdefs.h <<EOF +#define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int +EOF + + +echo $ac_n "checking size of unsigned long""... $ac_c" 1>&6 +echo "configure:1664: checking size of unsigned long" >&5 +if eval "test \"`echo '$''{'ac_cv_sizeof_unsigned_long'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1672 "configure" +#include "confdefs.h" +#include <stdio.h> +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(unsigned long)); + exit(0); +} +EOF +if { (eval echo configure:1683: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_sizeof_unsigned_long=`cat conftestval` +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_sizeof_unsigned_long=0 +fi +rm -fr conftest* +fi + +fi +echo "$ac_t""$ac_cv_sizeof_unsigned_long" 1>&6 +cat >> confdefs.h <<EOF +#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long +EOF + + + +# Extract the first word of "pppd", so it can be a program name with args. +set dummy pppd; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1706: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_PPPD'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$PPPD" in + /*) + ac_cv_path_PPPD="$PPPD" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_PPPD="$PPPD" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH:/sbin:/usr/sbin:/usr/local/sbin" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_PPPD="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_PPPD" && ac_cv_path_PPPD="NOTFOUND" + ;; +esac +fi +PPPD="$ac_cv_path_PPPD" +if test -n "$PPPD"; then + echo "$ac_t""$PPPD" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +# Extract the first word of "setsid", so it can be a program name with args. +set dummy setsid; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1743: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_SETSID'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$SETSID" in + /*) + ac_cv_path_SETSID="$SETSID" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_SETSID="$SETSID" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH:/sbin:/usr/sbin:/usr/local/sbin" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_SETSID="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_SETSID" && ac_cv_path_SETSID="""" + ;; +esac +fi +SETSID="$ac_cv_path_SETSID" +if test -n "$SETSID"; then + echo "$ac_t""$SETSID" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +# Extract the first word of "id", so it can be a program name with args. +set dummy id; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1780: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_ID'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$ID" in + /*) + ac_cv_path_ID="$ID" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_ID="$ID" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="/usr/xpg4/bin:$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_ID="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_ID" && ac_cv_path_ID="""" + ;; +esac +fi +ID="$ac_cv_path_ID" +if test -n "$ID"; then + echo "$ac_t""$ID" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +$ECHO -n "checking for Linux 2.4.X kernel-mode PPPoE support..." +if test "`uname -s`" = "Linux" ; then +if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1820 "configure" +#include "confdefs.h" +#include <sys/socket.h> +#include <net/ethernet.h> +#include <linux/if.h> +#include <linux/if_pppox.h> +int main() +{ + if (socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE) >= 0) return 0; else return 1; +} + +EOF +if { (eval echo configure:1832: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_linux_kernel_pppoe=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_linux_kernel_pppoe=no +fi +rm -fr conftest* +fi + +else + ac_cv_linux_kernel_pppoe=no +fi + +$ECHO $ac_cv_linux_kernel_pppoe +if test "$ac_cv_linux_kernel_pppoe" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_LINUX_KERNEL_PPPOE 1 +EOF + +fi + +if test "$GCC" = yes; then + CFLAGS="$CFLAGS -Wall -Wstrict-prototypes -ansi -pedantic" +fi + +if test "$PPPD" = "NOTFOUND"; then + $ECHO "" + $ECHO "*** Oops! I couldn't find pppd, the PPP daemon anywhere." + $ECHO "*** You must install pppd, version 2.3.10 or later." + $ECHO "*** I will keep going, but it may not work." + $ECHO "" +fi + + +PPPD_VERSION=`$PPPD --version 2>&1 | awk '{print $NF}'` + +case "$PPPD_VERSION" in +1.*|2.0.*|2.1.*|2.2.*|2.3.0|2.3.1|2.3.2|2.3.3|2.3.4|2.3.5|2.3.6) + $ECHO "" + $ECHO "*** Oops! Your version of pppd is $PPPD_VERSION, which is too old." + $ECHO "*** You need at least 2.3.7 (2.3.10 or newer recommended.)" + $ECHO "*** I will keep going, but it may not work." + $ECHO "" + ;; + +2.3.7|2.3.8|2.3.9) + $ECHO "" + $ECHO "*** Warning. Your version of pppd is $PPPD_VERSION. You will" + $ECHO "*** not be able to use connect-on-demand. Upgrade to pppd" + $ECHO "*** 2.3.10 or newer if you need connect-on-demand." + $ECHO "" + ;; + +2*|3*|4*|5*|6*|7*|8*|9*) + ;; + +*) + $ECHO "" + $ECHO "*** Oops. I cannot figure out what version of pppd you have." + $ECHO "*** All I got back was '$PPPD_VERSION'" + $ECHO "*** I will keep going, but it may not work." + $ECHO "" + ;; +esac + +$ECHO -n "checking packing order of bit fields... " +if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1905 "configure" +#include "confdefs.h" + +union foo { + struct bar { + unsigned int ver:4; + unsigned int type:4; + } bb; + unsigned char baz; +}; + +int +main(void) +{ + union foo x; + x.bb.ver = 1; + x.bb.type = 2; + if (x.baz == 0x21) { + return 1; + } else if (x.baz == 0x12) { + return 0; + } else { + return 2; + } +} +EOF +if { (eval echo configure:1931: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + PACK=normal +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + PACK=rev +fi +rm -fr conftest* +fi + + +if test "$PACK" = "rev" ; then + $ECHO "reversed" + cat >> confdefs.h <<\EOF +#define PACK_BITFIELDS_REVERSED 1 +EOF + +else + $ECHO "normal" +fi + +# Sigh... got to fix this up for tcl +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Fully resolve WRAPPER for Tcl script. +WRAPPER=${sbindir}/pppoe-wrapper +eval "WRAPPER=${WRAPPER}" +eval "WRAPPER=${WRAPPER}" + + +# Determine what targets to build +TARGETS="pppoe pppoe-server" + +# pppoe-sniff is built only on Linux and Solaris +if test "$ac_cv_header_linux_if_packet_h" = "yes" -o "$ac_cv_header_sys_dlpi_h" = "yes" ; then + TARGETS="$TARGETS pppoe-sniff" +fi + +# pppoe-relay is built only on Linux +if test "$ac_cv_header_linux_if_packet_h" = "yes" ; then + TARGETS="$TARGETS pppoe-relay" +fi + +# plugin is built only if we have kernel support +if test -n "$LINUX_KERNELMODE_PLUGIN" ; then + TARGETS="$TARGETS $LINUX_KERNELMODE_PLUGIN" +fi + + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS <<EOF +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile ../scripts/adsl-connect ../scripts/adsl-start ../scripts/adsl-stop ../scripts/adsl-init ../scripts/adsl-init-suse ../scripts/adsl-init-turbolinux ../scripts/adsl-setup ../gui/Makefile ../gui/tkpppoe config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS <<EOF + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g +s%@ECHO@%$ECHO%g +s%@LINUX_KERNELMODE_PLUGIN@%$LINUX_KERNELMODE_PLUGIN%g +s%@PPPD_INCDIR@%$PPPD_INCDIR%g +s%@PPPOE_RELAY@%$PPPOE_RELAY%g +s%@LIBOBJS@%$LIBOBJS%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@PPPD@%$PPPD%g +s%@SETSID@%$SETSID%g +s%@ID@%$ID%g +s%@WRAPPER@%$WRAPPER%g +s%@TARGETS@%$TARGETS%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <<EOF + +CONFIG_FILES=\${CONFIG_FILES-"Makefile ../scripts/adsl-connect ../scripts/adsl-start ../scripts/adsl-stop ../scripts/adsl-init ../scripts/adsl-init-suse ../scripts/adsl-init-turbolinux ../scripts/adsl-setup ../gui/Makefile ../gui/tkpppoe"} +EOF +cat >> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <<EOF + CONFIG_HEADERS="config.h" +EOF +cat >> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <<EOF + +EOF +cat >> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + + +$ECHO "" +$ECHO "On this platform, the following targets will be built:" +$ECHO " $TARGETS" +$ECHO "" +$ECHO "Type 'make' to compile the software." diff --git a/mdk-stage1/rp-pppoe/src/configure.in b/mdk-stage1/rp-pppoe/src/configure.in new file mode 100644 index 000000000..c11690179 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/configure.in @@ -0,0 +1,231 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(pppoe.c) + +AC_CONFIG_HEADER(config.h) + +AC_PREFIX_DEFAULT(/usr) + +dnl Checks for programs. +AC_PROG_CC + +dnl Checks for libraries. + +dnl Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(fcntl.h sys/ioctl.h sys/time.h syslog.h unistd.h net/if_arp.h netinet/if_ether.h getopt.h sys/uio.h sys/param.h fcntl.h net/bpf.h netpacket/packet.h net/ethernet.h asm/types.h linux/if_packet.h linux/if_ether.h linux/if_pppox.h sys/socket.h sys/cdefs.h linux/if.h net/if.h net/if_dl.h net/if_ether.h net/if_types.h netinet/if_ether.h net/if_types.h net/if_dl.h sys/dlpi.h ) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_HEADER_TIME +AC_STRUCT_TM + +dnl Check for an echo which supports -n -- another hack for Solaris +AC_PATH_PROG(ECHO, echo, "", /usr/ucb/bin:$PATH) + +dnl Check for sockaddr_ll +$ECHO -n "checking for struct sockaddr_ll... " +AC_TRY_COMPILE([#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +], [struct sockaddr_ll sa;], +ac_cv_struct_sockaddr_ll=yes, ac_cv_struct_sockaddr_ll=no) +$ECHO $ac_cv_struct_sockaddr_ll +if test "$ac_cv_struct_sockaddr_ll" = yes ; then +AC_DEFINE(HAVE_STRUCT_SOCKADDR_LL) +fi + +dnl Check for N_HDLC line discipline +$ECHO -n "checking for N_HDLC line discipline... " +AC_TRY_COMPILE([#include <linux/termios.h>], + [int x = N_HDLC;], + ac_cv_n_hdlc=yes, ac_cv_n_hdlc=no) +$ECHO $ac_cv_n_hdlc +if test "$ac_cv_n_hdlc" = yes ; then +AC_DEFINE(HAVE_N_HDLC) +fi + +AC_ARG_ENABLE(plugin, [ --enable-plugin=pppd_src_path build pppd plugin], ac_cv_pluginpath=$enableval, ac_cv_pluginpath=no) + +dnl Determine whether or not to build Linux pppd plugin +LINUX_KERNELMODE_PLUGIN="" +PPPD_INCDIR="" +if test "$ac_cv_header_linux_if_pppox_h" = yes ; then + if test "$ac_cv_pluginpath" != no ; then + LINUX_KERNELMODE_PLUGIN=rp-pppoe.so + PPPD_INCDIR=$ac_cv_pluginpath + fi +fi + +AC_SUBST(LINUX_KERNELMODE_PLUGIN) +AC_SUBST(PPPD_INCDIR) + +dnl Determine whether or not to build PPPoE relay +PPPOE_RELAY="" +if test "`uname -s`" = "Linux" ; then + PPPOE_RELAY=pppoe-relay +fi +AC_SUBST(PPPOE_RELAY) + +dnl Checks for library functions. +AC_FUNC_MEMCMP +AC_FUNC_SETVBUF_REVERSED +AC_TYPE_SIGNAL +AC_CHECK_FUNCS(select socket strerror strtol) +AC_PROG_INSTALL + +dnl Integer sizes +AC_CHECK_SIZEOF(unsigned short) +AC_CHECK_SIZEOF(unsigned int) +AC_CHECK_SIZEOF(unsigned long) + +dnl Check for location of pppd +AC_PATH_PROG(PPPD, pppd, NOTFOUND, $PATH:/sbin:/usr/sbin:/usr/local/sbin) + +dnl Check for setsid (probably Linux-specific) +AC_PATH_PROG(SETSID, setsid, "", $PATH:/sbin:/usr/sbin:/usr/local/sbin) + +dnl Check for an "id" which accepts "-u" option -- hack for Solaris. +AC_PATH_PROG(ID, id, "", /usr/xpg4/bin:$PATH) + +dnl Check for Linux-specific kernel support for PPPoE +$ECHO -n "checking for Linux 2.4.X kernel-mode PPPoE support..." +if test "`uname -s`" = "Linux" ; then +AC_TRY_RUN([#include <sys/socket.h> +#include <net/ethernet.h> +#include <linux/if.h> +#include <linux/if_pppox.h> +int main() +{ + if (socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE) >= 0) return 0; else return 1; +} +], ac_cv_linux_kernel_pppoe=yes, ac_cv_linux_kernel_pppoe=no) +else + ac_cv_linux_kernel_pppoe=no +fi + +$ECHO $ac_cv_linux_kernel_pppoe +if test "$ac_cv_linux_kernel_pppoe" = yes ; then + AC_DEFINE(HAVE_LINUX_KERNEL_PPPOE) +fi + +dnl GCC warning level +if test "$GCC" = yes; then + CFLAGS="$CFLAGS -Wall -Wstrict-prototypes" +fi + +dnl If we couldn't find pppd, die +if test "$PPPD" = "NOTFOUND"; then + $ECHO "" + $ECHO "*** Oops! I couldn't find pppd, the PPP daemon anywhere." + $ECHO "*** You must install pppd, version 2.3.10 or later." + $ECHO "*** I will keep going, but it may not work." + $ECHO "" +fi + +dnl Figure out pppd version. 2.3.7 to 2.3.9 -- issue warning. Less than +dnl 2.3.7 -- stop + +PPPD_VERSION=`$PPPD --version 2>&1 | awk '{print $NF}'` + +case "$PPPD_VERSION" in +1.*|2.0.*|2.1.*|2.2.*|2.3.0|2.3.1|2.3.2|2.3.3|2.3.4|2.3.5|2.3.6) + $ECHO "" + $ECHO "*** Oops! Your version of pppd is $PPPD_VERSION, which is too old." + $ECHO "*** You need at least 2.3.7 (2.3.10 or newer recommended.)" + $ECHO "*** I will keep going, but it may not work." + $ECHO "" + ;; + +2.3.7|2.3.8|2.3.9) + $ECHO "" + $ECHO "*** Warning. Your version of pppd is $PPPD_VERSION. You will" + $ECHO "*** not be able to use connect-on-demand. Upgrade to pppd" + $ECHO "*** 2.3.10 or newer if you need connect-on-demand." + $ECHO "" + ;; + +2*|3*|4*|5*|6*|7*|8*|9*) + ;; + +*) + $ECHO "" + $ECHO "*** Oops. I cannot figure out what version of pppd you have." + $ECHO "*** All I got back was '$PPPD_VERSION'" + $ECHO "*** I will keep going, but it may not work." + $ECHO "" + ;; +esac + +dnl Figure out packing order of structures +$ECHO -n "checking packing order of bit fields... " +AC_TRY_RUN([ +union foo { + struct bar { + unsigned int ver:4; + unsigned int type:4; + } bb; + unsigned char baz; +}; + +int +main(void) +{ + union foo x; + x.bb.ver = 1; + x.bb.type = 2; + if (x.baz == 0x21) { + return 1; + } else if (x.baz == 0x12) { + return 0; + } else { + return 2; + } +}], PACK=normal, PACK=rev) + +if test "$PACK" = "rev" ; then + $ECHO "reversed" + AC_DEFINE(PACK_BITFIELDS_REVERSED) +else + $ECHO "normal" +fi + +# Sigh... got to fix this up for tcl +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Fully resolve WRAPPER for Tcl script. +WRAPPER=${sbindir}/pppoe-wrapper +eval "WRAPPER=${WRAPPER}" +eval "WRAPPER=${WRAPPER}" +AC_SUBST(WRAPPER) + +# Determine what targets to build +TARGETS="pppoe pppoe-server" + +# pppoe-sniff is built only on Linux and Solaris +if test "$ac_cv_header_linux_if_packet_h" = "yes" -o "$ac_cv_header_sys_dlpi_h" = "yes" ; then + TARGETS="$TARGETS pppoe-sniff" +fi + +# pppoe-relay is built only on Linux +if test "$ac_cv_header_linux_if_packet_h" = "yes" ; then + TARGETS="$TARGETS pppoe-relay" +fi + +# plugin is built only if we have kernel support +if test -n "$LINUX_KERNELMODE_PLUGIN" ; then + TARGETS="$TARGETS $LINUX_KERNELMODE_PLUGIN" +fi + +AC_SUBST(TARGETS) + +AC_OUTPUT(Makefile ../scripts/adsl-connect ../scripts/adsl-start ../scripts/adsl-stop ../scripts/adsl-init ../scripts/adsl-init-suse ../scripts/adsl-init-turbolinux ../scripts/adsl-setup ../gui/Makefile ../gui/tkpppoe) + +$ECHO "" +$ECHO "On this platform, the following targets will be built:" +$ECHO " $TARGETS" +$ECHO "" +$ECHO "Type 'make' to compile the software." diff --git a/mdk-stage1/rp-pppoe/src/debug.c b/mdk-stage1/rp-pppoe/src/debug.c new file mode 100644 index 000000000..052dca6c7 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/debug.c @@ -0,0 +1,143 @@ +/*********************************************************************** +* +* 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. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" +#include <sys/time.h> +#include <time.h> +#include <unistd.h> +#include <ctype.h> + +/********************************************************************** +*%FUNCTION: dumpHex +*%ARGUMENTS: +* fp -- file to dump to +* buf -- buffer to dump +* len -- length of data +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Dumps buffer to fp in an easy-to-read format +***********************************************************************/ +void +dumpHex(FILE *fp, unsigned char const *buf, int len) +{ + int i; + int base; + + if (!fp) return; + + /* do NOT dump PAP packets */ + if (len >= 2 && buf[0] == 0xC0 && buf[1] == 0x23) { + fprintf(fp, "(PAP Authentication Frame -- Contents not dumped)\n"); + return; + } + + for (base=0; base<len; base += 16) { + for (i=base; i<base+16; i++) { + if (i < len) { + fprintf(fp, "%02x ", (unsigned) buf[i]); + } else { + fprintf(fp, " "); + } + } + fprintf(fp, " "); + for (i=base; i<base+16; i++) { + if (i < len) { + if (isprint(buf[i])) { + fprintf(fp, "%c", buf[i]); + } else { + fprintf(fp, "."); + } + } else { + break; + } + } + fprintf(fp, "\n"); + } +} + +/********************************************************************** +*%FUNCTION: dumpPacket +*%ARGUMENTS: +* fp -- file to dump to +* packet -- a PPPoE packet +* dir -- either SENT or RCVD +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Dumps the PPPoE packet to fp in an easy-to-read format +***********************************************************************/ +void +dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir) +{ + int len = ntohs(packet->length); + + /* 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_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)); +} diff --git a/mdk-stage1/rp-pppoe/src/discovery.c b/mdk-stage1/rp-pppoe/src/discovery.c new file mode 100644 index 000000000..fc3568db5 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/discovery.c @@ -0,0 +1,629 @@ +/*********************************************************************** +* +* discovery.c +* +* Perform PPPoE discovery +* +* Copyright (C) 1999 by Roaring Penguin Software Inc. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef USE_LINUX_PACKET +#include <sys/ioctl.h> +#include <fcntl.h> +#endif + +#include <signal.h> + +/********************************************************************** +*%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. +***********************************************************************/ +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. +***********************************************************************/ +int +packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) +{ + int forMe = 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 +***********************************************************************/ +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: + 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: + 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; i<len && i < 20; i++) { + printf(" %02x", (unsigned) data[i]); + } + if (i < len) printf("..."); + printf("\n"); + } + conn->cookie.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; i<len && i < 20; i++) { + printf(" %02x", (unsigned) data[i]); + } + if (i < len) printf("..."); + printf("\n"); + } + conn->relayId.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 { + syslog(LOG_ERR, "PADO: Service-Name-Error: %.*s", (int) len, data); + exit(1); + } + break; + case TAG_AC_SYSTEM_ERROR: + if (conn->printACNames) { + printf("Got a System-Error tag: %.*s\n", (int) len, data); + } else { + syslog(LOG_ERR, "PADO: System-Error: %.*s", (int) len, data); + exit(1); + } + break; + case TAG_GENERIC_ERROR: + if (conn->printACNames) { + printf("Got a Generic-Error tag: %.*s\n", (int) len, data); + } else { + syslog(LOG_ERR, "PADO: Generic-Error: %.*s", (int) len, data); + 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 +***********************************************************************/ +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_SERVICE_NAME_ERROR: + syslog(LOG_ERR, "PADS: Service-Name-Error: %.*s", (int) len, data); + fprintf(stderr, "PADS: Service-Name-Error: %.*s\n", (int) len, data); + exit(1); + case TAG_AC_SYSTEM_ERROR: + syslog(LOG_ERR, "PADS: System-Error: %.*s", (int) len, data); + fprintf(stderr, "PADS: System-Error: %.*s\n", (int) len, data); + exit(1); + case TAG_GENERIC_ERROR: + syslog(LOG_ERR, "PADS: Generic-Error: %.*s", (int) len, data); + fprintf(stderr, "PADS: Generic-Error: %.*s\n", (int) len, data); + exit(1); + 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 +***********************************************************************/ +void +sendPADI(PPPoEConnection *conn) +{ + PPPoEPacket packet; + unsigned char *cursor = packet.payload; + PPPoETag *svc = (PPPoETag *) (&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); + + /* 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; + + svc->type = TAG_SERVICE_NAME; + svc->length = htons(namelen); + CHECK_ROOM(cursor, packet.payload, namelen+TAG_HDR_SIZE); + + if (conn->serviceName) { + memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); + } + 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; + } + + packet.length = htons(plen); + + sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "SENT"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } +} + +/********************************************************************** +*%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 +***********************************************************************/ +void +waitForPADO(PPPoEConnection *conn, int timeout) +{ + fd_set readable; + int r; + struct timeval tv; + PPPoEPacket packet; + int len; + + struct PacketCriteria pc; + pc.conn = conn; + pc.acNameOK = (conn->acName) ? 0 : 1; + pc.serviceNameOK = (conn->serviceName) ? 0 : 1; + + do { + if (BPF_BUFFER_IS_EMPTY) { + tv.tv_sec = timeout; + tv.tv_usec = 0; + + FD_ZERO(&readable); + FD_SET(conn->discoverySocket, &readable); + + while(1) { + r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); + if (r >= 0 || errno != EINTR) break; + } + if (r < 0) { + fatalSys("select (waitForPADO)"); + } + if (r == 0) return; /* Timed out */ + } + + /* Get the packet */ + receivePacket(conn->discoverySocket, &packet, &len); + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + continue; + } + +#ifdef USE_BPF + /* If it's not a Discovery packet, loop again */ + if (etherType(&packet) != Eth_PPPOE_Discovery) continue; +#endif + + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "RCVD"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } + /* 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; + } + conn->numPADOs++; + if (conn->printACNames) { + printf("--------------------------------------------------\n"); + } + parsePacket(&packet, parsePADOTags, &pc); + if (pc.acNameOK && pc.serviceNameOK) { + memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); + if (conn->printACNames) { + printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned) conn->peerEth[0], + (unsigned) conn->peerEth[1], + (unsigned) conn->peerEth[2], + (unsigned) conn->peerEth[3], + (unsigned) conn->peerEth[4], + (unsigned) conn->peerEth[5]); + continue; + } + conn->discoveryState = STATE_RECEIVED_PADO; + break; + } + } + } while (conn->discoveryState != STATE_RECEIVED_PADO); +} + +/*********************************************************************** +*%FUNCTION: sendPADR +*%ARGUMENTS: +* conn -- PPPoE connection structur +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Sends a PADR packet +***********************************************************************/ +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)); + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "SENT"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } +} + +/********************************************************************** +*%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 +***********************************************************************/ +void +waitForPADS(PPPoEConnection *conn, int timeout) +{ + fd_set readable; + int r; + struct timeval tv; + PPPoEPacket packet; + int len; + + do { + if (BPF_BUFFER_IS_EMPTY) { + tv.tv_sec = timeout; + tv.tv_usec = 0; + + 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) return; + } + + /* Get the packet */ + receivePacket(conn->discoverySocket, &packet, &len); + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + continue; + } + +#ifdef USE_BPF + /* If it's not a Discovery packet, loop again */ + if (etherType(&packet) != Eth_PPPOE_Discovery) continue; +#endif + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "RCVD"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } + + /* 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 */ + parsePacket(&packet, parsePADSTags, conn); + 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", (int) ntohs(conn->session)); + + /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */ + if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) { + syslog(LOG_ERR, "Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session)); + } +} + +/********************************************************************** +*%FUNCTION: discovery +*%ARGUMENTS: +* conn -- PPPoE connection info structure +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Performs the PPPoE discovery phase +***********************************************************************/ +void +discovery(PPPoEConnection *conn) +{ + int padiAttempts = 0; + int padrAttempts = 0; + int timeout = PADI_TIMEOUT; + + /* Skip discovery and don't open discovery socket? */ + if (conn->skipDiscovery && conn->noDiscoverySocket) { + conn->discoveryState = STATE_SESSION; + return; + } + + conn->discoverySocket = + openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); + + /* 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) { + rp_fatal("Timeout waiting for PADO packets"); + } + sendPADI(conn); + conn->discoveryState = STATE_SENT_PADI; + waitForPADO(conn, timeout); + + /* If we're just probing for access concentrators, don't do + exponential backoff. This reduces the time for an unsuccessful + probe to 15 seconds. */ + if (!conn->printACNames) { + timeout *= 2; + } + if (conn->printACNames && conn->numPADOs) { + break; + } + } while (conn->discoveryState == STATE_SENT_PADI); + + /* If we're only printing access concentrator names, we're done */ + if (conn->printACNames) { + printf("--------------------------------------------------\n"); + exit(0); + } + + timeout = PADI_TIMEOUT; + do { + padrAttempts++; + if (padrAttempts > MAX_PADI_ATTEMPTS) { + rp_fatal("Timeout waiting for PADS packets"); + } + 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/mdk-stage1/rp-pppoe/src/if.c b/mdk-stage1/rp-pppoe/src/if.c new file mode 100644 index 000000000..fec09b273 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/if.c @@ -0,0 +1,1092 @@ +/*********************************************************************** +* +* 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. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +#include <netpacket/packet.h> +#elif defined(HAVE_LINUX_IF_PACKET_H) +#include <linux/if_packet.h> +#endif + +#ifdef HAVE_NET_ETHERNET_H +#include <net/ethernet.h> +#endif + +#ifdef HAVE_ASM_TYPES_H +#include <asm/types.h> +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_NET_IF_ARP_H +#include <net/if_arp.h> +#endif + +#ifdef USE_DLPI + +#include <limits.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/dlpi.h> +#include <sys/bufmod.h> +#include <stdio.h> +#include <signal.h> +#include <stropts.h> + +/* function declarations */ + +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); +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 <net/bpf.h> +#include <fcntl.h> + +unsigned char *bpfBuffer; /* Packet filter buffer */ +int bpfLength = 0; /* Packet filter buffer length */ +int bpfSize = 0; /* Number of unread bytes in buffer */ +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_BPF +/********************************************************************** +*%FUNCTION: getHWaddr +*%ARGUMENTS: +* ifname -- name of interface +* hwaddr -- buffer for ehthernet address +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Locates the Ethernet hardware address for an interface. +***********************************************************************/ +void +getHWaddr(int sock, char const *ifname, unsigned char *hwaddr) +{ + char inbuf[8192]; + const struct sockaddr_dl *sdl; + struct ifconf ifc; + struct ifreq ifreq, *ifr; + int i; + int found = 0; + + ifc.ifc_len = sizeof(inbuf); + ifc.ifc_buf = inbuf; + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { + fatalSys("SIOCGIFCONF"); + } + ifr = ifc.ifc_req; + ifreq.ifr_name[0] = '\0'; + for (i = 0; i < ifc.ifc_len; ) { + ifr = (struct ifreq *)((caddr_t)ifc.ifc_req + i); + i += sizeof(ifr->ifr_name) + + (ifr->ifr_addr.sa_len > sizeof(struct sockaddr) + ? ifr->ifr_addr.sa_len + : sizeof(struct sockaddr)); + if (ifr->ifr_addr.sa_family == AF_LINK) { + sdl = (const struct sockaddr_dl *) &ifr->ifr_addr; + if ((sdl->sdl_type == IFT_ETHER) && + (sdl->sdl_alen == ETH_ALEN) && + !strncmp(ifname, ifr->ifr_name, sizeof(ifr->ifr_name))) { + if (found) { + char buffer[256]; + sprintf(buffer, "interface %.16s has more than one ethernet address", ifname); + rp_fatal(buffer); + } else { + found = 1; + memcpy(hwaddr, LLADDR(sdl), ETH_ALEN); + } + } + } + } + if (!found) { + char buffer[256]; + sprintf(buffer, "interface %.16s has no ethernet address", ifname); + rp_fatal(buffer); + } +} + +/********************************************************************** +*%FUNCTION: initFilter +*%ARGUMENTS: +* fd -- file descriptor of BSD device +* type -- Ethernet frame type (0 for watch mode) +* hwaddr -- buffer with ehthernet address +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Initializes the packet filter rules. +***********************************************************************/ +void +initFilter(int fd, UINT16_t type, unsigned char *hwaddr) +{ + /* Packet Filter Instructions: + * Note that the ethernet type names come from "pppoe.h" and are + * used here to maintain consistency with the rest of this file. */ + static struct bpf_insn bpfRun[] = { /* run PPPoE */ + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), /* ethernet type */ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_PPPOE_SESSION, 5, 0), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_PPPOE_DISCOVERY, 0, 9), + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0), /* first word of dest. addr */ +#define PPPOE_BCAST_CMPW 4 /* offset of word compare */ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 2), + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4), /* next 1/2 word of dest. */ +#define PPPOE_BCAST_CMPH 6 /* offset of 1/2 word compare */ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 4, 0), + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0), /* first word of dest. addr */ +#define PPPOE_FILTER_CMPW 8 /* offset of word compare */ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 3), + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4), /* next 1/2 word of dest. */ +#define PPPOE_FILTER_CMPH 10 /* offset of 1/rd compare */ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 1), + BPF_STMT(BPF_RET+BPF_K, (u_int) -1), /* keep packet */ + BPF_STMT(BPF_RET+BPF_K, 0), /* drop packet */ + }; + + /* Fix the potentially varying parts */ + bpfRun[1].code = (u_short) BPF_JMP+BPF_JEQ+BPF_K; + bpfRun[1].jt = 5; + bpfRun[1].jf = 0; + bpfRun[1].k = Eth_PPPOE_Session; + + bpfRun[2].code = (u_short) BPF_JMP+BPF_JEQ+BPF_K; + bpfRun[2].jt = 0; + bpfRun[2].jf = 9; + bpfRun[2].k = Eth_PPPOE_Discovery; + + { + struct bpf_insn bpfInsn[sizeof(bpfRun) / sizeof(bpfRun[0])]; + struct bpf_program bpfProgram; + memcpy(bpfInsn, bpfRun, sizeof(bpfRun)); + bpfInsn[PPPOE_BCAST_CMPW].k = ((0xff << 24) | (0xff << 16) | + (0xff << 8) | 0xff); + bpfInsn[PPPOE_BCAST_CMPH].k = ((0xff << 8) | 0xff); + bpfInsn[PPPOE_FILTER_CMPW].k = ((hwaddr[0] << 24) | (hwaddr[1] << 16) | + (hwaddr[2] << 8) | hwaddr[3]); + bpfInsn[PPPOE_FILTER_CMPH].k = ((hwaddr[4] << 8) | hwaddr[5]); + bpfProgram.bf_len = (sizeof(bpfInsn) / sizeof(bpfInsn[0])); + bpfProgram.bf_insns = &bpfInsn[0]; + + /* Apply the filter */ + if (ioctl(fd, BIOCSETF, &bpfProgram) < 0) { + fatalSys("ioctl(BIOCSETF)"); + } + } +} + +/********************************************************************** +*%FUNCTION: openInterface +*%ARGUMENTS: +* ifname -- name of interface +* type -- Ethernet frame type (0 for any frame type) +* hwaddr -- if non-NULL, set to the hardware address +*%RETURNS: +* A file descriptor for talking with the Ethernet card. Exits on error. +* Note that the Linux version of this routine returns a socket instead. +*%DESCRIPTION: +* Opens a BPF on an interface for all PPPoE traffic (discovery and +* session). If 'type' is 0, uses promiscuous mode to watch any PPPoE +* traffic on this network. +***********************************************************************/ +int +openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) +{ + static int fd = -1; + char bpfName[32]; + u_int optval; + struct bpf_version bpf_ver; + struct ifreq ifr; + int sock; + int i; + + /* BSD only opens one socket for both Discovery and Session packets */ + if (fd >= 0) { + return fd; + } + + /* Find a free BPF device */ + for (i = 0; i < 256; i++) { + sprintf(bpfName, "/dev/bpf%d", i); + if (((fd = open(bpfName, O_RDWR, 0)) >= 0) || + (errno != EBUSY)) { + break; + } + } + if (fd < 0) { + switch (errno) { + case EACCES: /* permission denied */ + { + char buffer[256]; + sprintf(buffer, "Cannot open %.32s -- pppoe must be run as root.", bpfName); + rp_fatal(buffer); + } + break; + case EBUSY: + case ENOENT: /* no such file */ + if (i == 0) { + rp_fatal("No /dev/bpf* devices (check your kernel configuration for BPF support)"); + } else { + rp_fatal("All /dev/bpf* devices are in use"); + } + break; + } + fatalSys(bpfName); + } + + if ((sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) { + fatalSys("socket"); + } + + /* Check that the interface is up */ + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { + fatalSys("ioctl(SIOCGIFFLAGS)"); + } + if ((ifr.ifr_flags & IFF_UP) == 0) { + char buffer[256]; + sprintf(buffer, "Interface %.16s is not up\n", ifname); + rp_fatal(buffer); + } + + /* Fill in hardware address and initialize the packet filter rules */ + if (hwaddr == NULL) { + rp_fatal("openInterface: no hwaddr arg."); + } + getHWaddr(sock, ifname, hwaddr); + initFilter(fd, type, hwaddr); + + /* Sanity check on MTU -- apparently does not work on OpenBSD */ +#if !defined(__OpenBSD__) + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(sock, 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); + } +#endif + + /* done with the socket */ + if (close(sock) < 0) { + fatalSys("close"); + } + + /* Check the BPF version number */ + if (ioctl(fd, BIOCVERSION, &bpf_ver) < 0) { + fatalSys("ioctl(BIOCVERSION)"); + } + if ((bpf_ver.bv_major != BPF_MAJOR_VERSION) || + (bpf_ver.bv_minor < BPF_MINOR_VERSION)) { + char buffer[256]; + sprintf(buffer, "Unsupported BPF version: %d.%d (kernel: %d.%d)", + BPF_MAJOR_VERSION, BPF_MINOR_VERSION, + bpf_ver.bv_major, bpf_ver.bv_minor); + rp_fatal(buffer); + } + + /* allocate a receive packet buffer */ + if (ioctl(fd, BIOCGBLEN, &bpfLength) < 0) { + fatalSys("ioctl(BIOCGBLEN)"); + } + if (!(bpfBuffer = (unsigned char *) malloc(bpfLength))) { + rp_fatal("malloc"); + } + + /* reads should return as soon as there is a packet available */ + optval = 1; + if (ioctl(fd, BIOCIMMEDIATE, &optval) < 0) { + fatalSys("ioctl(BIOCIMMEDIATE)"); + } + + /* Bind the interface to the filter */ + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, &ifr) < 0) { + char buffer[256]; + sprintf(buffer, "ioctl(BIOCSETIF) can't select interface %.16s", + ifname); + rp_fatal(buffer); + } + + syslog(LOG_INFO, "Interface=%.16s HWaddr=%02X:%02X:%02X:%02X:%02X:%02X Device=%.32s Buffer size=%d", + ifname, + hwaddr[0], hwaddr[1], hwaddr[2], + hwaddr[3], hwaddr[4], hwaddr[5], + bpfName, bpfLength); + return fd; +} + +#endif /* USE_BPF */ + +#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; + +#ifdef HAVE_STRUCT_SOCKADDR_LL + struct sockaddr_ll sa; +#else + struct sockaddr sa; +#endif + + memset(&sa, 0, sizeof(sa)); + +#ifdef HAVE_STRUCT_SOCKADDR_LL + domain = PF_PACKET; + stype = SOCK_RAW; +#else + domain = PF_INET; + stype = SOCK_PACKET; +#endif + + if ((fd = socket(domain, stype, htons(type))) < 0) { + /* Give a more helpful message for the common error case */ + if (errno == EPERM) { + rp_fatal("Cannot create raw socket -- pppoe must be run as root."); + } + fatalSys("socket"); + } + + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { + fatalSys("setsockopt"); + } + + /* Fill in hardware address */ + if (hwaddr) { + 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); +#ifdef ARPHRD_ETHER + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + char buffer[256]; + sprintf(buffer, "Interface %.16s is not Ethernet", ifname); + rp_fatal(buffer); + } +#endif + if (NOT_UNICAST(hwaddr)) { + char buffer[256]; + sprintf(buffer, + "Interface %.16s has broadcast/multicast MAC address??", + ifname); + rp_fatal(buffer); + } + } + + /* Sanity check on MTU */ + 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); + } + +#ifdef HAVE_STRUCT_SOCKADDR_LL + /* 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; + +#else + strcpy(sa.sa_data, ifname); +#endif + + /* We're only interested in packets on specified interface */ + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + fatalSys("bind"); + } + + return fd; +} + +#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 defined(USE_BPF) + if (write(sock, pkt, size) < 0) { + sysErr("write (sendPacket)"); + return -1; + } +#elif defined(HAVE_STRUCT_SOCKADDR_LL) + if (send(sock, pkt, size, 0) < 0) { + sysErr("send (sendPacket)"); + return -1; + } +#else +#ifdef USE_DLPI + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + + u_char addr[MAXDLADDR]; + u_char phys[MAXDLADDR]; + u_char sap[MAXDLADDR]; + u_char xmitbuf[MAXDLBUF]; + int data_size; + + short tmp_sap; + + tmp_sap = htons(pkt->ethHdr.h_proto); + data_size = size - sizeof(struct ethhdr); + + memcpy((char *)phys, (char *)pkt->ethHdr.h_dest, ETHERADDRL); + memcpy((char *)sap, (char *)&tmp_sap, sizeof(ushort_t)); + memcpy((char *)xmitbuf, (char *)pkt + sizeof(struct ethhdr), data_size); + + if (dl_saplen > 0) { /* order is sap+phys */ + (void) memcpy((char*)addr, (char*)&sap, dl_abssaplen); + (void) memcpy((char*)addr+dl_abssaplen, (char*)phys, ETHERADDRL); + } else { /* order is phys+sap */ + (void) memcpy((char*)addr, (char*)phys, ETHERADDRL); + (void) memcpy((char*)addr+ETHERADDRL, (char*)&sap, dl_abssaplen); + } + +#ifdef DL_DEBUG + printf("%02x:%02x:%02x:%02x:%02x:%02x %02x:%02x\n", + addr[0],addr[1],addr[2],addr[3],addr[4],addr[5], + addr[6],addr[7]); +#endif + + dlunitdatareq(sock, addr, dl_addrlen, 0, 0, xmitbuf, data_size); + + + +#else + struct sockaddr sa; + + if (!conn) { + rp_fatal("relay and server not supported on Linux 2.0 kernels"); + } + strcpy(sa.sa_data, conn->ifName); + if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) { + sysErr("sendto (sendPacket)"); + return -1; + } +#endif +#endif + 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; +} + +#ifdef USE_DLPI +/********************************************************************** +*%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 fd; + long buf[MAXDLBUF]; + + union DL_primitives *dlp; + + char base_dev[PATH_MAX]; + int ppa; + + if(strlen(ifname) > PATH_MAX) { + rp_fatal("socket: string to long"); + } + + ppa = atoi(&ifname[strlen(ifname)-1]); + strncpy(base_dev, ifname, PATH_MAX); + base_dev[strlen(base_dev)-1] = '\0'; + + if (( fd = open(base_dev, O_RDWR)) < 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"); + } + + dlinforeq(fd); + dlinfoack(fd, (char *)buf); + + dlp = (union DL_primitives*) buf; + + dl_abssaplen = ABS(dlp->info_ack.dl_sap_length); + dl_saplen = dlp->info_ack.dl_sap_length; + if (ETHERADDRL != (dlp->info_ack.dl_addr_length - dl_abssaplen)) + fatalSys("invalid destination physical address length"); + dl_addrlen = dl_abssaplen + ETHERADDRL; + + dlattachreq(fd, ppa); + dlokack(fd, (char *)buf); + + dlbindreq(fd, type, 0, DL_CLDLS, 0, 0); + dlbindack(fd, (char *)buf); + + if ( strioctl(fd, DLIOCRAW, -1, 0, NULL) < 0 ) { + fatalSys("DLIOCRAW"); + } + + if (ioctl(fd, I_FLUSH, FLUSHR) < 0) fatalSys("I_FLUSH"); + + return fd; +} + +/* cloned from dlcommon.c */ + +void dlpromisconreq(int fd, u_long level) +{ + dl_promiscon_req_t promiscon_req; + struct strbuf ctl; + int flags; + + promiscon_req.dl_primitive = DL_PROMISCON_REQ; + promiscon_req.dl_level = level; + + ctl.maxlen = 0; + ctl.len = sizeof (promiscon_req); + ctl.buf = (char *) &promiscon_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + fatalSys("dlpromiscon: putmsg"); + +} + +void dlinforeq(int fd) +{ + dl_info_req_t info_req; + struct strbuf ctl; + int flags; + + info_req.dl_primitive = DL_INFO_REQ; + + ctl.maxlen = 0; + ctl.len = sizeof (info_req); + ctl.buf = (char *) &info_req; + + flags = RS_HIPRI; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + fatalSys("dlinforeq: putmsg"); +} + +void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen) +{ + long buf[MAXDLBUF]; + union DL_primitives *dlp; + struct strbuf data, ctl; + + dlp = (union DL_primitives*) buf; + + dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ; + dlp->unitdata_req.dl_dest_addr_length = addrlen; + dlp->unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t); + dlp->unitdata_req.dl_priority.dl_min = minpri; + dlp->unitdata_req.dl_priority.dl_max = maxpri; + + (void) memcpy(OFFADDR(dlp, sizeof (dl_unitdata_req_t)), addrp, addrlen); + + ctl.maxlen = 0; + ctl.len = sizeof (dl_unitdata_req_t) + addrlen; + ctl.buf = (char *) buf; + + data.maxlen = 0; + data.len = datalen; + data.buf = (char *) datap; + + if (putmsg(fd, &ctl, &data, 0) < 0) + fatalSys("dlunitdatareq: putmsg"); +} + +void dlinfoack(int fd, char *bufp) +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlinfoack"); + + dlp = (union DL_primitives *) ctl.buf; + + expecting(DL_INFO_ACK, dlp); + + if (ctl.len < sizeof (dl_info_ack_t)) { + char buffer[256]; + sprintf(buffer, "dlinfoack: response ctl.len too short: %d", ctl.len); + rp_fatal(buffer); + } + + if (flags != RS_HIPRI) + rp_fatal("dlinfoack: DL_INFO_ACK was not M_PCPROTO"); + + if (ctl.len < sizeof (dl_info_ack_t)) { + char buffer[256]; + sprintf(buffer, "dlinfoack: short response ctl.len: %d", ctl.len); + rp_fatal(buffer); + } +} + +void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest) +{ + dl_bind_req_t bind_req; + struct strbuf ctl; + int flags; + + bind_req.dl_primitive = DL_BIND_REQ; + bind_req.dl_sap = sap; + bind_req.dl_max_conind = max_conind; + bind_req.dl_service_mode = service_mode; + bind_req.dl_conn_mgmt = conn_mgmt; + bind_req.dl_xidtest_flg = xidtest; + + ctl.maxlen = 0; + ctl.len = sizeof (bind_req); + ctl.buf = (char *) &bind_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + fatalSys("dlbindreq: putmsg"); +} + +void dlattachreq(int fd, u_long ppa) +{ + dl_attach_req_t attach_req; + struct strbuf ctl; + int flags; + + attach_req.dl_primitive = DL_ATTACH_REQ; + attach_req.dl_ppa = ppa; + + ctl.maxlen = 0; + ctl.len = sizeof (attach_req); + ctl.buf = (char *) &attach_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + fatalSys("dlattachreq: putmsg"); +} + +void dlokack(int fd, char *bufp) +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlokack"); + + dlp = (union DL_primitives *) ctl.buf; + + expecting(DL_OK_ACK, dlp); + + if (ctl.len < sizeof (dl_ok_ack_t)) { + char buffer[256]; + sprintf(buffer, "dlokack: response ctl.len too short: %d", ctl.len); + rp_fatal(buffer); + } + + if (flags != RS_HIPRI) + rp_fatal("dlokack: DL_OK_ACK was not M_PCPROTO"); + + if (ctl.len < sizeof (dl_ok_ack_t)) { + char buffer[256]; + sprintf(buffer, "dlokack: short response ctl.len: %d", ctl.len); + rp_fatal(buffer); + } +} + +void dlbindack(int fd, char *bufp) +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlbindack"); + + dlp = (union DL_primitives *) ctl.buf; + + expecting(DL_BIND_ACK, dlp); + + if (flags != RS_HIPRI) + rp_fatal("dlbindack: DL_OK_ACK was not M_PCPROTO"); + + if (ctl.len < sizeof (dl_bind_ack_t)) { + char buffer[256]; + sprintf(buffer, "dlbindack: short response ctl.len: %d", ctl.len); + rp_fatal(buffer); + } +} + +int strioctl(int fd, int cmd, int timout, int len, char *dp) +{ + struct strioctl sioc; + int rc; + + sioc.ic_cmd = cmd; + sioc.ic_timout = timout; + sioc.ic_len = len; + sioc.ic_dp = dp; + rc = ioctl(fd, I_STR, &sioc); + + if (rc < 0) + return (rc); + else + return (sioc.ic_len); +} + +void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller) +{ + int rc; + static char errmsg[80]; + + /* + * Start timer. + */ + (void) signal(SIGALRM, sigalrm); + if (alarm(MAXWAIT) < 0) { + (void) sprintf(errmsg, "%s: alarm", caller); + fatalSys(errmsg); + } + + /* + * Set flags argument and issue getmsg(). + */ + *flagsp = 0; + if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) { + (void) sprintf(errmsg, "%s: getmsg", caller); + fatalSys(errmsg); + } + + /* + * Stop timer. + */ + if (alarm(0) < 0) { + (void) sprintf(errmsg, "%s: alarm", caller); + fatalSys(errmsg); + } + + /* + * Check for MOREDATA and/or MORECTL. + */ + if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) { + char buffer[256]; + sprintf(buffer, "%s: MORECTL|MOREDATA", caller); + rp_fatal(buffer); + } + + if (rc & MORECTL) { + char buffer[256]; + sprintf(buffer, "%s: MORECTL", caller); + rp_fatal(buffer); + } + + if (rc & MOREDATA) { + char buffer[256]; + sprintf(buffer, "%s: MOREDATA", caller); + rp_fatal(buffer); + } + + /* + * Check for at least sizeof (long) control data portion. + */ + if (ctlp->len < sizeof (long)) { + char buffer[256]; + sprintf(buffer, "getmsg: control portion length < sizeof (long): %d", ctlp->len); + rp_fatal(buffer); + } +} + +void sigalrm(int sig) +{ + (void) rp_fatal("sigalrm: TIMEOUT"); +} + +void expecting(int prim, union DL_primitives *dlp) +{ + if (dlp->dl_primitive != (u_long)prim) { + char buffer[256]; + sprintf(buffer, "expected %s got %s", dlprim(prim), dlprim(dlp->dl_primitive)); + rp_fatal(buffer); + exit(1); + } +} + +char *dlprim(u_long prim) +{ + static char primbuf[80]; + + switch ((int)prim) { + CASERET(DL_INFO_REQ); + CASERET(DL_INFO_ACK); + CASERET(DL_ATTACH_REQ); + CASERET(DL_DETACH_REQ); + CASERET(DL_BIND_REQ); + CASERET(DL_BIND_ACK); + CASERET(DL_UNBIND_REQ); + CASERET(DL_OK_ACK); + CASERET(DL_ERROR_ACK); + CASERET(DL_SUBS_BIND_REQ); + CASERET(DL_SUBS_BIND_ACK); + CASERET(DL_UNITDATA_REQ); + CASERET(DL_UNITDATA_IND); + CASERET(DL_UDERROR_IND); + CASERET(DL_UDQOS_REQ); + CASERET(DL_CONNECT_REQ); + CASERET(DL_CONNECT_IND); + CASERET(DL_CONNECT_RES); + CASERET(DL_CONNECT_CON); + CASERET(DL_TOKEN_REQ); + CASERET(DL_TOKEN_ACK); + CASERET(DL_DISCONNECT_REQ); + CASERET(DL_DISCONNECT_IND); + CASERET(DL_RESET_REQ); + CASERET(DL_RESET_IND); + CASERET(DL_RESET_RES); + CASERET(DL_RESET_CON); + default: + (void) sprintf(primbuf, "unknown primitive 0x%lx", prim); + return (primbuf); + } +} + +#endif /* USE_DLPI */ diff --git a/mdk-stage1/rp-pppoe/src/install-sh b/mdk-stage1/rp-pppoe/src/install-sh new file mode 100755 index 000000000..58719246f --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/install-sh @@ -0,0 +1,238 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/mdk-stage1/rp-pppoe/src/md5.c b/mdk-stage1/rp-pppoe/src/md5.c new file mode 100644 index 000000000..5b7a0d7b2 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/md5.c @@ -0,0 +1,246 @@ +/* + * 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. + * + * 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 <string.h> /* for memcpy() */ +#include "md5.h" + +void byteReverse(unsigned char *buf, unsigned longs); + +/* + * Note: this code is harmless on little-endian machines. + */ +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<<s | 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/mdk-stage1/rp-pppoe/src/md5.h b/mdk-stage1/rp-pppoe/src/md5.h new file mode 100644 index 000000000..e264f686d --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/md5.h @@ -0,0 +1,27 @@ +#ifndef MD5_H +#define MD5_H + +#ifdef __alpha +typedef unsigned int uint32; +#else +typedef unsigned long uint32; +#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/mdk-stage1/rp-pppoe/src/plugin.c b/mdk-stage1/rp-pppoe/src/plugin.c new file mode 100644 index 000000000..d1097e291 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/plugin.c @@ -0,0 +1,397 @@ +/*********************************************************************** +* +* 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 <mostrows@styx.uwaterloo.ca>, +* Jamal Hadi Salim <hadi@cyberus.ca> +* 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. +***********************************************************************/ + +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 <linux/types.h> +#include <syslog.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <net/ethernet.h> +#include <net/if_arp.h> +#include <linux/if_pppox.h> + +/* From sys-linux.c in pppd -- MUST FIX THIS! */ +extern int new_style_driver; + +static char *service = NULL; +static char *acName = NULL; +static char *existingSession = NULL; + +static option_t Options[] = { + { "rp_pppoe_service", o_string, &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)" }, + { NULL } +}; +int (*OldDevnameHook)(const char *name) = 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 (service) { + SET_STRING(conn->serviceName, acName); + } + SET_STRING(conn->ifName, devnam); + conn->discoverySocket = -1; + conn->sessionSocket = -1; + conn->useHostUniq = 1; + 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; + + 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; i<ETH_ALEN; i++) { + conn->peerEth[i] = (unsigned char) mac[i]; + } + } else { + discovery(conn); + if (conn->discoveryState != STATE_SESSION) { + fatal("Unable to complete PPPoE Discovery"); + } + } + + /* Make the session socket */ + conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE); + if (conn->sessionSocket < 0) { + fatal("Failed to create PPPoE socket: %m"); + } + 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); + if (connect(conn->sessionSocket, (struct sockaddr *) &sp, + sizeof(struct sockaddr_pppox)) < 0) { + fatal("Failed to connect PPPoE socket: %d %m", errno); + return -1; + } + return conn->sessionSocket; +} + +static void +PPPOESendConfig(int unit, + 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); + } + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + fatal("Couldn't create IP socket: %m"); + } + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + ifr.ifr_mtu = mtu; + if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) { + fatal("ioctl(SIOCSIFMTU): %m"); + } + (void) close (sock); +} + + +static void +PPPOERecvConfig(int unit, + int mru, + u_int32_t asyncmap, + int pcomp, + int accomp) +{ + if (mru > MAX_PPPOE_MTU) { + error("Couldn't increase MRU to %d", mru); + } +} + +static void +PPPOESetXaccm(int unit, + ext_accm accm) +{ + /* Do nothing */ +} + +/********************************************************************** + * %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); +} + +static int +PPPOESetSpeed(const char *speed) +{ + return 0; +} + +static void +PPPOEDeviceCheckHook(void) +{ + if (!options_for_dev(_PATH_ETHOPT, devnam)) { + exit(EXIT_OPTION_ERROR); + } +} + +/********************************************************************** + * %FUNCTION: PPPoEDevnameHook + * %ARGUMENTS: + * name -- name of device + * %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) and sets devstat.st_mode + * so S_ISCHR(devstat.st_mode) != 1 for internal pppd consumption. + ***********************************************************************/ +static int +PPPoEDevnameHook(const char *name) +{ + int r = 1; + int fd; + struct ifreq ifr; + + /* Open a socket */ + if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { + r = 0; + } + + /* Try getting interface index */ + if (r) { + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + 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", name); + r=0; + } + } + } + } + + /* Close socket */ + close(fd); + if (r) { + strncpy(devnam, name, sizeof(devnam)); + if (device_check_hook != PPPOEDeviceCheckHook) { + devstat.st_mode = S_IFSOCK; + device_init_hook = PPPOEInitDevice; + setspeed_hook = PPPOESetSpeed; + device_check_hook = PPPOEDeviceCheckHook; + connect_device_hook = PPPOEConnectDevice; + disconnect_device_hook = PPPOEDisconnectDevice; + send_config_hook = PPPOESendConfig; + recv_config_hook = PPPOERecvConfig; + set_xaccm_hook = PPPOESetXaccm; + 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; + + ccp_allowoptions[0].deflate = 0 ; + ccp_wantoptions[0].deflate = 0 ; + + ipcp_allowoptions[0].neg_vj=0; + ipcp_wantoptions[0].neg_vj=0; + + ccp_allowoptions[0].bsd_compress = 0; + ccp_wantoptions[0].bsd_compress = 0; + + PPPOEInitDevice(); + } + return 1; + } + + if (OldDevnameHook) r = OldDevnameHook(name); + return r; +} + +/********************************************************************** + * %FUNCTION: plugin_init + * %ARGUMENTS: + * None + * %RETURNS: + * Nothing + * %DESCRIPTION: + * Initializes hooks for pppd plugin + ***********************************************************************/ +void +plugin_init(void) +{ + if (!new_style_driver) { + fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?"); + } + OldDevnameHook = setdevname_hook; + setdevname_hook = PPPoEDevnameHook; + add_options(Options); + + info("Roaring Penguin PPPoE Plugin Initialized"); +} + +/********************************************************************** +*%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) +{ + char buf[1024]; + printErr(str); + sprintf(buf, "RP-PPPoE: %.256s", str); + sendPADT(conn, buf); + 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); +} diff --git a/mdk-stage1/rp-pppoe/src/ppp.c b/mdk-stage1/rp-pppoe/src/ppp.c new file mode 100644 index 000000000..72020a76c --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/ppp.c @@ -0,0 +1,258 @@ +/*********************************************************************** +* +* 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. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_N_HDLC +#ifndef N_HDLC +#include <linux/termios.h> +#endif +#endif + +int PPPState; +int PPPPacketSize; +unsigned char PPPXorValue; + +UINT16_t 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/mdk-stage1/rp-pppoe/src/pppoe-server.c b/mdk-stage1/rp-pppoe/src/pppoe-server.c new file mode 100644 index 000000000..e43e63553 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/pppoe-server.c @@ -0,0 +1,1247 @@ +/*********************************************************************** +* +* pppoe.h +* +* 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$ +* +***********************************************************************/ + +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.h" +#include "md5.h" + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#include <signal.h> + +/* Hack for daemonizing */ +#define CLOSEFD 64 + +/* Max. 64 sessions by default */ +#define DEFAULT_MAX_SESSIONS 64 + +/* A list of client sessions */ +struct ClientSession *Sessions = NULL; + +/* The number of session slots */ +size_t NumSessionSlots; + +/* Offset of first session */ +size_t SessOffset = 0; + +/* Socket for client's discovery phases */ +int Socket = -1; + +/* Pipe written on reception of SIGCHLD */ +int Pipe[2] = {-1, -1}; +int ReapPending = 0; + +/* Synchronous mode */ +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 */ + +unsigned char CookieSeed[SEED_LEN]; + +/* Default interface if no -I option given */ +#define DEFAULT_IF "eth0" +char *IfName = NULL; + +/* 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}; +unsigned char RemoteIP[IPV4ALEN] = {10, 67, 15, 1}; /* Counter STARTS here */ + +PPPoETag hostUniq; +PPPoETag relayId; +PPPoETag receivedCookie; +PPPoETag requestedService; + +#define HOSTNAMELEN 256 + +static void startPPPD(struct 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) + +/* Use Linux kernel-mode PPPoE? */ +int UseLinuxKernelModePPPoE = 0; + +/********************************************************************** +*%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; + + if (!fp) { + sysErr("Cannot open address pool file"); + } + + while (!feof(fp)) { + if ((fscanf(fp, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) && + a < 256 && b < 256 && c < 256 && d < 256) { + if (install) { + Sessions[numAddrs].ip[0] = (unsigned char) a; + Sessions[numAddrs].ip[1] = (unsigned char) b; + Sessions[numAddrs].ip[2] = (unsigned char) c; + Sessions[numAddrs].ip[3] = (unsigned char) d; + } + numAddrs++; + } + } + 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: + /* Should do something -- currently ignored */ + 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: findSession +*%ARGUMENTS: +* pid -- PID of child which owns session. If PID is 0, searches for +* empty session slots. +*%RETURNS: +* A pointer to the session, or NULL if no such session found. +*%DESCRIPTION: +* Searches for specified session. +**********************************************************************/ +struct ClientSession * +findSession(pid_t pid) +{ + size_t i; + for (i=0; i<NumSessionSlots; i++) { + if (Sessions[i].pid == pid) { + return &Sessions[i]; + } + } + return NULL; +} + +/********************************************************************** +*%FUNCTION: reapSessions +*%ARGUMENTS: +* myAddr -- my Ethernet address +* sock -- my discovery socket +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Reaps children which have exited and removes their sessions +**********************************************************************/ +void +reapSessions(unsigned char *myAddr, int sock) +{ + int status; + pid_t pid; + struct ClientSession *session; + + /* Temporary structure for sending PADT's. */ + PPPoEConnection conn; + memset(&conn, 0, sizeof(conn)); + + /* Initialize fields of conn which do not depend on peer */ + memcpy(conn.myEth, myAddr, ETH_ALEN); + conn.useHostUniq = 0; + conn.discoverySocket = sock; + + while((pid = waitpid(-1, &status, WNOHANG)) > 0) { + session = findSession(pid); + if (!session) { + syslog(LOG_ERR, "Child %d died but couldn't find session!", + (int) pid); + } else { + syslog(LOG_INFO, + "Session %d closed for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d)", + ntohs(session->sess), + session->eth[0], session->eth[1], session->eth[2], + session->eth[3], session->eth[4], session->eth[5], + (int) session->ip[0], (int) session->ip[1], + (int) session->ip[2], (int) session->ip[3]); + conn.session = session->sess; + memcpy(conn.peerEth, session->eth, ETH_ALEN); + if (session->recvdPADT) { + sendPADT(&conn, "RP-PPPoE: Received PADT from peer"); + } else { + sendPADT(&conn, "RP-PPPoE: Child pppd process terminated"); + } + session->pid = 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[SMALLBUF]; + snprintf(buf, SMALLBUF, "%s: %s", 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: 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: +* sock -- Ethernet socket +* myAddr -- my Ethernet address +* packet -- PPPoE PADI packet +* len -- length of received packet +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Sends a PADO packet back to client +***********************************************************************/ +void +processPADI(int sock, unsigned char *myAddr, + PPPoEPacket *packet, int len) +{ + PPPoEPacket pado; + PPPoETag acname; + PPPoETag servname; + PPPoETag cookie; + size_t acname_len; + unsigned char *cursor = pado.payload; + UINT16_t plen; + + /* 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; + } + + acname.type = htons(TAG_AC_NAME); + acname_len = strlen(ACName); + acname.length = htons(acname_len); + memcpy(acname.payload, ACName, acname_len); + + servname.type = htons(TAG_SERVICE_NAME); + servname.length = 0; + + relayId.type = 0; + hostUniq.type = 0; + parsePacket(packet, parsePADITags, NULL); + + /* Generate a cookie */ + cookie.type = htons(TAG_AC_COOKIE); + cookie.length = htons(COOKIE_LEN); + genCookie(packet->ethHdr.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; + + CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE); + memcpy(cursor, &servname, TAG_HDR_SIZE); + cursor += TAG_HDR_SIZE; + plen += TAG_HDR_SIZE; + + CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE + COOKIE_LEN); + memcpy(cursor, &cookie, TAG_HDR_SIZE + COOKIE_LEN); + cursor += TAG_HDR_SIZE + COOKIE_LEN; + plen += TAG_HDR_SIZE + COOKIE_LEN; + + if (relayId.type) { + CHECK_ROOM(cursor, pado.payload, ntohs(relayId.length) + TAG_HDR_SIZE); + 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) { + CHECK_ROOM(cursor, pado.payload, ntohs(hostUniq.length)+TAG_HDR_SIZE); + memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); + cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; + plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; + } + pado.length = htons(plen); + sendPacket(NULL, sock, &pado, (int) (plen + HDR_SIZE)); +} + +/********************************************************************** +*%FUNCTION: processPADT +*%ARGUMENTS: +* sock -- Ethernet socket +* myAddr -- my Ethernet address +* packet -- PPPoE PADT packet +* len -- length of received packet +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Kills session whose session-ID is in PADT packet. +***********************************************************************/ +void +processPADT(int sock, unsigned char *myAddr, + PPPoEPacket *packet, int len) +{ + size_t i; + + /* 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 (Sessions[i].pid) { + Sessions[i].recvdPADT = 1; + parsePacket(packet, parseLogErrs, NULL); + kill(Sessions[i].pid, SIGTERM); + } +} + +/********************************************************************** +*%FUNCTION: processPADR +*%ARGUMENTS: +* sock -- Ethernet socket +* myAddr -- my Ethernet address +* 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(int sock, unsigned char *myAddr, + PPPoEPacket *packet, int len) +{ + unsigned char cookieBuffer[COOKIE_LEN]; + struct ClientSession *cliSession; + pid_t child; + PPPoEPacket pads; + unsigned char *cursor = pads.payload; + UINT16_t plen; + PPPoETag servname; + + /* 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; + } + + 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 -- we only offer service "" */ + 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; + } + + if (requestedService.length) { + syslog(LOG_ERR, "Received PADR packet asking for unsupported service %.*s", (int) ntohs(requestedService.length), requestedService.payload); + sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, + TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: Invalid service name tag"); + return; + } + + /* Looks cool... find a slot for the session */ + cliSession = findSession(0); + 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->recvdPADT = 0; + + /* 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"); + return; + } + if (child != 0) { + /* In the parent process. Mark pid in session slot */ + cliSession->pid = child; + return; + } + + /* In the child process. */ + + /* pppd has a nasty habit of killing all processes in its process group. + Start a new session to stop pppd from killing us! */ + setsid(); + + /* Send PADS and Start pppd */ + memcpy(pads.ethHdr.h_dest, packet->ethHdr.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; + + servname.type = htons(TAG_SERVICE_NAME); + servname.length = 0; + + memcpy(cursor, &servname, TAG_HDR_SIZE); + cursor += TAG_HDR_SIZE; + plen += TAG_HDR_SIZE; + + 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)); + startPPPD(cliSession); +} + +/********************************************************************** +*%FUNCTION: childHandler +*%ARGUMENTS: +* sig -- signal number +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Called by SIGCHLD. Writes one byte to Pipe to wake up the select +* loop and cause reaping of dead sessions +***********************************************************************/ +void +childHandler(int sig) +{ + if (!ReapPending) { + ReapPending = 1; + write(Pipe[1], &ReapPending, 1); + } +} + +/********************************************************************** +*%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, " -R ip -- Set start address of remote IP pool.\n"); + 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, " -h -- Print usage information.\n\n"); + fprintf(stderr, "PPPoE-Server Version %s, Copyright (C) 2001 Roaring Penguin Software Inc.\n", VERSION); + fprintf(stderr, "PPPoE-Server comes with ABSOLUTELY NO WARRANTY.\n"); + fprintf(stderr, "This is free software, and you are welcome to redistribute it\n"); + fprintf(stderr, "under the terms of the GNU General Public License, version 2\n"); + fprintf(stderr, "or (at your option) any later version.\n"); + fprintf(stderr, "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; + int opt; + unsigned char myAddr[ETH_ALEN]; + PPPoEPacket packet; + int len; + int sock; + int d[IPV4ALEN]; + int beDaemon = 1; + struct sigaction act; + int maxFD; + unsigned int discoveryType, sessionType; + char *addressPoolFname = NULL; + +#ifndef HAVE_LINUX_KERNEL_PPPOE + char *options = "hI:C:L:R:T:m:FN:f:o:sp:"; +#else + char *options = "hI:C:L:R:T:m:FN:f:o:skp:"; +#endif + + /* Initialize syslog */ + openlog("pppoe-server", LOG_PID, LOG_DAEMON); + + /* Default number of session slots */ + NumSessionSlots = DEFAULT_MAX_SESSIONS; + + /* Parse command-line options */ + while((opt = getopt(argc, argv, options)) != -1) { + switch(opt) { +#ifdef HAVE_LINUX_KERNEL_PPPOE + case 'k': + UseLinuxKernelModePPPoE = 1; + break; +#endif + case 'p': + 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': + 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': + SET_STRING(IfName, optarg); + break; + case 'C': + SET_STRING(ACName, optarg); + break; + case 'L': + case 'R': + /* Get local/remote IP address */ + if (sscanf(optarg, "%d.%d.%d.%d", &d[0], &d[1], &d[2], &d[3]) != 4) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + for (i=0; i<IPV4ALEN; i++) { + if (d[i] < 0 || d[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); + } + } + +#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 + + if (!IfName) { + IfName = DEFAULT_IF; + } + + 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); + } + + /* 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(struct ClientSession)); + if (!Sessions) { + rp_fatal("Cannot allocate memory for session slots"); + } + + /* Fill in remote IP addresses from pool */ + if (addressPoolFname) { + (void) parseAddressPool(addressPoolFname, 1); + } + + /* For testing -- generate sequential remote IP addresses */ + for(i=0; i<NumSessionSlots; i++) { + Sessions[i].pid = 0; + Sessions[i].sess = htons(i+1+SessOffset); + + if (!addressPoolFname) { + memcpy(Sessions[i].ip, RemoteIP, sizeof(RemoteIP)); + + /* Increment IP */ + RemoteIP[3]++; + if (!RemoteIP[3]) { + RemoteIP[3] = 0; + RemoteIP[2]++; + if (!RemoteIP[2]) { + RemoteIP[1]++; + if (!RemoteIP[1]) { + RemoteIP[0]++; + } + } + } + } + } + + /* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */ + if (beDaemon) { + i = fork(); + if (i < 0) { + fatalSys("fork"); + } else if (i != 0) { + /* parent */ + exit(EXIT_SUCCESS); + } + setsid(); + signal(SIGHUP, SIG_IGN); + i = fork(); + if (i < 0) { + fatalSys("fork"); + } else if (i != 0) { + exit(EXIT_SUCCESS); + } + + chdir("/"); + closelog(); + for (i=0; i<CLOSEFD; i++) close(i); + /* We nuked our syslog descriptor... */ + openlog("pppoe-server", LOG_PID, LOG_DAEMON); + } + + /* Initialize our random cookie. Try /dev/urandom; if that fails, + use PID and rand() */ + fp = fopen("/dev/urandom", "r"); + if (fp) { + fread(&CookieSeed, 1, SEED_LEN, fp); + fclose(fp); + } else { + CookieSeed[0] = getpid() & 0xFF; + CookieSeed[1] = (getpid() >> 8) & 0xFF; + for (i=2; i<SEED_LEN; i++) { + CookieSeed[i] = (rand() >> (i % 9)) & 0xFF; + } + } + + sock = openInterface(IfName, Eth_PPPOE_Discovery, myAddr); + + /* Set signal handler for SIGCHLD */ + act.sa_handler = childHandler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NOCLDSTOP | SA_RESTART; + if (sigaction(SIGCHLD, &act, NULL) < 0) { + fatalSys("sigaction"); + } + + /* Set up pipe for signal handler */ + if (pipe(Pipe) < 0) { + fatalSys("pipe"); + } + + /* Main server loop */ + maxFD = sock; + if (Pipe[0] > maxFD) maxFD = Pipe[0]; + maxFD++; + + for(;;) { + fd_set readable; + FD_ZERO(&readable); + FD_SET(sock, &readable); + FD_SET(Pipe[0], &readable); + + while(1) { + i = select(maxFD, &readable, NULL, NULL, NULL); + if (i >= 0 || errno != EINTR) break; + } + if (i < 0) { + fatalSys("select"); + } + + if (FD_ISSET(Pipe[0], &readable)) { + /* Clear pipe */ + char buf[SMALLBUF]; + read(Pipe[0], buf, SMALLBUF); + } + + if (ReapPending) { + ReapPending = 0; + reapSessions(myAddr, sock); + } + if (!FD_ISSET(sock, &readable)) { + continue; + } + + if (receivePacket(sock, &packet, &len) < 0) { + continue; + } + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + continue; + } + + /* Sanity check on packet */ + if (packet.ver != 1 || packet.type != 1) { + /* Syslog an error */ + continue; + } + switch(packet.code) { + case CODE_PADI: + processPADI(sock, myAddr, &packet, len); + break; + case CODE_PADR: + processPADR(sock, myAddr, &packet, len); + break; + case CODE_PADT: + /* Kill the child */ + processPADT(sock, myAddr, &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; + } + } + return 0; +} + +/********************************************************************** +*%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(struct ClientSession *session) +{ + /* Leave some room */ + char *argv[20]; + + char buffer[SMALLBUF]; + + argv[0] = "pppd"; + argv[1] = "pty"; + + snprintf(buffer, SMALLBUF, "%s -n -I %s -e %d:%02x:%02x:%02x:%02x:%02x:%02x%s", + PPPOE_PATH, IfName, + ntohs(session->sess), + session->eth[0], session->eth[1], session->eth[2], + session->eth[3], session->eth[4], session->eth[5], + PppoeOptions); + argv[2] = strdup(buffer); + if (!argv[2]) { + /* TODO: Send a PADT */ + exit(EXIT_FAILURE); + } + + argv[3] = "file"; + argv[4] = PPPOE_SERVER_OPTIONS; + + snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d", + (int) LocalIP[0], (int) LocalIP[1], + (int) LocalIP[2], (int) LocalIP[3], + (int) session->ip[0], (int) session->ip[1], + (int) session->ip[2], (int) session->ip[3]); + syslog(LOG_INFO, + "Session %d created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d)", + ntohs(session->sess), + session->eth[0], session->eth[1], session->eth[2], + session->eth[3], session->eth[4], session->eth[5], + (int) session->ip[0], (int) session->ip[1], + (int) session->ip[2], (int) session->ip[3]); + argv[5] = buffer; /* No need for strdup -- about to execv! */ + argv[6] = "nodetach"; + argv[7] = "noaccomp"; + argv[8] = "nobsdcomp"; + argv[9] = "nodeflate"; + argv[10] = "nopcomp"; + argv[11] = "novj"; + argv[12] = "novjccomp"; + argv[13] = "default-asyncmap"; + if (Synchronous) { + argv[14] = "sync"; + argv[15] = NULL; + } else { + argv[14] = 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(struct ClientSession *session) +{ + /* Leave some room */ + char *argv[20]; + + char buffer[SMALLBUF]; + + argv[0] = "pppd"; + argv[1] = "plugin"; + argv[2] = PLUGIN_PATH; + argv[3] = IfName; + snprintf(buffer, SMALLBUF, "%d:%02x:%02x:%02x:%02x:%02x:%02x", + ntohs(session->sess), + session->eth[0], session->eth[1], session->eth[2], + session->eth[3], session->eth[4], session->eth[5]); + argv[4] = "rp_pppoe_sess"; + argv[5] = strdup(buffer); + if (!argv[5]) { + /* TODO: Send a PADT */ + exit(EXIT_FAILURE); + } + argv[6] = "file"; + argv[7] = PPPOE_SERVER_OPTIONS; + + snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d", + (int) LocalIP[0], (int) LocalIP[1], + (int) LocalIP[2], (int) LocalIP[3], + (int) session->ip[0], (int) session->ip[1], + (int) session->ip[2], (int) session->ip[3]); + syslog(LOG_INFO, + "Session %d created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d)", + ntohs(session->sess), + session->eth[0], session->eth[1], session->eth[2], + session->eth[3], session->eth[4], session->eth[5], + (int) session->ip[0], (int) session->ip[1], + (int) session->ip[2], (int) session->ip[3]); + argv[8] = buffer; + argv[9] = "nodetach"; + argv[10] = "noaccomp"; + argv[11] = "nobsdcomp"; + argv[12] = "nodeflate"; + argv[13] = "nopcomp"; + argv[14] = "novj"; + argv[15] = "novjccomp"; + argv[16] = "default-asyncmap"; + argv[17] = NULL; + execv(PPPD_PATH, argv); + exit(EXIT_FAILURE); +} + +/********************************************************************** +*%FUNCTION: startPPPD +*%ARGUMENTS: +* session -- client session record +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Starts PPPD +***********************************************************************/ +void +startPPPD(struct ClientSession *session) +{ + if (UseLinuxKernelModePPPoE) startPPPDLinuxKernelMode(session); + else startPPPDUserMode(session); +} + diff --git a/mdk-stage1/rp-pppoe/src/pppoe-sniff.c b/mdk-stage1/rp-pppoe/src/pppoe-sniff.c new file mode 100644 index 000000000..aa796547b --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/pppoe-sniff.c @@ -0,0 +1,258 @@ +/*********************************************************************** +* +* 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. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +#ifdef USE_DLPI +#include <sys/dlpi.h> +/* 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 + + 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 > 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 > 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/mdk-stage1/rp-pppoe/src/pppoe.c b/mdk-stage1/rp-pppoe/src/pppoe.c new file mode 100644 index 000000000..8ffe37248 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/pppoe.c @@ -0,0 +1,834 @@ +/*********************************************************************** +* +* pppoe.c +* +* Implementation of user-space PPPoE redirector for Linux. +* +* Copyright (C) 2000-2001 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. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef USE_LINUX_PACKET +#include <sys/ioctl.h> +#include <fcntl.h> +#endif + +#include <signal.h> + +#ifdef HAVE_N_HDLC +#ifndef N_HDLC +#include <linux/termios.h> +#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 */ + +PPPoEConnection *Connection = NULL; /* Must be global -- used + in signal handler */ +/*********************************************************************** +*%FUNCTION: sendSessionPacket +*%ARGUMENTS: +* conn -- PPPoE connection +* packet -- the packet to send +* len -- length of data to send +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Transmits a session packet to the peer. +***********************************************************************/ +void +sendSessionPacket(PPPoEConnection *conn, PPPoEPacket *packet, int len) +{ + packet->length = htons(len); + if (optClampMSS) { + clampMSS(packet, "outgoing", optClampMSS); + } + if (sendPacket(conn, conn->sessionSocket, packet, len + HDR_SIZE) < 0) { + exit(EXIT_FAILURE); + } + if (conn->debugFile) { + dumpPacket(conn->debugFile, packet, "SENT"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } +} + +#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. +***********************************************************************/ +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; + } + + syslog(LOG_INFO, + "Session terminated -- received PADT from access concentrator"); + parsePacket(packet, parseLogErrs, NULL); + 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. +***********************************************************************/ +void +sessionDiscoveryPacket(PPPoEConnection *conn) +{ + PPPoEPacket packet; + int len; + + if (receivePacket(conn->discoverySocket, &packet, &len) < 0) { + return; + } + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + return; + } + + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "RCVD"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } + + 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; + } + + syslog(LOG_INFO, + "Session terminated -- received PADT from peer"); + parsePacket(&packet, parseLogErrs, NULL); + 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; + + /* Open a session socket */ + conn->sessionSocket = openInterface(conn->ifName, Eth_PPPOE_Session, conn->myEth); + + /* 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"); + 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 +***********************************************************************/ +void +sigPADT(int src) +{ + syslog(LOG_DEBUG,"Received signal %d.",(int)src); + sendPADT(Connection, "RP-PPPoE: Received signal"); + 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 + fprintf(stderr, " -T timeout -- Specify inactivity timeout in seconds.\n"); + fprintf(stderr, " -D filename -- Log debugging information in filename.\n"); + fprintf(stderr, " -V -- Print version and exit.\n"); + fprintf(stderr, " -A -- Print access concentrator names and exit.\n"); + fprintf(stderr, " -S name -- Set desired service name.\n"); + fprintf(stderr, " -C name -- Set desired access concentrator name.\n"); + fprintf(stderr, " -U -- Use Host-Unique to allow multiple PPPoE sessions.\n"); + fprintf(stderr, " -s -- Use synchronous PPP encapsulation.\n"); + fprintf(stderr, " -m MSS -- Clamp incoming and outgoing MSS options.\n"); + fprintf(stderr, " -p pidfile -- Write process-ID to pidfile.\n"); + fprintf(stderr, " -e sess:mac -- Skip discovery phase; use existing session.\n"); + fprintf(stderr, " -n -- Do not open discovery socket.\n"); + fprintf(stderr, " -k -- Kill a session with PADT (requires -e)\n"); + fprintf(stderr, " -d -- Perform discovery, print session info and exit.\n"); + fprintf(stderr, " -f disc:sess -- Set Ethernet frame types (hex).\n"); + fprintf(stderr, " -h -- Print usage information.\n\n"); + fprintf(stderr, "PPPoE Version %s, Copyright (C) 2001 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 -- 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; + + PPPoEConnection conn; + +#ifdef HAVE_N_HDLC + int disc = N_HDLC; + long flags; +#endif + + /* Initialize connection info */ + memset(&conn, 0, sizeof(conn)); + conn.discoverySocket = -1; + conn.sessionSocket = -1; + + /* For signal handler */ + Connection = &conn; + + /* Initialize syslog */ + openlog("pppoe", LOG_PID, LOG_DAEMON); + + while((opt = getopt(argc, argv, "I:VAT:D:hS:C:Usm:np:e:kdf:")) != -1) { + switch(opt) { + 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': + pidfile = fopen(optarg, "w"); + if (pidfile) { + fprintf(pidfile, "%lu\n", (unsigned long) getpid()); + fclose(pidfile); + } + 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; + case 'D': + conn.debugFile = fopen(optarg, "w"); + 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; + 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 + } + + /* Set signal handlers: send PADT on TERM, HUP and INT */ + if (!conn.printACNames) { + signal(SIGTERM, sigPADT); + signal(SIGHUP, sigPADT); + signal(SIGINT, sigPADT); + +#ifdef HAVE_N_HDLC + if (conn.synchronous) { + if (ioctl(0, TIOCSETD, &disc) < 0) { + printErr("Unable to set line discipline to N_HDLC -- synchronous mode probably will fail"); + } 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 + + } + + 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); + } + 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: %.256s", str, strerror(errno)); + printErr(buf); + sendPADT(Connection, "RP-PPPoE: System call error"); + 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) +{ + char buf[1024]; + printErr(str); + sprintf(buf, "RP-PPPoE: %.256s", str); + sendPADT(Connection, buf); + 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 > len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + return; + } + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "RCVD"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } + +#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_source, conn->peerEth, ETH_ALEN)) { + /* Not for us -- must be another session. This is not an error, + so don't log anything. */ + return; + } + + if (packet.session != conn->session) { + /* Not for us -- must be another session. This is not an error, + so don't log anything. */ + return; + } + plen = ntohs(packet.length); + if (plen + HDR_SIZE > len) { + syslog(LOG_ERR, "Bogus length field in session packet %d (%d)", + (int) plen, (int) len); + return; + } + + /* Clamp MSS */ + if (clampMss) { + clampMSS(&packet, "incoming", clampMss); + } + + /* Compute FCS */ + fcs = pppFCS16(PPPINITFCS16, header, 2); + fcs = pppFCS16(fcs, packet.payload, plen) ^ 0xffff; + tail[0] = fcs & 0x00ff; + tail[1] = (fcs >> 8) & 0x00ff; + + /* Build a buffer to send to PPP */ + *ptr++ = FRAME_FLAG; + *ptr++ = FRAME_ADDR; + *ptr++ = FRAME_ESC; + *ptr++ = FRAME_CTRL ^ FRAME_ENC; + + for (i=0; i<plen; i++) { + c = packet.payload[i]; + if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) { + *ptr++ = FRAME_ESC; + *ptr++ = c ^ FRAME_ENC; + } else { + *ptr++ = c; + } + } + for (i=0; i<2; i++) { + c = tail[i]; + if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) { + *ptr++ = FRAME_ESC; + *ptr++ = c ^ FRAME_ENC; + } else { + *ptr++ = c; + } + } + *ptr++ = FRAME_FLAG; + + /* Ship it out */ + if (write(1, pppBuf, (ptr-pppBuf)) < 0) { + fatalSys("asyncReadFromEth: write"); + } +} + +/********************************************************************** +*%FUNCTION: syncReadFromEth +*%ARGUMENTS: +* conn -- PPPoE connection info +* sock -- Ethernet socket +* clampMss -- if true, clamp MSS. +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Reads a packet from the Ethernet interface and sends it to sync PPP +* device. +***********************************************************************/ +void +syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss) +{ + PPPoEPacket packet; + int len; + int plen; + struct iovec vec[2]; + unsigned char dummy[2]; +#ifdef USE_BPF + int type; +#endif + + 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; + } + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "RCVD"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } + +#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_source, conn->peerEth, ETH_ALEN)) { + /* Not for us -- must be another session. This is not an error, + so don't log anything. */ + return; + } + if (packet.session != conn->session) { + /* Not for us -- must be another session. This is not an error, + so don't log anything. */ + return; + } + plen = ntohs(packet.length); + if (plen + HDR_SIZE > len) { + syslog(LOG_ERR, "Bogus length field in session packet %d (%d)", + (int) plen, (int) len); + return; + } + + /* Clamp MSS */ + if (clampMss) { + clampMSS(&packet, "incoming", clampMss); + } + + /* Ship it out */ + vec[0].iov_base = (void *) dummy; + dummy[0] = FRAME_ADDR; + dummy[1] = FRAME_CTRL; + vec[0].iov_len = 2; + vec[1].iov_base = (void *) packet.payload; + vec[1].iov_len = plen; + + if (writev(1, vec, 2) < 0) { + fatalSys("syncReadFromEth: write"); + } +} + diff --git a/mdk-stage1/rp-pppoe/src/pppoe.h b/mdk-stage1/rp-pppoe/src/pppoe.h new file mode 100644 index 000000000..da300c17d --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/pppoe.h @@ -0,0 +1,331 @@ +/*********************************************************************** +* +* 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. +* +* $Id$ +* +***********************************************************************/ + +#ifdef __sun__ +#define __EXTENSIONS__ +#endif + +#include "config.h" + +#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) +#define _POSIX_SOURCE 1 /* For sigaction defines */ +#endif + +#include <stdio.h> /* For FILE */ +#include <sys/types.h> /* 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_NET_BPF_H) +#define USE_BPF 1 +#elif defined(HAVE_SYS_DLPI_H) +#define USE_DLPI +#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 <sys/cdefs.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +/* Ugly header files on some Linux boxes... */ +#if defined(HAVE_LINUX_IF_H) +#include <linux/if.h> +#elif defined(HAVE_NET_IF_H) +#include <net/if.h> +#endif + +#ifdef HAVE_NET_IF_TYPES_H +#include <net/if_types.h> +#endif + +#ifdef HAVE_NET_IF_DL_H +#include <net/if_dl.h> +#endif + +/* I'm not sure why this is needed... I do not have OpenBSD */ +#if defined(__OpenBSD__) +#include <net/ppp_defs.h> +#include <net/if_ppp.h> +#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 <sys/ethernet.h> +#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 16-bit integer type +#endif + +#ifdef HAVE_LINUX_IF_ETHER_H +#include <linux/if_ether.h> +#endif + +#include <netinet/in.h> + +#ifdef HAVE_NETINET_IF_ETHER_H +#include <sys/types.h> + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifndef HAVE_SYS_DLPI_H +#include <netinet/if_ether.h> +#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; + +/* PPPoE codes */ +#define CODE_PADI 0x09 +#define CODE_PADO 0x07 +#define CODE_PADR 0x19 +#define CODE_PADS 0x65 +#define CODE_PADT 0xA7 +#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 + +/* 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); + +/* Structures used by PPPoE server */ +struct ClientSession { + pid_t pid; /* PID of child handling session */ + unsigned char ip[IPV4ALEN]; /* IP address of peer */ + UINT16_t sess; /* Session number */ + unsigned char eth[ETH_ALEN]; /* Peer's Ethernet address */ + int recvdPADT; /* Peer sent a PADT */ +}; + +#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 */ +} PPPoEConnection; + +/* Structure used to determine acceptable PADO or PADS packet */ +struct PacketCriteria { + PPPoEConnection *conn; + int acNameOK; + int serviceNameOK; +}; + +/* 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); +void dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir); +void dumpHex(FILE *fp, unsigned char const *buf, int len); +int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra); +void parseLogErrs(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 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/mdk-stage1/rp-pppoe/src/relay.c b/mdk-stage1/rp-pppoe/src/relay.c new file mode 100644 index 000000000..9738cb8a8 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/relay.c @@ -0,0 +1,1541 @@ +/*********************************************************************** +* +* relay.c +* +* Implementation of PPPoE relay +* +* 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$ +* +***********************************************************************/ +static char const RCSID[] = +"$Id$"; + +#define _GNU_SOURCE 1 /* For SA_RESTART */ + +#include "relay.h" + +#include <signal.h> + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#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; i<NumInterfaces; i++) { + if (fd == Interfaces[i].discoverySock || + fd == Interfaces[i].sessionSock) return 1; + } + return 0; +} + +/********************************************************************** +*%FUNCTION: addTag +*%ARGUMENTS: +* packet -- a PPPoE packet +* tag -- tag to add +*%RETURNS: +* -1 if no room in packet; number of bytes added otherwise. +*%DESCRIPTION: +* Inserts a tag as the first tag in a PPPoE packet. +***********************************************************************/ +int +addTag(PPPoEPacket *packet, PPPoETag const *tag) +{ + return insertBytes(packet, packet->payload, 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 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; + 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<CLOSEFD; i++) { + if (!keepDescriptor(i)) { + close(i); + } + } + /* We nuked our syslog descriptor... */ + openlog("pppoe-relay", LOG_PID, LOG_DAEMON); + } + + /* Kick off SIGALRM if there is an idle timeout */ + if (IdleTimeout) alarm(1); + + /* Enter the relay loop */ + relayLoop(); + + /* Shouldn't ever get here... */ + return EXIT_FAILURE; +} + +/********************************************************************** +*%FUNCTION: addInterface +*%ARGUMENTS: +* ifname -- interface name +* clientOK -- true if this interface should relay PADI, PADR packets. +* acOK -- true if this interface should relay PADO, PADS packets. +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Opens an interface; sets up discovery and session sockets. +***********************************************************************/ +void +addInterface(char const *ifname, + int clientOK, + int acOK) +{ + PPPoEInterface *i; + if (NumInterfaces >= 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<MaxSessions-1; i++) { + AllSessions[i].prev = &AllSessions[i-1]; + AllSessions[i].next = &AllSessions[i+1]; + } + if (MaxSessions > 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; i++) { + AllSessions[i].sesNum = htons((UINT16_t) i+1); + } + + /* Initialize hashes in a linked list */ + AllHashes[0].prev = NULL; + AllHashes[0].next = &AllHashes[1]; + for (i=1; i<2*MaxSessions-1; i++) { + AllHashes[i].prev = &AllHashes[i-1]; + AllHashes[i].next = &AllHashes[i+1]; + } + AllHashes[2*MaxSessions-1].prev = &AllHashes[2*MaxSessions-2]; + AllHashes[2*MaxSessions-1].next = NULL; + + FreeHashes = AllHashes; +} + +/********************************************************************** +*%FUNCTION: createSession +*%ARGUMENTS: +* ac -- Ethernet interface on access-concentrator side +* cli -- Ethernet interface on client side +* acMac -- Access concentrator's MAC address +* cliMac -- Client's MAC address +* acSess -- Access concentrator's session ID. +*%RETURNS: +* PPPoESession structure; NULL if one could not be allocated +*%DESCRIPTION: +* Initializes relay hash table and session tables. +***********************************************************************/ +PPPoESession * +createSession(PPPoEInterface const *ac, + PPPoEInterface const *cli, + unsigned char const *acMac, + unsigned char const *cliMac, + UINT16_t acSes) +{ + PPPoESession *sess; + SessionHash *acHash, *cliHash; + + if (NumSessions >= 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<NumInterfaces; i++) { + sock = Interfaces[i].discoverySock; + if (sock > 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; i<NumInterfaces; i++) { + if (FD_ISSET(Interfaces[i].sessionSock, &readableCopy)) { + relayGotSessionPacket(&Interfaces[i]); + } + } + + /* Now handle discovery packets */ + for (i=0; i<NumInterfaces; i++) { + if (FD_ISSET(Interfaces[i].discoverySock, &readableCopy)) { + relayGotDiscoveryPacket(&Interfaces[i]); + } + } + + /* Handle the session-cleaning process */ + if (FD_ISSET(CleanPipe[0], &readableCopy)) { + char dummy; + CleanCounter = 0; + read(CleanPipe[0], &dummy, 1); + if (IdleTimeout) cleanSessions(); + } + } +} + +/********************************************************************** +*%FUNCTION: relayGotDiscoveryPacket +*%ARGUMENTS: +* iface -- interface on which packet is waiting +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Receives and processes a discovery packet. +***********************************************************************/ +void +relayGotDiscoveryPacket(PPPoEInterface const *iface) +{ + PPPoEPacket packet; + int size; + + if (receivePacket(iface->discoverySock, &packet, &size) < 0) { + return; + } + /* Ignore unknown code/version */ + if (packet.ver != 1 || packet.type != 1) { + return; + } + + /* Validate length */ + if (ntohs(packet.length) + HDR_SIZE > size) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + return; + } + + /* Drop Ethernet frame padding */ + if (size > ntohs(packet.length) + HDR_SIZE) { + size = ntohs(packet.length) + HDR_SIZE; + } + + switch(packet.code) { + case CODE_PADT: + relayHandlePADT(iface, &packet, size); + break; + case CODE_PADI: + relayHandlePADI(iface, &packet, size); + break; + case CODE_PADO: + relayHandlePADO(iface, &packet, size); + break; + case CODE_PADR: + relayHandlePADR(iface, &packet, size); + break; + case CODE_PADS: + relayHandlePADS(iface, &packet, size); + break; + default: + syslog(LOG_ERR, "Discovery packet on %s with unknown code %d", + iface->name, (int) packet.code); + } +} + +/********************************************************************** +*%FUNCTION: relayGotSessionPacket +*%ARGUMENTS: +* iface -- interface on which packet is waiting +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Receives and processes a session packet. +***********************************************************************/ +void +relayGotSessionPacket(PPPoEInterface const *iface) +{ + PPPoEPacket packet; + int size; + SessionHash *sh; + PPPoESession *ses; + + if (receivePacket(iface->sessionSock, &packet, &size) < 0) { + return; + } + + /* Ignore unknown code/version */ + if (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 > size) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + return; + } + + /* Drop Ethernet frame padding */ + if (size > ntohs(packet.length) + HDR_SIZE) { + size = ntohs(packet.length) + HDR_SIZE; + } + + /* We're in business! Find the hash */ + sh = findSession(packet.ethHdr.h_source, packet.session); + if (!sh) { + /* Don't log this. Someone could be running the client and the + relay on the same box. */ + return; + } + + /* Relay it */ + ses = sh->ses; + ses->epoch = Epoch; + sh = sh->peer; + packet.session = sh->sesNum; + memcpy(packet.ethHdr.h_source, sh->interface->mac, ETH_ALEN); + memcpy(packet.ethHdr.h_dest, sh->peerMac, ETH_ALEN); +#if 0 + fprintf(stderr, "Relaying %02x:%02x:%02x:%02x:%02x:%02x(%s:%d) to %02x:%02x:%02x:%02x:%02x:%02x(%s:%d)\n", + sh->peer->peerMac[0], sh->peer->peerMac[1], sh->peer->peerMac[2], + sh->peer->peerMac[3], sh->peer->peerMac[4], sh->peer->peerMac[5], + sh->peer->interface->name, ntohs(sh->peer->sesNum), + sh->peerMac[0], sh->peerMac[1], sh->peerMac[2], + sh->peerMac[3], sh->peerMac[4], sh->peerMac[5], + sh->interface->name, ntohs(sh->sesNum)); +#endif + sendPacket(NULL, sh->interface->sessionSock, &packet, size); +} + +/********************************************************************** +*%FUNCTION: relayHandlePADT +*%ARGUMENTS: +* iface -- interface on which packet was received +* packet -- the PADT packet +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Receives and processes a PADT packet. +***********************************************************************/ +void +relayHandlePADT(PPPoEInterface const *iface, + PPPoEPacket *packet, + int size) +{ + SessionHash *sh; + PPPoESession *ses; + + 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(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/mdk-stage1/rp-pppoe/src/relay.h b/mdk-stage1/rp-pppoe/src/relay.h new file mode 100644 index 000000000..d438a657f --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/relay.h @@ -0,0 +1,97 @@ +/********************************************************************** +* +* relay.h +* +* Definitions for PPPoE relay +* +* 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$ +* +***********************************************************************/ + +#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 |